Документация СУБД «Квант-Гибрид» v.1.5.0

СУБД «Квант-Гибрид» представляет собой цифровую платформу общего назначения, применимую для широкого круга разработчиков государственных информационных систем, приложений, а также для корпоративных отделов по цифровой трансформации предприятий крупного и среднего бизнеса

Документация СУБД «Квант-Гибрид» (Quantum Hybrid Base, далее по тексту QHB) представлена в 3 частях и приложениях:

Часть I - общее знакомство с внутренним строением QHB

Часть II - рассматриваются темы, представляющие интерес для администратора базы данных QHB

Часть III - рассматриваются темы, связанные с формальными знаниями языка SQL

Приложения

Состояние документирования

Мы, разработчики QHB, с признательностью примем от Вас информацию об ошибках в Документации на почтовый адрес qhb.support@quantom.info. Мы не скрываем, что продукт QHB создавался как форк от свободно распространяемого open source продукта PostgreSQL. Вероятные ошибки в документации могут быть связаны именно с этим.

О состоянии Документации и её перевода мы будем регулярно сообщать в сопроводительной документации к каждому релизу. Недостающие и/или непереведённые главы Вы можете найти на официальной странице документации PostgreSQL по адресу: https://www.postgresql.org/docs/.

Версия документации: 1.5.0
Номер ревизии: b2d09ce9

Дополнительно

Вы можете скачать дополнительно:

Документация в формате epub

Ознакомьтесь со списком поддерживаемых платформ и замечаниями к релизу QHB.

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

Документация на СУБД «Квант-Гибрид» предыдущих версий:

Документация на QHB 1.4.0

Документация на QHB 1.3.0 | Документация на QHB 1.3.1

Документация на QHB 1.2.0

Документация на QHB 1.1.0

Юридическое уведомление

Программное обеспечение СУБД «Квант-Гибрид» v.1.5.0

Copyright © KVANTOM LLC, 2019-2023

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

Программное обеспечение было создано в результате правомерной переработки Системы Управления Базами Данных PostgreSQL, распространяемой Калифорнийским университетом по открытой лицензии, и в отношении такой исходной программы применимы следующие условия ее использования по открытой лицензии:

Portions Copyright © 1996-2023, PostgreSQL Global Development Group

Portions Copyright © 1994 Regents of the University of California

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

КАЛИФОРНИЙСКИЙ УНИВЕРСИТЕТ НЕ НЕСЕТ НИКАКОЙ ОТВЕТСТВЕННОСТИ ЗА ЛЮБЫЕ ПОВРЕЖДЕНИЯ, ВКЛЮЧАЯ ПОТЕРЮ ДОХОДА, НАНЕСЕННЫЕ ПРЯМЫМ ИЛИ НЕПРЯМЫМ, СПЕЦИАЛЬНЫМ ИЛИ СЛУЧАЙНЫМ ИСПОЛЬЗОВАНИЕМ ДАННОГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ЕГО ДОКУМЕНТАЦИИ, ДАЖЕ ЕСЛИ КАЛИФОРНИЙСКИЙ УНИВЕРСИТЕТ БЫЛ ИЗВЕЩЕН О ВОЗМОЖНОСТИ ТАКИХ ПОВРЕЖДЕНИЙ.

КАЛИФОРНИЙСКИЙ УНИВЕРСИТЕТ СПЕЦИАЛЬНО ОТКАЗЫВАЕТСЯ ПРЕДОСТАВЛЯТЬ ЛЮБЫЕ ГАРАНТИИ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ТОЛЬКО ЭТИМИ ГАРАНТИЯМИ: НЕЯВНЫЕ ГАРАНТИИ ПРИГОДНОСТИ ТОВАРА ИЛИ ПРИГОДНОСТИ ДЛЯ ОТДЕЛЬНОЙ ЦЕЛИ. ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ НА ОСНОВЕ ПРИНЦИПА "КАК ЕСТЬ" И КАЛИФОРНИЙСКИЙ УНИВЕРСИТЕТ НЕ ОБЯЗАН ПРЕДОСТАВЛЯТЬ СОПРОВОЖДЕНИЕ, ПОДДЕРЖКУ, ОБНОВЛЕНИЯ, РАСШИРЕНИЯ ИЛИ ИЗМЕНЕНИЯ.

Кроме того, Программное обеспечение содержит модули собственной разработки:

TARQ - самобалансирующийся менеджер кэша дисковых блоков с автоматической компенсацией нагрузки на дисковую систему;

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

библиотечный кэш разобранных запросов;

серверный процесс, организующий фоновую запись на диск;

RBytea - модуль для внешнего хранения больших бинарных объектов с сохранением способа их обработки в прикладных системах;

QSS - прозрачное шифрование данных с использованием алгоритма ГОСТ Р 3412-15 "Кузнечик" для произвольных объектов, включая внешние большие объекты;

подсистема сбора и агрегации метрик;

QDL - модуль для прямой загрузки больших объёмов данных из текстового представления непосредственно в страницы данных;

модуль хранения Append_Only;

модуль хранения Holdmem;

Qbackup - подсистема резервного копирования и восстановления с поддержкой каталога, инкрементальных резервных копий, архивации резервных копий;

2Q - встроенная очередь сообщений;

UMCA - дамп и загрузка содержимого менеджера кэша дисковых блоков;

Qbim - расширение, реализующее битемпоральную модель данных;

Qbayes - расширение, инструментарий для задач расчёта вероятностных моделей с применением сетей Байеса;

бинарные утилиты для управления СУБД;

подсистема интернационализации i18n.

Программное обеспечение и все его компоненты предоставляются по принципу «как есть», что подразумевает: Пользователю известны важнейшие свойства Программного обеспечения, Пользователь несет риск соответствия Программного обеспечения его желаниям и потребностям, а также риск соответствия объема предоставляемых прав своим потребностям.

Пользователь принимает на себя обязательство не производить следующие действия с Программным обеспечением - эмулировать, декомпилировать, дизассемблировать, дешифровать, модифицировать и иные аналогичные действия.

Настоящее соглашение регулируется действующим законодательством Российской Федерации.

Правила сообщения об ошибках

Если вы найдёте ошибку в QHB, сообщите нам об этом. Ваши отчёты об ошибках играют важную роль в повышении надежности QHB, потому что даже при самом высоком качестве кода нельзя гарантировать, что каждая функция QHB будет работать на любой платформе и при любых обстоятельствах.

Следующие шаги призваны помочь вам в формировании отчётов об ошибках, которые могут быть обработаны эффективно. Мы не предлагаем выполнять их досконально, но лучше им следовать.

Мы не можем обещать немедленного исправления каждой ошибки. Если ошибка очевидна, критична или затрагивает большинство пользователей, то велика вероятность, что кто-то ею займется. Иногда мы рекомендуем вам обновить версию, чтобы увидеть, воспроизводится ли ошибка. Мы также можем решить, что данная ошибка не может быть исправлена до того, как будет выпущено какое-либо серьезное, запланированное обновление. Или, может быть, исправление слишком сложно, и на повестке дня есть более важные вещи. Если вам нужна немедленная помощь, подумайте о заключении договора о коммерческой поддержке.

Выявление ошибок

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

Далее приведены лишь некоторые примеры возможных ошибок:

Программа завершается аварийным сигналом или сообщением об ошибке операционной системы, указывающем на проблему в программе. (Контрпримером может быть сообщение «Недостаточно места на диске» - эту проблему вы должны решить самостоятельно).

Программа отказывается принимать допустимые (согласно документации) данные.

Программа принимает недопустимые входные данные без предупреждения или сообщения об ошибке. Но имейте в виду, что данные, которые вы считаете некорректными, мы можем считать приемлемым расширением или совместимым с принятой практикой.

Не получается скомпилировать, собрать или установить QHB на поддерживаемых платформах в соответствии с инструкциями.

Здесь под определением «программа» подразумевается любой исполняемый файл, а не только серверный процесс.

Медленная работа - не обязательно ошибка. Прочитайте документацию или обратитесь в один из списков рассылки за помощью в настройке ваших приложений. Несоблюдение SQL-стандарта не обязательно является ошибкой, если соответствие для конкретной функции явно не заявлено.

Что сообщить

Самое важное, что следует помнить о сообщениях об ошибках, - сообщайте все факты и только факты. Не размышляйте о том, что, по вашему мнению, пошло не так, что «казалось, что оно делает» или какая часть программы содержит ошибку. Если вы не знакомы с реализацией, вы, скорее всего, ошибётесь и не поможете нам. Грамотные объяснения являются отличным дополнением к фактам, но не заменяют их. Если мы собираемся исправить ошибку, мы все равно должны будем посмотреть в чём она заключается. Сообщить голые факты относительно просто (можно просто скопировать и вставить текст с экрана), но слишком часто важные детали не упоминаются, потому что считаются незначительными и неважными или кажется, что отчет будет понятен и без них.

В каждом отчете об ошибке следует указать следующую информацию:

Точная последовательность действий начиная с запуска программы, необходимая для воспроизведения проблемы. Она должна быть полной; если вывод зависит от данных в таблицах, то недостаточно указать только SELECT без предшествующих операторов CREATE TABLE и INSERT. У нас не будет времени на восстановление вашей схемы базы данных, и если предполагается, что мы должны создать собственные данные, мы, вероятно, пропустим эту проблему.

Если ваше приложение использует какой-либо другой клиентский интерфейс, например PHP, попробуйте изолировать ошибочные запросы. Вряд ли мы будем устанавливать веб-сервер для воспроизведения вашей ошибки. В любом случае не забудьте предоставить конкретные входные файлы; не гадайте о том, что проблема возникает для «больших файлов» или «баз данных среднего размера» и т. д., поскольку эта информация слишком расплывчата.

Результат выполнения. Пожалуйста, не пишите, что это "не работает" или "сломалось". Если есть сообщение об ошибке, покажите его, даже если оно выглядит непонятным. Если программа завершается ошибкой операционной системы, приведите эту ошибку. Если ничего не происходит, так и напишите. Даже если результатом вашего теста является сбой программы или что-то очевидное - на нашей платформе это может не произойти. Проще всего будет скопировать текст с терминала, если это возможно.

Важно описать результат, который вы ожидали получить. Если вы просто напишете "Эта команда выдает мне такой результат" Или "Это не то, что я ожидал", мы можем запустить ваш пример сами, просмотреть вывод и решить, что все в порядке и результат соответствует ожиданиям. Мы вряд ли будем тратить время на расшифровку точного смысла ваших команд. Особенно воздерживайтесь от простого заявления: "Это не то, что делает SQL/Oracle". Выяснение соответствия SQL-стандартам зачастую трудоёмкое и скучное занятие, а логика работы других реляционных баз данных может быть не полностью документирована или отличаться в нюансах. (Если ваша проблема - сбой программы, то вы, очевидно, можете пропустить этот пункт).

Все параметры командной строки и другие параметры запуска, включая все связанные переменные окружения или файлы конфигурации, которые вы изменяли со значений по умолчанию. Пожалуйста, предоставьте точную информацию. Если вы используете предварительно упакованный дистрибутив, который запускает сервер базы данных во время загрузки, вы должны попытаться выяснить, как это сделать.

Все что вы сделали не так, как написано в инструкции по установке.

Версия QHB. Чтобы узнать версию сервера, к которому вы подключены Вы можете запустить команду, SELECT version();. Большинство исполняемых программ также поддерживает опцию --version; по крайней мере qhb --version и psql --version должно работать. Если функция или параметры не существуют, значит скорее всего вашу версию пора обновить. Если вы запускаете предварительно упакованную версию, такую ​​как RPM, укажите subversion, которую может иметь пакет. Если вы пишете о снимке Git, укажите это, а также хеш коммита.

Если ваша версия старее актуальной, мы почти наверняка предложим вам её обновить. В каждом новом выпуске содержится много исправлений и улучшений, поэтому вполне возможно, что ошибка, с которой вы столкнулись в более старой версии QHB, уже исправлена. Мы можем предоставить только ограниченную поддержку для программ, использующих старые версии QHB; если вам этого недостаточно - рассмотрите возможность заключения договора о коммерческой поддержке.

Информация о платформе. Включает в себя имя и версию ядра, библиотеку C/RUST, процессор, информацию о памяти и так далее. В большинстве случаев достаточно сообщить поставщика и версию, но не следует предполагать, что все знают, что именно содержит «Debian» или что все работают на x86_64. Если у вас есть проблемы с установкой, то вам также необходима информация о наборе инструментов на вашем компьютере (компилятор, make и т.д.).

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

Не тратьте все свое время, чтобы выяснить, при каких входных данных ошибка исчезает. Скорее всего это не поможет решить проблему. Если окажется, что вам удалось найти решение раньше нас, пожалуйста, уделите немного времени и поделитесь своими решениями. Кроме того, не тратьте свое время на предположения о причине ошибки. Если ошибка воспроизводится, то мы установим причину.

При написании отчета об ошибке избегайте путаницы в терминологии. Официальное наименование Программного пакета - "КВАНТ-ГИБРИД", для краткости в документации скорее всего будет использовано название "QHB". Если вы говорите о бэкенд-процессе, отметьте это, а не просто говорите «Сбои QHB». Сбой одного бэкенд-процесса сильно отличается от сбоя родительского процесса "qhb"; пожалуйста, не пишите, что "процесс упал", когда имеется в виду, что один серверный процесс вышел из строя, или наоборот. Кроме того, клиентские программы, такие как интерактивный интерфейс "PSQL", полностью отделены от бэкенда. Пожалуйста, постарайтесь указать, проявляется ли проблема на стороне клиента или сервера.

Как сообщать об ошибках

Отправляйте отчёты об ошибках по адресу:

qhb.support@quantom.info

В теме письма желательно указать краткое описание проблемы, возможно, включив в неё часть сообщения об ошибке.

Часть I. Внутреннее устройство

Добро пожаловать в руководство по внутреннему устройству СУБД «Квант-Гибрид» (QHB). Основная цель этой части - познакомить вас на практике с основными аспектами системы QHB, без глубокого погружения в рассматриваемые темы.

Освоив это руководство, вы можете перейти к чтению части II для установки и администрирования своего собственного сервера или части III, для получения информации по использованию языка SQL в QHB.

Основы архитектуры

Модуль безопасного хранения QSS

Индексы

Краткая справка по индексам

Типы индексов

Многоколоночные индексы

Индексы и ORDER BY

Объединение нескольких индексов

Уникальные индексы

Индексы по выражениям

Частичные индексы

Сканирование только по индексу и покрывающие индексы

Классы операторов и семейства операторов

Индексы и правила сортировки

Анализ использования индексов

Индексы B-деревья

Индексы GIN

Индексы GiST

Индексы SP-GiST

Индексы BRIN

Параллельный контроль

Многоверсионная модель

Изоляция транзакций

Явная блокировка

Проверка согласованности данных на уровне приложения

Ограничения

Блокировка и индексы

Большие объекты

Краткая справка по большим объектам

Особенности реализации

Клиентские интерфейсы

Серверные функции

Пример программы

Расширение SQL

Как работает расширяемость

Система типов QHB

Пользовательские функции

Пользовательские процедуры

Функции на языке запросов (SQL)

Перегрузка функций

Категории изменчивости функций

Функции на процедурном языке

Внутренние функции

Функции на нативном языке

Информация по оптимизации функций

Пользовательские агрегаты

Пользовательские типы

Пользовательские операторы

Информация по оптимизации оператора

Интерфейсные расширения для индексов

Упаковка связанных объектов в расширение

Инфраструктура сборки расширений

Триггеры

Обзор триггерного поведения

Видимость изменений данных

Написание триггерных функций на C

Полный пример запуска

Триггеры событий

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

Матрица запуска событий

Написание функций запуска событий на C

Полный пример запуска события

Пример запуска события перезаписи таблицы

Интерфейс программирования сервера

Функции интерфейса

Вспомогательные функции интерфейса

Управление памятью

Управление транзакциями

Видимость изменений данных

Примеры

Фоновые рабочие процессы

Процесс QHB

Описание

Параметры

Переменные среды

Диагностика

Примечания

Ошибки

Примеры

Смотрите Также

Системные каталоги

Обзор

pg_aggregate

pg_am

pg_amop

pg_amproc

pg_attrdef

pg_attribute

pg_authid

pg_auth_members

pg_cast

pg_class

pg_collation

pg_constraint

pg_conversion

pg_database

pg_db_role_setting

pg_default_acl

pg_depend

pg_description

pg_enum

pg_event_trigger

pg_extension

pg_foreign_data_wrapper

pg_foreign_server

pg_foreign_table

pg_index

pg_inherits

pg_init_privs

pg_language

pg_largeobject

pg_largeobject_metadata

pg_namespace

pg_opclass

pg_operator

pg_opfamily

pg_partitioned_table

pg_policy

pg_proc

pg_publication

pg_publication_rel

pg_range

pg_replication_origin

pg_rewrite

pg_seclabel

pg_sequence

pg_shdepend

pg_shdescription

pg_shseclabel

pg_statistic

pg_statistic_ext

pg_statistic_ext_data

pg_subscription

pg_subscription_rel

pg_tablespace

pg_transform

pg_trigger

pg_ts_config

pg_ts_config_map

pg_ts_dict

pg_ts_parser

pg_ts_template

pg_type

pg_user_mapping

Системные представления

pg_available_extensions

pg_available_extension_versions

pg_config

pg_cursors

pg_file_settings

pg_group

pg_hba_file_rules

pg_indexes

pg_locks

pg_matviews

pg_policies

pg_prepared_statements

pg_prepared_xacts

pg_publication_tables

pg_replication_origin_status

pg_replication_slots

pg_roles

pg_rules

pg_seclabels

pg_sequences

pg_settings

pg_shadow

pg_stats

pg_stats_ext

pg_tables

pg_timezone_abbrevs

pg_timezone_names

pg_user

pg_user_mappings

pg_views

Определение интерфейса метода доступа к таблице

Определение интерфейса метода доступа индекса

Базовая структура API для индексов

Функции доступа к индексу

Индексное сканирование

Вопросы блокировки индекса

Проверка уникальности индекса

Функции оценки стоимости индекса

Общие записи WAL

Индексы B-деревья

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

Вспомогательные функции B-деревьев

Физическое хранилище базы данных

Структура файлов базы данных

TOAST

Карта свободного пространства

Карта видимости

Слой инициализации

Внутренняя структура страницы базы данных

Как планировщик использует статистику

Примеры оценок строк

Примеры многомерной статистики

Планировщик статистики и безопасности

Асинхронный пул соединений QCP

Модуль прямой загрузки больших объёмов данных QDL

Менеджер кэша дисковых блоков TARQ

Дамп и загрузка содержимого менеджера кэша дисковых блоков UMCA

Параметр HOLDMEM и дополнительные кэши дисковых блоков

Таблицы APPEND_ONLY

Сервер метрик

Встроенная очередь сообщений

Планировщик заданий QSched

Переменные уровня сессии qhb-variables

Профилирование долгих запросов

Формат манифеста резервной копии

Основы архитектуры

Прежде чем мы продолжим, необходимо понять основы архитектуры системы QHB.
Понимание того, как взаимодействуют части QHB, сделает эту главу несколько понятнее.

В терминах баз данных QHB использует модель клиент / сервер. Сессия QHB состоит из следующих взаимодействующих процессов (программ):

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

Клиентское приложение пользователя, которое будет выполнять операции с базой данных. Клиентские приложения могут быть очень разнообразными по своей природе: клиент может быть текстовым инструментом, графическим приложением, веб-сервером, который обращается к базе данных для отображения веб-страниц, или специализированным инструментом обслуживания базы данных. Некоторые клиентские приложения поставляются с дистрибутивом QHB, но большинство разработано пользователями.

Обычно для клиент-серверных приложений, клиент и сервер находятся на разных хостах. В этом случае они общаются через сетевое соединение по протоколу TCP/IP. Следует помнить об этом, поскольку файлы, к которым можно получить доступ на клиентском компьютере, могут быть недоступны (или могут быть доступны только с использованием другого имени файла) на сервере базы данных.

Сервер QHB может обрабатывать несколько одновременных подключений от клиентов. Для этого запускается (при помощи системного вызова "fork") новый процесс для каждого соединения. С этого момента клиент и новый серверный процесс обмениваются данными без вмешательства главного процесса qhb. Таким образом, главный процесс сервера всегда работает, ожидая клиентских подключений, тогда как процессы клиента и связанных серверов создаются и удаляются. (Все это происходит, конечно, прозрачно для пользователя.)

Модуль безопасного хранения QSS

Описание

Модуль безопасного хранения «КВАНТ-ГИБРИД» (Quantum Secure Storage, QSS) позволяет создавать таблицы, которые шифруются с поддержкой криптоалгоритма ГОСТ Р 3412-15 "Кузнечик" при записи на диск.

При старте сервера СУБД главный процесс QHB читает с диска мастер-ключ шифрования и сохраняет его в общей памяти, доступной дочерним процессам.

При чтении/записи блока или страницы с/на диск этот ключ используется для их расшифровки и зашифрования.

Использование

Использование модуля в SQL:

create table t_qss(c1 int, c2 varchar) USING qss;

В qhb.conf добавляется параметр qss_mode: int с возможными режимами:

0 — выключено: попытка создания новых таблиц с using qss или чтение/запись в уже существующие приведет к ошибкам;

1 — включено: при запуске сервера считывается qss.toml и производится загрузка актуального мастер-ключа с его расшифровкой; при любых ошибках сервер останавливается.

Также, для ускорения работы с таблицей, возможно создать ее с параметром HOLDMEM. Для этого необходимо при создании пометить эту таблицу как UNLOGGED и добавить WITH (HOLDMEM = ONLY). Запрос на создание такой таблицы выглядит так:

CREATE UNLOGGED TABLE TABLE_NAME (...) USING qss WITH (HOLDMEM = ONLY)

Дополнительную информацию см. в разделе HOLDMEM.

Файлы, используемые в QSS:

.
└── PGDATA
    ├── base
    ├── qhb.conf
    └── qss
        ├── 0.key
        ├── 1.key
        └── qss.toml

В qss.toml хранятся общие настройки qss (например, путь к динамической библиотеке работы с криптографическим маркером (криптотокеном)) и дополнительная информация по ключам, например, режим расшифровки ключа:

не зашифрован;

ключ расшифровки находится в файловой системе, например, на примонтированном usb, с указанием пути;

ключ расшифровки находится на криптотокене, с указанием его идентификатора.

Бинарная версии ключа (файл .key) состоит из заголовка постоянного размера с информацией: версия (1), режима расшифровки (должен совпадать с записью в файле-конфигурации) и 32 байтов зашифрованного мастер-ключа.

Утилита qss_mgr и управление ключами

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

bash-4.2$ qss_mgr --help
qss_mgr 1.1.0
qss создает защищенное хранилище в базе данных QHB

ПРИМЕНЕНИЕ:
    qss_mgr [ФЛАГИ] [ПАРАМЕТРЫ] <СУБКОМАНДА>

ФЛАГИ:
    -h, --help
            Выводит справочную информацию

        --new
            Работает с набором ключей для замены ключа

    -V, --version
            Выводит информацию о версии

ПАРАМЕТРЫ:
    -d, --data-dir <каталог-данных>
            Указывает каталог с данными базы данных [env: PGDATA=/tmp/qhb-data/]

СУБКОМАНДЫ:
    add        Добавить новую версию мастер-ключа, зашифрованную другим секретным ключом
    del        Удалить ключ
    help       Вывести данное сообщение или справку по заданным субкомандам
    init       Запустить файл конфигурации и первый зашифрованный мастер-ключ
    use-new    Создать резервную копию текущего набора ключей и заменить его новым
    verify     Проверить ключ или ключи

Общий флаг --new позволяет работать с «новым» набором ключей, переключиться на который можно командой use-new.

Инициализация с добавлением первого мастер-ключа

Примечание
в режиме pkcs11 для ввода пин-кода используется утилита qss-pinpad, запущенная на этом же компьютере в другом терминале.

Запуск файла конфигурации и первого зашифрованного мастер-ключа
ПРИМЕНЕНИЕ:
    qss_mgr init [ПАРАМЕТРЫ] [мастер-ключ]

ПАРАМЕТРЫ:
    -k, --key <ключ>                                    Ключ для шифрования
    -f, --key-format <формат-ключа>                      Формат шифруемого ключа: bin, base64, armored [по умолчанию: bin]
        --master-key-format <формат-мастер-ключа>        Формат исходного мастер-ключа: bin, base64, armored [по умолчанию: bin]
    -m, --mode <режим>                                  Режим шифрования мастер-ключа: fs, pkcs11 [по умолчанию: pkcs11]
        --module <путь-к-модулю-pkcs11>                  Модуль PKCS11
        --token-key-id <id-ключа-токена>                  ID пользовательского ключа на токене для pkcs11
        --token-serial-number <серийный-номер-токена>    Серийный номер токена для pkcs11

АРГУМЕНТЫ:
    <master-key>    Файл исходного мастер-ключа

Добавление мастер-ключа, зашифрованного ключом другого пользователя

Добавление новой версии мастер-ключа, зашифрованного другим секретным ключом
ПРИМЕНЕНИЕ:
    qss_mgr add [ПАРАМЕТРЫ] [мастер-ключ]

ПАРАМЕТРЫ:
    -k, --key <ключ>                                    Ключ для шифрования
    -f, --key-format <формат-ключа>                      Формат шифруемого ключа: bin, base64, armored [по умолчанию: bin]
        --master-key-format <формат-мастер-ключа>        Формат исходного мастер-ключа: bin, base64, armored [по умолчанию: bin]
    -m, --mode <режим>                                  Режим шифрования мастер-ключа: fs, pkcs11 [по умолчанию: pkcs11]
    -n, --num <индекс>                                    Индекс предыдущего ключа, откуда загружается мастер-ключ
        --token-key-id <id-ключа-окена>                  ID пользовательского ключа на токене для pkcs11
        --token-serial-number <серийный-номер-токена>    Серийный номер токена для pkcs11

АРГУМЕНТЫ:
    <мастер-ключ>    Файл исходного мастер-ключа

Удаление ключа

Удаление ключа

ПРИМЕНЕНИЕ:
    qss_mgr del [ФЛАГИ] --num <индекс>

ФЛАГИ:
    -q, --quiet      Тихий режим: не запрашивать подтверждение

ПАРАМЕТРЫ:
    -n, --num <индекс>    Индекс ключа

Переключение на новый набор ключей

Создать резервную копию текущего набора ключей и заменить его новым

ПРИМЕНЕНИЕ:
    qss_mgr use-new

Проверка ключей

Проверить ключ или ключи. При проверке всех ключей проверить, что мастер-ключ не изменился
ПРИМЕНЕНИЕ:
    qss_mgr verify [ПАРАМЕТРЫ]

ПАРАМЕТРЫ:
    -n, --num <индекс>              Индекс ключа; если не указан, пытается проверить все ключи

Утилита qss_recrypt

Утилита qss_recrypt используется для перешифровки таблиц базы данных новым набором ключей.

Список команд

qss_reqcrypt 1.1.0
qss перешифровывает кластер QHB новым мастер-ключом

ПРИМЕНЕНИЕ:
    qss_recrypt [ФЛАГИ] --data-dir <каталог-данных> <СУБКОМАНДА>

ФЛАГИ:
    -h, --help       Выводит справочную информацию
    -V, --version    Выводит информацию о версии
    -v, --verbose    Устанавливает уровень журналирования для Debug [по умолчанию: Info]

ПАРАМЕТРЫ:
        --data-dir <каталог-данных>    Указывает каталог с данными базы данных [среда: PGDATA=/tmp/qhb-data/]

СУБКОМАНДЫ:
    add        Сканировать базу данных и добавить данные о ее зашифрованных таблицах в файл конфигурации
    help       Вывести данное сообщение или справку по заданным субкомандам
    recrypt    Перешифровать кластер базы данных

Сканирование базы данных и сохранение данных о ее зашифрованных таблицах в файл конфигурации

Сканирование базы данных и добавление данных о ее зашифрованных таблицах в файл конфигурации

ПРИМЕНЕНИЕ:
    qss_recrypt --data-dir <каталог-данных> add [ФЛАГИ] [ПАРАМЕТРЫ]

ФЛАГИ:
        --no-timeout    Запустить приложение без задержки; используется вместо -t 0s

ПАРАМЕТРЫ:
    -d, --dbname <имя-БД>        Указывает имя базы данных, к которой нужно подключиться [среда: PGDATABASE=]
    -h, --host <хост>            Указывает имя хост-узла, на котором запускается сервер. Если значение
                                 начинается с косой черты (/), узел используется в качестве каталога для сокета Unix-домена [среда:
                                 PGHOST=/home/evgen/work/db/build/dbsockets]
    -p, --port <порт>            Указывает порт TCP или локальное расширение файла сокета Unix-домена, на котором
                                 сервер перехватывает подключения [среда: PGPORT=]  [по умолчанию: 5432]
    -t, --timeout <время-ожидания>      Ожидание в секундах до попытки подключения; поддерживает "человеческое время", -t 3s
    -U, --username <имя-пользователя>    Подключение к базе данных под именем данного пользователя, а не под именем по умолчанию [среда: PGUSER=]

Перешифровка кластера (экземпляра QHB)

При старте будет выполнена расшифровка старого мастер-ключа (с запросом пина криптотокена при необходимости), а потом нового.

Перешифровка кластера базы данных

ПРИМЕНЕНИЕ:
    qss_recrypt --data-dir <каталог-данных> recrypt [ФЛАГИ]

ФЛАГИ:
    -y, --assumeyes    автоматически отвечать "да" на все вопросы
    -h, --help         Выводит справочную информацию
    -V, --version      Выводит информацию о версии

Перед перешифровкой выдается предупреждение: «Пожалуйста, не запускайте сервер СУБД во время работы утилиты, это приведет к непоправимому повреждению данных» или "Please do not start the DBMS server while the utility is running, it will cause irreparable data corruption".

Если не указан флаг -y, то после предупреждения задается вопрос и ожидается подтверждение продолжения.

Утилита qss_pinpad

Утилита qss_pinpad предназначена для ввода пина криптотокена при использовании ключей в режиме pkcs11.

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

Утилита magma_key_gen

Утилита magma_key_gen предназначена для генерации пользовательских ключей QSS на криптотокенах, поддерживающих аппаратное шифрование по ГОСТ 34.12-2018 и ГОСТ 34.13-2018. Утилита поставляется в пакете qhb-contrib.

magma_key_gen принимает следующие аргументы командной строки:

Аргумент | Описание
--id id | идентификатор создаваемого ключа
--module pkcs11-module-path | путь к библиотеке криптотокена. По умолчанию используется /usr/lib64/librtpkcs11ecp.so

Если ключ с таким идентификатором уже существует, программа выводит об этом сообщение и завершает работу.

Пример создания и включения зашифрованной таблицы

Получение тестовых ключей

Создать мастер-ключ и ключ пользователя:

head -c 32 /dev/urandom > master_key.bin
head -c 32 /dev/urandom > user1.bin

Инициализация QSS в режиме мастер-ключа, подписанного пользовательским ключом, на файловой системе

qss_mgr init \
  --module=/usr/lib64/librtpkcs11ecp.so \
  --mode fs \
  --key user1.bin \
  master_key.bin

Инициализация QSS в режиме мастер-ключа, подписанного пользовательским ключом, на криптотокене

qss_mgr init \
  --module=/usr/lib64/librtpkcs11ecp.so \
  --mode pkcs11 \
  --token-serial-number 3c4c6444 \
  --token-key-id 1234 \
  master_key.bin

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

Должен быть доступен ключ первого пользователя.

qss_mgr add \
  -mode fs \
  -k user2.bin \
  -n 0

Включение QSS на сервере

echo "qss_mode = 1" >> "${PGDATA}/qhb.conf"
qhb-ctl restart

Создание зашифрованной таблицы

create table t_qss(c1 int, c2 varchar) USING qss;

Подготовка к перешифровке базы данных новым мастер-ключом

Добавление нового мастер-ключа

qss_mgr --new init \
  --module=/usr/lib64/librtpkcs11ecp.so \
  --mode fs \
  --key user1.bin \
  new_master_key.bin

Сбор информации о зашифрованных таблицах в базе данных

Выполняется при запущенном кластере, необходимо повторить для всех баз данных в кластере, содержащих зашифрованные таблицы.

qss_recrypt --data-dir "${PGDATA}" add \
  --dbname my

Запуск перешифровки кластера

Выполняется на остановленном кластере.

qss_recrypt --data-dir "${PGDATA}" recrypt

Переключение на новый мастер-ключ

Выполняется на остановленном кластере. Переносит текущий набор ключей в папку old, заменяя его подготовленным набором ключей из папки new.

qss_mgr --data-dir "${PGDATA}" use-new

Индексы

Индексы являются распространенным способом повышения производительности базы данных. Индекс позволяет серверу базы данных находить и извлекать определенные строки гораздо быстрее, чем без индекса. Но индексы также увеличивают нагрузку на систему баз данных в целом, поэтому их следует использовать разумно.

Краткая справка по индексам

Типы индексов

Многоколоночные индексы

Индексы и ORDER BY

Объединение нескольких индексов

Уникальные индексы

Индексы по выражениям

Частичные индексы

Сканирование только по индексу и покрывающие индексы

Классы операторов и семейства операторов

Индексы и правила сортировки

Анализ использования индексов

Краткая справка по индексам

Предположим, у нас есть таблица, подобная этой:

CREATE TABLE test1 (
    id integer,
    content varchar
);

и приложение делает много запросов вида

SELECT content FROM test1 WHERE id = constant;

Без предварительной подготовки система должна будет сканировать всю таблицу test1, строка за строкой, чтобы найти все соответствующие записи. Если в test1 есть много строк и только несколько из них (возможно, ноль или одна) будут возвращены таким запросом, то это явно неэффективный метод. Но если система получила указание поддерживать индекс для столбца id, она может использовать более эффективный метод для поиска подходящих строк. Например, достаточно будет пойти на несколько уровней вглубь дерева поиска.

Подобный подход используется в большинстве научно-популярных книг: термины и понятия, которые часто ищут читатели, собраны в алфавитном указателе в конце книги. Заинтересованный читатель может сравнительно быстро просканировать указатель и перейти к соответствующей странице (страницам), а не читать всю книгу целиком, чтобы найти интересующий материал. Подобно тому, как задача автора состоит в том, чтобы предвидеть элементы, которые читатели могут искать, задача программиста базы данных — предвидеть, какие индексы будут полезны.

Следующая команда может использоваться для создания подобного индекса для столбца id:

CREATE INDEX test1_id_index ON test1 (id);

Имя test1_id_index можно выбрать любое, но в идеале оно должно напоминать вам о назначении индекса.

Чтобы удалить индекс, используйте команду DROP INDEX. Индексы могут быть добавлены и удалены из таблиц в любое время.

После создания индекса дальнейшее вмешательство не требуется: система будет обновлять индекс при изменении таблицы и будет использовать индекс в запросах, когда считает, что это будет более эффективно, чем последовательное сканирование таблицы. Но вам, возможно, придется регулярно запускать команду ANALYZE для обновления статистики, чтобы планировщик запросов мог принимать обоснованные решения. См. главу Советы по производительности для получения информации о том, как узнать, используется ли индекс, и когда и почему планировщик может предпочесть не использовать индекс.

Индексы также могут использоваться командами UPDATE и DELETE с условиями фильтрации. Кроме того, индексы могут использоваться при соединении таблиц, поэтому индекс, определенный для столбца, который является частью условия соединения, также может значительно ускорить запросы с соединениями.

Создание индекса для большой таблицы может занять много времени. По умолчанию QHB позволяет выполнять чтение (SELECT) из таблицы параллельно с созданием индекса, но модификации (INSERT, UPDATE, DELETE) блокируются до завершения построения индекса. В нагруженной системе это часто недопустимо. Можно разрешить модификации параллельно с созданием индекса, но следует учитывать несколько моментов — для получения дополнительной информации см. раздел Неблокирующее построение индексов.

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

Типы индексов

QHB предоставляет несколько типов индексов: B-дерево, Hash, GiST, SP-GiST, GIN и BRIN. Каждый тип индекса использует свой алгоритм, который лучше всего подходит для разных типов запросов. По умолчанию команда CREATE INDEX создает B-дерево, потому что оно подходит в наиболее распространенных ситуациях.

B-деревья могут обрабатывать запросы на равенство, неравенство и принадлежность интервалу значений. В частности, планировщик запросов QHB будет рассматривать возможность использования индекса типа B-дерево всякий раз, когда индексированный столбец участвует в условии с одним из этих операторов:

<
<=
=
>=
>

Конструкции, эквивалентные комбинациям этих операторов, такие как BETWEEN и IN, также могут быть реализованы с помощью поиска по B-дереву. Кроме того, B-дерево может быть использовано при запросе условия IS NULL или IS NOT NULL на столбец индекса.

Оптимизатор также может использовать B-дерево для запросов, включающих операторы сопоставления с образцом LIKE и ~, если шаблон является константой и привязан к началу строки; например, col LIKE 'foo%' or col ~ '^foo', но не col LIKE '%bar'. Однако, если ваша база данных использует локаль, отличную от C, вам нужно будет создать индекс со специальным классом операторов для поддержки индексации запросов на сопоставление с образцом; см. раздел Классы операторов и семейства операторов ниже. Также возможно использовать B-дерево для ILIKE и ~*, но только если шаблон начинается с символов, для которых нет верхнего и нижнего регистра, например, цифр.

Индексы типа B-дерево также можно использовать для извлечения данных в отсортированном порядке. Это не всегда быстрее, чем простое сканирование и сортировка, но часто полезно.

Хеш-индексы могут обрабатывать только простые сравнения на равенство. Планировщик запросов рассмотрит возможность использование хеш-индекса, когда индексируемый столбец участвует в сравнении с использованием оператора =. Следующая команда создает хеш-индекс:

CREATE INDEX name ON table USING HASH (column);

Индексы GiST — это не единая категория индексов, а скорее инфраструктура, с помощью которой может быть реализовано множество различных стратегий индексации. Соответственно, конкретные операторы, с которыми может использоваться индекс GiST, варьируются в зависимости от стратегии индексации (класса операторов). Например, стандартный дистрибутив QHB включает классы операторов GiST для нескольких двумерных геометрических типов данных, которые поддерживают индексированные запросы с использованием следующих операторов:

<< &< &> >> <<| &<| |&> |>> @> <@ ~= &&

(Значение этих операторов см. в разделе Геометрические функции и операторы).

Многие другие классы операторов GiST доступны в коллекции contrib или в виде отдельных проектов.

Индексы GiST могут оптимизировать поиск «ближайшего соседа», например, такой запрос

SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;

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

Индексы SP-GiST, так же как и индексы GiST, предлагают инфраструктуру, которая поддерживает различные виды поиска. SP-GiST позволяет реализовать широкий спектр различных несбалансированных дисковых структур данных, таких как дерево квадрантов, k-мерные деревья и префиксные деревья (Tries). Например, стандартный дистрибутив QHB включает классы операторов SP-GiST для точек двумерного пространства, которые позволяют использовать индекс для запросов с использованием следующих операторов:

<< >> ~= <@ <^ >^

(Значение этих операторов см. в разделе Геометрические функции и операторы).

Как и GiST, SP-GiST поддерживает поиск «ближайшего соседа».

Индексы GIN — это «инвертированные индексы», которые подходят для индексации столбцов, значение которых представляет из себя коллекцию элементов, и последующего поиска по отдельным элементам. Инвертированный индекс содержит отдельную запись для каждого элемента и может эффективно обрабатывать запросы, которые проверяют наличие определенных элементов.

Подобно GiST и SP-GiST, GIN может поддерживать множество различных пользовательских стратегий индексирования, и конкретные операторы, с которыми может использоваться индекс GIN, различаются в зависимости от стратегии индексирования. Например, стандартный дистрибутив QHB включает класс операторов GIN для массивов, который поддерживает индексированные запросы с использованием операторов:

<@ @> = &&

Значение этих операторов см. в разделе Функции и операторы для массивов. Классы операторов GIN, включенные в стандартную поставку, а также входящие в коллекцию contrib, перечислены в разделе Встроенные классы операторов GIN.

Индексы BRIN (сокращение от Block Range Indexes, индекс диапазона блоков) хранят сводные данные о значениях, хранящихся в последовательных диапазонах физических блоков таблицы. Как и GiST, SP-GiST и GIN, BRIN может поддерживать множество различных стратегий индексирования, и конкретные операторы, с которыми может использоваться индекс BRIN, различаются в зависимости от стратегии индексирования. Для типов данных, имеющих линейный порядок сортировки, индекс хранит минимальные и максимальные значения в столбце для каждого диапазона блоков. Это позволяет использовать индекс для запросов, использующих следующие операторы < <= = >= >. Для получения дополнительной информации см. главу Индексы BRIN.

Многоколоночные индексы

Индекс может быть определен для более чем одного столбца таблицы. Например, если у вас есть такая таблица:

CREATE TABLE test2 (
  major int,
  minor int,
  name varchar
);

(предположим, что вы так храните каталог /dev в базе данных), и вы часто делаете запросы вида

SELECT name FROM test2 WHERE major = constant AND minor = constant;

то может быть целесообразно создать индекс по столбцам major и minor вместе, например:

CREATE INDEX test2_mm_idx ON test2 (major, minor);

В настоящее время только индексы типа B-tree, GiST, GIN и BRIN поддерживают многоколоночные индексы. Можно указать до 32 столбцов.

Многоколоночный индекс типа B-дерево может использоваться с условиями запроса, которые включают любое подмножество столбцов индекса, но индекс наиболее эффективен, когда заданы ограничения на ведущие (крайние левые) столбцы.

Точное правило такое: должно быть ограничение равенства для нескольких (0+) первых столбцов индекса, плюс, возможно, ограничение неравенства на 1 следующий столбец — такие условия (т.н. "предикат поиска") позволяют сканировать узкий диапазон индекса.

Ограничения на прочие столбцы индекса ("дополнительный фильтр поиска") проверяются при сканировании индекса прямо в нем, экономя обращения к таблице, но эти ограничения не уменьшают диапазон сканирования индекса. Например, если есть индекс по (a, b, c), а условие запроса WHERE a = 5 AND b <= 50 AND c = 100, то индекс будет сканироваться от первой записи a = 5 до последней записи a = 5 AND b <= 50, и для каждой записи этого диапазона будет проверяться дополнительное условие с = 100. Этот индекс в принципе может быть использован и для запросов, которые имеют ограничения на b и/или c без ограничения на a — но в этом случае будет сканироваться весь индекс, и скорее всего планировщик предпочтет последовательное сканирование таблицы такому использованию индекса.

Многоколоночный индекс GiST может использоваться с условиями запроса, которые включают любое подмножество столбцов индекса. Условия для дополнительных столбцов ограничивают записи, возвращаемые индексом, но условие для первого столбца является наиболее важным для определения того, какую часть индекса необходимо сканировать. Индекс GiST будет относительно неэффективным, если его первый столбец имеет мало вариантов различных значений, даже если в дополнительных столбцах много вариантов значений.

Многоколоночный индекс GIN может использоваться с условиями запроса, которые включают любое подмножество столбцов индекса. В отличие от B-дерева или GiST, эффективность поиска по индексу одинакова независимо от того, какие столбцы индекса входят в условие запроса.

Многоколоночный индекс BRIN может использоваться с условиями запроса, которые включают любое подмножество столбцов индекса. Подобно GIN и в отличие от B-дерева или GiST, эффективность поиска по индексу одинакова независимо от того, какие столбцы индекса входят в условие запроса. Единственная причина иметь несколько индексов BRIN в одной таблице вместо одного многоколоночного индекса BRIN — это задать для нескольких индексов разное значение параметра pages_per_range.

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

Многоколоночные индексы следует использовать с осторожностью. В большинстве случаев достаточно одноколоночного индекса, который занимает меньше места и работает быстрее. Индексы с более чем тремя столбцами понадобятся только в экзотических ситуациях. См. также разделы Объединение нескольких индексов и Сканирование только по индексу и покрывающие индексы, где обсуждаются достоинства различных конфигураций индексов.

Индексы и ORDER BY

В дополнение к просто поиску строк, удовлетворяющих запросу, индекс может выдать их в определенном отсортированном порядке. Это позволяет реализовать указание ORDER BY без отдельного шага сортировки. Из всех типов индексов, поддерживаемых в настоящее время QHB, только B-дерево умеет сортированный вывод — другие типы индексов возвращают строки в неопределенном, зависящем от реализации порядке.

Даже если есть индекс, умеющий выдавать результаты в порядке, требуемом в ORDER BY, у планировщика всегда есть другой вариант: получить выборку другим способом, а потом отсортировать. Например, если в выборку попадает большая часть таблицы, будет выгоднее использовать сканирование таблицы с последующей сортировкой (последовательное чтение таблицы быстрее, чем хождение по ссылкам из индекса).

Важным частным случаем является ORDER BY в сочетании с LIMIT n: использование индекса позволит сразу отсечь n строк в порядке ORDER BY, а при использовании сканирования таблицы придётся достать и отсортировать всю выборку, чтобы получить первые n строк.

По умолчанию индексы B-дерева хранят свои записи в порядке возрастания, значения NULL после всех остальных (в случае равенства ссылка на строку в куче (TID) определяет порядок). Это означает, что прямое сканирование индекса по столбцу x приводит к выводу, удовлетворяющему ORDER BY x (точнее, ORDER BY x ASC NULLS LAST). Тот же индекс также можно сканировать в обратном направлении, получая выходные данные, удовлетворяющие ORDER BY x DESC (или, точнее, ORDER BY x DESC NULLS FIRST, именно такой порядок противоположен предыдущему).

Вы можете настроить порядок индекса B-дерево, включив опции ASC/DESC, NULLS FIRST/NULLS LAST при создании индекса, например:

CREATE INDEX test2_info_nulls_low ON test2 (info NULLS FIRST);
CREATE INDEX test3_desc_index ON test3 (id DESC NULLS LAST);

Индекс, созданный как ASC NULLS FIRST может удовлетворять либо ORDER BY x ASC NULLS FIRST либо ORDER BY x DESC NULLS LAST в зависимости от того, в каком направлении он сканируется.

Вы можете спросить, зачем предлагать все четыре варианта, когда два варианта вместе с возможностью обратного сканирования будут охватывать все варианты ORDER BY. В одноколоночных индексах параметры действительно избыточны, но в многоколоночных индексах они могут быть полезны. Рассмотрим индекс из двух столбцов для (x, y) : он подходит для ORDER BY x, y если мы сканируем вперед, или ORDER BY x DESC, y DESC, если мы сканируем назад. Но он не может поддержать порядок ORDER BY x ASC, y DESC. Для такого порядка годится индекс (x ASC, y DESC) или (x DESC, y ASC)

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

Объединение нескольких индексов

Простое сканирование индекса может использоваться для условий на отдельные столбцы индекса, объединённых по AND. Например, при наличии индекса (a, b) условие запроса WHERE a = 5 AND b = 6 может использовать индекс, но запрос, например, WHERE a = 5 OR b = 6 не может напрямую использовать индекс.

К счастью, QHB имеет возможность комбинировать несколько индексов (включая многократное использование одного и того же индекса) для обработки случаев, которые не могут быть реализованы при сканировании одного индекса. Система может реализовать условия AND и OR за нескольких сканирований индекса. Например, запрос типа WHERE x = 42 OR x = 47 OR x = 53 OR x = 99 можно реализовать через сканирование четырех отдельных диапазонов индекса по x, каждое по одному из условий x = ?. Результаты этих сканирований затем объединяются для получения результата. Другой пример: если у нас есть отдельные индексы для x и y, одна из возможных реализаций запроса WHERE x = 5 AND y = 6 состоит в том, чтобы использовать каждый индекс для соответствующего условия, а затем посчитать пересечение двух множеств строк.

Чтобы объединить несколько индексов, система сканирует каждый необходимый индекс и сохраняет множество строк, подходящий под эту часть условия, в памяти в виде битовой карты. Затем битовые карты можно объединять или пересекать в соответствии с запросом. Наконец, фактические строки таблицы посещаются и возвращаются. Строки таблицы посещаются в физическом порядке, потому что так они лежат в битовой карте; это означает, что любой порядок выдачи исходных индексов потерян, и поэтому потребуется отдельный шаг сортировки, если в запросе есть предложение ORDER BY. По этой причине, а также потому, что каждое дополнительное сканирование индекса добавляет дополнительное время, планировщик иногда предпочитает использовать простое сканирование индекса, даже если доступны дополнительные индексы, которые также можно было бы использовать.

Во всех приложениях, кроме самых простых, могут быть полезны различные комбинации индексов, и разработчик базы данных должен найти компромисс, какие индексы иметь. Иногда лучше использовать многоколоночные индексы, а иногда лучше создавать отдельные индексы и полагаться на функцию комбинирования индексов. Например, если ваша рабочая нагрузка включает в себя набор запросов, которые иногда содержат условие только на столбец x, иногда только на столбец y, а иногда на оба столбца, вы можете создать два отдельных индекса для x и y, полагаясь на комбинацию индексов для обработки запросов, которые используйте оба столбца. Вы также можете создать многоколоночный индекс для (x, y). Этот индекс эффективнее, чем комбинация индексов, для запросов, включающих оба столбца, но, как обсуждалось в разделе Многоколоночные индексы, он почти бесполезен для запросов, включающих только y, поэтому он не должен быть единственным индексом. Комбинация многоколоночного индекса и отдельного индекса по y будет неплохим вариантом. Для запросов, включающих только x, можно использовать многоколонный индекс, хотя он будет больше и, следовательно, медленнее, чем индекс только для x. Третий вариант заключается в создании всех трех индексов, но это разумно, только если поиск в таблице происходит гораздо чаще, чем модификации, и все три типа запросов одинаково частые. Если один из типов запросов встречается значительно реже, чем другие, то лучше создать только два индекса, которые лучше всего соответствуют двум более частым запросам.

Уникальные индексы

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

CREATE UNIQUE INDEX name ON table (column [, ...]);

В настоящее время только индексы типа B-дерево могут быть объявлены уникальными.

Когда индекс объявляется уникальным, несколько строк таблицы с одинаковыми индексируемыми значениями не допускаются. Нулевые значения не считаются равными, т.е. уникальный индекс не мешает иметь несколько строк со значением NULL. Уникальный индекс из нескольких столбцов будет отклонять только те случаи, когда все индексируемые столбцы равны в нескольких строках.

QHB автоматически создает уникальный индекс, когда для таблицы определено уникальное ограничение или первичный ключ. Индекс охватывает столбцы, которые составляют первичный ключ или уникальное ограничение (это может быть многоколонный индекс, если необходимо), и он используется при проверке ограничения.

Заметка
Не надо вручную создавать индексы для уникальных столбцов; это создаст копию автоматически созданного индекса.

Индексы по выражениям

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

Например, распространенный способ сравнения без учета регистра состоит в использовании функции lower:

SELECT * FROM test1 WHERE lower(col1) = 'value';

Этот запрос может использовать индекс, если он был определен по функции lower(col1) :

CREATE INDEX test1_lower_col1_idx ON test1 (lower(col1));

Если объявить этот индекс уникальным, то это не позволит вставить в таблицу несколько строк, значение col1 которых отличается только регистром. Т.о. индексы по выражениям позволяют задавать нетривиальные ограничения уникальности. Например, следующий уникальный индекс предотвращает сохранение чисел с одинаковой целой частью в столбце с действительным числами:

CREATE UNIQUE INDEX test1_uniq_int ON tests ((floor(double_col)));

В качестве другого примера, если вы часто делаете запросы вроде

SELECT * FROM people WHERE (first_name || ' ' || last_name) = 'John Smith';

тогда, возможно, стоит создать такой индекс:

CREATE INDEX people_names ON people ((first_name || ' ' || last_name));

Синтаксис команды CREATE INDEX обычно требует записи круглых скобок вокруг выражения, как показано в последнем примере. Скобки могут быть опущены, когда выражение является просто вызовом функции, как в первом примере.

Индексы по выражениям относительно дороги в обслуживании, поскольку производные выражения должны вычисляться для каждой строки после вставки и каждого изменения. Однако выражения индекса не пересчитываются во время поиска по индексу, поскольку результат вычисления уже хранятся в индексе. В обоих приведенных выше примерах система видит запрос как WHERE indexed_column = ’constant’, поэтому скорость поиска эквивалентна любому другому простому запросу индекса. Таким образом, индексы по выражениям полезны, когда скорость поиска важнее скорости вставки и обновления.

Частичные индексы

Частичный индекс — это индекс, построенный на подмножестве таблицы; подмножество определяется условным выражением (называемым предикатом частичного индекса). Индекс содержит записи только для тех строк таблицы, которые удовлетворяют предикату. Частичные индексы являются специализированной функцией, но есть несколько ситуаций, в которых они полезны.

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

Пример. Настройка частичного индекса для исключения частых значений

Предположим, вы храните журналы доступа веб-сервера в базе данных. Большинство обращений происходит из диапазона IP-адресов вашей организации, но некоторые из других источников (например, сотрудники, подключающиеся по телефонной линии). Если вы ищете по IP-адресу в основном для внешних обращений, вам, вероятно, не нужно индексировать диапазон IP-адресов, который соответствует подсети вашей организации.

Предположим, что таблица такая:

CREATE TABLE access_log (
    url varchar,
    client_ip inet,
    ...
);

Чтобы создать частичный индекс, который соответствует нашему примеру, используйте такую команду:

CREATE INDEX access_log_client_ip_ix ON access_log (client_ip)
WHERE NOT (client_ip > inet '192.168.100.0' AND
           client_ip < inet '192.168.100.255');

Типичный запрос, который может использовать этот индекс:

SELECT *
FROM access_log
WHERE url = '/index.html' AND client_ip = inet '212.78.10.32';

Здесь IP-адрес из запроса покрывается частичным индексом. Следующий запрос не может использовать частичный индекс, так как он использует IP-адрес, который исключен из индекса:

SELECT *
FROM access_log
WHERE url = '/index.html' AND client_ip = inet '192.168.100.23';

Заметьте, что этот тип частичного индекса требует, чтобы общие значения были предопределены, поэтому такие частичные индексы лучше всего использовать для данных, распределение которых не меняются. Такие индексы можно время от времени пересоздавать, чтобы приспособиться к новым распределениям данных, но это добавляет усилий на обслуживание.

Другое возможное использование частичного индекса — включение в индекс только тех значений, которые участвуют в типичной рабочей нагрузке (как показано в следующем примере). Это дает преимущества, описанные выше, однако не позволяет использовать индекс при поиске по "нетипичным" значениям. Очевидно, что выбор частичных индексов в таком сценарии потребует большой осторожности и экспериментов.

Пример. Настройка частичного индекса для исключения «неинтересных» значений

Если у вас есть таблица, которая содержит как оплаченные, так и неоплаченные заказы, где неоплаченные заказы занимают небольшую долю в общей таблице, и при этом эти строки являются наиболее востребованными, вы можете повысить производительность, создав индекс только для неоплаченных заказов. Команда создания индекса будет выглядеть так:

CREATE INDEX orders_unbilled_index ON orders (order_nr)
    WHERE billed is not true;

Возможный запрос, использующий этот индекс:

SELECT * FROM orders WHERE billed is not true AND order_nr < 10000;

Однако индекс также может использоваться в запросах, которые вообще не включают order_nr, например:

SELECT * FROM orders WHERE billed is not true AND amount > 5000.00;

Это не так эффективно, как частичный индекс по столбцу amount, поскольку система должна сканировать весь индекс. Тем не менее, если неоплаченных заказов относительно мало, использование этого частичного индекса для того, чтобы найти неоплаченные заказы с любым дополнительным условием может быть выигрышным.

Обратите внимание, что такой запрос не может использовать этот индекс:

SELECT * FROM orders WHERE order_nr = 3501;

Заказ 3501 может быть и оплачен, поэтому нельзя ограничиться поиском в частичном индексе.

Пример 3.2 также иллюстрирует, что индексированный столбец и столбец, используемый в предикате, не обязаны совпадать. QHB поддерживает частичные индексы с произвольными предикатами, при условии, что задействованы только столбцы индексируемой таблицы. Однако имейте в виду, что предикат должен соответствовать условиям, используемым в запросах, которые должны использовать индекс. Чтобы быть точным, частичный индекс может использоваться в запросе, только если система может аналитически распознать, что из условия WHERE всегда следует предикат индекса. QHB не имеет сложного средства проверки теорем, способного распознавать математически эквивалентные выражения, написанные в разных формах. (Мало того, что такое средство чрезвычайно трудно создать, оно, вероятно, будет работать слишком медленно, чтобы использоваться в планировщике запросов.) Система может распознавать простые следствия из неравенства, например, x &lt; 1 подразумевает x &lt; 2 . А в общем случае, лучше бы предиката индекса точно соответствовал части WHERE, иначе индекс не будет признан пригодным для использования. Сопоставление происходит во время планирования запроса, а не во время выполнения. Как следствие, параметризованные запросы могут не работать с частичным индексом. Например, подготовленный запрос с параметром может иметь условие WHERE x &lt; ?, а предикат индекса x &lt; 2 — индекс не будет использоваться для этого запроса, т.к. для некоторых значений параметра это приводило бы к неправильным результатам.

Третье возможное использование для частичных индексов — не для запросов, а для поддержания уникальности. Идея заключается в том, чтобы создать уникальный индекс для подмножества таблицы, как в следующем примере. Это обеспечивает уникальность среди строк, которые удовлетворяют предикату индекса, без ограничения для тех, которые не удовлетворяют.

Пример. Настройка частичного уникального индекса

Предположим, у нас есть таблица с описанием результатов теста. Мы хотим убедиться, что для данной комбинации субъекта и цели хранится только одна «успешная» запись, а записей о «неудачных» испытаниях может храниться много. Вот один из способов сделать это:

CREATE TABLE tests (
    subject text,
    target text,
    success boolean,
    ...
);

CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target)
    WHERE success;

Это особенно эффективный подход, когда мало успешных тестов и много неудачных.

Следующий индекс запрещает создание более одной строки со значением NULL в данном столбце, используя предложение частичного индекса для обработки только значений нулевого столбца и используя выражение для перевода NULL в true:

CREATE UNIQUE INDEX tests_target_one_null ON tests ((target IS NULL)) WHERE target IS NULL;

Наконец, частичный индекс также можно использовать, чтобы запретить планировщику использовать этот индекс для конкретных запросов. Иногда особое распределение данных может привести к тому, что система будет использовать индекс, когда это на самом деле не нужно. В этом случае индекс можно настроить таким образом, чтобы он не был доступен для некорректного запроса. Обычно QHB делает разумный выбор в отношении использования индекса (например, он не использует при низкоселективной выборке, поэтому предыдущие примеры в первую очередь экономят место, занимаемое индексом, а не отваживают планировщик от использования индекса), а крайне неправильный выбор плана является, вероятно, ошибкой в системе.

Имейте в виду, что создание частичного индекса подразумевает, что вы знаете больше, чем планировщик запросов, в частности, вы знаете, когда использование индекса будет выгодным. Формирование этих знаний требует опыта и понимания того, как работают индексы в QHB. В большинстве случаев преимущество частичного индекса над обычным индексом будет минимальным.

Более подробную информацию о частичных индексах можно найти у Stonebraker, Seshadri.

Сканирование только по индексу и покрывающие индексы

Все индексы в QHB являются вторичными, то есть каждый индекс хранится отдельно от основной области данных таблицы (которая в терминологии QHB называется кучей(heap) таблицы). Это означает, что при обычном сканировании индекса каждый поиск строки требует извлечения данных как из индекса, так и из кучи. Кроме того, хотя записи индекса, которые соответствуют заданному индексируемому условию WHERE, обычно близки друг к другу в индексе, строки таблицы, на которые они ссылаются, могут находиться где угодно в куче. И та часть доступа по индексу, которая состоит из обращения к строкам в куче, включает в себя много произвольного доступа, который может быть медленным, особенно на традиционных вращающихся носителях. (Как описано в Разделе Объединение нескольких индексов, сканирования-на-битовых-картах пытаются уменьшить эту стоимость, делая доступ к куче последовательным, но он все равно производится.)

Чтобы решить эту проблему производительности, QHB поддерживает сканирование только по индексу, которое может реализовывать запросы только на основании индекса без какого-либо доступа к куче. Основная идея состоит в том, чтобы возвращать значения непосредственно из записи индекса, а не обращаться к соответствующей записи кучи. Существуют два фундаментальных ограничения на использование этого метода:

Тип индекса должен поддерживать сканирование только по индексу. Индексы B-дерева поддерживают. Индексы GiST и SP-GiST поддерживают сканирование только по индексу для некоторых классов операторов, но не для всех. Остальные типы индексов не поддерживают. Основным требованием является то, что индекс должен физически хранить или иметь возможность восстановить исходное значение данных для каждой записи индекса. В качестве контрпримера, индексы GIN не могут поддерживать сканирование только по индексу, поскольку каждая запись индекса содержит только часть исходного значения столбца.

Запрос должен ссылаться только на столбцы, хранящиеся в индексе. Например, предполагая индекс по столбцам x и y таблицы, которая также имеет столбец z, эти запросы могут использовать сканирование только по индексу:

SELECT x, y FROM tab WHERE x = 'key';
SELECT x FROM tab WHERE x = 'key' AND y < 42;

а эти запросы не могут:

SELECT x, z FROM tab WHERE x = 'key';
SELECT x FROM tab WHERE x = 'key' AND z < 42;

(Для индексов по выражению и частичных индексов все сложнее, см. ниже.)

Если эти два фундаментальных требования выполнены, то все значения данных, требуемые запросом, доступны из индекса, поэтому физически возможно сканирование только по индексу. Но для любого сканирования таблицы в QHB есть дополнительное требование: система должна убедиться, что каждая извлеченная строка «видима» для MVCC-снимка текущего запроса, как описано в главе Параллельный контроль. Информация о видимости не хранится в записях индекса, а только в записях кучи; так что на первый взгляд может показаться, что для любого поиска в любом случае потребуется доступ к куче. И это действительно так, если строка таблицы была недавно изменена. Однако для редко меняющихся данных есть способ обойти эту проблему. QHB отслеживает для каждой страницы в куче таблицы, все ли строки, хранящиеся на этой странице, достаточно стары, чтобы быть видимыми для всех текущих и будущих транзакций. Эта информация сохраняется в виде одного бита в карте видимости таблицы. Сканирование только по индексу после нахождения подходящей записи индекса проверяет бит видимости для соответствующей страницы кучи. Если он установлен, строка определенно видимая, и поэтому данные могут быть возвращены без обращения к куче. Если он не установлен, запись кучи должна быть посещена, чтобы выяснить, видна ли строка, поэтому никакого преимущества в производительности по сравнению со стандартным сканированием индекса не достигается. Даже в успешном случае этот подход требует обращения к карте видимости; но поскольку карта видимости на четыре порядка меньше описываемой ею кучи, для доступа к ней требуется гораздо меньше операций ввода-вывода. В большинстве случаев карта видимости постоянно хранится в памяти.

Короче говоря, хотя сканирование только по индексу возможно с учетом двух фундаментальных требований, оно будет выигрышным, только если для значительной части страниц кучи таблицы установлены биты видимости. Такие таблицы, в которых большая часть строк неизменна, достаточно распространены, чтобы сделать этот тип сканирования очень полезным на практике.

Чтобы эффективно использовать функцию сканирования только по индексу, вы можете создать покрывающий индекс, т.е. индекс, специально включающий лишние столбцы, которые требуются конкретному запросу, который вы часто выполняете. Поскольку запросам обычно требуется получать больше столбцов, чем только те, по которым они осуществляют поиск, QHB позволяет создавать индекс, в котором некоторые столбцы являются просто «дополнительной нагрузкой», а не частью ключа поиска. Это делается путем добавления указания INCLUDE со списком дополнительных столбцов. Допустим, вы часто делаете запрос вида

SELECT z FROM tab WHERE x = 'key';

Традиционный подход к ускорению таких запросов заключается в создании индекса только по x. Тем не менее, индекс, заданный как

CREATE INDEX tab_x_z ON tab(x) INCLUDE (z);

может обрабатывать эти запросы как сканирование только по индексу, потому что z можно получить из индекса, не посещая кучу.

Поскольку столбец z не является частью ключа поиска индекса, он не обязательно должен иметь тип данных, который может обрабатывать индекс; он просто хранится в индексе и не обрабатывается механизмом индекса. Кроме того, если индекс является уникальным, то есть

CREATE UNIQUE INDEX tab_x_z ON tab(x) INCLUDE (z);

условие уникальности применяется только к столбцу x, а не к комбинации x и z. (При описании UNIQUE и PRIMARY KEY тоже можно задать INCLUDE, это альтернативный синтаксис для создания такого индекса.)

Добавлять в индекс неключевые столбцы дополнительной нагрузки следует обдуманно, особенно если эти столбцы широкие. Если кортеж индекса превысит максимально допустимый размер для данного типа индекса, вставка данных завершится неудачно. В любом случае неключевые столбцы дублируют данные из таблицы и увеличивают размер индекса, что потенциально замедляет поиск. И помните, что нет никакого смысла включать столбцы полезной нагрузки в индекс, если таблица изменяется часто, т.к. все равно потребуется обращаться к куче. Если есть обращение к строке в куче, получить значение всех столбцов столбца можно оттуда, и это ничего не стоит. Другие ограничения заключаются в том, что в качестве дополнительных столбцов нельзя включать выражения, и что только B-деревья и GiST в настоящее время поддерживают включенные столбцы.

До того, как в QHB появилась функция INCLUDE, люди иногда создавали покрывающие индексы, записывая столбцы полезной нагрузки как обычные столбцы индекса, то есть

CREATE INDEX tab_x_y ON tab(x, y);

даже если они не собирались использовать y как часть WHERE. Это прекрасно работает, если дополнительные столбцы являются конечными столбцами; делать их ведущими столбцами неразумно по причинам, изложенным в разделе Многоколоночные индексы. Однако этот метод не подходит для уникальных индексов.

Усечение суффиксов всегда удаляет неключевые столбцы с верхних уровней B-Дерева. Как столбцы дополнительной нагрузки, они никогда не используются при сканировании индекса. Процесс усечения в принципе удаляет один или несколько конечных столбцов ключа из не-листьев B-Дерева, когда они не требуются для обхода дерева. Поэтому на практике покрытие индексов даже без предложения INCLUDE часто позволяет избежать хранения столбцов, которые являются дополнительной нагрузкой, на верхних уровнях. Однако явное определение столбцов дополнительной нагрузки в качестве неключевых столбцов надежно сохраняет кортежи на верхних уровнях небольшими.

Теоретически, сканирование только по индексу может использоваться с индексами по выражениям. Например, имея индекс по f(x), где x — столбец таблицы, можно выполнить запрос

SELECT f(x) FROM tab WHERE f(x) < 1;

как сканирование только по индексу; и это очень привлекательно, если f() дорогая для вычисления функция. Однако планировщик QHB в настоящее время не очень разбирается в таких случаях. Он считает, что запрос потенциально может быть выполнен при сканировании только по индексу, только когда из индекса доступны все столбцы, необходимые для запроса. В этом примере x не требуется, кроме как в контексте f(x), но планировщик этого не замечает и приходит к выводу, что сканирование только по индексу невозможно. Если очень надо, то это ограничение можно обойти, добавив x в качестве включенного столбца, например

CREATE INDEX tab_f_x ON tab (f(x)) INCLUDE (x);

Дополнительное предостережение. Если цель состоит в том, чтобы избежать повторного вычисления f(x), то есть ещё одна проблема. Не факт, что планировщик додумается использовать значение, взятое из индекса, для всех вхождений f(x). Например, в запросе

SELECT B.name FROM tab join B on B.id = f(x) WHERE f(x) < 1;

даже если будет использовано сканирование только по индексу, значение f(x) для соединения будет вычислено заново (x возьмут из дополнительной колонки индекса). Этот недостаток может быть исправлен в будущих версиях QHB.

Частичные индексы также интересны при сканировании только по индексам. Рассмотрим частичный индекс из примера выше:

CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target)
    WHERE success;

Можем ли мы выполнить сканирование только по индексу, чтобы удовлетворить следующий запрос?

SELECT target FROM tests WHERE subject = 'some-subject' AND success;

Есть проблема: WHERE содержит success, который не является столбцом индекса. Тем не менее, сканирование только по индексу возможно, потому что плану не нужно перепроверять эту часть WHERE во время выполнения: все записи, найденные в индексе, обязательно имеют success = true поэтому в плане это не нужно явно проверять.

Классы операторов и семейства операторов

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

CREATE INDEX name ON table (column opclass [sort options] [, ...]);

Класс операторов определяет операторы, которые будут использоваться индексом для этого столбца. Например, индекс B-дерева для типа int4 будет использовать класс int4_ops ; этот класс операторов включает функции сравнения для значений типа int4. На практике класс операторов по умолчанию для типа данных столбца обычно достаточен. Основная причина наличия классов операторов заключается в том, что для некоторых типов данных может быть более одного осмысленного поведения индекса. Например, мы могли бы захотеть отсортировать тип данных комплексного числа или по абсолютному значению или по реальной части. Мы могли бы сделать это, определив два класса операторов для типа данных, а затем выбрав подходящий класс при создании индекса. Класс оператора определяет основной порядок сортировки (который затем можно изменить, добавив параметры сортировки COLLATE, ASC/DESC, NULLS FIRST/NULLS LAST).

Есть также несколько встроенных классов операторов, кроме классов по умолчанию:

Классы операторов text_pattern_ops, varchar_pattern_ops и bpchar_pattern_ops поддерживают индексы B-дерева для типов text, varchar и char соответственно. Отличие от классов операторов по умолчанию состоит в том, что значения сравниваются строго символ за символом, а не в соответствии с правилами сортировки, специфичными для локали. Это делает эти классы операторов пригодными для использования в запросах, включающих выражения сопоставления с образцом (регулярные выражения LIKE или POSIX), когда база данных использует локаль, отличную от «C». Например, вы можете проиндексировать столбец varchar следующим образом:

CREATE INDEX test_index ON test_table (col varchar_pattern_ops);

Обратите внимание, что вам придется создать еще и индекс с классом операторов по умолчанию, если вы хотите, чтобы запросы, включающие обычные сравнения <, <=, > или >= использовали индекс. Такие запросы не могут использовать операторы класса xxx_pattern_ops (сравнения на равенство — могут). Можно создать несколько индексов про один столбец, но с разными классами операторов. Если вы используете локаль C, вам не нужен индекс с операторами класса xxx_pattern_ops, т.к. в локали C индекс с операторами по умолчанию можно использовать и для запросов сопоставления с образцом.

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

SELECT am.amname AS index_method,
       opc.opcname AS opclass_name,
       opc.opcintype::regtype AS indexed_type,
       opc.opcdefault AS is_default
    FROM pg_am am, pg_opclass opc
    WHERE opc.opcmethod = am.oid
    ORDER BY index_method, opclass_name;

Класс операторов — это просто подмножество более крупной структуры, называемой семейством операторов. В тех случаях, когда несколько типов данных имеют сходное поведение, часто бывает полезно определить операторы над семейством типов данных и разрешить им работать с индексами. Для этого классы операторов для каждого из типов должны быть сгруппированы в одно и то же семейство операторов. Операторы семейства типов являются членами семейства, но не привязаны к конкретному классу в семействе.

Эта расширенная версия предыдущего запроса показывает семейство операторов, к которому принадлежит каждый класс операторов:

SELECT am.amname AS index_method,
       opc.opcname AS opclass_name,
       opf.opfname AS opfamily_name,
       opc.opcintype::regtype AS indexed_type,
       opc.opcdefault AS is_default
    FROM pg_am am, pg_opclass opc, pg_opfamily opf
    WHERE opc.opcmethod = am.oid AND
          opc.opcfamily = opf.oid
    ORDER BY index_method, opclass_name;

Этот запрос показывает все определенные семейства операторов и все операторы, включенные в каждое семейство:

SELECT am.amname AS index_method,
       opf.opfname AS opfamily_name,
       amop.amopopr::regoperator AS opfamily_operator
    FROM pg_am am, pg_opfamily opf, pg_amop amop
    WHERE opf.opfmethod = am.oid AND
          amop.amopfamily = opf.oid
    ORDER BY index_method, opfamily_name, opfamily_operator;

Индексы и правила сортировки

Индекс может использовать только одно правило сортировки (COLLATION) на столбец индекса. Если требуется поиск по столбцу с разными правилами сортировки, придется создать несколько индексов.

Рассмотрим следующие команды:

CREATE TABLE test1c (
    id integer,
    content varchar COLLATE "x"
);

CREATE INDEX test1c_content_index ON test1c (content);

Индекс унаследует правила сортировки от базового столбца content. Так что запрос вида

SELECT * FROM test1c WHERE content > constant;

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

SELECT * FROM test1c WHERE content > constant COLLATE "y";

Если такие запросы тоже нужны, то можно создать дополнительный индекс, который будет поддерживать правило сортировки "y", например:

CREATE INDEX test1c_content_y_index ON test1c (content COLLATE "y");

Анализ использования индексов

Хотя индексы в QHB не нуждаются в обслуживании или настройке, все же важно проверить, какие индексы действительно используются реальной рабочей нагрузкой запросов. Изучение использования индекса для отдельного запроса выполняется командой EXPLAIN; его применение для этой цели иллюстрируется в разделе Использование EXPLAIN. Также возможно собрать общую статистику об использовании индекса на работающем сервере, как описано в разделе Сборщик статистики.

Сложно сформулировать общую процедуру выбора индексов для создания. Существует ряд типичных случаев, которые были показаны в примерах в предыдущих разделах. Часто требуется много экспериментов. Остаток этого раздела содержит несколько советов на эту тему:

В первую очередь запустите ANALYZE. Эта команда собирает статистику о распределении значений в таблице. Эта информация необходима для оценки количества строк, возвращаемых запросом, которое необходимо планировщику для реалистичной оценки затрат для каждого возможного плана запроса. При отсутствии какой-либо реальной статистики предполагаются некоторые значения по умолчанию, которые почти наверняка будут неточными. Поэтому изучение планов выполнения без запуска ANALYZE является заведомо бесперспективным. См. разделы Обновление статистики планировщика и Процесс «Автовакуум» для получения дополнительной информации.

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

Особенно фатально использовать очень маленькие наборы тестовых данных. Выборка 1000 из 100000 строк может быть кандидатом на использование индекса, но если на тестовых данных вы выбираете 1 из 100 строк, то вы получите совсем другой результат. Все 100 строк, вероятно, помещаются на одной странице, и последовательное чтение всей таблицы будет безусловно быстрее на таком объеме,

Также будьте осторожны при генерации синтетических тестовых данных (это часто неизбежно, когда приложение еще не работает). Однородные значения, полностью случайные значения или значения, вставленные в порядке возрастания, исказят статистику по сравнению с реальными данными.

Когда индексы не используются, для тестирования может быть полезно форсировать их использование. Существуют параметры времени выполнения, которые могут выключать различные типы планов (см. раздел Конфигурация метода планирования). Например, выключение последовательного сканирования (enable_seqscan) и последовательных соединений (enable_nestloop), которые являются наиболее простыми планами, заставит систему использовать другой план. Если система все еще выбирает последовательное сканирование или последовательное соединение, то, вероятно, существует более фундаментальная причина, по которой индекс не используется, например, условие запроса не соответствует индексу. (Какой запрос можно использовать какой тип индекса объясняется в предыдущих разделах.)

Если искусственными ограничениями удалось заставить планировщик использовать индекс, то есть две варианта: либо планировщик прав, и использование индекса плохо подходит, либо оценки затрат планов запросов не отражают реальность. Посмотрите оценки стоимости планов с индексом и без индекса командой EXPLAIN ANALYZE, а также сравните реальное время выполнения.

Если окажется, что оценки затрат ошибочны, опять-таки есть несколько вариантов. Общая стоимость вычисляется умножением оценки количества строк на стоимость операций, производимых с каждой строкой. Стоимость операций можно скорректированы с помощью параметров (описано в разделе Константы стоимости для планировщика). Неточная оценка количества строк(селективности) обычно связана с неточной статистикой. Это можно исправить, настроив параметры сбора статистики (см. ALTER TABLE).

Если вам не удастся скорректировать оценки стоимостей, чтобы они соответствовали реальности, вам, возможно, придется силой заставить планировщик использовать индекс. Вы также можете связаться с разработчиками QHB для изучения проблемы.

Индексы B-деревья

QHB включает в себя реализацию стандартной структуры данных индекса B-дерева (многонаправленного сбалансированного дерева — btree). Любой тип данных, который можно отсортировать в четком линейном порядке, может быть загружен в индекс B-дерево. Единственное ограничение заключается в том, что запись индекса не может превышать приблизительно одной трети страницы (после сжатия TOAST, если применимо).

Поскольку любой класс операторов btree определяет порядок сортировки на своем типе данных, классы операторов btree (точнее, на самом деле семейства операторов) стали использоваться в QHB для общего представления и понимания семантики сортировки. Поэтому они приобрели некоторые функции, которые выходят за рамки того, что было бы необходимо только для поддержки индексов B-деревьев, и их используют части системы, которые довольно далеки от методов доступа по B-дереву.

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

Класс операторов btree должен предоставлять пять операторов сравнения: <, <=, =, >= и >. Можно было бы ожидать, что в эти операторы также должен входить <>, но это не так, потому что практически никогда не имеет смысл использовать <> в предложении WHERE для поиска по индексу. (Для некоторых целей планировщик обрабатывает <> как связанный с классом операторов B-дерева; но он находит данный оператор через отрицание оператора =, а не обращаясь к pg_amop).

Если несколько типов данных имеют почти одинаковую семантику сортировки, их классы операторов можно сгруппировать в семейство операторов. Это выгодно, потому что позволяет планировщику делать выводы о межтиповых сравнениях. Каждый класс операторов в семействе должен содержать однотиповые операторы (и связанные с ними вспомогательные функции) для своего типа входных данных, в то время как межтиповые операторы сравнения и вспомогательные функции «слабо» связаны с семейством. Рекомендуется включать в семейство полный набор межтиповых операторов, таким образом гарантируя, что планировщик сможет вызвать любые условия сравнения, которые выведет из транзитивности.

Существует несколько основных предположений, которым должно удовлетворять семейство операторов для B-деревьев:

Оператор = должен быть отношением эквивалентности, то есть для любых отличных от NULL значений A, B, C определенного типа данных:

A = A истинно (закон рефлексивности)

если A = B, то B = A (закон симметричности)

если A = B и B = C, то A = C (закон транзитивности)

Оператор < должен представлять из себя отношение строгого порядка, то есть для любых отличных от NULL значений A, B, C:

A < A ложно (закон антирефлексивности)

если A < B и B < C, то A < C (закон транзитивности)

Более того, порядок должен быть полным; то есть для любых отличных от NULL значений A, B должно быть верно ровно 1 утверждение из 3:

A < B, либо B < A, либо A = B (закон трихотомии) (Конечно, закон трихотомии обосновывает определение вспомогательной функции сравнения.)

Остальные три оператора определяются через операторы = и < очевидным образом и должны работать согласованно с последними.

Для семейства операторов, поддерживающих несколько типов данных, вышеуказанные законы должны выполняться, когда A, B, C берутся из любых типов данных в семействе. Закон транзитивности обеспечить сложнее всего, поскольку в ситуациях с разными типами это зависит от согласованности поведения двух или трех различных операторов. Для примера, нельзя поместить операторы для float8 и numeric в одно семейство, по крайней мере, не в сегодняшней ситуации, когда для сравнения с float8 значения numeric тоже преобразуются в тип float8. Из-за ограничения точности типа float8 различные значения numeric при приведении к float8 превращаются в одно и то же число, тем самым нарушая закон транзитивности.

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

Должно быть достаточно очевидно, почему индекс B-дерево требует выполнения вышеприведенных законов внутри одного типа данных: без этого не будет упорядочивания ключей. Кроме того, для индексного поиска, использующего ключ сравнения другого типа данных, требуется, чтобы значения двух типов сравнивались разумно. Расширение семейства до трех или более типов данных не является обязательным для самого механизма индекса B-дерево, но планировщик полагается на них в целях оптимизации.

Вспомогательные функции B-деревьев

Как показано в таблице Вспомогательные функции B-деревьев, B-дерево определяет одну обязательную и четыре факультативные вспомогательные функции:

order

Для каждой комбинации типов данных, для которых семейство операторов btree предоставляет операторы сравнения, оно должно предоставить и вспомогательную функцию сравнения, зарегистрированную в pg_amproc как вспомогательная функция номер 1 со свойствами amproclefttype/amprocrighttype, равными левому и правому типу данных сравнения (т. е. тем же типам данных, с которыми зарегистрированы соответствующие операторы в pg_amop). Функция сравнения должна принимать отличные от NULL значения A и B и возвращать значение int32, которое будет < 0, 0 или > 0, когда A < B, A = B или A > B соответственно. Результат NULL не допускается: все значения типа данных должны быть сравнимы.

Если сравниваемые значения имеют сортируемый тип данных, то во вспомогательную функцию сравнения будет передан OID соответствующего правила сортировки, используя стандартный механизм PG_GET_COLLATION(). .

sortsupport

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

.

in_range

Дополнительно семейство операторов btree может предоставлять вспомогательные функции in_range, регистрируемые как вспомогательная функция номер 3. Такие функции не используется при операциях с индексами B-деревьями; они расширяют семантику семейства операторов, чтобы то могло поддерживать оконные предложения, содержащие типы границ рамки RANGE смещение PRECEDING и RANGE смещение FOLLOWING (см. раздел Вызовы оконных функций). По сути, дополнительная информация позволяет добавлять или вычитать значение смещения способом, соответствующим принятому в семействе порядку сортировки.

Функция in_range должна иметь сигнатуру

in_range(значение type1, база type1, смещение type2, вычитание bool, меньше bool)
returns bool

Значение и база должны быть одинакового типа, одного из поддерживаемых семейством операторов (т. е. типом, для которого предоставляется упорядочивание). Однако смещение может быть другого типа, который никаким другим образом данным семейством может и не поддерживаться. Например, встроенное семейство операторов time_ops предоставляет функцию in_range которая имеет параметр смещение типа interval. Семейство может предоставлять функцию in_range для любого поддерживаемого типа и одного или нескольких типов смещения. Каждая вспомогательная функция in_range должна быть зарегистрирована в pg_amproc с amproclefttype, равным type1, и amprocrighttype, равным type2.

Основополагающая семантика функции in_range зависит от двух логических флаговых параметров. Она должна прибавить или вычесть из базы смещение и после этого сравнить результат со значением следующим образом:

- если !***вычитание*** и !***меньше***, возвращается ***значение*** \>=
  (***база + смещение***)

- если !***вычитание*** и ***меньше***, возвращается ***значение*** \<=
  (***база + смещение***)

- если ***вычитание*** и !***меньше***, возвращается ***значение*** \>=
  (***база - смещение***)

- если ***вычитание*** и ***меньше***, возвращается ***значение*** \<=
  (***база - смещение***)

Прежде чем делать это, функция должна проверить знак смещения и, если оно отрицательное, выдавать ошибку ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE (22013) с текстом ошибки «invalid preceding or following size in window function» (неверный предшествующий или последующий размер в оконной функции). (Этого требует стандарт SQL, хотя нестандартные семейства операторов, вероятно, могут проигнорировать это ограничение, т. к., по-видимому, в нем нет особой семантической необходимости.) Эта проверка возложена на in_range, чтобы основному коду не нужно было понимать, что для конкретного типа данных означает «меньше нуля».

Дополнительно ожидается, что функции in_range должны, в частности, избегать возникновения ошибки в случае переполнения при вычислении база + смещение или база - смещение. Правильный результат сравнения можно получить, даже если это значение выходит за границы диапазона типа данных. Обратите внимание, что если тип данных включает такие понятия, как «бесконечность» или «NaN», может понадобиться повышенная осторожность для обеспечения согласованности результатов in_range с обычным порядком сортировки этого семейства операторов.

Результаты функции in_range должны соответствовать порядку сортировки, установленному семейством операторов. Выражаясь точнее, при любых фиксированных значениях смещения и вычитания справедливо следующее:

- Если *in\_range* с ***меньше*** = true возвращает *true* для некоторого
  ***значения1*** и ***базы***, *true* должно возвращаться для каждого
  ***значения2*** \<= ***значению1*** с той же ***базой***.

- Если *in\_range* с ***меньше*** = true возвращает *false* для некоторого
  ***значения1*** и ***базы***, *false* должно возвращаться для каждого
  ***значения2*** \>= ***значению1*** с той же ***базой***.

- Если *in\_range* с ***меньше*** = true возвращает *true* для некоторого
  ***значения*** и ***базы1***, *true* должно возвращаться для каждой
  ***базы2*** \>= ***базе1*** с тем же ***значением***.

- Если *in\_range* с ***меньше*** = true возвращает *false* для некоторого
  ***значения*** и ***базы1***, *false* должно возвращаться для каждой
  ***базы2*** \<= ***базе1*** с тем же ***значением***.

При меньше = false должны выполнятся аналогичные утверждения с противоположными условиями.

Если упорядочиваемый тип (type1) является сортируемым, функции in_range с помощью стандартного механизма PG_GET_COLLATION() будет передан OID соответствующего правила сортировки.

Функции in_range не обязаны обрабатывать NULL в аргументах и обычно помечаются как строгие. .

equalimage

Дополнительно семейство операторов btree может предоставить вспомогательные функции equalimage («равенство подразумевает равенство образов»), регистрируемые как вспомогательная функция номер 4. Эти функции позволяют основному коду определять, безопасно ли применять оптимизацию с дедупликацией в В-дереве. На данный момент функции equalimage вызываются только при построении или перестроении индекса.

Функция equalimage должна иметь сигнатуру

equalimage(opcintype oid) returns bool

Возвращаемое значение является статической информацией о классе операторов и правиле сортировки. Результат true показывает, что функция order для класса операторов будет гарантированно возвращать 0 («аргументы равны»), только когда его аргументы А и В также взаимозаменяемы без потери семантической информации. Если функция equalimage не обнаружена или возвращает false, это означает, что нельзя предполагать выполнение данного условия.

Аргумент opcintype является pg_type.oid типа данных, который индексируется данным классом операторов. Это удобство позволяет повторно использовать в разных классах операторов одну и ту же нижележащую функцию equalimage. Если opcintype относится к сортируемому типу данных, функции equalimage с помощью стандартного механизма PG_GET_COLLATION() будет передан OID соответствующего правила сортировки.

С точки зрения класса операторов результат true означает, что дедупликация безопасна (или безопасна для правила сортировки, чей OID был передан его функции equalimage). Однако основной код будет считать дедупликацию безопасной для индекса, только если каждый индексируемый столбец использует класс операторов, регистрирующий функцию equalimage, и все эти функции при вызове действительно возвращают true.

Равенство образов является условием, почти равнозначным простому битовому равенству. Есть лишь одно небольшое различие: при индексировании типа данных valerna представление двух равных образов на диске может отличаться в битовом отношении вследствие несогласованного применения сжатия TOAST к входным данным. Строго говоря, когда функция equalimage класса операторов возвращает true, безопасно предположить, что функция на С datum_image_eq() всегда будет согласована с функцией order класса операторов (при условии, что обеим функциям передан одинаковый OID правила сортировки).

Основной код совершенно не способен сделать какие-либо выводы относительно статуса класса операторов «равенство подразумевает равенство образов» в семействе операторов для множества типов данных на основе сведений о других классах операторов в том же семействе. Также семейству операторов нет смысла регистрировать межтиповую функцию equalimage, а попытка сделать это приведет к ошибке. Причина этого в том, что статус «равенство подразумевает равенство образов» зависит не только от семантик сортировки/равенства, которые более или менее определены на уровне семейства операторов. В целом, эти семантики, которые реализует один конкретный тип данных, должны рассматриваться по отдельности.

Для классов операторов, включенных в базовый продукт QHB, принято соглашение регистрировать стандартную универсальную функцию equalimage. Большинство классов операторов регистрирует функцию btequalimage(), которая указывает, что дедупликация безопасна без каких-либо условий. Классы операторов для сортируемых типов данных, таких как text, регистрируют функцию btvarstrequalimage(), которая указывает, что дедупликация безопасна с детерминированными правилами сортировки. Для сохранения контроля в сторонних расширениях наилучшим решением будет регистрировать их собственные специальные функции equalimage. .

options

Дополнительно семейство операторов btree может предоставлять вспомогательные функции options («параметры класса операторов»), регистрируемые как вспомогательная функция номер 5. Эти функции определяют набор видимых пользователю параметров, которые управляют поведением класса операторов.

Вспомогательная функция options должна иметь сигнатуру

options(relopts local_relopts *) returns void

Эта функция передает указатель на структуру local_relopts, в которую нужно внести набор параметров класса операторов. К этим параметрам можно обращаться из других вспомогательных функций при помощи макросов PG_HAS_OPCLASS_OPTIONS() и PG_GET_OPCLASS_OPTIONS().

На данный момент ни у одного класса операторов btree нет вспомогательной функции options. В отличие от GiST, SP-GiST, GIN и BRIN, В-дерево не допускает гибкое представление ключей. Так что, вероятно, в актуальном методе доступа по индексу В-дереву у функции options нет практического применения. Тем не менее, эта вспомогательная функция была добавлена в В-дерево в целях единообразия и, возможно, станет полезной при дальнейшем развитии реализации В-дерева в QHB.

Реализация

В этом разделе изложена подробная информация о реализации индекса В-дерева, которая может быть полезна для специалистов.

Структура В-дерева

В QHB индексы В-деревья — это многоуровневые древовидные структуры, где каждый уровень дерева можно использовать в качестве двусвязного списка страниц. Единственная метастраница хранится в фиксированном положении в начале первого файла сегмента индекса. Все остальные страницы делятся на листовые и внутренние. Листовые страницы находятся на самом нижнем уровне дерева. Все остальные уровни состоят из внутренних страниц. Каждая листовая страница содержит кортежи, которые указывают на строки таблицы. Каждая внутренняя страница содержит кортежи, которые указывают на следующий уровень вниз по дереву. Обычно листовые страницы составляют более 99% всех страниц. Как внутренние, так и листовые страницы используют стандартный формат страницы, описанный в разделе Внутренняя структура страницы базы данных.

Новые листовые страницы добавляются в индекс В-дерево, когда существующая листовая страница не может вместить поступающий кортеж. Операция разделения страницы освобождает место для элементов, которые изначально принадлежали переполнившейся странице, перенося какую-то их часть на новую страницу. Разделение страницы также вставляет в родительскую страницу новую ссылку вниз, на новую страницу, что, в свою очередь, может вызвать разделение родительской страницы. Страницы разделяются «каскадно вверх» рекурсивным образом. Когда же в итоге и корневая страница не может вместить новую ссылку вниз, выполняется операция разделения корневой страницы. Это добавляет к структуре дерева новый уровень путем создания новой корневой страницы, которая находится на уровень выше исходной корневой страницы.

Восходящее удаление индексных кортежей

В индексах B-деревьях не учитывается напрямую, что в среде MVCC может быть несколько действующих версий одной логической строки таблицы; для индекса каждый кортеж является независимым объектом, требующим отдельной записи в индексе. Кортежи «отработанных версий» иногда могут накапливаться и отрицательно влиять на время отклика и скорость обработки запросов. Обычно это происходит при нагрузках с преобладанием UPDATE, когда для большинства отдельных операций изменения данных нельзя применить оптимизацию HOT. Изменение значения даже одного столбца, покрываемого одним индексом, во время UPDATE всегда требует наличия нового набора индексных кортежей — по одному для каждого индекса в таблице. В частности, обратите внимание, что сюда входят и те индексы, которые не были «логически изменены» командой UPDATE. Во всех индексах будут нужны последующие физические индексные кортежи, указывающие на последнюю версию строки в таблице. Каждый новый кортеж в каждом индексе, как правило, должен сосуществовать с исходным «измененным» кортежем в течение короткого периода времени (обычно недолго после фиксации транзакции UPDATE).

В индексах B-деревьях постепенно удаляются индексные кортежи отработанных версий посредством выполнения процедуры восходящего удаления индексных кортежей. Каждый проход процедуры удаления запускается, реагируя на ожидаемое «разделение страниц из-за отрабатывания версий». Это касается только тех индексов, которые не были логически изменены операторами UPDATE, где в противном случае на определенных страницах произошло бы концентрированное накопление устаревших версий. Реализация обычно старается избежать разделения страниц, хотя возможно, что определенные эвристические алгоритмы на ее уровне не сумеют идентифицировать и удалить даже один мусорный индексный кортеж (в этом случае проблема с новым входящим кортежем, не помещающимся на листовой странице, решается путем разделения страницы или в результате дедупликации). Наибольшее количество версий, которое нужно преодолеть при каждом сканировании индекса (для каждой отдельной логической строки), является важным отрицательным фактором общей производительности и скорости отклика системы. Процедура восходящего удаления индексных кортежей выбирает на листовой странице предположительно мусорные кортежи на основании качественных характеристик, включающих логические строки и версии. Это диаметрально отличает ее от «нисходящей» уборки индекса, выполняемой рабочими процессами автоочистки, которая запускается при превышении определенных количественных пороговых значений на уровне таблицы (см. подраздел Процесс «Автовакуум»).

Примечание
Не все операции удаления, проводимые в индексах B-деревьях, относятся к операциям восходящего удаления. Существует другая категория таких операций: простое удаление индексных кортежей. Это отложенная операция обслуживания, удаляющая индексные кортежи, которые считаются безопасными для удаления (те, для идентификаторов которых уже установлен бит LP_DEAD). Как и восходящее удаление индексных кортежей, простое удаление происходит в тот момент, когда ожидается разделение страницы, для предотвращения этого разделения.
Простое удаление является спонтанным, в том смысле, что оно может произойти, только когда при недавних сканированиях индекса для обработанных элементов были установлены биты LP_DEAD. Ранее единственной категорией удаления в B-дереве было простое удаление. Главное различие между ним и восходящим удалением состоит в том, что только первое спонтанно обусловлено активностью сканирований индекса, тогда как второе предметно нацелено на отработанные версии, оставляемые командами UPDATE, которые не изменяют логически индексированные столбцы.

Восходящее удаление выполняет основную работу по уборке всех мусорных кортежей из определенных индексов с определенной нагрузкой. Такой эффект ожидается для любого индекса B-дерева, которые в значительной степени затрагивается отрабатыванием версий из-за команд UPDATE, редко или вообще никогда не изменяющих логически столбцы, покрываемые индексом. Среднее и наибольшее количество версий для логической строки может поддерживаться на низком уровне исключительно за счет целенаправленных инкрементных проходов удаления. Вполне возможно, что размер определенных индексов на диске никогда не увеличится ни на одну страницу/блок, несмотря на постоянное отрабатывание версий командами UPDATE. Но даже тогда в итоге понадобится полная «зачистка» индекса операцией VACUUM (обычно выполняемой в рабочем процессе autovacuum) как часть совокупной уборки таблицы и всех ее индексов.

В отличие от VACUUM, восходящее удаление не дает никаких твердых гарантий относительно возраста старейшего мусорного индексного кортежа. Ни в одном индексе не допускается сохранение «плавающих» мусорных индексных кортежей, ставших мертвыми до консервативной точки отсечения, являющейся общей для таблицы и всех ее индексов. Этот фундаментальный инвариант на уровне таблицы обеспечивает безопасную циркуляцию табличных TID. Именно поэтому разные логические строки спустя какое-то время могут повторно использовать один и тот же TID (хотя это невозможно для двух логических строк, время жизни которых укладывается в один и тот же цикл VACUUM).

Дедупликация

Дубликат — это кортеж листовой страницы (кортеж, который указывает на строку таблицы), в котором все ключевые столбцы индекса содержат значения, совпадающие с соответствующими значениями столбца из по крайней мере еще одного кортежа листовой страницы в том же индексе. На практике дублирующиеся кортежи встречаются довольно часто. Индексы В-деревья могут использовать для дубликатов особое экономящее пространство представление при включении факультативного механизма — дедупликации.

Действие механизма дедупликации заключается в периодическом объединении групп дублирующихся кортежей с образованием одного кортежа со списком идентификаторов для каждой группы. В таком представлении ключевое значение (или значения) столбца находится в единственном экземпляре. Затем идет отсортированный массив идентификаторов TID, которые указывают на строки в таблице. Это значительно уменьшает размер хранимых индексов, где каждое значение (или каждая отдельная комбинация значений столбца) появляется, в среднем, несколько раз. Это может значительно сократить время отклика запросов и в целом существенно повысить скорость их обработки. Также могут сильно снизиться издержки на регулярную очистку индекса.

Примечание
Дедупликация в В-дереве также эффективна при работе с «дубликатами», которые содержат значение NULL, несмотря на то, что значения NULL, согласно операторам = из любого класса операторов В-дерева, не считаются равными друг другу. С точки зрения любой части реализации, которая понимает дисковую структуру В-дерева, NULL — это просто еще одно значение из домена значений индекса.

Процесс дедупликации запускается только по необходимости, когда вставляется новый элемент, который не помещается на существующую листовую страницу. Это предотвращает (или, по крайней мере, откладывает) разделение листовой страницы. В отличие от кортежей со списком идентификаторов GIN, в B-дереве этим кортежам не нужно расширяться всякий раз, когда вставляется новый дубликат; они просто являются альтернативным физическим представлением исходного логического содержимого листовой страницы. При смешанной нагрузке типа чтение/запись такая модель ставит во главу угла согласованную производительность. Для большинства клиентских приложений дедупликация должна обеспечить как минимум удовлетворительное увеличение производительности. По умолчанию она включена.

Команды CREATE INDEX и REINDEX используют дедупликацию, чтобы создать кортежи со списком идентификаторов, хотя применяемая ими стратегия слегка отличается. Каждая группа обычных дублирующихся кортежей, обнаруженных во взятых из таблицы отсортированных входных данных, объединяется в кортеж со списком идентификаторов до того, как добавляется на текущую ожидающую листовую страницу. В каждый такой кортеж упаковывается максимально возможное количество идентификаторов TID. Листовые страницы записываются обычным способом, без каких-либо отдельных проходов для исключения дубликатов. Эта стратегия очень подходит командам CREATE INDEX и REINDEX, поскольку они относятся к разовым групповым операциям.

При рабочих нагрузках с преобладанием записи, которые не получают преимуществ от дедупликации из-за малого количества или отсутствия дублированных значений в индексах, ее применение приведет к небольшому постоянному снижению производительности (если только она явно не выключена). Чтобы выключить дедупликацию в отдельных индексах, можно воспользоваться параметром хранения deduplicate_items. При рабочих нагрузках только на чтение производственных издержек не возникает, поскольку кортежи со списком идентификаторов читаются по меньшей мере так же эффективно, как и кортежи в стандартном представлении. Поэтому обычно в таких случаях выключать дедупликацию бесполезно.

Иногда дедупликация может применяться для уникальных индексов (а также уникальных ограничений). Это позволяет листовым страницам временно «поглощать» лишние дубликаты отработанных версий. Дедупликация в уникальных индексах дополняет восходящее удаление индексных кортежей, особенно в случаях, когда длительная транзакция удерживает снимок, блокирующий сбор мусора. Цель этого — выиграть время для восстановления эффективности стратегии восходящего удаления индексных кортежей. Откладывание разделения страниц до естественного завершения одной длительной транзакции позволяет успешно выполнить проход восходящего удаления, который ранее завершился неудачей.

Совет
Для определения того, следует ли дедупликации выполняться в уникальном индексе, применяется специальный эвристический алгоритм. Зачастую он может перейти напрямую к разделению листовой страницы без расходов на лишние циклы холостых проходов дедупликации. Если вас беспокоят издержки на бесполезную дедупликацию, можете выборочно задать значение deduplicate_items = off для отдельных индексов. В уникальных же индексах дедупликацию вполне можно оставить включенной — потери от этого будут невелики.

Из-за ограничений на уровне реализации дедупликацию можно использовать не везде. Безопасность ее применения определяется при выполнении команды CREATE INDEX или REINDEX.

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

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

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

Дедупликация не может применяться с типом jsonb, поскольку внутри класса операторов В-дерева jsonb используется тип numeric.

Дедупликация не может применяться с типами float4 и float8, поскольку у этих типов разное представление для значений -0 и 0, которые при этом все равно считаются равными. Это различие должно быть сохранено.

Есть еще одно дополнительное ограничение на уровне реализации, которое может быть убрано в будущих версиях QHB:

Дедупликация не может применяться с типами-контейнерами (такими как составные или диапазонные типы или массивы).

Есть еще одно дополнительное ограничение на уровне реализации, которое действует независимо от используемого класса оператора или правила сортировки:

Дедупликация не может применяться в индексах с INCLUDE.

Индексы GiST

Введение

Встроенные классы операторов

Расширяемость

Реализация

Примеры

Введение

GiST означает "обобщенное поисковое дерево" (Generalized Search Tree). Это сбалансированный, древовидный метод доступа, который работает как основа для произвольной схемы индексирования. B-деревья, R-деревья и многие другие схемы индексирования могут быть реализованы с помощью GiST.

Одним из преимуществ GiST является то, что он позволяет разрабатывать пользовательские типы данных вместе с соответствующими методами доступа эксперту предметной области без участия специалиста по базам данных.

Встроенные классы операторов

Основной дистрибутив QHB включает классы операторов GiST, показанные в следующей таблице:

Имя | Индексируемый тип данных | Индексируемые операторы | Операторы сортировки
box_ops | box | && &> &< &<| >> << <<| <@ @> @ |&> | >> ~ ~= | <->
circle_ops | circ | && &> &< &<| >> << <<| <@ @> @ |&> | >> ~ ~= | <->
inet_ops | inet, cidr | && >> >>= > >= <> << <<= < <= = |
point_ops | point | >> >^ << <@ <@ <@ <^ ~= | <->
poly_ops | polygon | && &> &< &<| >> << <<| <@ @> @ |&> | >> ~ ~= | <->
range_ops | любой диапазонный тип | && &> &< >> << <@ -|- = @> @> |
tsquery_ops | tsquery | <@ @> |
tsvector_ops | tsvector | @@ |

По историческим причинам, класс операторов inet_ops не является классом по умолчанию для типов inet и cidr. Чтобы использовать его, указывайте имя класса явно в CREATE INDEX, например

CREATE INDEX ON my_table USING GIST (my_inet_column inet_ops);

Расширяемость

Традиционно, внедрение нового метода доступа к индексам означало много сложной работы. Необходимо было разобраться во внутренней работе базы данных: принципах работы менеджера блокировок и журнала упреждающей записи. Интерфейс GiST имеет высокий уровень абстракции, требующий от автора метода доступа только реализации семантики типа данных, к которому происходит обращение. Сам слой GiST заботится о параллелизме, журналировании и поиске в древовидной структуре.

Эту расширяемость не следует путать с расширяемостью других стандартных поисковых деревьев в плане типов данных, которые они могут обрабатывать. Например, QHB поддерживает расширяемые B-деревья и хеш-индексы. Это означает, что в QHB вы можете построить B-дерево или хеш-индекс над любым типом данных, каким захотите. Но B-деревья поддерживают только предикаты диапазона (<, =, >), а хеш-индексы только равенства.

Таким образом, если вы проиндексируете, скажем, коллекцию изображений с помощью B-дерева, вы сможете делать только запросы вида “изображение А равно изображению Б?”, “изображение А меньше изображения Б?” и т.п. В зависимости от того, как вы определяете “равно”, “меньше” и “больше” в этом контексте, это может иметь какой-то смысл. Однако, используя индекс на основе GiST, вы можете поддержать запросы, специфичные для предметной области, например "найти все изображения лошадей” или "найти все засвеченные фотографии".

Все, что требуется для запуска метода доступа GiST, — это реализовать несколько пользовательских методов, которые определяют поведение ключей в дереве. Конечно, эти методы должны быть довольно причудливыми, чтобы поддерживать причудливые запросы, но для всех стандартных запросов (B-деревья, R-деревья и т. д.) они довольно прямолинейны. Короче говоря, GiST сочетает в себе расширяемость с универсальностью, переиспользованием кода и чистым интерфейсом.

Существует пять методов, которые должен предоставить класс оператора индекса для GiST, и четыре, которые являются необязательными. Корректность индекса обеспечивается правильной реализацией методов same, consistent и union, а эффективность (размер и скорость работы) определяется методами penalty и picksplit.

Два из опциональных методов: compress и decompress позволяют индексу хранить во внутренних (т.е. не листовых) вершинах дерева данные другого типа, нежели индексируемый тип. Листья в любом случае будут хранить данные такого же типа, как и индексируемый столбец, а другие узлы могут хранить произвольную структуру (C-style-struct), но все же в рамках общих ограничений QHB на типы данных (про данные переменной длины см. varlena). Если тот тип данных, что будет лежать в промежуточных вершинах дерева, существует на уровне SQL, то можно задать ему свойство STORAGE в команде CREATE OPERATOR CLASS. На самом деле, можно иметь много разных типов содержимого вершин. Есть главный тип, в котором производятся все вычисления при построении и навигации индекса, а при сохранении в вершины индекса вызывается compress, и в compress можно преобразовать в любой бинарный формат, в том числе с потерей информации. Если compress нет, то главный тип и содержимое всех вершин совпадает с индексируемым типом.

Необязательный метод №8 — distance, который нужен, если класс операторов хочет поддержать упорядоченные сканирования (поиск N ближайших соседей). Девятый метод fetch нужен, если класс операторов хочет поддерживать сканирование только по индексу, но при этом хранит альтернативный тип в промежуточных вершинах (использует compress/decompress).

consistent

Для значения p в вершине индекса и поискового значения q, эта функция возвращает, является ли запрос q "согласованным" со значением p. Для листовых записей индекса это эквивалентно проверке индексируемого условия, а для внутренних вершин дерева это определяет, необходимо ли сканировать соответствующее поддерево. Когда функция возвращает истину, она должна также заполнить флаг перепроверки recheck. Это указывает, является ли q безусловно подходящим или только возможно подходящим. Выставьте recheck = false, когда индекс полностью проверил условие предиката, а если recheck = true, то эта строка является только кандидатом на совпадение. В этом случае система будет перепроверять запрос на значении, взятом из строки. Такое соглашение позволяет GiST поддерживать как точные (lossless) так и грубые (lossy) индексы.

SQL-объявление функции должно выглядеть следующим образом:

CREATE OR REPLACE FUNCTION my_consistent(internal, data_type, smallint, oid, internal)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

Примерный шаблон реализации на C:

PG_FUNCTION_INFO_V1(my_consistent);

Datum
my_consistent(PG_FUNCTION_ARGS)
{
    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    data_type  *query = PG_GETARG_DATA_TYPE_P(1);
    StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
    /* Oid subtype = PG_GETARG_OID(3); */
    bool       *recheck = (bool *) PG_GETARG_POINTER(4);
    data_type  *key = DatumGetDataType(entry->key);
    bool        retval;

    /*
    * Рассчитать возвращаемое значение как функцию от стратегии, ключа и запроса.
    *
    * Используйте GIST_LEAF(entry) чтобы понять, в каком месте дерева вас вызвали.
    * Это очень полезно. Например, при реализации оператора =
    *  вы можете во внутренних вершинах проверять, что пересечение не пусто,
    *  а в листьях проверять на точное равенство.
    */

    *recheck = true;        /* ну или false, если проверка была 100%-ная */

    PG_RETURN_BOOL(retval);
}

Здесь, key является элементом в индексе, а query — значение, которое ищут. Параметр StrategyNumber указывает, какой из операторов вашего класса применяется: он соответствует номер одного из оператора из команды CREATE OPERATOR CLASS.

В зависимости от того, какие операторы вы включили в класс, тип данных query может быть разными, в том числе разным для разных операторов. В любом случае, он будет соответствовать типу второго аргумента оператора, а первый аргумент оператора будет главного типа содержимого индекса. (Приведенная выше заготовка кода предполагает, что все запросы будет одинакового типа data_type; если это не так, то сначала надо узнать тип второго параметра оператора, а потом доставать значение из query.) В SQL-объявлении функции consistent рекомендуется указывать тип данных query совпадающим с индексируемым типом данных, даже если реально передаваемый тип данных может быть другим в зависимости от оператора.

union

Этот метод объединяет информацию в дереве: получает на вход содержимое нескольких вершин, и создаёт подходящее содержимое для их общей родительской вершины.

SQL-объявление функции должно выглядеть следующим образом:

CREATE OR REPLACE FUNCTION my_union(internal, internal)
RETURNS storage_type
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

Примерный шаблон реализации на C:

PG_FUNCTION_INFO_V1(my_union);

Datum
my_union(PG_FUNCTION_ARGS)
{
    GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
    GISTENTRY  *ent = entryvec->vector;
    data_type  *out,
            *tmp,
            *old;
    int         numranges,
                i = 0;

    numranges = entryvec->n;
    tmp = DatumGetDataType(ent[0].key);
    out = tmp;

    if (numranges == 1)
    {
        out = data_type_deep_copy(tmp);

        PG_RETURN_DATA_TYPE_P(out);
    }

    for (i = 1; i < numranges; i++)
    {
        old = out;
        tmp = DatumGetDataType(ent[i].key);
        out = my_union_implementation(out, tmp);
    }

    PG_RETURN_DATA_TYPE_P(out);
}

Как вы можете видеть, в этой заготовке мы считаем, что для нашего типа данных union (X, Y, Z) = union(union(X, Y), Z). Но поддержать типы данных, для которых это не так, тоже будет несложно.

Результатом функции объединения должно быть значение главного типа содержимого индекса (как говорилось выше, он может отличаться или не отличаться от индексируемого типа). Функция union должна возвращать указатель на кусок памяти, выделенной с помощью palloc. Даже если результат совпадает с одним из входных аргументов, вы не может просто вернуть на него указатель, вы должны склонировать содержимое.

Как видно из примера, первый внутренний (internal) аргумент функции union — это указатель GistEntryVector. Второй аргумент является указателем на целочисленную переменную, которую можно игнорировать.

compress

Преобразует элемент данных в формат, подходящий для физического хранения на странице индекса. Если метод отсутствует, то элементы данных хранятся в индексе без изменений.

SQL-объявление функции должно выглядеть следующим образом:

CREATE OR REPLACE FUNCTION my_compress(internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

Примерный шаблон реализации на C:

PG_FUNCTION_INFO_V1(my_compress);

Datum
my_compress(PG_FUNCTION_ARGS)
{
    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    GISTENTRY  *retval;

    if (entry->leafkey)
    {
        // заменить entry->key заархивированной версией

        compressed_data_type *compressed_data = palloc(sizeof(compressed_data_type));

        /* здесь заполнить *compressed_data на основании entry->key ... */

        retval = palloc(sizeof(GISTENTRY));
        gistentryinit(*retval, PointerGetDatum(compressed_data),
                    entry->rel, entry->page, entry->offset, FALSE);
    }
    else
    {
        // Как правило, для не листовых вершин ничего не надо делать
        retval = entry;
    }

    PG_RETURN_POINTER(retval);
}

В этом коде нужно заменить compressed_data_type на индексируемый тип, так как содержимое листовых узлов должно быть такого типа.

decompress

Преобразует сохраненное представление данных в главный тип содержимого, которым могут манипулировать другие методы GiST в классе операторов. Если метод декомпрессии отсутствует, предполагается, что другие методы GiST могут работать непосредственно на сохраненном формате данных. Декомпрессия не обязательно противоположна компрессии; в частности, если сжатие с потерями, для декомпрессии невозможно точно восстановить исходные данные. Поведение decompress и fetch также могут различаться т.к. первое возвращает главный тип содержимого индекса (например, тип первого аргумента consistent), а второе — индексируемый тип, и эти типы могут различаться.

SQL-объявление функции должно выглядеть следующим образом:

CREATE OR REPLACE FUNCTION my_decompress(internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

Тривиальная реализация на C:

PG_FUNCTION_INFO_V1(my_decompress);

Datum
my_decompress(PG_FUNCTION_ARGS)
{
    PG_RETURN_POINTER(PG_GETARG_POINTER(0));
}

Приведенный выше пример подходит для случая, когда никакая декомпрессия не требуется. (Но в таком случае лучше, конечно, вообще отказаться от этого метода.)

penalty

Возвращает "штраф" за вставку новой записи в данную ветвь дерева. При вставке элемента его будут пихать по пути с наименьшим штрафом. Значение штрафа должно быть неотрицательным. Если вернуть отрицательное значение, оно будет рассматриваться как ноль.

SQL-объявление функции должно выглядеть следующим образом:

CREATE OR REPLACE FUNCTION my_penalty(internal, internal, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;  -- в некоторых случаях у вас получится нестрогая функция

Шаблон реализации на C:

PG_FUNCTION_INFO_V1(my_penalty);

Datum
my_penalty(PG_FUNCTION_ARGS)
{
    GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
    GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
    float      *penalty = (float *) PG_GETARG_POINTER(2);
    data_type  *orig = DatumGetDataType(origentry->key);
    data_type  *new = DatumGetDataType(newentry->key);

    *penalty = my_penalty_implementation(orig, new);
    PG_RETURN_POINTER(penalty);
}

По историческим причинам, penalty не возвращает результат типа float, а кладет его по указателю, переданному третьим аргументом. А собственно возвращаемое значение функции игнорируется; но принято возвращать указатель на результат как в примере.

Функция penalty имеет решающее значение для хорошей работы индекса. Она используется во время вставки, чтобы определить, в какую ветвь пойти при выборе места для добавления новой записи в дереве. Во время выполнения запроса, чем больше сбалансирован индекс, тем быстрее выполняется поиск.

picksplit

При необходимости разбиения страницы индекса эта функция решает, какие записи на странице должны оставаться на старой странице, а какие должны быть перемещены на новую страницу.

SQL-объявление функции должно выглядеть следующим образом:

CREATE OR REPLACE FUNCTION my_picksplit(internal, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

Шаблон реализации на C:

PG_FUNCTION_INFO_V1(my_picksplit);

Datum
my_picksplit(PG_FUNCTION_ARGS)
{
    GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
    GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
    OffsetNumber maxoff = entryvec->n - 1;
    GISTENTRY  *ent = entryvec->vector;
    int         i,
                nbytes;
    OffsetNumber *left,
            *right;
    data_type  *tmp_union;
    data_type  *unionL;
    data_type  *unionR;
    GISTENTRY **raw_entryvec;

    maxoff = entryvec->n - 1;
    nbytes = (maxoff + 1) * sizeof(OffsetNumber);

    v->spl_left = (OffsetNumber *) palloc(nbytes);
    left = v->spl_left;
    v->spl_nleft = 0;

    v->spl_right = (OffsetNumber *) palloc(nbytes);
    right = v->spl_right;
    v->spl_nright = 0;

    unionL = NULL;
    unionR = NULL;

    /* Initialize the raw entry vector. */
    raw_entryvec = (GISTENTRY **) palloc(entryvec->n * sizeof(void *));
    for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
        raw_entryvec[i] = &(entryvec->vector[i]);

    for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
    {
        int         real_index = raw_entryvec[i] - entryvec->vector;

        tmp_union = DatumGetDataType(entryvec->vector[real_index].key);
        Assert(tmp_union != NULL);

        /*
        * Выбрать, куда положить элемент индекса и соответственно изменить unionL и unionR
        * Добавить его в v->spl_left, либо в v->spl_right, и счетчики тоже изменить соответственно.
        */

        if (my_choice_is_left(unionL, curl, unionR, curr))
        {
            if (unionL == NULL)
                unionL = tmp_union;
            else
                unionL = my_union_implementation(unionL, tmp_union);

            *left = real_index;
            ++left;
            ++(v->spl_nleft);
        }
        else
        {
            // то же самое для направо...
        }
    }

    v->spl_ldatum = DataTypeGetDatum(unionL);
    v->spl_rdatum = DataTypeGetDatum(unionR);
    PG_RETURN_POINTER(v);
}

Обратите внимание, что результатом работы функции picksplit является изменение GIST_SPLITVEC-структуры, пришедшей параметром. Возвращаемое значение функции игнорируется, но принято возвращать указатель на эту структуру как в примере.

Как и penalty, функция picksplit имеет решающее значение для хорошей работы индекса. Придумать, как будут работать penalty и picksplit — самое сложное в дизайне GiST-индекса.

same

Возвращает true, если две записи индекса идентичны, в противном случае false.

SQL-объявление функции должно выглядеть следующим образом:

CREATE OR REPLACE FUNCTION my_same(storage_type, storage_type, internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

Шаблон реализации на C:

PG_FUNCTION_INFO_V1(my_same);

Datum
my_same(PG_FUNCTION_ARGS)
{
    prefix_range *v1 = PG_GETARG_PREFIX_RANGE_P(0);
    prefix_range *v2 = PG_GETARG_PREFIX_RANGE_P(1);
    bool       *result = (bool *) PG_GETARG_POINTER(2);

    *result = my_eq(v1, v2);
    PG_RETURN_POINTER(result);
}

По историческим причинам, функция same не просто возвращает логический результат, а помещает его по указателю, переданному третьим аргументом. Возвращаемое значение функции игнорируется, но принято возвращать этот же указатель как показано в примере.

distance

Для значения p в вершине индекса и поискового значения q, эта функция возвращает "расстояние" от q до p. Для листовой вершины это скорее всего точное расстояние между двумя точками, а для не листовой вершины следует понимать это как расстояние от q до ближайшего элемента из поддерева (возможно, заниженное, но не завышенное). Эта функция должна быть предоставлена, если класс операторов содержит какие-либо операторы упорядочивания. Запрос, использующий оператор упорядочивания, будет реализован путем возврата записей индекса с наименьшими значениями "расстояния", поэтому работа distance должна быть согласованы с семантикой этого оператора.

SQL-объявление функции должно выглядеть следующим образом:

CREATE OR REPLACE FUNCTION my_distance(internal, data_type, smallint, oid, internal)
RETURNS float8
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

Примерный шаблон реализации на C:

PG_FUNCTION_INFO_V1(my_distance);

Datum
my_distance(PG_FUNCTION_ARGS)
{
    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    data_type  *query = PG_GETARG_DATA_TYPE_P(1);
    StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
    /* Oid subtype = PG_GETARG_OID(3); */
    /* bool *recheck = (bool *) PG_GETARG_POINTER(4); */
    data_type  *key = DatumGetDataType(entry->key);
    double      retval;

    /*
    * Вычисление результата как функции от стратегии(оператора), ключа и поискового значения
    */

    PG_RETURN_FLOAT8(retval);
}

Аргументы у distance такие же, как и у consistent.

Допускаются неточности при вычислении расстояния, главное вернуть не больше, чем реальное расстояние. Например, в геометрических приложениях расстояние до группы обычно считают как расстояние до ограничивающего группу параллелепипеда. Для внутренней вершины дерева возвращаемое расстояние не должно быть больше расстояния до любого из дочерних вершин. Если возвращенное расстояние не является точным, функция должна установить *recheck в true. (Это не обязательно для внутренних вершин дерева; для них вычисление всегда предполагается неточным). В этом случае движок вычислит точное расстояние после извлечения строки из кучи и при необходимости пересортирует строки.

Если функция расстояния хотя бы иногда возвращает *recheck = true, то тип (float8/float4) и масштаб значений должны быть такие же как и у исходного оператора упорядочивания, потому что будут сортировать результаты distance с результатами оператора вперемешку. А если всегда *recheck = false, то результаты distance могут быть любыми float8 значениями, несогласованными с оператором, т.к. их будут сравнивать только между собой. (Бесконечность и минус бесконечность зарезервированы для обработки специальных случаев, таких как NULL, поэтому не надо возвращать такие значения).

fetch

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

SQL-объявление функции должно выглядеть следующим образом:

CREATE OR REPLACE FUNCTION my_fetch(internal)
RETURNS internal
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;

Аргумент является указателем на структуру GISTENTRY. На входе, поле key — содержимое листа дерева в сжатом виде (т.е. результат работы compress), NOT NULL. Возвращаемое значение — другая GISTENTRY, чье поле key содержит те же данные в исходном, несжатом виде. Если функция compress класса операторов предоставлена, но ничего не делает для листовых записей, то метод fetch может возвращать аргумент без изменений. Если же функции compress вообще нет, тогда и fetch не нужна.

Шаблон реализации на C:

PG_FUNCTION_INFO_V1(my_fetch);

Datum
my_fetch(PG_FUNCTION_ARGS)
{
    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    input_data_type *in = DatumGetPointer(entry->key);
    fetched_data_type *fetched_data;
    GISTENTRY  *retval;

    retval = palloc(sizeof(GISTENTRY));
    fetched_data = palloc(sizeof(fetched_data_type));

    /*
    * Собственно преобразовать fetched_data в Datum исходного индексируемого типа
    */

    /* fill *retval from fetched_data. */
    gistentryinit(*retval, PointerGetDatum(converted_datum),
                entry->rel, entry->page, entry->offset, FALSE);

    PG_RETURN_POINTER(retval);
}

Если функция compress сохраняет данные в листах с искажением (с потерей информации), то класс оператора не может поддерживать сканирование только по индексу и не должен определять функцию fetch.

Все методы поддержки GiST обычно вызываются в короткоживущих контекстах памяти, то есть CurrentMemoryContext сбрасывается после обработки каждого кортежа. Это позволяет не беспокоиться о вызовах pfree. Однако в некоторых случаях хотелось бы иметь долгоживущий объект для кэширования данных при повторных вызовах. Чтобы сделать это, выделите память в контексте fcinfo->flinfo->fn_mcxt, и положите указатель в fcinfo->flinfo->fn_extra. Время жизни такого объекта — одна индексная операция (одно сканирование, одна вставка или построение индекса). Если вы заменяете fcinfo->flinfo->fn_extra, а там было ненулевое значение, то вы должны его освободить, иначе будут утечки.

Реализация

Построение больших индексов GiST путем простой последовательной вставки всех кортежей бывает медленным, потому что, когда индекс большой и вытесняется из памяти на диск, вставка очередного кортежа происходит в случайное место индекса и вызывает чтение/запись на диск случайных страниц. QHB поддерживает более эффективный, буферизованный метод построения индексов GiST, что может значительно уменьшить количество операций ввода-вывода при обработке неупорядоченного набора данных. Для упорядоченных наборов данных преимущество меньше или отсутствует, т.к. для них последовательная вставка тоже прекрасно работает (в каждый момент времени работа ведется только в нескольких страницах, которые влезают в память).

Однако для построения индекса с буферизацией будет больше вызовов penalty, т.е. больше нагрузка на процессор. Кроме того, требуется дополнительное временное пространство на диске, примерно равное итоговому размеру индекса. Также построение с буферизацией и без может приводить к разным по качеству индексам. Это зависит от реализации penalty, picksplit и операторов. Более качественный (сбалансированный) индекс может получиться как при одном, так и при другом алгоритме построения индекса.

Глобально поведение управляется параметром effective_cache_size: когда размер индекса превышает или явно собирается превысить effective_cache_size, построение GiST-индекса переключается на буферизованный метод. Также буферизацию можно включить/выключить явно для конкретного индекса. Для этого используется опция buffering команды CREATE INDEX. Поведение по умолчанию подходит для большинства случаев, но выключение буферизации может ускорить построение, если вам известно, что входные данные упорядочены.

Примеры

Исходный дистрибутив QHB содержит несколько примеров индексных методов, реализованных на основе GiST. В основной дистрибутив входит полнотекстовый поиск (типы tsvector и tsquery), а также R-Деревья для некоторых встроенных типов геометрических данных.

Следующие модули из contrib также содержат классы операторов GiST:

Модуль | Описание функциональности
btree_gist | Функциональность, эквивалентная B-дереву для некоторых типов данных
cube | Индексация многомерных кубов
hstore | Модуль для хранения пар (ключ, значение)
intarray | RD-дерево для одномерного массива int4
ltree | Индексирование древовидных путей
pg_trgm | Степень сходства текстов в метрике триграмм
seg | Индексирование интервалов действительных чисел

Индексы SP-GiST

Введение

Встроенные классы операторов

Расширяемость

Реализация

Ограничения SP-GiST

SP-GiST без меток узлов

Внутренние вершины "all-the-same"

Введение

SP-GiST — это сокращение от Space-partitioned GiST (обобщённое дерево по разбитому на партиции пространству). SP-GiST представляет собой дерево поиска, ветви которого не пересекаются (в отличие от GiST). В таком виде представимы многие несбалансированные структуры данных, в том числе деревья квадрантов, K-мерные деревья и префиксное деревья (Tries). Общая особенность этих структур заключается в том, что они многократно разбивают пространство поиска на непересекающиеся партиции, которые не обязательно должны быть одинакового размера. Поиск, хорошо соответствующий правилу разбиения, может быть очень быстрым.

Эти популярные структуры данных были первоначально разработаны для использования в оперативной памяти. В основной памяти они обычно проектируются как множество динамически выделенных узлов, связанных указателями. В отличие от B-Дерева у каждой вершины бывает всего по 2-4 дочерних, а высота дерева получается соответственно большая; большая высота — это проход большого количества вершин при поиске. Если хранить это прямо в таком виде на диске, то будет много чтений произвольного доступа. Для хранения на диске деревья должны наоборот сильно ветвиться. Основная задача, решаемая SP-GiST, заключается в размещении вершин поискового дерева на дисковых страницах таким образом, чтобы поиск требовал доступа только к нескольким дисковым страницам, даже если он проходит через много вершин.

Как и GiST, SP-GiST предназначена для разработки пользовательских типов данных с соответствующими методами доступа экспертом в прикладной области типа данных.

Встроенные классы операторов

Основной дистрибутив QHB включает классы операторов SP-GiST, показанные в таблице.

Имя | Индексированный Тип Данных | Индексируемые Операторы | Операторы сортировки
kd_point_ops | point | << <@ <^ >> >^ ~= | <->
quad_point_ops | point | << <@ <^ >> >^ ~= | <->
range_ops | любой диапазонный тип | && &< &> -|- << <@ = >> @> |
box_ops | box | << &< && &> >> ~= @> <@ &<| <<| |>> |&> | <->
poly_ops | polygon | << &< && &> >> ~= @> <@ &<| <<| |>> |&> | <->
text_ops | text | < <= = > >= ~<=~ ~<~ ~>=~ ~>~ ^@ |
inet_ops | inet, cidr | && >> >>= > >= <> << <<= < <= = |

Из двух классов операторов для типа point, quad_point_ops — это класс по умолчанию. kd_point_ops поддерживает те же операторы, но использует другую структуру данных индекса, которая может обеспечить лучшую производительность в некоторых приложениях.

Классы операторов quad_point_ops, kd_point_ops и poly_ops поддерживают оператор упорядочения <->, который позволяет выполнять поиск k ближайших соседей (k-NN search) среди проиндексированных точек или многоугольников.

Расширяемость

SP-GiST предлагает интерфейс с высоким уровнем абстракции, требующий от разработчика реализовать только методы, специфичные для данного типа данных. Ядро SP-GiST обеспечивает эффективное хранение на диске и движок поиска в древовидной структуре, а также решает вопросы конкурентного доступа и логирования.

Листовые вершины SP-GiST-дерева содержат значения того же типа данных, что и индексируемый столбец. Однако они могут хранить значение не целиком, а только суффикс. В этом случае вспомогательные функции класса операторов должны уметь восстанавливать полное значение, собирая значения в родительских вершинах при проходе через них от корня к листу.

Содержимое внутренние вершин более сложное. Каждая внутренняя вершина содержит набор из одного или нескольких узлов (nodes), каждый из которых является ветвью дерева (= областью пространства). Узел содержит либо ссылку на внутреннюю вершину (если ветвь дерева большая), либо короткий список листьев, все из которых располагаются на одной странице индекса (если ветвь маленькая). Каждый узел обычно имеет метку (label) которая описывает его; например, в префиксном дереве метка узла может быть следующей буквой строкового значения. (Класс оператора может опустить метки узлов, если он работает с фиксированным набором узлов для всех внутренних вершин, см. SP-GiST без меток узлов).

Внутренняя вершина опционально может иметь префикс (prefix), описывающее все его члены. В префиксном дереве это может быть общий префикс представленных строк. Значение prefix не обязательно является действительно префиксом, но может быть любыми данными, необходимыми классу операторов; например, в дереве квадрантов в prefix хранится центральная точка, по которой происходит разбиение на квадранты. (А 4 узла соответствуют 4-ем областям пространства после разбиения.). Зачем существуют и префиксы, и метки? Метка нужна для того, чтобы решить, в какую из дочерних вершин пойти, не обращаясь к этим вершинам; метки хранятся в родительской вершине. Префикс наоборот хранится в самой вершине, и позволяет делать некоторые операции к ней, не обращаясь к родителю.

Некоторые алгоритмы работы с деревом требуют знания уровня ("глубины") текущей вершины, поэтому ядро SP-GiST предоставляет классам операторов возможность управлять подсчетом уровней при спуске по дереву. Как говорилось выше, есть поддержка восстановления значений по кусочкам, если это необходимо. И наконец, при спуске по дереву можно передавать дополнительный объект любого типа, он называется traverse value.

Примечание
Ядро SP-GiST берет на себя заботу о NULL-значения. Хотя индексы SP-GiST хранят записи для проиндексированных NULL, это скрыто от кода класса оператора индекса: индексируемые NULL-значения никогда не будут переданы методам класса оператора, равно как и NULL-запросы. (Предполагается, что операторы SP-GiST являются строгими (STRICT), и NULL-значения заведомо не подходят под условия.) По этой причине NULL-значения больше не будут обсуждаться в этом разделе.

Существует пять пользовательских методов, которые должен предоставить класс оператора индекса для SP-GiST, и один необязательный. Все пять обязательных методов принимают 2 аргумента типа internal, это указатели на структуры, в первой расположены все входные значения, а во вторую надо поместить выходные значения. Возвращаемое значение из 4-ех методов void, а метод leaf_consistent возвращает bool. Методы не должны изменять какие-либо поля своих входных структур. Во всех случаях выходная структура инициализируется нулями перед вызовом пользовательского метода. Необязательный шестой метод compress принимает единственный аргумент datum — значение, которое индексируем, и возвращает его же в формате для хранения в листовой вершине.

Пять обязательных пользовательских методов:

config

Возвращает статическую информацию о реализации индекса, включая OID'ы типа данных префикса и типа данных меток узлов.

SQL-объявление функции должно выглядеть следующим образом:

CREATE FUNCTION my_config(internal, internal) RETURNS void ...

Первый аргумент — это указатель на структуру spgConfigIn, содержащую входные данные для функции. Второй аргумент является указателем на структуру spgConfigOut, которую функция должна заполнить результирующими данными.

typedef struct spgConfigIn
{
    Oid         attType;        /* Индексируемый тип данных*/
} spgConfigIn;

typedef struct spgConfigOut
{
    Oid         prefixType;     /* Какой будет тип данных префикса */
    Oid         labelType;      /* Какой будет тип данных метки */
    Oid         leafType;       /* Какой будет тип данных значений в листах */
    bool        canReturnData;  /* Умеет реконструировать значения */
    bool        longValuesOK;   /* Умеет обрабатывать длинные значения */
} spgConfigOut;

Значение attType будет вам интересно, если ваш класс операторов умеет работать с несколькими типами данных.

Для классов операторов, которые не используют префиксы, prefixType следует задать VOIDOID. Аналогично, для классов операторов, которые не используют метки узлов, labelType следует задать VOIDOID. canReturnData должно быть установлено в true, если класс оператора способен восстанавливать исходное значение проиндексированного поля (для сканирования только по индексу). longValuesOK должно быть установлено true, только если attType имеет переменную длину, и класс оператора умеет нарезать длинные значения для размещения в нескольких узлах (см. Ограничения SP-GiST).

leafType обычно используют равный attType (оставить leafType нулевым работает так же, но лучше явно выставьте leafType = attType). Если attType и leafType различаются, должен быть предоставлен необязательный метод compress. Метод compress переводит индексируемые значения из attType в leafType. Примечание: leaf_consistent и inner_consistent получают в качестве аргументов attType, leafType на них не влияет.

choose

Решает, как именно будем вставлять новое значение в ветку дерева.

SQL-объявление функции должно выглядеть следующим образом:

CREATE FUNCTION my_choose(internal, internal) RETURNS void ...

Первый аргумент — это указатель на структуру spgConfigIn, содержащую входные данные для функции. Второй аргумент является указателем на структуру spgConfigOut, которую функция должна заполнить результирующими данными.

typedef struct spgChooseIn
{
    Datum       datum;          /* индексируемое значение */
    Datum       leafDatum;      /* данные для записи в лист (все или суффикс) */
    int         level;          /* текущая глубина в дереве, считая с 0 */

    /* Содержимое текущей вершины */
    bool        allTheSame;     /* вершина помечена all-the-same? */
    bool        hasPrefix;      /* у вершины есть prefix? */
    Datum       prefixDatum;    /* если да, то значение prefix */
    int         nNodes;         /* количество дочерних узлов */
    Datum      *nodeLabels;     /* label'ы дочерних узлов в виде линейного массива */
} spgChooseIn;

typedef enum spgChooseResultType
{
    spgMatchNode = 1,           /* давайте спустимся в один из дочерних узлов */
    spgAddNode,                 /* давайте добавим новый дочерний узел этой вершине */
    spgSplitTuple               /* давайте разобьем текущую вершину (поменяет ее prefix) */
} spgChooseResultType;

typedef struct spgChooseOut
{
    spgChooseResultType resultType;     /* тип действия, 3 варианта, см. выше */
    union
    {
        struct                  /* если тип spgMatchNode */
        {
            int         nodeN;      /* номер узла, в который пойдем (от 0) */
            int         levelAdd;   /* прибавить вот столько к глубине */
            Datum       restDatum;  /* новое значение leafDatum */
        }           matchNode;
        struct                  /* если тип spgAddNode */
        {
            Datum       nodeLabel;  /* label нового узла */
            int         nodeN;      /* позиция вставки (от 0) */
        }           addNode;
        struct                  /* если тип spgSplitTuple */
        {
            /* Информация для создания верхней из 2 вершин после разбиения */
            bool        prefixHasPrefix;    /* вершина будет иметь prefix? */
            Datum       prefixPrefixDatum;  /* если да, то значение prefix */
            int         prefixNNodes;       /* число дочерних узлов */
            Datum      *prefixNodeLabels;   /* их label'ы */
            int         childNodeN;         /* номер дочернего узла, куда класть вторую вершину,
                                            *   образующуюся при разбиении (от 0)
                                            */

            /* Информация для создания нижней из 2 вершин (делается из текущей вершины и получает ее содержимое) */
            bool        postfixHasPrefix;   /* вершина будет иметь prefix? */
            Datum       postfixPrefixDatum; /* если да, то значение prefix */
        }           splitTuple;
    }           result;
} spgChooseOut;

datum — это исходное значение типа attType, которое должно был быть вставлено в индекс. Если есть метод compress, то leafDatum — это значение типа leafType, которое сперва получают вызовом compress, а потом при спуске по дереву методы choose или picksplit меняют его; когда процесс вставки достигает конечной страницы, текущее значение параметра leafDatum это то, что будет сохранено во вновь созданной листовой вершине. Если нет метода compress, то leafDatum не меняется при спуске по дереву и совпадает с datum. level — текущая глубина в дереве (прибавляется не всегда на +1, а вы ее меняете по своему усмотрению), для корня дерева 0. allTheSame имеет значение true, если текущая внутренняя вершина помечена как содержащая несколько эквивалентных узлов (см. Внутренние вершины "all-the-same"). hasPrefix имеет значение true, если текущая внутренняя вершина содержит префикс; и если содержит, то prefixDatum является его значением. nNodes — это число дочерних узлов, содержащихся в вершине, а nodeLabels — это массив значений их меток, или NULL, если нет никаких меток.

Функция choose может решить, что новое значение соответствует одному из существующих дочерних узлов, или что новый дочерний узел должен быть добавлен, или что новое значение несовместимо с префиксом вершины, и поэтому над ней нужно добавить родительскую вершину с менее ограничительным префиксом.

Если новое значение соответствует одному из существующих дочерних узлов, установите resultType равным spgMatchNode. Установите nodeN равным индексу (с нуля) этого узла в массиве узлов. levelAdd — сколько прибавить к глубине, например, если вашим методам глубина не интересна, то оставляйте ноль, и ее не будут считать. Если ваши методы реализуют хранение только суффикса, то в restDatum положите остаток суффикса, а если нет, то положите туда тоже, что и пришло (leafDatum).

Если необходимо добавить новый дочерний узел, установите resultType равным spgAddNode. Положите в nodeLabel метку, которая будет использоваться для нового узла, а в nodeN индекс (от нуля), в который необходимо вставить узел в массив узлов. После добавления узла метод choose будет вызван снова для той же самой вершины, и на этот раз должен привести к результату spgMatchNode.

Если новое значение не соответствует префиксу вершины, установите resultType равным spgSplitTuple. Это действие перемещает все существующие узлы в новую внутреннюю вершину более низкого уровня, а текущую вершину превращает в вершину, имеющую одну нисходящую ссылку, указывающую на новую вершину более низкого уровня. Установите prefixHasPrefix, чтобы указать, должна ли новая верхняя вершина иметь префикс, и если да, то заполните prefixPrefixDatum значением префикса. Это новое значение префикса должно быть менее строгим, чем исходное, достаточно нестрогим, чтобы новое значение подходило. Установите prefixNNodes равным числу узлов, необходимых в новой вершине, и сразу же задайте им всем метки: в prefixNodeLabels поместите указатель на массив, выделенный с помощью palloc, содержащий их метки, или, если метки не требуются, то prefixNodeLabels = NULL. Обратите внимание, что общий размер новой верхней вершины не должен превышать общего размера вершины, которую он замещает; это ограничивает длину нового префикса и новых меток. Задайте childNodeN — номер дочернего узла, соответствующего нижней вершине, образовавшейся при разбиении. Для нижнего узла нужно задать только postfixHasPrefix и postfixPrefixDatum. Сочетание префиксов двух вершин и, возможно, метки узла должно иметь такое же семантическое значение, что и префикс вершины до её разбиения, потому что нет возможности пойти в дочерние узлы и там что-то поправить. После того, как узел был разделен, то функция choose будет вызвана снова для верхней из двух вершин. Этот вызов скорее всего вернет результат spgAddNode: раз старая вершина не годилась для вставки, то после разбиения вы захотите создать ей сестринскую.

Итого, вы можете строить дерево, либо добавляя детей в текущую вершину, либо вставляя вершину над текущей, и добавлять детей уже к ней. Перебалансировку сделать нельзя.

picksplit

Решает, как создать новую внутреннюю вершину над набором листовых вершин. Вызывается, когда узел, содержащий листовые вершины, переполнился, и надо превратить его во внутреннюю вершину, содержащую несколько узлов с тем же контентом.

SQL-объявление функции должно выглядеть следующим образом:

CREATE FUNCTION my_picksplit(internal, internal) RETURNS void ...

Первый аргумент — это указатель на структуру spgConfigIn, содержащую входные данные для функции. Второй аргумент является указателем на структуру spgConfigOut, которую функция должна заполнить результирующими данными.

typedef struct spgPickSplitIn
{
    int         nTuples;        /* кол-во листовых вершин */
    Datum      *datums;         /* их значения (линейный массив длины nTuples) */
    int         level;          /* текущая глубина (считая от 0) */
} spgPickSplitIn;

typedef struct spgPickSplitOut
{
    bool        hasPrefix;      /* нужен ли prefix? */
    Datum       prefixDatum;    /* если да, то значение prefix'а */

    int         nNodes;         /* кол-во дочерних узлов */
    Datum      *nodeLabels;     /* их label'ы (или NULL, если не надо меток) */

    int        *mapTuplesToNodes;   /* раскладка листьев по дочерним узлам */
    Datum      *leafTupleDatums;    /* новые значения листовых узлов */
} spgPickSplitOut;

nTuples — количество листовых вершин, которые сейчас раскладываем. datums — это массив их значений типа leafType level это текущий уровень (глубина), одинаковый у всех листовых вершин, и у новой внутренней вершины, предположительно, будет такой же.

Установите hasPrefix, чтобы указать, должна ли новая внутренняя вершина иметь префикс, и если да, то заполните prefixDatum значением префикса. Поместите в nNodes количество дочерних узлов у новой вершины, а в nodeLabels массив их меток или NULL, если метки узлов не требуются. Задайте, в какой узел поместить каждую из листовых вершин — mapTuplesToNodes, длина равна nTuples, каждый элемент — номер узла (от 0). Заполните leafTupleDatums значениями, хранимыми в листах (если вы не храните только суффиксы для компактности, это будет тоже самое, что и входные данные datums, но все равно надо сделать копию). Обратите внимание, что picksplit должна выделить память под nodeLabels, mapTuplesToNodes и leafTupleDatums с помощью palloc.

Если во входных аргументах больше одного листа, то ожидается, что picksplit их как-то отклассифицирует и разделит на несколько узлов (picksplit вызывают с этой целью). Если picksplit поместит все предложенные листы в один узел, то ядро SP-GiST делает вывод, что они условно одинаковые, разделит их на узлы случайным образом (делить всё-таки надо из-за ограничения на количество дочерних листов). Эти несколько узлов все получат одинаковую метку (ту, которую вернула picksplit) и флаг allTheSame чтобы показать, что это произошло. Функции choose и inner_consistent должны проявлять надлежащую осторожность с такими внутренними вершинами. Дополнительную информацию смотрите в Внутренние вершины "all-the-same".

Функция picksplit может быть запущена для 1 листа только в том случае, если функция config установила longValuesOK = true, и встретилось значение длиннее, чем влезает на страницу. В этом случае смысл операции состоит в том, чтобы побить значение на префикс (который сохранят во внутренней вершине) и суффикс, который сохранят в листе или, если он всё ещё слишком длинный, снова вызовут picksplit. Дополнительную информацию смотрите в Ограничения SP-GiST.

inner_consistent

Возвращает набор ветвей, в которые следует сходить при поиске.

SQL-объявление функции должно выглядеть следующим образом:

CREATE FUNCTION my_inner_consistent(internal, internal) RETURNS void ...

Первый аргумент — это указатель на структуру spgConfigIn, содержащую входные данные для функции. Второй аргумент является указателем на структуру spgConfigOut, которую функция должна заполнить результирующими данными.

typedef struct spgInnerConsistentIn
{
    /* Свойства исходного поискового запроса */
    ScanKey     scankeys;       /* массив значений, которые ищем, и предикатов (операторов) */
    ScanKey     orderbys;       /* массив операторов упорядочивания и значений для них */
    int         nkeys;          /* длина массива scankeys */
    int         norderbys;      /* длина массива orderbys */

    /* Состояние алгоритма обхода дерева */
    Datum       reconstructedValue;     /* кумулятивный префикс родителей */
    void       *traversalValue; /* накопительный объект класса операторов */
    MemoryContext traversalMemoryContext;   /* контекст памяти, на котором аллоцировать traversalValue */
    int         level;          /* текущая глубина (от 0) */
    bool        returnData;     /* нужно ли восстанавливать оригинальное значение? */

    /* Свойства текущей внутренней вершины */
    bool        allTheSame;     /* она помечена all-the-same? */
    bool        hasPrefix;      /* у нее есть prefix? */
    Datum       prefixDatum;    /* если да, то какой */
    int         nNodes;         /* количество дочерних узлов */
    Datum      *nodeLabels;     /* метки дочерних узлов (NULL, если нет меток) */
} spgInnerConsistentIn;

typedef struct spgInnerConsistentOut
{
    int         nNodes;         /* в сколько узлов (веток) надо сходить */
    int        *nodeNumbers;    /* массив с номерами узлов длины nNodes */
    int        *levelAdds;      /* на сколько увеличить глубину при входе в узел (массив длины nNodes) */
    Datum      *reconstructedValues;    /* кумулятивный префикс при входе в узел (массив длины nNodes)*/
    void      **traversalValues;        /* накопительные объекты (массив длины nNodes) */
    double    **distances;      /* массив из nNodes массивов из norderbys расстояний до выбранных узлов */
} spgInnerConsistentOut;

Массив scankeys длины nkeys описывает условия поиска в индексе. Эти условия объединяются по И(AND) — только строки, удовлетворяющие всем из них считаются подходящими. (Обратите внимание, что возможен вариант nkeys = 0, и это означает, что условия нет, и нужны просто все записи индекса). Обычно inner_consistent волнуют только поля sk_strategy и sk_argument каждой записи массива scankeys, которые соответственно дают оператор и значение сравнения. В частности, нет необходимости проверять sk_flags на предмет, является ли значение сравнения NULL, потому что основной код SP-GiST отфильтрует такие условия. Массив orderbys длины norderbys описывает операторы упорядочивания (если таковые имеются) аналогичным образом.

reconstructedValue — аккумулятор префикса, накапливаемый при проходе от корня (из префиксов вершин и, возможно, меток узлов). При заходе в корень это 0, а дальше зависит от выходных значений inner_consistent. Если ваш класс операторов не использует восстановление значений по префиксам внутренних вершин, то используйте 0 на всех уровнях. reconstructedValue всегда типа leafType.

traversalValue — указатель на любой ваш объект, который вы вернули из inner_consistent при заходе в родителя; для корня traversalValue == NULL. traversalMemoryContext — это контекст памяти, в котором надо аллоцировать элементы traversalValues (см. ниже). level — текущая глубина. returnData имеет значение true, если в рамках запроса требуются восстановленные данные (если config вернула !canReturnData, то их не затребуют). allTheSame — свойство текущей вершины, в этом случае все узлы имеют одинаковую метку (если таковая имеется), и поэтому либо все, либо ни один из них не соответствует запросу (см. Внутренние вершины "all-the-same" ). hasPrefix + prefixDatum — префикс текущей вершины. nNodes — это число дочерних узлов, а nodeLabels — их метки.

В результате работы nNodes устанавливается в число дочерних узлов (ветвей дерева), которые надо посетить, потому что там могут быть результаты, подходящем под запрос. Часто это всего один узел, но потенциально может быть много, и это усложняет интерфейс: все выходные результаты являются массивами из nNodes элементов, индекс в которых — это номер узла среди рекомендуемых. nodeNumbers — номера рекомендуемых узлов среди дочерних узлов вершины. levelAdds — приращение глубины при спуске в данный узел; обычно это +1 для всех узлов, но вы можете решить, что какие-то узлы "особо заглубленные", например, на основании метки; если никакие методы вашего класса операторов не интересуется глубиной, то можете оставить указатель levelAdds нулевым. Если ваш класс операторов использует восстановление значений по префиксам внутренних вершин, то вам нужно заполнить reconstructedValues для передачи в дочерние вершины. Если происходит поиск ближайших соседей, заполните distances расстояниями до узлов, которые рекомендуете посетить (внешний массив по номеру узла, внутренний по номеру метрики из orderbys), если нет, то оставьте указатель distances нулевым; узлы с меньшим расстоянием будут посещены в первую очередь.

Если вам нужно передать какую-то дополнительную информацию в методы вашего класса операторов, заполните traversalValues, иначе оставьте его нулевым. Учтите, что массивы nodeNumbers, levelAdds, distances, reconstructedValues, и traversalValues надо выделять palloc-ом в текущем контексте, а элементы traversalValues надо выделять в контексте аллокации traversalMemoryContext, при этом каждый элемент должен быть отдельной единицей аллокации (нельзя использовать 1 единицу аллокации и положить в массив traversalValues указатель на ее середину).

leaf_consistent

Возвращает true, если листовая вершина удовлетворяет запросу.

SQL-объявление функции должно выглядеть следующим образом:

CREATE FUNCTION my_leaf_consistent(internal, internal) RETURNS bool ...

Первый аргумент — это указатель на структуру spgConfigIn, содержащую входные данные для функции. Второй аргумент является указателем на структуру spgConfigOut, которую функция должна заполнить результирующими данными.

typedef struct spgLeafConsistentIn
{
    /* Свойства исходного поискового запроса */
    ScanKey     scankeys;       /* массив значений, которые ищем, и предикатов (операторов) */
    ScanKey     orderbys;       /* массив операторов упорядочивания и значений для них */
    int         nkeys;          /* длина массива scankeys */
    int         norderbys;      /* длина массива orderbys */

    /* Состояние алгоритма обхода дерева */
    Datum       reconstructedValue;     /* кумулятивный префикс родителей */
    void       *traversalValue; /* накопительный объект класса операторов */
    int         level;          /* текущая глубина (от 0) */
    bool        returnData;     /* нужно ли возвращать оригинальное значение столбца? */

    /* Свойства текущей листовой вершины */
    Datum       leafDatum;      /* данные, хранящиеся в листе */
} spgLeafConsistentIn;

typedef struct spgLeafConsistentOut
{
    Datum       leafValue;        /* оригинальное значение столбца, если надо */
    bool        recheck;          /* true, если надо перепроверить условие по значению из строки кучи */
    bool        recheckDistances; /* true, если расстояние надо уточнить по значению из строки кучи */
    double     *distances;        /* массив из norderbys расстояний до данного листа  */
} spgLeafConsistentOut;

Массив scankeys длины nkeys описывает условия поиска в индексе. Эти условия объединяются по И(AND) — только строки, удовлетворяющие всем из них считаются подходящими. (Обратите внимание, что возможен вариант nkeys = 0, и это означает, что любой лист подходит). Обычно leaf_consistent волнуют только поля sk_strategy и sk_argument каждой записи массива scankeys, которые соответственно дают оператор и значение сравнения. В частности, нет необходимости проверять sk_flags на предмет, является ли значение сравнения NULL, потому что основной код SP-GiST отфильтрует такие условия. Массив orderbys длины norderbys описывает операторы упорядочивания (если таковые имеются) аналогичным образом.

reconstructedValue — аккумулятор префикса, накопленный при проходе от корня. Может быть NULL, если префикс не собирали; имеет тип leafType. traversalValue указатель на любой ваш объект, который вы вернули из inner_consistent при заходе в родителя. level — глубина листа (если вы ее считали). returnData имеет значение true, если нужно восстановить исходные данные столбца (если config вернула !canReturnData, то их не затребуют). leafDatum — это значение в листовой вершине типа leafType.

Функция должна возвращать true, если лист соответствует запросу, иначе false. Если результат true, и returnData true, тогда надо заполнить leafValue значением столбца (типа attType). Это может быть в точности leafDatum или комбинация из reconstructedValue и leafDatum. Также, recheck может быть установлено в true, если соответствие является не стопроцентным, и надо перепроверить условие на значении, взятом из строки кучи. Если выполняется упорядоченный поиск, заполните массив distances для массива значений расстояний в соответствии с массивом orderbys. В противном случае оставьте его NULL. Если хотя бы одно из возвращенных расстояний не является точным, установите recheckDistances в true; в этом случае исполнитель вычислит точные расстояния после извлечения строки из кучи и при необходимости переупорядочит строки.

Необязательный пользовательский метод:

compress

Преобразует индексируемое значение (типа attType) в формат для физического хранения в листовой вершине индекса (типа leafType). Выходное значение не должно быть помещенным в TOAST.

SQL-объявление функции может выглядеть следующим образом:

CREATE FUNCTION my_compress(internal) RETURNS internal ...

Все вспомогательные методы SP-GiST обычно вызываются в контексте кратковременной памяти; то есть, CurrentMemoryContext сбрасывается после обработки каждой вершины, поэтому можно не освобождать память. Метод config является исключением: он должен стараться избегать утечки памяти. Но обычно методу config незачем выделять память.

Если к типу индексируемого столбца применимы правила сортировки, то действующее для индекса правило сортировки будет передано всем вспомогательным методам через стандартный механизм PG_GET_COLLATION().

Реализация

В этом разделе рассматриваются детали реализации и другие хитрости, которые могут быть полезны для разработчиков классов операторов SP-GiST.

Ограничения SP-GiST

Ограничения вытекают из того, что любая листовая или внутренняя вершина должна помещаться на одной странице индекса (по умолчанию 8кБ). При индексировании столбцов переменной длины это может вызывать ошибку, когда встретится конкретное особо длинное значение. Ваш класс операторов может поддержать хранение длинных значений, если устроит что-то вроде префиксного дерева: исходное значение столбца делится между всеми вершинами на пути от корня до листа, при этом каждый отдельный кусок достаточно короткий, чтобы влезть на страницу; для этого можно искусственно создать цепочку из вершин с единственным потомком. Если ваш класс операторов готов всё это делать, то верните longValuesOK из config.

Для промежуточных вершин это ограничение означает, что не может быть слишком много дочерних узлов, и у них не могут быть слишком длинные префиксы. Далее, если узел внутренней вершины указывает на набор листов, эти листы должны находиться на одной странице (такое проектное решение принято для экономии места и более кучного размещения ветви в дисковой странице). Если набор дочерних узлов становится слишком большим, то добавляется новая внутренняя вершина, а листы разбиваются на несколько узлов этой вершины. На этом шаге вызывается picksplit для набора листов, которая должна их разбить, иначе ядро SP-GiST прибегает к чрезвычайным мерам, описанным в Внутренние вершины "all-the-same".

SP-GiST без меток узлов

Некоторые древовидные алгоритмы используют фиксированный набор узлов для каждой внутренней вершины; например, в дереве квадрантов всегда есть ровно четыре узла, соответствующие четырем квадрантам вокруг центральной точки внутренней вершины. В таком случае код обычно работает с узлами по номеру, и нет необходимости в явных метках узлов. Чтобы подавить метки узлов (и тем самым сэкономить место), функция picksplit может возвращать значение NULL для массива nodeLabels, и аналогично функция choose может возвращать значение NULL для массива prefixNodeLabels во время действия spgSplitTuple. Это, в свою очередь, приведет к тому, что nodeLabels станет NULL во время последующих вызовов choose и inner_consistent. Теоретически можно иметь часть внутренних вершин с метками узлов, а часть без.

Если у внутренней вершины нет меток узлов, то метод choose не должен возвращать spgAddNode, поскольку предполагается, что набор узлов в таких случаях является фиксированным.

Внутренние вершины "all-the-same"

Ядро SP-GiST может переопределять результаты вызова функции picksplit, если picksplit не удается разделить предоставленные листовые значения по крайней мере на две категории узлов. Когда это происходит, создается новая внутренняя вершина с несколькими узлами, каждый из которых имеет ту же метку, что picksplit дал одному узлу, который он вернул, а конечные значения делятся случайным образом между этими эквивалентными узлами. Во внутренней вершине устанавливается флаг allTheSame, чтобы предупредить функции choose и inner_consistent о том, что разбиение внутренней вершины на узлы не такое, как они ожидают.

При работе choose с allTheSame-вершиной результат выбора spgMatchNode интерпретируется как "вставить в любой из узлов", значение nodeN игнорируется. Выбор spgAddNode вообще не допустим и вызовет ошибку (т.к. получилось бы, что часть узлов "all-the-same", а новый узел особенный).

При работе inner_consistent с allTheSame-вершиной она должна выбирать все узлы или не одного (т.к. формально они все одинаковые). Для этого может потребоваться или не потребоваться какой-либо специальный код.

Индексы GIN

Введение

GIN расшифровывается как «Generalized Inverted Index» (Обобщённый инвертированный индекс). GIN предназначается для случаев, когда индексируемые значения являются составными, а запросы, на обработку которых рассчитан индекс, ищут по наличию элементов в этих составных объектах. Например, такими объектами могут быть документы, а запросы могут выполнять поиск документов, содержащих определённые слова.

В этом разделе мы используем термин объект или элемент, говоря о составном значении, которое индексируется, и термин ключ, говоря о включённом в него элементе. GIN всегда хранит и ищет ключи, а не объекты как таковые.

Индекс GIN хранит пары (ключ => набор-строк), где набор-строк содержит идентификаторы строк, в которых есть данный ключ. Один и тот же идентификатор строки может фигурировать в нескольких списках, так как объект может содержать больше одного ключа. Значение каждого ключа хранится только один раз, так что индекс GIN очень компактен в случаях, когда один ключ встречается много раз.

GIN является обобщённым в том смысле, что ядро GIN не знает о конкретных операциях, которые он ускоряет. Конкретная семантика определяется вспомогательными методами (стратегиями), которые управляют тем, как извлекаются ключи из индексируемых объектов и условий запросов, и как установить, действительно ли удовлетворяет запросу строка, содержащая некоторые значения ключей. Как и GiST, GIN позволяет разрабатывать дополнительные типы данных с соответствующими методами доступа экспертам в предметной области типа данных.

Встроенные классы операторов

Основной дистрибутив QHB включает классы операторов GIN, показанные в следующей таблице. Некоторые из расширений, перечисленные в примерах, предоставляют дополнительные классы операторов GIN.

Имя | Индексированный Тип Данных | Индексируемые Операторы
array_ops | anyarray | && = @<
jsonb_ops | jsonb | ? ?& ? @> @? @@
jsonb_path_ops | jsonb | @> @? @@
tsvector_ops | tsvector | @@ @@@

Из двух классов операторов для типа jsonb, jsonb_ops это значение по умолчанию. jsonb_path_ops поддерживает меньшее количество операторов, но обеспечивает лучшую производительность для этих операторов. Дополнительную информацию смотрите в разделе Индексация jsonb.

Расширяемость

Интерфейс GIN имеет высокий уровень абстракции, требующий от автора метода доступа только реализации семантики типа данных, с которым происходит работа. Слой GIN сам заботится о параллелизме, журналировании и поиске в древовидной структуре.

Все, что требуется для работы метода доступа GIN — это реализовать несколько пользовательских методов, которые определяют поведение ключей в дереве и отношения между ключами, индексируемыми элементами и индексируемыми запросами. Короче говоря, GIN сочетает расширяемость с универсальностью, повторным использованием кода и чистым интерфейсом.

Два метода, которые обязан предоставить класс операторов для GIN:

extractValue

Прототип метода на C:

Datum *extractValue(Datum itemValue, int32 *nkeys, bool **nullFlags)

Возвращает массив ключей (выделенный с помощью palloc) индексируемого объекта. Количество возвращаемых ключей должно быть сохранено в *nkeys. Если какой-то из ключей может быть NULL, то необходимо также заполнить nullFlags (это массив типа bool длиной *nkeys, true означает, что этот ключ NULL; аллоцировать тоже palloc'ом). Если все ключи не NULL, то можно оставить *nullFlags нулевым. Возвращаемое значение тоже может быть нулевым, если объект вообще не содержит ключей.

extractQuery

Прототип метода на C:

Datum *extractQuery(Datum query, int32 *nkeys, StrategyNumber n, bool **pmatch,
                    Pointer **extra_data, bool **nullFlags, int32 *searchMode)

Возвращает массив ключей (выделенный с помощью palloc) для запроса. query — это правый аргумент индексируемого оператора, где левый аргумент — это индексируемый столбец. Аргумент n задаёт номер стратегии оператора в классе операторов (см. раздел Стратегии методов индексов). Разные операторы могут иметь разный тип правой части, и, соответственно, query может быть разного типа — и даже при одинаковом типе запрос может интерпретироваться по-разному для разных операторов. Для различения и нужен параметр n.

Количество возвращаемых ключей должно быть записано в *nkeys. Если какой-то из ключей может быть NULL, то необходимо также заполнить nullFlags (это массив типа bool длиной *nkeys, true означает, что этот ключ NULL; аллоцировать тоже palloc'ом). Если все ключи не NULL, то можно оставить *nullFlags нулевым. Возвращаемое значение тоже может быть нулевым, если объект вообще не содержит ключей.

Выходной аргумент searchMode позволяет функции extractQuery выбрать режим, в котором должен выполняться поиск. Если *searchMode установить в GIN_SEARCH_MODE_DEFAULT (это значение уже установлено перед вызовом), подходящими кандидатами считаются объекты, которые соответствуют хотя бы одному из возвращённых ключей. Если *searchMode установить в GIN_SEARCH_MODE_INCLUDE_EMPTY, то в дополнение к объектам с минимум одним совпадением ключа, подходящими кандидатами будут считаться и объекты, вообще не содержащие ключей. (Этот режим полезен для реализации, например, оператора является-подмножеством). Если *searchMode установить в GIN_SEARCH_MODE_ALL, подходящими кандидатами считаются все отличные от NULL объекты в индексе, независимо от того, встречаются ли в них возвращаемые ключи. (Этот режим намного медленнее других, так как он по сути требует сканирования всего индекса, но он может быть необходим для корректной обработки крайних случаев. Оператор, который часто выбирает этот режим, скорее всего не подходит для реализации в классе операторов GIN).

Выходной аргумент pmatch используется, когда поддерживаются частичные ключи в запросах. Выделите массив из *nkeys элементов типа bool и задайте значение true для тех ключей, для которых выделенный ключ является частичным. Если *pmatch нулевой (а он нулевой перед вызовом), GIN полагает, что все ключи точные, а не частичные.

Выходной аргумент extra_data используется, чтобы привязать к каждому ключу дополнительную информацию, которая будет передана в методы consistent и comparePartial. Поместите в *extra_data массив из из *nkeys указателей на объекты производного типа. Перед вызовом указатель *extra_data нулевой, поэтому просто игнорируйте его, если не надо никакой дополнительной информации. Если массив *extra_data задан, то он передаётся в метод consistent целиком, а в comparePartial передаётся один соответствующий элемент.

Класс операторов должен также предоставить функцию для проверки, соответствует ли индексированный объект запросу. Поддерживаются две её вариации: булевская consistent и triConsistent с трехзначным результатом. Если предоставлена только consistent, то некоторые оптимизации, построенные на отбраковывании объектов до выборки всех ключей, выключаются. Если предоставлена только triConsistent, то она будет использоваться и вместо consistent, т.к. является более общей. Однако, если вычисление булевской вариации дешевле, то имеет смысл реализовать обе.

consistent

Прототип метода на C:

bool consistent(bool check[], StrategyNumber n, Datum query, int32 nkeys,
                Pointer extra_data[], bool *recheck, Datum queryKeys[], bool nullFlags[])

Возвращает true, если индексированный элемент удовлетворяет оператору запроса с номером стратегии n (или "возможно удовлетворяет", если дополнительно ставится флаг перепроверки). Проиндексированное значение в этом методе не доступно (GIN их нигде не хранит), есть только информация, какие из ключей запроса встретились в данном кортеже индекса. В метод передаётся всё, что вернула extractQuery при разборе запроса: массив ключей queryKeys, какие из них NULL — nullFlags, дополнительную информацию extra_data, но главное — какие из этих ключей присутствуют в текущем кортеже индекса: check. Все эти массивы имеют длину nkeys, если ненулевые. На всякий случай передаётся и оригинальный запрос query.

Если extractQuery выдала NULL-ключ, и в кортеже тоже есть NULL-ключ, то считается, что ключ найден (что является нетипичным при обработке NULL-значений, и похоже на семантику IS NOT DISTINCT FROM). Если с точки зрения вашего класса операторов это особенная ситуация, то, встретив check[i] == true, вы можете проверить nullFlags[i], чтобы понять, было ли это попадание в NULL-ключ. В случае положительного результата флаг *recheck следует оставить false, если вы уверены, что строка подходит, или выставить true, чтобы оператор перепроверили на данных, взятых из строки таблицы из кучи.

triConsistent

Прототип метода на C:

GinTernaryValue triConsistent(GinTernaryValue check[], StrategyNumber n, Datum query,
                            int32 nkeys, Pointer extra_data[], Datum queryKeys[],
                            bool nullFlags[])

Метод похож на consistent, но результат и входной аргумент check имеют тип GinTernaryValue, который состоит из 3-х вариантов: GIN_TRUE, GIN_FALSE и GIN_MAYBE. GIN_MAYBE означает "может быть подходит". Вы должны руководствоваться общими правилами тернарной логики: возвращать GIN_TRUE, только если строка подходит под запрос в предположении "худшего случая" для ключей, которые GIN_MAYBE, т.е. предполагая, что они все не подходят. И наоборот, возвращать GIN_FALSE, только если строка не подходит, даже в "лучшем случае" для GIN_MAYBE-ключей.

Выходного параметра recheck нет, вместо этого результат GIN_MAYBE трактуется как "надо перепроверить", а GIN_TRUE как "точно подходит".

Далее, ядру GIN нужно уметь сортировать ключи. Вы можете предоставить функцию сортировки compare. Если вы этого не сделаете, то GIN будет использовать сортировку по умолчанию для соответствующего типа данных.

compare

Прототип метода на C:

int compare(Datum a, Datum b)

Сравнивать два ключа (а не исходных индексируемых значения) и вернуть целое число < 0/0/> 0, когда первый ключ меньше/равен/больше второго. NULL-ключи никогда не передаются в эту функцию.

И ещё один необязательный метод:

comparePartial

Прототип метода на C:

int comparePartial(Datum partial_key, Datum key, StrategyNumber n, Pointer extra_data)

Осуществляет проверку соответствия ключа индекса частичному ключу запроса. Результат 0 означает, что ключ подходит; меньше нуля — не подходит, но следует продолжить сканировать ключи; больше нуля — следует прекратить сканирование набора ключей, т.к. все остальные точно не подходят. NULL-ключи никогда не передаются в эту функцию.

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

Для поддержки нечётких запросов класс операторов должен предоставлять comparePartial, а extractQuery должен выставлять флаг pmatch для частичных ключей. Дополнительную информацию смотрите в разделе Алгоритм нечёткого поиска.

Фактические типы переменных типа Datum, указанных выше, варьируются для разных классов операторов. Входное значение extractValue всегда имеет тип индексируемого столбца, а значения всех ключей имеют тип STORAGE класса операторов. Параметр query, передаваемый в extractQuery, consistent, triConsistent, будет такого типа, как правый аргумент поискового оператора; это не обязательно такой же тип, как у индексируемого столбца, главное, чтобы из него можно было извлечь ключи для поиска. Однако в SQL-объявлениях функций extractQuery, consistent, triConsistent рекомендуется указывать тип query совпадающим с типом столбца, если для разных операторов разный тип правой части.

Реализация

Внутри индекс GIN содержит B-дерево, построенное по ключам, где каждый ключ является элементом одного или нескольких проиндексированных объектов (например, элемент массива), и где каждая листовая вершина содержит либо массив("posting list") указателей на строки в куче (если этот массив достаточно маленький, чтобы поместиться в вершине индекса), либо указатель на B-дерево указателей кучи ("posting tree", “дерево рассылки”). На рис. 1 показаны эти компоненты индекса GIN.

В индекс может быть явно включён ключ со значением NULL. Кроме того, ключ NULL считается содержащимся во всех строках, в которых индексируемый столбец имеет значение NULL, либо если ExtractValue не выделила из значения ни одного ключа. Это сделано, чтобы можно было искать пустые значения.

Многоколоночные индексы GIN реализуются путем построения единого B-дерева, значения которого — пары (номер столбца, ключ), причём тип ключа может быть разным для разных столбцов.

Рисунок 1. Внутренняя структура GIN

Быстрое обновление GIN

Обновление индекса GIN, как правило, происходит медленно, и это неотъемлемое свойство инвертированных индексов: вставка или обновление одной строки таблицы может вызвать множество вставок в индекс (по одной для каждого ключа, извлеченного из индексируемого элемента). Ядро GIN способно отложить большую часть этой работы, вставляя новые кортежи во временный, несортированный список ожидающих записей. Когда происходит VACUUM или VACUUM ANALYZE, или когда явно вызывается функция gin_clean_pending_list, или когда длина списка ожидания превышает значение параметра gin_pending_list_limit, все кортежи из списка ожидания помещаются в основное дерево, используя метод групповой вставки, такой же как и при первичном построении индекса. Это значительно увеличивает скорость обновления индекса GIN, даже считая дополнительные накладные расходы на вакуум. Кроме того, перестроение индекса можно вынести в отдельный фоновый процесс, чтобы избежать подвисания клиента во время вставки.

Главным недостатком этого подхода является замедление поиска. Кроме обычного поиска в индексе требуется также сканировать список ожидания, поэтому большой список ожидания значительно замедлит поиск. Еще один недостаток заключается в том, что, хотя амортизированное время модификации снижается, некоторое конкретное обновление может вызывать переполнение списка ожидания и подвиснуть надолго. Обе проблемы можно свести к минимуму, правильно настроив автовакуум, или вовремя запуская gin_clean_pending_list из другого служебного процесса.

Если максимальное время модификации важнее, чем среднее, использование отложенных записей можно выключить, изменив параметр fastupdate индекса GIN. Дополнительные сведения см. в разделе Параметры хранения индекса.

Алгоритм нечёткого поиска

GIN может поддерживать запросы "нечёткого поиска", в которых вместо конкретного ключа указывается диапазон ключей для поиска. Диапазон всегда согласованный с порядком сортировки ключей (определяемым методом compare, либо сортировкой по умолчанию для типа ключа), и, желательно, чтобы он не был слишком широким. Как бы ни звучало указание нечёткого поиска в исходном запросе, extractQuery должна установить флаг pmatch, а в значение ключа поместить нижнюю границу диапазона ключей. Одновременно это должно быть поддержано методом comparePartial, который должен определить верхнюю границу диапазона поиска (например, её можно передать через extra_data). Дерево ключей сканируется начиная с нижней границы, и пока comparePartial не вернёт > 0; все ключи, отобранные comparePartial, считаются подходящими.

Пример для поиска ключа по префиксу очень понятный: нижняя граница диапазона ключей совпадает с префиксом, метод comparePartial получает partial_key, равный этому префиксу, и его реализация тривиальна. В общем случае нижняя и верхняя граница, и условие поиска — разные вещи, и надо как-то их все передать в comparePartial. Например, поиск слов, получающихся из 'спорт' перестановкой двух соседних букв, может потребовать сканирования диапазона ['опрст'; 'тсрпо'] или ['о'; 'у').

Советы и рекомендации по применению GIN

Создание или вставка

Если вы собираетесь вставить много данных, рассмотрите вариант удаления индекса и построения его с нуля. Этот совет верен для большинства индексов, но вставка в GIN-индекс может быть особенно медленной. См. также раздел Быстрое обновление GIN.

maintenance_work_mem

Время построения GIN-индекса сильно зависит от параметра maintenance_work_mem; рассмотрите вариант увеличения этого параметра в сессии, где выполняется создание индекса.

gin_pending_list_limit

Как уже было сказано в разделе Быстрое обновление GIN, параметр gin_pending_list_limit является основным средством настройки производительности GIN-индекса (вторым по значимости средством является частота запуска автовакуума). Параметр gin_pending_list_limit можно задать отдельно для каждого индекса, изменив его параметры хранения. Общее правило, что для редко меняющихся таблиц gin_pending_list_limit должен быть меньше.

gin_fuzzy_search_limit

Основной целью разработки GIN-индексов был полнотекстовый поиск. Полнотекстовый поиск часто возвращает огромное количество строк, особенно если искать частые слова. Прочитать такое количество строк с диска и отсортировать (отранжировать) их в памяти обычно неприемлемо в нагруженной системе. Чтобы облегчить контроль таких запросов, GIN имеет настраиваемый верхний предел на количество возвращаемых строк: параметр gin_fuzzy_search_limit. По умолчанию он установлен в 0 (то есть без ограничений). Если задано ненулевое ограничение, то все результаты поиска после заданного количества молча отбрасываются. Ограничение на количество результатов является "мягким" в том смысле, что фактическое число возвращаемых результатов может несколько отличаться от указанного предела в зависимости от запроса и качества генератора случайных чисел системы.

По опыту, значения в тысячах (например, 5000 — 20000) работают хорошо.

Ограничения

GIN предполагает, что индексируемые операторы являются строгими(STRICT). Это означает, что если значение столбца NULL, то extractValue не вызывается (но создаётся запись индекса, что эта строка содержит NULL-ключ); если поисковое значение NULL, то extractQuery не вызывается и результат поиска считается пустым. Однако запрос может содержать в себе что-то, что класс операторов истолкует как поиск NULL-ключа, например, пустую строку.

Примеры

Основной дистрибутив QHB включает классы операторов GIN, ранее перечисленные в таблице в разделе Встроенные классы операторов. Следующие расширения из contrib также содержат классы операторов GIN:

btree_gin

Функциональность B-дерева для некоторых типов данных

hstore

Расширение для хранения пар (ключ, значение)

intarray

Расширенная поддержка для int[ ]

pg_trgm

Сходство текста, основанное на сопоставлении триграмм

Индексы BRIN

Введение

BRIN расшифровывается как «индекс диапазона блоков» (Block Range Index). BRIN предназначен для обработки очень больших таблиц, в которых определенные столбцы имеют некую естественную корреляцию с их физическим расположением в таблице. Диапазон блоков — это группа страниц, которые физически соседствуют в таблице; для каждого диапазона блоков в индексе сохраняется некоторая сводная информация. Например, таблица, хранящая заказы на поставку, может иметь столбец даты добавления каждого заказа, и в большинстве случаев записи для более ранних заказов в таблице будут размещены выше; в таблице со столбцом почтового индекса могут храниться все коды городов, сгруппированные естественным образом.

Индексы BRIN отвечают на запросы посредством обычного сканирования по битовой карте, возвращая все кортежи всех страниц в каждом диапазоне, если сводная информация, сохраненная в индексе, согласуется с условиями запроса. Исполнитель запроса отвечает за перепроверку этих кортежей и сброс тех, что не соответствуют условиям запроса — другими словами, эти индексы крайне неточные. Но поскольку индекс BRIN очень мал, сканирование индекса добавляет мало накладных расходов по сравнению с последовательным сканированием, при этом помогая избежать сканирования больших областей таблицы, которые определенно не содержат подходящих кортежей.

Конкретные данные, которые будет хранить индекс BRIN, а также конкретные запросы, которые этот индекс сможет удовлетворить, зависят от класса операторов, выбранного для каждого столбца индекса. Например, типы данных с линейным порядком сортировки могут иметь классы операторов, которые хранят минимальное и максимальное значение в каждом диапазоне блоков; геометрические типы могут хранить ограничивающую рамку для всех объектов в диапазоне блоков.

Размер диапазона блоков определяется во время создания индекса параметром хранения pages_per_range. Количество записей индекса будет равно размеру отношения в страницах, деленному на выбранное значение pages_per_range. Чем меньше число, тем больше размер индекса (из-за необходимости хранить больше записей), но в то же время может повыситься точность сохраняемой сводной информации, а во время сканирования можно будет пропустить больше блоков данных.

Обслуживание индекса

Во время создания индекса сканируются все существующие страницы кучи, и для каждого диапазона, включая возможно неполный диапазон в конце, создается сводный кортеж индекса. По мере того, как данными наполняются новые страницы, в диапазонах страниц, для которых уже есть сводная информация, эта информация обновляется данными из новых кортежей. Когда создается новая страница, не попадающая в последний диапазон, для которого есть сводка, новый диапазон не получает автоматически кортеж со сводкой; эти кортежи остаются неучтенными, пока позже не запустится расчет сводных данных, создавая начальные сводки. Этот процесс можно запустить вручную с помощью функции brin_summarize_range(regclass, bigint) или brin_summarize_new_values(regclass) либо автоматически, когда таблицу будет обрабатывать VACUUM, или посредством автоматического создания сводки, выполняемого при автоочистке, по мере добавления записей. (Последний триггер по умолчанию выключен и может быть включен параметром autosummarize.) И наоборот, можно удалить сводку для диапазона при помощи функции brin_desummarize_range(regclass, bigint), что может быть полезно, когда кортеж индекса перестает быть хорошим представлением данных, так как они изменились.

Когда включен режим автопересчета сводки, при каждом заполнении диапазона страниц механизма автоочистки посылается запрос на выполнение целевого пересчета для этого диапазона, который будет произведен в конце следующего прохода рабочего процесса в той же базе данных. Если очередь запросов переполнена, запрос в нее не записывается, а в журнал сервера передается следующее сообщение:

LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was not recorded

Когда это происходит, сводка для такого диапазона будет рассчитана обычным образом при выполнении следующей регулярной очистки таблицы.

Встроенные классы операторов

Основной дистрибутив QHB включает классы операторов BRIN, показанные в Таблице 1.

Классы операторов minmax хранят минимальное и максимальное значения, встречающиеся в индексированном столбце в пределах определенного диапазона. Классы операторов inclusion хранят значение, в котором содержатся значения индексированного столбца в пределах определенного диапазона. Классы операторов bloom строят фильтр Блума для всех значений в определенном диапазоне. Классы операторов minmax-multi хранят несколько минимальных и максимальных значения, представляющих значения, встречающиеся в индексированном столбце в пределах определенного диапазона.

Таблица 1. Встроенные классы операторов BRIN

Имя

Индексируемые операторы

bit_minmax_ops
= (bit,bit)

< (bit,bit)

<= (bit,bit)

> (bit,bit)

>= (bit,bit)

box_inclusion_ops
@> (box,point)

<< (box,box)

&< (box,box)

&> (box,box)

>> (box,box)

<@ (box,box)

@> (box,box)

~= (box,box)

~= (box,box)

<<| (box,box)

&<| (box,box)

|&> (box,box)

|>> (box,box)

bpchar_bloom_ops
= (character,character)

bpchar_minmax_ops
= (character,character)

< (character,character)

<= (character,character)

> (character,character)

>= (character,character)

bytea_bloom_ops
= (bytea,bytea)

bytea_minmax_ops
= (bytea,bytea)

< (bytea,bytea)

<= (bytea,bytea)

> (bytea,bytea)

>= (bytea,bytea)

char_bloom_ops
= ("char","char")

char_minmax_ops
= ("char","char")

< ("char","char")

<= ("char","char")

> ("char","char")

>= ("char","char")

date_bloom_ops
= (date,date)

date_minmax_ops
= (date,date)

< (date,date)

<= (date,date)

> (date,date)

>= (date,date)

date_minmax_multi_ops
= (date,date)

< (date,date)

<= (date,date)

> (date,date)

>= (date,date)

float4_bloom_ops
= (float4,float4)

float4_minmax_ops
= (float4,float4)

< (float4,float4)

<= (float4,float4)

> (float4,float4)

>= (float4,float4)

float4_minmax_multi_ops
= (float4,float4)

< (float4,float4)

<= (float4,float4)

> (float4,float4)

>= (float4,float4)

float8_bloom_ops
= (float8,float8)

float8_minmax_ops
= (float8,float8)

< (float8,float8)

<= (float8,float8)

> (float8,float8)

>= (float8,float8)

float8_minmax_multi_ops
= (float8,float8)

< (float8,float8)

<= (float8,float8)

> (float8,float8)

>= (float8,float8)

inet_inclusion_ops
<< (inet,inet)

<<= (inet,inet)

>> (inet,inet)

>>= (inet,inet)

= (inet,inet)

&& (inet,inet)

inet_bloom_ops
= (inet,inet)

inet_minmax_ops
= (inet,inet)

< (inet,inet)

<= (inet,inet)

> (inet,inet)

>= (inet,inet)

inet_minmax_multi_ops
= (inet,inet)

< (inet,inet)

<= (inet,inet)

> (inet,inet)

>= (inet,inet)

int2_bloom_ops
= (int2,int2)

int2_minmax_ops
= (int2,int2)

< (int2,int2)

<= (int2,int2)

> (int2,int2)

>= (int2,int2)

int2_minmax_multi_ops
= (int2,int2)

< (int2,int2)

<= (int2,int2)

> (int2,int2)

>= (int2,int2)

int4_bloom_ops
= (int4,int4)

int4_minmax_ops
= (int4,int4)

< (int4,int4)

<= (int4,int4)

> (int4,int4)

>= (int4,int4)

int4_minmax_multi_ops
= (int4,int4)

< (int4,int4)

<= (int4,int4)

> (int4,int4)

>= (int4,int4)

int8_bloom_ops
= (bigint,bigint)

int8_minmax_ops
= (bigint,bigint)

< (bigint,bigint)

<= (bigint,bigint)

> (bigint,bigint)

>= (bigint,bigint)

int8_minmax_multi_ops
= (bigint,bigint)

< (bigint,bigint)

<= (bigint,bigint)

> (bigint,bigint)

>= (bigint,bigint)

interval_bloom_ops
= (interval,interval)

interval_minmax_ops
= (interval,interval)

< (interval,interval)

<= (interval,interval)

> (interval,interval)

>= (interval,interval)

interval_minmax_multi_ops
= (interval,interval)

< (interval,interval)

<= (interval,interval)

> (interval,interval)

>= (interval,interval)

macaddr_bloom_ops
= (macaddr,macaddr)

macaddr_minmax_ops
= (macaddr,macaddr)

< (macaddr,macaddr)

<= (macaddr,macaddr)

> (macaddr,macaddr)

>= (macaddr,macaddr)

macaddr_minmax_multi_ops
= (macaddr,macaddr)

< (macaddr,macaddr)

<= (macaddr,macaddr)

> (macaddr,macaddr)

>= (macaddr,macaddr)

macaddr8_bloom_ops
= (macaddr8,macaddr8)

macaddr8_minmax_ops
= (macaddr8,macaddr8)

< (macaddr8,macaddr8)

<= (macaddr8,macaddr8)

> (macaddr8,macaddr8)

>= (macaddr8,macaddr8)

macaddr8_minmax_multi_ops
= (macaddr8,macaddr8)

< (macaddr8,macaddr8)

<= (macaddr8,macaddr8)

> (macaddr8,macaddr8)

>= (macaddr8,macaddr8)

name_bloom_ops
= (name,name)

name_minmax_ops
= (name,name)

< (name,name)

<= (name,name)

> (name,name)

>= (name,name)

numeric_bloom_ops
= (numeric,numeric)

numeric_minmax_ops
= (numeric,numeric)

< (numeric,numeric)

<= (numeric,numeric)

> (numeric,numeric)

>= (numeric,numeric)

numeric_minmax_multi_ops
= (numeric,numeric)

< (numeric,numeric)

<= (numeric,numeric)

> (numeric,numeric)

>= (numeric,numeric)

oid_bloom_ops
= (oid,oid)

oid_minmax_ops
= (oid,oid)

< (oid,oid)

<= (oid,oid)

> (oid,oid)

>= (oid,oid)

oid_minmax_multi_ops
= (oid,oid)

< (oid,oid)

<= (oid,oid)

> (oid,oid)

>= (oid,oid)

pg_lsn_bloom_ops
= (pg_lsn,pg_lsn)

pg_lsn_minmax_ops
= (pg_lsn,pg_lsn)

< (pg_lsn,pg_lsn)

<= (pg_lsn,pg_lsn)

> (pg_lsn,pg_lsn)

>= (pg_lsn,pg_lsn)

pg_lsn_minmax_multi_ops
= (pg_lsn,pg_lsn)

< (pg_lsn,pg_lsn)

<= (pg_lsn,pg_lsn)

> (pg_lsn,pg_lsn)

>= (pg_lsn,pg_lsn)

range_inclusion_ops
= (anyrange,anyrange)

< (anyrange,anyrange)

<= (anyrange,anyrange)

> (anyrange,anyrange)

>= (anyrange,anyrange)

&& (anyrange,anyrange)

@> (anyrange,anyelement)

@> (anyrange,anyrange)

<@ (anyrange,anyrange)

<< (anyrange,anyrange)

>> (anyrange,anyrange)

&< (anyrange,anyrange)

&> (anyrange,anyrange)

-|- (anyrange,anyrange)

text_bloom_ops
= (text,text)

text_minmax_ops
= (text,text)

< (text,text)

<= (text,text)

> (text,text)

>= (text,text)

tid_bloom_ops
= (tid,tid)

tid_minmax_ops
= (tid,tid)

< (tid,tid)

<= (tid,tid)

> (tid,tid)

>= (tid,tid)

tid_minmax_multi_ops
= (tid,tid)

< (tid,tid)

<= (tid,tid)

> (tid,tid)

>= (tid,tid)

timestamp_bloom_ops
= (timestamp,timestamp)

timestamp_minmax_ops
= (timestamp,timestamp)

< (timestamp,timestamp)

<= (timestamp,timestamp)

> (timestamp,timestamp)

>= (timestamp,timestamp)

timestamp_minmax_multi_ops
= (timestamp,timestamp)

< (timestamp,timestamp)

<= (timestamp,timestamp)

> (timestamp,timestamp)

>= (timestamp,timestamp)

timestamptz_bloom_ops
= (timestamptz,timestamptz)

timestamptz_minmax_ops
= (timestamptz,timestamptz)

< (timestamptz,timestamptz)

<= (timestamptz,timestamptz)

> (timestamptz,timestamptz)

>= (timestamptz,timestamptz)

timestamptz_minmax_multi_ops
= (timestamptz,timestamptz)

< (timestamptz,timestamptz)

<= (timestamptz,timestamptz)

> (timestamptz,timestamptz)

>= (timestamptz,timestamptz)

time_bloom_ops
= (time,time)

time_minmax_ops
= (time,time)

< (time,time)

<= (time,time)

> (time,time)

>= (time,time)

time_minmax_multi_ops
= (time,time)

< (time,time)

<= (time,time)

> (time,time)

>= (time,time)

timetz_bloom_ops
= (timetz,timetz)

timetz_minmax_ops
= (timetz,timetz)

< (timetz,timetz)

<= (timetz,timetz)

> (timetz,timetz)

>= (timetz,timetz)

timetz_minmax_multi_ops
= (timetz,timetz)

< (timetz,timetz)

<= (timetz,timetz)

> (timetz,timetz)

>= (timetz,timetz)

uuid_bloom_ops
= (uuid,uuid)

uuid_minmax_ops
= (uuid,uuid)

< (uuid,uuid)

<= (uuid,uuid)

> (uuid,uuid)

>= (uuid,uuid)

uuid_minmax_multi_ops
= (uuid,uuid)

< (uuid,uuid)

<= (uuid,uuid)

> (uuid,uuid)

>= (uuid,uuid)

varbit_minmax_ops
= (varbit,varbit)

< (varbit,varbit)

<= (varbit,varbit)

> (varbit,varbit)

>= (varbit,varbit)

Параметры класса операторов

Для некоторых встроенных классов операторов разрешено задавать параметры, влияющие на их поведение. У каждого класса операторов имеется собственный набор допустимых операторов. Указывать параметры можно только для классов операторов bloom и minmax-multi.

Классы операторов bloom принимают эти параметры:

n_distinct_per_range
Определяет предположительное количество различных значений, отличных от NULL, в диапазоне блоков, используемых индексами BRIN типа bloom для установки размера фильтра Блума. Его поведение аналогично таковому у параметра n_distinct для ALTER TABLE. Когда задается положительное значение, считается, что каждый диапазон блоков содержит именно это количество различных значений, отличных от NULL. Когда задается отрицательное значение, которое должно быть больше либо равно -1, считается, что количество различных значений, отличных от NULL, линейно возрастает в зависимости от максимально возможного количества кортежей в диапазоне блоков (примерно 290 строк на блок). Значение по умолчанию — -0.1, а минимальное количество различных значений, отличных от NULL, которое можно задать, — 16.

false_positive_rate
Определяет целевую долю ложноположительных срабатываний, используемых индексами BRIN типа bloom для установки размера фильтра Блума. Значение должно находиться в пределах от 0.0001 до 0.25. Значение по умолчанию — 0.01, т. е. доля ложноположительных срабатываний составляет 1%.

Классы операторов minmax-multi принимают эти параметры:

values_per_range
Определяет максимальное количество значений, хранимых индексами BRIN типа minmax для создания сводки по диапазону блоков. Каждое значение может представлять либо точку, либо границу интервала. Значения должны находиться в пределах от 8 до 256, а значение по умолчанию равно 32.

Расширяемость

Интерфейс BRIN имеет высокий уровень абстракции, требующий от разработчика метода доступа описать только семантику обрабатываемого типа данных. Сам уровень BRIN обеспечивает параллельный доступ, протоколирование в журнал и поиск в структуре индекса.

Все, что требуется, чтобы получить работающий метод доступа BRIN, — это реализовать несколько пользовательских методов, которые определят поведение сводных значений, хранящихся в индексе, и их взаимодействие с ключами сканирования. Иными словами, BRIN сочетает расширяемость с универсальностью, повторным использованием кода и простым интерфейсом.

Существует четыре метода, которые должен предоставить класс операторов для BRIN:

BrinOpcInfo *opcInfo(Oid type_oid)

Возвращает внутреннюю информацию о сводных данных индексированных столбцов. Возвращаемое значение должно указывать на BrinOpcInfo, выделенную с помощью palloc. Определение структуры такое:

typedef struct BrinOpcInfo
{
    /* Количество столбцов, хранящихся в столбце индекса этого класса операторов */
    uint16      oi_nstored;

    /* Непрозрачный указатель для внутреннего использования классом операторов */
    void       *oi_opaque;

    /* Элементы кэша типов для сохраненных столбцов */
    TypeCacheEntry *oi_typcache[FLEXIBLE_ARRAY_MEMBER];
} BrinOpcInfo;

Brinpcinfo.oi_opaque могут использовать процедуры класса операторов для передачи информации между вспомогательными функциями при сканировании индекса.

bool consistent(BrinDesc *bdesc, BrinValues *column, ScanKey key)

Показывает, все ли записи ScanKey соответствуют заданным индексированным значениям некоторого диапазона. Номер целевого атрибута передается как часть ключа сканирования. Для одного атрибута может передаваться несколько ключей сканирования; количество записей определяется параметром nkeys.

bool consistent(BrinDesc *bdesc, BrinValues *column, ScanKey key)

Показывает, соответствует ли эта запись ScanKey заданным индексированным значениям некоторого диапазона. Номер целевого атрибута передается как часть ключа сканирования. Это более старый обратно совместимый вариант функции consistent.

bool addValue(BrinDesc *bdesc, BrinValues *column, Datum newval, bool isnull)

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

bool unionTuples(BrinDesc *bdesc, BrinValues *a, BrinValues *b)

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

Дополнительно класс операторов для BRIN может задать следующий метод:

void options(local_relopts *relopts)

Определяет набор видимых пользователю параметров, управляющих поведением класса операторов.

Функции options передается указатель на структуру local_relopts, которую нужно заполнить набором параметров, специфичных для класса операторов. К этим параметрам можно обращаться из других вспомогательных функций с помощью макросов PG_HAS_OPCLASS_OPTIONS() и PG_GET_OPCLASS_OPTIONS().

Поскольку в BRIN извлечение ключа из индексируемых значений и представление этого ключа допускают гибкость, они могут зависеть от параметров, заданных пользователем.

Основной дистрибутив включает поддержку четырех типов классов операторов: minmax, minmax-multi, inclusion и bloom. Использующие их определения классов операторов поставляются для резидентных типов данных по мере необходимости. Для других типов данных пользователь может определить дополнительные классы операторов с помощью равнозначных определений без необходимости писать какой-либо исходный код; достаточно объявить подходящие записи в каталоге. Обратите внимание, что предположения о семантике стратегий операторов зашиты в исходном коде вспомогательных функций.

Также возможно создать классы операторов, воплощающие совершенно иную семантику, предоставив реализации четырех основных вспомогательных функций, описанных выше. Обратите внимание, что обратная совместимость между разными основными версиями не гарантируется: например, в следующих выпусках могут потребоваться дополнительные вспомогательные функции.

Для написания класса операторов для типа данных, реализующего полностью упорядоченное множество, можно воспользоваться вспомогательными функциями minmax наряду с соответствующими операторами, как показано в Таблице 2. Все члены класса операторов (функции и операторы) являются обязательными.

Таблица 2. Номера стратегий и вспомогательных функций для классов операторов minmax

Член класса операторов | Объект
Вспомогательная функция 1 | внутренняя функция brin_minmax_opcinfo()
Вспомогательная функция 2 | внутренняя функция brin_minmax_add_value()
Вспомогательная функция 3 | внутренняя функция brin_minmax_consistent()
Вспомогательная функция 4 | внутренняя функция brin_minmax_union()
Стратегия оператора 1 | оператор меньше
Стратегия оператора 2 | оператор меньше-или-равно
Стратегия оператора 3 | оператор равно
Стратегия оператора 4 | оператор больше-или-равно
Стратегия оператора 5 | оператор больше

Для написания класса операторов для сложного типа данных, значения которого включены в другой тип, можно воспользоваться вспомогательными функциями inclusion наряду с соответствующими операторами, как показано в Таблице 3. Для этого требуется всего одна дополнительная функция, которую можно написать на любом языке. Для дополнительной функциональности можно определить больше функций. Все операторы являются необязательными. Некоторым из них требуется наличие других операторов, что показано в таблице в виде зависимостей.

Таблица 3. Номера стратегий и вспомогательных функций для классов операторов inclusion

Член класса операторов | Объект | Зависимость
Вспомогательная функция 1 | внутренняя функция brin_inclusion_opcinfo() |
Вспомогательная функция 2 | внутренняя функция brin_inclusion_add_value() |
Вспомогательная функция 3 | внутренняя функция brin_inclusion_consistent() |
Вспомогательная функция 4 | внутренняя функция brin_inclusion_union() |
Вспомогательная функция 11 | функция для слияния двух элементов |
Вспомогательная функция 12 | необязательная функция для проверки возможности слияния двух элементов |
Вспомогательная функция 13 | необязательная функция для проверки, содержится ли один элемент в другом |
Вспомогательная функция 14 | необязательная функция для проверки, является ли элемент пустым |
Стратегия оператора 1 | оператор левее | Стратегия оператора 4
Стратегия оператора 2 | оператор не-простирается-правее | Стратегия оператора 5
Стратегия оператора 3 | оператор перекрывается |
Стратегия оператора 4 | оператор не-простирается-левее | Стратегия оператора 1
Стратегия оператора 5 | оператор правее | Стратегия оператора 2
Стратегия оператора 6, 18 | оператор то-же-или-равно | Стратегия оператора 7
Стратегия оператора 7, 13, 16, 24, 25 | оператор содержит-или-равно |
Стратегия оператора 8, 14, 26, 27 | оператор содержится-в-или-равно | Стратегия оператора 3
Стратегия оператора 9 | оператор не-простирается-выше | Стратегия оператора 11
Стратегия оператора 10 | оператор ниже | Стратегия оператора 12
Стратегия оператора 11 | оператор выше | Стратегия оператора 9
Стратегия оператора 12 | оператор не-простирается-ниже | Стратегия оператора 10
Стратегия оператора 20 | оператор меньше | Стратегия оператора 5
Стратегия оператора 21 | оператор меньше-или-равно | Стратегия оператора 5
Стратегия оператора 22 | оператор больше | Стратегия оператора 1
Стратегия оператора 23 | оператор больше-или-равно | Стратегия оператора 1

Номера вспомогательных функций от 1 до 10 зарезервированы для внутренних функций BRIN, поэтому функции уровня SQL начинаются с номера 11. Вспомогательная функция номер 11 является основной функцией, требуемой для постройки индекса. Она должна принимать два аргумента с тем же типом данных, что и у класса операторов, и возвращать их объединение. Класс операторов inclusion может сохранять объединенные значения в различных типах данных, если это определено в параметре STORAGE. Значение, возвращаемое функцией объединения, должно соответствовать типу данных STORAGE.

Вспомогательные функции номер 12 и номер 14 предоставляются для поддержки нерегулярностей встроенных типов данных. Функция номер 12 используется для поддержки сетевых адресов из различных семейств, которые нельзя объединять. Функция номер 14 используется для поддержки пустых диапазонов. Функция номер 13 является необязательной, но рекомендуемой; она позволяет проверить новое значение, прежде чем оно будет передано функции объединения. Поскольку инфраструктура BRIN может упростить некоторые операции, когда объединение не меняется, использование этой функции может повысить производительность индекса.

Для написания класса операторов для типа данных, реализующего только оператор равенства и поддерживающего хеширование, можно использовать вспомогательные процедуры bloom наряду с соответствующими операторами, как показано в Таблице 4. Все члены класса операторов (процедуры и операторы) являются обязательными.

Таблица 4. Номера стратегий и вспомогательных процедур для классов операторов bloom

Член класса операторов | Объект
Вспомогательная процедура 1 | внутренняя функция brin_bloom_opcinfo()
Вспомогательная процедура 2 | внутренняя функция brin_bloom_add_value()
Вспомогательная процедура 3 | внутренняя функция brin_bloom_consistent()
Вспомогательная процедура 4 | внутренняя функция brin_bloom_union()
Вспомогательная процедура 11 | функция для вычисления хеша элемента
Стратегия оператора 1 | оператор равно

Номера вспомогательных процедур 1-10 зарезервированы для внутренних функций BRIN, поэтому функции уровня SQL начинаются с номера 11. Вспомогательная функция номер 11 является основной функцией, требуемой для построения индекса. Она должна принимать один аргумент с тем же типом данных, что и у класса операторов, и возвращать хеш этого значения.

Класс операторов minmax-multi также предназначен для типов данных, реализующих полностью упорядоченное множество, и может рассматриваться просто как расширение класса операторов minmax. В то время как класс операторов minmax рассчитывает сводку по значениям из каждого диапазона блоков как один непрерывный интервал, minmax-multi позволяет рассчитать сводку в виде нескольких меньших интервалов для более эффективной обработки выпадающих значений. Можно использовать вспомогательные процедуры minmax-multi наряду с соответствующими операторами, как показано в Таблице 5. Все члены класса операторов (процедуры и операторы) являются обязательными.

Таблица 5. Номера стратегий и вспомогательных процедур для классов операторов minmax-multi

Член класса операторов | Объект
Вспомогательная процедура 1 | внутренняя функция brin_minmax_multi_opcinfo()
Вспомогательная процедура 2 | внутренняя функция brin_minmax_multi_add_value()
Вспомогательная процедура 3 | внутренняя функция brin_minmax_multi_consistent()
Вспомогательная процедура 4 | внутренняя функция brin_minmax_multi_union()
Вспомогательная процедура 11 | функция для вычисления расстояния между двумя значениями (длины диапазона)
Стратегия оператора 1 | оператор меньше
Стратегия оператора 2 | оператор меньше-или-равно
Стратегия оператора 3 | оператор равно
Стратегия оператора 4 | оператор больше-или-равно
Стратегия оператора 5 | оператор больше

Классы операторов minmax и inclusion поддерживают операторы с разными типами данных, хотя с ними зависимости становятся более сложными. Классу операторов minmax требуется, чтобы для двух аргументов одного типа определялся полный набор операторов. Это позволяет поддерживать дополнительные типы данных, определяя дополнительные наборы операторов. Стратегии класса операторов inclusion зависят от других стратегий, как показано в Таблице 3, или от своих собственных стратегий оператора. Для них требуется, чтобы оператор зависимости был определен с типом данных STORAGE в качестве левого аргумента и другим поддерживаемым типом в качестве правого аргумента поддерживаемого оператора. См. определение float4_minmax_ops в качестве примера minmax и box_inclusion_ops в качестве примера inclusion.

Управление параллельным доступом

В этой главе описывается поведение СУБД QHB, когда две или более сессий пытаются одновременно обратиться к одним и тем же данным. Задачами, стоящими перед СУБД в такой ситуации, являются обеспечение для всех сеансов эффективного доступа и сохранение при этом целостности данных. Рекомендуем ознакомиться с материалами, освещаемыми в этой главе, всем разработчикам приложений баз данных.

Многоверсионная модель

QHB предоставляет разработчикам богатый набор инструментов для управления параллельным доступом к данным. Внутренняя согласованность данных поддерживается с помощью многоверсионной модели (Multiversion Concurrency Control, MVCC). Она устроена таким образом, что каждая команда SQL видит снимок данных (версию базы данных), сделанный некоторое время назад, независимо от текущего состояния нижележащих данных. Благодаря этому команды не видят несогласованных данных, появившихся в результате параллельных транзакций, вносящих изменения в одни и те же строки данных, тем самым обеспечивая изоляцию транзакций для каждого сеанса баз данных. MVCC, отказываясь от методологий блокировки традиционных СУБД, сводит к минимуму конфликты блокировок, чтобы обеспечить разумную производительность в многопользовательских средах.

Основное преимущество использования модели управления параллельным доступом MVCC вместо блокировок состоит в том, что в MVCC блокировки, полученные для запроса (чтения) данных, не конфликтуют с блокировками, полученными для записи данных, а потому чтение и запись никогда не блокируют друг друга. QHB гарантирует это даже при самом строгом уровне изоляции транзакций за счет использования инновационного уровня изоляции Сериализуемая изоляция снимков (Serializable Snapshot Isolation, SSI).

Также в QHB доступны средства блокировки на уровне таблиц и строк
для приложений, которые обычно не нуждаются в полной изоляции транзакций и предпочитают явно управлять конкретными точками конфликта. Однако правильное использование MVCC обычно обеспечивает лучшую производительность, чем блокировки. Кроме того, определяемые приложением консультативные блокировки предоставляют механизм для получения блокировок, которые не привязаны к одной транзакции.

Изоляция транзакций

Стандарт SQL определяет четыре уровня изоляции транзакций. Наиболее строгим является сериализуемый, который определяется стандартом в параграфе, гласящим, что любое одновременное выполнение набора из нескольких сериализуемых транзакций должно гарантированно давать тот же эффект, что и запуск их по очереди в некотором порядке. Три других уровня определяются в терминах явлений, возникающих в результате взаимодействия между параллельными транзакциями, которые не должны происходить на каждом уровне. В стандарте отмечается, что из определения сериализуемого уровня вытекает, что на этом уровне ни одно из этих явлений не возможно. (В этом нет ничего удивительного — если эффект транзакций должен быть таким же, как и при выполнении их по очереди, как можно увидеть какие-либо явления, вызванные их взаимодействием?)

Явления, которые запрещены на разных уровнях:

«Грязное» чтение (dirty read)

Транзакция считывает данные, записанные параллельной незафиксированной транзакцией.

Неповторяемое чтение (nonrepeatable read)

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

Фантомное чтение (phantom read)

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

Аномалии сериализации (serialization anomaly)

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

Уровни изоляции транзакций, описанные в стандарте SQL и реализованные в QHB, описаны в таблице ниже.

Таблица: Уровни изоляции транзакций

Уровень изоляции | Грязное чтение | Неповторяемое чтение | Фантомное чтение | Аномалия сериализации
Read uncommitted (Чтение незафиксированных данных) | Разрешено, но не в QHB | Возможно | Возможно | Возможно
Read committed (Чтение зафиксированных данных) | Невозможно | Возможно | Возможно | Возможно
Repeatable read (Повторяемое чтение) | Невозможно | Невозможно | Разрешено, но не в QHB | Возможно
Serializable (Сериализуемость) | Невозможно | Невозможно | Невозможно | Невозможно

В QHB вы можете запросить любой из четырех стандартных уровней изоляции транзакций, но внутренне реализованы только три различных уровня, то есть режим Read Uncommitted в QHB ведет себя как Read Committed. Причиной является то, что это единственный разумный способ сопоставить стандартные уровни изоляции с архитектурой многоверсионного управления QHB.

Из таблицы также видно, что реализация Repeatable Read в QHB не позволяет выполнять фантомные чтения. Стандарт SQL допускает более строгое поведение: четыре уровня изоляции определяют только то, какие явления не должны происходить, а не какие обязательно должны. Поведение доступных уровней изоляции подробно описано в следующих подразделах.

Чтобы установить уровень изоляции транзакции, используйте команду SET TRANSACTION.

Важно!
Для некоторых типов данных и функций QHB имеются специальные правила, касающиеся поведения транзакций. В частности, изменения, внесенные в последовательность (и, следовательно, счетчик столбца, объявленного как serial), сразу видны всем другим транзакциям и не откатываются назад, если транзакция, которая внесла изменения, прерывается. См. главы Функции управления последовательностями и Последовательные типы.

Уровень изоляции Read Committed

Read Committed — уровень изоляции транзакций, выбираемый в QHB по умолчанию. Когда транзакция использует этот уровень изоляции, запрос SELECT (без предложения FOR UPDATE/SHARE) видит только данные, зафиксированные до начала запроса; он никогда не видит ни незафиксированные данные, ни изменения, зафиксированные во время выполнения запроса параллельными транзакциями. По сути, запрос SELECT видит снимок базы данных на момент начала выполнения запроса. Однако при этом SELECT видит результаты предыдущих изменений, произошедших в его собственной транзакции, даже если они еще не зафиксированы. Также обратите внимание, что две последовательные команды SELECT могут видеть разные данные (даже если они выполняются в рамках одной транзакции), если другие транзакции фиксируют изменения данных после запуска первого SELECT и до запуска второго.

Относительно поиска целевых строк команды UPDATE, DELETE, SELECT FOR UPDATE и SELECT FOR SHARE ведут себя так же, как и SELECT: они будут находить только те целевые строки, которые были зафиксированы на момент запуска команды. Однако к моменту ее обнаружения такая целевая строка может быть уже изменена, удалена или заблокирована другой параллельной транзакцией. В этом случае запланированная изменяющая данные транзакция будет дожидаться фиксации или отката первой изменяющей данные транзакции (если та еще выполняется). Если первая транзакция откатывается, результат ее действий нивелируется, и вторая транзакция может продолжить изменение изначально обнаруженной строки. Если первая транзакция фиксируется и при этом удаляет строку, то вторая эту строку проигнорирует, а в противном случае попытается выполнить свою операцию с измененной версией строки. Условие поиска в команде (предложение WHERE) вычисляется повторно для проверки, по-прежнему ли измененная версия строки ему соответствует. Если да, то вторая изменяющая данные транзакция продолжает свою операцию с измененной версией строки. Применительно к SELECT FOR UPDATE и SELECT FOR SHARE это означает, что обновленная версия строки блокируется и возвращается клиенту.

Команда INSERT с предложением ON CONFLICT DO UPDATE ведет себя схожим образом. В режиме Read Commited каждая строка, предлагаемая для добавления, будет либо добавлена, либо изменена. Если не возникает несвязанных ошибок, гарантируется один из этих двух исходов. Если конфликт вызван другой транзакцией, результат который пока не видим для INSERT, то предложение UPDATE подействует на эту строку, даже несмотря на то, что обычным путем данная команда может не видеть никакую версию этой строки.

Выполнение INSERT с предложением ON CONFLICT DO NOTHING может привести к тому, что добавление строки не продолжиться из-за результата действий другой транзакции, эффекты которой не видны для снимка INSERT. Опять же, это характерно только для режима Read Committed.

В силу вышеприведенных правил, изменяющая команда может увидеть снимок несогласованного состояния: она может видеть результаты выполнения параллельных изменяющих команд в той же строке, которую пытается изменить она, но при этом не видит их результаты в других строках в базе данных. Вследствие такого поведения режим Read Commited не подходит для команд со сложным сценарием поиска; однако он вполне пригоден для более простых случаев. Например, рассмотрим сальдо банковского счета со следующими транзакциями:

BEGIN;
UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 12345;
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 7534;
COMMIT;

Если две такие транзакции одновременно пытаются изменить остаток на счете 12345, мы, разумеется, хотим, чтобы вторая транзакция началась с обновленной версии строки счета. Поскольку каждая команда влияет только на заранее определенную строку, если она увидит в измененную версию этой строки, это не вызовет проблем несогласованности.

В более сложных операциях режим Read Committed может привести к нежелательным результатам. Например, рассмотрим команду DELETE, работающую с данными, которые добавляются и удаляются из ее ограничительных критериев другой командой. Например, предположим, что website — это таблица из двух строк, где website.hits равны 9 и 10:

BEGIN;
UPDATE website SET hits = hits + 1;
-- выполняется параллельно в другом сеансе:  DELETE FROM website WHERE hits = 10;
COMMIT;

Команда DELETE не сделает ничего, даже несмотря на то, что строка website.hits = 10 была в таблице как до, так и после выполнения UPDATE. Это связано с тем, что строка со значением 9 до изменения пропускается, а когда UPDATE завершается и DELETE получает блокировку, новым значением строки становится уже не 10, а 11, которое больше не соответствует критериям.

Поскольку режим Read Committed запускает каждую команду с новым снимком состояния, который включает в себя все транзакции, зафиксированные вплоть до этого момента, последующие команды в той же транзакции в любом случае будут видеть результаты зафиксированной параллельной транзакции. Вопрос здесь в том, видит ли одна команда абсолютно согласованное представление базы данных

Частичной изоляции транзакций, обеспечиваемой режимом Read Committed, достаточно для многих приложений, и этот режим быстр и прост в использовании; однако он подходит не для всех случаев. Приложениям, которые выполняют сложные запросы и обновления, может потребоваться более строго согласованное представление базы данных, чем то, которое дает режим Read Committed.

Уровень изоляции Repeatable Read

Уровень изоляции Repeatable Read видит только данные, зафиксированные до начала транзакции; он никогда не видит ни незафиксированные данные, ни изменения, зафиксированные во время выполнения транзакции параллельными транзакциями. (Тем не менее, запрос видит результаты предыдущих изменений, произошедших в его собственной транзакции, даже если те еще не зафиксированы.) Это более надежная гарантия, чем требуется стандартом SQL для этого уровня изоляции, которая предотвращает все явления, описанные в таблице Уровни изоляции транзакций, за исключением аномалий сериализации. Как упомянуто выше, это не запрещено стандартом, описывающим только минимальную защиту, которую должен обеспечивать каждый уровень изоляции.

Этот уровень отличается от Read Committed тем, что запрос в транзакции повторяемого чтения видит снимок состояния на момент начала первой команды в транзакции (не считая команд управления транзакцией), а не начала текущей команды в рамках транзакции. Таким образом, последовательные команды SELECT в одной транзакции видят одни и те же данные, т. е. они не видят изменений, внесенных другими транзакциями, зафиксированными после запуска их собственной транзакции.

Приложения, использующие этот уровень, должны быть готовы повторить транзакции при возникновении сбоев сериализации.

Относительно поиска целевых строк команды UPDATE, DELETE, SELECT FOR UPDATE и SELECT FOR SHARE ведут себя так же, как и SELECT: они будут находить только те целевые строки, которые были зафиксированы на момент запуска команды. Однако моменту ее обнаружения такая целевая строка может быть уже изменена, удалена или заблокирована другой параллельной транзакцией. В этом случае транзакция в режиме Repeatable Read будет дожидаться фиксации или отката первой изменяющей данные транзакции (если та еще выполняется). Если первая транзакция откатывается, результат ее действий нивелируется, и текущая транзакция может продолжить изменение изначально обнаруженной строки. Но если первая транзакция фиксируется (и не просто блокирует строку, а на самом деле изменяет или удаляет ее), то произойдет откат текущей транзакции с сообщением

ERROR:  could not serialize access due to concurrent update

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

Когда приложение получает это сообщение об ошибке, оно должно прервать текущую транзакцию и повторить всю транзакцию с самого начала. Во второй раз транзакция увидит ранее зафиксированное изменение как часть своего начального представления базы данных, поэтому в использовании новой версии строки в качестве отправной точки для изменения в новой транзакции нет логического конфликта.

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

Режим Repeatable Read обеспечивает строгую гарантию того, что каждая транзакция видит полностью стабильное представление базы данных. Тем не менее, это представление не всегда будет соответствовать последовательному (по очереди) выполнению параллельных транзакций одного уровня. Например, на этом уровне даже транзакция только для чтения может видеть измененную контрольную запись, показывающую, что пакет был завершен, но не видеть одну из подробных записей, которая логически является частью пакета, потому что эта транзакция прочитала более раннюю версию контрольной записи. Попытки обеспечить соблюдение бизнес-правил транзакциями, выполняющимися на этом уровне изоляции, вряд ли сработают правильно без осторожного использования явных блокировок конфликтующих транзакций.

Для реализации уровня изоляции Repeatable Read применяется подход, который называется в академической литературе по базам данных и в других СУБД Изоляция снимков (Snapshot Isolation). По сравнению с системами, использующими традиционный метод блокировок, затрудняющий параллельное выполнение, при этом подходе наблюдается другое поведение и другая производительность. В некоторых СУБД могут существовать даже два отдельных уровня Repeatable Read и Snapshot Isolation с различным поведением. Допускаемые особые условия, представляющие отличия двух этих подходов, не были формализованы разработчиками теории БД до развития стандарта SQL, и их рассмотрение выходит за рамки данного руководства.

Уровень изоляции Serializable

Уровень изоляции Serializable обеспечивает самую строгую изоляцию транзакций. Этот уровень эмулирует последовательное выполнение всех зафиксированных транзакций, как если бы транзакции были выполнены одна за другой, поочередно, а не одновременно. Однако, как и на уровне Repeatable Read, на этом уровне приложения должны быть готовы повторить транзакции в случае сбоев сериализации. Фактически этот уровень изоляции работает точно так же, как и Repeatable Read, за исключением того, что он отслеживает условия, при которых выполнение набора параллельных сериализуемых транзакций не будет согласовано со всеми возможными последовательными (по очереди) выполнениями этих же транзакций. Этот мониторинг не вводит каких-либо блокировок, кроме присутствующих в повторяемом чтении, но создает некоторые накладные расходы на мониторинг, а при выявлении условий, которые могут вызвать аномалию сериализации, вызовет сбой сериализации.

В качестве примера рассмотрим таблицу mytab, изначально содержащую:

class | value
------+-------
    1 |    10
    1 |    20
    2 |   100
    2 |   200

Предположим, что сериализуемая транзакция A вычисляет:

SELECT SUM(value) FROM mytab WHERE class = 1;

а затем вставляет результат (30) в качестве value в новую строку с class = 2. Одновременно сериализуемая транзакция B вычисляет:

SELECT SUM(value) FROM mytab WHERE class = 2;

и получает результат 300, который вставляет в новую строку с class = 1. Затем обе транзакции пытаются зафиксироваться. Если какая-либо транзакция выполняется на уровне изоляции Repeatable Read, обеим будет разрешено зафиксироваться; но поскольку результат не соответствует последовательному порядку, использование транзакций в режиме Serializable позволит зафиксировать одну транзакцию и откатит другую с этим сообщением:

ERROR:  could not serialize access due to read/write dependencies among transactions

Это связано с тем, что если бы A выполнилась до B, то B вычислила бы сумму 330, а не 300, и точно так же при выполнении в обратном порядке А вычислила бы другую сумму.

Рассчитывая, что транзакции в режиме Serializable предотвратят аномалии, важно помнить, что данные, читаемые из постоянной пользовательской таблицы, не должны рассматриваться как действительные до тех пор, пока не произойдет успешная фиксация прочитавшей их транзакции. Это верно в том числе и для транзакций, которые выполняют только чтение, за исключением ситуаций, когда данные считываются в откладываемой транзакции такого типа. В этом случае считается, что данные станут действительными сразу после их прочтения, поскольку прежде, чем начать читать любые данные, такая транзакция ждет, пока не сможет получить снимок, гарантированно предотвращающий подобные проблемы. Во всех остальных случаях приложения не должны полагаться на результаты чтения в транзакции, которая после этого прервалась; при сбое и ошибке приложения должны повторять транзакцию, пока та не будет успешно зафиксирована.

Для гарантии истинной сериализуемости QHB использует предикатную блокировку, т. е. он сохраняет блокировки, которые позволяют ему определять, когда запись могла бы повлиять на результат предыдущего чтения из параллельной транзакции, если бы та выполнялась первой. В QHB эти блокировки не приводят к реальной блокировке данных и, следовательно, никак не могут вызвать взаимоблокировку транзакций. Они используются для идентификации и пометки зависимостей между параллельными транзакциями уровня Serializable, которые в определенных комбинациях могут привести к аномалиям сериализации. Напротив, транзакции уровня *Read Committed или Repeatable Read для обеспечения согласованности данных придется либо блокировать таблицу полностью, что может помешать другим пользователям ее использовать, либо применить команду SELECT FOR UPDATE или SELECT FOR SHARE, которые могут не только заблокировать другие транзакции, но и открыть доступ к диску.

Предикатные блокировки в QHB, как и в большинстве других СУБД, основаны на данных, фактически доступных транзакции. Они будут отображаться в системном представлении pg_locks со значением mode равным SIReadLock. Какие именно блокировки будут получены во время выполнения запроса, зависит от плана, используемого этим запросом, при этом несколько детализированных блокировок (например, блокировки кортежей) в процессе транзакции могут объединяться в более общие (например, блокировки страниц) для экономии памяти, затрачиваемой на отслеживание блокировок. На самом деле транзакция READ ONLY способна освободить свои блокировки SIRead до завершения, если обнаружит, что конфликты, которые могут привести к аномалии сериализации, по-прежнему исключены. Фактически транзакции READ ONLY зачастую могут установить этот факт еще при запуске и этим избежать каких-либо предикатных блокировок. Если явно запросить транзакцию SERIALIZABLE READ ONLY DEFERRABLE, она будет блокирована, пока не сможет установить этот факт. (Это единственный случай, когда транзакции уровня Serializable блокируются, а транзакции уровня Repeatable Read — нет.) С другой стороны, блокировки SIRead часто должны сохраняться и после фиксации транзакции, пока не завершатся перекрывающие ее транзакции чтения-записи.

Последовательное использование сериализуемых транзакций может упростить разработку. Гарантия того, что любая совокупность успешно зафиксированных параллельных сериализуемых транзакций приведет к тому же результату, как если бы они выполнялись по очереди, означает, что если вы сумеете доказать, что одна транзакция определенного содержания будет работать правильно при отдельном запуске, то можете быть уверены, что эта транзакция будет работать правильно в любой комбинации сериализуемых транзакций, даже не имея никакой информации о том, что они могут делать, либо же она не будет успешно зафиксирована. При этом важно, чтобы в среде, в которой используется этот метод, был реализован общий способ обработки ошибок сериализации (которые всегда возвращаются со значением SQLSTATE равном ’40001’), поскольку будет очень сложно заранее предсказать, какие именно транзакции могут стать жертвами зависимостей чтения/записи и не будут зафиксированы, чтобы предотвратить аномалии сериализации. Мониторинг зависимостей чтения/записи создает нагрузку, как и перезапуск транзакций, которые прерываются с ошибкой сериализации, но если сравнить с затратами и блокировками, связанными с использованием явных блокировок и SELECT FOR UPDATE или SELECT FOR SHARE, то для некоторых сред сериализуемые транзакции гораздо выгоднее в плане производительности.

Хотя уровень изоляции Serializable в QHB позволяет фиксировать параллельные транзакции, только если может доказать, что существует последовательный порядок выполнения, который даст тот же результат, он не всегда предотвращает ошибки, которые не возникли бы при действительно последовательном выполнении. В частности, можно столкнуться с уникальными нарушениями ограничений, вызванными конфликтами с перекрывающимися сериализуемыми транзакциями, даже после явной проверки на отсутствие ключа перед попыткой его добавить. Этого можно избежать, убедившись, что все сериализуемые транзакции, которые добавляют потенциально конфликтующие ключи, сначала явно проверяют, могут ли они это сделать. Например, представьте приложение, которое запрашивает у пользователя новый ключ, а затем проверяет, существует ли он, сначала пытаясь выбрать его, либо генерирует новый ключ, выбирая ключ с максимальным существующим значением и добавляя его. Если некоторые сериализуемые транзакции добавляют новые ключи напрямую, не следуя этому протоколу, нарушения уникальных ограничений могут быть зарегистрированы даже в тех случаях, когда они не могли бы произойти при последовательном выполнении параллельных транзакций.

Чтобы обеспечить оптимальную производительность при использовании сериализуемых транзакций для управления параллельным доступом, следует учитывать следующие рекомендации:

По возможности объявляйте транзакции как READ ONLY.

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

Не вкладывайте в одну транзакцию больше команд, чем это необходимо для обеспечения целостности.

Не оставляйте соединения висеть «простаивающими в транзакции» дольше необходимого. Для автоматического отключения длительных сеансов можно использовать параметр конфигурации idle_in_transaction_session_timeout.

Исключите явные блокировки SELECT FOR UPDATE и SELECT FOR SHARE там, где они больше не нужны благодаря защите, автоматически предоставляемой сериализуемыми транзакциями.

Когда система вынуждена объединять несколько предикатных блокировок уровня страниц в одну предикатную блокировку уровня отношений, поскольку в таблице предикатных блокировок не хватает памяти, может возрасти частота сбоев сериализации. Этого можно избежать, увеличив параметр max_pred_locks_per_transaction, max_pred_locks_per_relation и/или max_pred_locks_per_page.

Последовательное сканирование всегда будет влечь за собой предикатные блокировки уровня отношений. Это может привести к увеличению частоты сбоев сериализации. В таких случаях бывает полезно подтолкнуть систему к использованию индексного сканирования путем уменьшения параметра random_page_cost и/или увеличения параметра cpu_tuple_cost. Обязательно сопоставьте любое снижение числа откатов и перезапусков транзакций с любым общим изменением времени выполнения запроса, чтобы оценить выгоду.

Для реализации уровня изоляции Serializable применяется подход, который называется в академической литературе по базам данных Сериализуемая изоляция снимков (Serializable Snapshot Isolation), с дополнительными проверками на предмет аномалий сериализации. По сравнению с другими системами, использующими традиционный метод блокировок, при этом подходе наблюдается другое поведение и другая производительность.

Явная блокировка

Для управления параллельным доступом к данным в таблицах QHB предоставляет различные режимы блокировки. Эти режимы можно использовать для блокировки со стороны приложения в ситуациях, когда MVCC не вызывает желаемого поведения. Кроме того, большинство команд QHB автоматически получают блокировки соответствующих режимов для гарантии того, что во время выполнения команды ссылочные таблицы не будут несовместимым образом удалены или изменены. (Например, TRUNCATE не может безопасно выполняться параллельно с другими операциями в той же таблице, поэтому во избежание конфликтов эта команда получает эксклюзивную блокировку для таблицы.)

Чтобы проверить список текущих незавершенных блокировок на сервере базы данных, воспользуйтесь системным представлением pg_locks. Дополнительную информацию о мониторинге состояния подсистемы диспетчера блокировки см. в главе Мониторинг активности базы данных.

Блокировки на уровне таблицы

В приведенном ниже списке показаны доступные режимы блокировки и контексты, в которых они автоматически используются QHB. Также любую из этих блокировок можно получить явно с помощью команды LOCK. Помните, что все эти режимы блокировки являются блокировками на уровне таблицы, даже если имя режима содержит слово «строка»; такие имена сложились исторически. В некоторой степени имена отражают типичное использование каждого режима блокировки — но семантика не имеет значения. Единственное, что на самом деле отличает один режим блокировки от другого, это набор режимов блокировки, с которыми конфликтует каждый из них (см. таблицу Конфликтующие режимы блокировки). Две транзакции не могут одновременно владеть блокировками конфликтующих режимов на одной и той же таблице. (Однако транзакция никогда не конфликтует сама с собой. Например, она может запросить блокировку ACCESS EXCLUSIVE а затем ACCESS SHARE для той же таблицы.) При этом многие транзакции свободно могут одновременно владеть бесконфликтными режимами блокировки. Обратите внимание, в частности, на то, что некоторые режимы блокировки конфликтуют сами с собой (например, блокировкой ACCESS EXCLUSIVE в один момент времени может владеть только одна транзакция), тогда как другие этого не делают (например, блокировкой ACCESS SHARE могут одновременно владеть несколько транзакций).

Режимы блокировки на уровне таблицы

ACCESS SHARE

Конфликтует только с режимом блокировки ACCESS EXCLUSIVE.

Команда SELECT запрашивает этот режим блокировки для ссылочных таблиц. В целом, любой запрос, который только читает таблицу и не изменяет ее, получит этот режим блокировки.

ROW SHARE

Конфликтует с режимами блокировки EXCLUSIVE и ACCESS EXCLUSIVE.

Команды SELECT FOR UPDATE и SELECT FOR SHARE запрашивают этот режим блокировки для целевой таблицы (или таблиц) (в дополнение к блокировкам ACCESS SHARE для любых других таблиц, на которые есть ссылки, но которые не выбраны предложением FOR UPDATE/FOR SHARE).

ROW EXCLUSIVE

Конфликтует с режимами блокировки SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE и ACCESS EXCLUSIVE.

Команды UPDATE, DELETE и INSERT запрашивают этот режим блокировки для целевой таблицы (в дополнение к блокировкам ACCESS SHARE для любых других ссылочных таблиц). В целом, этот режим блокировки получит любая команда, которая изменяет данные в таблице.

SHARE UPDATE EXCLUSIVE

Конфликтует с режимами блокировки SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE и ACCESS EXCLUSIVE. Этот режим защищает таблицу от параллельного запуска изменений схемы и команды VACUUM.

Запрашивается командами VACUUM (без FULL), ANALYZE, CREATE INDEX CONCURRENTLY, REINDEX CONCURRENTLY, CREATE STATISTICS, а также некоторыми вариантами ALTER INDEX и ALTER TABLE (подробную информацию см. в описании команд ALTER INDEX и ALTER TABLE).

SHARE

Конфликтует с режимами блокировки ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE ROW EXCLUSIVE, EXCLUSIVE и ACCESS EXCLUSIVE. Этот режим защищает таблицу от параллельных изменений данных.

Запрашивается командой CREATE INDEX (без CONCURRENTLY).

SHARE ROW EXCLUSIVE

Конфликтует с режимами блокировки ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE и ACCESS EXCLUSIVE. Этот режим защищает таблицу от параллельных изменений данных и является самоисключающим, так что получить его может только один сеанс за раз.

Запрашивается командой CREATE TRIGGER и некоторыми формами ALTER TABLE (см. ALTER TABLE).

EXCLUSIVE

Конфликтует с режимами блокировки ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE и ACCESS EXCLUSIVE. Этот режим допускает только одновременные блокировки ACCESS SHARE, т. к. параллельно с транзакцией, владеющей этим режимом блокировки, может выполняться только чтение из таблицы.

Запрашивается командой REFRESH MATERIALIZED VIEW CONCURRENTLY.

ACCESS EXCLUSIVE

Конфликтует со всеми режимами блокировки (ACCESS SHARE, ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE и ACCESS EXCLUSIVE). Этот режим гарантирует, что только транзакция, владеющая этой блокировкой, имеет доступ к таблице (любым способом).

Запрашивается командами REINDEX DROP TABLE, TRUNCATE, REINDEX, CLUSTER, VACUUM FULL и REFRESH MATERIALIZED VIEW (без CONCURRENTLY). Многие формы ALTER INDEX и ALTER TABLE также запрашивают блокировку на этом уровне. Это также режим блокировки по умолчанию для операторов LOCK TABLE, которые не указывают режим явно.

Подсказка
Только блокировка ACCESS EXCLUSIVE блокирует оператор SELECT (без FOR UPDATE/SHARE).

После получения блокировка обычно удерживается до конца транзакции. Но если блокировка получена после установления точки сохранения, то при откате к этой точке блокировка немедленно снимается. Это согласуется с принципом, что команда ROLLBACK отменяет все эффекты команд после точки сохранения. То же самое относится и к блокировкам, полученным в блоке исключений PL/pgSQL: при выходе из блока с ошибкой эти блокировки освобождаются.

Таблица: Конфликтующие режимы блокировки

Запрашиваемый
режим
блокировки

Текущий режим блокировки

ACCESS
SHARE

ROW
SHARE

ROW
EXCLUSIVE

SHARE
UPDATE
EXCLUSIVE

SHARE

SHARE
ROW
EXCLUSIVE

EXCLUSIVE

ACCESS
EXCLUSIVE

ACCESS SHARE

X

ROW SHARE

X
X

ROW EXCLUSIVE

X
X
X
X

SHARE UPDATE EXCLUSIVE

X
X
X
X
X

SHARE

X
X

X
X
X

SHARE ROW EXCLUSIVE

X
X
X
X
X
X

EXCLUSIVE

X
X
X
X
X
X
X

ACCESS EXCLUSIVE
X
X
X
X
X
X
X
X

Блокировки на уровне строк

В дополнение к блокировкам на уровне таблиц существуют блокировки на уровне строк, которые перечислены ниже с контекстами, в которых они автоматически используются QHB. Полный список конфликтов между блокировками уровня строк см. в таблице Конфликтующие блокировки на уровне строк. Обратите внимание, что одна транзакция может владеть конфликтующими блокировками одной строки, даже в разных субтранзакциях; но две разные транзакции не могут владеть конфликтующие блокировки одной и той же строки. Блокировки на уровне строк не влияют на запросы данных; они блокируют только записи и блокировки в одну и ту же строку. Блокировки уровня строк, как и блокировки уровня таблицы, освобождаются в конце транзакции или при откате к точке сохранения.

Режимы блокировки на уровне строк

FOR UPDATE

FOR UPDATE блокирует извлеченные командой SELECT строки как для изменения. Это предотвращает их блокировку, изменение или удаление другими транзакциями до завершения текущей транзакции. То есть другие транзакции, пытающиеся выполнить с этими строками UPDATE, DELETE, SELECT FOR UPDATE, SELECT FOR NO KEY UPDATE, SELECT FOR SHARE или SELECT FOR KEY SHARE, будут заблокированы до завершения текущей транзакции; и наоборот, SELECT FOR UPDATE будет ожидать параллельной транзакции, которая выполнила любую из этих команд в той же строке, а затем заблокирует и вернет измененную строку (или не вернет, если та была удалена). Однако в транзакции REPEATABLE READ или SERIALIZABLE возникнет ошибка, если строка, подлежащая блокировке, изменилась с момента запуска транзакции. Подробнее это рассматривается в подразделе Проверка согласованности данных на уровне приложения.

Режим блокировки FOR UPDATE также запрашивается на уровне строки любой командой DELETE, а также командой UPDATE, изменяющей значения в определенных столбцах. В настоящее время вариант с UPDATE предусмотрен для столбцов с уникальным индексом, который можно использовать во внешнем ключе (поэтому частичные индексы и индексы выражений не учитываются), но в будущем это может измениться.

FOR NO KEY UPDATE

Ведет себя аналогично FOR UPDATE, за исключением того, что полученная блокировка слабее: она не будет блокировать команды SELECT FOR KEY SHARE, которые пытаются получить блокировку в тех же строках. Также этот режим блокировки активируется любой командой UPDATE, которая не запрашивает блокировку FOR UPDATE.

FOR SHARE

Ведет себя аналогично FOR NO KEY UPDATE, за исключением того, что для каждой извлеченной строки запрашивается разделяемая, а не исключительная блокировка. Разделяемая блокировка блокирует в этих строках выполнение другими транзакциями команд UPDATE, DELETE, SELECT FOR UPDATE или SELECT FOR NO KEY UPDATE, но не препятствует выполнению SELECT FOR SHARE или SELECT FOR KEY SHARE.

FOR KEY SHARE

Ведет себя аналогично FOR SHARE, за исключением того, что эта блокировка слабее: она блокирует SELECT FOR UPDATE, но не SELECT FOR NO KEY UPDATE. Блокировка разделяемым ключом не дает другим транзакциям выполнить команду DELETE или любую команду UPDATE, изменяющую значения ключа, но не другие UPDATE, а также не препятствует выполнению SELECT FOR NO KEY UPDATE, SELECT FOR SHARE или SELECT FOR KEY SHARE.

QHB не хранит в памяти никакой информации об измененных строках, поэтому нет ограничений на количество строк, заблокированных за раз. Однако блокировка строки может привести к записи на диск, например, SELECT FOR UPDATE изменяет выбранные строки, чтобы пометить их как заблокированные, и в результате происходит запись на диск.

Таблица: Конфликтующие блокировки на уровне строк

Запрошенный режим блокировки | Текущий режим блокировки
FOR KEY SHARE | FOR SHARE | FOR NO KEY UPDATE | FOR UPDATE
FOR KEY SHARE |  |  |  | X
FOR SHARE |  |  | X | X
FOR NO KEY UPDATE |  | X | X | X
FOR UPDATE | X | X | X | X

Блокировки на уровне страницы

В дополнение к блокировкам уровня таблиц и строк для управления доступом на чтение/запись к страницам таблицы в общем пуле буферов используются
разделяемые/эксклюзивные блокировки на уровне страниц. Эти блокировки снимаются сразу после извлечения или изменения строки. Обычно разработчикам приложений нет необходимости иметь дело с блокировками на уровне страниц, но они упомянуты здесь для полноты картины.

Взаимные блокировки

Использование явной блокировки может увеличить вероятность взаимоблокировок, при которых каждая из двух (или более) транзакций удерживает блокировки, которые нужны другой. Например, если транзакция 1 получает эксклюзивную блокировку таблицы A, а затем пытается получить эксклюзивную блокировку таблицы B, которую до этого уже получила транзакция 2, в настоящий момент желающая получить получить эксклюзивную блокировку таблицы A, ни одна из этих транзакций не сможет продолжить работу. QHB автоматически выявляет случаи взаимоблокировки и разрешает их, прерывая одну из задействованных транзакций и позволяя другой(им) завершить работу. (Какая именно транзакция будет прервана, предсказать трудно, и на это не стоит полагаться.)

Обратите внимание, что взаимные блокировки также могут возникать в результате блокировок на уровне строк (а значит, они могут возникать, даже если в отсутствие явной блокировки). Рассмотрим случай, когда две параллельные транзакции изменяют таблицу. Первая транзакция выполняет:

UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 11111;

Она получает блокировку на уровне строк в строке с указанным номером счета. Затем вторая транзакция выполняет:

UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 22222;
UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 11111;

Первая команда UPDATE успешно получает блокировку на уровне строк в указанной строке, поэтому она успешно обновляет эту строку. Однако вторая команда UPDATE обнаруживает, что строка, которую она пытается изменить, уже заблокирована, поэтому она ожидает завершения транзакции, получившей блокировку. Теперь уже вторая транзакция ожидает завершения первой, прежде чем продолжить свою работу. Затем первая транзакция выполняет:

UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 22222;

Первая транзакция пытается получить блокировку на уровне строки в указанной строке, но не может: такой блокировкой уже владеет вторая транзакция. Поэтому она ожидает завершения второй транзакции. Таким образом, первая транзакция блокируется второй, а вторая — первой: возникает взаимоблокировка. QHB обнаружит эту ситуацию и прервет одну из транзакций.

Лучшая защита от взаимных блокировок, как правило, состоит в том, чтобы избежать их, добиваясь того, чтобы все приложения, обращающиеся к базе данных, запрашивали блокировки для нескольких объектов в согласованном порядке. В приведенном выше примере, если бы обе транзакции изменили строки в одном и том же порядке, взаимоблокировка не возникла бы. Следует также убедиться, что первой для объекта в транзакции запрашивается блокировка с наиболее ограничительным режимом, который потребуется для этого объекта. Если это невозможно проконтролировать заранее, то взаимные блокировки можно обработать на лету, повторяя прерывающиеся из-за них транзакции.

В случае, если ситуация взаимоблокировки не будет выявлена, транзакция, ищущая блокировку на уровне таблицы или на уровне строки, будет бесконечно долго ожидать освобождения конфликтующих блокировок. Это означает, что для приложения не стоит держать транзакции открытыми в течение длительных периодов времени (например, во время ожидания ввода данных пользователем).

Рекомендательные блокировки

QHB предоставляет средства для создания блокировок, смысл которых определяет приложение. Они называются рекомендательными блокировками, потому что система не навязывает их использование — правильное их применение зависит от самого приложения. Рекомендательные блокировки могут быть полезны для стратегий блокировки, которые плохо подходят для модели MVCC. Например, рекомендательные блокировки часто применяются для эмулирования стратегий пессимистической блокировки, типичных для так называемых систем управления данными «плоский файл». Хотя для той же цели можно использовать хранящийся в таблице флаг, рекомендательные блокировки работают быстрее, не раздувают таблицы и автоматически удаляются сервером в конце сеанса.

В QHB есть два способа получить рекомендательную блокировку: на уровне сеанса или на уровне транзакции. При получении на уровне сеанса рекомендательная блокировка удерживается до своего явного освобождения, либо до завершения сеанса. В отличие от стандартных запросов на блокировку, запросы на рекомендательную блокировку на уровне сеанса не учитывают семантику транзакции: блокировка, полученная в транзакции, будет удерживаться даже после отката последней, и аналогично освобождение блокировки будет действовать, даже если вызвавшая его транзакция позже завершится неудачей. Владеющий блокировкой процесс может вызывать ее несколько раз, причем еще до момента ее фактического снятия каждому выполненному запросу на блокировку должен соответствовать запрос на ее освобождение. С другой стороны, запросы на блокировку на уровне транзакции ведут себя скорее как обычные запросы на блокировку: они автоматически освобождаются в конце транзакции, и явной операции разблокировки не выполняется. Для кратковременного использования рекомендательной блокировки такое поведение зачастую более удобно, чем поведение на уровне сеанса. Запросы на уровне сеанса и на уровне транзакции для одного и того же идентификатора рекомендательной блокировки будут блокировать друг друга прогнозируемым образом. Если сеанс уже владеет данной рекомендательной блокировкой, дополнительные ее запросы от этого сеанса всегда будут успешными, даже если эту блокировку ожидают другие сеансы; это утверждение верно независимо от того, на каком уровне — сеанса или транзакции — удерживается или заново запрашивается существующая блокировка.

Как и все остальные блокировки в QHB, полный список рекомендательных блокировок, которыми в настоящее время владеют любые сеансы, можно найти в системном представлении pg_locks.

Как рекомендательные, так и обычные блокировки хранятся в пуле общей памяти, размер которой определяется переменными конфигурации max_locks_per_transaction и max_connections. Необходимо соблюдать осторожность, чтобы не исчерпать эту память, иначе сервер вообще не сможет предоставить никаких блокировок. Это ограничивает количество рекомендательных блокировок, предоставляемых сервером: обычно от десятков до сотен тысяч в зависимости от конфигурации сервера.

В некоторых случаях, используя рекомендательные методы блокировки, особенно в запросах, включающих явное упорядочение и предложения LIMIT, необходимо тщательно контролировать блокировки, получаемые в зависимости от порядка вычисления выражений SQL. Например:

SELECT pg_advisory_lock(id) FROM foo WHERE id = 12345; -- ok
SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100; -- опасно!
SELECT pg_advisory_lock(q.id) FROM
(
  SELECT id FROM foo WHERE id > 12345 LIMIT 100
) q; -- ok

В приведенных выше запросах вторая форма опасна, поскольку LIMIT не обязательно будет применен до выполнения функции блокировки. Это может привести к получению некоторых блокировок, которые приложение не ожидает и, следовательно, не сможет освободить (пока не завершится сеанс). С точки зрения приложения такие блокировки оказались бы в подвешенном состоянии, хоть и остались бы видимыми в pg_locks.

Функции, предоставляемые для управления рекомендательными блокировками, описаны в разделе Функции рекомендательных блокировок.

Проверка согласованности данных на уровне приложения

Используя транзакции Read Committed, очень сложно обеспечить деловой регламент в отношении целостности данных, поскольку представление данных смещается с каждым оператором, и даже один оператор может не ограничиваться своим снимком состояния, если возникает конфликт записи.

Хотя транзакция Repeatable Read в процессе выполнения имеет стабильное представление данных, существует небольшая проблема с использованием снимков MVCC для проверок согласованности данных, связанных с так называемыми конфликтами чтения/записи. Если одна транзакция записывает данные, а параллельная транзакция пытается их прочитать (до или после записи), она не может увидеть работу другой транзакции. В этом случае создается впечатление, что читающая транзакция выполнилась первой, независимо от того, какая из них началась или зафиксировалась раньше. Если дело ограничивается только этим, проблем нет, но если читающая транзакция помимо этого записывает данные, которые считываются параллельной транзакцией, то теперь уже эта транзакция будет исполняться так, будто запустилась раньше обеих вышеупомянутых транзакций. Если же транзакция, которая вроде как выполнялась последней, на самом деле фиксируется первой, то в графе порядка выполнения транзакций легко может возникнуть цикл. При его появлении проверки согласованности не будут работать правильно без посторонней помощи.

Как показано в разделе Подзапросы, транзакции Serializable — это те же транзакции Repeatable Read, дополненные неблокирующим мониторингом опасных шаблонов конфликтов чтения/записи. При обнаружении шаблона, который может вызвать цикл в видимом порядке выполнения, одна из вовлеченных транзакций откатывается, чтобы прервать цикл.

Обеспечение согласованности с сериализуемыми транзакциями

Если для всех операций записи и чтения, которые требуют согласованного просмотра данных, используются транзакции уровня изоляции Serializable, то согласованность обеспечивается без каких-либо дополнительных усилий. Программное обеспечение из других сред, использующее для обеспечения согласованности сериализуемые транзакции, в QHB в этом смысле должно «просто работать».

Использование этого метода позволит программистам приложений избежать ненужной нагрузки, если программное обеспечение приложения проходит через инфраструктуру, которая автоматически повторяет транзакции, откатывающиеся с ошибкой сериализации. Может быть полезно задать параметру default_transaction_isolation значение serializable (т. е. сделать Serializable уровнем изоляции по умолчанию). Также было бы разумно предпринять некоторые действия для предотвращения использования других уровней изоляции (непреднамеренно или для нарушения проверок согласованности) проверяя уровень изоляции транзакции в триггерах.

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

Предупреждение!

Уровень защиты согласованности с использованием сериализуемых транзакций еще не поддерживается в режиме горячего резервирования. Из-за этого там, где используется горячее резервирование, может понадобиться применить уровень Repeatable Read и явную блокировку на главном сервере.

Обеспечение согласованности с помощью явных блокировок

Когда возможны несериализуемые записи, для обеспечения текущей достоверности строки и ее защиты от параллельных изменений, необходимо использовать SELECT FOR UPDATE, SELECT FOR SHARE или соответствующую команду LOCK TABLE. (SELECT FOR UPDATE и SELECT FOR SHARE блокируют от параллельных изменений только возвращенные строки, тогда как LOCK TABLE блокирует всю таблицу.) Это следует учитывать при переносе приложений на QHB из других сред.

Для тех, кто конвертирует из других сред, также следует отметить тот факт, что применение SELECT FOR UPDATE не гарантирует, что параллельная транзакция не станет изменять или удалять выбранную строку. Для получения такой гарантии в QHB нужно изменить строку, даже если нет необходимости менять значение. SELECT FOR UPDATE временно блокирует другие транзакции, не давая им получить ту же блокировку или выполнить UPDATE или DELETE, которые могут повлиять на заблокированную строку, но как только транзакция, удерживающая эту блокировку, фиксируется или откатывается, заблокированная транзакция продолжит выполнять конфликтующую операцию, если только во время удержания блокировки для данной строки действительно не был выполнен UPDATE.

Глобальные проверки достоверности использованием несериализуемых MVCC требуют большей продуманности. Например, банковскому приложению может понадобиться проверить, что сумма всех кредитов в одной таблице равна сумме дебетов в другой, во время активного изменения обеих таблиц. Сравнение результатов двух последовательных команд SELECT sum(...) в режиме Read Committed не будет достоверным, поскольку второй запрос, скорее всего, будет включать результаты транзакций, не учитываемые первой. Подсчет двух сумм в одной транзакции Repeatable Read даст точную картину результатов действий только тех транзакций, которые были зафиксированы до начала данной — но можно с полным основанием задаться вопросом, будет ли ответ по-прежнему актуален на момент его выдачи. Если транзакция Repeatable Read сама внесла какие-то изменения, прежде чем попыталась проверить согласованность, полезность это проверки становится еще более сомнительной, поскольку теперь она включает некоторые, но не все изменения, случившиеся после запуска транзакции. В таких случаях осмотрительный человек может захотеть заблокировать все задействованные в проверке таблицы, чтобы получить бесспорную картину реальной действительности. Блокировка в режиме SHARE (или более строгая) гарантирует отсутствие незафиксированных изменений в заблокированной таблице, за исключением тех, что внесла текущая транзакция.

Также обратите внимание, что при использовании явных блокировок для предотвращения параллельных изменений следует либо применять режим Read Committed, либо в режиме Repeatable Read обязательно получить блокировки перед выполнением запросов. Блокировка, полученная транзакцией Repeatable Read, гарантирует, что никакие другие транзакции, изменяющие таблицу не выполняются, но если снимок состояния, видимый транзакцией, предшествует получению блокировки, в нем могут отсутствовать некоторые уже зафиксированные на данный момент изменения в таблице. Снимок транзакции Repeatable Read фактически создается в начале ее первой команды запроса или изменения данных (SELECT, INSERT, UPDATE или DELETE), поэтому получить явные блокировки можно до его создания.

Ограничения

Некоторые команды DDL (в настоящее время это только TRUNCATE и формы ALTER TABLE, перезаписывающие таблицу) небезопасны с точки зрения MVCC. Это означает, что после фиксации усечения или перезаписи таблица отобразится пустой для параллельных транзакций, если они используют снимок, сделанный перед фиксацией команды DDL. Это станет проблемой только для транзакции, которая не обращалась к рассматриваемой таблице до запуска команды DDL — любая транзакция, которая обращалась к ней раньше, получила бы по меньшей мере блокировку таблицы ACCESS SHARE, которая заблокировала бы эту команду DDL до завершения данной транзакции. Таким образом, эти команды не повлекут за собой каких-либо явных несоответствий с содержанием таблицы для последовательных запросов к целевой таблице, но могут вызвать видимое несоответствие между содержимым целевой таблицы и другими таблицами в базе данных.

Поддержка уровня изоляции транзакций Serializable пока еще не реализована для целевых серверов репликации с горячим резервированием. Самым строгим уровнем изоляции, поддерживаемым в настоящее время в режиме горячего резервирования, является Repeatable Read. Хотя выполнение всех постоянных операций записи в базу данных на главном сервере в транзакциях Serializable гарантирует, что все резервные серверы в конечном итоге достигнут согласованного состояния, транзакция Repeatable Read, выполняемая на резервном сервере, иногда может видеть переходное состояние, не согласующееся ни с одним результатом последовательного выполнения транзакций на главном сервере.

Внутреннее обращение к системным каталогам проводится за рамками уровня изоляции текущей транзакции. Это означает, что вновь созданные объекты базы данных, например таблицы, являются видимыми для параллельных транзакций Repeatable Read и Serializable, несмотря на то, что строки в них не видны. Напротив, запросы, которые просматривают системные каталоги напрямую, не видят строки, представляющие параллельно созданные объекты базы данных, если используют более высокие уровни изоляции.

Блокировка и индексы

Хотя QHB обеспечивает неблокирующий доступ на чтение/запись к данным таблицы, на данный момент такой доступ поддерживается не для всех методов индексного доступа, реализованных в QHB. Различные типы индексов обрабатываются следующим образом:

Индексы B-деревья, GiST и SP-GiST

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

Хеш-индексы

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

Индексы GIN

Для доступа на чтение/запись используются краткосрочные разделяемые/исключительные блокировки на уровне страницы. Блокировки освобождаются сразу после извлечения или добавления каждой строки индекса. Но обратите внимание, что добавление значения в поле с GIN-индексом обычно приводит к нескольким добавлениям ключа индекса в каждую строку, поэтому GIN может проделывать значительную работу для вставки всего одного значения.

В настоящее время для параллельных приложений наилучшую производительность обеспечивают индексы B-деревья; поскольку они также имеют больше функций, чем хеш-индексы, именно их рекомендуется использовать для параллельных приложений, которым необходимо индексировать скалярные данные. При работе с нескалярными данными B-деревья бесполезны, и вместо них следует использовать индексы GiST, SP-GiST или GIN.

Большие объекты

В QHB имеется механизм для работы с большими объектами, который обеспечивает потоковый доступ к пользовательским данным, хранящимся в специальной структуре больших объектов. Потоковый доступ полезен при работе со значениями данных, которые слишком велики, чтобы с легкостью обрабатывать их как единое целое.

В этой главе описываются реализация и интерфейсы языка программирования и запросов к данным больших объектов в QHB. В этой главе для примеров используется библиотека libpq для языка C, но ту же функциональность поддерживает большинство программных интерфейсов QHB. Другие интерфейсы могут использовать внутри себя интерфейс больших объектов для обеспечения универсального подхода к работе с большими значениями. Здесь это не описано.

Краткая справка по большим объектам

Все большие объекты хранятся в одной системной таблице с именем pg_largeobject. Каждый большой объект также имеет запись в системной таблице pg_largeobject_metadata. Большие объекты можно создавать, изменять и удалять с помощью API чтения/записи, аналогичного стандартным операциям с файлами.

QHB также поддерживает систему хранения под названием «TOAST», которая автоматически сохраняет значения, превышающие одну страницу базы данных, во вторичную область хранения для каждой таблицы. Из-за этого механизм для работы с большими объектами оказывается отчасти устаревшим. Одно из оставшихся преимуществ больших объектов заключается в том, что они позволяют использовать значения размером до 4 ТБ, в то время как поля в TOAST могут быть размером не более 1 ГБ. Кроме того, чтение и изменение большого объекта можно успешно выполнять по частям, тогда как при большинстве операций с полем в TOAST значение будет считываться или записываться как единое целое.

Особенности реализации

Программная реализация работы с большими объектами состоит в том, что те разбиваются на «фрагменты», которые сохраняются в строках таблиц в базе данных. При произвольном доступе на чтение и запись быстрый поиск правильного номера фрагмента обеспечивает индекс B-дерево.

Фрагменты сохраненного большого объекта необязательно должны быть смежными. Например, если приложение открывает новый большой объект, перемещается к смещению 1000000 байт и записывает там несколько байт, это не приводит к выделению дополнительных 1000000 байт в хранилище; там будут размещены только фрагменты, охватывающие диапазон фактически записанных байт данных. Однако операция чтения будет считывать нули для любых нераспределенных участков, предшествующих последнему существующему фрагменту. Это соответствует обычному поведению «разреженных» файлов в файловых системах Unix.

У больших объектов имеется владелец и набор прав доступа, которыми можно управлять командами GRANT и REVOKE. Для чтения большого объекта требуются права SELECT, а для записи или усечения — права UPDATE. Удалять большой объект, добавлять для него комментарий или менять его владельца может только его владелец (или суперпользователь базы данных). Это поведение можно скорректировать, изменив параметр времени выполнения lo_compat_privileges.

Клиентские интерфейсы

В этом разделе описываются средства, которые библиотека клиентского интерфейса libpq QHB предоставляет для доступа к большим объектам. Интерфейс больших объектов QHB похож на интерфейс файловой системы Unix, с аналогами функций open, read, write, lseek и т. д.

Все операции с большими объектами, использующие эти функции, должны выполняться в блоке транзакций SQL, поскольку дескрипторы файлов больших объектов действительны только в течение транзакции.

Если при выполнении любой из этих функций возникает ошибка, то функция возвращает значение, в ином случае невозможное, обычно это 0 или -1. Сообщение, описывающее ошибку, хранится в объекте соединения и может быть извлечено с помощью PQerrorMessage.

Клиентские приложения, использующие эти функции, должны включать файл заголовка libpq/libpq-fs.h и соединяться с библиотекой libpq.

Создание большого объекта

Функция

Oid lo_creat(PGconn *conn, int mode);

создает новый большой объект. Возвращаемое значение — это OID, присвоенный новому крупному объекту, или InvalidOid (ноль) при сбое. Аргумент mode не используется и игнорируется, однако для обратной совместимости лучше задать в нем значение INV_READ (чтение), INV_WRITE (запись) или INV_READ | INV_WRITE (чтение/запись). (Эти символьные константы определены в заголовочном файле libpq/libpq-fs.х.)

Пример:

inv_oid = lo_creat(conn, INV_READ|INV_WRITE);

Функция

Oid lo_create(PGconn *conn, Oid lobjId);

также создает новый крупный объект. Назначаемый OID может быть задан с помощью lobjId; если этот OID уже используется для некоторого большого объекта, происходит ошибка. Если lobjId имеет значение InvalidOid (ноль), то lo_create назначает неиспользуемый OID (это поведение аналогично lo_creat). Возвращаемое значение — это OID, присвоенный новому крупному объекту, или InvalidOid (ноль) при ошибке.

Пример:

inv_oid = lo_create(conn, desired_oid);

Импорт большого объекта

Чтобы импортировать в качестве большого объекта файл операционной системы, вызовите:

Oid lo_import(PGconn *conn, const char *filename);

в filename указывается имя файла операционной системы, который будет импортирован как большой объект. Возвращаемое значение — это OID, присвоенный новому крупному объекту, или InvalidOid (ноль) при ошибке. Обратите внимание, что файл считывается библиотекой клиентского интерфейса, а не сервером, поэтому он должен существовать в файловой системе клиента и быть доступен для чтения клиентским приложением.

Функция

Oid lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId);

также импортирует новый крупный объект. Назначаемый OID может быть задан с помощью lobjId; если этот OID уже используется для некоторого большого объекта, то происходит ошибка. Если lobjId имеет значение InvalidOid (ноль), то lo_import_with_oid назначает неиспользуемый OID (это поведение аналогично lo_import). Возвращаемое значение — это OID, присвоенный новому крупному объекту, или InvalidOid (ноль) при ошибке.

Экспорт большого объекта

Чтобы экспортировать большой объект в файл операционной системы, вызовите

int lo_export(PGconn *conn, Oid lobjId, const char *filename);

В аргументе lobjId указывается OID большого объекта для экспорта, а в аргументе имя файла — имя файла в операционной системе. Обратите внимание, что файл записывается библиотекой клиентского интерфейса, а не сервером. Возвращает 1 при успешном выполнении, -1 при ошибке.

Открытие существующего большого объекта

Чтобы открыть существующий большой объект для чтения или записи, вызовите

int lo_open(PGconn *conn, Oid lobjId, int mode);

В аргументе lobjId указывается OID большого объекта для открытия. Биты в аргументе mode определяют, будет ли объект открыт для чтения (INV_READ), записи (INV_WRITE) или и того и другого. (Эти символьные константы определены в заголовочном файле libpq/libpq-fs.х.) lo_open возвращает дескриптор большого объекта (неотрицательный) для последующего использования в lo_read, lo_write, lo_lseek, lo_lseek64, lo_tell, lo_tell64, lo_truncate, lo_truncate64 и lo_close. Дескриптор действителен только во время выполнения текущей транзакции. При ошибке возвращается значение -1.

В настоящее время сервер не различает режимы INV_WRITE и INV_READ | INV_WRITE: читать данные из дескриптора разрешено в любом случае. Однако между этими режимами и одиночным режимом INV_READ есть существенная разница: в режиме INV_READ записывать данные в дескриптор нельзя, а данные, считанные из него, будут отражать содержимое большого объекта во время снимка состояния транзакции, который был активен при выполнении lo_open, независимо от последующих записей, сделанных этой или другими транзакциями. Чтение из дескриптора, открытого с помощью INV_WRITE, возвращает данные, отражающие все операции записи других зафиксированных транзакций, а также операции записи текущей транзакции. Это похоже на отличия режимов REPEATABLE READ и READ COMMITTED для обычных команд SQL SELECT.

Функция lo_open завершится ошибкой, пользователь не имеет права SELECT для большого объекта или если указан режим INV_WRITE и отсутствует право UPDATE. Эти проверки наличия прав можно выключить с помощью параметра времени выполнения lo_compat_privileges.

Пример:

inv_fd = lo_open(conn, inv_oid, INV_READ|INV_WRITE);

Запись данных в большой объект

Функция

int lo_write(PGconn *conn, int fd, const char *buf, size_t len);

записывает len байт из буфера buf (который должен иметь размер len) в дескриптор большого объекта fd. Аргумент fd должен быть возвращен предыдущим вызовом lo_open. Возвращается количество фактически записанных байт (в текущей реализации это значение всегда будет равно len, если только нет ошибки). В случае ошибки возвращаемое значение равно -1.

Хотя параметр len объявляется как size_t, эта функция будет отклонять значения длины, превышающие INT_MAX. На практике все равно лучше всего передавать данные фрагментами не больше нескольких мегабайт.

Чтение данных из большого объекта

Функция

int lo_read(PGconn *conn, int fd, char *buf, size_t len);

читает до len байт из дескриптора большого объекта fd в буфер buf (который должен иметь размер len). Аргумент fd должен быть возвращен предыдущим вызовом lo_open. Возвращается количество фактически прочитанных байт; это число будет меньше len, если большой объект уже был прочитан до конца. В случае ошибки возвращаемое значение равно -1.

Хотя параметр len объявляется как size_t, эта функция будет отклонять значения длины, превышающие INT_MAX. На практике все равно лучше всего передавать данные фрагментами не больше нескольких мегабайт.

Перемещение в большом объекте

Чтобы изменить текущее местоположение чтения или записи, связанное с дескриптором большого объекта, вызовите

int lo_lseek(PGconn *conn, int fd, int offset, int whence);

Эта функция перемещает указатель текущего местоположения для дескриптора большого объекта, идентифицированного с помощью fd, к новому местоположению, заданному аргументом offset. Допустимыми значениями для аргумента whence являются SEEK_SET (перемещение от начала объекта), SEEK_CUR (перемещение с текущей позиции) и SEEK_END (перемещение от конца объекта). Возвращаемое значение — это новый указатель местоположения или -1 при ошибке.

При работе с большими объектами, размер которых может превышать 2 ГБ, вместо этого используйте

pg_int64 lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence);

Эта функция ведет себя так же, как и lo_lseek, но может принять значение offset, превышающее 2 ГБ и/или вернуть результат, превышающий 2 ГБ. Обратите внимание, что если новый указатель местоположения больше 2 ГБ, то lo_lseek выдаст ошибку.

Получение текущего положения в большом объекте

Чтобы получить текущее местоположение чтения или записи для дескриптора большого объекта, вызовите

int lo_tell(PGconn *conn, int fd);

Если возникает ошибка, возвращаемое значение равно -1.

При работе с большими объектами, размер которых может превышать 2 ГБ, вместо этого используйте

pg_int64 lo_tell64(PGconn *conn, int fd);

Эта функция ведет себя так же, как и lo_tell, но может вернуть результат, превышающий 2 ГБ. Обратите внимание, что если текущее местоположение чтения/записи больше 2 ГБ, то lo_tell выдаст ошибку.

Усечение большого объекта

Чтобы усечь большой объект до заданной длины, вызовите

int lo_truncate(PGcon *conn, int fd, size_t len);

Эта функция усекает дескриптор большого объекта fd до длины len. Аргумент fd должен быть возвращен предыдущим вызовом lo_open. Если len превышает текущую длину большого объекта, тот расширяется до заданной длины с нулевыми байтами (’\0’). В случае успеха lo_truncate возвращает нуль. При ошибке возвращается значение -1.

Местоположение чтения/записи, связанное с дескриптором fd, не изменяется.

Хотя параметр len объявлен как size_t, lo_truncate будет отклонять значения длины, превышающие INT_MAX.

При работе с большими объектами, размер которых может превышать 2 ГБ, вместо этого используйте

int lo_truncate64(PGcon *conn, int fd, pg_int64 len);

Эта функция ведет себя так же, как и lo_truncate, но может принимать значение len, превышающее 2 ГБ.

Закрытие дескриптора большого объекта

Дескриптор большого объекта можно закрыть, вызвав

int lo_close(PGconn *conn, int fd);

где fd — это дескриптор большого объекта, возвращенный функцией lo_open. В случае успеха lo_close возвращает нуль. При ошибке возвращается значение -1.

Все дескрипторы больших объектов, которые остаются открытыми в конце транзакции, будут закрыты автоматически.

Удаление большого объекта

Чтобы удалить большой объект из базы данных, вызовите

int lo_unlink(PGconn *conn, Oid lobjId);

В аргументе lobjId указывается OID большого объекта, подлежащего удалению. Возвращает 1 в случае успеха и -1 при ошибке.

Серверные функции

Серверные функции, предназначенные для работы с большими объектами из SQL, перечислены в таблице «SQL-ориентированные функции больших объектов».

Таблица: SQL-ориентированные функции больших объектов

Функция | Возвращаемый тип | Описание | Пример | Результат
lo_from_bytea(loid oid, string bytea) | oid | Создает большой объект и сохраняет в нем данные, возвращая его OID. Если loid равен нулю, система выберет OID сама. | lo_from_bytea(0, ’\\xffffff00’) | 24528
lo_put(loid oid, offset bigint, str bytea) | void | Записывает данные по заданному смещению. | lo_put(24528, 1, ’\\xaa’) |
lo_get(loid oid [, from bigint, for int]) | bytea | Извлекает содержимое или его подстроку. | lo_get(24528, 0, 3) | \xffaaff

Каждой из описанных ранее клиентских функций соответствуют дополнительные серверные функции; на самом деле, по большей части клиентские функции представляют собой просто интерфейсы к аналогичным серверным функциям. К функциям, которые так же удобно вызывать с помощью команд SQL, являются lo_creat, lo_create, lo_unlink, lo_import и lo_export. Вот примеры их использования:

CREATE TABLE image (
    name            text,
    raster          oid
);

SELECT lo_creat(-1);       -- возвращает OID нового, пустого большого объекта

SELECT lo_create(43213);   -- пытается создать большой объект с OID 43213

SELECT lo_unlink(173454);  -- удаляет большой объект с OID 173454

INSERT INTO image (name, raster)
    VALUES ('beautiful image', lo_import('/etc/motd'));

INSERT INTO image (name, raster)  -- то же, что выше, но с предопределенным OID
    VALUES ('beautiful image', lo_import('/etc/motd', 68583));

SELECT lo_export(image.raster, '/tmp/motd') FROM image
    WHERE name = 'beautiful image';

Серверные функции lo_import и lo_export ведут себя совершенно иначе, нежели их аналоги на стороне клиента. Эти две функции читают и записывают файлы в файловой системе сервера, используя разрешения пользователя-владельца базы данных. Поэтому по умолчанию их могут использовать только суперпользователи. Напротив, функции импорта и экспорта на стороне клиента считывают и записывают файлы в файловой системе клиента, используя разрешения клиентской программы. Выполнение клиентских функций не требует никаких прав доступа к базе данных, за исключением права на чтение или запись соответствующего большого объекта.

Внимание!
С помощью команды GRANT можно разрешить использование серверных функций lo_import и lo_export не только суперпользователям, но при этом необходимо внимательно рассмотреть последствия для безопасности. Злонамеренный пользователь, имеющий такие права, может с легкостью воспользоваться ими, чтобы стать суперпользователем (например, путем перезаписи файлов конфигурации сервера), или атаковать остальную файловую систему сервера, не утруждаясь получением собственно прав суперпользователя базы данных. Поэтому доступ к ролям, имеющим такие права, следует контролировать так же тщательно, как и доступ к ролям суперпользователя. Тем не менее, если для выполнения некоторых рутинных задач требуется применение серверных функций lo_import или lo_export, безопаснее использовать роль с такими правами, чем с полными правами суперпользователя, так как это помогает снизить риск сбоев от случайных ошибок.

Функциональные возможности lo_read и lo_write также предоставляются через вызовы на стороне сервера, но имена серверных функций, в отличие от имен интерфейсов на стороне клиента, не содержат подчеркиваний. Эти функции следует вызывать как loread и lowrite.

Пример программы

Данный пример — это простая программа, которая показывает, как можно использовать интерфейс больших объектов в libpq. Части этой программы закомментированы, но оставлены в источнике для удобства читателя. Эту программу также можно найти в src/test/examples/testlo.с в исходном дистрибутиве.

Большие объекты с примером программы libpq

/*-------------------------------------------------------------------------
 *
 * testlo.c
 *    test using large objects with libpq
 *
 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    src/test/examples/testlo.c
 *
 *-------------------------------------------------------------------------
 */
#include <stdio.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "libpq-fe.h"
#include "libpq/libpq-fs.h"

#define BUFSIZE         1024

/*
 * importFile -
 *    import file "in_filename" into database as large object "lobjOid"
 *
 */
static Oid
importFile(PGconn *conn, char *filename)
{
    Oid         lobjId;
    int         lobj_fd;
    char        buf[BUFSIZE];
    int         nbytes,
                tmp;
    int         fd;

    /*
     * open the file to be read in
     */
    fd = open(filename, O_RDONLY, 0666);
    if (fd < 0)
    {                           /* error */
        fprintf(stderr, "cannot open unix file\"%s\"\n", filename);
    }

    /*
     * create the large object
     */
    lobjId = lo_creat(conn, INV_READ | INV_WRITE);
    if (lobjId == 0)
        fprintf(stderr, "cannot create large object");

    lobj_fd = lo_open(conn, lobjId, INV_WRITE);

    /*
     * read in from the Unix file and write to the inversion file
     */
    while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
    {
        tmp = lo_write(conn, lobj_fd, buf, nbytes);
        if (tmp < nbytes)
            fprintf(stderr, "error while reading \"%s\"", filename);
    }

    close(fd);
    lo_close(conn, lobj_fd);

    return lobjId;
}

static void
pickout(PGconn *conn, Oid lobjId, int start, int len)
{
    int         lobj_fd;
    char       *buf;
    int         nbytes;
    int         nread;

    lobj_fd = lo_open(conn, lobjId, INV_READ);
    if (lobj_fd < 0)
        fprintf(stderr, "cannot open large object %u", lobjId);

    lo_lseek(conn, lobj_fd, start, SEEK_SET);
    buf = malloc(len + 1);

    nread = 0;
    while (len - nread > 0)
    {
        nbytes = lo_read(conn, lobj_fd, buf, len - nread);
        buf[nbytes] = '\0';
        fprintf(stderr, ">>> %s", buf);
        nread += nbytes;
        if (nbytes <= 0)
            break;              /* no more data? */
    }
    free(buf);
    fprintf(stderr, "\n");
    lo_close(conn, lobj_fd);
}

static void
overwrite(PGconn *conn, Oid lobjId, int start, int len)
{
    int         lobj_fd;
    char       *buf;
    int         nbytes;
    int         nwritten;
    int         i;

    lobj_fd = lo_open(conn, lobjId, INV_WRITE);
    if (lobj_fd < 0)
        fprintf(stderr, "cannot open large object %u", lobjId);

    lo_lseek(conn, lobj_fd, start, SEEK_SET);
    buf = malloc(len + 1);

    for (i = 0; i < len; i++)
        buf[i] = 'X';
    buf[i] = '\0';

    nwritten = 0;
    while (len - nwritten > 0)
    {
        nbytes = lo_write(conn, lobj_fd, buf + nwritten, len - nwritten);
        nwritten += nbytes;
        if (nbytes <= 0)
        {
            fprintf(stderr, "\nWRITE FAILED!\n");
            break;
        }
    }
    free(buf);
    fprintf(stderr, "\n");
    lo_close(conn, lobj_fd);
}

/*
 * exportFile -
 *    export large object "lobjOid" to file "out_filename"
 *
 */
static void
exportFile(PGconn *conn, Oid lobjId, char *filename)
{
    int         lobj_fd;
    char        buf[BUFSIZE];
    int         nbytes,
                tmp;
    int         fd;

    /*
     * open the large object
     */
    lobj_fd = lo_open(conn, lobjId, INV_READ);
    if (lobj_fd < 0)
        fprintf(stderr, "cannot open large object %u", lobjId);

    /*
     * open the file to be written to
     */
    fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
    if (fd < 0)
    {                           /* error */
        fprintf(stderr, "cannot open unix file\"%s\"",
                filename);
    }

    /*
     * read in from the inversion file and write to the Unix file
     */
    while ((nbytes = lo_read(conn, lobj_fd, buf, BUFSIZE)) > 0)
    {
        tmp = write(fd, buf, nbytes);
        if (tmp < nbytes)
        {
            fprintf(stderr, "error while writing \"%s\"",
                    filename);
        }
    }

    lo_close(conn, lobj_fd);
    close(fd);

    return;
}

static void
exit_nicely(PGconn *conn)
{
    PQfinish(conn);
    exit(1);
}

int
main(int argc, char **argv)
{
    char       *in_filename,
               *out_filename;
    char       *database;
    Oid         lobjOid;
    PGconn     *conn;
    PGresult   *res;

    if (argc != 4)
    {
        fprintf(stderr, "Usage: %s database_name in_filename out_filename\n",
                argv[0]);
        exit(1);
    }

    database = argv[1];
    in_filename = argv[2];
    out_filename = argv[3];

    /*
     * set up the connection
     */
    conn = PQsetdb(NULL, NULL, NULL, NULL, database);

    /* check to see that the backend connection was successfully made */
    if (PQstatus(conn) != CONNECTION_OK)
    {
        fprintf(stderr, "Connection to database failed: %s",
                PQerrorMessage(conn));
        exit_nicely(conn);
    }

    /* Set always-secure search path, so malicious users can't take control. */
    res = PQexec(conn,
                 "SELECT pg_catalog.set_config('search_path', '', false)");
    if (PQresultStatus(res) != PGRES_TUPLES_OK)
    {
        fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
        PQclear(res);
        exit_nicely(conn);
    }
    PQclear(res);

    res = PQexec(conn, "begin");
    PQclear(res);
    printf("importing file \"%s\" ...\n", in_filename);
/*  lobjOid = importFile(conn, in_filename); */
    lobjOid = lo_import(conn, in_filename);
    if (lobjOid == 0)
        fprintf(stderr, "%s\n", PQerrorMessage(conn));
    else
    {
        printf("\tas large object %u.\n", lobjOid);

        printf("picking out bytes 1000-2000 of the large object\n");
        pickout(conn, lobjOid, 1000, 1000);

        printf("overwriting bytes 1000-2000 of the large object with X's\n");
        overwrite(conn, lobjOid, 1000, 1000);

        printf("exporting large object to file \"%s\" ...\n", out_filename);
/*      exportFile(conn, lobjOid, out_filename); */
        if (lo_export(conn, lobjOid, out_filename) < 0)
            fprintf(stderr, "%s\n", PQerrorMessage(conn));
    }

    res = PQexec(conn, "end");
    PQclear(res);
    PQfinish(conn);
    return 0;
}

Серверное программирование

Эта часть документации посвящена расширению функциональности сервера путем реализации пользовательских функций, типов данных, триггеров и т. д. Это сложные темы, к которым рекомендуется переходить только после изучения всей остальной документации для пользователей QHB.

Примечание
В других частях, в главах Процедурные языки и PL/pgSQL — процедурный язык SQL описываются языки программирования на стороне сервера, доступные в дистрибутиве QHB, а также общие вопросы, касающиеся языков программирования на стороне сервера. Но прежде чем погружаться в этот материал, важно изучить разделы главы Расширение SQL, где рассматриваются функции.

Содержание

Расширение SQL

Триггеры

Триггеры событий

Интерфейс программирования сервера

Фоновые рабочие процессы

Расширение SQL

В следующих разделах мы обсудим, как можно расширить язык запросов SQL в QHB посредством добавления:

функций (начиная с раздела Пользовательские функции)

агрегатов (начиная с раздела Пользовательские агрегаты)

типов данных (начиная с раздела Пользовательские типы)

операторов (начиная с раздела Пользовательские операторы)

классов операторов для индексов (начиная с раздела Интерфейсные расширения для индексов)

пакетов связанных объектов (начиная с раздела Упаковка связанных объектов в расширение)

Как работает расширяемость

QHB является расширяемым, потому что его работа управляется каталогами. Если вы знакомы со стандартными реляционными системами баз данных, то знаете, что они хранят информацию о базах данных, таблицах, столбцах и т. д. в так называемых системных каталогах (в некоторых системах называемых словарем данных). Каталоги доступны пользователю в виде таблиц, похожих на любые другие таблицы, но СУБД хранит в них свою внутреннюю отчетность. Одним из ключевых отличий QHB от стандартных систем баз данных состоит в том, что QHB хранит в своих каталогах гораздо больше информации: не только о таблицах и столбцах, но и обо всех существующих типах данных, функциях, методах доступа и тому подобном. Эти таблицы могут быть изменены пользователем, а поскольку работа QHB основана на этих таблицах, это значит, что пользователи могут расширять QHB. Для сравнения: обычные системы баз данных можно расширить, только изменив жестко закодированные процедуры в их исходном коде или загрузив модули, специально написанные поставщиком СУБД.

Более того, сервер QHB может динамически загружать в себя код, написанный пользователем. То есть пользователь может выбрать файл с объектным кодом (например, разделяемую библиотеку), который реализует новый тип или функцию, и QHB по мере необходимости загрузит его. Код, написанный на SQL, добавить на сервер еще проще. Эта способность изменять свою работу «на лету» делает QHB исключительно подходящим для быстрого создания прототипов новых приложений и структур хранения.

Система типов QHB

Типы данных QHB можно разделить на базовые типы, контейнерные типы, домены и псевдотипы.

Базовые типы

Базовые типы — это типы, такие как integer, которые реализуются ниже уровня языка SQL (обычно на низкоуровневом языке, например, C или Rust). В целом они соответствуют тому, что часто называют абстрактными типами данных.

Встроенные базовые типы описаны в главе Типы данных.

Перечислимые типы (enum) можно рассматривать как подкатегорию базовых типов. Основное различие в том, что их можно создать, используя одни только SQL-команды, без какого-либо низкоуровневого программирования. Дополнительную информацию см. в разделе Перечислимые типы.

Контейнерные типы

В QHB есть три вида «контейнерных» типов, т.е. типов, которые содержат внутри себя несколько значений другого типа. Это массивы, составные типы и диапазоны.

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

Составные типы, или типы строк, создаются всякий раз, когда пользователь создает таблицу. Кроме того, с помощью команды CREATE TYPE можно создать «независимый» составной тип без привязки к таблице. Составной тип — это просто список типов со связанными именами полей. Значением составного типа является строка или запись из значений полей. Дополнительную информацию см. в разделе Составные типы.

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

Домены

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

Псевдотипы

Существует несколько «псевдотипов» для особых целей. Псевдотипы не могут выступать в качестве столбцов таблиц или составляющих контейнерных типов, но их можно использовать для объявления аргументов и результирующих типов функций. Это обеспечивает в системе типов механизм, позволяющий идентифицировать особые классы функций. В таблице Псевдотипы перечислены существующие псевдотипы.

Полиморфные типы

Особый интерес представляют пять псевдотипов: anyelement, anyarray, anynonarray, anyenum и anyrange, которые в совокупности называются полиморфными типами. Любая функция, объявленная с использованием этих типов, называется полиморфной функцией. Полиморфная функция может работать со многими различными типами данных, причем конкретный тип (или типы) определяется из типов данных, фактически переданных ей в конкретном вызове.

Полиморфные аргументы и результаты связываются друг с другом и разрешаются в конкретный тип данных при синтаксическом анализе запроса, вызывающего полиморфную функцию. В каждой позиции (аргументе или возвращаемом значении), объявленной как anyelement, может передаваться любой фактический тип данных, но в каждом отдельно взятом вызове все эти фактические типы должны быть одинаковыми. В каждой позиции, объявленной как anyarray, может передаваться любой тип данных массива, но все они тоже должны быть одинаковыми. И точно так же позиции, объявленные как anyrange, должны принадлежать к одному диапазонному типу. Более того, если некоторые позиции объявлены как anyarray, а другие как anyelement, то фактическим типом в позициях anyarray должен быть массив, элементы которого имеют тот же тип, что и элементы в позициях anyelement. Аналогично если одни позиции объявлены как anyrange, а другие объявлены как anyelement или anyarray, фактическим типом в позициях anyrange должен быть диапазон, подтип которого совпадает с типом, передаваемым в позициях anyelement, и типом элементов в позициях anyarray. Псевдотип anynonarray обрабатывается так же, как anyelement, но с дополнительным ограничением — фактический тип не должен быть типом массива. Псевдотип anyenum тоже обрабатывается как anyelement и тоже с дополнительным ограничением — фактический тип должен быть перечисляемым типом.

Таким образом, когда несколько аргументов объявлены с полиморфным типом, в конечном счете допускается только одна определенная комбинация фактических типов аргументов. Например, функция, объявленная как equal(anyelement, anyelement), примет в аргументах любые два значения, но только если они принадлежат к одному типу данных.

Если возвращаемое значение функции объявляется как полиморфный тип, должна быть хотя бы одна позиция аргумента, тоже относящаяся к полиморфному типу, а предоставленный в качестве аргумента фактический тип данных определяет фактический тип результата для этого вызова. Например, если бы уже не существовало механизма индексирования массива, можно было бы создать функцию, реализующую индексирование как subscript(anyarray, integer) returns anyelement. Это объявление ограничивает первый фактический аргумент типом массива и позволяет синтаксическому анализатору вывести из него правильный тип результата. Другой пример: функция, объявленная как f(anyarray) returns anyenum будет принимать только массивы перечисляемого типа.

В большинстве случаев синтаксический анализатор может вывести фактический тип данных для полиморфного типа результата из аргументов другого полиморфного типа; например, anyarray можно вывести из anyelement и наоборот. Исключение составляет полиморфный результат типа anyrange, требующий аргумент типа anyrange; его нельзя вывести из аргументов anyarray или anyelement. Это связано с тем, что у нескольких диапазонных типов может быть один подтип.

Обратите внимание, что псевдотипы anynonarray и anyenum представляют не отдельные типовые переменные; они принадлежат к тому же типу, что и anyelement, всего лишь с одним дополнительным ограничением. Например, объявление функции как f(anyelement, anyenum) равнозначно объявлению ее как f(anyenum, anyenum): оба фактических аргумента должны относиться к одному перечисляемому типу.

Функция с переменным числом аргументов (которая принимает переменное число аргументов, как описано в подразделе Функции SQL с переменным числом аргументов) тоже может быть полиморфной: для этого надо объявить ее последний параметр как VARIADIC anyarray. В целях сопоставления аргументов и определения фактического типа результата такая функция ведет себя так же, как если бы в ней записали соответствующее число параметров anynonarray.

Пользовательские функции

В QHB предусмотрено четыре вида функций:

функции на языке запросов (функции, написанные на SQL)

функции на процедурном языке (например, на PL/pgSQL* или *PL/Tcl)

внутренние функции

функции на нативном языке

Каждый вид функции может принимать в качестве аргументов (параметров) базовые типы, составные типы или их комбинации. Кроме того, каждый вид функции может возвращать базовый или составной тип. Также можно определить функции, возвращающие множества базовых или составных значений.

Многие виды функций могут принимать или возвращать определенные псевдотипы (например, полиморфные типы), но доступные средства для работы с ними различаются. За более подробной информацией обратитесь к описанию каждого вида функций.

Определять функции SQL проще всего, поэтому сначала мы рассмотрим их. Большинство концепций, представленных для функций SQL, переносятся и на другие типы функций.

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

Пользовательские процедуры

Процедура — это объект базы данных, похожий на функцию. Отличие состоит в том, что процедура не возвращает значения, поэтому для нее не объявляется возвращаемый тип. В том время как функция вызывается как часть запроса или команды DML, процедура вызывается отдельно с помощью команды CALL. Если команда CALL не является частью явной транзакции, процедура на многих серверных языках во время своего выполнения может фиксировать, откатывать и начинать новые транзакции, что для функций невозможно.

Изложенные далее в этой главе пояснения по созданию пользовательских функций также применимы и к процедурам, за исключением того, что для процедур надо использовать команду CREATE PROCEDURE, у них отсутствует возвращаемый тип и для них не учитываются некоторые другие свойства, например строгость.

Функции и процедуры в совокупности также называют подпрограммами. Существуют команды ALTER ROUTINE и DROP ROUTINE, которые могут работать с функциями и процедурами без необходимости указания точного вида объекта. Однако обратите внимание, что команды CREATE ROUTINE нет.

Функции на языке запросов (SQL)

Функции SQL выполняют произвольный список операторов SQL, возвращая результат последнего запроса в списке. В простом случае (не с множеством) будет возвращена первая строка результата последнего запроса. (Следует учесть, что понятие «первая строка» в результате из нескольких строк определено точно, только если используется ORDER BY). Если последний запрос вообще не вернет никаких строк, то будет возвращено значение NULL.

Как вариант, можно объявить функцию SQL как возвращающую множество (то есть несколько строк), указав в качестве возвращаемого типа функции SETOF некий_тип или объявив ее с указанием RETURNS TABLE(столбцы). В этом случае будут возвращены все строки результата последнего запроса. Более подробная информация приведена ниже.

Тело функции SQL должно представлять собой список операторов SQL, разделенных точкой с запятой. После последнего оператора точку с запятой ставить необязательно. Если не объявлено, что функция возвращает void, последним оператором должен быть SELECT, либо INSERT, UPDATE или DELETE с предложением RETURNING.

Любой набор команд на языке SQL можно собрать вместе и определить как функцию. Помимо запросов SELECT, эти команды могут включать запросы, изменяющие данные (INSERT, UPDATE и DELETE), а также другие команды SQL. (В функциях SQL нельзя использовать команды управления транзакциями, например COMMIT, SAVEPOINT, и некоторые служебные команды, например VACUUM). Однако последняя команда должна быть SELECT или команда с предложением RETURNING, возвращающая то, что указано в качестве возвращаемого типа функции. Или же, если нужно создать функцию SQL, которая выполняет действие, но не возвращает полезное значение, можно определить ее как возвращающую void. Например, эта функция удаляет строки с отрицательными зарплатами из таблицы emp:

CREATE FUNCTION clean_emp() RETURNS void AS '
    DELETE FROM emp
        WHERE salary < 0;
' LANGUAGE SQL;

SELECT clean_emp();

 clean_emp
-----------
(1 row)

Примечание
До выполнения какой-либо из ее команд тело функции SQL анализируется целиком. Хотя функция SQL может содержать команды, которые изменяют системные каталоги (например, CREATE TABLE), эффекты таких команд не будут видны во время синтаксического анализа последующих команд в функции. Так, например, команды CREATE TABLE foo (...); INSERT INTO foo VALUES(...); не будут работать, как ожидается, если они упакованы в одну SQL-функцию, поскольку на момент синтаксического анализа команды INSERT таблица foo еще не будет существовать. В такой ситуации рекомендуется вместо функции SQL использовать PL/pgSQL.

Синтаксис команды CREATE FUNCTION требует, чтобы тело функции было записано как строковая константа. Обычно для строковой константы наиболее удобно использовать знаки доллара (см. раздел Строковые константы с экранированием знаками доллара). Если вы решите использовать обычный синтаксис строковой константы с одинарными кавычками, вам следует удваивать одинарные кавычки (’) и обратную косую черту (\) (предполагается комбинированный синтаксис строки) в теле функции (см. раздел Строковые константы).

Аргументы для функций SQL

На аргументы функции SQL можно ссылаться в теле функции, используя имена или номера. Примеры обоих методов приведены ниже.

Чтобы использовать имя, объявите аргумент функции с именем, а затем просто напишите это имя в теле функции. Если имя аргумента совпадает с именем любого столбца в текущей команде SQL в составе функции, имя столбца будет иметь приоритет. Чтобы обойти это, уточните имя аргумента именем самой функции, то есть запишите его как имя_функции.имя_аргумента. (Если и оно будет конфликтовать с уточненным именем столбца, то снова выиграет имя столбца. Этой неоднозначности можно избежать, выбрав для таблицы в команде SQL другой псевдоним.)

В более старом подходе с номерами на аргументы ссылаются, используя синтаксис $n; $1 обозначает первый входной аргумент, $2 — второй и т. д. Это будет работать независимо от того, был ли конкретный аргумент объявлен с именем или нет.

Если аргумент имеет составной тип, то для обращения к его атрибутам можно использовать запись с точкой, например аргумент.поле или $1.поле. Опять же, здесь может понадобиться уточнить имя аргумента именем функции,чтобы сделать имя аргумента однозначным.

Аргументы функции SQL могут использоваться только как значения данных, но не как идентификаторы. Так, например, это приемлемо:

INSERT INTO mytable VALUES ($1);

а это не будет работать:

INSERT INTO $1 VALUES (42);

Функции SQL c базовыми типами

Простейшая возможная функция SQL не имеет аргументов и просто возвращает базовый тип, например integer:

CREATE FUNCTION one() RETURNS integer AS $$
    SELECT 1 AS result;
$$ LANGUAGE SQL;

-- Альтернативный синтаксис строковой константы:
CREATE FUNCTION one() RETURNS integer AS '
    SELECT 1 AS result;
' LANGUAGE SQL;

SELECT one();

 one
-----
   1

Обратите внимание, что мы определили псевдоним столбца в теле функции для ее результата (с именем result), но этот псевдоним не виден вне функции. Соответственно, и результат помечен как one, а не result.

Почти так же легко определять функции SQL, которые принимают в качестве аргументов базовые типы:

CREATE FUNCTION add_em(x integer, y integer) RETURNS integer AS $$
    SELECT x + y;
$$ LANGUAGE SQL;

SELECT add_em(1, 2) AS answer;

 answer
--------
      3

Как вариант, можно обойтись без имен аргументов и использовать номера:

CREATE FUNCTION add_em(integer, integer) RETURNS integer AS $$
    SELECT $1 + $2;
$$ LANGUAGE SQL;

SELECT add_em(1, 2) AS answer;

 answer
--------
      3

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

CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$
    UPDATE bank
        SET balance = balance - debit
        WHERE accountno = tf1.accountno;
    SELECT 1;
$$ LANGUAGE SQL;

Пользователь может выполнить эту функцию для дебетования счета 17 на 100 рублей следующим образом:

SELECT tf1(17, 100.0);

В этом примере мы выбрали для первого аргумента имя accountno, но оно совпадает с именем столбца в таблице bank. В команде UPDATE accountno ссылается на столбец bank.accountno, поэтому для ссылки на аргумент нужно использовать tf1.accountno. Конечно, этого можно было избежать, использовав для аргумента другое имя.

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

CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$
    UPDATE bank
        SET balance = balance - debit
        WHERE accountno = tf1.accountno;
    SELECT balance FROM bank WHERE accountno = tf1.accountno;
$$ LANGUAGE SQL;

которое корректирует этот баланс и возвращает новый. То же самое можно сделать одной командой, используя RETURNING:

CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS numeric AS $$
    UPDATE bank
        SET balance = balance - debit
        WHERE accountno = tf1.accountno
    RETURNING balance;
$$ LANGUAGE SQL;

Функция SQL должна возвращать в точности объявленный тип результата. Для этого может потребоваться добавление явного приведения. Например, предположим, что мы хотим, чтобы предыдущая функция add_em возвращала вместо типа integer тип float8. Этот вариант не сработает:

CREATE FUNCTION add_em(integer, integer) RETURNS float8 AS $$
    SELECT $1 + $2;
$$ LANGUAGE SQL;

несмотря на то, что в других контекстах QHB без проблем вставил бы явное приведение, чтобы преобразовать integer во float8. Нужно написать вот так:

CREATE FUNCTION add_em(integer, integer) RETURNS float8 AS $$
    SELECT ($1 + $2)::float8;
$$ LANGUAGE SQL;

Функции SQL с составными типами

При написании функций, работающих с аргументами составных типов, придется не только указывать, какой аргумент, но и какой атрибут (поле) этого аргумента нам нужен. Например, предположим, что emp — это таблица, содержащая данные о сотрудниках, и, следовательно, имя составного типа каждой строки данной таблицы тоже emp. Вот функция double_salary, которая вычисляет, какой была бы чья-то зарплата, если ее удвоить:

CREATE TABLE emp (
    name        text,
    salary      numeric,
    age         integer,
    cubicle     point
);

INSERT INTO emp VALUES ('Bill', 4200, 45, '(2,1)');

CREATE FUNCTION double_salary(emp) RETURNS numeric AS $$
    SELECT $1.salary * 2 AS salary;
$$ LANGUAGE SQL;

SELECT name, double_salary(emp.*) AS dream
    FROM emp
    WHERE emp.cubicle ~= point '(2,1)';

 name | dream
------+-------
 Bill |  8400

Обратите внимание на использование синтаксиса $1.salary для выбора одного поля из значения строки аргумента. Также обратите внимание, как вызывающая команда SELECT использует указание имя_таблицы.*, чтобы выбрать текущую строку таблицы как составное значение. На строку таблицы также можно сослаться, используя только имя таблицы, например так:

SELECT name, double_salary(emp) AS dream
    FROM emp
    WHERE emp.cubicle ~= point '(2,1)';

но этот альтернативный способ не рекомендуется, поскольку легко вызывает путаницу. (Подробную информацию об этих двух записях для составного значения строки таблицы см. в разделе Использование составных типов в запросах.)

Иногда удобно создавать значение составного аргумента на лету. Это можно сделать с помощью конструкции типа ROW. Например, мы можем подправить данные перед передачей в функцию:

SELECT name, double_salary(ROW(name, salary * 1.1, age, cubicle)) AS dream
    FROM emp;

Также можно создать функцию, которая возвращает составной тип. Вот пример функции, которая возвращает одну строку emp:

CREATE FUNCTION new_emp() RETURNS emp AS $$
    SELECT text 'None' AS name,
        1000.0 AS salary,
        25 AS age,
        point '(2,2)' AS cubicle;
$$ LANGUAGE SQL;

В этом примере мы заполнили все поля константами, но вместо них могли быть произвольные вычисления.

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

Порядок значений в списке запроса должен точно соответствовать порядку появления столбцов в таблице, связанной с составным типом. (Имена столбцов, как показано в примере выше, в этой системе ни на что не влияют.)

Необходимо сделать так, чтобы тип каждого выражения совпадал с соответствующим столбцом составного типа, при необходимости вставляя приведение. Иначе мы получим такие ошибки:

ERROR:  function declared to return emp returns varchar instead of text at column 1
-- ОШИБКА: функция, объявленная как возвращающая emp, возвращает varchar вместо text в столбце 1

Как и в случае с базовым типом, функция не будет автоматически добавлять никакие приведения.

Другой способ определить ту же функцию:

CREATE FUNCTION new_emp() RETURNS emp AS $$
    SELECT ROW('None', 1000.0, 25, '(2,2)')::emp;
$$ LANGUAGE SQL;

Здесь мы записали команду SELECT, которая возвращает только один столбец надлежащего составного типа. На самом деле в данной ситуации этот вариант ничуть не лучше, но в некоторых случаях это удобная альтернатива — например, если нужно вычислить результат, вызвав другую функцию, возвращающую нужное составное значение. Другой пример: если мы пытаемся написать функцию, которая возвращает не обычный составной тип, а домен поверх составного типа, необходимо всегда записывать ее как возвращающую единственный столбец, так как другого способа получить значение именно доменного типа нет.

Эту функцию можно вызвать напрямую или указав ее в выражении значения:

SELECT new_emp();

         new_emp
--------------------------
 (None,1000.0,25,"(2,2)")

или вызвав ее как табличную функцию:

SELECT * FROM new_emp();

 name | salary | age | cubicle
------+--------+-----+---------
 None | 1000.0 |  25 | (2,2)

Второй способ более подробно описан в подразделе Функции SQL как источники таблиц.

При вызове функции, возвращающей составной тип, вам может потребоваться взять только одно поле (атрибут) из ее результата. Это можно сделать, записав следующее:

SELECT (new_emp()).name;

 name
------
 None

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

SELECT new_emp().name;
ERROR:  syntax error at or near "."
--ОШИБКА: синтаксическая ошибка (примерное положение: ".")
LINE 1: SELECT new_emp().name;
                        ^

Другой вариант — использовать функциональную запись для извлечения атрибута:

SELECT name(new_emp());

 name
------
 None

Как объяснено в разделе Использование составных типов в запросах, запись поля и функциональная запись равнозначны.

Еще один способ использования функции, возвращающей составной тип, это передать ее результат другой функции, которая принимает нужный тип строки на вход:

CREATE FUNCTION getname(emp) RETURNS text AS $$
    SELECT $1.name;
$$ LANGUAGE SQL;

SELECT getname(new_emp());
 getname
---------
 None
(1 row)

Функции SQL с выходными параметрами

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

CREATE FUNCTION add_em (IN x int, IN y int, OUT sum int)
AS 'SELECT x + y'
LANGUAGE SQL;

SELECT add_em(3,7);
 add_em
--------
     10
(1 row)

В сущности, это не отличается от версии add_em, показанной в подразделе Функции SQL c базовыми типами. Реальная ценность выходных параметров в том, что они предоставляют удобный способ определения функций, возвращающих несколько столбцов. Например,

CREATE FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int)
AS 'SELECT x + y, x * y'
LANGUAGE SQL;

 SELECT * FROM sum_n_product(11,42);
 sum | product
-----+---------
  53 |     462
(1 row)

По сути, мы здесь создали анонимный составной тип для результата функции. Приведенный выше пример имеет тот же конечный результат, что и запись

CREATE TYPE sum_prod AS (sum int, product int);

CREATE FUNCTION sum_n_product (int, int) RETURNS sum_prod
AS 'SELECT $1 + $2, $1 * $2'
LANGUAGE SQL;

но первый вариант зачастую удобнее, так как нет необходимости тратить время на отдельное определение составного типа. Обратите внимание, что имена, заданные выходным параметрам, присвоены им не просто для красоты — они определяют имена столбцов анонимного составного типа. (Если опустить имя выходного параметра, система выберет его самостоятельно.)

Обратите внимание, что выходные параметры не включаются в список аргументов при вызове такой функции из SQL. Причина этого в том, что QHB определяет сигнатуру вызова функции, рассматривая только входные параметры. Также это означает, что при обращении к функции с тем, чтобы, к примеру, удалить ее, учитываются только входные параметры. Приведенную выше функцию можно удалить любым из следующих вариантов:

DROP FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int);
DROP FUNCTION sum_n_product (int, int);

Параметры могут быть помечены как IN (по умолчанию), OUT, INOUT или VARIADIC. Параметр INOUT работает и как входной параметр (часть списка входных аргументов), и как выходной (часть типа записи результата). Параметры VARIADIC являются входными параметрами, но обрабатываются особым образом, описанным в следующем разделе.

Функции SQL с переменным числом аргументов

Функции SQL могут быть объявлены как принимающие переменное число аргументов, при условии, что все «необязательные» аргументы имеют один тип данных. Необязательные аргументы будут переданы функции в виде массива. Для этого при объявлении функции последний параметр помечается как VARIADIC и должен иметь тип массива. Например:

CREATE FUNCTION mleast(VARIADIC arr numeric[]) RETURNS numeric AS $$
    SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;

SELECT mleast(10, -1, 5, 4.4);
 mleast
--------
     -1
(1 row)

В сущности, все предоставленные аргументы, начиная с позиции VARIADIC и дальше, собираются в одномерный массив, как если бы вы написали

SELECT mleast(ARRAY[10, -1, 5, 4.4]);    -- это не сработает

Однако вы не можете написать так — или, по крайней мере, это не будет соответствовать определению этой функции. Параметр, помеченный как VARIADIC, соответствует одному или нескольким параметрами типа его элемента, а не его собственного типа.

Иногда полезно иметь возможность передавать уже созданный массив в функцию с переменным числом аргументов; это особенно удобно, когда одна функция с переменным числом аргументов хочет передать свой массив параметров другой. Кроме того, это единственный безопасный способ вызвать такую функцию, находящуюся в схеме, которая позволяет создавать объекты недоверенным пользователям (см. примечание ниже). Это можно сделать, указав в вызове VARIADIC:

SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);

Это предотвращает развертывание переменного параметра функции в тип его элемента, тем самым позволяя нормально сопоставить его со значением аргумента-массива. VARIADIC можно добавить только к последнему фактическому аргументу вызова функции.

Указание VARIADIC в вызове также является единственным способом передачи пустого массива в функцию с переменным числом аргументов, например:

SELECT mleast(VARIADIC ARRAY[]::numeric[]);

Простая запись SELECT mleast() не работает, поскольку переменный параметр должен соответствовать хотя бы одному фактическому аргументу. (Вы можете определить вторую функцию с тем же именем mleast, но без параметров, если хотите разрешить такие вызовы).

Параметры элемента массива, создаваемые из переменных параметров, обрабатываются как не имеющие собственных имен. Это значит, что вызвать функцию с переменными параметрами, используя именованные аргументы, нельзя (см. раздел Вызов функции), если только при вызове не указано VARIADIC. Например, этот вариант сработает:

SELECT mleast(VARIADIC arr => ARRAY[10, -1, 5, 4.4]);

а эти варианты — нет:

SELECT mleast(arr => 10);
SELECT mleast(arr => ARRAY[10, -1, 5, 4.4]);

Важно!
Если функция объявлена с параметром-массивом VARIADIC и при ее вызове не указывается ключевое слово VARIADIC, то эта функция обрабатывается, как если бы этот параметр был заменен одним или несколькими параметрами типа его элемента, что требуется для соответствия вызову. После такого развертывания по фактическим типам аргументов эта функция может совпасть с некоторой функцией с постоянным числом аргументов. В этом случае используется функция, появившаяся на пути поиска раньше, а если обе функции находятся в одной схеме, то предпочтение отдается функции с постоянным числом аргументов.

Это создает угрозу безопасности при вызове с полным именем функции с переменным числом аргументов, находящейся в схеме, которая позволяет создавать объекты недоверенным пользователям. (Эта угроза неактуальна для имен без схемы, так как путь поиска, содержащий схемы, позволяющие недоверенным пользователям создавать объекты, не соответствует шаблону безопасного использования схем.) Злонамеренный пользователь может перехватить управление и выполнять произвольные функции SQL, как будто их выполняете вы. Подставив в вызов ключевое слово VARIADIC, вы устраните эту угрозу. Для вызовов, содержащих параметры VARIADIC "any", зачастую не существует равнозначной формулировки с ключевым словом VARIADIC. Чтобы обеспечить безопасность таких вызовов, схема функции должна позволять создавать объекты только доверенным пользователям.

Функции SQL со значениями аргументов по умолчанию

Функции могут быть объявлены со значениями по умолчанию для некоторых или всех входных аргументов. Значения по умолчанию подставляются всякий раз, когда функция вызывается с недостаточным количеством фактических аргументов. Поскольку аргументы могут быть пропущены только с конца списка фактических аргументов, все параметры после параметра со значением по умолчанию также должны иметь значения по умолчанию. (Хотя использование записи с именованными аргументами могло бы ослабить это ограничение, оно все-таки остается в силе, чтобы позиционная запись аргумента работала адекватно.) Независимо от того, используете вы эту возможность или нет, она требует мер предосторожности при вызове функций в базах данных, где одни пользователи не доверяют другим.

Например:

CREATE FUNCTION foo(a int, b int DEFAULT 2, c int DEFAULT 3)
RETURNS int
LANGUAGE SQL
AS $$
    SELECT $1 + $2 + $3;
$$;

SELECT foo(10, 20, 30);
 foo
-----
  60
(1 row)

SELECT foo(10, 20);
 foo
-----
  33
(1 row)

SELECT foo(10);
 foo
-----
  15
(1 row)

SELECT foo();  -- не работает, потому что для первого аргумента нет значения по умолчанию
ERROR:  function foo() does not exist
-- ОШИБКА: функция foo() не существует

Вместо ключевого слова DEFAULT можно также использовать знак =:

CREATE FUNCTION foo(a int, b int = 2, c int = 3)

Важно!
Функции, имеющие значения по умолчанию для параметров, считаются совпадающими с вызовом, в котором опущено ноль и более позиций параметров, установленных по умолчанию. Если вызову соответствует более одной такой функции, используется та, которая появляется на пути поиска раньше. Если в одной схеме существуют две и более таких функций с идентичными типами в позициях обязательных параметров (что возможно, если у них разные наборы пропускаемых параметров), система не сможет определить, какая из них более предпочтительна, и выдаст ошибку «неоднозначный вызов функции», если не сможет найти лучшее соответствие для вызова.

Это создает угрозу при вызове с полным именем любой функции , находящейся в схеме, которая позволяет создавать объекты недоверенным пользователям. (Эта угроза неактуальна для имен без схемы, так как путь поиска, содержащий схемы, позволяющие недоверенным пользователям создавать объекты, не соответствует шаблону безопасного использования схем.) Злонамеренный пользователь может создать функцию с именем уже существующей функции, дублируя ее параметры и добавляя дополнительные со значениями по умолчанию. Это помешает новым вызовам добраться до оригинальной функции. Чтобы предотвратить эту угрозу, помещайте функции в схемы, которые позволяют создавать объекты только доверенным пользователям.

Функции SQL как источники таблиц

Все функции SQL можно использовать в предложении FROM запроса, но особенно это полезно для функций, возвращающих составные типы. Если функция определена как возвращающая базовый тип, она создаст таблицу с одним столбцом. Если функция определена как возвращающая составной тип, она создаст столбец для каждого атрибута составного типа.

Вот пример:

CREATE TABLE foo (fooid int, foosubid int, fooname text);
INSERT INTO foo VALUES (1, 1, 'Joe');
INSERT INTO foo VALUES (1, 2, 'Ed');
INSERT INTO foo VALUES (2, 1, 'Mary');

CREATE FUNCTION getfoo(int) RETURNS foo AS $$
    SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;

SELECT *, upper(fooname) FROM getfoo(1) AS t1;

 fooid | foosubid | fooname | upper
-------+----------+---------+-------
     1 |        1 | Joe     | JOE
(1 row)

Как показывает пример, мы можем работать со столбцами результата функции точно так же, как если бы они были столбцами обычной таблицы.

Обратите внимание, что мы получили из функции только одну строку. Это связано с тем, что мы не использовали указание SETOF. Оно описано в следующем разделе.

Функции SQL, возвращающие множества

Когда функция SQL объявляется как возвращающая SETOF некий_тип, конечный запрос функции выполняется полностью, и каждая выводимая ей строка возвращается как элемент результирующего множества.

Это свойство обычно используется при вызове функции в предложении FROM. В этом случае каждая строка, возвращаемая функцией, становится строкой таблицы, видимой запросом. Например, предположим, что таблица foo имеет то же содержимое, что и выше, и выполняем:

CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$
    SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;

SELECT * FROM getfoo(1) AS t1;

После чего получаем:

fooid | foosubid | fooname
-------+----------+---------
    1 |        1 | Joe
    1 |        2 | Ed
(2 rows)

Также можно вернуть несколько строк со столбцами, определяемыми выходными параметрами, следующим образом:

CREATE TABLE tab (y int, z int);
INSERT INTO tab VALUES (1, 2), (3, 4), (5, 6), (7, 8);

CREATE FUNCTION sum_n_product_with_tab (x int, OUT sum int, OUT product int)
RETURNS SETOF record
AS $$
    SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;

SELECT * FROM sum_n_product_with_tab(10);
 sum | product
-----+---------
  11 |      10
  13 |      30
  15 |      50
  17 |      70
(4 rows)

Ключевой момент здесь в том, что нужно записать RETURNS SETOF record, чтобы показать, что функция возвращает несколько строк, а не одну. Если существует только один выходной параметр, то вместо record напишите его тип.

Часто бывает полезно создать результат запроса, вызывая функцию, возвращающую множество, несколько раз, при каждом вызове передавая параметры последовательно из строк таблицы или подзапроса. Предпочтительный способ сделать это — использовать ключевое слово LATERAL, описанное в разделе Подзапросы LATERAL. Вот пример использования функции, возвращающей множество, для перечисления элементов древовидной структуры:

SELECT * FROM nodes;
   name    | parent
-----------+--------
 Top       |
 Child1    | Top
 Child2    | Top
 Child3    | Top
 SubChild1 | Child1
 SubChild2 | Child1
(6 rows)

CREATE FUNCTION listchildren(text) RETURNS SETOF text AS $$
    SELECT name FROM nodes WHERE parent = $1
$$ LANGUAGE SQL STABLE;

SELECT * FROM listchildren('Top');
 listchildren
--------------
 Child1
 Child2
 Child3
(3 rows)

SELECT name, child FROM nodes, LATERAL listchildren(name) AS child;
  name  |   child
--------+-----------
 Top    | Child1
 Top    | Child2
 Top    | Child3
 Child1 | SubChild1
 Child1 | SubChild2
(5 rows)

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

Функции, возвращающие множества, также можно вызывать в списке выборки запроса. Для каждой строки, которая генерируется самим запросом, вызывается функция, возвращающая множество, и для каждого элемента множества ее результатов генерируется выходная строка. Предыдущий пример также можно выполнить с помощью таких запросов:

SELECT listchildren('Top');
 listchildren
--------------
 Child1
 Child2
 Child3
(3 rows)

SELECT name, listchildren(name) FROM nodes;
  name  | listchildren
--------+--------------
 Top    | Child1
 Top    | Child2
 Top    | Child3
 Child1 | SubChild1
 Child1 | SubChild2
(5 rows)

В последнем SELECT обратите внимание, что для Child2, Child3 и т.д. нет выходных строк. Это происходит потому, что listchildren возвращает 0 строк для этих аргументов, поэтому строки результата не генерируются. Такое же поведение мы получили при внутреннем соединении с результатом функции с использованием синтаксиса LATERAL.

Поведение QHB с функцией, возвращающей множество, в списке выборки запроса почти не отличается от поведения с такой функцией, записанной в предложении LATERAL FROM. Например,

SELECT x, generate_series(1,5) AS g FROM tab;

почти равнозначно

SELECT x, g FROM tab, LATERAL generate_series(1,5) AS g;

Записи могли бы быть полностью идентичны, но в этом конкретном примере планировщик может решить поместить g во внешнюю сторону соединения с вложенным циклом, так как у g нет фактической латеральной зависимости от tab. Это привело бы к другому порядку выходных строк. Функции, возвращающие множества, в списке выборки всегда рассматриваются так, как если бы они находились внутри соединения с вложенным циклом с остальным предложением FROM, поэтому такие функции выполняются полностью, прежде чем начинается рассмотрение следующей строки предложения FROM.

Если в списке выборки запроса находится несколько функций, возвращающих множества, поведение сходно с тем, которое мы получаем, помещая функции в один элемент LATERAL ROWS FROM( ... ) предложения FROM. Для каждой строки из нижележащего выдается выходная строка, использующая первый результат из каждой функции, затем выходная строка, использующая второй результат, и так далее. Если какие-либо из функций, возвращающих множества, выдают меньше результатов, чем другие, недостающие данные заменяются значениями NULL, так что общее количество строк, генерируемых для одной нижележащей строки, равно количеству строк, выдаваемых функцией с наибольшим числом строк в возвращаемом множестве. Таким образом, функции, возвращающие множества, выполняются «в унисон», пока не будут исчерпаны, после чего выполнение продолжается со следующей нижележащей строкой.

Функции, возвращающие множества, могут быть вложенными в списке выборки, хотя это не допускается в элементах предложения FROM. В таких случаях каждый уровень вложенности обрабатывается отдельно, как если бы это был отдельный элемент LATERAL ROWS FROM( ... ). Например, в

SELECT srf1(srf2(x), srf3(y)), srf4(srf5(z)) FROM tab;

возвращающие множества функции srf2, srf3 и srf5 будут выполняться в унисон для каждой строки tab, а затем к каждой строке, произведенной нижними функциями, будут применяться srf1 и srf4.

Функции, возвращающие множества, нельзя использовать в конструкциях с условным вычислением, таких как CASE или COALESCE. Например, рассмотрим запрос

SELECT x, CASE WHEN x > 0 THEN generate_series(1, 5) ELSE 0 END FROM tab;

Может показаться, что он должен выдать пять копий входных строк, в которых x > 0, и по одной копии всех остальных строк; но на самом деле, поскольку generate_series(1, 5) будет выполняться в неявном элементе LATERAL FROM до того, как выражение CASE вообще будет рассмотрено, запрос должен был бы создать по пять копий каждой входной строки. Во избежание путаницы в таких случаях выдается ошибка при синтаксическом анализе запроса.

Примечание
Если последней командой функции является INSERT, UPDATE или DELETE с RETURNING, эта команда всегда будет выполняться полностью, даже если функция не объявлена с помощью SETOF или вызывающий запрос не извлекает все строки результата. Любые дополнительные строки, созданные предложением RETURNING, просто отбрасываются, но изменения в обрабатываемой таблице все равно произойдут (и все завершатся до возврата из функции).

Функции SQL, возвращающие таблицы

Есть еще один способ объявить функцию как возвращающую множество — использовать синтаксис RETURNS TABLE(столбцы). Это равнозначно использованию одного или нескольких параметров OUT с обозначением функции как возвращающей SETOF record (или SETOF тип единственного выходного параметра, если применимо). Эта запись указана в последних версиях стандарта SQL, так что этот вариант может быть более переносимым, чем использование SETOF.

Например, предыдущий пример с суммой и произведением можно также переписать так:

CREATE FUNCTION sum_n_product_with_tab (x int)
RETURNS TABLE(sum int, product int) AS $$
    SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;

Не допускается использование явных параметров OUT или INOUT с записью RETURNS TABLE — все выходные столбцы следует записать в списке TABLE.

Полиморфные функции SQL

Функции SQL могут принимать и возвращать полиморфные типы anyelement, anyarray, anynonarray, anyenum и anyrange. Более подробное объяснение полиморфных функций см. в подразделе Полиморфные типы. Вот пример полиморфной функции make_array, которая создает массив из двух элементов произвольного типа данных:

CREATE FUNCTION make_array(anyelement, anyelement) RETURNS anyarray AS $$
    SELECT ARRAY[$1, $2];
$$ LANGUAGE SQL;

SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray;
 intarray | textarray
----------+-----------
 {1,2}    | {a,b}
(1 row)

Обратите внимание на использование приведения типа 'a'::text, указывающего, что аргумент имеет тип text. Это необходимо, если аргумент является простой строковой константой, поскольку иначе он будет восприниматься как имеющий тип unknown, а массив типов unknown является недопустимым. Без приведения типа вы будете получать ошибки вроде этой:

ERROR:  could not determine polymorphic type because input has type "unknown"
-- ОШИБКА: не удалось определить полиморфный тип, так как входные аргументы имеют тип "unknown"

Разрешается иметь полиморфные аргументы и фиксированный тип результата, но не наоборот. Например:

CREATE FUNCTION is_greater(anyelement, anyelement) RETURNS boolean AS $$
    SELECT $1 > $2;
$$ LANGUAGE SQL;

SELECT is_greater(1, 2);
 is_greater
------------
 f
(1 row)

CREATE FUNCTION invalid_func() RETURNS anyelement AS $$
    SELECT 1;
$$ LANGUAGE SQL;
ERROR:  cannot determine result data type -- ОШИБКА: не удалось определить тип данных результата
DETAIL:  A function returning a polymorphic type must have at least one polymorphic argument.
-- ПОДРОБНОСТИ: Функция, возвращающая полиморфный тип, должна иметь хотя бы один полиморфный аргумент.

Полиморфизм можно использовать с функциями, имеющими выходные аргументы. Например:

CREATE FUNCTION dup (f1 anyelement, OUT f2 anyelement, OUT f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE SQL;

SELECT * FROM dup(22);
 f2 |   f3
----+---------
 22 | {22,22}
(1 row)

Кроме того, полиморфизм можно использовать с функциями с переменным числом аргументов. Например:

CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$
    SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;

SELECT anyleast(10, -1, 5, 4);
 anyleast
----------
       -1
(1 row)

SELECT anyleast('abc'::text, 'def');
 anyleast
----------
 abc
(1 row)

CREATE FUNCTION concat_values(text, VARIADIC anyarray) RETURNS text AS $$
    SELECT array_to_string($2, $1);
$$ LANGUAGE SQL;

SELECT concat_values('|', 1, 4, 2);
 concat_values
---------------
 1|4|2
(1 row)

Функции SQL и правила сортировки

Когда функция SQL имеет один или несколько параметров сортируемых типов данных, правило сортировки выбирается при каждом вызове функции, в зависимости от правил сортировки, связанных с фактическими аргументами, как описано в разделе Поддержка правил сортировки. Если правило сортировки определено успешно (т. е. отсутствуют конфликты между неявно установленными правилами сортировки аргументов), то все сортируемые параметры неявно воспринимаются как имеющие такое правило. Это повлияет на поведение операций, связанных с сортировкой, внутри функции. Например, для описанной выше функции anyleast результат

SELECT anyleast('abc'::text, 'ABC');

будет зависеть от правила сортировки базы данных по умолчанию. В локали C результатом будет ABC, но во многих других локалях это будет abc. Правило сортировки можно установить принудительно, добавив предложение COLLATE к любому из аргументов, например:

SELECT anyleast('abc'::text, 'ABC' COLLATE "C");

Либо, если вы хотите, чтобы функция работала с конкретным правилом сортировки, независимо от того, с каким она была вызвана, вставьте в определение функции нужное количество предложений COLLATE. Эта версия anyleast всегда будет использовать локаль en_US для сравнения строк:

CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$
    SELECT min($1[i] COLLATE "en_US") FROM generate_subscripts($1, 1) g(i);
$$ LANGUAGE SQL;

Но обратите внимание, что применение правила к несортируемому типу данных вызовет ошибку.

Если среди фактических аргументов нельзя выбрать общие правила сортировки, функция SQL будет воспринимать свои параметры как имеющие правило сортировки по умолчанию для их типов данных (которое обычно является правилом сортировки по умолчанию для базы данных, но может отличаться для параметров доменных типов).

Поведение сортируемых параметров можно рассматривать как ограниченную форму полиморфизма, применимую только к текстовым типам данных.

Перегрузка функций

Можно определить несколько функций с одним и тем же именем SQL, если они принимают разные аргументы. Другими словами, имена функций можно перегружать. Независимо от того, пользуйтесь вы этой возможностью или нет, она требует мер предосторожности при вызове функций в базах данных, в которых одни пользователи не доверяют другим. При выполнении запроса сервер выбирает, какую именно функцию вызывать, исходя из количества и типов данных предоставленных аргументов. Также перегрузку можно использовать для имитации функций с переменным числом аргументов, вплоть до конечного максимального числа.

Примечание
Отсутствие точного совпадения создает угрозу безопасности при вызове с полным именем функции в схеме, где недоверенные пользователи могут создавать объекты. (Эта угроза неактуальна для имен без схемы, так как путь поиска, содержащий схемы, позволяющие недоверенным пользователям создавать объекты, не соответствует шаблону безопасного использования схем.) В таких ситуациях приведите типы аргументов, чтобы получить точное совпадение (В случаях с типом unknown совпадения на этом этапе не будет никогда.) Тщательно проверяйте приводимые типы аргументов, так как в случае ошибки можно вызвать совсем другую функцию.

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

CREATE FUNCTION test(int, real) RETURNS ...
CREATE FUNCTION test(smallint, double precision) RETURNS ...

то нельзя сразу понять, какая функция вызовется с какими-нибудь простыми входными аргументами вроде test(1, 1.5). Конечно, есть конкретные правила выбора, но они могут измениться в следующей версии, а кроме того, неразумно разрабатывать систему, которая будет незаметно полагаться на такие зыбкие механизмы.

Имя функции, принимающей единственный аргумент составного типа, как правило, не должно совпадать с именем какого-либо атрибута (поля) этого типа. Помните, что запись атрибут(таблица) считается равнозначной записи таблица.атрибут. В случае возникновения неоднозначности между функцией, принимающей составной тип, и атрибутом составного типа, предпочтение всегда будет отдаваться атрибуту. Этот выбор можно обойти, дополнив имя функции схемой (то есть записав схема.функция(таблица)), но лучше избегать этой проблемы, выбирая не конфликтующие имена.

Другой конфликт может возникнуть обычной функцией и функцией с переменными параметрами. Например, можно создать функции foo(numeric) и foo(VARIADIC numeric[]). В этом случае неясно, какая из них должна подходить для вызова при передаче одного числового аргумента, например foo(10.1). Правило состоит в том, что используется функция, появляющаяся в пути поиска раньше, или, если обе функции находятся в одной схеме, выбирается обычная.

При перегрузке нативных функций на языке C/RUST существует дополнительное ограничение: имя уровня С каждой функции в семействе перегруженный функций должно отличаться от имен уровня С всех остальных функций, как внутренних, так и загружаемых динамически. При нарушении этого правила поведение будет зависеть от платформы. Можно получить ошибку компоновщика, либо будет вызвана одна из функций (обычно внутренняя). Альтернативная форма предложения AS команды SQL CREATE FUNCTION отделяет имя функции SQL от имени в исходном коде на C/RUST. Например:

CREATE FUNCTION test(int) RETURNS int
    AS 'имя_файла', 'test_1arg'
    LANGUAGE C;
CREATE FUNCTION test(int, int) RETURNS int
    AS 'имя_файла', 'test_2arg'
    LANGUAGE C;

Имена функций на C/RUST здесь отражают одно из многих возможных соглашений.

Категории изменчивости функций

Все функции делятся по степени изменчивости с возможными вариантами: VOLATILE, STABLE или IMMUTABLE. VOLATILE является значением по умолчанию, если категория не указана явно в команде CREATE FUNCTION . Категория изменчивости — это обещание оптимизатору касательно поведения функции:

Изменчивая функция (VOLATILE) может делать все, что угодно, включая изменение базы данных. Она может возвращать разные результаты при последовательных вызовах с одинаковыми аргументами. Оптимизатор не делает никаких предположений о поведении таких функций. Запрос, использующий изменчивую функцию, будет вычислять ее заново для каждой строки, где требуется ее значение.

Стабильная функция (STABLE) не может изменять данные и гарантированно возвращает одинаковые результаты, получая одинаковые аргументы для всех строк в рамках одного оператора. Эта категория позволяет оптимизатору заменить несколько вызовов функции с одними и теми же аргументами одним. В частности, выражение, содержащее такую функцию, безопасно использовать в условии поиска по индексу. (Поскольку поиск по индексу вычислит значение для сравнения только один раз, а не для каждой строки, использовать в таком поиске функцию категории VOLATILE нельзя.)

Постоянная функция (IMMUTABLE) не может изменять данные и гарантированно возвращает одинаковые результаты, всегда получая одинаковые аргументы. Эта категория позволяет оптимизатору предварительно вычислить функцию, когда запрос вызывает ее с постоянными аргументами. Например, запрос типа SELECT ... WHERE x = 2 + 2 можно упростить до SELECT ... WHERE x = 4, потому что нижележащая функция оператора сложения целых чисел помечена как IMMUTABLE.

Для достижения наилучших результатов оптимизации следует назначить своим функциям самую строгую категорию изменчивости, которая им подходит.

Любая функция с побочными эффектами должна быть помечена как VOLATILE, чтобы оптимизатор не мог исключить ее вызовы. Даже функция без побочных эффектов должна быть помечена как VOLATILE, если ее значение может измениться в течение выполнения одного запроса: например, random(), currval(), timeofday().

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

Разница между категориями STABLE и IMMUTABLE почти незаметна, если рассматривать простые интерактивные запросы, которые планируются и выполняются немедленно: не имеет большого значения, выполняется ли функция однократно во время планирования или однократно в начале выполнения запроса. Но если план сохраняется для последующего повторного использования, возникает существенная разница. Если пометить функцию как IMMUTABLE, когда она не является постоянной, эта функция может оказаться преждевременно сжатой до константы во время планирования, и в итоге при последующих выполнениях плана будет использоваться неактуальное значение. Это опасно при использовании подготовленных операторов или языков функций, кэширующих планы (например, PL/pgSQL).

У функций, написанных на SQL или на одном из стандартных процедурных языков, имеется еще одно важное свойство, определяемое категорией изменчивости, а именно видимость любых изменений данных, которые были сделаны командой SQL, вызывающей функцию. Функция VOLATILE увидит такие изменения, а функция STABLE или IMMUTABLE — нет. Это поведение реализуется с помощью снимков в MVCC (см. раздел Управление параллельным доступом): функции STABLE и IMMUTABLE используют снимок, созданный в начале вызывающего запроса, тогда как функции VOLATILE получают новый снимок в начале каждого запроса, который они выполняют.

Примечание
Функции, написанные на C/RUST, могут обращаться со снимками как угодно, но обычно предпочтительнее делать так, чтобы они работали аналогично.

Вследствие такого обращения со снимками функцию, содержащую только команды SELECT, можно безопасно пометить как STABLE, даже если она выбирает данные из таблиц, которые могут изменяться параллельно выполняющимися запросами. QHB будет выполнять все команды функции STABLE, используя снимок, созданный для вызывающего запроса, поэтому функция будет видеть фиксированное представление базы данных в течение всего этого запроса.

Аналогичное поведение со снимками команды SELECT проявляют и в отношении функций IMMUTABLE. В целом вообще неразумно выбирать из базы данных таблицы в функциях IMMUTABLE, поскольку постоянство нарушится при первом же изменении содержимого таблицы. Однако QHB не запрещает вам это делать.

К распространенным ошибкам относится пометка функции IMMUTABLE когда ее результаты зависят от параметра конфигурации. Например, функция, управляющая временем, вполне может выдавать результаты, которые зависят от настройки TimeZone. В целях безопасности такие функции должны быть помечены как STABLE.

Примечание
В целях предотвращения модификации данных QHB требует, чтобы функции, помеченные как STABLE или IMMUTABLE, не содержали никаких команд SQL, кроме SELECT. (это не вполне ошибкоустойчиво, так как такие функции все равно могут вызывать функции VOLATILE, модифицирующие базу данных. Если вы это сделаете, то обнаружите, что функция STABLE или IMMUTABLE не замечает изменений в базе данных, сделанных вызванной функцией, так как те не проявляются в ее снимке.)

Функции на процедурном языке

QHB позволяет писать пользовательские функции на других языках, помимо SQL и C/RUST. Эти языки обычно называются процедурными языками (PL). Процедурные языки не встроены в сервер QHB; они реализованы в подгружаемых модулях. Дополнительную информацию см. в главе Процедурные языки и следующих за ней главах.

Внутренние функции

Внутренние функции — это функции, написанные на C или на Rust, которые статически скомпонованы в сервер QHB. В «теле» определения функции указано ее имя на C, которое не обязательно должно совпадать с именем, объявленным для использования в SQL. (Из соображений обратной совместимости принимается и пустое «тело» функции, означая, что имя функции на C совпадает с SQL-именем).

Обычно все присутствующие на сервере внутренние функции объявляются во время инициализации кластера базы данных (см. раздел Создание кластера базы данных), но пользователь может использовать CREATE FUNCTION для создания дополнительного псевдонима внутренней функции. Внутренние функции объявляются в CREATE FUNCTION с именем языка internal. Например, таким образом можно создать псевдоним для функции sqrt:

CREATE FUNCTION square_root(double precision) RETURNS double precision
    AS 'dsqrt'
    LANGUAGE internal
    STRICT;

(Большинство внутренних функций следует объявлять как «строгие» (STRICT).)

Примечание
Не все «предопределенные» функции являются «внутренними» в вышеописанном смысле. Некоторые предопределенные функции написаны на SQL.

Функции на нативном языке

Пользовательские нативные функции могут быть написаны на C, Rust, С++ или других компилируемых с С языках. Такие функции компилируются в динамически загружаемые объекты (также называемые разделяемыми библиотеками) и загружаются сервером по требованию. Именно возможность динамической загрузки отличает «нативные» функции от «внутренних» — фактические стандарты оформления кода у них по сути одинаковы. (Соответственно, стандартная библиотека внутренних функций является обширным источником примеров кода для пользовательских нативных функций.)

В настоящее время для нативных функций применяется только одно соглашение о вызовах, называемое «версия 1» («extern C», все означенные языки имеют возможность следовать такому соглашению). Поддержка этого соглашения (для языка С) обозначается вызовом функции с макросом PG_FUNCTION_INFO_V1(), как показано ниже.

Динамическая загрузка

При первом вызове в сеансе пользовательской функции в определенном загружаемом объектном файле динамический загрузчик загружает этот объектный файл в память, чтобы можно было вызвать эту функцию. Поэтому в команде CREATE FUNCTION для пользовательской функции на C/RUST нужно указывать две детали: имя загружаемого объектного файла и имя уровня С (символ ссылки) конкретной функции, вызываемой в этом объектном файле. Если имя уровня С не указано явно, предполагается, что оно совпадает с именем функции в SQL.

Для поиска разделяемого объектного файла по имени, указанному в команде CREATE FUNCTION, используется следующий алгоритм:

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

Если имя начинается со строки $libdir, эта часть заменяется именем каталога библиотек пакетов QHB, которое определяется во время сборки.

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

В противном случае (файл не найден в пути или содержит не абсолютный путь к каталогу), динамический загрузчик попытается принять имя как есть, что, скорее всего, не удастся. (Ненадежно полагаться на текущий рабочий каталог).

Если эта последовательность не работает, к указанному имени добавляется расширение имени файла разделяемой библиотеки, принятое на данной платформе (зачастую .so), и эта последовательность повторяется. Если это также не удается, происходит сбой загрузки.

Рекомендуется размещать разделяемые библиотеки относительно $libdir или по пути динамической библиотеки. Это упрощает обновление версий, если новая установка находится в другом месте. Какой именно каталог обозначается как $libdir, можно узнать с помощью команды pg_config --pkglibdir.

Идентификатор пользователя, от имени которого работает сервер QHB, должен иметь возможность пройти путь к файлу, который вы собираетесь загрузить. Создание файла или каталога более высокого уровня, который недоступен для чтения или исполнения пользователем qhb, — распространенная ошибка.

В любом случае, имя файла, указанное в команде CREATE FUNCTION, записывается в системные каталоги буквально, поэтому если файл необходимо загрузить снова, применяется та же процедура.

Примечание
QHB не будет компилировать функцию на C/Rust автоматически. Прежде чем ссылаться на объектный файл в команде CREATE FUNCTION, его необходимо скомпилировать. Дополнительную информацию см в разделе Компиляция и связывание динамически загружаемых функций.

Чтобы убедиться, что динамически загружаемый объектный файл не загружается на несовместимый сервер, QHB проверяет, содержит ли этот файл «магический блок» с надлежащим содержимым. Это позволяет серверу обнаруживать очевидные несовместимости, такие как код, скомпилированный для другой основной версии QHB. Чтобы включить в модуль магический блок, запишите это в один (и только один) из исходных файлов модуля после включения заголовочного файла fmgr.h:

PG_MODULE_MAGIC;

После первого использования динамически загруженный объектный файл сохраняется в памяти. Последующие вызовы в том же сеансе функции (или функций) в этом файле приведут только к небольшим накладным расходам, связанным с поиском в таблице символов. Если вам необходимо принудительно перезагрузить объектный файл, например, после его перекомпиляции, начните новый сеанс.

Динамически загружаемый файл может дополнительно содержать функции инициализации и завершения. Если файл содержит функцию с именем _PG_init, эта функция будет вызвана сразу после загрузки файла. Эта функция не принимает параметры и должна возвращать void. Если файл содержит функцию с именем _PG_fini, эта функция будет вызвана непосредственно перед выгрузкой файла. Эта функция аналогично не принимает параметры и должна возвращать void. Обратите внимание, что _PG_fini будет вызываться только во время выгрузки файла, а не во время завершения процесса. (В настоящее время выгрузки выключены и никогда не произойдут, но это может измениться в будущем).

Базовые типы в функциях языка C/RUST

Чтобы знать, как писать функции на языке C/RUST, нужно знать, как QHB внутренне представляет базовые типы данных и как их можно передавать в функции и из функций. Внутри QHB рассматривает базовый тип как «блок памяти». Пользовательские функции, которые вы определяете для типа, в свою очередь, определяют как QHB может с ним работать. То есть QHB будет только сохранять и извлекать данные с диска, а ваши пользовательские функции использовать для ввода, обработки и вывода данных.

Базовые типы могут иметь один из трех внутренних форматов:

передается по значению, фиксированной длины

передается по ссылке, фиксированной длины

передается по ссылке, переменной длины

Типы, передаваемые по значению, могут иметь длину только 1, 2 или 4 байта (а также 8 байтов, если sizeof(Datum) равен 8 на вашей машине). Типы следует аккуратно определять таким образом, чтобы они были одинакового размера (в байтах) на всех архитектурах. Например, тип long опасен, потому что на одних машинах его размер составляет 4 байта, а на других — 8 байтов, тогда как размер типа int на большинстве машин Unix составляет 4 байта. Разумной реализацией типа int4 на машинах Unix может быть:

/* 4-байтное целое, передаваемое по значению */
typedef int int4;

(В настоящем коде С QHB этот тип называется int32, потому что в C существует соглашение, согласно которому int XX означает XX бит. Поэтому обратите внимание также на то, что тип int8 в С имеет размер 1 байт. тип int8, принятый в SQL, в С называется int64. См. также таблицу Эквивалентные типы C для встроенных типов SQL.)

С другой стороны, типы фиксированной длины любого размера могут передаваться по ссылке. Например, вот пример реализации типа QHB:

/* 16-байтная структура, передаваемая по ссылке */
typedef struct
{
    double  x, y;
} Point;

В функции QHB и из них могут передаваться только указатели на такие типы. Чтобы вернуть значение такого типа, выделите требуемый объем памяти с помощью palloc, заполните выделенную память и верните указатель на нее. (При этом, если вы просто хотите вернуть то же значение, что было получено в одном из ваших входных аргументов того же типа данных, вы можете пропустить лишний palloc и просто вернуть указатель на входное значение.)

Наконец, все типы переменной длины также должны передаваться по ссылке. Все типы переменной длины должны начинаться с непрозрачного поля длины размером ровно 4 байта, которое будет установлено с помощью SET_VARSIZE; никогда не устанавливайте это поле напрямую! Все данные, которые будут храниться в этом типе, должны быть расположены в памяти сразу после этого поля длины. Поле длины содержит общую длину структуры, то есть включает в себя размер самого поля длины.

Еще один важный момент — старайтесь не оставлять неинициализированных фрагментов в значениях типа данных; например, обязательно обнуляйте любые байты заполнителя выравнивания, которые могут присутствовать в структурах. Без этого планировщик может рассматривать логически равнозначные константы вашего типа данных как неравные, что приведет к построению неэффективных (хотя и корректных) планов.

Предупреждение
Никогда не изменяйте содержимое входного значения, передаваемого по ссылке. Если вы это сделаете, то скорее всего повредите данные на диске, поскольку полученный вами указатель может указывать напрямую на буфер диска. Единственное исключение из этого правила объясняется в подразделе Пользовательские агрегаты.

В качестве примера мы можем определить тип text следующим образом:

typedef struct {
    int32 length;
    char data[FLEXIBLE_ARRAY_MEMBER];
} text;

Запись [FLEXIBLE_ARRAY_MEMBER] означает, что фактическая длина части данных в этом объявлении не указана.

При манипулировании типами переменной длины следует быть внимательными, чтобы правильно распределить объем памяти и правильно задать поле длины. Например, если мы хотим сохранить 40 байтов в структуре типа text, мы можем использовать такой фрагмент кода:

#include "postgres.h"
...
char buffer[40]; /* наши исходные данные */
...
text *destination = (text *) palloc(VARHDRSZ + 40);
SET_VARSIZE(destination, VARHDRSZ + 40);
memcpy(destination->data, buffer, 40);
...

VARHDRSZ — это то же самое, что и sizeof(int32), но для обозначения размера заголовка типа переменной длины хорошим стилем считается использование макроса VARHDRSZ. Кроме того, поле длины должно устанавливаться макросом SET_VARSIZE, а не путем простого присваивания.

Таблица Эквивалентные типы C для встроенных типов SQL указывает, какой тип языка C/RUST соответствует тому или иному типу SQL при написании функции на языке C/RUST с использованием встроенного типа QHB. В столбце «Определен в» указан заголовочный файл, который необходимо включить, чтобы получить определение типа. (Фактическое определение может быть в другом файле, который включается из указанного. Рекомендуется, чтобы пользователи придерживались определенного интерфейса.) Обратите внимание, что в любой исходный файл всегда следует сначала включать postgres.h, поскольку он объявляет ряд вещей, которые вам в любом случае понадобятся.

Таблица 1. Эквивалентные типы C для встроенных типов SQL

Тип SQL | Тип C | Определен в
boolean | bool | postgres.h (возможно, встроенный компилятор)
box | BOX* | utils/geo_decls.h
bytea | bytea* | postgres.h
"char" | char | (встроенный компилятор)
character | BpChar* | postgres.h
cid | CommandId | postgres.h
date | DateADT | utils/date.h
smallint ( int2 ) | int16 | postgres.h
int2vector | int2vector* | postgres.h
integer ( int4 ) | int32 | postgres.h
real ( float4 ) | float4* | postgres.h
double precision ( float8 ) | float8* | postgres.h
interval | Interval* | datatype/timestamp.h
lseg | LSEG* | utils/geo_decls.h
name | Name | postgres.h
oid | Oid | postgres.h
oidvector | oidvector* | postgres.h
path | PATH* | utils/geo_decls.h
point | POINT* | utils/geo_decls.h
regproc | regproc | postgres.h
text | text* | postgres.h
tid | ItemPointer | storage/itemptr.h
time | TimeADT | utils/date.h
time with time zone | TimeTzADT | utils/date.h
timestamp | Timestamp* | datatype/timestamp.h
varchar | VarChar* | postgres.h
xid | TransactionId | postgres.h

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

Соглашение о вызовах версии 1

Соглашение о вызовах версии 1 опирается на макросы, которые устраняют большую часть сложности передачи аргументов и результатов. Функция на C/RUST версии 1 всегда объявляется так:

Datum funcname(PG_FUNCTION_ARGS)

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

PG_FUNCTION_INFO_V1(funcname);

(Обычно его записывают непосредственно перед самой функцией.) Этот вызов макроса не требуется для функций internal (внутренних), поскольку QHB предполагает, что все внутренние функции используют соглашение версии 1. Однако для динамически загружаемых функций этот макрос необходим.

В функции версии 1 каждый фактический аргумент выбирается с помощью макроса PG_GETARG_xxx(), который соответствует типу данных аргумента. (В нестрогих функциях необходимо предварительно проверить аргумент на NULL, используя PG_ARGISNULL(); см. ниже.) Результат возвращается макросом PG_RETURN_xxx() для возвращаемого типа. PG_GETARG_xxx() принимает в качестве аргумента номер выбираемого аргумента функции, где нумерация начинается с 0. PG_RETURN_xxx() принимает в качестве аргумента фактическое значение, которое нужно вернуть.

Вот несколько примеров использования соглашения о вызовах версии 1:

#include "postgres.h"
#include <string.h>
#include "fmgr.h"
#include "utils/geo_decls.h"

PG_MODULE_MAGIC;

/* по значению */

PG_FUNCTION_INFO_V1(add_one);

Datum
add_one(PG_FUNCTION_ARGS)
{
    int32   arg = PG_GETARG_INT32(0);

    PG_RETURN_INT32(arg + 1);
}

/* по ссылке, фиксированной длины */

PG_FUNCTION_INFO_V1(add_one_float8);

Datum
add_one_float8(PG_FUNCTION_ARGS)
{
    /* Макрос для FLOAT8 скрывает свою способность передачи по ссылке. */
    float8   arg = PG_GETARG_FLOAT8(0);

    PG_RETURN_FLOAT8(arg + 1.0);
}

PG_FUNCTION_INFO_V1(makepoint);

Datum
makepoint(PG_FUNCTION_ARGS)
{
    /* Здесь способность Point к передаче по ссылке не скрыта. */
    Point     *pointx = PG_GETARG_POINT_P(0);
    Point     *pointy = PG_GETARG_POINT_P(1);
    Point     *new_point = (Point *) palloc(sizeof(Point));

    new_point->x = pointx->x;
    new_point->y = pointy->y;

    PG_RETURN_POINT_P(new_point);
}

/* по ссылке, переменной длины */

PG_FUNCTION_INFO_V1(copytext);

Datum
copytext(PG_FUNCTION_ARGS)
{
    text     *t = PG_GETARG_TEXT_PP(0);

    /*
     * VARSIZE_ANY_EXHDR — это размер структуры в байтах минус
     * VARHDRSZ или VARHDRSZ_SHORT ее заголовочного файла. Постройте копию с
     * заголовочным файлом полной длины.
     */
    text     *new_t = (text *) palloc(VARSIZE_ANY_EXHDR(t) + VARHDRSZ);
    SET_VARSIZE(new_t, VARSIZE_ANY_EXHDR(t) + VARHDRSZ);

    /*
     * VARDATA — это указатель на область данных новой структуры. Источником
     * может быть короткий элемент данных, поэтому извлекайте его данные посредством VARDATA_ANY.
     */
    memcpy((void *) VARDATA(new_t), /* пункт назначения */
           (void *) VARDATA_ANY(t), /* источник */
           VARSIZE_ANY_EXHDR(t));   /* сколько байтов */
    PG_RETURN_TEXT_P(new_t);
}

PG_FUNCTION_INFO_V1(concat_text);

Datum
concat_text(PG_FUNCTION_ARGS)
{
    text  *arg1 = PG_GETARG_TEXT_PP(0);
    text  *arg2 = PG_GETARG_TEXT_PP(1);
    int32 arg1_size = VARSIZE_ANY_EXHDR(arg1);
    int32 arg2_size = VARSIZE_ANY_EXHDR(arg2);
    int32 new_text_size = arg1_size + arg2_size + VARHDRSZ;
    text *new_text = (text *) palloc(new_text_size);

    SET_VARSIZE(new_text, new_text_size);
    memcpy(VARDATA(new_text), VARDATA_ANY(arg1), arg1_size);
    memcpy(VARDATA(new_text) + arg1_size, VARDATA_ANY(arg2), arg2_size);
    PG_RETURN_TEXT_P(new_text);
}

Предполагая, что приведенный выше код был подготовлен в файле funcs.c и скомпилирован в общий объект, мы могли бы определить функции для QHB с помощью таких команд:

CREATE FUNCTION add_one(integer) RETURNS integer
     AS 'КАТАЛОГ/funcs', 'add_one'
     LANGUAGE C STRICT;

-- обратите внимание — это перегрузка имени функции SQL "add_one"
CREATE FUNCTION add_one(double precision) RETURNS double precision
     AS 'КАТАЛОГ/funcs', 'add_one_float8'
     LANGUAGE C STRICT;

CREATE FUNCTION makepoint(point, point) RETURNS point
     AS 'КАТАЛОГ/funcs', 'makepoint'
     LANGUAGE C STRICT;

CREATE FUNCTION copytext(text) RETURNS text
     AS 'КАТАЛОГ/funcs', 'copytext'
     LANGUAGE C STRICT;

CREATE FUNCTION concat_text(text, text) RETURNS text
     AS 'КАТАЛОГ/funcs', 'concat_text'
     LANGUAGE C STRICT;

Здесь КАТАЛОГ обозначает каталог файла разделяемой библиотеки (например, каталог учебного руководства по QHB, который содержит код для примеров, используемых в этом разделе). (Лучше было бы просто написать 'funcs' в предложении AS после добавления КАТАЛОГА в путь поиска. В любом случае, мы можем опустить принятое в системе расширение для разделяемых библиотек, обычно .so.)

Обратите внимание, что мы указали функции как «строгие» (strict), имея в виду, что система должна автоматически подразумевать результат NULL, если в каком-то из входных значений передается NULL. Благодаря этому, мы избегаем необходимости проверять входные значения на NULL в коде функции. Без этого нам пришлось бы явно проверять значения на NULL, используя PG_ARGISNULL().

Макрос PG_ARGISNULL(n) позволяет функции проверять каждое входное значение на NULL. (Конечно, это необходимо делать только в функциях, не объявленных как «строгие»). Как и в случае с макросом PG_GETARG_xxx(), нумерация входных аргументов начинается с нуля. Обратите внимание, что не следует выполнять PG_GETARG_xxx(), не убедившись, что соответствующий аргумент не NULL. Чтобы вернуть результат NULL, выполните макрос PG_RETURN_NULL(); это работает как в строгих, так и в нестрогих функциях.

На первый взгляд, соглашения о кодировании в версии 1 могут показаться просто бессмысленным мракобесием по сравнению с использованием простых соглашений о вызовах языка C/RUST. Тем не менее, они позволяют работать с аргументами и возвращаемыми значениями, в которых может передаваться NULL, а также со значениями в формате TOAST (сжатыми или находящимися вне основной программы).

Другие возможности, предоставляемые интерфейсом версии 1, — это два варианта макроса PG_GETARG_xxx(). Первый из них, PG_GETARG_xxx_COPY(), гарантирует возврат копии указанного аргумента, который безопасен для внесения записей. (Обычные макросы иногда будут возвращать указатель на значение, физически хранящееся в таблице, в которую нельзя записывать. Использование макроса PG_GETARG_xxx_COPY() гарантирует доступный для записи результат). Второй вариант состоит из макроса PG_GETARG_xxx_SLICE(), который принимает три аргумента. Первый — это номер аргумента функции (как указано выше). Второй и третий — это смещение и длина возвращаемого сегмента. Смещения отсчитываются от нуля, а отрицательная длина требует возврата оставшейся части значения. Эти макросы обеспечивают более эффективный доступ к частям больших значений в том случае, если они имеют «внешний» (external) тип хранения. (Тип хранения столбца можно указать командой ALTER TABLE имя_таблицы ALTER COLUMN имя_столбца SET STORAGE тип_хранения. тип_хранения может быть plain, external, extended или main.)

Наконец, соглашения о вызовах функций версии 1 позволяют возвращать множества результатов (подраздел Возврат множеств) и реализовывать триггерные функции (глава Триггеры).

Написание кода

Прежде чем перейти к более сложным темам, мы должны обсудить некоторые правила кодирования для функций на языке C/RUST QHB. Хотя и возможно загрузить в QHB функции, написанные на других языках помимо C/RUST, обычно это сложно (если вообще возможно), потому что другие языки, такие как C++, FORTRAN или Pascal, зачастую не следуют соглашению о вызовах, принятому в C/RUST. То есть другие языки передают аргументы и возвращают значения между функциями разными способами. По этой причине мы будем предполагать, что ваши функции C/RUST на самом деле написаны на C/RUST.

Основные правила написания и построения функций C/RUST следующие:

Чтобы узнать, где в вашей системе (или в системе, на которой будут работать ваши пользователи) установлены заголовочные файлы сервера QHB, используйте pg_config --includedir-server.

Для компиляция и компоновка вашего кода, который можно динамически загрузить в QHB, всегда требуются специальные флаги. Подробное объяснение того, как это сделать для вашей конкретной операционной системы, см. в подразделе Компиляция и связывание динамически загружаемых функций.

Не забудьте определить «магический блок» для вашей общей библиотеки, как описано в подразделе Динамическая загрузка.

При выделении памяти используйте функции QHB palloc и pfree вместо соответствующих функций библиотеки C malloc и free. Память, выделенная palloc, будет автоматически освобождаться в конце каждой транзакции, предотвращая утечки памяти.

Всегда обнуляйте байты ваших структур, используя функцию memset (или сразу выделяйте их функцией palloc0). Даже если вы присвоите значение каждому полю вашей структуры, в ней могут остаться заполняющие байты выравнивания («прорехи» в структуре), содержащие «мусорные »значения. Без этого будет трудно поддерживать индексы или соединения по хешу, так как для вычисления хеша придется выбирать из вашей структуры данных только значимые биты. Планировщик тоже иногда полагается на сравнение констант с помощью побитового равенства, так что если логически равные значения окажутся неравными побитово, можно получить нежелательные результаты планирования.

Большинство внутренних типов QHB объявлены в postgres.h, в то время как интерфейсы диспетчера функций ( PG_FUNCTION_ARGS и т. д.) находятся в fmgr.h, поэтому нужно будет включить как минимум эти два файла. По причинам переносимости лучше сначала включить postgres.h, а не заголовочные файлы других систем или пользователей. При включении postgres.h также автоматически будут включены elog.h и palloc.h.

Имена символов, определенные в объектных файлах, не должны конфликтовать друг с другом или с символами, определенными в исполняемых файлах сервера QHB. При получении сообщения об ошибках в связи с подобными конфликтами вам придется переименовать ваши функции или переменные.

Компиляция и связывание динамически загружаемых функций

Прежде чем вы сможете использовать свои функции C/RUST, расширяющие возможности QHB, их нужно скомпилировать и скомпоновать особым образом для создания файла, который сервер сможет динамически загрузить. Точнее говоря, необходимо создать разделяемую библиотеку.

Для получения информации, выходящей за рамки этого раздела, вам следует прочитать документацию по вашей операционной системе, в частности, страницы руководства компилятора C/RUST, cc, и компоновщика, ld. Кроме того, исходный код QHB содержит несколько рабочих примеров в каталоге contrib. Однако, полагаясь на эти примеры, вы сделаете свои модули зависимыми от доступности исходного кода QHB.

Создание разделяемых библиотек обычно аналогично сборке исполняемых файлов: сначала исходные файлы компилируются в объектные файлы, затем объектные файлы связываются вместе. Объектные файлы должны быть созданы как позиционно-независимый код (PIC), что концептуально означает, что при загрузке исполняемым файлом они могут быть размещены в произвольном месте в памяти загружаются исполняемым файлом. (Объектные файлы, предназначенные для компоновки самих исполняемых файлов, обычно компилируются по-другому). Команда для сборки разделяемой библиотеки содержит специальные флаги, чтобы отличать ее от сборки исполняемого файла (по крайней мере, теоретически — в некоторых системах эта практика намного уродливее).

В следующем примере мы предполагаем, что ваш исходный код находится в файле foo.c и мы создадим разделяемую библиотеку foo.so Промежуточный объектный файл будет называться foo.o, если не указано иное. Разделяемая библиотека может содержать более одного объектного файла, но здесь мы используем только один.

Linux

Флаг компилятора для создания PIC — -fPIC. Флаг компилятора для создания разделяемой библиотеки — -shared. Полный пример выглядит так:

cc -fPIC -c foo.c
cc -shared -o foo.so foo.o

Полученный файл разделяемой библиотеки затем можно загрузить в QHB. Задавая имя файла команде CREATE FUNCTION, необходимо указать имя файла разделяемой библиотеки, а не промежуточного объектного файла. Обратите внимание, что стандартное расширение разделяемой библиотеки, принятое в системе (обычно .so или .sl ), в команде CREATE FUNCTION можно опустить, и обычно так и следует сделать для лучшей переносимости.

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

Аргументы составного типа

У составных типов нет фиксированного макета, как у структур C. Аргументы составного типа могут содержать поля NULL. Кроме того, составные типы, являющиеся частью иерархии наследования, могут иметь иные поля, нежели остальные члены той же иерархии наследования. Поэтому QHB предоставляет функциям интерфейс для доступа к полям составных типов из C.

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

SELECT name, c_overpaid(emp, 1500) AS overpaid
    FROM emp
    WHERE name = 'Bill' OR name = 'Sam';

Используя соглашения о вызовах версии 1, мы можем определить c_overpaid как:

#include "postgres.h"
#include "executor/executor.h"  /* для GetAttributeByName() */

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(c_overpaid);

Datum
c_overpaid(PG_FUNCTION_ARGS)
{
    HeapTupleHeader  t = PG_GETARG_HEAPTUPLEHEADER(0);
    int32            limit = PG_GETARG_INT32(1);
    bool isnull;
    Datum salary;

    salary = GetAttributeByName(t, "salary", &isnull);
    if (isnull)
        PG_RETURN_BOOL(false);
    /* Как вариант, можно записать PG_RETURN_NULL() для значения поля «зарплата», равного NULL. */

    PG_RETURN_BOOL(DatumGetInt32(salary) > limit);
}

GetAttributeByName — системная функция QHB, которая возвращает атрибуты из указанной строки. Она принимает три аргумента: аргумент типа HeapTupleHeader, передаваемый в функцию, имя требуемого атрибута и возвращаемый параметр, который сигнализирует о том, что атрибут имеет значение NULL. GetAttributeByName возвращает значение Datum, которое можно преобразовать в правильный тип данных с помощью соответствующего макроса DatumGetXXX(). Обратите внимание, что возвращаемое значение не имеет смысла, если установлен флаг NULL; всегда проверяйте этот флаг, прежде чем пытаться что-либо сделать с результатом.

Существует также функция GetAttributeByNum, которая выбирает целевой атрибут по номеру столбца, а не по имени.

Следующая команда объявляет функцию c_overpaid в SQL:

CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean
    AS 'КАТАЛОГ/funcs', 'c_overpaid'
    LANGUAGE C STRICT;

Обратите внимание, что мы использовали STRICT, чтобы не пришлось проверять, имеют ли входные аргументы значение NULL.

Возврат строк (составных типов)

Чтобы вернуть значение строки или составного типа из функции на языке C/RUST, можно использовать специальный API, который предоставляет макросы и функции, скрывающие большую часть сложности построения составных типов данных. Чтобы использовать этот API, исходный файл должен включать:

#include "funcapi.h"

Существует два способа создания составного значения данных (далее «кортеж»): его можно построить из массива значений Datum или из массива строк C/RUST, которые можно передать во функции преобразования ввода для типов данных столбца кортежа. В любом случае сначала необходимо получить или создать дескриптор TupleDesc для структуры кортежа. При работе со значениями Datum вы передаете TupleDesc функции BlessTupleDesc, а затем вызываете для каждой строки heap_form_tuple. При работе со строками C/RUST вы передаете TupleDesc функции TupleDescGetAttInMetadata, а затем вызываете для каждой строки BuildTupleFromCStrings. В случае функции, возвращающей множество кортежей, все этапы настройки можно выполнить один раз при первом вызове функции.

Для настройки необходимого TupleDesc доступно несколько вспомогательных функций. Рекомендуемый способ сделать это в большинстве функций, возвращающих составные значения, это вызвать:

TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo,
                                   Oid *resultTypeId,
                                   TupleDesc *resultTupleDesc)

передавая ту же самую структуру fcinfo, которая была передана самой вызывающей функции. (Для этого, конечно, нужно использовать соглашения о вызовах версии 1). В resultTypeId можно передать NULL или адрес локальной переменной для получения OID типа результата функции. resultTupleDesc должен быть адресом локальной переменной TupleDesc. Убедитесь, что результатом является TYPEFUNC_COMPOSITE; если это так, значит, resultTupleDesc был заполнен требуемой структурой TupleDesc. (Если это не так, вы можете сообщить об ошибке по образцу: «функция, возвращающая запись, вызвана в контексте, не допускающем запись типа»).

Совет
Функция get_call_result_type разрешает получить фактический тип результата полиморфной функции; так что она полезна в функциях, которые возвращают скалярные полиморфные результаты, а не только в функциях, которые возвращают составные типы. Выходной параметр resultTypeId полезен в первую очередь для функций, возвращающих полиморфные скаляры.

Примечание
У get_call_result_type есть родственная функция get_expr_result_type, которую можно использовать для получения ожидаемого типа результата для вызова функции, представленного деревом выражения. Ее можно использовать, если требуется определить тип результата вне самой функции. Существует также функция get_func_result_type, которую можно использовать, когда известен только OID функции. Однако обе эти функции не подходят для функций, объявленных как возвращающие record, а функция get_func_result_type не позволяет получить полиморфные типы, поэтому вместо них рекомендуется все-таки пользоваться функцией get_call_result_type.

Ранее для получения TupleDesc использовались также следующие, сейчас признанные устаревшими функции:

TupleDesc RelationNameGetTupleDesc(const char *relname)

(возвращает TupleDesc для типа строки указанного отношения) и:

TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)

(возвращает TupleDesc на основе OID типа). Ее можно использовать для получения TupleDesc для базового или составного типа. Однако она не подходит для функции, которая возвращает record, и не может разрешать полиморфные типы.

После получения TupleDesc вызовите:

TupleDesc BlessTupleDesc(TupleDesc tupdesc)

если планируете работать со значениями Datum, или:

AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)

если планируете работать со строками C/RUST. Если вы пишете функцию, возвращающую множество данных, можно сохранить результаты этих функций в структуре FuncCallContext — используйте поле tuple_desc или attinmeta соответственно.

При работе с Datum используйте функцию:

HeapTuple heap_form_tuple(TupleDesc tupdesc, Datum *values, bool *isnull)

чтобы скомпоновать HeapTuple из переданных ей пользовательских данных в форме Datum.

При работе со строками C/RUST используйте функцию:

HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)

чтобы скомпоновать HeapTuple из переданных ей пользовательских данных в форме строки C/RUST. values — это массив строк C/RUST, по одной на каждый атрибут возвращаемой строки. Каждая строка C/RUST должна иметь формат, ожидаемый функцией ввода типа данных атрибута. Чтобы вернуть значение NULL для одного из этих атрибутов, нужно задать NULL в соответствующем указателе в массиве values. Эту функцию нужно будет вызывать снова для каждой возвращаемой строки.

Как только вы соберете кортеж для возврата из своей функции, его следует преобразовать в тип Datum. Воспользуйтесь функцией:

HeapTupleGetDatum(HeapTuple tuple)

чтобы преобразовать HeapTuple в надлежащий Datum. Этот Datum можно вернуть напрямую, если вы намереваетесь вернуть только одну строку, или использовать как текущее возвращаемое значение в функции, возвращающей множество.

Пример приведен в следующем разделе.

Возврат множеств

Для функций на C/RUST существует две возможности возврата множеств (нескольких строк). Первый способ, называемый методом ValuePerCall (значение за вызов), состоит в многократном вызове функции, возвращающей множество (при этом ей каждый раз передаются одни и те же аргументы), и при каждом вызове она возвращает по одной новой строке, пока те не закончатся, о чем функция просигнализирует, возвращая NULL. Таким образом, функция, возвращающая множество (Set-Returning Function, SRF), должна от вызова к вызову сохранять свое состояние в достаточной степени, чтобы помнить, что она делает, и при каждом вызове возвращать надлежащие данные. Другой способ, называемый методом Materialize (Материализация), состоит в том, что SRF заполняет и возвращает объект tuplestore, содержащий сразу все результирующее множество; таким образом, для получения всего результата производится всего один вызов, и никакое состояние между вызовами сохранять не нужно.

Пользуясь методом ValuePerCall, важно помнить, что выполнение запроса до завершения не гарантировано; иными словами, при указании такого параметра, как LIMIT, исполнитель запроса может перестать вызывать функцию, возвращающую множества, раньше, чем будут выбраны все строки. Это означает, что выполнять в последнем вызове действия, связанные с очисткой, небезопасно, так как он может вообще не произойти. Для функций, которым необходим доступ к внешним ресурсам, например файловым дескрипторам, рекомендуется использовать метод Materialize.

В оставшейся части данного раздела описывается ряд наиболее часто применяющихся (хотя и не обязательных) вспомогательных макросов для SRF, реализующих метод ValuePerCall. Дополнительную информацию о методе Materialize можно найти в файле src/backend/utils/fmgr/README. Кроме того, в модулях contrib в комплекте исходного кода QHB содержится много примеров SRF, реализующих как метод ValuePerCall, так и метод Materialize.

Для использования описанных здесь макросов поддержки ValuePerCall включите funcapi.h. Эти макросы работают со структурой FuncCallContext, содержащей состояние, которое нужно сохранять между вызовами. Внутри вызываемой SRF указатель на FuncCallContext удерживается между вызовами в поле fcinfo->flinfo->fn_extra. Макросы автоматически заполняют данное поле при первом использовании и в при последующих вызовах ожидают обнаружить в нем этот же указатель.

typedef struct FuncCallContext
{
    /*
     * Счетчик числа произведенных ранее вызовов
     *
     * посредством макроса SRF_FIRSTCALL_INIT() call_cntr присваивается начальное значение 0 , которое
     * увеличивается на 1 при каждом вызове SRF_RETURN_NEXT().
     */
    uint64 call_cntr;

    /*
     * (НЕОБЯЗАТЕЛЬНО) максимальное число вызовов
     *
     * max_calls не является обязательным и присутствует здесь только для удобства.
     * Если это значение не задано, вы должны предоставить альтернативный способ определить, когда
     * функция закончила работу.
     */
    uint64 max_calls;

    /*
     * (НЕОБЯЗАТЕЛЬНО) указатель на разнообразную дополнительную информацию, предоставленную пользователем
     *
     * user_fctx используется как указатель на ваши собственные данные, позволяя сохранять
     * произвольную контекстную информацию между вызовами вашей функции.
     */
    void *user_fctx;

    /*
     * (НЕОБЯЗАТЕЛЬНО) указатель на структуру, содержащую метаданные ввода типа атрибута
     *
     * attinmeta применяется при возврате кортежей (т. е. составных типов данных)
     * и не применяется при возврате базовых типов данных. Он нужен, только
     * если вы намерены использовать BuildTupleFromCStrings() для создания возвращаемого
     * кортежа.
     */
    AttInMetadata *attinmeta;

    /*
     * Контекст памяти, используемый для структур, которые должны пережить несколько вызовов
     *
     * multi_call_memory_ctx настраивается при помощи SRF_FIRSTCALL_INIT() и используется
     * SRF_RETURN_DONE() для очистки. Это наиболее подходящий контекст
     * для любых блоков памяти, которые предназначены для многократного использования при повторных вызовах
     * SRF.
     */
    MemoryContext multi_call_memory_ctx;

    /*
     * (НЕОБЯЗАТЕЛЬНО) указатель на структуру, содержащую описание кортежа
     *
     * tuple_desc применяется при возврате кортежей (т. е. составных типов данных)
     * и требуется, только если вы собираетесь формировать кортежи с помощью функции
     * heap_form_tuple(), а не BuildTupleFromCStrings().  Обратите внимание, что
     * сохраняемый здесь указатель TupleDesc обычно должен сначала пройти через вызов
     * BlessTupleDesc().
     */
    TupleDesc tuple_desc;

} FuncCallContext;

Для SRF предоставляется несколько макросов, использующих эту инфраструктуру:

SRF_IS_FIRSTCALL()

Используйте этот макрос, чтобы определить, вызывается ли ваша функция в первый раз или. При первом вызове (но не при последующих) используйте:

SRF_FIRSTCALL_INIT()

чтобы инициализировать FuncCallContext. При каждом вызове функции, включая первый, используйте:

SRF_PERCALL_SETUP()

чтобы подготовиться к применению FuncCallContext.

Если у вашей функции есть данные для возврата, используйте:

SRF_RETURN_NEXT(funcctx, result)

чтобы передать их вызывающему. (переменная result должна иметь тип Datum и содержать либо одно значение, либо кортеж, подготовленный, как описано выше). Наконец, когда ваша функция закончит возвращать данные, используйте:

SRF_RETURN_DONE(funcctx)

чтобы провести очистку и завершить работу SRF.

Текущий контекст памяти, в котором вызывается SRF, является временным и будет очищаться между вызовами. Это означает, что вам не нужно вызывать pfree для всех блоков памяти, которые вы выделили с помощью palloc; они все равно будут освобождены. Однако если вы хотите распределить какие-либо структуры данных между вызовами, вам следует поместить их в другое место. Подходящим местом для любых данных, которые должны сохраняться до завершения работы SRF, является контекст памяти, на который ссылается multi_call_memory_ctx. В большинстве случаев это означает, что вы должны переключиться на multi_call_memory_ctx при выполнении настройки первого вызова. Для сохранения указателя на такие долгоживущие структуры данных используйте поле funcctx->user_fctx. (Данные, выделенные в контексте multi_call_memory_ctx, автоматически освободятся в конце запроса, поэтому их также нет необходимости освобождать вручную.)

Предупреждение!
В то время как фактические аргументы функции остаются неизменными между вызовами, если вы распаковываете значения аргументов (что обычно делается прозрачно с помощью макроса PG_GETARG_xxx) во временном контексте, то распакованные копии будут освобождаться в каждом цикле. Соответственно, если вы сохраняете ссылки на такие значения в вашем user_fctx, следует либо скопировать их после распаковки в multi_call_memory_ctx, либо обязательно распаковывать значения только в этом контексте.

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

Datum
my_set_returning_function(PG_FUNCTION_ARGS)
{
    FuncCallContext  *funcctx;
    Datum             result;
    другие необходимые объявления

    if (SRF_IS_FIRSTCALL())
    {
        MemoryContext oldcontext;

        funcctx = SRF_FIRSTCALL_INIT();
        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
        /* Здесь содержится код подготовки при первом вызове: */
        пользовательский код
        если возвращается составной тип
            сформировать TupleDesc и, возможно, AttInMetadata
        конец условия возвращаемого составного типа
        пользовательский код
        MemoryContextSwitchTo(oldcontext);
    }

    /* Здесь содержится код подготовки при каждом вызове: */
    пользовательский код
    funcctx = SRF_PERCALL_SETUP();
    пользовательский код

    /* это единственный способ, которым мы можем проверить, последний ли это вызов: */
    if (funcctx->call_cntr < funcctx->max_calls)
    {
        /* Здесь мы возвращаем еще один результат: */
        пользовательский код
        получение результирующих значений Datum
        SRF_RETURN_NEXT(funcctx, result);
    }
    else
    {
        /* Здесь мы заканчиваем возвращать результаты, и нам просто нужно провести очистку: */
        пользовательский код
        SRF_RETURN_DONE(funcctx);
    }
}

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

PG_FUNCTION_INFO_V1(retcomposite);

Datum
retcomposite(PG_FUNCTION_ARGS)
{
    FuncCallContext     *funcctx;
    int                  call_cntr;
    int                  max_calls;
    TupleDesc            tupdesc;
    AttInMetadata       *attinmeta;

    /* действия, производимые только при первом вызове функции */
    if (SRF_IS_FIRSTCALL())
    {
        MemoryContext   oldcontext;

        /* создать контекст функции для сохранения данных между вызовами */
        funcctx = SRF_FIRSTCALL_INIT();

        /* переключить на контекст памяти, подходящий для нескольких вызовов функции */
        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

        /* общее число кортежей, подлежащих возврату */
        funcctx->max_calls = PG_GETARG_UINT32(0);

        /* Сформировать дескриптор кортежей для нашего результирующего типа */
        if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("function returning record called in context "
                            "that cannot accept type record")));
                            /* "функция, возвращающая запись, вызвана в контексте, который не может принять запись типа" /*

        /*
         * создать метаданные атрибута, которые позже понадобятся для формирования кортежей непосредственно из
         * строк C/RUST
         */
        attinmeta = TupleDescGetAttInMetadata(tupdesc);
        funcctx->attinmeta = attinmeta;

        MemoryContextSwitchTo(oldcontext);
    }

    /* действия, производимые при каждом вызове функции */
    funcctx = SRF_PERCALL_SETUP();

    call_cntr = funcctx->call_cntr;
    max_calls = funcctx->max_calls;
    attinmeta = funcctx->attinmeta;

    if (call_cntr < max_calls)    /* выполняется, когда предстоит еще несколько вызовов */
    {
        char       **values;
        HeapTuple    tuple;
        Datum        result;

        /*
         * подготовить массив значений для формирования возвращаемого кортежа.
         * Это должен быть массив строк C/RUST, который
         * позже будет обработан функциями ввода для типов данных.
         */
        values = (char **) palloc(3 * sizeof(char *));
        values[0] = (char *) palloc(16 * sizeof(char));
        values[1] = (char *) palloc(16 * sizeof(char));
        values[2] = (char *) palloc(16 * sizeof(char));

        snprintf(values[0], 16, "%d", 1 * PG_GETARG_INT32(1));
        snprintf(values[1], 16, "%d", 2 * PG_GETARG_INT32(1));
        snprintf(values[2], 16, "%d", 3 * PG_GETARG_INT32(1));

        /* сформировать кортеж */
        tuple = BuildTupleFromCStrings(attinmeta, values);

        /* преобразовать кортеж в данные */
        result = HeapTupleGetDatum(tuple);

        /* провести очистку (на самом деле в ней нет необходимости) */
        pfree(values[0]);
        pfree(values[1]);
        pfree(values[2]);
        pfree(values);

        SRF_RETURN_NEXT(funcctx, result);
    }
    else    /* выполняется, когда вызовов больше не осталось */
    {
        SRF_RETURN_DONE(funcctx);
    }
}

Один из способов объявить эту функцию в SQL:

CREATE TYPE __retcomposite AS (f1 integer, f2 integer, f3 integer);

CREATE OR REPLACE FUNCTION retcomposite(integer, integer)
    RETURNS SETOF __retcomposite
    AS 'имя_файла', 'retcomposite'
    LANGUAGE C IMMUTABLE STRICT;

Другой способ — использовать параметры OUT:

CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer,
    OUT f1 integer, OUT f2 integer, OUT f3 integer)
    RETURNS SETOF record
    AS 'имя_файла', 'retcomposite'
    LANGUAGE C IMMUTABLE STRICT;

Обратите внимание, что в этом методе выходной тип функции формально является анонимным типом record.

Полиморфные аргументы и возвращаемые типы

Функции на языке C/RUST можно объявить как принимающие и возвращающие полиморфные типы anyelement, anyarray, anynonarray, anyenum и anyrange. Более подробное объяснение, касающееся полиморфных функций, см. в подразделе Полиморфные типы. Когда аргументы или возвращаемые типы функции определены как полиморфные типы, автор функции не может заранее знать, с каким типом данных она будет вызываться или какой должна возвращать. В fmgr.h предусмотрены две подпрограммы, позволяющие функции C/RUST версии 1 определить фактические типы данных своих аргументов и тип, который ей нужно вернуть. Эти подпрограммы называются get_fn_expr_rettype(FmgrInfo *flinfo) и get_fn_expr_argtype(FmgrInfo *flinfo, int argnum). Они возвращают OID типа результата или аргумента, либо InvalidOid, если информация недоступна. Структура flinfo обычно доступна по ссылке fcinfo->flinfo. Параметр argnum (номер аргумента) задается, начиная с нуля. В качестве альтернативы get_fn_expr_rettype также можно использовать get_call_result_type. Помимо этого, существует функция get_fn_expr_variadic, которую позволяет определить, были ли переменные аргументы объединены в массив. Это полезно прежде всего для функций VARIADIC "any", поскольку такое объединение всегда будет происходить для функций с переменными аргументами, принимающих обычные типы массивов.

Например, предположим, что мы хотим написать функцию, которая принимает один элемент любого типа и возвращает одномерный массив этого типа:

PG_FUNCTION_INFO_V1(make_array);
Datum
make_array(PG_FUNCTION_ARGS)
{
    ArrayType  *result;
    Oid         element_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
    Datum       element;
    bool        isnull;
    int16       typlen;
    bool        typbyval;
    char        typalign;
    int         ndims;
    int         dims[MAXDIM];
    int         lbs[MAXDIM];

    if (!OidIsValid(element_type))
        elog(ERROR, "could not determine data type of input");

    /* получить предоставляемый элемент; будьте осторожны, если это NULL */
    isnull = PG_ARGISNULL(0);
    if (isnull)
        element = (Datum) 0;
    else
        element = PG_GETARG_DATUM(0);

    /* мы имеем дело с одной размерностью */
    ndims = 1;
    /* и одним элементом */
    dims[0] = 1;
    /* и нижняя граница равна 1 */
    lbs[0] = 1;

    /* получить требуемую информацию о типе элемента */
    get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);

    /* теперь сформировать массив */
    result = construct_md_array(&element, &isnull, ndims, dims, lbs,
                                element_type, typlen, typbyval, typalign);

    PG_RETURN_ARRAYTYPE_P(result);
}

Следующая команда объявляет функцию make_array в SQL:

CREATE FUNCTION make_array(anyelement) RETURNS anyarray
    AS 'КАТАЛОГ/funcs', 'make_array'
    LANGUAGE C IMMUTABLE;

Существует вариант полиморфизма, который доступен только для функций на языке C/RUST: они могут быть объявлены как принимающие параметры типа "any". (Обратите внимание, что это имя типа должно быть заключено в двойные кавычки, поскольку оно также является зарезервированным словом SQL). Он работает так же, как anyelement, за исключением того, что он не требует, чтобы различные аргументы "any" имели одинаковый тип, и не помогает определить тип результата функции. Функцию C/RUST также можно объявить с последним параметром VARIADIC "any". Он будет соответствовать одному или нескольким фактическим аргументам любого типа (не обязательно одинакового). Эти аргументы не будут собраны в массив, как это происходит с обычными функциями с переменными аргументами; они просто будут переданы функции по отдельности. Если применяется это свойство, то определять количество фактических аргументов и их типов нужно с помощью макроса PG_NARGS() и методов, описанных выше. Кроме того, пользователи такой функции могут захотеть использовать ключевое слово VARIADIC в вызове своей функции, ожидая, что функция будет рассматривать элементы массива как отдельные аргументы. При необходимости это поведение должна реализовать сама функция, предварительно определив с помощью get_fn_expr_variadic, был ли фактический аргумент помечен как VARIADIC.

Общая память и легкие блокировки

Встраиваемые расширения могут резервировать легкие блокировки и область в общей памяти при запуске сервера. Разделяемую библиотеку расширения нужно загрузить заранее, указав ее в shared_preload_libraries. Общая память резервируется путем вызова:

void RequestAddinShmemSpace(int size)

из вашей функции _PG_init.

Легкие блокировки резервируются путем вызова:

void RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)

из _PG_init. Это обеспечит доступность массива легких блокировок num_lwlocks под именем tranche_name. Для получения указателя на этот массив воспользуйтесь функцией GetNamedLWLockTranche.

Во избежание возможных условий гонки каждый сервер должен использовать легкую блокировку AddinShmemInitLock при подключении и инициализации распределения общей памяти, как показано здесь:

static mystruct *ptr = NULL;

if (!ptr)
{
        bool    found;

        LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
        ptr = ShmemInitStruct("my struct name", size, &found);
        if (!found)
        {
                инициализировать содержимое области shmem (общей памяти);
                получить все требуемые легкие блокировки:
                ptr->locks = GetNamedLWLockTranche("my tranche name");
        }
        LWLockRelease(AddinShmemInitLock);
}

Использование C ++ для расширяемости

Хотя серверная часть QHB написана на C/Rust, расширения для него можно написать на C++, если следовать этим рекомендациям:

Все функции, к которым обращается сервер, должны представлять для него интерфейс C; затем эти функции C/Rust могут вызывать функции C++. Например, для функций, к которым обращается сервер, нужно указать extern C. Это также необходимо для любых функций, которые передаются в виде указателей между серверной частью и кодом C++.

Освободите память, используя подходящий метод освобождения. Например, большая часть памяти сервера выделяется с помощью palloc(), поэтому для ее освобождения воспользуйтесь pfree(). В таких случаях использование принятой в C++ операции delete приведет к ошибке.

Не допускайте распространения исключений в коде C/Rust (используйте универсальный блок на верхнем уровне всех функций extern C/Rust). Это необходимо, даже если код C++ явно не генерирует какие-либо исключения, потому исключения все равно могут генерироваться такими событиями, как нехватка памяти. Любые исключения должны быть перехвачены и соответствующие ошибки переданы обратно в интерфейс C. Если возможно, скомпилируйте код C++ с указанием -fno-exception, чтобы полностью убрать исключения; в таких случаях вы должны выявлять ошибки в вашем коде C++, например, проверять на NULL адрес, возвращенный new().

При вызове серверных функций из кода C++ убедитесь, что стек вызовов C++ содержит только простые старые структуры данных (POD). Это необходимо, потому что ошибки сервера генерируют удаленную функцию longjmp (), которая не разворачивает стек вызовов C++ надлежащим образом для объектов, отличных от POD.

Резюмируя вышесказанное, лучше всего поместить код C++ за стену из функций extern C/RUST, которые взаимодействуют с сервером и предотвращают возникновение исключений, утечки памяти и потери стека вызовов.

Информация по оптимизации функций

По умолчанию функция — это просто «черный ящик», о поведении которого система знает очень мало. Однако это означает, что запросы, вызывающие функцию, могут выполняться менее эффективно, чем могли бы. Существует возможность предоставить планировщику дополнительную информацию, которая поможет ему оптимизировать вызовы функций.

Некоторые основные факты передаются декларативными аннотациями в команде CREATE FUNCTION. Наиболее важным из них является категория изменчивости функции (IMMUTABLE, STABLE или VOLATILE); при определении функции следует уделять особое внимание тому, чтобы правильно указать эту категорию. Также можно задать характеристику безопасности параллельного исполнения (PARALLEL UNSAFE, PARALLEL RESTRICTED или PARALLEL SAFE) также должно быть указано, если вы рассчитываете использовать эту функцию в параллельных запросах. Также может быть полезно указать примерную стоимость выполнения функции и/или количество строк, которые должна вернуть функция, возвращающая множества. Однако декларативный способ указания этих двух фактов позволяет указывать только константное значение, а этого зачастую недостаточно.

Также есть возможность прикрепить вспомогательную функцию для планировщика к функции, вызываемой из SQL (для вспомогательной она будет целевой функцией), и таким образом передать информацию о целевой функции, которая слишком сложна, чтобы ее можно было представить декларативно. Вспомогательную функцию для планировщика следует писать на языке C/RUST (хотя язык целевых функций может быть другим), поэтому ее можно отнести к расширенным возможностям, которыми будет пользоваться сравнительно небольшое число людей.

Вспомогательная функция для планировщика должна иметь следующую сигнатуру SQL:

supportfn(internal) returns internal

Чтобы соединить ее с целевой функций, нужно при создании последней добавить предложение SUPPORT.

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

Некоторые вызовы функций можно упростить при планировании, исходя из характеристик самой функции. Например, функцию int4mul(n, 1) можно упростить до просто n. Преобразование такого рода может выполнять вспомогательная функция для планировщика, обрабатывая запросы типа SupportRequestSimplify. Вспомогательная функция будет вызываться всякий раз, когда в дереве проанализированного запроса будет найдена ее целевая функция. Если вспомогательная функция обнаружит, что этот конкретный вызов можно упростить до какого-либо другого вида, она может сформировать и возвратить дерево запроса, представляющее уже это измененное выражение. Это автоматически работает и для операторов, основанных на этой функции, — в данном примере n * 1 также будет упрощено до n. (Но имейте в виду, что это просто пример; конкретно эту оптимизацию стандартный QHB на самом деле не производит.) Мы не гарантируем, что QHB никогда не вызовет целевую функцию в случаях, которые может упростить вспомогательная функция. Следует обеспечить строгую идентичность между упрощенным выражением и фактическим выполнением целевой функции.

Для целевых функций, возвращающих boolean, часто бывает полезно оценить процент строк, которые будут выбраны предложением WHERE при помощи этой функции. Эту оценку может выполнить вспомогательная функция, обрабатывающая запросы типа SupportRequestSelectivity.

Если время выполнения целевой функции сильно зависит от ее входных аргументов, имеет смысл предоставить ей переменную оценку затрат. Это можно сделать при помощи вспомогательной функции, обрабатывающей запросы типа SupportRequestCost.

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

Для целевых функций, возвращающих boolean, существует возможность преобразовать вызов функции, находящейся в WHERE, в предложение (или предложения) с индексируемыми операторами. Преобразованные предложения должны быть строго идентичны условию функции либо могут быть в некоторой степени менее строгими (то есть они могут принимать некоторые значения, которые не принимает функция с этим условием). В последнем случае условие индекса считается неполным; его можно использовать для поиска по индексу, но для каждой строки, возвращаемой этим индексом, должен выполняться вызов функции, чтобы проверить, удовлетворяет ли строка условию WHERE. Для создания таких условий вспомогательная функция должна обрабатывать запросы типа SupportRequestIndexCondition.

Пользовательские агрегаты

Агрегатные функции в QHB определяются в терминах значений состояния и функций перехода состояния. То есть агрегат работает с некоторым значением состояния, которое обновляется при обработке каждой последующей входной строки. Чтобы создать новую агрегатную функцию, надо выбрать тип данных для значения состояния, его начальное значение и функцию перехода состояния. Функция перехода состояния принимает предыдущее значение состояния и входное значение (или значения) агрегата для текущей строки и возвращает новое значение состояния. В случае, если желаемый результат агрегатной функции отличается от данных, которые нужно сохранить в изменяющемся значении состояния, можно также указать функцию завершения. Функция завершения принимает конечное значение состояния и возвращает то, что требуется в качестве результата агрегатной функции. В принципе, функции перехода и завершения — это просто обычные функции, которые можно использовать и вне контекста агрегата. (На практике для повышения производительности часто бывает полезно создать специализированные функции перехода, которые могут работать только при вызове в составе агрегата).

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

Если мы определяем агрегат, не использующий функцию завершения, то он будет вычислять выполняемую функцию значений столбца из каждой строки. Примером такой агрегатной функции является sum. sum начинается с нуля и всегда добавляет к накапливаемой сумме значение текущей строки. Например, если мы хотим заставить агрегатную функцию sum работать с типом данных для комплексных чисел, нам понадобится всего лишь функция сложения для этого типа данных. Определение агрегатной функции может выглядеть так:

CREATE AGGREGATE sum (complex)
(
    sfunc = complex_add,
    stype = complex,
    initcond = '(0,0)'
);

И использовать эту функцию можно будет так:

SELECT sum(a) FROM test_complex;

   sum
-----------
 (34,53.9)

(Заметьте, что мы полагаемся на перегрузку функций: существует более одной агрегатной функции с именем sum, но QHB может определить, какую из них применять к столбцу типа complex.)

Приведенное выше определение sum вернет ноль (значение начального состояния), если среди входных данных нет значений, отличных от NULL. Возможно, в таком случае у нас возникнет желание вернуть вместо этого NULL — по стандарту SQL ожидается, что sum будет вести себя именно так. Мы можем добиться этого, просто опустив фразу initcond, так что начальным значением состояния будет NULL. Обычно это будет означать, что функции sfunc понадобится проверить входное значение состояния на NULL. Но для sum и некоторых других простых агрегатных функций, таких как max и min, достаточно вставить в переменную состояния первое входное значение не NULL, а затем начать применять функцию перехода со второго значения не NULL. QHB сделает это автоматически, если значение начального состояния равно NULL и функция перехода помечена как «strict» (строгая) (т. е. не должна вызываться для входных значений NULL).

Еще одна особенность поведения по умолчанию функции перехода «strict» заключается в том, что каждый раз, когда попадается входное значение NULL, предыдущее значение состояние остается неизменным. Таким образом, значения NULL игнорируются. Если вам нужно другое поведение для входных значений NULL, не объявляйте вашу функцию перехода строгой; вместо этого запрограммируйте ее проверять входные значения на NULL и обрабатывать их, как требуется.

Функция avg (среднее арифметическое) является примером более сложного агрегата. Ей требуется два компонента текущего состояния: сумма входных значений и их количество. Итоговый результат получается делением этих величин. Обычно эта функция реализуется, используя в качестве значения состояния массив. Например, встроенная реализация avg(float8) выглядит так:

CREATE AGGREGATE avg (float8)
(
    sfunc = float8_accum,
    stype = float8[],
    finalfunc = float8_avg,
    initcond = '{0,0,0}'
);

Примечание
Для float8_accum требуется массив не из двух, а из трех элементов, потому что помимо суммы и количества входных значений она также вычисляет сумму квадратов. Это сделано для того, чтобы данную функцию можно было использовать и для других агрегатов кроме avg.

Вызовы агрегатных функций в SQL позволяют использовать указания DISTINCT и ORDER BY, которые определяют, какие строки и в каком порядке передаются в функцию перехода агрегата. Эта возможность реализована за кадром и не затрагивает вспомогательные функции агрегатов.

Дополнительную информацию см. в описании команды CREATE AGGREGATE.

Режим движущегося агрегата

Агрегатные функции могут дополнительно поддерживать режим движущегося агрегата, который позволяет гораздо быстрее выполнять агрегатные функции в окнах со сдвигающимися начальными точками рамки. (Информацию об использовании агрегатных функций в качестве оконных функций см. в разделе Оконные функции и подразделе Вызовы оконных функций.) Основная идея в том, что в дополнение к обычной «прямой» функции перехода агрегат предоставляет обратную функцию перехода, которая позволяет удалять строки из значения состояния выполняющегося агрегата, когда те покидают рамку окна. Например, агрегат sum, который использует сложение в качестве функции прямого перехода, будет использовать в качестве функции обратного перехода вычитание. Без функции обратного перехода механизм оконной функции должен пересчитывать агрегат заново при каждом перемещении рамки окна, в результате чего время выполнения будет пропорционально числу входных строк, умноженному на среднюю длину рамки. При использовании функции обратного перехода время выполнения пропорционально только количеству входных строк.

В функцию обратного перехода передается текущего значение состояния и входное значение (или значения) агрегата для самой первой строки, включенной в текущее состояние. Она должна восстановить то значение состояния, которое получилось бы, если бы эта входная строка никогда не была агрегирована, но агрегировались все последующие. Иногда для этого нужно, чтобы функция прямого перехода сохраняла больше сведений о состоянии, чем требуется для простого режима агрегирования. По этой причине режим движущегося агрегата реализуется совершенно иначе, чем обычный режим: для него определяется свой тип данных состояния, собственная функция прямого перехода и, при необходимости, своя функция завершения. Они могут совпадать с типом данных и аналогичными функциями обычного режима, если нет необходимости в дополнительном состоянии.

В качестве примера мы можем дополнить рассмотренную выше агрегатную функцию sum, чтобы она поддерживала режим движущегося агрегата так:

CREATE AGGREGATE sum (complex)
(
    sfunc = complex_add,
    stype = complex,
    initcond = '(0,0)',
    msfunc = complex_add,
    minvfunc = complex_sub,
    mstype = complex,
    minitcond = '(0,0)'
);

Параметры, имена которых начинаются с m, относятся к реализации движущегося агрегата. За исключением функции обратного перехода minvfunc, они соответствуют параметрам обычного агрегата без m.

Функция прямого перехода для режима движущегося агрегата не разрешено возвращать NULL в качестве нового значения состояния. Если функция обратного перехода возвращает NULL, это воспринимается как признак того, что она не может инвертировать расчет состояния для этого конкретного входного значения, а значит, агрегатное вычисление нужно произвести заново с отметки текущей позиции начала рамки. Это соглашение позволяет использовать режим движущегося агрегата в нечастых ситуациях, когда отматывать обратно текущее значение состояния непрактично. В таких случаях функция обратного перехода может «застопориться», но в большинстве случаев она все равно продолжит пробиваться вперед, насколько это возможно. Например, агрегатная функция, работающая с числами с плавающей точкой может застопориться, когда ей понадобится убрать из текущего значения состояния значение NaN (не число).

При написании вспомогательных функций движущегося агрегата важно убедиться, что функция обратного перехода может точно восстановить верное значение состояния. Иначе в результатах могут возникнуть заметные пользователю различия в зависимости от того, используется ли режим движущегося агрегата. Примером агрегата, для которого добавление функции обратного перехода на первый взгляд кажется простым, а на самом деле вышеуказанное требование не будет выполняться, является sum с входным типом float4 или float8. Наивное объявление sum(float8) может быть таким:

CREATE AGGREGATE unsafe_sum (float8)
(
    stype = float8,
    sfunc = float8pl,
    mstype = float8,
    msfunc = float8pl,
    minvfunc = float8mi
);

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

SELECT
  unsafe_sum(x) OVER (ORDER BY n ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING)
FROM (VALUES (1, 1.0e20::float8),
             (2, 1.0::float8)) AS v (n,x);

Вторым результатом этого запроса будет 0, а не ожидаемое значение 1. Причиной является ограниченная точность значений с плавающей запятой: при добавлении 1 к 1e20 снова получится 1e20, а при вычитании 1e20 из этого получится 0, а не 1. Обратите внимание, что это принципиальное ограничение всей арифметики чисел с плавающей точкой, а не только QHB.

Агрегатные функции с полиморфными и переменными аргументами

Агрегатные функции могут использовать полиморфные функции перехода состояния или функции завершения, так что эти же функции можно использовать для реализации нескольких агрегатов. Объяснение полиморфных функций см. в подразделе Полиморфные типы. Если пойти еще дальше, сама агрегатная функция может быть указана с полиморфными типами входных данных и состояния, что позволяет одному определению агрегата служить для использования с разными типами входных данных. Вот пример полиморфного агрегата:

CREATE AGGREGATE array_accum (anyelement)
(
    sfunc = array_append,
    stype = anyarray,
    initcond = '{}'
);

Здесь фактическим типом состояния для любого конкретного вызова агрегата является массив, элементы которого имеют фактический тип входных данных. Поведение данного агрегата заключается в объединении всех входных данных в массиве этого типа. (Примечание: встроенная агрегатная функция array_agg обеспечивает аналогичную функциональность с лучшей производительностью, чем была бы у функции с приведенным выше определением.)

Вот результат использования в качестве аргументов двух различных фактических типов данных:

SELECT attrelid::regclass, array_accum(attname)
    FROM pg_attribute
    WHERE attnum > 0 AND attrelid = 'pg_tablespace'::regclass
    GROUP BY attrelid;

   attrelid    |              array_accum
---------------+---------------------------------------
 pg_tablespace | {spcname,spcowner,spcacl,spcoptions}
(1 row)

SELECT attrelid::regclass, array_accum(atttypid::regtype)
    FROM pg_attribute
    WHERE attnum > 0 AND attrelid = 'pg_tablespace'::regclass
    GROUP BY attrelid;

   attrelid    |        array_accum
---------------+---------------------------
 pg_tablespace | {name,oid,aclitem[],text[]}
(1 row)

Обычно у агрегатной функции с полиморфным типом результата тип состояния тоже будет полиморфным, как в приведенном выше примере. Это необходимо, поскольку иначе нельзя адекватно объявить функцию завершения: у нее должен быть полиморфный тип результата, но тип аргумента не будет полиморфным, что команда CREATE FUNCTION отвергнет на основании того, что при вызове невозможно определить тип результата. Но иногда полиморфный тип состояния неудобен в использовании. Чаще всего это происходит, когда вспомогательные функции агрегата должны быть написаны на нативном языке и тип состояния должен быть объявлен как internal, поскольку для него нет эквивалента на уровне SQL. Для решения этой проблемы можно объявить функцию завершения принимающей дополнительные «фиктивные» аргументы, которые соответствуют входным аргументам агрегата. В этих фиктивных аргументах всегда передаются значения NULL, так как при вызове функции завершения какое-либо определенное значение недоступно. Единственное их предназначение — позволить типу результата полиморфной функции завершения связаться с типом(ами) входных данных агрегата. Например, определение встроенного агрегата array_agg выглядит так:

CREATE FUNCTION array_agg_transfn(internal, anynonarray)
  RETURNS internal ...;
CREATE FUNCTION array_agg_finalfn(internal, anynonarray)
  RETURNS anyarray ...;

CREATE AGGREGATE array_agg (anynonarray)
(
    sfunc = array_agg_transfn,
    stype = internal,
    finalfunc = array_agg_finalfn,
    finalfunc_extra
);

Здесь характеристика finalfunc_extra указывает, что функция завершения помимо значения состояния получает дополнительные фиктивные аргументы, соответствующие входным аргументам агрегата. Дополнительный аргумент anynonarray позволяет сделать объявление array_agg_finalfn допустимым.

Агрегатную функцию можно заставить принимать различное количество аргументов, объявив ее последний аргумент как массив VARIADIC по аналогии с обычными функциями; см. подраздел Функции SQL с переменным числом аргументов. Функции перехода агрегата должны иметь тот же тип массива, что и их последний аргумент. Обычно функции перехода тоже объявляются как VARIADIC, но это не строгое требование.

Примечание
Агрегатные функции с переменным числом аргументов легко использовать неправильно в сочетании с указанием ORDER BY (см. раздел Агрегатные выражения), поскольку синтаксический анализатор не может определить, было ли передано нужное количество фактических параметров в такой комбинации. Помните, что все, расположенное справа от ORDER BY, является ключом сортировки, а не аргументом агрегата. Например, в

SELECT myaggregate(a ORDER BY a, b, c) FROM ...

анализатор увидит один аргумент агрегатной функции и три ключа сортировки. Однако пользователь мог иметь в виду следующее:

SELECT myaggregate(a, b, c ORDER BY a) FROM ...

Если myaggregate является VARIADIC, то оба этих вызова совершенно корректны. По той же причине стоит дважды подумать, прежде чем создавать агрегатные функции с одинаковыми именами, но разным количеством обычных аргументов.

Сортирующие агрегатные функции

Все вышеописанные агрегаты относятся к «нормальным» агрегатным функциям. QHB также поддерживает сортирующие агрегатные функции, которые имеют два ключевых отличия от нормальных. Во-первых, помимо обычных агрегируемых аргументов, которые вычисляются для каждой входной строки, сортирующая агрегатная функция может принимать «непосредственные» аргументы, которые вычисляются только один раз за всю операцию агрегирования. Во-вторых, синтаксис обычных агрегируемых аргументов задает порядок их сортировки явно. Сортирующий агрегат обычно используется для выполнения вычисления, которое зависит от конкретного порядка строк, например ранг или процентиль, поэтому порядок сортировки является обязательным аспектом каждого вызова. К примеру, встроенное определение функции percentile_disc равнозначно следующему:

CREATE FUNCTION ordered_set_transition(internal, anyelement)
  RETURNS internal ...;
CREATE FUNCTION percentile_disc_final(internal, float8, anyelement)
  RETURNS anyelement ...;

CREATE AGGREGATE percentile_disc (float8 ORDER BY anyelement)
(
    sfunc = ordered_set_transition,
    stype = internal,
    finalfunc = percentile_disc_final,
    finalfunc_extra
);

Этот агрегат принимает непосредственный аргумент float8 (дробь процентиля) и агрегируемые входные данные, которые могут иметь любой сортируемый тип. Например, его можно использовать для расчета среднего семейного дохода:

SELECT percentile_disc(0.5) WITHIN GROUP (ORDER BY income) FROM households;
 percentile_disc
-----------------
           50489

Здесь 0.5 — это непосредственный аргумент; если бы дробь процентиля менялась от строки к строке, это не имело бы никакого смысла.

В отличие от нормальных агрегатов, сортировка входных строк для сортирующего агрегата проходит не за кадром, а является задачей вспомогательных функций агрегата. Типичный подход к реализации такой сортировки заключается в сохранении ссылки на объект «tuplesort» в значении состояния агрегата, загрузке входящих строк в этот объект и последующем окончании сортировки и выдаче данных в функции завершения. Такая модель позволяет функции завершения выполнять специальные операции, например добавлять «гипотетические» строки в сортируемые данные. Хотя нормальные агрегаты часто можно реализовать с помощью вспомогательных функций, написанных на PL/pgSQL или другом процедурном языке, сортирующие агрегаты, как правило, должны записываться на нативном языке, так как их значение состояния нельзя выразить каким-либо типом данных SQL. (Обратите внимание, что в приведенном выше примере значение состояния объявлено как имеющее тип internal — это типичный случай.) Кроме того, поскольку сортировку выполняет функция завершения, невозможно продолжать добавление строк, повторно вызывая функцию перехода. Это означает, что функция завершения не может иметь характеристику READ_ONLY; она должна объявляться в CREATE AGGREGATE с характеристикой READ_WRITE или SHAREABLE (если для дополнительных вызовов функции завершения можно использовать уже отсортированное состояние).

Функция перехода состояния для сортирующего агрегата получает текущее значение состояния и агрегируемые входные значения каждой строки и возвращает измененное значение состояния. Это определение аналогично таковому обычных агрегатов, но обратите внимание, что непосредственные аргументы (если таковые имеются) не передаются. Функция завершения получает последнее значение состояния, значения непосредственных аргументов (если таковые имеются) и (если указано finalfunc_extra) значения NULL, соответствующие агрегируемым входным данным. Для нормальных агрегатов указание finalfunc_extra по-настоящему полезно, только если агрегат полиморфный; тогда дополнительные фиктивные аргументы нужны, чтобы связать тип результата функции завершения с типом(ами) входных данных агрегата.

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

Частичная агрегация

Дополнительно агрегатная функция может поддерживать частичное агрегирование. Идея частичного агрегирования состоит в том, чтобы независимо выполнять функцию перехода состояния агрегата для различных подмножеств входных данных, а затем комбинировать значения состояний, вычисленные по этим подмножествам, для получения того же значения состояния, которое получилось бы при сканировании всех входных данных за один раз. Этот режим можно использовать для параллельного агрегирования, когда разные рабочие процессы сканируют разные части таблицы. Каждый процесс создает частичное значение состояния, и в конце эти значения объединяются для получения окончательного значения состояния. (Возможно, в будущем этот режим также будет использоваться для таких целей, как комбинированное агрегирование локальных и удаленных таблиц, но пока это не реализовано).

Для поддержки частичного агрегирования определение агрегата должно предоставлять комбинирующую функцию, которая принимает два значения типа состояния агрегата (представляющих результаты агрегирования по двум подмножествам входных строк) и создает новое значение типа состояния, представляющее состояние, каким оно было бы после агрегирования комбинации этих подмножеств строк. При этом относительный порядок входных строк в этих двух подмножествах не указывается. Это означает, что для агрегатов, зависящих от порядка строк, обычно невозможно определить осмысленную комбинирующую функцию.

В качестве простых примеров, агрегаты MAX и MIN могут поддерживать частичное агрегирование, если в качестве комбинирующей функции указать функцию сравнения значений (большее-из-двух или меньшее-из-двух соответственно), которую они также используют в качестве функции перехода. Агрегаты SUM просто используют в качестве комбинирующей функцию сложения. (Опять же, эта функция выступает и в качестве их функции перехода, если только значение состояния не выходит за рамки типа входных данных.)

Комбинирующая функция используется почти так же, как функция перехода, но принимает в качестве второго аргумента значение типа состояния, а не нижележащего входного типа. В частности, у нее такие же правила относительно значений NULL и строгих функций. Кроме того, если в определении агрегата задается значение initcond, отличное от NULL, имейте в виду, оно будет использоваться не только как начальное состояние для каждого прохода частичного агрегирования, но и как начальное состояние для комбинирующей функции, которая будет вызываться для комбинирования каждого частичного результата в этом состоянии.

Если тип состояния агрегата объявлен как internal, именно комбинирующая функция отвечает за то, чтобы ее результат был размещен в контексте памяти, подходящем для значений состояния агрегата. В частности, это означает, что получив в первом аргументе NULL, нельзя просто вернуть второй аргумент, поскольку это значение будет иметь неправильный контекст и недостаточно долгий срок существования.

Когда тип состояния агрегата объявлен как internal, обычно в определении агрегата также уместно задать функцию сериализации и функцию десериализации, которые позволяют копировать значение состояния из одного процесса в другой. Без этих функция нельзя осуществить параллельное агрегирование, а также вероятно не будут работать будущие приложения, например локальное/удаленное агрегирование.

Функция сериализации должна принимать один аргумент типа internal и возвращать результат типа bytea, который представляет собой значение состояния, упакованное в плоский массив байтов. Функция десериализации, наоборот, обращает это преобразование. Она должна принимать два аргумента с типами bytea и internal и возвращать результат типа internal. (Второй аргумент не используется и всегда равен нулю, но он необходим из соображений безопасности типов.) Результат функции десериализации нужно просто разместить в текущем контексте памяти, поскольку в отличие от результата комбинирующей функции он недолговечен.

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

Вспомогательные функции для агрегатов

Функция, написанная на нативном языке, может определить, была ли она вызвана как вспомогательная функция агрегата, вызвав AggCheckCallContext, например:

if (AggCheckCallContext(fcinfo, NULL))

Единственная причина такой проверки состоит в том, что в случае положительного результата первый входной аргумент должен быть временным значением состояния, которое можно безопасно изменить на месте, вместо того чтобы размещать новую копию. Пример можно увидеть в функции int8inc(). (Хотя агрегатным функциям перехода всегда разрешено изменять значение перехода на месте, агрегатным функциям завершения в целом следует этого избегать; если они это делают, такое поведение должно быть объявлено при создании агрегатной функции. Более подробную информацию см. в описании команды CREATE AGGREGATE.)

Второй аргумент AggCheckCallContext можно использовать для получения контекста памяти, в котором хранятся значения состояния агрегата. Это полезно для функций перехода, которые хотят использовать в качестве значений состояния «развернутые» объекты (см. подраздел Особенности TOAST). При первом вызове эта функция перехода должна вернуть развернутый объект, чей контекст памяти является дочерним по отношению к контексту состояния агрегата, и затем продолжать возвращать тот же развернутый объект при последующих вызовах. Для примера рассмотрим функцию array_append(). (array_append() не является функцией перехода какого-либо встроенного агрегата, но написана так, чтобы эффективно работать в таком качестве в пользовательском агрегате).

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

Пользовательские типы

Как описано в подразделе Система типов QHB, QHB может быть расширен для поддержки новых типов данных. В этом разделе описывается, как определить новые базовые типы, то есть типы данных, определенные ниже уровня языка SQL. Создание нового базового типа требует реализации функций для работы с этим типом на языке низкого уровня, как правило, на нативном языке.

Пользовательский тип должен всегда иметь функции ввода и вывода. Эти функции определяют, как тип будет отображаться в строках (при вводе и выводе для пользователя) и как этот тип расположен в памяти. Функция ввода принимает в качестве аргумента строку символов, заканчивающуюся нулем, и возвращает внутреннее (в памяти) представление типа. Функция вывода принимает в качестве аргумента внутреннее представление типа и возвращает строку символов, заканчивающуюся нулем. Если мы хотим сделать с типом что-то большее, нежели просто сохранить его, то для реализации любых операций, которые мы хотели бы иметь для этого типа, нужно предоставить дополнительные функции.

Предположим, мы хотим определить тип complex, который представляет комплексные числа. Естественным способом представления комплексного числа в памяти будет следующая структура C:

typedef struct Complex {
    double      x;
    double      y;
} Complex;

Нам нужно будет сделать этот тип передаваемым по ссылке, поскольку он слишком велик, чтобы поместиться в одно значение Datum.

В качестве внешнего строкового представления типа мы выберем строку вида (x,y).

Функции ввода и вывода обычно не сложно написать, особенно функцию вывода. Но при определении внешнего строкового представления типа помните, что в конечном итоге вам придется написать законченный и надежный синтаксический анализатор для этого представления в входной функции. Например:

PG_FUNCTION_INFO_V1(complex_in);

Datum
complex_in(PG_FUNCTION_ARGS)
{
    char       *str = PG_GETARG_CSTRING(0);
    double      x,
                y;
    Complex    *result;

    if (sscanf(str, " ( %lf, %lf )", &x, &y) != 2)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                 errmsg("invalid input syntax for type %s: \"%s\"",
                        "complex", str)));

    result = (Complex *) palloc(sizeof(Complex));
    result->x = x;
    result->y = y;
    PG_RETURN_POINTER(result);
}

Функция вывода может быть простой:

PG_FUNCTION_INFO_V1(complex_out);

Datum
complex_out(PG_FUNCTION_ARGS)
{
    Complex    *complex = (Complex *) PG_GETARG_POINTER(0);
    char       *result;

    result = psprintf("(%g,%g)", complex->x, complex->y);
    PG_RETURN_CSTRING(result);
}

Нужно позаботиться о том, чтобы входные и выходные функции получились противоположными друг другу. Если вы этого не сделаете, то столкнетесь с серьезными проблемами, когда вам понадобится сохранить данные в файл и затем опять прочитать их. Это особенно распространенная проблема, когда дело касается чисел с плавающей запятой.

Дополнительно пользовательский тип может предоставлять функции ввода и вывода в двоичном формате. Двоичный ввод/вывод обычно быстрее, но сильнее зависит от платформы, чем текстовый. Как и в случае с текстовым вводом/выводом, точное определение того, каким будет внешнее двоичное представление, остается за вами. Большинство встроенных типов данных пытаются обеспечить машинно-независимое двоичное представление. Для типа complex мы воспользуемся преобразователями двоичного ввода/вывода для типа float8:

PG_FUNCTION_INFO_V1(complex_recv);

Datum
complex_recv(PG_FUNCTION_ARGS)
{
    StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
    Complex    *result;

    result = (Complex *) palloc(sizeof(Complex));
    result->x = pq_getmsgfloat8(buf);
    result->y = pq_getmsgfloat8(buf);
    PG_RETURN_POINTER(result);
}

PG_FUNCTION_INFO_V1(complex_send);

Datum
complex_send(PG_FUNCTION_ARGS)
{
    Complex    *complex = (Complex *) PG_GETARG_POINTER(0);
    StringInfoData buf;

    pq_begintypsend(&buf);
    pq_sendfloat8(&buf, complex->x);
    pq_sendfloat8(&buf, complex->y);
    PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}

После того, как мы написали функции ввода/вывода и скомпилировали их в разделяемую библиотеку, мы можем определить тип complex в SQL. Сначала мы объявим тип-оболочку:

CREATE TYPE complex;

Он служит заполнителем, позволяющим нам ссылаться на этот тип при определении его функций ввода/вывода. Теперь мы можем определить функции ввода/вывода:

CREATE FUNCTION complex_in(cstring)
    RETURNS complex
    AS 'имя_файла'
    LANGUAGE C IMMUTABLE STRICT;

CREATE FUNCTION complex_out(complex)
    RETURNS cstring
    AS 'имя_файла'
    LANGUAGE C IMMUTABLE STRICT;

CREATE FUNCTION complex_recv(internal)
   RETURNS complex
   AS 'имя_файла'
   LANGUAGE C IMMUTABLE STRICT;

CREATE FUNCTION complex_send(complex)
   RETURNS bytea
   AS 'имя_файла'
   LANGUAGE C IMMUTABLE STRICT;

Наконец, мы можем предоставить полное определение типа данных:

CREATE TYPE complex (
   internallength = 16,
   input = complex_in,
   output = complex_out,
   receive = complex_recv,
   send = complex_send,
   alignment = double
);

Когда вы определяете новый базовый тип, QHB автоматически обеспечивает поддержку массивов этого типа. Тип массива обычно имеет то же имя, что и базовый тип, с добавлением символа подчеркивания (_) в начале.

Теперь, когда тип данных существует, мы можем объявить дополнительные функции для выполнения полезных операций с этим типом. Поверх этих функций можно определить операторы и, при необходимости, создать классы операторов для поддержки индексации этого типа. Эти дополнительные уровни обсуждаются в следующих разделах.

Если внутреннее представление типа данных имеет переменную длину, оно должно соответствовать стандартной схеме данных переменной длины: первые четыре байта должны занимать поле char[4], к которому никогда не следует обращаться напрямую (традиционно называемое vl_len_). Чтобы сохранить в этом поле общий размер элемента (включая само поле длины), нужно использовать макрос SET_VARSIZE(), а чтобы получить его — VARSIZE(). (Эти макросы существуют, потому что поле длины может кодироваться по-разному в зависимости от платформы.)

Более подробную информацию см. в описании команды CREATE TYPE.

Особенности TOAST

Если значения вашего типа данных сильно различаются по размеру (во внутреннем представлении), обычно желательно сделать это тип подходящим для TOAST (см. раздел TOAST). Стоит это сделать даже в том случае, когда значения всегда слишком малы для сжатия или внешнего хранения, поскольку TOAST может сэкономить пространство и с данными малого размера, уменьшая издержки в заголовке.

Чтобы поддерживать хранение TOAST, функции на C/RUST, работающие с этим типом данных, должны позаботиться о распаковке любых сжатых значений, которые передаются им с помощью макроса PG_DETOAST_DATUM. (Эта деталь обычно скрывается путем определения макроса GETARG_DATATYPE_P для конкретного типа). Затем при запуске команды CREATE TYPE укажите внутреннюю длину как variable и выберите какой-нибудь подходящий вариант хранения, отличный от plain.

Если выравнивание данных неважно (либо для конкретной функции, либо потому, что для этого типа данных в любом случае применяется выравнивание по байтам), то можно избежать некоторых издержек, связанных с макросом PG_DETOAST_DATUM. Вместо него можно использовать макрос PG_DETOAST_DATUM_PACKED (обычно скрывается путем определения макроса GETARG_DATATYPE_PP) и применить макросы VARSIZE_ANY_EXHDR и VARDATA_ANY для обращения к потенциально сжатым данным. Опять же, данные, возвращаемые этими макросами, не выравниваются, даже если определение типа данных требует выравнивания. Если выравнивание важно, следует задействовать обычный интерфейс PG_DETOAST_DATUM.

Примечание
В более старом коде поле vl_len_ часто объявлялось как int32, а не char[4]. Это нормально, пока в структуре есть другие поля с выравниванием как минимум int32. Но при работе с потенциально невыровненными данными такое строго определение использовать опасно; компилятор может воспринять его как право предполагать, что данные действительно выровнены, что приведет к дампу памяти в архитектурах, строгих к выравниванию.

Еще одна особенность, предоставляемая поддержкой TOAST, — это возможность иметь развернутое представление данных в памяти, работать с которым удобнее, чем с форматом, хранящимся на диске. Обычный, или «плоский», формат хранения varlena, в конечном счете, является простым набором байтов; например, он не может содержать указатели, так как может быть скопирован в другие области памяти. Для сложных типов данных может быть довольно дорого работать с плоским форматом, поэтому QHB предоставляет способ «развернуть» плоский формат в представление, более подходящее для вычислений, а затем передавать этот формат в памяти между функциями, работающими с этим типом данных.

Чтобы использовать развернутое хранилище, тип данных должен определять развернутый формат, который следует правилам, приведенным в src/include/utils/expandeddatum.h, и предоставить функции для «разворачивания» плоского значения varlena в этот формат и «сворачивания» его обратно в обычное представление varlena. Затем добейтесь того, чтобы все функции на C/RUST для этого типа данных могли принимать любое представление, возможно, путем преобразования одной в другую сразу при получении. Это не требует одновременного исправления всех существующих функций для этого типа данных, поскольку стандартный макрос PG_DETOAST_DATUM способен преобразовать развернутые входные данные в обычный плоский формат. Таким образом, существующие функции, работающие с плоским форматом varlena, продолжат работать, хотя и не очень эффективно, с развернутыми входными данными; их не нужно преобразовывать до тех пор, пока не понадобится повысить производительность.

Функции на C/RUST, которые знают, как работать с развернутым представлением, обычно делятся на две категории: те, которые могут обрабатывать только развернутый формат, и те, которые могут обрабатывать как развернутые, так и плоские входные данные varlena. Первые легче написать, но они могут быть менее эффективными в целом, потому что преобразование плоских входных данных в развернутую форму для использования одной-единственной функцией может стоить больше, чем экономится при работе с развернутым форматом. Когда требуется обрабатывать только развернутый формат, преобразование плоских входных данных в развернутую форму можно скрыть внутри макроса, извлекающего аргументы, чтобы функция была не более сложной, чем та, которая работает с традиционными входными данными varlena. Чтобы обработать оба типа входных данных, напишите функцию извлечения аргументов, которая будет распаковывать входные данные varlena с коротким заголовком, а также внешние и сжатые, но не развернутые. Такую функцию можно определить как возвращающую указатель на объединение плоского формата varlena и развернутого формата. Чтобы определить, какой именно формат был получен, можно воспользоваться макросом VARATT_IS_EXPANDED_HEADER().

Инфраструктура TOAST не только позволяет отличать обычные значения varlena от развернутых значений, но также различает указатели «для чтения-записи» и «только для чтения» на развернутые значения. Функции на C/RUST, которым нужно проверять только развернутое значение или которые будут изменять его только безопасными и не видимыми семантически способами, нет необходимости обращать внимание на то, какой тип указателя они получают. Функциям на C/RUST, которые выдают измененную версию входных данных, разрешено изменять развернутые входные данные на месте при получении указателя для чтения-записи, но не когда получают указатель только для чтения; в этом случае они должны сначала скопировать это значение, создав новое значение, подлежащее изменению. Функция на C/RUST, которая создала новое развернутое значение, всегда должна возвращать указатель на него для чтения-записи. Кроме того, функция на C/RUST, которая изменяет развернутое значение для чтения-записи на месте, должна позаботиться о том, чтобы оставить его в нормальном состоянии, если во время ее работы произойдет сбой.

Примеры работы с расширенными значениями см. в стандартной инфраструктуре массивов, в частности в src/backend/utils/adt/array_expanded.c.

Пользовательские операторы

Каждый оператор является «синтаксическим сахаром» для вызова базовой функции, которая выполняет реальную работу; поэтому прежде чем вы сможете создать оператор, сначала нужно создать базовую функцию. Тем не менее, оператор не просто синтаксический сахар, так как несет дополнительную информацию, которая помогает планировщику запросов оптимизировать запросы, использующие этот оператор. Следующий раздел будет посвящен рассмотрению этой дополнительной информации.

QHB поддерживает левый унарный, правый унарный и бинарный операторы. Операторы могут быть перегружены; то есть одно и то же имя оператора может использоваться для разных операторов с разным количеством и типами операндов. При выполнении запроса система определяет, какой оператор вызвать, по числу и типам предоставленных аргументов.

Вот пример создания оператора для сложения двух комплексных чисел. Предположим, что мы уже создали определение типа complex (см. подраздел Пользовательские типы). Сначала нам нужна функция, выполняющая работу, а затем мы сможем определить оператор:

CREATE FUNCTION complex_add(complex, complex)
    RETURNS complex
    AS 'имя_файла', 'complex_add'
    LANGUAGE C IMMUTABLE STRICT;

CREATE OPERATOR + (
    leftarg = complex,
    rightarg = complex,
    function = complex_add,
    commutator = +
);

Теперь мы можем выполнить такой запрос:

SELECT (a + b) AS c FROM test_complex;

        c
-----------------
 (5.2,6.05)
 (133.42,144.95)

Здесь мы показали, как создать бинарный оператор. Чтобы создать унарный оператор, просто опустите leftarg (для левого унарного) или rightarg (для правого унарного). Единственными обязательными элементами в команде CREATE OPERATOR являются предложение function и объявления аргументов. Предложение commutator, показанное в примере, служит дополнительной подсказкой оптимизатору запросов. Более подробно о предложении commutator и других подсказках для оптимизатора рассказывается в следующем подразделе.

Информация по оптимизации оператора

Определение оператора QHB может включать несколько дополнительных предложений, которые сообщают системе полезные сведения о поведении оператора. Эти предложения по возможности следует указывать, поскольку они могут значительно ускорить выполнение запросов, использующих этот оператор. Но если вы их указываете, следует убедиться, что они корректны! Неправильное использование предложения оптимизации может привести к замедлению запросов, неочевидно ошибочному результату или другим бедам. Если вы не уверены в предложении оптимизации, его всегда можно опустить; единственным последствием будет то, что запросы могут выполняться медленнее, чем нужно.

В будущих версиях QHB могут появиться дополнительные предложения оптимизации. Здесь описываются те предложения, которые поддерживаются 1.5.0.

Также есть возможность добавить к функции, реализующей оператор, вспомогательную функцию для планировщика, тем самым открывая другой способ информирования системы о поведении оператора. Дополнительную информацию см. в подразделе Информация по оптимизации функций.

COMMUTATOR

Предложение COMMUTATOR, если предоставлено, задает оператор, коммутирующий определяемый. Мы говорим, что оператор A является коммутатором оператора B, если (x A y) равно (y B x) для всех возможных входных значений x, y. Обратите внимание, что B также является коммутатором A. Например, операторы < и > для определенного типа данных обычно являются коммутаторами друг друга, а оператор + обычно коммутирует сам с собой. А вот оператор - обычно не коммутирует ни с каким другим.

Тип левого операнда коммутируемого оператора такой же, как тип правого операнда его коммутатора, и наоборот. Таким образом, имя коммутирующего оператора — это все, что нужно дать QHB для поиска коммутатора, и все, что необходимо указать в предложении COMMUTATOR.

Очень важно предоставлять информацию о коммутаторах тем операторам, которые будут использоваться в индексах и предложениях соединения, поскольку это позволяет оптимизатору запросов «переворачивать» такое предложение, приводя его к форме, необходимой для различных типов планов. Например, рассмотрим запрос с предложением WHERE вроде tab1.x = tab2.y, где tab1.x и tab2.y имеют пользовательский тип, и предположим, что по столбцу tab2.y есть индекс. Оптимизатор сможет задействовать сканирование по индексу, только если сумеет определить, как перевернуть это предложение, превратив его в tab2.y = tab1.x, поскольку механизм сканирования по индексу ожидает увидеть индексированный столбец слева от переданного ему оператора. QHB не будет по умолчанию считать, что такое преобразование возможно — создатель оператора = должен это указать, добавив в оператор информацию о коммутаторе.

Когда вы определяете оператор, коммутирующий сам себя, вы просто делаете это. Когда вы определяете пару коммутирующих операторов, все становится немного сложнее: как оператор определяемый первым, может ссылаться на другой, который вы еще не определили? Есть два решения этой проблемы:

Один из способов — опустить предложение COMMUTATOR в первом определяемом вами операторе, а затем указать его в определении второго оператора. Поскольку QHB знает, что коммутирующие операторы идут парами, когда он увидит второе определение, то автоматически вернется и заполнит пропущенное предложение COMMUTATOR в первом определении.

Другой, более простой способ — просто включить предложения COMMUTATOR в оба определения. Когда QHB обрабатывает первое определение и понимает, что COMMUTATOR ссылается на несуществующий оператор, система сделает фиктивную запись для этого оператора в системном каталоге. Эта фиктивная запись будет содержать актуальные данные только для имени оператора, левого и правого типов операндов и типа результата, поскольку это все, что QHB может выяснить на данный момент. Запись первого оператора будет связана с этой фиктивной записью. Позже, когда вы определите второй оператор, система обновит фиктивную запись дополнительной информацией из второго определения. Если вы попытаетесь использовать фиктивный оператор до его заполнения, то просто получите сообщение об ошибке.

NEGATOR

Предложение NEGATOR, если предоставлено, задает оператор, обратный определяемому. Мы говорим, что оператор A является обратным к оператору B, если оба возвращают логические результаты и (x A y) равно NOT (x B y) для всех возможных входных значений x, y. Обратите внимание, что B также является обратным к A. Например, < и >= являются парой обратных друг к другу для большинства типов данных. Никакой оператор никогда не может быть обратным к себе самому.

В отличие от коммутаторов, пару унарных операторов вполне можно пометить как обратные друг к другу; это будет означать, что (A x) равно NOT (B x) для всех x (и аналогично для правых унарных операторов).

У обратного оператора должны быть те же типы левого и/или правого операнда, что и у определяемого оператор, поэтому, как и в случае с COMMUTATOR, в предложении NEGATOR следует указывать только имя оператора.

Предоставление обратного оператора очень полезно для оптимизатора запросов, поскольку тот позволяет упростить выражения типа NOT (x >= y) до x < y. Это происходит чаще, чем можно подумать, так как операции NOT могут добавляться вследствие других преобразований.

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

RESTRICT

Предложение RESTRICT, если предоставлено, задает функцию оценки избирательности ограничения для оператора. (Обратите внимание, что это имя функции, а не оператора.) Предложения RESTRICT имеют смысл только для бинарных операторов, возвращающих boolean. Идея оценки избирательности ограничения состоит в том, чтобы примерно определить, какая доля строк в таблице будет удовлетворять условию предложения WHERE вида:

column OP constant

для текущего оператора и определенного значения константы. Это помогает оптимизатору, давая ему некоторое представление о том, сколько строк будет исключено предложениями WHERE такого вида. (А что произойдет, если константа будет слева, спросите вы? Ну, как раз для таких случаев и предназначен COMMUTATOR...)

Написание новых функций оценки избирательности ограничения выходит за рамки этой главы, но, к счастью, обычно для большинства ваших собственных операторов можно просто воспользоваться одним из стандартных системных оценщиков. Вот стандартные оценщики ограничений:

Вспомогательная функция | Оператор
eqsel | =
neqsel | <>
scalarltsel | <
scalarlesel | <=
scalargtsel | >
scalargesel | >=

Например, вы можете обойтись функциями eqsel или neqsel для операторов, которые имеют очень высокую или очень низкую избирательность, даже если на самом деле они не имеют никакого отношения к равенству или неравенству. Например, геометрические операторы приблизительного равенства используют eqsel в предположении, что обычно им соответствует лишь небольшая часть записей в таблице.

Для сравнения типов данных, которые можно каким-либо разумным способом преобразовать в числовые скалярные значения для сравнения диапазонов, можно использовать функции scalarltsel, scalarlesel, scalargtsel и scalargesel. По возможности, добавьте свой тип данных в число типов, которые понимает функция convert_to_scalar(). Если вы этого не сделаете, все будет работать, но оценки оптимизатора будут не так хороши, как могли бы быть.

JOIN

Предложение JOIN, если предоставлено, задает функцию оценки избирательности соединения для оператора. (Обратите внимание, что это имя функции, а не оператора.) Предложения JOIN имеют смысл только для бинарных операторов, возвращающих boolean. Идея оценки избирательности соединения состоит в том, чтобы примерно определить, какая доля строк в паре таблиц будет удовлетворять условию предложения WHERE вида:

ON table1.column1 OP table2.column2

для данного оператора. Как и в случае с RESTRICT, это существенно помогает оптимизатору, позволяя ему выяснить, какая из нескольких возможных последовательностей соединения скорее всего окажется наименее трудоемкой.

Как и ранее, в этой главе мы не будем разъяснять, как писать свой функции оценки избирательности соединения, а просто предложим использовать один из подходящих стандартных оценщиков:

Вспомогательная функция | Оператор
eqjoinsel | =
neqjoinsel | <>
scalarltjoinsel | <
scalarlejoinsel | <=
scalargtjoinsel | >
scalargejoinsel | >=
areajoinsel | сравнения областей в плоскости
positionjoinsel | сравнения положений в плоскости
contjoinsel | проверка на включение в плоскости

HASHES

Предложение HASHES, если присутствует, сообщает системе, что для соединения на основе этого оператора разрешается использовать метод соединения по хешу. HASHES имеет смысл только для двоичного оператора, который возвращает boolean, и на практике оператор должен выражать равенство значений для некоторого типа данных или пары типов данных.

Соединение по хешу основано на том, что оператор соединения может возвращать true только для тех пар значений слева и справа, которые хешируют один и тот же хеш-код. Если два значения помещаются в разные хеш-блоки, объединение вообще никогда их не сравнит, неявно предполагая, что результат оператора соединения должен быть ложным. Поэтому нет смысла указывать HASHES для операторов, которые не выражают некоторую форму равенства. В большинстве случаев практично поддерживать хеширование только для тех операторов, которые принимают одинаковый тип данных с обеих сторон. Однако иногда возможно разработать хеш- функции, совместимые с двумя и более типами данных, то есть функции, которые будут генерировать одинаковые хеш-коды для «равных» значений, даже если эти значения имеют разные представления. Например, довольно просто реализовать это свойство при хешировании целых чисел различной ширины.

Чтобы получить характеристику HASHES, оператор соединения должен принадлежать семейству операторов индексирования по хешу. Это не проверяется при создании оператора, поскольку, разумеется, в этот момент соответствующее семейство операторов еще не может существовать. Но попытки использовать такой оператор для соединений по хешу приведут к ошибке во время выполнения, если такого семейства операторов не окажется. Семейство операторов нужно системе, чтобы найти функции для хеширования входного типа (или типов) данных оператора. Конечно, прежде чем получится создать семейство операторов, нужно создать подходящие функции хеширования.

При создании функции хеширования следует соблюдать осторожность, поскольку машинная архитектура может провоцировать ошибки. Например, если ваш тип данных представляет собой структуру, в которой есть незначащие биты-заполнители, то нельзя просто передать всю структуру функции hash_any. (Если только вы не напишете свои операторы и функции так, чтобы число неиспользуемых битов всегда равнялось нулю, что является рекомендуемой стратегией.) В качестве другого примера можно привести машины, использующих числа с плавающей запятой в соответствии со стандартом IEEE, в которых отрицательный ноль и положительный ноль — это разные значения (отличаются комбинациями битов), но при сравнении они считаются равными. Если значение с плавающей запятой может содержать отрицательный ноль, то перед хешированием нужен дополнительный шаг, чтобы для него выдавалось то же значение хеша, что и для положительного нуля.

Оператор соединения по хешу должен иметь коммутатор (это может быть тот же оператор, если у него два типа данных операндов одного вида, либо, если виды разные, связанный оператор равенства), принадлежащий тому же семейству операторов. В противном случае при использовании оператора могут возникнуть ошибки планировщика. Кроме того, желательно (но не обязательно), чтобы семейство операторов хеширования, поддерживающее несколько типов данных, предоставляло операторы равенства для всех возможных комбинаций этих типов данных; это способствует лучшей оптимизации.

Примечание
Функция, реализующая оператор соединения по хешу, должна быть помечена как постоянная (IMMUTABLE) или стабильная (STABLE). Если эта функция изменчивая (VOLATILE), система никогда не будет пытаться использовать этот оператор для соединения по хешу.

Примечание
Если оператор соединения по хешу реализован через строгую функцию (STRICT), эта функция также должна быть полной: то есть она должна возвращать true или false, но не NULL для любых входных значений, отличных от NULL. Если это правило не соблюдается, оптимизация операций IN с хешем может привести к неверным результатам. (В частности, IN может вернуть false, когда правильным ответом, в соответствии со стандартом, должен быть NULL, или выдать ошибку, сообщающую, что он не готов к результату NULL.)

MERGES

Предложение MERGES, если присутствует, сообщает системе, что для соединения на основе этого оператора разрешается использовать метод соединения слиянием. MERGES имеет смысл только для двоичного оператора, который возвращает boolean, и на практике оператор должен выражать равенство значений для некоторого типа данных или пары типов данных.

Объединение слиянием основано на идее упорядочивания левой и правой таблиц и последующего их параллельного сканирования. Таким образом, оба типа данных должны поддерживать сортировку в полном объеме, а оператор соединения должен успешно работать только с парами значений, попадающих при данном порядке сортировки в «одно и то же место». На практике это означает, что оператор соединения должен вести себя как равенство. Но также возможно объединить слиянием и два разных типа данных, если они логически совместимы. Например, оператор равенства smallint и integer является оператором соединения слиянием. Нам понадобятся только операторы сортировки, которые приведут оба типа данных в логически совместимую последовательность.

Чтобы получить характеристику MERGES, оператор соединения должен являться членом семейства операторов индекса btree, реализующим равенство. Это не проверяется при создании оператора, поскольку, разумеется, соответствующее семейство операторов еще не может существовать. Но оператор не будет фактически использоваться для соединений слиянием, пока не будет найдено соответствующее семейство операторов. Таким образом, флаг MERGES служит подсказкой для планировщика, что стоит искать подходящее семейство.

Оператор соединением слиянием должен иметь коммутатор (это может быть тот же оператор, если у него два типа данных операндов одного вида, либо, если виды разные, связанный оператор равенства), относящийся к тому же семейству операторов. В противном случае при использовании оператора могут возникнуть ошибки планировщика. Кроме того, желательно (но не обязательно), чтобы семейство операторов btree, поддерживающее несколько типов данных, предоставляло операторы равенства для всех возможных комбинаций этих типов данных; это способствует лучшей оптимизации.

Примечание
Функция, реализующая оператор соединения слиянием, должна быть помечена как постоянная (IMMUTABLE) или стабильная (STABLE). Если эта функция изменчивая (VOLATILE), система никогда не будет пытаться использовать этот оператор для соединения слиянием.

Интерфейсные расширения для индексов

Описанные выше процедуры позволяют вам определять новые типы, функции и операторы. Однако мы еще не можем определить индекс по столбцу нового типа данных. Для этого мы должны определить класс операторов для нового типа данных. Далее в этом подразделе мы проиллюстрируем эту концепцию на примере: новый класс операторов для индексного метода B-дерева, который сохраняет и сортирует комплексные числа в порядке возрастания абсолютного значения.

Классы операторов можно сгруппировать в семейства операторов, показывающие зависимости между семантически совместимыми классами. Когда задействован только один тип данных, достаточно класса операторов, поэтому сначала мы сосредоточимся на этом случае, а затем вернемся к семействам операторов.

Индексные методы и классы операторов

Таблица pg_am содержит одну строку для каждого индексного метода (внутренне известного как метод доступа). Поддержка обычного доступа к таблицам встроена в QHB, но все индексные методы описаны в pg_am. Можно добавить новый метод доступа по индексу, написав необходимый код и затем создав запись в pg_am — но это выходит за рамки этой главы (см. главу Определение интерфейса для индексных методов доступа).

Процедуры индексного метода непосредственно ничего не знают о типах данных, с которыми тот будет работать. Вместо этого набор операций, которые нужны индексному методу для работы с конкретным типом данных, определяет класс операторов. Классы операторов называются так, потому что они указывают только одну вещь — множество операторов в предложении WHERE, которые можно использовать с индексом (то есть можно преобразовать в префикс для сканирования индекса). Класс оператора также может указывать некую вспомогательную функцию, которая необходима внутренним операциям индексного метода, но не соответствует напрямую ни одному оператору предложения WHERE, который может обрабатываться с индексом.

Для одного и того же типа данных и индексного метода можно определить несколько классов операторов. Благодаря этому для одного типа данных можно определить несколько вариантов доступа по индексу с различной семантикой. Например, для B-дерева требуется определить порядок сортировки для каждого типа данных, с которым он работает. Для типа комплексных чисел может быть полезно иметь один класс операторов B-дерева, который сортирует данные по абсолютному значению комплексного числа, еще один, который сортирует по вещественной части, и т. д. Как правило, считается, что один из классов операторов будет применяться чаще всего, и его помечают как класс операторов по умолчанию для этого типа данных и индексного метода.

Одно и то же имя класса оператора можно использовать для нескольких различных индексных методов (например, для методов B-дерева и хеш-индекса применяются классы операторов с именем int4_ops), но каждый такой класс является независимым объектом и должен определяться отдельно.

Стратегии индексного метода

Операторам, связанным с классом операторов, присваиваются «номера стратегий», которые служат для идентификации семантики каждого оператора в контексте его класса операторов. Например, B-деревья предписывают строгий порядок ключей, от меньшего к большему, и поэтому в данном контексте представляют интерес операторы типа «меньше» и «больше или равно». Поскольку QHB позволяет пользователю определять операторы, QHB не может посмотреть на имя оператора (например, < или >=) и сказать, какое это сравнение. Вместо этого индексный метод определяет набор «стратегий», которые можно рассматривать как обобщенные операторы. Каждый класс операторов определяет, какой фактический оператор соответствует каждой стратегии для конкретного типа данных и интерпретации семантики индекса.

Индексный метод B-дерева определяет пять стратегий, показанных в таблице 2.

Таблица 2. Стратегии В-дерева

Операция | Номер стратегии
меньше | 1
меньше или равно | 2
равно | 3
больше или равно | 4
больше | 5

Хеш-индексы поддерживают только сравнения на равенство, поэтому они используют только одну стратегию, показанную в таблице 3.

Таблица 3. Хеш-стратегии

Операция | Номер стратегии
равно | 1

Индексы GiST более гибкие: у них вообще нет фиксированного набора стратегий. Вместо этого вспомогательная процедура «согласованности» каждого конкретного класса операторов GiST интерпретирует номера стратегий как ей угодно. Например, некоторые из встроенных классов операторов индекса GiST индексируют двумерные геометрические объекты, реализуя стратегии «R-дерева», показанные в таблице 4. Четыре из них являются настоящими двумерными тестами (пересекается с, одинаковы, содержит, содержится в); четыре из них учитывают только абсциссы, а еще четыре проводят те же тесты, только с ординатами.

Таблица 4. Стратегии двумерного R-дерева GiST

Операция | Номер стратегии
строго слева от | 1
не простирается правее | 2
пересекается с | 3
не простирается левее | 4
строго справа от | 5
одно и то же | 6
содержит | 7
содержится в | 8
не простирается выше | 9
строго ниже | 10
строго выше | 11
не простирается ниже | 12

По гибкости индексы SP-GiST аналогичны индексам GiST: у них нет фиксированного набора стратегий. Вместо этого вспомогательные процедуры каждого класса операторов интерпретируют номера стратегий в соответствии с определением класса операторов. В качестве примера в таблице 5 приведены номера стратегий, используемые встроенными классами операторов для точек.

Таблица 5. Стратегии SP-GiST для точек

Операция | Номер стратегии
строго слева от | 1
строго справа от | 5
одинаковы | 6
содержится в | 8
строго ниже | 10
строго выше | 11

Индексы GIN аналогичны индексам GiST и SP-GiST: у них тоже нет фиксированного набора стратегий. Вместо этого вспомогательные процедуры каждого класса операторов интерпретируют номера стратегий в соответствии с определением класса операторов. В качестве примера в таблице 6 приведены номера стратегий, используемые встроенным классом операторов для массивов.

Таблица 6. Стратегии GIN для массивов

Операция | Номер стратегии
пересекается с | 1
содержит | 2
содержится в | 3
равно | 4

Индексы BRIN аналогичны индексам GiST, SP-GiST и GIN: у них тоже нет фиксированного набора стратегий. Вместо этого вспомогательные процедуры каждого класса операторов интерпретируют номера стратегий в соответствии с определением класса операторов. В качестве примера в таблице 7 приведены номера стратегий, используемые встроенными классами операторов Minmax.

Таблица 7. Стратегии BRIN Minmax

Операция | Номер стратегии
меньше | 1
меньше или равно | 2
равно | 3
больше или равно | 4
больше | 5

Обратите внимание, что все вышеперечисленные операторы возвращают логические значения. На практике все операторы, определенные как операторы поиска для индексного метода, должны возвращать тип boolean, так как они должны находиться на верхнем уровне предложения WHERE, чтобы обрабатываться с индексом. (Некоторые методы доступа к индексу также поддерживают операторы упорядочивания, которые обычно не возвращают логические значения; это свойство обсуждается в подразделе Операторы упорядочивания.)

Вспомогательные процедуры индексного метода

Стратегии обычно не дают системе достаточно информации, чтобы понять, как использовать индекс. На практике для работы индексным методам требуются дополнительные вспомогательные процедуры. Например, индексный метод B-дерева должен уметь сравнивать два ключа и определять, больше, равен или меньше ли один другого. Точно так же метод хеш-индекса должен иметь возможность вычислять хеш-коды для значений ключа. Эти операции не соответствуют операторам, используемым в условиях в командах SQL; они являются внутренними административными процедурами, используемыми индексными методами.

Как и в случае стратегий, класс операторов определяет, какие конкретные функции должны играть каждую из этих ролей для данного типа данных и семантической интерпретации. Индексный метод определяет набор функций, которые ему нужны, а класс операторов определяет подходящие для использования функции, назначая им «номера функций поддержки», указанные в индексном методе.

Для B-деревьев требуются вспомогательные функции сравнения и могут предоставляться две дополнительные вспомогательные функции по выбору разработчика класса операторов, как показано в таблице 8. Требования к этим вспомогательным функциям рассматриваются далее в разделе Вспомогательные функции B-деревьев.

Таблица 8. Вспомогательные функции B-деревьев

Функция | Номер функции
Сравнивает два ключа и возвращает целое число меньше нуля, ноль или целое число больше нуля, показывающее, что первый ключ меньше, равен или больше второго | 1
Возвращает адреса вызываемых из C вспомогательных функций сортировки (необязательная) | 2
Сравнивает значение теста с базовым значением плюс/минус смещение и возвращает true или false в зависимости от результата сравнения (необязательная) | 3

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

Таблица 9. Вспомогательные функции хеша

Функция | Номер функции
Вычисляет 32-битное значение хеша для ключа | 1
Вычисляет 64-битное значение хеша для ключа с заданной 64-битной солью; если соль равна 0, младшие 32 бита результата должны соответствовать значению, которое было бы вычислено функцией 1 (необязательная) | 2

Индексы GiST имеют девять вспомогательных функций, две из которых являются необязательными, как показано в таблице 10. (Дополнительную информацию см. в главе Индексы GiST.)

Таблица 10. Вспомогательные функции GiST

Функция | Описание | Номер функции
consistent | определяет, удовлетворяет ли ключ условию запроса | 1
union | вычисляет объединение набора ключей | 2
compress | вычисляет сжатое представление ключа или индексируемого значения | 3
decompress | вычисляет развернутое представление сжатого ключа | 4
penalty | вычисляет издержки добавления нового ключа в поддерево с заданным ключом | 5
picksplit | определяет, какие записи страницы должны быть перемещены на новую страницу, и вычисляет ключи объединения для результирующих страниц | 6
equal | сравнивает два ключа и возвращает true, если они равны | 7
distance | определяет расстояние от ключа до значения запроса (необязательная) | 8
fetch | вычисляет исходное представление сжатого ключа для сканирования только по индексу (необязательная) | 9

Для индексов SP-GiST требуется пять вспомогательных функций, как показано в таблице 11. (Дополнительную информацию см. в главе Индексы SP-GiST.)

Таблица 11. Вспомогательные функции SP-GiST

Функция | Описание | Номер функции
config | предоставляет основную информацию о классе операторов | 1
choose | определяет, как вставить новое значение во внутренний кортеж | 2
picksplit | определяет, как разделить множество значений | 3
inner_consistent | определяет, какие внутренние ветви нужно искать для запроса | 4
leaf_consistent | определяет, удовлетворяет ли ключ условию запроса | 5

Индексы GIN имеют шесть вспомогательных функций, три из которых являются необязательными, как показано в таблице 12. (Дополнительную информацию см. в главе Индексы GIN.)

Таблица 12. Вспомогательные функции GIN

Функция | Описание | Номер функции
compare | сравнивает два ключа и возвращает целое число меньше нуля, ноль или целое число больше нуля, показывающее, что первый ключ меньше, равен или больше второго | 1
extractValue | извлекает ключи из индексируемого значения | 2
extractQuery | извлекает ключи из условия запроса | 3
consistent | определяет, соответствует ли значение условию запроса (логический вариант) (необязательна, если присутствует вспомогательная функция 6) | 4
comparePartial | сравнивает частичный ключ из запроса и ключ из индекса и возвращает целое число меньше ноль, нуля или целое число больше нуля, показывающее, что GIN должен игнорировать эту запись индекса, относиться к записи как к соответствующей или остановить сканирование индекса (необязательно) | 5
triConsistent | определяет, соответствует ли значение условию запроса (троичный вариант) (необязательна, если присутствует вспомогательная функция 4) | 6

Индексы BRIN имеют четыре основные вспомогательные функции, как показано в таблице 13; для этих основных функций может потребоваться предоставить дополнительные вспомогательные функции. (Дополнительную информацию см. в разделе Расширяемость.)

Таблица 13. Вспомогательные функции BRIN

Функция | Описание | Номер функции
opcInfo | возвращает внутреннюю информацию, описывающую сводные данные индексированных столбцов | 1
add_value | добавляет новое значение в существующий сводный кортеж индекса | 2
consistent | определяет, соответствует ли значение условию запроса | 3
union | вычисляет объединение двух сводных кортежей | 4

В отличие от операторов поиска, вспомогательные функции возвращают тот тип данных, который ожидает конкретный индексный метод; например, в случае функции сравнения для B-деревьев, целое число со знаком. Количество и типы аргументов каждой вспомогательной функции также зависят от индексного метода. Для B-дерева и хеша вспомогательные функции сравнения и хеширования принимают те же типы входных данных, что и операторы, включенные в класс операторов, но к большинству вспомогательных функций GiST, SP-GiST, GIN и BRIN это не относится.

Пример

Теперь, ознакомившись с основными идеями, можно перейти к обещанному примеру создания нового класса операторов. (Рабочую копию этого примера можно найти в src/tutorial/complex.c и src/tutorial/complex.sql в комплекте поставки файлов исходного кода). Класс операторов включает операторы, которые сортируют комплексные числа в порядке их абсолютных значений, поэтому мы выбираем для него имя complex_abs_ops. Во-первых, нам нужен набор операторов. Процедура определения операторов была рассмотрена в разделе Пользовательские операторы. Для класса операторов B-деревьев нам понадобятся следующие операторы:

абсолютное значение меньше (стратегия 1)

абсолютное значение меньше или равно (стратегия 2)

абсолютное значение равно (стратегия 3)

абсолютное значение больше или равно (стратегия 4)

абсолютное значение больше (стратегия 5)

Наименее подверженный ошибкам способ определения связанного набора операторов сравнения состоит в том, чтобы сначала написать вспомогательную функцию сравнения для B-дерева, а затем написать другие функции в виде однострочных оболочек это вспомогательной функции. Это снижает вероятность получения несогласованных результатов в тупиковых ситуациях. Следуя этому подходу, сначала мы пишем:

#define Mag(c)  ((c)->x*(c)->x + (c)->y*(c)->y)

static int
complex_abs_cmp_internal(Complex *a, Complex *b)
{
    double      amag = Mag(a),
                bmag = Mag(b);

    if (amag < bmag)
        return -1;
    if (amag > bmag)
        return 1;
    return 0;
}

Теперь функция «меньше» выглядит так:

PG_FUNCTION_INFO_V1(complex_abs_lt);

Datum
complex_abs_lt(PG_FUNCTION_ARGS)
{
    Complex    *a = (Complex *) PG_GETARG_POINTER(0);
    Complex    *b = (Complex *) PG_GETARG_POINTER(1);

    PG_RETURN_BOOL(complex_abs_cmp_internal(a, b) < 0);
}

Остальные четыре функции отличаются только тем, как они сравнивают результат внутренней функции с нулем.

Далее мы объявляем в SQL функции и операторы на основе этих функций:

CREATE FUNCTION complex_abs_lt(complex, complex) RETURNS bool
    AS 'имя_файла', 'complex_abs_lt'
    LANGUAGE C IMMUTABLE STRICT;

CREATE OPERATOR < (
   leftarg = complex, rightarg = complex, procedure = complex_abs_lt,
   commutator = >, negator = >=,
   restrict = scalarltsel, join = scalarltjoinsel
);

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

Здесь также стоит обратить внимание на следующие моменты:

Может быть только один оператор с именем, скажем, = и принимающий тип complex для обоих операндов. В этом случае у нас нет другого оператора = для complex, но если бы мы создавали тип данных, применимый на практике, то, вероятно, предпочли бы, чтобы оператор = был обычной операцией, проверяющей равенство комплексных чисел (а не равенство абсолютных значений). В этом случае для complex_abs_eq нужно выбрать другое имя оператора.

Хотя QHB может работать с функциями, имеющими одинаковое имя SQL, если у них разные типы данных аргументов, C может работать только с одной глобальной функцией, имеющей данное имя. Поэтому не следует давать функции на C/RUST какое-то простое имя вроде abs_eq. Обычно, во избежание конфликтов с функциями для других типов данных, рекомендуется включать в имя функции на C/RUST имя типа данных.

Мы могли бы дать функции имя abs_eq в SQL, полагаясь на то, что QHB отличит ее по типу данных аргумента от любой другой функции SQL с тем же именем. Чтобы упростить пример, мы дали функции одинаковые имена на уровне C и уровне SQL.

Следующим шагом является регистрация вспомогательной процедуры, необходимой для B-деревьев. В нашем примере реализующий это код C находится в том же файле, который содержит функции оператора. Вот как мы объявляем функцию:

CREATE FUNCTION complex_abs_cmp(complex, complex)
    RETURNS integer
    AS 'имя_файла'
    LANGUAGE C IMMUTABLE STRICT;

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

CREATE OPERATOR CLASS complex_abs_ops
    DEFAULT FOR TYPE complex USING btree AS
        OPERATOR        1       <,
        OPERATOR        2       <=,
        OPERATOR        3       =,
        OPERATOR        4       >=,
        OPERATOR        5       >,
        FUNCTION        1       complex_abs_cmp(complex, complex);

И готово! Теперь должно быть возможно создавать и использовать индексы B-деревья по столбцам complex.

Мы могли бы сделать записи операторов более подробными, например:

OPERATOR        1       < (complex, complex),

но когда операторы принимают тот же тип данных, для которого мы определяем класс операторов, в этом нет необходимости.

В приведенном выше примере предполагается, что вы хотите сделать этот новый класс операторов классом операторов B-деревьев по умолчанию для типа данных complex. Если вы этого не хотите, просто пропустите слово DEFAULT.

Классы операторов и семейства операторов

До сих пор мы неявно предполагали, что класс операторов имеет дело только с одним типом данных. Хотя, безусловно, в конкретном индексируемом столбце может быть только один тип данных, часто имеет смысл индексировать операции, сравнивающие индексированный столбец со значением другого типа. Кроме того, если в сочетании с классом операторов возможно использование межтипового оператора, зачастую у другого типа данных тоже имеется собственный связанный класс операторов. В подобных случаях полезно установить явную связь между связанными классами, так как это может помочь планировщику с оптимизацией запросов SQL (особенно для классов операторов B-дерева, поскольку планировщик хорошо знает, как с ними работать).

Для удовлетворения этих потребностей QHB использует концепцию семейства операторов. Семейство операторов содержит один или несколько классов операторов, а также может содержать индексируемые операторы и соответствующие вспомогательные функции, принадлежащие семейству в целом, но не к какому-либо одному классу в нем. Мы называем такие операторы и функции «слабосвязанными» с семейством, в противоположность обычной связи с определенным классом. Как правило, каждый класс операторов содержит операторы, работающие с одним типом данных, тогда как межтиповые операторы слабо связаны с семейством.

Все операторы и функции в семействе операторов должны иметь совместимую семантику, где требования совместимости устанавливаются индексным методом. Исходя из этого, вы можете спросить, зачем выделять отдельные подмножества семейства как классы операторов; и действительно, для многих целей разделение на классы не имеет значения, и единственной полезной группировкой является семейство. Смысл определения классов операторов состоит в том, что они указывают, какая часть семейства необходима для поддержки какого-либо конкретного индекса. Если существует индекс, использующий класс операторов, то этот класс нельзя удалить, не удалив сам индекс, — но остальные части семейства операторов, а именно другие классы операторов и операторы со слабой связью, удалить можно. Таким образом, класс операторов следует определить так, чтобы он содержал минимальный набор операторов и функций, которые объективно нужны для работы с индексом по определенному типу данных, а затем в качестве слабосвязанных членов семейства операторов можно добавить связанные, но не существенные операторы.

В качестве примера, в QHB есть встроенное семейство операторов B-дерева integer_ops, которое включает классы операторов int8_ops, int4_ops и int2_ops для индексов bigint (int8), integer (int4) и smallint (int2) соответственно. Семейство также содержит межтиповые операторы сравнения, позволяющие сравнивать любые два из этих типов, так чтобы индекс по одному из этих типов можно было искать, используя значение сравнения другого типа. Семейство можно представить следующими определениями:

CREATE OPERATOR FAMILY integer_ops USING btree;

CREATE OPERATOR CLASS int8_ops
DEFAULT FOR TYPE int8 USING btree FAMILY integer_ops AS
  -- стандартные сравнения int8
  OPERATOR 1 <,
  OPERATOR 2 <=,
  OPERATOR 3 =,
  OPERATOR 4 >=,
  OPERATOR 5 >,
  FUNCTION 1 btint8cmp(int8, int8),
  FUNCTION 2 btint8sortsupport(internal),
  FUNCTION 3 in_range(int8, int8, int8, boolean, boolean) ;

CREATE OPERATOR CLASS int4_ops
DEFAULT FOR TYPE int4 USING btree FAMILY integer_ops AS
  -- стандартные сравнения int4
  OPERATOR 1 <,
  OPERATOR 2 <=,
  OPERATOR 3 =,
  OPERATOR 4 >=,
  OPERATOR 5 >,
  FUNCTION 1 btint4cmp(int4, int4),
  FUNCTION 2 btint4sortsupport(internal),
  FUNCTION 3 in_range(int4, int4, int4, boolean, boolean) ;

CREATE OPERATOR CLASS int2_ops
DEFAULT FOR TYPE int2 USING btree FAMILY integer_ops AS
  -- стандартные сравнения int2
  OPERATOR 1 <,
  OPERATOR 2 <=,
  OPERATOR 3 =,
  OPERATOR 4 >=,
  OPERATOR 5 >,
  FUNCTION 1 btint2cmp(int2, int2),
  FUNCTION 2 btint2sortsupport(internal),
  FUNCTION 3 in_range(int2, int2, int2, boolean, boolean) ;

ALTER OPERATOR FAMILY integer_ops USING btree ADD
  -- межтиповые сравнения int8 и int2
  OPERATOR 1 < (int8, int2),
  OPERATOR 2 <= (int8, int2),
  OPERATOR 3 = (int8, int2),
  OPERATOR 4 >= (int8, int2),
  OPERATOR 5 > (int8, int2),
  FUNCTION 1 btint82cmp(int8, int2),

  -- межтиповые сравнения int8 и int4
  OPERATOR 1 < (int8, int4),
  OPERATOR 2 <= (int8, int4),
  OPERATOR 3 = (int8, int4),
  OPERATOR 4 >= (int8, int4),
  OPERATOR 5 > (int8, int4),
  FUNCTION 1 btint84cmp(int8, int4),

  -- межтиповые сравнения int4 и int2
  OPERATOR 1 < (int4, int2),
  OPERATOR 2 <= (int4, int2),
  OPERATOR 3 = (int4, int2),
  OPERATOR 4 >= (int4, int2),
  OPERATOR 5 > (int4, int2),
  FUNCTION 1 btint42cmp(int4, int2),

  -- межтиповые сравнения int4 и int8
  OPERATOR 1 < (int4, int8),
  OPERATOR 2 <= (int4, int8),
  OPERATOR 3 = (int4, int8),
  OPERATOR 4 >= (int4, int8),
  OPERATOR 5 > (int4, int8),
  FUNCTION 1 btint48cmp(int4, int8),

  -- межтиповые сравнения int2 и int8
  OPERATOR 1 < (int2, int8),
  OPERATOR 2 <= (int2, int8),
  OPERATOR 3 = (int2, int8),
  OPERATOR 4 >= (int2, int8),
  OPERATOR 5 > (int2, int8),
  FUNCTION 1 btint28cmp(int2, int8),

  -- межтиповые сравнения int2 и int4
  OPERATOR 1 < (int2, int4),
  OPERATOR 2 <= (int2, int4),
  OPERATOR 3 = (int2, int4),
  OPERATOR 4 >= (int2, int4),
  OPERATOR 5 > (int2, int4),
  FUNCTION 1 btint24cmp(int2, int4),

  -- межтиповые функции in_range
  FUNCTION 3 in_range(int4, int4, int8, boolean, boolean),
  FUNCTION 3 in_range(int4, int4, int2, boolean, boolean),
  FUNCTION 3 in_range(int2, int2, int8, boolean, boolean),
  FUNCTION 3 in_range(int2, int2, int4, boolean, boolean) ;

Обратите внимание, что это определение «перегружает» стратегию оператора и номера вспомогательных функций: каждый номер встречается в семействе несколько раз. Это разрешено, если каждый экземпляр определенного номера имеет разные типы входных данных. Экземпляры, у которых оба типа входных данных совпадают с входным типом класса оператора, являются первичными операторами и вспомогательными функциями для этого класса операторов, и в большинстве случаев их следует объявлять как часть класса операторов, а не слабосвязанные члены семейства.

В семействе операторов B-дерева все операторы должны быть совместимы в контексте сортировки, как подробно описано в разделе Поведение классов операторов btree. Для каждого оператора в семействе должна быть предусмотрена вспомогательная функция с теми же двумя типами входных данных, что и у оператора. Рекомендуется, чтобы семейство было полным, т. е. содержало все операторы для каждой комбинации типов данных. Каждый класс операторов должен включать в себя только однотиповые операторы и вспомогательную функцию для своего типа данных.

Чтобы создать семейство операторов хеширования для нескольких типов данных, нужно создать совместимые вспомогательные функции хеша для каждого типа данных, поддерживаемого этим семейством. Здесь совместимость означает, что функции гарантированно возвращают один и тот же хеш-код для любых двух значений, которые операторы равенства семейства считают равными, даже когда те имеют разные типы. Обычно этого трудно добиться, когда типы имеют разные физические представления, но в некоторых случаях это можно сделать. Более того, преобразование значения одного типа данных, представленного в семействе операторов, в другой тип, также представленный в этом семействе, путем неявного или двоичного приведения, не должно изменять вычисленное значение хеша. Обратите внимание, что единственная вспомогательная функция задается для каждого типа данных, а не для каждого оператора равенства. Рекомендуется, чтобы семейство было полным, т. е. предоставляло оператор равенства для каждой комбинации типов данных. Каждый класс операторов должен включать только однотиповой оператор равенства и вспомогательную функцию для своего типа данных.

Индексы GiST, SP-GiST и GIN не имеют явного представления о межтиповых операциях. Набор поддерживаемых операторов определяется только теми операциями, которые могут обрабатывать основные вспомогательные функции данного класса операторов.

В BRIN требования зависят от структуры, предоставляющей классы операторов. Для классов операторов, основанных на minmax, требуется такое же поведение, как и для семейств операторов B-дерева: все операторы в семействе должны поддерживать совместимую сортировку, а приведение не должно изменять соответствующий порядок сортировки.

Системные зависимости от классов операторов

QHB использует классы операторов, чтобы оценивать свойства операторов разными способами, а не только определять, можно ли их использовать с индексами. Поэтому советуем создать классы операторов, даже если у вас нет намерения индексировать какие-либо столбцы вашего типа данных.

В частности, существуют функции SQL, такие как ORDER BY и DISTINCT, которым требуются сравнение и сортировка значений. Чтобы реализовать эти функции для определенного пользователем типа данных, QHB ищет класс оператора B-дерева по умолчанию для этого типа данных. Член «равно» этого класса операторов определяет представление системы о равенстве значений для GROUP BY и DISTINCT, а порядок сортировки, налагаемый классом операторов, определяет порядок ORDER BY по умолчанию.

Если для типа данных не существует класса операторов B-дерева по умолчанию, система будет искать класс операторов хеширования по умолчанию. Но поскольку этот класс операторов обеспечивает только равенство, он способен поддерживать только группирование, а не сортировку.

Если для типа данных не существует класса операторов по умолчанию, то при попытке использовать вышеперечисленные функции SQL с этим типом данных вы получите ошибки вида «не удалось определить оператор упорядочивания».

Возможна сортировка по классу операторов B-дерева, отличному от заданного по умолчанию, если указать, например, в предложении USING оператор «меньше» этого класса:

SELECT * FROM mytable ORDER BY somecol USING ~<~;

Также, указав в USING оператор «больше» этого класса, можно выбрать сортировку по убыванию.

Сравнение массивов определенного пользователем типа также основывается на семантике, определенной классом операторов B-дерева по умолчанию для этого типа. Если таковой отсутствует, но есть класс операторов хеширования по умолчанию, то поддерживается равенство массивов, но не упорядочивание.

Еще одна особенность SQL, которая требует даже больших знаний о типе данных, — это режим определения рамки RANGE смещение PRECEDING/FOLLOWING для оконных функций (см. раздел Вызовы оконных функций). Для запроса вида

SELECT sum(x) OVER (ORDER BY x RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING)
  FROM mytable;

недостаточно знать, как упорядочить по x; база данных также должна понимать, как «вычесть 5» или «прибавить 10» к значению x текущей строки, чтобы определить границы текущей рамки окна. Сравнить результирующие границы со значениями x других строк можно с помощью операторов сравнения, предоставляемых классом операторов B-дерева, который определяет порядок ORDER BY, — но операторы сложения и вычитания не входят в этот класс операторов. Тогда какие операторы следует использовать? Жестко фиксировать выбранные операторы в коде было бы нежелательно, поскольку для разных порядков сортировки (разные классы операторов B-дерева) может потребоваться разное поведение. Поэтому класс операторов B-дерева позволяет указывать вспомогательную функцию in_range, в которой объединены сложение и вычитание, подходящие для порядка сортировки. Он даже способен предоставить более одной такой функции, в случае если в качестве смещения в предложениях RANGE имеет смысл использовать несколько разных типов данных. Если в классе операторов B-дерева, связанном с указанным для окна предложением ORDER BY, нет соответствующей вспомогательной функции in_range, режим RANGE смещение PRECEDING FOLLOWING не поддерживается.

Еще одной важной особенностью является то, что оператор равенства, находящийся в семействе операторов хеширования, является кандидатом для применения при объединении и агрегировании по хешу и связанными с ними оптимизациями. Семейство операторов хеширования играет здесь ключевую роль, так как именно в нем задаются функции хеширования.

Операторы упорядочивания

Некоторые методы доступа к индексу (в настоящее время только GiST и SP-GiST) поддерживают концепцию операторов упорядочивания. До сих пор мы рассматривали операторы поиска. Оператор поиска — это оператор, для которого можно выполнить поиск по индексу, чтобы найти все строки, удовлетворяющие условию WHERE индексированный_столбец оператор константа. Обратите внимание, что при этом ничего не говорится о порядке, в котором будут возвращены подходящие строки. Оператор упорядочивания, напротив, не ограничивает набор возвращаемых строк, а вместо этого определяет их порядок. С оператором упорядочивания можно, просканировав индекс, получить строки в порядке, представленном условием ORDER BY индексированный_столбец оператор константа. Причина определения операторов упорядочивания таким образом состоит в том, что оно поддерживает поиск ближайшего соседа, если этот оператор измеряет расстояние. Например, запрос типа

SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;

находит десять мест, ближайших к заданной целевой точке. Индекс GiST по столбцу location может сделать это эффективно, потому что <-> — это оператор упорядочивания.

В то время как операторы поиска должны возвращать логические результаты, операторы упорядочивания обычно возвращают какой-то другой тип, например, float или numeric для расстояний. Этот тип обычно не совпадает с типом индексируемых данных. Чтобы избежать жестко зафиксированных в коде предположений о поведении различных типов данных, при определении оператора упорядочивания необходимо указать семейство операторов B-дерева, которое определяет порядок сортировки результирующего типа данных. Как было сказано в предыдущем подразделе, семейства операторов B-дерева определяют понятие упорядочивания в QHB, так что это естественное представление. Поскольку оператор <-> для точек возвращает float8, его можно указать в команде создания класса операторов так:

OPERATOR 15    <-> (point, point) FOR ORDER BY float_ops

где float_ops — это встроенное семейство операторов, которое включает операции с float8. В этом объявлении говорится, что индекс может возвращать строки в порядке возрастания значений оператора <->.

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

Есть две особенности классов операторов, которые мы еще не обсуждали, в основном потому, что они не подходят для наиболее часто используемых индексных методов.

Как правило, объявление оператора в качестве члена класса (или семейства) операторов означает, что индексный метод может отобрать точно набор строк, которые удовлетворяют условию WHERE с этим оператором. Например, запрос:

SELECT * FROM table WHERE integer_column < 4;

может быть точно удовлетворен индексом B-дерева по целочисленному столбцу. Но бывают случаи, когда индекс полезен как примерный указатель на соответствующие строки. Например, если индекс GiST хранит только ограничивающие прямоугольники для геометрических объектов, то он не сможет точно удовлетворить условию WHERE, которое проверяет пересечение между непрямоугольными объектами, такими как многоугольники. Тем не менее, этот индекс можно применить для поиска объектов, у которых ограничивающий прямоугольник пересекает ограничивающий прямоугольник целевого объекта, а затем выполнить точную проверку пересечения только для объектов, найденных по индексу. Если этот сценарий применим, такой индекс считается «неполным» для оператора. Поиск по неполному индексу реализуется с помощью индексного метода, возвращающего флаг recheck (перепроверить), когда строка действительно может или не может удовлетворять условию запроса. Затем базовая система проверит извлеченную строку по исходному условию запроса, чтобы понять, следует ли возвращать ее как действительного соответствующую ему. Этот подход работает, если индекс гарантированно возвращает все необходимые строки плюс, возможно, некоторые дополнительные строки, которые можно отбросить, вызвав исходный оператор. Индексные методы, поддерживающие неполный поиск (в настоящее время это GiST, SP-GiST и GIN), позволяют вспомогательным функциям отдельных классов операторов устанавливать флаг recheck, так что по сути это свойство класса операторов.

Вернемся к ситуации, когда мы храним в индексе только ограничивающий прямоугольник сложного объекта, например многоугольника. В этом случае нет смысла хранить в элементе индекса весь многоугольник — с тем же успехом там можно хранить и более простой объект типа box. Эта ситуация выражается указанием STORAGE в команде CREATE OPERATOR CLASS, которое мы запишем примерно так:

CREATE OPERATOR CLASS polygon_ops
    DEFAULT FOR TYPE polygon USING gist AS
        ...
        STORAGE box;

В настоящее время только индексные методы GiST, GIN и BRIN поддерживают тип STORAGE, который отличается от типа данных столбца. В GiST преобразованием типов данных при помощи STORAGE должны заниматься вспомогательные процедуры compress и decompress. В GIN тип STORAGE определяет тип значений «ключа», который обычно отличается от типа индексируемого столбца — например, в классе операторов для столбцов с целочисленным массивом ключами могут быть просто целые числа. В GIN за извлечение ключей из индексированных значений отвечают вспомогательные функции extractValue и extractQuery. BRIN аналогичен GIN: тип STORAGE определяет тип хранимых обобщенных значений, а вспомогательные процедуры классов операторов отвечают за их правильную интерпретацию.

Упаковка связанных объектов в расширение

Полезное расширение QHB обычно включает несколько объектов SQL; например, для нового типа данных потребуются новые функции, новые операторы и, возможно, новые классы операторов индекса. Целесообразно собрать все эти объекты в один пакет, чтобы упростить управление базой данных. В QHB такой пакет называется расширением. Чтобы определить расширение, понадобится как минимум файл скрипта, содержащий команды SQL для создания объектов расширения, и управляющий файл, задающий несколько основных свойств самого расширения. Если расширение содержит код на C/RUST, в него обычно также включается файл разделяемой библиотеки в котором был скомпилирован этот код. Как только вы предоставите все эти файлы, простая команда CREATE EXTENSION загрузит их в вашу базу данных.

При загрузке группы «слабосвязанных» объектов в вашу базу данных основное преимущество расширений перед обычным скриптом SQL заключается в том, что в первом случае QHB будет понимать, что объекты расширения связаны между собой. Все эти объекты можно удалить одной командой DROP EXTENSION (нет необходимости разрабатывать отдельный скрипт «uninstall»). Еще полезнее то, что утилита qhb_dump знает, что не должна выгружать отдельные компонентные объекты расширения — вместо этого она просто включит в дампы команду CREATE EXTENSION. Это значительно упрощает миграцию на новую версию расширения, которая может содержать дополнительные или другие объекты по сравнению со старой. Обратите внимание, однако, что при загрузке такого дампа в новую базу данных необходимо наличие управляющего файла, скрипта и других файлов расширения.

QHB не позволит вам удалить отдельный объект, содержащийся в расширении, кроме как при удалении всего расширения. Кроме того, хотя изменить определение компонентного объекта расширения и можно (например, с помощью команды CREATE OR REPLACE FUNCTION для функции), имейте в виду, что измененное определение не будет выгружено qhb_dump. Такое изменение обычно имеет смысл только в том случае, если вы одновременно вносите такое же изменение в файл скрипта расширения. (Но для таблиц, содержащих данные конфигурации, имеются специальные средства; см. подраздел Таблицы конфигурации расширений.) В производственных условиях для изменения компонентных объектов расширения обычно лучше создавать скрипт обновления расширения.

Скрипт расширения может устанавливать права доступа для объектов, являющихся частью расширения, с помощью команд GRANT и REVOKE. Окончательный набор прав для каждого объекта (если они установлены) будет сохранен в системном каталоге pg_init_privs. При использовании qhb_dump в дамп будет включена команда CREATE EXTENSION а затем набор операторов GRANT и REVOKE, необходимых, чтобы установить для объектов те же права, какие были на момент выгрузки.

В настоящее время QHB не поддерживает скрипты расширений, вызывающие операторы CREATE POLICY или SECURITY LABEL. Ожидается, что они будут установлены после создания расширения. В дампы, созданные qhb_dump, будут включены все политики RLS и метки безопасности на объектах расширения.

В механизме расширения также имеются средства поддержки для скриптов модификации упаковки, которые корректируют определения содержащихся в расширении объектов SQL. Например, если версия расширения 1.1 по сравнению с версией 1.0 добавляет одну функцию и изменяет тело другой, разработчик расширения может предоставить скрипт обновления, который вносит только эти два изменения. Затем, выполнив, команду ALTER EXTENSION UPDATE, можно применить эти изменения и отследить, какая версия расширения фактически установлена в заданной базе данных.

Виды объектов SQL, которые могут быть членами расширения, перечислены в описании команды ALTER EXTENSION. В частности, объекты уровня кластера, такие как базы данных, роли и табличные пространства, не могут быть членами расширения, поскольку расширение известно только в пределах одной базе данных. (Хотя скрипту расширения и не запрещено создавать такие объекты, если он это сделает, они не будут отслеживаться как часть расширения.) Также обратите внимание, что хотя таблица может быть членом расширения, подконтрольные ей объекты, такие как индексы, не считаются непосредственными членами расширения. Другим важным моментом является то, что схемы могут принадлежать расширениям, но не наоборот: расширение как таковое имеет неполное имя и не существует «внутри» какой-либо схемы. Тем не менее, компонентные объекты расширения будут принадлежать схемам, если это уместно для их типов. Самое расширение может иметь или не иметь оснований для того, чтобы владеть схемой (или схемами), в которой находятся его компонентные объекты.

Если скрипт расширения создает какие-либо временные объекты (например, временные таблицы), эти объекты обрабатываются как элементы расширения до конца текущего сеанса, но потом автоматически удаляются, как и любой временный объект. Это является исключением из правила, запрещающего удаление компонентных объектов без удаления всего расширения.

Файлы расширений

Команда CREATE EXTENSION задействует для каждого расширения управляющий файл, который должен называться так же, как расширение, с суффиксом .control, и должен быть помещен в каталог SHAREDIR/extension. Также должен быть хотя бы один скрипт-файл SQL с именем, соответствующим шаблону расширение--версия.sql (например, foo--1.0.sql для версии 1.0 расширения foo). По умолчанию скрипт (или скрипты) также размещается в каталоге SHAREDIR/extension, но в управляющем файле для них можно указать и другой каталог.

Формат управляющего файла расширения тот же, что и у файла qhb.conf, а именно список назначений имя_параметра = значение, по одному на строку. Также допускаются пустые строки и комментарии, начинающиеся с #. Не забудьте взять в кавычки все значения, отличные от единственного слова или числа.

В управляющем файле может задавать следующие параметры:

directory (string)

Каталог, содержащий скрипт(ы) SQL расширения. Если не задан абсолютный путь, имя относится к серверному каталогу SHAREDIR. Поведение по умолчанию равнозначно указанию directory = ’extension’.

default_version (string)

Версия расширения по умолчанию (та, которая будет установлена, если в CREATE EXTENSION не указана никакая версия). Хотя этот параметр можно опустить, но в отсутствие указания VERSION это приведет к сбою CREATE EXTENSION, поэтому обычно так делать не рекомендуется.

comment (string)

Комментарий (любая строка) о расширении. Комментарий применяется при изначальном создании расширения, но не при его обновлении (поскольку при этом могут замениться комментарии, добавленные пользователем). Кроме того, комментарий расширения можно задать, написав команду COMMENT в скрипт-файле.

encoding (string)

Кодировка набора символов, используемая в скрипт-файле (или файлах). Этот параметр следует указывать, если скрипт-файлы содержат символы, не относящиеся к ASCII. В противном случае предполагается, что в этих файлах используется кодировка базы данных.

module_pathname (string)

Значение этого параметра будет подставляться вместо каждого вхождения MODULE_PATHNAME в скрипт-файлах. Если этот параметр не задан, замена не производится. Как правило, для этого параметра устанавливается значение $libdir/имя_разделяемой_библиотеки, а затем в командах CREATE FUNCTION для функций на языке C/RUST используется MODULE_PATHNAME, поэтому скрипт- файлам не нужно жестко задавать имя разделяемой библиотеки.

requires (string)

Список имен расширений, от которых зависит это расширение, например requires = foo, bar’. Эти расширения должны быть установлены до того, как можно будет установить это расширение.

superuser (boolean)

Если этот параметр имеет значение true (по умолчанию), только суперпользователи могут создать расширение или обновить его до новой версии. Если установлено значение false, требуются только те права, которые необходимы для выполнения команд в скрипте установки или обновления.

relocatable (boolean)

Расширение является перемещаемым, если после изначального его создания содержащиеся в нем объекты можно переместить в другую схему расширения. По умолчанию установлено значение false, то есть расширение не является перемещаемым. Дополнительную информацию см. в подразделе Перемещаемость расширений.

schema (string)

Этот параметр можно установить только для неперемещаемых расширений. Он обеспечивает загрузку расширения только в указанную схему и не в какую другую. Параметр schema используется только при изначальном создании расширения, но не при его обновлении. Дополнительную информацию см. в подразделе Перемещаемость расширений.

В дополнение к основному управляющему файлу расширение.control расширение может содержать добавочные управляющие файлы с именами вида расширение--версия.control. Если они имеются, то должны находиться в каталоге скрипт-файлов. Добавочные управляющие файлы имеют тот же формат, что и основной управляющий файл. Любые параметры, установленные в добавочном управляющем файле, переопределяют параметры основного при установке этой версии расширения или обновлении до нее. Однако в добавочном управляющем файле нельзя установить параметры directory и default_version.

Скрипт-файлы SQL расширения могут содержать любые команды SQL, кроме команд управления транзакциями (BEGIN, COMMIT и т. д.) и команд, которые нельзя выполнить внутри блока транзакции (например, VACUUM). Это связано с тем, что скрипт-файлы неявно выполняются внутри блока транзакции.

Скрипт-файлы SQL расширения также могут содержать строки, начинающиеся с \echo, которые будут игнорироваться (обрабатываться как комментарии) механизмом расширения. Это средство обычно используется для вывода ошибки, если скрипт-файл выполняется в psql, а не загружается командой CREATE EXTENSION (см. пример скрипта в подразделе Пример расширения). Без этого пользователи могут случайно загрузить содержимое расширения как «слабосвязанные» объекты, а не как собственно расширение, — и получить состояние, которое несколько трудновато исправить.

Хотя скрипт-файлы могут содержать любые символы, разрешенные указанной кодировкой, управляющие файлы должны содержать только символы ASCII, поскольку QHB никак не может узнать кодировку управляющего файла. На практике вызывает сложности, только если вы хотите использовать символы не из ASCII в комментарии расширения. В этом случае рекомендуется не использовать параметр comment в управляющем файле, а вместо этого задать комментарий командой COMMENT ON EXTENSION в скрипт-файле.

Перемещаемость расширений

Пользователи часто испытывают желание загрузить объекты, содержащиеся в расширении, в схему, отличную от той, что планировал автор расширения. Существует три поддерживаемых уровня перемещаемости:

Полностью перемещаемое расширение можно переместить в другую схему в любое время, даже после его загрузки в базу данных. Это делается с помощью команды ALTER EXTENSION SET SCHEMA, которая автоматически переименовывает все компонентные объекты, перенося их в новую схему. Обычно это возможно только в том случае, если расширение не содержит внутренних предположений о том, в какой схеме находятся все его объекты. Кроме того, для начала все объекты расширения должны находиться в одной схеме (исключая объекты, которые не принадлежат никакой схеме, например, процедурные языки). Отметьте расширение как полностью перемещаемое, задав relocatable = true в его управляющем файле.

Расширение может быть перемещаемым во время установки, но не после. Обычно это происходит, когда скрипт-файлу расширения нужно явно ссылаться на целевую схему, например, при настройке свойств search_path для функций SQL. Для такого расширения задайте relocatable = false в его управляющем файле и используйте псевдоимя @extschema@ для обращения к целевой схеме в скрипт-файле. Все вхождения этой строки будут заменены фактическим именем целевой схемы перед выполнением скрипта. Пользователь может задать целевую схему, используя параметр SCHEMA команды CREATE EXTENSION.

Если расширение вообще не поддерживает перемещение, установите в его управляющем файле relocatable = false, а также задайте в параметре schema имя предполагаемой целевой схемы. Это предотвратит использование параметра SCHEMA команды CREATE EXTENSION, если только в ней не указана та же схема, что и в управляющем файле. Этот выбор обычно необходим, если расширение содержит внутренние предположения об именах схем, которые нельзя заменить псевдоименем @extschema@. Механизм замещения @extschema@ доступен и в этом случае, хотя он будет иметь ограниченное применение, поскольку имя схемы определяется управляющим файлом.

Во всех случаях скрипт-файл будет выполняться с параметром search_path, изначально заданным для указания на целевую схему; то есть CREATE EXTENSION делает то же, что и:

SET LOCAL search_path TO @extschema@;

Это позволяет объектам, созданным скрипт-файлом, перейти в целевую схему. Скрипт файл может изменить search_path если пожелает, но обычно это нежелательно. search_path восстанавливает прежнее значение по завершении CREATE EXTENSION.

Целевая схема определяется параметром schema в управляющем файле, если он указан, либо параметром SCHEMA в команде CREATE EXTENSION, если он задан, либо текущей схемой создания объектов по умолчанию (первой в параметре search_path вызывающего). Когда используется параметр schema управляющего файла, целевая схема будет создана, если она еще не существует, но в двух других случаях она уже должна существовать.

Если в параметре requires в управляющем файле перечислены какие-либо обязательные расширения, их целевые схемы добавляются к начальному значению search_path. Это делает их объекты видимыми для скрипт-файла нового расширения.

Хотя неперемещаемое расширение может содержать объекты, распределенные по нескольким схемам, обычно желательно поместить все объекты, предназначенные для внешнего использования, в одну схему, которая считается целевой схемой расширения. Такое расположение удачно сочетается со значением search_path по умолчанию при создании зависимых расширений.

Таблицы конфигурации расширений

Некоторые расширения включают таблицы конфигурации, содержащие данные, которые могут быть добавлены или изменены пользователем после установки расширения. Обычно, если таблица является частью расширения, ни определение таблицы, ни ее содержимое не будут выгружены qhb_dump. Но такое поведение для таблицы конфигурации нежелательно; любые изменения данных, сделанные пользователем, должны быть включены в дампы, иначе после резервного копирования и перезагрузки расширение будет вести себя по-другому.

Чтобы решить эту проблему, скрипт-файл расширения может пометить созданную им таблицу или последовательность как отношение конфигурации, что заставит qhb_dump включить в дампы содержимое таблицы или последовательности (но не ее определение). Для этого после создания таблицы или последовательности нужно вызвать функцию pg_extension_config_dump(regclass, text), например

CREATE TABLE my_config (key text, value text);
CREATE SEQUENCE my_config_seq;

SELECT pg_catalog.pg_extension_config_dump('my_config', '');
SELECT pg_catalog.pg_extension_config_dump('my_config_seq', '');

Таким образом можно пометить любое количество таблиц или последовательностей. Также можно пометить последовательности, связанные со столбцами serial или bigserial.

Когда второй аргумент pg_extension_config_dump является пустой строкой, с помощью qhb_dump выгружается все содержимое таблицы. Обычно это правильно только в том случае, если при создании скриптом расширения данная таблица изначально пуста. Если же в таблице содержится смесь исходных данных и данных, добавленных пользователем, то второй аргумент pg_extension_config_dump предоставляет условие WHERE, которое выбирает данные, подлежащие выгрузке. Например, можно создать таблицу

CREATE TABLE my_config (key text, value text, standard_entry boolean);

SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry');

а затем сделать так, чтобы поле standard_entry содержало true только в строках, созданных скриптом расширения.

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

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

Условие фильтра, связанное с таблицей конфигурации, можно изменить, снова вызвав pg_extension_config_dump. (Обычно это может пригодиться в скрипте обновления расширения). Единственный способ пометить таблицу как больше не являющуюся таблицей конфигурации — это отвязать ее от расширения с помощью команды ALTER EXTENSION ... DROP TABLE.

Обратите внимание, что отношения внешних ключей между этими таблицами будут определять порядок, в котором их выгружает qhb_dump. В частности, qhb_dump попытается выгрузить сначала основную таблицу, а затем таблицу, которая на нее ссылается. Поскольку отношения внешних ключей устанавливаются во время выполнения команды CREATE EXTENSION (до загрузки данных в таблицы), циклические зависимости не поддерживаются. При наличии циклических зависимостей данные все равно будут выгружены, но этот дамп нельзя будет восстановить напрямую, и потребуется вмешательство пользователя.

Чтобы выгрузить состояние последовательностей, связанных со столбцами serial или bigserial, их следует помечать напрямую. Одной только маркировки их родительских отношений для этой цели недостаточно.

Обновления расширений

Одним из преимуществ механизма расширения является то, что он предоставляет удобные способы управления обновлениями команд SQL, которые определяют объекты расширения. Это делается путем назначения имени или номера каждой выпущенной версии скрипта установки расширения. Кроме того, если вы хотите, чтобы пользователи могли динамически обновлять свои базы данных от одной версии до другой, нужно предоставить скрипты обновления, которые вносят необходимые изменения для перехода с одной версии на другую. Скриптам обновления назначаются имена вида расширение--старая_версия--целевая_версия.sql (например, foo--1.0--1.1.sql содержит команды для изменения версии расширения foo с 1.0 на 1.1).

При наличии подходящего скрипта обновления команда ALTER EXTENSION UPDATE обновит установленное расширение до указанной новой версии. Скрипт обновления выполняется в той же среде, которую команда CREATE EXTENSION предоставляет для скриптов установки: в частности, search_path устанавливается таким же образом, а любые новые объекты, созданные скриптом, автоматически добавляются в расширение. Кроме того, если скрипт решит удалить компонентные объекты расширения, они автоматически от него отсоединятся.

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

Механизм обновления можно применять для решения важной особой задачи: преобразования «слабосвязанной» коллекции объектов в расширение. Ранее многие писали модули расширений, которые просто создавали различные разрозненные объекты. При наличии базы данных, уже содержащей такие объекты, как можно преобразовать объекты в правильно упакованное расширение? Один из способов — удалить их, а затем выполнить простую команду CREATE EXTENSION, но это нежелательно, если у объектов есть зависимости (например, если в таблице есть столбцы с типом данных, созданным расширением). Чтобы исправить эту ситуацию, нужно создать пустое расширение, затем воспользоваться командой ALTER EXTENSION ADD, чтобы присоединить к расширению каждый существующий объект, после чего, наконец, создать все новые объекты, которые имеются в текущей версии расширения, но отсутствуют в неупакованном выпуске. CREATE EXTENSION поддерживает этот сценарий предложением FROM old_version, благодаря которому команда запускает не обычный скрипт установки для целевой версии, а скрипт обновления с именем расширение--старая_версия--целевая_версия.sql. Выбор имени фиктивной версии для использования в качестве имени старая_версия остается за автором расширения, хотя по общепринятому соглашению обычно задается unpackaged. Если у вас есть несколько предыдущих версий, которые необходимо обновить до стиля расширения, используйте для их идентификации разные фиктивные имена.

Чтобы достигнуть запрошенной версии, команда ALTER EXTENSION может выполнять последовательности скрипт-файлов обновления. Например, если доступны только foo--1.0--1.1.sql и foo--1.1--2.0.sql, ALTER EXTENSION будет применять их последовательно, если будет запрошено обновление до версии 2.0, а в данный момент установлена версия 1.0.

QHB не делает никаких предположений о свойствах имен версий: например, он не знает, следует ли 1.1 за 1.0. Он просто сопоставляет доступные имена версий и следует по пути, который требует применения наименьшего количества скриптов обновления. (Имя версии может быть фактически любой строкой, которая не содержит -- или не начинается и не заканчивается -.)

Иногда бывает полезно предоставить скрипты «понижения версии», например foo- 1.1--1.0.sql, чтобы получить возможность отменить изменения, внесенные версией 1.1. Если вы их предоставляете, остерегайтесь вероятности неожиданного применения такого скрипта, если окажется, что он обеспечивает более короткий путь. Рискованная ситуация возникает тогда, когда имеется скрипт обновления «короткого пути», который перепрыгивает через несколько версий, и скрипт понижения версии до начальной точки первого скрипта. Возможно, для понижения версии с последующим обновлением по короткому пути потребуется меньше шагов, чем для последовательного повышения версии. Если скрипт понижения версии удалит какие-либо незаменимые объекты, это приведет к нежелательным результатам.

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

SELECT * FROM pg_extension_update_paths('имя_расширения');

Она показывает каждую пару различных известных имен версий для указанного расширения, а также последовательность пути обновления, которая будет принята для перехода от исходной версии до целевой, или NULL, если доступного пути обновления нет. Путь выводится в текстовом виде с разделителями --. Если вы предпочитаете формат массива, можно использовать regexp_split_to_array(path,'--').

Установка расширений с помощью скриптов обновления

Расширение, существующее уже некоторое время, вероятно, будет иметь несколько версий, для которых автору потребуется написать скрипт обновления. Например, если вы выпустили расширение foo в версиях 1.0, 1.1 и 1.2, для них понадобятся скрипты обновления foo--1.0--1.1.sql и foo--1.1-- .2.sql. Прежде нужно было также создавать новые скрипт-файлы foo-- .1.sql и foo--1.2.sql, которые напрямую собирали более новые версии расширений, иначе те можно было установить только путем установки 1.0 с последующим обновлением. Это было утомительно и требовало многократных повторений, но теперь в этом нет необходимости, поскольку команда CREATE EXTENSION способна автоматически следовать цепочкам обновлений. Например, если доступны только скрипт-файлы foo--1.0.sql, foo--1.0--1.1.sql и foo--1.1--1.2.sql, то запрос на установку версии 1.2 выполняется с помощью последовательного запуска этих трех скриптов. Процесс будет проходить так же, как если бы вы сначала установили версию 1.0, а затем обновили ее до 1.2. (Как и в случае с ALTER EXTENSION UPDATE, если доступно несколько путей, предпочтение отдается кратчайший путь.) Размещение скрипт-файлов расширения по такому образцу может снизить трудозатраты на выпуск небольших обновлений.

Если вы используете добавочные (ориентированные на версию) управляющие файлы для расширения, поддерживаемого по этому образцу, имейте в виду, что управляющий файл нужен каждой версии, даже если у нее нет отдельного скрипта установки, поскольку этот управляющий файл будет определять, как именно выполняется неявное обновление до эту версии. Например, если в файле foo--1.0.control задается requires = 'bar', а в других управляющих файлах foo этого не происходит, зависимость расширения от bar будет сброшена при обновлении с версии 1.0 до другой.

Примечания о безопасности расширений

Широко распространяемые расширения должны иметь минимальные предположения о базе данных, в которой они находятся. Следовательно, функции, предоставляемые расширениями, необходимо писать в безопасном стиле, который нельзя будет скомпрометировать в атаках, использующих пути поиска.

Расширение, у которого свойство superuser имеет значение true, также должно быть защищено от угроз безопасности, связанных с действиями, которые происходят в скриптах установки и обновления этого расширения. Злонамеренному пользователю не составит особого труда создать объекты типа троянского коня, которые впоследствии скомпрометируют выполнение небрежно написанного скрипта, позволив этому пользователю получить права суперпользователя.

Советы по безопасному написанию функций представлены ниже в подразделе Примечания о безопасности функций в расширениях, а советы по написанию скриптов установки — в подразделе Примечания о безопасности скриптов расширений.

Примечания о безопасности функций в расширениях

Предоставляемые расширениями функции на языках SQL и PL, подвержены атакам, использующим пути поиска, во время выполнения, поскольку синтаксический анализ таких функций происходит при их выполнении, а не создании.

Страница команды CREATE FUNCTION содержит совет относительно безопасного написания функций с характеристикой SECURITY DEFINER. Имеет смысл применять эти методики и для всех функций, предоставляемых расширением, так как эти функции может вызвать пользователь с расширенными правами.

Если вы не можете настроить search_path так, чтобы он содержал только безопасные схемы, считайте, что каждое неполное имя может быть разрешено в объект, определенный злонамеренным пользователем. Избегайте конструкций, явно зависящих от search_path; например, IN и CASE выражение WHEN всегда выбирают оператор по пути поиска. Вместо них используйте OPERATOR(схема.=) ANY и CASE WHEN выражение.

Обычно расширение общего назначения не должно рассчитывать на то, что оно будет установлено в безопасной схеме, что означает, что даже уточненные схемой ссылки на его собственные объекты не вполне безопасны. Например, если в расширении есть функция myschema.myfunc(bigint), то вызовы вроде myschema.myfunc(42) могут быть перехвачены вредоносной функцией myschema.myfunc(integer). Позаботьтесь о том, чтобы типы данных функции и параметров оператора точно соответствовали объявленным типам их аргументов, и при необходимости используйте явные приведения.

Примечания о безопасности скриптов расширений

Скрипты установки и обновления расширения следует писать так, чтобы защитить их от атак с использованием пути поиска во время их выполнения. Если имя объекта в скрипте может оказаться разрешено в какой-либо другой объект, нежели планировал автор скрипта, то компрометация расширения произойдет сразу же или позже, при использовании неверно определенного объекта расширения.

Команды DDL, например CREATE FUNCTION и CREATE OPERATOR CLASS, в целом безопасны, но избегайте любых команд, содержащих расширения общего назначения. Например, требуют проверки команда CREATE VIEW, а также выражение DEFAULT в команде CREATE FUNCTION.

Иногда скрипту расширения может понадобиться выполнить SQL общего назначения, например, чтобы внести в каталог изменения, невозможные через DDL. Обязательно выполняйте такие команды с безопасным search_path; не полагайтесь на защищенность пути, установленного при выполнении CREATE/ALTER EXTENSION. Наилучшим выходом будет временно задать search_path значение 'pg_catalog, pg_temp' и везде, где потребуется, добавить явные указания схемы, в которую устанавливается расширение. (Этот прием также может быть полезен при создании представлений.)

Ссылки на другие расширения крайне сложно полностью обезопасить, отчасти из-за отсутствия уверенности, в какой схеме находится другое расширения. Риски уменьшаются, если оба расширения устанавливаются в одну схему, так как при этом вредоносный объект не сможет оказаться перед целевым расширением в используемом во время установки search_path. Однако в настоящее время нет механизма, который бы это требовал.

Не используйте команду CREATE OR REPLACE FUNCTION, за исключением случаев, когда скрипт обновления должен изменить определение функции, которая гарантированно уже является членом расширения. (Это касается и других вариантов команд с вариантом OR REPLACE.) Использование OR REPLACE опасно не только тем, что так можно случайно переписать чужую функцию, но и тем, что создает угрозу безопасности, так как у переписанной функции владелец все равно останется прежний, и он сможет модифицировать ее.

Пример расширения

Вот полный пример расширения, созданного исключительно средствами SQL, составного типа с двумя элементами, который может хранить в своих слотах, названных k и v, значения любого типа. Для хранения все нетекстовые значения автоматически преобразуются в текст.

Скриптовый файл pair--1.0.sql выглядит так:

-- выразит недовольство, если скрипт получен из psql, а не посредством команды CREATE EXTENSION
\на экране отобразится: Use "CREATE EXTENSION pair" to load this file. (Используйте "CREATE EXTENSION pair", чтобы загрузить этот файл ) \quit (выйти)

CREATE TYPE pair AS ( k text, v text );

CREATE OR REPLACE FUNCTION pair(text, text)
RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::@extschema@.pair;';

CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, FUNCTION = pair);

-- "SET search_path" легче корректировать, но полные имена работают лучше.
CREATE OR REPLACE FUNCTION lower(pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW(lower($1.k), lower($1.v))::@extschema@.pair;'
SET search_path = pg_temp;

CREATE OR REPLACE FUNCTION pair_concat(pair, pair)
RETURNS pair LANGUAGE SQL
AS 'SELECT ROW($1.k OPERATOR(pg_catalog.||) $2.k,
               $1.v OPERATOR(pg_catalog.||) $2.v)::@extschema@.pair;';

Управляющий файл pair.control выглядит так:

# расширение pair
comment = 'Тип данных для пары ключ/значение'
default_version = '1.0'
# не может быть перемещаемым, так как использует @extschema@
relocatable = false

Хотя вам вряд ли понадобится сборочный файл для установки этих двух файлов в правильный каталог, можно использовать Makefile, содержащий это:

EXTENSION = pair
DATA = pair--1.0.sql

PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

Этот сборочный файл опирается на инфраструктуру PGXS, которая описывается в следующем подразделе. Команда make install установит управляющие файлы и скрипт-файлы в правильный каталог, который определит pg_config.

Как только файлы будут установлены, используйте команду CREATE EXTENSION, чтобы загрузить объекты в ту или иную базу данных.

Инфраструктура сборки расширений

Если вы обдумываете распространение ваших модулей расширения QHB, то учтите, что подготовить для них переносимую систему сборки может быть довольно сложно. Поэтому инсталляция QHB включает в себя инфраструктуру сборки расширений, называемую PGXS, поэтому простые модули расширений можно собрать просто на уже установленном сервере. PGXS в основном предназначен для расширений, которые включают в себя код на C/RUST, хотя ее также можно использовать и для расширений на чистом SQL. Обратите внимание, что PGXS не претендует на то, чтобы быть универсальной инфраструктурой системы сборки, которую можно применять для создания любого программного средства, взаимодействующего с QHB; она всего лишь автоматизирует общие правила сборки для простых модулей расширения сервера. Для более сложных пакетов вам, по всей видимости, придется написать собственную систему сборки.

Чтобы использовать инфраструктуру PGXS для вашего расширения, вы должны написать простой сборочный файл. В нем нужно установить некоторые переменные и включить глобальный сборочный файл PGXS. Вот пример, который создает модуль расширения с именем isbn_issn, состоящий из разделяемой библиотеки, содержащей некоторый код на C/RUST, управляющего файла расширения, скрипта SQL, включаемого файла (он требуется только в том случае, если у других модулей возникнет необходимость получить доступ к функциям расширения напрямую, без использования SQL) и текстового файла документации:

MODULES = isbn_issn
EXTENSION = isbn_issn
DATA = isbn_issn--1.0.sql
DOCS = README.isbn_issn
HEADERS_isbn_issn = isbn_issn.h

PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

Последние три строки всегда должны быть неизменными. Ранее в файле вы присваиваете значения переменным или добавляете собственные правила создания.

Установите одну из этих трех переменных, чтобы указать, что будет собрано:

MODULES

список объектов разделяемой библиотеки, которые должны быть собраны из исходных файлов с одинаковым основанием (не включайте в этот список суффиксы библиотеки)

MODULE_big

разделяемая библиотека, которая должна быть собрана из нескольких исходных файлов (перечислите объектные файлы в OBJS)

PROGRAM

исполняемая программа, которая должна быть собрана (перечислите объектные файлы в OBJS)

Также можно установить следующие переменные:

EXTENSION

имена расширений; для каждого имени нужно предоставить файл расширение.control, который будет установлен в префикс/share/extension

MODULEDIR

подкаталог в каталоге префикс/share в который должны быть установлены файлы DATA и DOCS (если не задан, по умолчанию используется extension, если установлена переменная EXTENSION, или contrib, если нет)

DATA

случайные файлы для установки в префикс/share/$MODULEDIR

DATA_built

случайные файлы для установки в префикс/share/$MODULEDIR, которые сначала нужно собрать

DATA_TSEARCH

случайные файлы для установки в префикс/share/tsearch_data

DOCS

случайные файлы для установки в префикс/doc/$MODULEDIR

HEADERS HEADERS_built

Файлы для установки (и, возможно, сборки) в префикс/include/server/$MODULEDIR/$MODULE_big.

В отличие от файлов DATA_built, файлы в HEADERS_built не удаляются целью clean; если хотите их удалить, добавьте их в EXTRA_CLEAN или напишите собственные правила для этого.

HEADERS_$MODULE HEADERS_built_$MODULE

Файлы для установки (после сборки, если указано) в префикс/include/server/$MODULEDIR/$MODULE, где $MODULE должно быть именем модуля, используемым в MODULES или MODULE_big.

В отличие от файлов DATA_built, файлы в HEADERS_built_$MODULE не удаляются целью clean; если вы хотите их удалить, добавьте их в EXTRA_CLEAN или напишите собственные правила для этого.

Допускается использовать для одного модуля обе переменные или любую их комбинацию, если только в списке MODULES нет двух имен модулей, которые отличаются только наличием префикса built_, что может привести к неоднозначности. В этом (будем надеяться, маловероятном) случае следует использовать только переменные HEADERS_built_$MODULE.

SCRIPTS

скрипт-файлы (не двоичные файлы) для установки в префикс/bin

SCRIPTS_built

скрипт-файлы (не двоичные файлы) для установки в префикс/bin, которые сначала нужно собрать

REGRESS

список регрессионных тестов (без суффикса), см. ниже

REGRESS_OPTS

дополнительные параметры для передачи pg_regress

ISOLATION

список изоляционных тестов, более подробную информацию см. ниже

ISOLATION_OPTS

дополнительные параметры для передачи pg_isolation_regress

TAP_TESTS

параметр, определяющий, нужно ли запускать тесты TAP, см. ниже

NO_INSTALLCHECK

не определять цель installcheck; полезна, например, если тестам требуется специальная конфигурация или не используется pg_regress

EXTRA_CLEAN

дополнительные файлы для удаления при make clean

PG_CPPFLAGS

флаги, которые будут добавлены перед другими в CPPFLAGS

PG_CFLAGS

флаги, которые будут добавлены в CFLAGS

PG_CXXFLAGS

флаги, которые будут добавлены в CXXFLAGS

PG_LDFLAGS

флаги, которые будут добавлены перед другими в LDFLAGS

PG_LIBS

будет добавлено в строку компоновки PROGRAM

SHLIB_LINK

будет добавлено в строку компоновки MODULE_big

PG_CONFIG

путь к программе pg_config в инсталляции QHB, с которой будет проведена сборка (обычно указывается просто pg_config, и используется первая же такая программа в вашем пути PATH)

Поместите этот сборочный файл под именем Makefile в каталог, содержащий ваше расширение. Затем можно выполнить make для компиляции, а потом make install для установки вашего модуля. По умолчанию расширение компилируется и устанавливается для той инсталляции QHB, которая соответствует первой программе pg_config, найденной при поиске по пути в вашем PATH. Можно воспользоваться и другой инсталляцией, настроив PG_CONFIG так, чтобы тот указывал на ее программу pg_config, либо в сборочном файле, либо в командной строке make.

Кроме того, если вы хотите сохранить каталог сборки отдельно, можно запустить make в каталоге за пределами исходного дерева вашего расширения. Эта процедура также называется сборкой с VPATH. Выполняется она так:

mkdir build_dir
cd build_dir
make -f /path/to/extension/source/tree/Makefile
make -f /path/to/extension/source/tree/Makefile install

Как вариант, можно подготовить каталог для сборки с VPATH аналогично тому, как это делается для кода ядра. Один из способов сделать это — использовать скрипт ядра config/prep_buildtree. После этого можно выполнить сборку, установив переменную VPATH для make следующим образом:

make VPATH=/path/to/extension/source/tree
make VPATH=/path/to/extension/source/tree install

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

Скрипты, перечисленные в переменной REGRESS, используются для регрессионного тестирования вашего модуля, и вызвать их можно командой make installcheck после выполнения команды make install. Чтобы запустить тестирование, необходим работающий сервер QHB. Скрипт-файлы, перечисленные в REGRESS, должны находиться в подкаталоге с именем sql/ в каталоге вашего расширения. Эти файлы должны иметь расширение .sql, которое нельзя включать в список REGRESS в сборочном файле. Для каждого теста также должен создаваться файл с ожидаемым выводом в подкаталоге expected/, с тем же основанием и расширением .out. Команда make installcheck выполняет каждый тестовый скрипт с помощью psql и сравнивает полученный вывод с соответствующим ожидаемым файлом. Все обнаруженные различия будут записаны в файл regression.diffs в формате diff -c. Обратите внимание, что при попытке запустить тест, в котором отсутствует ожидаемый файл, этот тест будет отображаться как «проблемный», поэтому убедитесь, что у вас есть все ожидаемые файлы.

Скрипты, перечисленные в переменной ISOLATION, используются для нагрузочного тестирования работы модуля при проведении параллельного сеанса, и вызвать их можно командой make installcheck после выполнения команды make install. Чтобы запустить тестирование, необходим работающий сервер QHB. Скрипт-файлы, перечисленные в ISOLATION, должны находиться в подкаталоге specs/ в каталоге вашего расширения. Эти файлы должны иметь расширение .spec, которое нельзя включать в список ISOLATION в сборочном файле. Для каждого теста также должен быть файл с ожидаемым выводом в подкаталоге expected/, с тем же основанием и расширением .out. Команда make installcheck выполняет каждый тестовый скрипт и сравнивает полученный вывод с соответствующим ожидаемым файлом. Все обнаруженные различия будут записаны в файл output_iso/regression.diffs в формате diff -c. Обратите внимание, что при попытке запустить тест, в котором отсутствует ожидаемый файл, этот тест будет отображаться как «проблемный», поэтому убедитесь, что у вас есть все ожидаемые файлы.

Переменная TAP_TESTS позволяет использовать TAP-тесты. Данные, получаемые при каждом прогоне, хранятся в подкаталоге tmp_check/.

Совет
Самый простой способ создать ожидаемые файлы — создать пустые файлы, а затем выполнить тестовый прогон (который, конечно, выявит различиях). Проверьте фактические файлы результатов, сохраненные в каталоге results/ (для тестов REGRESS) или каталоге *output_iso/results/ (для тестов ISOLATION), а затем, если они соответствуют вашим ожиданиям от теста, скопируйте их в expected/.

Триггеры

Эта глава содержит общую информацию о написании триггерных функций. Триггерные функции могут быть написаны на большинстве доступных процедурных языков, включая PL/pgSQL. Прочитав текущую главу, вы можете обратиться к главе PL/pgSQL, чтобы узнать подробности о написании на нем триггера.

Также можно написать триггерную функцию на C или Rust, хотя большинству людей проще использовать один из процедурных языков. В настоящее время невозможно написать триггерную функцию на простом языке функций SQL.

Обзор триггерного поведения

Триггер - это указание, согласно которой база данных должна автоматически выполнять определенную функцию всякий раз, когда выполняется определенный тип операции. Триггеры могут быть прикреплены к таблицам (разделенным или нет), представлениям и сторонним таблицам.

В таблицах и внешних таблицах можно определить триггеры для выполнения до или после любой операции INSERT, UPDATE или DELETE, либо один раз для измененной строки, либо один раз для оператора SQL. Триггеры UPDATE могут быть установлены на срабатывание только в том случае, если в предложении SET UPDATE упоминаются определенные столбцы. Триггеры также могут быть TRUNCATE. Если происходит событие триггера, функция триггера вызывается в соответствующее время для обработки события.

В представлениях можно определить триггеры для выполнения вместо операций INSERT, UPDATE или DELETE. Такие триггеры INSTEAD OF запускаются один раз для каждой строки, которую необходимо изменить. Функция триггера отвечает за выполнение необходимых модификаций базовой таблицы (или таблиц) представления и при необходимости возвращает измененную строку, как она будет отображаться в представлении. Триггеры для представлений также могут быть определены для выполнения один раз для каждого оператора SQL, до или после операций INSERT, UPDATE или DELETE. Однако такие триггеры срабатывают только в том случае, если в представлении также имеется триггер INSTEAD OF. В противном случае любой оператор, нацеленный на представление должен быть переписан в оператор, влияющий на его базовую таблицу (или таблицы), и тогда триггеры, которые будут срабатывать.

Функция триггера должна быть определена до создания триггера и объявлена как функция, не имеющая аргументов и возвращающая тип trigger. Функция триггера получает свой ввод через специально переданную структуру TriggerData, а не в виде аргументов обычной функции.

После создания подходящей триггерной функции триггер устанавливается с помощью CREATE TRIGGER. Одна и та же триггерная функция может использоваться для нескольких триггеров.

QHB предлагает как триггеры для каждой строки, так и триггеры для каждой инструкции. При использовании триггера, функция вызывается один раз для каждой строки, на которую влияет оператор, вызвавший триггер. Напротив, триггер для каждого оператора вызывается только один раз при выполнении, независимо от количества строк, затронутых этим оператором. В частности, оператор, который влияет на ноль строк, все равно будет приводить к выполнению любых применимых триггеров для каждого оператора. Эти два типа триггеров иногда называют триггерами уровня строки и триггерами уровня оператора, соответственно. Триггеры в TRUNCATE могут быть определены только на уровне оператора, а не для каждой строки.

Триггеры также классифицируются в зависимости от того, срабатывают они до, после или вместо операции. Они называются триггерами BEFORE, триггерами AFTER и триггерами INSTEAD OF соответственно. Триггеры уровня BEFORE естественным образом срабатывают до того, как оператор начинает что-либо делать, в то время как AFTER запускаются на самом конце оператора. Эти типы триггеров могут быть определены в таблицах, представлениях или внешних таблицах. Уровень BEFORE вызывает срабатывание непосредственно перед тем, как будет обработана определенная строка, в то время как уровень AFTER срабатывает в конце инструкции. Эти типы триггеров могут быть определены только для секционированных и внешних таблиц, но не для представлений. Триггеры INSTEAD OF могут быть определены только для представлений и только на уровне строк; они запускаются немедленно, так как каждая строка в представлении определяется как нуждающаяся в операции.

Оператор, нацеленный на родительскую таблицу в иерархии наследования или разбиения, не вызывает срабатывания триггеров уровня дочерних таблиц (запускаются только триггеры уровня операторов) родительской таблицы. Однако триггеры на уровне строк любых затронутых дочерних таблиц будут срабатывать.

Если INSERT содержит предложение ON CONFLICT DO UPDATE, возможно, что эффекты триггеров BEFORE INSERT и BEFORE UPDATE уровня строки могут быть применены так, как это видно из конечного состояния обновленной строки, если ссылка на столбец была EXCLUDED. Однако для выполнения обоих наборов триггеров уровня BEFORE необязательно указывать ссылку на столбец EXCLUDED. Возможность неожиданных результатов должна быть рассмотрена, когда есть триггеры уровня строки BEFORE INSERT и BEFORE UPDATE, которые изменяют вставляемую / обновляемую строку (это может быть проблематично, даже если изменения более или менее эквивалентны, или даже идемпотентны). Обратите внимание, что триггеры UPDATE на уровне оператора выполняются, когда указано ON CONFLICT DO UPDATE, независимо от того, затронул ли UPDATE какие-либо строки или нет, и независимо от того, выбирался ли альтернативный путь UPDATE. INSERT с предложением ON CONFLICT DO UPDATE сначала выполнит триггеры уровня BEFORE INSERT уровня INSERT, затем триггеры BEFORE UPDATE, затем триггеры AFTER UPDATE и, наконец, триггеры AFTER INSERT.

Если UPDATE в многораздельной таблице приводит к перемещению строки в другой раздел, он будет выполнен как DELETE из исходного раздела, за которым следует INSERT в новый раздел. В этом случае все триггеры BEFORE UPDATE уровня строки и все триггеры BEFORE DELETE уровня строки запускаются в исходном разделе. Затем все триггеры BEFORE INSERT уровня строки запускаются в целевом разделе. Возможность неожиданных результатов следует учитывать, когда все эти триггеры влияют на перемещение строки. Что касается триггеров AFTER ROW применяются триггеры AFTER DELETE и AFTER INSERT; но не AFTER UPDATE, потому что UPDATE было преобразовано в DELETE и INSERT. Что касается триггеров уровня оператора, ни один из триггеров DELETE или INSERT не запускается, даже если происходит перемещение строки; сработают только триггеры UPDATE определенные в целевой таблице, используемой в операторе UPDATE.

Триггерные функции, вызываемые триггерами для каждого оператора, всегда должны возвращать NULL. Триггерные функции, вызываемые триггерами для каждой строки, могут возвращать строку таблицы (значение типа HeapTuple) вызывающему исполнителю, если они того пожелают. Триггер на уровне строк, срабатывающий до операции, имеет следующие варианты:

Он может вернуть NULL, чтобы пропустить операцию для текущей строки. Это указывает исполнителю не выполнять операцию на уровне строк, которая вызвала триггер (вставка, изменение или удаление определенной строки таблицы).

Только для триггеров INSERT и UPDATE на уровне строк возвращаемая строка становится строкой, которая будет вставлена или заменит обновляемую строку. Это позволяет триггерной функции изменять вставляемую или обновляемую строку.

Триггер BEFORE уровня строки, который не имеет намерения вызвать какое-либо из этих поведений, должен быть аккуратен. В таком случае необходимо возвращать в качестве своего результата ту же самую строку, которая была передана. (то есть строка NEW для триггеров INSERT и UPDATE, строка OLD для триггеров DELETE).

Триггер INSTEAD OF уровня строк должен либо возвращать NULL, чтобы указать, что он не изменил никаких данных из базовых таблиц представления, либо возвращать строку представления, которая была передана (строка NEW для операций INSERT и UPDATE, или OLD ряд для DELETE операции). Ненулевое возвращаемое значение используется, чтобы сигнализировать, что триггер выполнил необходимые модификации данных в представлении. Это приведет к увеличению числа строк, на которые влияет команда. Для операций INSERT и UPDATE триггер может изменить строку NEW перед ее возвратом. Это изменит данные, возвращаемые INSERT RETURNING или UPDATE RETURNING, и будет полезно, когда представление не будет отображать точно те же данные, которые были предоставлены.

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

Некоторые соображения применимы к сгенерированным столбцам. Сохраненные сгенерированные столбцы вычисляются после триггеров BEFORE и до триггеров AFTER. Поэтому сгенерированное значение может быть проверено в триггерах AFTER. В триггерах BEFORE строка OLD содержит старое сгенерированное значение, как и следовало ожидать, но строка NEW еще не содержит нового сгенерированного значения и не должна быть доступна. В интерфейсе языка C содержимое столбца на этом этапе не определено; язык программирования более высокого уровня должен предотвращать доступ к сохраненному сгенерированному столбцу в строке NEW в триггере BEFORE. Изменения значения сгенерированного столбца в триггере BEFORE игнорируются и будут перезаписаны.

Если для одного и того же события в одном и том же отношении определено более одного триггера, триггеры сработают в алфавитном порядке по имени триггера. В случае триггеров BEFORE и INSTEAD OF возможно измененная строка, возвращаемая каждым триггером, становится входом для следующего триггера. Если какой-либо триггер BEFORE или INSTEAD OF возвращает NULL, операция прекращается для этой строки, и последующие триггеры не запускаются (для этой строки).

Определение триггера также может указывать логическое условие WHEN, которое будет проверено, чтобы увидеть, должен ли триггер срабатывать. В триггерах уровня строки условие WHEN может проверять старые и (или) новые значения столбцов строки. (Триггеры уровня оператора также могут иметь условия WHEN, хотя для них эта функция не так полезна.) В триггере BEFORE условие WHEN оценивается непосредственно перед выполнением функции, поэтому использование WHEN существенно не отличается от проверки того же условия в начале триггерной функции. Однако в триггере AFTER условие WHEN оценивается сразу после обновления строки и определяет, стоит ли в очереди событие для запуска триггера в конце оператора. Таким образом, когда условие WHEN триггера AFTER не возвращает истину, нет необходимости ставить в очередь событие или повторно извлекать строку в конце оператора. Это может привести к значительному ускорению операторов, которые изменяют множество строк, если триггер нужно запустить только для нескольких строк. INSTEAD OF не поддерживают условия WHEN.

Как правило, триггеры BEFORE уровня строки используются для проверки или изменения данных, которые будут вставлены или обновлены. Например, триггер BEFORE может использоваться для вставки текущего времени в столбец timestamp или для проверки согласованности двух элементов строки. Триггеры AFTER наиболее разумно используются для распространения обновлений на другие таблицы или для проверки согласованности с другими таблицами. Причина такого разделения заключается в том, что триггер AFTER должен быть уверен, что видит окончательное значение строки, а триггер BEFORE нет; после этого могут быть другие BEFORE триггерами. Если у вас нет особой причины для выполнения триггера BEFORE или AFTER, случай BEFORE более эффективен, поскольку информацию об операции не нужно сохранять до конца оператора.

Если триггерная функция выполняет команды SQL, эти команды могут снова запускать триггеры. Это известно как каскадные триггеры. Нет прямого ограничения на количество каскадных уровней. Каскады могут вызывать рекурсивный вызов одного и того же триггера; например, триггер INSERT может выполнить команду, которая вставляет дополнительную строку в ту же таблицу, вызывая повтор триггера INSERT. Программист триггера обязан избегать бесконечной рекурсии в таких сценариях.

Когда определяется триггер, для него могут быть указаны аргументы. Цель включения аргументов в определение триггера - разрешить различным триггерам с одинаковыми требованиями вызывать одну и ту же функцию. Например, может быть обобщенная триггерная функция, которая принимает в качестве аргументов два имени столбца и помещает текущего пользователя в один, а текущую метку времени - в другой. Правильно написанная триггерная функция не зависит от конкретной таблицы, для которой она запускается. Таким образом, эту же функцию можно использовать для событий INSERT в любой таблице с подходящими столбцами, например, для автоматического отслеживания создания записей в таблице транзакций. Он также может использоваться для отслеживания событий последнего обновления, если он определен как триггер UPDATE.

Каждый язык программирования, который поддерживает триггеры, имеет свой собственный метод, позволяющий сделать входные данные триггера доступными для функции триггера. Эти входные данные включают тип события триггера (например, INSERT или UPDATE), а также любые аргументы, которые были перечислены в CREATE TRIGGER. Для триггера уровня строки входные данные также включают строку NEW для триггеров INSERT и UPDATE и (или) или строку OLD для триггеров UPDATE и DELETE.

По умолчанию триггеры уровня оператора не имеют никакого способа проверить отдельные строки, измененные этим самым оператором. Но триггер AFTER STATEMENT может запросить создание таблиц переходов, чтобы сделать наборы затронутых строк доступными для триггера. AFTER ROW также могут запрашивать таблицы переходов, чтобы они могли видеть общие изменения в таблице, а также изменения в отдельной строке, для которой они в данный момент запускаются. Способ проверки таблиц переходов тоже зависит от используемого языка программирования, но типичный подход заключается в том, чтобы заставить таблицы переходов действовать как временные таблицы только для чтения, к которым могут обращаться команды SQL, выполненные в функции триггера.

Видимость изменений данных

Если вы выполняете команды SQL в своей функции триггера, и эти команды обращаются к таблице, вам необходимо знать правила видимости данных, поскольку они определяют, будут ли эти команды SQL видеть изменение данных, для которых сработал триггер. Кратко:

Триггеры уровня оператора следуют простым правилам видимости: ни одно из изменений, внесенных оператором, не видно триггерам BEFORE уровня оператора, тогда как все модификации видны триггерам AFTER уровня оператора.

Изменение данных (вставка, обновление или удаление), вызывающее срабатывание триггера, естественно, невидимо для команд SQL, выполняемых в триггере BEFORE уровне строк, поскольку это еще не произошло.

Однако команды SQL, выполняемые в триггере BEFORE на уровне строк, увидят влияние изменений данных для строк, ранее обработанных в той же внешней команде. Это требует осторожности, поскольку порядок этих событий изменений в общем случае не предсказуем; команда SQL, которая влияет на несколько строк, может посещать строки в любом порядке.

Аналогично, триггер INSTEAD OF уровня строк будет видеть влияние изменений данных, выполненных предыдущими срабатываниями триггеров INSTEAD OF в той же внешней команде.

Когда запускается триггер AFTER уровня строк, все изменения данных, сделанные внешней командой, уже завершены и видны для вызываемой функции триггера.

Если ваша триггерная функция написана на любом из стандартных процедурных языков, то приведенные выше операторы применимы, только если функция объявлена как VOLATILE. Функции, которые объявлены как STABLE или IMMUTABLE, не увидят изменений, внесенных вызывающей командой в любом случае.

Дополнительную информацию о правилах видимости данных можно найти в Видимость изменений данных SPI. Пример в разделе Полный пример запуска содержит демонстрацию этих правил.

Написание триггерных функций на C

В этом разделе описываются детали низкого уровня интерфейса для функции триггера. Эта информация необходима только при написании триггерных функций на языке C. Если вы используете язык более высокого уровня, то эти детали обрабатываются для вас автоматически. В большинстве случаев вам следует подумать об использовании процедурного языка, прежде чем писать свои триггеры на C. В документации каждого процедурного языка объясняется, как написать триггер на этом языке.

Функции запуска должны использовать интерфейс диспетчера функций «version 1».

Когда функция вызывается менеджером триггера, ей не передаются никакие обычные аргументы, но ей передается указатель «context», указывающий на структуру TriggerData. Функции на C могут проверить, были ли они вызваны из диспетчера триггеров или нет, выполнив макрос:

CALLED_AS_TRIGGER(fcinfo)

который распахивается в

((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))

Если это возвращает true, тогда безопасно привести fcinfo->context к типу TriggerData * и использовать структуру TriggerData. Функция не должна изменять структуру TriggerData или любые данные, на которые она указывает.

Структура TriggerData определена в commands/trigger.h:

typedef struct TriggerData
{
    NodeTag          type;
    TriggerEvent     tg_event;
    Relation         tg_relation;
    HeapTuple        tg_trigtuple;
    HeapTuple        tg_newtuple;
    Trigger         *tg_trigger;
    TupleTableSlot  *tg_trigslot;
    TupleTableSlot  *tg_newslot;
    Tuplestorestate *tg_oldtable;
    Tuplestorestate *tg_newtable;
} TriggerData;

где члены определены следующим образом:

type - всегда T_TriggerData.

tg_event - описывает событие, для которого вызывается функция. Вы можете использовать следующие макросы для проверки tg_event:

TRIGGER_FIRED_BEFORE(tg_event) - возвращает true, если триггер сработал до операции.

TRIGGER_FIRED_AFTER(tg_event) - возвращает true, если триггер сработал после операции.

TRIGGER_FIRED_INSTEAD(tg_event) - возвращает true, если сработал триггер вместо операции.

TRIGGER_FIRED_FOR_ROW(tg_event) - возвращает true, если триггер сработал для события уровня строки.

TRIGGER_FIRED_FOR_STATEMENT(tg_event) - возвращает true, если триггер сработал для события уровня оператора.

TRIGGER_FIRED_BY_INSERT(tg_event) возвращает true, если триггер был запущен командой INSERT.

TRIGGER_FIRED_BY_UPDATE(tg_event) - возвращает true, если триггер был запущен командой UPDATE.

TRIGGER_FIRED_BY_DELETE(tg_event) - возвращает true, если триггер был запущен командой DELETE.

TRIGGER_FIRED_BY_TRUNCATE(tg_event)_ - возвращает true, если триггер был запущен командой TRUNCATE.

tg_relation - это указатель на структуру, описывающую отношение, для которого сработал триггер. Посмотрите в utils/rel.h подробности об этой структуре. Наиболее интересными являются tg_relation->rd_att (дескриптор кортежей отношений) и tg_relation->rd_rel->relname (имя отношения; тип не char*, а NameData; используйте SPI_getrelname(tg_relation) для получения char*, если вам нужно копия имени).

tg_trigtuple - это указатель на строку, для которой сработал триггер. Эта строка вставляется, обновляется или удаляется. Если этот триггер был запущен для INSERT или DELETE. Это то, что вы должны вернуть из функции, если вы не хотите заменять строку другой (в случае INSERT) или пропустить операцию. Для триггеров во внешних таблицах значения системных столбцов здесь не указаны.

tg_newtuple - это указатель на новую версию строки, если триггер сработал для UPDATE, и NULL если он сработал для INSERT или DELETE. Это то, что вы должны вернуть из функции, если событие - UPDATE и вы не хотите заменять эту строку другой или пропустить операцию. Для триггеров во внешних таблицах значения системных столбцов здесь не указаны.

tg_trigger - это указатель на структуру типа Trigger, определенную в utils/reltrigger.h.

typedef struct Trigger
{
    Oid         tgoid;
    char       *tgname;
    Oid         tgfoid;
    int16       tgtype;
    char        tgenabled;
    bool        tgisinternal;
    Oid         tgconstrrelid;
    Oid         tgconstrindid;
    Oid         tgconstraint;
    bool        tgdeferrable;
    bool        tginitdeferred;
    int16       tgnargs;
    int16       tgnattr;
    int16      *tgattr;
    char      **tgargs;
    char       *tgqual;
    char       *tgoldtable;
    char       *tgnewtable;
} Trigger;

где:

tgname - это имя триггера.

tgnargs - это количество аргументов в tgargs.

tgargs - массив указателей на аргументы, указанные в операторе CREATE TRIGGER.

Другие члены предназначены только для внутреннего использования.

tg_trigtuplebuf - буфер, содержащий tg_trigtuple или InvalidBuffer если такого кортежа нет или он не хранится в дисковом буфере.

tg_newtuplebuf - буфер, содержащий tg_newtuple или InvalidBuffer если такого кортежа нет или он не хранится в буфере диска.

tg_oldtable - это указатель на структуру типа Tuplestorestate содержащую ноль или более строк в формате, заданном параметром tg_relation, или указатель NULL если нет отношения OLD TABLE.

tg_newtable - это указатель на структуру типа Tuplestorestate содержащую ноль или более строк в формате, заданном параметром tg_relation, или указатель NULL если нет отношения NEW TABLE.

Чтобы запросы, отправленные через SPI, ссылались на таблицы переходов, см. SPI_register_trigger_data.

Внимание! Функция триггера должна возвращать указатель NULL (но НЕ нулевое значение SQL, т.е. не устанавливать isNull как true). Будьте внимательны, возвращая либо tg_trigtuple либо tg_newtuple, в зависимости от ситуации, если вы не хотите изменять строку, с которой вы работаете.

Полный пример запуска

Вот очень простой пример триггерной функции, написанной на C. (Примеры триггеров, написанных на процедурных языках, можно найти в документации по процедурным языкам.)

Функция trigf сообщает количество строк в таблице ttest и пропускает фактическую операцию, если команда пытается вставить нулевое значение в столбец x. (Таким образом, триггер действует как ненулевое ограничение, но не прерывает транзакцию.)

Во-первых, определение таблицы:

CREATE TABLE ttest (
    x integer
);

Это исходный код функции триггера:

#include "postgres.h"
#include "fmgr.h"
#include "executor/spi.h"       /* this is what you need to work with SPI */
#include "commands/trigger.h"   /* ... triggers ... */
#include "utils/rel.h"          /* ... and relations */

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(trigf);

Datum
trigf(PG_FUNCTION_ARGS)
{
    TriggerData *trigdata = (TriggerData *) fcinfo->context;
    TupleDesc   tupdesc;
    HeapTuple   rettuple;
    char       *when;
    bool        checknull = false;
    bool        isnull;
    int         ret, i;

    /* make sure it's called as a trigger at all */
    if (!CALLED_AS_TRIGGER(fcinfo))
        elog(ERROR, "trigf: not called by trigger manager");

    /* tuple to return to executor */
    if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
        rettuple = trigdata->tg_newtuple;
    else
        rettuple = trigdata->tg_trigtuple;

    /* check for null values */
    if (!TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)
        && TRIGGER_FIRED_BEFORE(trigdata->tg_event))
        checknull = true;

    if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
        when = "before";
    else
        when = "after ";

    tupdesc = trigdata->tg_relation->rd_att;

    /* connect to SPI manager */
    if ((ret = SPI_connect()) < 0)
        elog(ERROR, "trigf (fired %s): SPI_connect returned %d", when, ret);

    /* get number of rows in table */
    ret = SPI_exec("SELECT count(*) FROM ttest", 0);

    if (ret < 0)
        elog(ERROR, "trigf (fired %s): SPI_exec returned %d", when, ret);

    /* count(*) returns int8, so be careful to convert */
    i = DatumGetInt64(SPI_getbinval(SPI_tuptable->vals[0],
                                    SPI_tuptable->tupdesc,
                                    1,
                                    &isnull));

    elog (INFO, "trigf (fired %s): there are %d rows in ttest", when, i);

    SPI_finish();

    if (checknull)
    {
        SPI_getbinval(rettuple, tupdesc, 1, &isnull);
        if (isnull)
            rettuple = NULL;
    }

    return PointerGetDatum(rettuple);
}

После того, как вы скомпилировали исходный код (см. Раздел 37.10.5), объявите функцию и триггеры:

CREATE FUNCTION trigf() RETURNS trigger
    AS 'filename'
    LANGUAGE C;

CREATE TRIGGER tbefore BEFORE INSERT OR UPDATE OR DELETE ON ttest
    FOR EACH ROW EXECUTE FUNCTION trigf();

CREATE TRIGGER tafter AFTER INSERT OR UPDATE OR DELETE ON ttest
    FOR EACH ROW EXECUTE FUNCTION trigf();

Теперь вы можете проверить работу триггера:

=> INSERT INTO ttest VALUES (NULL);
INFO:  trigf (fired before): there are 0 rows in ttest
INSERT 0 0

-- Insertion skipped and AFTER trigger is not fired

=> SELECT * FROM ttest;
 x
---
(0 rows)

=> INSERT INTO ttest VALUES (1);
INFO:  trigf (fired before): there are 0 rows in ttest
INFO:  trigf (fired after ): there are 1 rows in ttest
                                       ^^^^^^^^
                             remember what we said about visibility.
INSERT 167793 1
vac=> SELECT * FROM ttest;
 x
---
 1
(1 row)

=> INSERT INTO ttest SELECT x * 2 FROM ttest;
INFO:  trigf (fired before): there are 1 rows in ttest
INFO:  trigf (fired after ): there are 2 rows in ttest
                                       ^^^^^^
                             remember what we said about visibility.
INSERT 167794 1
=> SELECT * FROM ttest;
 x
---
 1
 2
(2 rows)

=> UPDATE ttest SET x = NULL WHERE x = 2;
INFO:  trigf (fired before): there are 2 rows in ttest
UPDATE 0
=> UPDATE ttest SET x = 4 WHERE x = 2;
INFO:  trigf (fired before): there are 2 rows in ttest
INFO:  trigf (fired after ): there are 2 rows in ttest
UPDATE 1
vac=> SELECT * FROM ttest;
 x
---
 1
 4
(2 rows)

=> DELETE FROM ttest;
INFO:  trigf (fired before): there are 2 rows in ttest
INFO:  trigf (fired before): there are 1 rows in ttest
INFO:  trigf (fired after ): there are 0 rows in ttest
INFO:  trigf (fired after ): there are 0 rows in ttest
                                       ^^^^^^
                             remember what we said about visibility.
DELETE 2
=> SELECT * FROM ttest;
 x
---
(0 rows)

Более сложные примеры доступны в src/test/regress/regress.c и SPI.

Триггеры событий

В дополнение к механизму триггера, описанному в главе 7, QHB. также предоставляет триггеры событий. В отличие от обычных триггеров, которые присоединяются к одной таблице и захватывают только события DML, триггеры событий являются глобальными для конкретной базы данных и способны захватывать события DDL.

Как и обычные триггеры, триггеры событий могут быть написаны на любом процедурном языке, который включает поддержку триггеров событий, на C или Rust, но не на простом SQL.

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

Триггер события срабатывает всякий раз, когда событие, с которым оно связано, происходит в базе данных. В настоящее время поддерживаются только события ddl_command_start, ddl_command_end, table_rewrite и sql_drop. Поддержка дополнительных событий может быть добавлена в будущих выпусках.

Событие ddl_command_start наступает непосредственно перед выполнением ddl_command_start CREATE, ALTER, DROP, SECURITY LABEL, COMMENT, GRANT или REVOKE. Перед срабатыванием триггера события не проверяется, существует ли затронутый объект или нет. Однако это событие не возникает для команд DDL, нацеленных на общие объекты - базы данных, роли и табличные пространства - или для команд, нацеленных на сами триггеры событий. Механизм запуска событий не поддерживает эти типы объектов. ddl_command_start также происходит непосредственно перед выполнением команды SELECT INTO, поскольку это эквивалентно CREATE TABLE AS.

Событие ddl_command_end наступает сразу после выполнения этого же набора команд. Чтобы получить более подробную информацию о выполненных операциях DDL, используйте функцию возврата pg_event_trigger_ddl_commands() из ddl_command_end триггера события ddl_command_end (см. Раздел 9.28). Обратите внимание, что триггер срабатывает после выполнения действий (но до фиксации транзакции), и, таким образом, системные каталоги могут быть прочитаны как уже измененные.

Событие sql_drop происходит непосредственно перед ddl_command_end для любой операции, которая удаляет объекты базы данных. Функция pg_event_trigger_dropped_objects() возвращает список объектов, которые были удалены, используйте функцию возврата из триггера события sql_drop (см. Функции запуска событий). Обратите внимание, что триггер выполняется после того, как объекты были удалены из системных каталогов, поэтому их больше невозможно увидеть.

Событие table_rewrite наступает непосредственно перед перезаписью таблицы некоторыми действиями команд ALTER TABLE и ALTER TYPE. В то время как другие операторы управления доступны для перезаписи таблицы, такие как CLUSTER и VACUUM, событие table_rewrite не инициируют.

Триггеры событий (как и другие функции) не могут быть выполнены в прерванной транзакции. Таким образом, если команда DDL завершается с ошибкой, любые связанные триггеры ddl_command_end не будут выполнены. И наоборот, если триггер ddl_command_start завершился с ошибкой, дальнейшие триггеры событий ddl_command_start не будут предпринимать попыток выполнить саму команду. Точно так же, если триггер ddl_command_end завершается с ошибкой, эффекты оператора DDL будут откатываться, как и в любом другом случае, когда содержащая транзакция прерывается.

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

Триггеры событий создаются с помощью команды CREATE EVENT TRIGGER. Для того чтобы создать триггер события, вы должны сначала создать функцию со специальным типом возврата event_trigger. Эта функция не должна (и не может) возвращать значение; возвращаемый тип служит просто сигналом, что функция должна быть вызвана как триггер события.

Если для определенного события определено более одного триггера, они будут срабатывать в алфавитном порядке по имени триггера.

Определение триггера также может указывать условие WHEN так что, например, триггер ddl_command_start может быть запущен только для определенных команд, которые пользователь хочет перехватить. Обычно такие триггеры используются для ограничения диапазона операций DDL, которые могут выполнять пользователи.

Матрица запуска событий

В таблице 8.1 перечислены все команды, для которых поддерживаются триггеры событий.

Таблица 8.1. Поддержка триггеров событий по командам

Командный тег | ddl_command_start | ddl_command_end | sql_drop | table_rewrite | Примечания
ALTER AGGREGATE | X | X | - | - |
ALTER COLLATION | X | X | - | - |
ALTER CONVERSION | X | X | - | - |
ALTER DOMAIN | X | X | - | - |
ALTER DEFAULT PRIVILEGES | X | X | - | - |
ALTER EXTENSION | X | X | - | - |
ALTER FOREIGN DATA WRAPPER | X | X | - | - |
ALTER FOREIGN TABLE | X | X | X | - |
ALTER FUNCTION | X | X | - | - |
ALTER LANGUAGE | X | X | - | - |
ALTER LARGE OBJECT | X | X | - | - |
ALTER MATERIALIZED VIEW | X | X | - | - |
ALTER OPERATOR | X | X | - | - |
ALTER OPERATOR CLASS | X | X | - | - |
ALTER OPERATOR FAMILY | X | X | - | - |
ALTER POLICY | X | X | - | - |
ALTER PROCEDURE | X | X | - | - |
ALTER PUBLICATION | X | X | - | - |
ALTER SCHEMA | X | X | - | - |
ALTER SEQUENCE | X | X | - | - |
ALTER SERVER | X | X | - | - |
ALTER STATISTICS | X | X | - | - |
ALTER SUBSCRIPTION | X | X | - | - |
ALTER TABLE | X | X | X | X |
ALTER TEXT SEARCH CONFIGURATION | X | X | - | - |
ALTER TEXT SEARCH DICTIONARY | X | X | - | - |
ALTER TEXT SEARCH PARSER | X | X | - | - |
ALTER TEXT SEARCH TEMPLATE | X | X | - | - |
ALTER TRIGGER | X | X | - | - |
ALTER TYPE | X | X | - | X |
ALTER USER MAPPING | X | X | - | - |
ALTER VIEW | X | X | - | - |
COMMENT | X | X | - | - | Только для локальных объектов
CREATE ACCESS METHOD | X | X | - | - |
CREATE AGGREGATE | X | X | - | - |
CREATE CAST | X | X | - | - |
CREATE COLLATION | X | X | - | - |
CREATE CONVERSION | X | X | - | - |
CREATE DOMAIN | X | X | - | - |
CREATE EXTENSION | X | X | - | - |
CREATE FOREIGN DATA WRAPPER | X | X | - | - |
CREATE FOREIGN TABLE | X | X | - | - |
CREATE FUNCTION | X | X | - | - |
CREATE INDEX | X | X | - | - |
CREATE LANGUAGE | X | X | - | - |
CREATE MATERIALIZED VIEW | X | X | - | - |
CREATE OPERATOR | X | X | - | - |
CREATE OPERATOR CLASS | X | X | - | - |
CREATE OPERATOR FAMILY | X | X | - | - |
CREATE POLICY | X | X | - | - |
CREATE PROCEDURE | X | X | - | - |
CREATE PUBLICATION | X | X | - | - |
CREATE RULE | X | X | - | - |
CREATE SCHEMA | X | X | - | - |
CREATE SEQUENCE | X | X | - | - |
CREATE SERVER | X | X | - | - |
CREATE STATISTICS | X | X | - | - |
CREATE SUBSCRIPTION | X | X | - | - |
CREATE TABLE | X | X | - | - |
CREATE TABLE AS | X | X | - | - |
CREATE TEXT SEARCH CONFIGURATION | X | X | - | - |
CREATE TEXT SEARCH DICTIONARY | X | X | - | - |
CREATE TEXT SEARCH PARSER | X | X | - | - |
CREATE TEXT SEARCH TEMPLATE | X | X | - | - |
CREATE TRIGGER | X | X | - | - |
CREATE TYPE | X | X | - | - |
CREATE USER MAPPING | X | X | - | - |
CREATE VIEW | X | X | - | - |
DROP ACCESS METHOD | X | X | X | - |
DROP AGGREGATE | X | X | X | - |
DROP CAST | X | X | X | - |
DROP COLLATION | X | X | X | - |
DROP CONVERSION | X | X | X | - |
DROP DOMAIN | X | X | X | - |
DROP EXTENSION | X | X | X | - |
DROP FOREIGN DATA WRAPPER | X | X | X | - |
DROP FOREIGN TABLE | X | X | X | - |
DROP FUNCTION | X | X | X | - |
DROP INDEX | X | X | X | - |
DROP LANGUAGE | X | X | X | - |
DROP MATERIALIZED VIEW | X | X | X | - |
DROP OPERATOR | X | X | X | - |
DROP OPERATOR CLASS | X | X | X | - |
DROP OPERATOR FAMILY | X | X | X | - |
DROP OWNED | X | X | X | - |
DROP POLICY | X | X | X | - |
DROP PROCEDURE | X | X | X | - |
DROP PUBLICATION | X | X | X | - |
DROP RULE | X | X | X | - |
DROP SCHEMA | X | X | X | - |
DROP SEQUENCE | X | X | X | - |
DROP SERVER | X | X | X | - |
DROP STATISTICS | X | X | X | - |
DROP SUBSCRIPTION | X | X | X | - |
DROP TABLE | X | X | X | - |
DROP TEXT SEARCH CONFIGURATION | X | X | X | - |
DROP TEXT SEARCH DICTIONARY | X | X | X | - |
DROP TEXT SEARCH PARSER | X | X | X | - |
DROP TEXT SEARCH TEMPLATE | X | X | X | - |
DROP TRIGGER | X | X | X | - |
DROP TYPE | X | X | X | - |
DROP USER MAPPING | X | X | X | - |
DROP VIEW | X | X | X | - |
GRANT | X | X | - | - | Только для локальных объектов
IMPORT FOREIGN SCHEMA | X | X | - | - |
REFRESH MATERIALIZED VIEW | X | X | - | - |
REVOKE | X | X | - | - | Только для локальных объектов
SECURITY LABEL | X | X | - | - | Только для локальных объектов
SELECT INTO | X | X | - | - |

Написание функций запуска событий на C

В этом разделе описываются подробности низкоуровневого интерфейса для функции запуска события. Эта информация необходима только при написании функций триггера событий на C. Если вы используете язык более высокого уровня, то эти детали обрабатываются автоматически. В большинстве случаев вам следует подумать об использовании процедурного языка, прежде чем писать свои триггеры событий на C. В документации каждого процедурного языка объясняется, как написать триггер событий на этом языке.

Функции триггера событий должны использовать интерфейс диспетчера «version 1».

Когда функция вызывается менеджером триггера события, ей не передаются никакие обычные аргументы кроме указателя «context», указывающий на структуру EventTriggerData. Функции на C могут проверить, были ли они вызваны из менеджера триггеров событий или нет, выполнив макрос:

CALLED_AS_EVENT_TRIGGER(fcinfo)

который распахивается в

((fcinfo)->context != NULL && IsA((fcinfo)->context, EventTriggerData))

Если это возвращает true, тогда безопасно привести fcinfo->context к типу EventTriggerData * и использовать структуру EventTriggerData. Функция не должна изменять структуру EventTriggerData или любые данные, на которые она указывает.

Структура EventTriggerData определена в commands/event_trigger.h:

typedef struct EventTriggerData
{
    NodeTag     type;
    const char *event;      /* event name */
    Node       *parsetree;  /* parse tree */
    const char *tag;        /* command tag */
} EventTriggerData;

где члены определены следующим образом:

type - всегда T_EventTriggerData.

event описывает событие, для которого вызывается функция, одно из ddl_command_start, ddl_command_end, sql_drop, table_rewrite (см. Раздел 8.1 о значении этих событий).

parsetree - указатель на дерево разбора команды. Проверьте исходный код QHB для уточнения деталей. Структура дерева разбора может быть изменена без предварительного уведомления.

tag - тег команды, связанный с событием, для которого запускается триггер события, например "CREATE FUNCTION".

Внимание! Функция триггера события должна возвращать указатель NULL (но НЕ нулевое значение SQL, т.е. не устанавливать isNull как true).

Полный пример запуска события

Вот очень простой пример функции триггера события, написанной на C. (Примеры триггеров, написанных на процедурных языках, можно найти в документации по процедурным языкам.)

Функция noddl вызывает ошибку каждый раз, когда она вызывается. Определение триггера события связало функцию с событием ddl_command_start. В результате все команды DDL (с исключениями, упомянутыми в разделе 8.1) не могут быть запущены.

Это исходный код функции триггера:

#include "postgres.h"
#include "commands/event_trigger.h"

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(noddl);

Datum
noddl(PG_FUNCTION_ARGS)
{
    EventTriggerData *trigdata;

    if (!CALLED_AS_EVENT_TRIGGER(fcinfo))  /* internal error */
        elog(ERROR, "not fired by event trigger manager");

    trigdata = (EventTriggerData *) fcinfo->context;

    ereport(ERROR,
        (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                 errmsg("command \"%s\" denied", trigdata->tag)));

    PG_RETURN_NULL();
}

После того, как вы скомпилировали исходный код (см. Раздел 37.10.5), объявите функцию и триггеры:

CREATE FUNCTION noddl() RETURNS event_trigger
    AS 'noddl' LANGUAGE C;

CREATE EVENT TRIGGER noddl ON ddl_command_start
    EXECUTE FUNCTION noddl();

Теперь вы можете проверить работу триггера:

=# \dy
                     List of event triggers
 Name  |       Event       | Owner | Enabled | Function | Tags
-------+-------------------+-------+---------+----------+------
 noddl | ddl_command_start | dim   | enabled | noddl    |
(1 row)

=# CREATE TABLE foo(id serial);
ERROR:  command "CREATE TABLE" denied

В этой ситуации, чтобы иметь возможность запускать некоторые команды DDL, когда вам нужно это сделать, вы должны либо сбросить триггер события, либо выключить его. Также может быть удобно выключить триггер только на время транзакции:

BEGIN;
    ALTER EVENT TRIGGER noddl DISABLE;
    CREATE TABLE foo (id serial);
    ALTER EVENT TRIGGER noddl ENABLE;
COMMIT;

(Напомним, что на команды DDL самих триггеров событий не влияют триггеры событий.)

Пример запуска события перезаписи таблицы

Благодаря событию table_rewrite можно реализовать политику перезаписи таблицы, разрешив перезапись только в определенное время обслуживания.

Вот пример реализации такой политики.

CREATE OR REPLACE FUNCTION no_rewrite()
 RETURNS event_trigger
 LANGUAGE plpgsql AS
$$
---
--- Implement local Table Rewriting policy:
---   public.foo is not allowed rewriting, ever
---   other tables are only allowed rewriting between 1am and 6am
---   unless they have more than 100 blocks
---
DECLARE
  table_oid oid := pg_event_trigger_table_rewrite_oid();
  current_hour integer := extract('hour' from current_time);
  pages integer;
  max_pages integer := 100;
BEGIN
  IF pg_event_trigger_table_rewrite_oid() = 'public.foo'::regclass
  THEN
        RAISE EXCEPTION 'you''re not allowed to rewrite the table %',
                        table_oid::regclass;
  END IF;

  SELECT INTO pages relpages FROM pg_class WHERE oid = table_oid;
  IF pages > max_pages
  THEN
        RAISE EXCEPTION 'rewrites only allowed for table with less than % pages',
                        max_pages;
  END IF;

  IF current_hour NOT BETWEEN 1 AND 6
  THEN
        RAISE EXCEPTION 'rewrites only allowed between 1am and 6am';
  END IF;
END;
$$;

CREATE EVENT TRIGGER no_rewrite_allowed
                  ON table_rewrite
   EXECUTE FUNCTION no_rewrite();

Интерфейс программирования сервера

Интерфейс программирования сервера (Server Programming Interface, SPI) дает разработчикам пользовательских функций на C или RUST возможность запускать команды SQL внутри своих функций или процедур. SPI — это набор функций интерфейса, упрощающих доступ к анализатору, планировщику и исполнителю. Также SPI осуществляет управление памятью.

Примечание
Доступные процедурные языки предоставляют различные средства для выполнения команд SQL из функций. Большинство этих средств основаны на SPI, поэтому эта документация может быть полезна и для пользователей этих языков.

Обратите внимание, что если команда, вызванная через SPI, завершится с ошибкой, управление не вернется в вашу функцию на C/RUST. Скорее всего, транзакция или субтранзакция, в которой выполняется ваша функция C/RUST, откатится. (Это может показаться удивительным, учитывая, что у большинства функций SPI имеются документированные соглашения о возврате ошибок. Однако эти соглашения применяются только к ошибкам, обнаруженным в самих функциях SPI). Можно восстановить управление после ошибки, создав собственную субтранзакцию, окружающую вызовы SPI, в которых может быть ошибка.

В случае успеха функции SPI возвращают неотрицательный результат (либо через возвращаемое целочисленное значение, либо через глобальную переменную SPI_result, как описано ниже). В случае ошибки будет возвращен отрицательный результат или NULL.

Файлы исходного кода, использующие SPI, должны включать заголовочный файл executor/spi.h.

Функции интерфейса

SPI_connect — подключить функцию на C/RUST к менеджеру SPI
SPI_finish — выключить функцию на C/RUST от менеджера SPI
SPI_execute — выполнить команду
SPI_exec — Выполнить команду чтения/записи
SPI_execute_with_args — выполнить команду с внешними параметрами
SPI_prepare — подготовить оператор, но пока не выполнять его
SPI_prepare_cursor — подготовить оператор, но пока не выполнять его
SPI_prepare_params — подготовить оператор, но пока не выполнять его
SPI_getargcount — вернуть количество аргументов, необходимых для оператора, подготовленного функцией SPI_prepare
SPI_getargtypeid — вернуть OID типа данных для аргумента оператора, подготовленного функцией SPI_prepare
SPI_is_cursor_plan — вернуть true, если оператор, подготовленный функцией SPI_prepare, можно использовать с функцией SPI_cursor_open
SPI_execute_plan — выполнить оператор, подготовленный функцией SPI_prepare
SPI_execute_plan_with_paramlist — выполнить оператор, подготовленный функцией SPI_prepare
SPI_execp — выполнить оператор в режиме чтения/записи
SPI_cursor_open — установить курсор, используя оператор, созданный с помощью функции SPI_prepare
SPI_cursor_open_with_args — установить курсор, используя запрос и параметры
SPI_cursor_open_with_paramlist — установить курсор, используя параметры
SPI_cursor_find — найти существующий курсор по имени
SPI_cursor_fetch — извлечь несколько строк через курсор
SPI_cursor_move — переместить курсор
SPI_scroll_cursor_fetch — извлечь несколько строк через курсор
SPI_scroll_cursor_move — переместить курсор
SPI_cursor_close — закрыть курсор
SPI_keepplan — сохранить подготовленный оператор
SPI_saveplan — сохранить подготовленный оператор
SPI_register_relation — сделать эфемерное именованное отношение доступным по имени в запросах SPI
SPI_unregister_relation — удалить эфемерное именованное отношение из реестра
SPI_register_trigger_data — сделать эфемерные данные триггера доступными в запросах SPI

SPI_connect

SPI_connect, SPI_connect_ext — подключить функцию на C/RUST к менеджеру SPI

Синтаксис

int SPI_connect(void)

int SPI_connect_ext(int options)

Описание

SPI_connect устанавливает подключение вызова функции на C/RUST к менеджеру SPI. Эту функцию следует вызывать, если вы хотите выполнять команды через SPI. Некоторые служебные SPI-функции можно вызывать из неподключенных функций на C/RUST.

SPI_connect_ext делает то же самое, но принимает аргумент, позволяющий передавать дополнительные флаги. В настоящее время доступны следующие дополнительные значения:

SPI_OPT_NONATOMIC

Устанавливает подключение SPI в неатомарном режиме, то есть разрешает использование вызовов SPI_commit, SPI_rollback и SPI_start_transaction для управления транзакцией. В противном случае вызов данных функций приведет к немедленной ошибке.

Функция SPI_connect() равнозначна функции SPI_connect_ext(0).

Возвращаемое значение

SPI_OK_CONNECT

при успешном выполнении

SPI_ERROR_CONNECT

при ошибке

SPI_finish

SPI_finish — выключить функцию на C/RUST от менеджера SPI

Синтаксис

int SPI_finish (void)

Описание

SPI_finish закрывает существующее соединение с менеджером SPI. Эту функцию необходимо вызвать после завершения всех операций SPI, требуемых во время текущего вызова функции на C/RUST. Однако при прерывании транзакции с помощью elog(ERROR) о закрытии соединения можно не беспокоиться. В этом случае SPI очистится автоматически.

Возвращаемое значение

SPI_OK_FINISH

при корректном отключении

SPI_ERROR_UNCONNECTED

при вызове из неподключенной функции на C/RUST

SPI_execute

SPI_execute — выполнить команду

Синтаксис

int SPI_execute(const char * command, bool read_only, long count)

Описание

SPI_execute выполняет указанную команду SQL для получения строк в количестве, ограниченном значением count. Если параметр read_only имеет значение true, команда должна только читать данные, тем самым несколько снижая издержки на ее выполнение.

Эта функция может быть вызвана только из подключенной функции на C/RUST.

Если count равно нулю, то команда выполняется для всех строк, удовлетворяющих условиями запроса. Если count больше нуля, то будет получено не больше, чем count строк; выполнение остановится при достижении этого числа, почти так же, как при добавлении к запросу предложения LIMIT. Например,

SPI_execute("SELECT * FROM foo", true, 5);

получит из таблицы не более 5 строк. Обратите внимание, что такой предел действует, только когда команда действительно возвращает строки. Например,

SPI_execute("INSERT INTO foo SELECT * FROM bar", false, 5);

вставляет все строки из bar, игнорируя параметр count. Однако запрос

SPI_execute("INSERT INTO foo SELECT * FROM bar RETURNING *", false, 5);

вставит не более 5 строк, поскольку выполнение будет остановлено после получения пятой строки, извлеченной RETURNING.

Возможна передача несколько команд в одной строке; SPI_execute возвращает результат для команды, выполненной последней. Ограничение количества строк count применяется к каждой команде в отдельности (даже если на самом деле будет возвращен только последний результат). Это ограничение не применяется к скрытым командам, сгенерированным правилами.

Когда параметр read_only имеет значение false, SPI_execute увеличивает счетчик команд и вычисляет новый снимок перед выполнением каждой команды в строке. Снимок фактически не изменяется, если текущий уровень изоляции транзакции SERIALIZABLE или REPEATABLE READ, но в режиме READ COMMITTED обновление снимка позволяет каждой команде видеть результаты только что зафиксированных транзакций из других сеансов. Это важно для согласованного поведения, когда команды изменяют базу данных.

Когда параметр read_only имеет значение true, SPI_execute не обновляет ни снимок, ни счетчик команд, и допускает в строке команды только простые команды SELECT. Эти команды выполняются с использованием снимка, предварительно установленного для окружающего запроса. Этот режим выполнения несколько быстрее, чем режим чтения/записи, из-за устранения издержек на каждую команду. Это также позволяет создавать действительно стабильные функции: поскольку все последующие выполнения будут использовать один и тот же снимок, в результатах не будет никаких изменений.

Обычно неразумно смешивать команды только для чтения и чтения/записи в одной функции с использованием SPI; это может привести к очень запутанному поведению, поскольку запросы только для чтения не будут видеть результаты каких-либо обновлений базы данных, выполненных запросами чтения/записи.

Фактическое количество строк, для которых была выполнена (последняя) команда, возвращается в глобальной переменной SPI_processed. Если эта функция возвращает значение SPI_OK_SELECT, SPI_OK_INSERT_RETURNING, SPI_OK_DELETE_RETURNING или SPI_OK_UPDATE_RETURNING, то для доступа к строкам результата можно воспользоваться глобальным указателем SPITupleTable *SPI_tuptable. Некоторые служебные команды (такие как EXPLAIN) также возвращают наборы строк, и SPI_tuptable будет содержать их результат и в этих случаях. Некоторые служебные команды (COPY, CREATE TABLE AS) не возвращают набор строк, поэтому SPI_tuptable имеет значение NULL, но они по-прежнему возвращают количество обработанных строк в SPI_processed.

Структура SPITupleTable определяется таким образом:

typedef struct
{
    MemoryContext tuptabcxt;    /* контекст таблицы результатов в памяти */
    uint64      alloced;        /* количество зарезервированных в памяти значений */
    uint64      free;           /* количество свободных значений */
    TupleDesc   tupdesc;        /* дескриптор строки */
    HeapTuple  *vals;           /* строки */
} SPITupleTable;

vals — это массив указателей на строки. (Количество допустимых записей задается SPI_processed.) tupdesc — это дескриптор строки, который можно передать функциям SPI, работающим со строками. tuptabcxt, alloced и free являются внутренними полями, не предназначенными для использования процедурами, вызывающими SPI.

SPI_finish освобождает все SPITupleTable, выделенные для текущей функции на C/RUST. Вы можете досрочно освободить конкретную результирующую таблицу, если закончили с ней работать, вызвав SPI_freetuptable.

Аргументы

const char * command

строка, содержащая команду для выполнения

bool read_only

true для режима выполнения «только чтение»

long count

максимальное количество строк, которые нужно вернуть, или 0, если ограничений нет

Возвращаемое значение

Если выполнение команды было успешным, то будет возвращено одно из следующих (неотрицательных) значений:

SPI_OK_SELECT

если была выполнена команда SELECT (но не SELECT INTO)

SPI_OK_SELINTO

если была выполнена команда SELECT INTO

SPI_OK_INSERT

если была выполнена команда INSERT

SPI_OK_DELETE

если была выполнена команда DELETE

SPI_OK_UPDATE

если была выполнена команда UPDATE

SPI_OK_INSERT_RETURNING

если была выполнена команда INSERT RETURNING

SPI_OK_DELETE_RETURNING

если была выполнена команда DELETE RETURNING

SPI_OK_UPDATE_RETURNING

если была выполнена команда UPDATE RETURNING

SPI_OK_UTILITY

если была выполнена служебная команда (например, CREATE TABLE)

SPI_OK_REWRITTEN

если команда была преобразована правилом в команду другого вида (например, UPDATE стала INSERT).

При ошибке возвращается одно из следующих отрицательных значений:

SPI_ERROR_ARGUMENT

если command равен NULL или count меньше 0

SPI_ERROR_COPY

при попытке выполнить COPY TO stdout или COPY FROM stdin

SPI_ERROR_TRANSACTION

при попытке выполнить команду управления транзакцией ( BEGIN, COMMIT, ROLLBACK, SAVEPOINT, PREPARE TRANSACTION, COMMIT PREPARED, ROLLBACK PREPARED или любой их вариант)

SPI_ERROR_OPUNKNOWN

если тип команды неизвестен (такого происходить не должно)

SPI_ERROR_UNCONNECTED

при вызове из неподключенной функции на C/RUST

Примечания

Все функции SPI, выполняющие запросы, устанавливают значения как SPI_processed, так и SPI_tuptable (только указатель, а не содержимое структуры). Сохраните значения этих двух глобальных переменных в локальные переменные функции на C/RUST, если вам нужно получить доступ к таблице результатов выполнения SPI_execute или другой функции, выполняющие запросы, при последующих вызовах.

SPI_exec

SPI_exec — выполнить команду чтения/записи

Синтаксис

int SPI_exec(const char * command, long count)

Описание

SPI_exec работает так же, как SPI_execute, но параметр read_only всегда принимается как false.

Аргументы

const char * command

строка, содержащая запрос для выполнения

long count

максимальное количество строк, которые нужно вернуть, или 0, если нет ограничений

Возвращаемое значение

См. описание функции SPI_execute.

SPI_execute_with_args

SPI_execute_with_args — выполнить команду с внешними параметрами

Синтаксис

int SPI_execute_with_args(const char *command,
                          int nargs, Oid *argtypes,
                          Datum *values, const char *nulls,
                          bool read_only, long count)

Описание

SPI_execute_with_args выполняет команду, которая может включать ссылки на параметры, передаваемые извне. В тексте команды параметры обозначаются символами $n, а в вызове указываются типы данных и значения для каждого такого символа. Параметры read_only и count имеют ту же интерпретацию, что и в SPI_execute.

Основное преимущество этой функции по сравнению с SPI_execute состоит в том, что значения данных можно вставить в команду без громоздких кавычек/экранирования и таким образом сильно снизить риск атак с SQL-инъекцией.

Аналогичных результатов можно достичь, вызвав SPI_prepare, а затем SPI_execute_plan; однако при использовании этой функции план запроса всегда настраивается на предоставленные конкретные значения параметров. Для одноразового выполнения запроса SPI_execute_with_args предпочтительней. Если ту же команду нужно выполнить со многими различными параметрами, любой из этих методов может оказаться быстрее, в зависимости от стоимости перепланирования по сравнению с преимуществами специализированных планов.

Аргументы

const char * command

строка с командой

int nargs

количество входных параметров ($1, $2 и т. д.)

Oid * argtypes

массив длиной nargs, содержащий OID типов данных этих параметров

Datum * values

массив длиной nargs, содержащий фактические значения параметров

const char * nulls

массив длиной nargs, описывающий, какие параметры равны NULL

Если параметр nulls равен NULL, то SPI_execute_with_args предполагает, что ни один из параметров не содержит NULL. В противном случае каждая запись массива nulls должна содержать ' ', если значение соответствующего параметра не NULL, или 'n', если оно равно NULL. (В последнем случае фактическое значение соответствующей записи в параметре values не имеет значения.) Обратите внимание, что nulls — это не текстовая строка, а массив, поэтому завершающий символ '\0' не нужен.

bool read_only

true для выполнения запросов в режиме «только чтение»

long count

максимальное количество строк, которое должно быть возвращено, или 0, если нет ограничений

Возвращаемое значение

Возвращаемое значение такое же, как и для SPI_execute.

При успешном вызове переменные SPI_processed и SPI_tuptable устанавливаются так же, как в SPI_execute.

SPI_prepare

SPI_prepare — подготовить оператор, пока не выполняя его

Синтаксис

SPIPlanPtr SPI_prepare(const char * command, int nargs, Oid * argtypes)

Описание

SPI_prepare создает и возвращает подготовленный оператор для указанной команды, но не выполняет ее. Впоследствии подготовленный оператор может быть выполнен многократно с использованием SPI_execute_plan.

Когда одна и та же или схожая команда должна выполняться многократно, обычно выгодно провести синтаксический анализ только один раз и еще более выгодным может быть повторное использование плана выполнения команды. SPI_prepare преобразует строку команды в подготовленный оператор, который инкапсулирует результаты синтаксического анализа. Подготовленный оператор также предоставляет место для кэширования плана выполнения, если выясняется, что создание специализированного плана для каждого выполнения бесполезно.

Подготовленную команду можно обобщить, записав параметры ($1, $2 и т. д.) вместо значений, задаваемых константами в обычной команде. Тогда фактические значения параметров будут указываться при вызове SPI_execute_plan. Это позволяет использовать подготовленную команду в более широком диапазоне ситуаций, чем это было бы возможно без параметров.

Оператор, возвращаемый SPI_prepare, может использоваться только в текущем вызове функции на C/RUST, поскольку SPI_finish освобождает память, выделенную для такого оператора. Но этот оператор можно сохранить на потом, используя функции SPI_keepplan или SPI_saveplan.

Аргументы

const char * command

строка команды

int nargs

количество входных параметров ($1, $2 и т. д.)

Oid * argtypes

указатель на массив, содержащий OID типов данных параметров

Возвращаемое значение

SPI_prepare возвращает ненулевой указатель на SPIPlan, который является скрытой структурой, представляющей подготовленный оператор. В случае ошибки будет возвращен NULL, а переменная SPI_result будет содержать один из тех же кодов ошибок, которые используются SPI_execute, за исключением того, что код SPI_ERROR_ARGUMENT устанавливается, если command равен NULL, или nargs меньше 0, или nargs больше 0, а argtypes равен NULL.

Примечания

Если параметры не определены, общий план будет создан при первом использовании SPI_execute_plan, и он же будет использоваться для всех последующих выполнений. Если есть параметры, первые несколько использований SPI_execute_plan создадут специализированные планы, которые будут специфическими для предоставленных значений параметров. После достаточного использования одного и того же подготовленного оператора SPI_execute_plan построит общий план, и, если тот не намного дороже, чем специализированные планы, начнет использовать его, вместо того чтобы каждый раз создавать новый. Если это поведение по умолчанию не подходит, его можно изменить, передав флаг CURSOR_OPT_GENERIC_PLAN или CURSOR_OPT_CUSTOM_PLAN в SPI_prepare_cursor, чтобы принудительно использовать общие или специализированные планы соответственно.

Хотя основной смысл подготовленного оператора заключается в том, чтобы избежать повторного анализа и планирования оператора, QHB будет принудительно повторять анализ и планирование оператора перед его использованием в случаях, если с момента предыдущего использования подготовленного оператора изменяются определения (DDL) объектов базы данных, используемых в этом операторе. Кроме того, если значение параметра search_path изменяется от одного использования к следующему, оператор будет повторно проанализирован с использованием нового значения search_path. Дополнительную информацию о поведении подготовленных операторов см. в описании команды PREPARE.

Эта функция должна вызываться только из подключенной функции на C/RUST.

SPIPlanPtr объявлен как указатель на скрытый тип структуры в spi.h. Неразумно пытаться получить доступ к его содержимому напрямую, так как это повышает вероятность того, что ваш код сломается в будущих версиях QHB.

Имя SPIPlanPtr в некотором смысле сохранено по историческим причинам, поскольку теперь структуре данных не обязательно содержать план выполнения.

SPI_prepare_cursor

SPI_prepare_cursor — подготовить оператор, пока не выполняя его

Синтаксис

SPIPlanPtr SPI_prepare_cursor(const char * command, int nargs,
                              Oid * argtypes, int cursorOptions)

Описание

Функция SPI_prepare_cursor идентична SPI_prepare, но позволяет дополнительно передать планировщику «параметры курсора». Это битовая маска, имеющая значения, перечисленные в nodes/parsenodes.h для поля options структуры DeclareCursorStmt. SPI_prepare всегда принимает параметры курсора равными нулю.

Аргументы

const char * command

строка команды

int nargs

количество входных параметров ($1, $2 и т. д.)

Oid * argtypes

указатель на массив, содержащий OID типов данных параметров

int cursorOptions

целочисленная битовая маска параметров курсора; ноль — поведение по умолчанию

Возвращаемое значение

SPI_prepare_cursor имеет те же соглашения о возврате, что и SPI_prepare.

Примечания

Полезные биты для установки в cursorOptions включают:

CURSOR_OPT_SCROLL;

CURSOR_OPT_NO_SCROLL;

CURSOR_OPT_FAST_PLAN;

CURSOR_OPT_GENERIC_PLAN;

CURSOR_OPT_CUSTOM_PLAN.

В частности, следует отметить, что CURSOR_OPT_HOLD игнорируется.

SPI_prepare_params

SPI_prepare_params — подготовить оператор, пока не выполняя его

Синтаксис

SPIPlanPtr SPI_prepare_params(const char * command,
                              ParserSetupHook parserSetup,
                              void * parserSetupArg,
                              int cursorOptions)

Описание

SPI_prepare_params создает и возвращает подготовленный оператор для указанной команды, но не выполняет его. Эта функция равнозначна SPI_prepare_cursor, но дополнительно позволяет вызывающему ее задать функции обработки синтаксического анализа для управления разбором ссылок на внешние параметры.

Аргументы

const char * command

строка команды

ParserSetupHook parserSetup

функция настройки обработчиков синтаксического анализа

void * parserSetupArg

сквозной аргумент для parserSetup

int cursorOptions

целочисленная битовая маска параметров курсора; ноль — поведение по умолчанию

Возвращаемое значение

SPI_prepare_params имеет те же соглашения о возврате, что и SPI_prepare.

SPI_getargcount

SPI_getargcount — вернуть количество аргументов, необходимых оператору, подготовленному функцией SPI_prepare

Синтаксис

int SPI_getargcount(SPIPlanPtr plan)

Описание

SPI_getargcount возвращает количество аргументов, необходимых для выполнения оператора, подготовленного функцией SPI_prepare.

Аргументы

SPIPlanPtr plan

подготовленный оператор (возвращаемый функцией SPI_prepare)

Возвращаемое значение

Количество ожидаемых аргументов для параметра plan. Если plan равен NULL или недействителен, в SPI_result устанавливается код SPI_ERROR_ARGUMENT, и функция возвращает -1.

SPI_getargtypeid

SPI_getargtypeid — вернуть OID типа данных для аргумента оператора, подготовленного функцией SPI_prepare

Синтаксис

Oid SPI_getargtypeid(SPIPlanPtr plan, int argIndex)

Описание

SPI_getargtypeid возвращает OID, представляющий типа аргумента с индексом argIndex для оператора, подготовленного функцией SPI_prepare. Нумерация аргументов начинается с нуля.

Аргументы

SPIPlanPtr plan

подготовленный оператор (возвращаемый функцией SPI_prepare)

int argIndex

индекс аргумента, начиная с нуля

Возвращаемое значение

OID типа аргумента с заданным индексом. Если значение параметра plan неверное или равно NULL, либо значение argIndex меньше 0 или больше или равно количеству аргументов, объявленных для параметра plan, в SPI_result устанавливается код SPI_ERROR_ARGUMENT и возвращается InvalidOid.

SPI_is_cursor_plan

SPI_is_cursor_plan — вернуть true, если оператор, подготовленный функцией SPI_prepare, можно использовать с функцией SPI_cursor_open

Синтаксис

bool SPI_is_cursor_plan(SPIPlanPtr plan)

Описание

SPI_is_cursor_plan возвращает true, если оператор, подготовленный функцией SPI_prepare, можно передать в качестве аргумента функции SPI_cursor_open, либо false, если это не так. Для true в параметре plan должна быть представлена только одна команда, и эта команда должна возвращать кортежи вызывающей стороне. Например, команда SELECT подходит, если не содержит предложение INTO, а команда UPDATE — только если содержит предложение RETURNING.

Аргументы

SPIPlanPtr plan

подготовленный оператор (возвращаемый функцией SPI_prepare)

Возвращаемое значение

Значение true или false, указывающее, может ли plan создать курсор, при SPI_result равном нулю. Если определить ответ невозможно (например, если значение plan неверное или NULL, или при вызове без подключения к SPI), тогда в SPI_result устанавливается подходящий код ошибки и возвращается значение false.

SPI_execute_plan

SPI_execute_plan — выполнить оператор, подготовленный SPI_prepare

Синтаксис

int SPI_execute_plan(SPIPlanPtr plan, Datum * values, const char * nulls,
                     bool read_only, long count)

Описание

SPI_execute_plan выполняет оператор, подготовленный функцией SPI_prepare или родственными ей. Параметры read_only и count имеют то же назначение, что и в SPI_execute.

Аргументы

SPIPlanPtr plan

подготовленный оператор (возвращаемый функцией SPI_prepare)

Datum * values

Массив фактических значений параметров. Должен иметь ту же длину, что и количество аргументов оператора.

const char * nulls

Массив, описывающий, какие параметры содержат NULL. Должен иметь ту же длину, что и количество аргументов оператора.

Если параметр nulls равен NULL, то SPI_execute_plan предполагает, что ни один из параметров не равен NULL. В противном случае каждая запись массива nulls должна содержать ' ', если значение соответствующего параметра не NULL, или 'n', если оно равно NULL. (В последнем случае фактическое значение в соответствующем элементе values не имеет значения.) Обратите внимание, что nulls — это не текстовая строка, а массив, поэтому завершающий символ '\0' не нужен.

bool read_only

true для выполнения запроса в режиме «только чтение»

long count

максимальное количество строк, которые нужно вернуть, или 0, если нет ограничений

Возвращаемое значение

Возвращаемое значение такое же, как и для SPI_execute, со следующими дополнительными вариантами ошибочных (отрицательных) результатов:

SPI_ERROR_ARGUMENT

если параметр plan неверен или равен NULL, либо если параметр count меньше 0

SPI_ERROR_PARAM

если параметр values равен NULL, и plan подготовлен с другими параметрами

В случае успешного вызова переменные SPI_processed и SPI_tuptable устанавливаются как в SPI_execute.

SPI_execute_plan_with_paramlist

SPI_execute_plan_with_paramlist - выполнить оператор, подготовленный функцией SPI_prepare

Синтаксис

int SPI_execute_plan_with_paramlist(SPIPlanPtr plan,
                                    ParamListInfo params,
                                    bool read_only,
                                    long count)

Описание

SPI_execute_plan_with_paramlist выполняет оператор, подготовленный функцией SPI_prepare. Эта функция равнозначна SPI_execute_plan за исключением того, что информация о значениях параметров, передаваемых в запрос, представляется по-другому. Представление ParamListInfo может быть удобно для передачи значений, которые уже доступны в этом формате. Функция также поддерживает использование динамических наборов параметров посредством функций-обработчиков, указанных в ParamListInfo.

Аргументы

SPIPlanPtr plan

подготовленный оператор (возвращаемый функцией SPI_prepare)

ParamListInfo params

структура данных, содержащая типы и значения параметров; NULL, если параметров нет

bool read_only

true для выполнения запроса в режиме «только чтение»

long count

максимальное количество строк, которые нужно вернуть, или 0, если нет ограничений

Возвращаемое значение

Возвращаемое значение такое же, как у функции SPI_execute_plan.

В случае успешного вызова переменные SPI_processed и SPI_tuptable устанавливаются как при вызове функции SPI_execute_plan.

SPI_execp

SPI_execp — выполнить оператор в режиме чтения/записи

Синтаксис

int SPI_execp(SPIPlanPtr plan, Datum * values, const char * nulls, long count)

Описание

SPI_execp сходна с SPI_execute_plan, но параметр read_only всегда имеет значение false.

Аргументы

SPIPlanPtr plan

подготовленный оператор (возвращаемый функцией SPI_prepare)

Datum * values

Массив фактических значений параметров. Должен иметь ту же длину, что и число аргументов оператора.

const char * nulls

Массив, описывающий, какие параметры равны NULL. Должен иметь ту же длину, что и число аргументов оператора.

Если параметр nulls равен NULL, то SPI_execp предполагает, что ни один из параметров не содержит NULL. В противном случае каждая запись массива nulls должна содержать ' ', если значение соответствующего параметра не NULL, или 'n', если оно равно NULL. (В последнем случае фактическое значение в соответствующем элементе values не имеет значения.) Обратите внимание, что nulls — это не текстовая строка, а массив, поэтому завершающий символ '\0' не нужен.

long count

максимальное количество строк, которые нужно вернуть, или 0, если нет ограничений

Возвращаемое значение

Возвращаемое значение такое же, как у функции SPI_execute_plan.

В случае успешного вызова переменные SPI_processed и SPI_tuptable устанавливаются как при вызове функции SPI_execute_plan.

SPI_cursor_open

SPI_cursor_open — открыть курсор для оператора, созданного функцией SPI_prepare

Синтаксис

Portal SPI_cursor_open(const char * name, SPIPlanPtr plan,
                       Datum * values, const char * nulls,
                       bool read_only)

Описание

SPI_cursor_open открывает курсор (называемый внутри базы данных порталом), который будет выполнять оператор, подготовленный функцией SPI_prepare. Параметры этой функции имеют тот же смысл, что и соответствующие параметры SPI_execute_plan.

Использование курсора вместо непосредственного выполнения оператора имеет два преимущества. Во-первых, результирующие строки могут быть небольшими порциями, что позволяет избежать переполнения памяти при выполнении запросов, которые возвращают много строк. Во-вторых, портал может пережить текущую функцию C/RUST (фактически он может дожить до конца текущей транзакции). Возврат имени портала в код, вызывающий функцию на C/RUST, дает возможность выдавать результат в виде набора строк.

Данные переданных параметров будут скопированы в портал курсора, поэтому их можно освободить даже во время существования курсора.

Аргументы

const char * name

имя для портала или NULL, чтобы система сама выбрала имя

SPIPlanPtr plan

подготовленный оператор (возвращаемый функцией SPI_prepare)

Datum * values

Массив фактических значений параметров. Должен иметь ту же длину, что и число аргументов оператора.

const char * nulls

Массив, описывающий, какие параметры равны NULL. Должен иметь ту же длину, что и число аргументов оператора.

Если параметр nulls равен NULL, то SPI_cursor_open предполагает, что ни один из параметров не содержит NULL. В противном случае каждый элемент массива nulls должен содержать ' ', если значение соответствующего параметра не NULL, или 'n', если оно равно NULL. (В последнем случае фактическое значение в соответствующем элементе values не имеет значения.) Обратите внимание, что nulls — это не текстовая строка, а массив, поэтому завершающий символ '\0' не нужен.

bool read_only

true для выполнения запроса в режиме «только чтение»

Возвращаемое значение

Указатель на портал, содержащий курсор. Обратите внимание, что нет соглашения о возврате ошибок; все ошибки будут выдаваться через elog.

SPI_cursor_open_with_args

SPI_cursor_open_with_args — открыть курсор, используя запрос и параметры

Синтаксис

Portal SPI_cursor_open_with_args(const char *name,
                                 const char *command,
                                 int nargs, Oid *argtypes,
                                 Datum *values, const char *nulls,
                                 bool read_only, int cursorOptions)

Описание

SPI_cursor_open_with_args открывает курсор (называемый внутри базы данных порталом), который будет выполнять указанный запрос. Большинство параметров имеют те же значения, что и соответствующие параметры для SPI_prepare_cursor и SPI_cursor_open.

Для одноразового выполнения запроса эту функцию следует предпочесть последовательным вызовам SPI_prepare_cursor и SPI_cursor_open. Если одну и ту же команду нужно выполнить со множеством различных параметров, любой из этих двух способов может быть быстрее, в зависимости от стоимости перепланирования по сравнению с преимуществами специализированных планов.

Данные переданных параметров будут скопированы в портал курсора, поэтому их можно освободить даже во время существования курсора.

Аргументы

const char * name

имя для портала или NULL, чтобы система сама выбрала имя

const char * command

строка команды

int nargs

количество входных параметров ($1, $2 и т. д.)

Oid * argtypes

массив длиной nargs, содержащий OID типов данных параметров

Datum * values

массив длиной nargs, содержащий фактические значения параметров

const char * nulls

Массив длиной nargs, описывающий, какие параметры равны NULL.

Если параметр nulls равен NULL, то SPI_cursor_open_with_args предполагает, что ни один из параметров не содержит NULL. В противном случае каждый элемент массива nulls должен содержать ' ', если значение соответствующего параметра не NULL, или 'n', если оно равно NULL. (В последнем случае фактическое значение в соответствующем элементе values не имеет значения.) Обратите внимание, что nulls — это не текстовая строка, а массив, поэтому завершающий символ '\0' не нужен.

bool read_only

true для выполнения запроса в режиме «только чтение»

int cursorOptions

целочисленная битовая маска параметров курсора; ноль — поведение по умолчанию

Возвращаемое значение

Указатель на портал, содержащий курсор. Обратите внимание, что нет соглашения о возврате ошибок; все ошибки будут выдаваться через elog.

SPI_cursor_open_with_paramlist

SPI_cursor_open_with_paramlist — открыть курсор, используя параметры

Синтаксис

Portal SPI_cursor_open_with_paramlist(const char *name,
                                      SPIPlanPtr plan,
                                      ParamListInfo params,
                                      bool read_only)

Описание

SPI_cursor_open_with_paramlist открывает курсор (называемый внутри базы данных порталом), который будет выполнять оператор, подготовленный функцией SPI_prepare. Эта функция равнозначна SPI_cursor_open за исключением того, что информация о значениях параметров, передаваемых в запрос, представлена по-другому. Представление ParamListInfo может быть удобным для передачи значений, которые уже доступны в этом формате. Функция также поддерживает динамическую подстановку параметров через функции-обработчики, указанные в ParamListInfo.

Содержимое переданных параметров будет скопировано в портал курсора, поэтому они могут быть освобождены еще во время существования курсора.

Аргументы

const char * name

имя для портала или NULL, чтобы система сама выбрала имя

SPIPlanPtr plan

подготовленный оператор (возвращаемый функцией SPI_prepare)

ParamListInfo params

структура данных, содержащая типы и значения параметров; NULL, если параметров нет

bool read_only

true для выполнения запроса в режиме «только чтение»

Возвращаемое значение

Указатель на портал, содержащий курсор. Обратите внимание, что нет соглашения о возврате ошибок; все ошибки будут выдаваться через elog.

SPI_cursor_find

SPI_cursor_find — найти существующий курсор по имени

Синтаксис

Portal SPI_cursor_find(const char * name)

Описание

SPI_cursor_find находит существующий портал по имени. Это в первую очередь полезно для разрешения имени курсора, возвращенного в виде текста какой-то другой функцией.

Аргументы

const char * name

имя портала

Возвращаемое значение

Указатель на портал с заданным именем или NULL, если такой портал не найден.

SPI_cursor_fetch

SPI_cursor_fetch — извлечь несколько строк через курсор

Синтаксис

void SPI_cursor_fetch(Portal portal, bool forward, long count)

Описание

SPI_cursor_fetch извлекает несколько строк через курсор. Эта функция по действию аналогична подмножеству возможностей SQL-команды FETCH (расширенную функциональность предоставляет функция SPI_scroll_cursor_fetch).

Аргументы

Portal portal

портал, содержащий курсор

bool forward

true для извлечения с перемещением вперед, false — в обратном направлении

long count

максимальное количество строк для извлечения

Возвращаемое значение

В случае успешного вызова переменные SPI_processed и SPI_tuptable устанавливаются как в SPI_execute.

Примечания

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

SPI_cursor_move

SPI_cursor_move — переместить курсор

Синтаксис

void SPI_cursor_move(Portal portal, bool forward, long count)

Описание

SPI_cursor_move пропускает некоторое количество строк в курсоре. Эта функция по действию аналогична подмножеству возможностей SQL-команды MOVE (расширенную функциональность предоставляет функция SPI_scroll_cursor_move).

Аргументы

Portal portal

портал, содержащий курсор

bool forward

true для перемещения вперед, false — в обратном направлении

long count

максимальное количество строк для перемещения

Примечания

Перемещение в обратном направлении может завершиться ошибкой, если план курсора был создан без параметра CURSOR_OPT_SCROLL.

SPI_scroll_cursor_fetch

SPI_scroll_cursor_fetch — извлечь несколько строк через курсор

Синтаксис

void SPI_scroll_cursor_fetch(Portal portal, FetchDirection direction,
                             long count)

Описание

SPI_scroll_cursor_fetch извлекает несколько строк через курсора. Эта функция действует аналогично SQL-команде FETCH.

Аргументы

Portal portal

портал, содержащий курсор

FetchDirection direction

один из вариантов: FETCH_FORWARD, FETCH_BACKWARD, FETCH_ABSOLUTE или FETCH_RELATIVE

long count

количество строк для извлечения в направлении FETCH_FORWARD или FETCH_BACKWARD; абсолютный номер строки для извлечения с вариантом FETCH_ABSOLUTE; относительный номер строки для извлечения с вариантом FETCH_RELATIVE

Возвращаемое значение

В случае успешного вызова переменные SPI_processed и SPI_tuptable устанавливаются как в SPI_execute.

Примечания

Подробную интерпретацию параметров direction и count см. в описании SQL-команды FETCH.

При значении параметра direction (направление извлечения), отличном от FETCH_FORWARD, функция может завершиться ошибкой, если план курсора был создан без параметра CURSOR_OPT_SCROLL.

SPI_scroll_cursor_move

SPI_scroll_cursor_move — переместить курсор

Синтаксис

void SPI_scroll_cursor_move(Portal portal, FetchDirection direction,
                            long count)

Описание

SPI_scroll_cursor_move перемещает курсор на некоторое количество строк. Эта функция действует аналогично SQL-команде MOVE.

Аргументы

Portal portal

портал, содержащий курсор

FetchDirection direction

один из вариантов: FETCH_FORWARD, FETCH_BACKWARD, FETCH_ABSOLUTE или FETCH_RELATIVE

long count

количество строк, на которое перемещения курсор в направлении FETCH_FORWARD или FETCH_BACKWARD; абсолютный номер строки, к которой перемещается курсор, с вариантом FETCH_ABSOLUTE; относительный номер строки, к которой перемещается курсор, с вариантом FETCH_RELATIVE

Возвращаемое значение

В случае успешного вызова переменная SPI_processed устанавливается как в SPI_execute. Переменная SPI_tuptable всегда равна NULL, поскольку эта функция не возвращает строк.

Примечания

Подробную интерпретацию параметров direction и count см. в описании SQL-команды FETCH.

При значении параметра direction (направление перемещения курсора), отличном от FETCH_FORWARD, функция может завершиться ошибкой, если план курсора был создан без параметра CURSOR_OPT_SCROLL.

SPI_cursor_close

SPI_cursor_close — закрыть курсор

Синтаксис

void SPI_cursor_close(Portal portal)

Описание

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

Все открытые курсоры автоматически закрываются в конце транзакции. SPI_cursor_close нужно вызывать только в случае, если есть желание освободить ресурсы раньше.

Аргументы

Portal portal

портал, содержащий курсор

SPI_keepplan

SPI_keepplan — сохранить подготовленный оператор

Синтаксис

int SPI_keepplan(SPIPlanPtr plan)

Описание

SPI_keepplan сохраняет переданный оператор (подготовленный функцией SPI_prepare), чтобы его не ликвидировали ни функция SPI_finish, ни менеджер транзакций. Это дает возможность повторно использовать подготовленные операторы в последующих вызовах функции на C/RUST в текущем сеансе.

Аргументы

SPIPlanPtr plan

подготовленные оператор, подлежащий сохранению

Возвращаемое значение

0 в случае успеха; SPI_ERROR_ARGUMENT, если значение параметра plan неверное или равно NULL.

Примечания

Переданный оператор перемещается в постоянное хранилище посредством корректировки указателя (копирование данных не требуется). Если позже понадобится его удалить, воспользуйтесь функцией SPI_freeplan.

SPI_saveplan

SPI_saveplan — сохранить подготовленный оператор

Синтаксис

SPIPlanPtr SPI_saveplan(SPIPlanPtr plan)

Описание

SPI_saveplan копирует переданный оператор (подготовленный функцией SPI_prepare) в память, которая не будет освобождена ни SPI_finish, ни менеджером транзакций, и возвращает указатель на скопированный оператор. Это дает возможность повторно использовать подготовленные операторы при последующих вызовах функции на C/RUST в текущем сеансе.

Аргументы

SPIPlanPtr plan

подготовленный оператор, подлежащий сохранению

Возвращаемое значение

Указатель на скопированный оператор или NULL в случае ошибки. При ошибке SPI_result принимает одно из следующих значений:

SPI_ERROR_ARGUMENT

если значение параметра plan неверное или равно NULL

SPI_ERROR_UNCONNECTED

при вызове из неподключенной функции на C/RUST

Примечания

Первоначально переданный оператор не освобождается, поэтому при желании его можно освободить вызовом функции SPI_freeplan, чтобы избежать утечки памяти до выполнения SPI_finish.

В большинстве случаев SPI_keepplan предпочтительнее этой функции, поскольку она дает примерно такой же результат без необходимости физического копирования структур данных подготовленного оператора.

SPI_register_relation

SPI_register_relation — сделать эфемерное именованное отношение доступным по имени в запросах SPI

Синтаксис

int SPI_register_relation(EphemeralNamedRelation enr)

Описание

SPI_register_relation делает эфемерное именованное отношение, вместе со связанной информацией, доступным для запросов, планируемых и выполняемых через текущее подключение SPI.

Аргументы

EphemeralNamedRelation enr

запись эфемерного именованного отношения в реестре

Возвращаемое значение

Если выполнение команды было успешным, то будет возвращено следующее (неотрицательное) значение:

SPI_OK_REL_REGISTER

если отношение было успешно зарегистрировано по имени

При ошибке возвращается одно из следующих отрицательных значений:

SPI_ERROR_ARGUMENT

если значение параметра enr или его поля name равно NULL

SPI_ERROR_UNCONNECTED

при вызове из неподключенной функции на C/RUST

SPI_ERROR_REL_DUPLICATE

если имя, указанное в поле name параметра enr, уже зарегистрировано для этого подключения

SPI_unregister_relation

SPI_unregister_relation — удалить эфемерное именованное отношение из реестра

Синтаксис

int SPI_unregister_relation(const char * name)

Описание

SPI_unregister_relation удаляет эфемерное именованное отношение из реестра для текущего подключения.

Аргументы

const char * name

имя записи отношения в реестре

Возвращаемое значение

Если выполнение команды было успешным, то будет возвращено следующее (неотрицательное) значение:

SPI_OK_REL_UNREGISTER

если блок кортежей был успешно удален из реестра

При ошибке возвращается одно из следующих отрицательных значений:

SPI_ERROR_ARGUMENT

если значение параметра name равно NULL

SPI_ERROR_UNCONNECTED

при вызове из неподключенной функции на C/RUST

SPI_ERROR_REL_NOT_FOUND

если name не обнаруживается в реестре для текущего подключения

SPI_register_trigger_data

SPI_register_trigger_data — сделать эфемерные данные триггера доступными в запросах SPI

Синтаксис

int SPI_register_trigger_data(TriggerData *tdata)

Описание

SPI_register_trigger_data делает любые эфемерный отношения, захваченные триггером, доступными для запросов, планируемых и выполняемых через текущее подключение SPI. В настоящее время это подразумевает таблицы переходов, захваченные триггером AFTER, определенным с помощью предложения REFERENCING OLD/NEW TABLE AS. Эта функция должна вызываться после подключения функцией-обработчиком триггера на языке программирования.

Аргументы

TriggerData * tdata

объект TriggerData, передаваемый функции-обработчику триггера через fcinfo->context

Возвращаемое значение

Если выполнение команды было успешным, то будет возвращено следующее (неотрицательное) значение:

SPI_OK_TD_REGISTER

если захваченные данные триггера (если таковые имеются) были успешно зарегистрированы

При ошибке возвращается одно из следующих отрицательных значений:

SPI_ERROR_ARGUMENT

если значение параметра tdata равно NULL

SPI_ERROR_UNCONNECTED

при вызове из неподключенной функции на C/RUST

SPI_ERROR_REL_DUPLICATE

если имя любого переходного отношения в данных триггера уже зарегистрировано для этого подключения

Вспомогательные функции интерфейса

SPI_fname — определить имя столбца для указанного номера столбца
SPI_fnumber — определить номер столбца для указанного имени столбца
SPI_getvalue — вернуть строковое значение указанного столбца
SPI_getbinval — вернуть двоичное значение указанного столбца
SPI_gettype — вернуть имя типа данных указанного столбца
SPI_gettypeid — вернуть OID типа данных указанного столбца
SPI_getrelname — вернуть имя указанного отношения
SPI_getnspname — вернуть пространство имен указанного отношения
SPI_result_code_string — вернуть код ошибки в виде строки

Описанные здесь функции предоставляют интерфейс для извлечения информации из наборов результатов, возвращаемых SPI_execute и другими функциями SPI.

Все функции, описанные в этом разделе, могут использоваться и в подключенных, и в неподключенных функциях на C/RUST.

SPI_fname

SPI_fname — определить имя столбца для указанного номера столбца

Синтаксис

char * SPI_fname(TupleDesc rowdesc, int colnumber)

Описание

SPI_fname возвращает копию имени столбца с указанным номером. (Когда эта копия имени будет не нужна, ее можно освободить с помощью pfree.)

Аргументы

TupleDesc rowdesc

описание входной строки

int colnumber

номер столбца (отсчет начинается с 1)

Возвращаемое значение

Название столбца или NULL, если значение параметра colnumber выходит за пределы допустимого диапазона. В случае ошибки в SPI_result устанавливается SPI_ERROR_NOATTRIBUTE.

SPI_fnumber

SPI_fnumber — определить номер столбца для указанного имени столбца

Синтаксис

int SPI_fnumber(TupleDesc rowdesc, const char * colname)

Описание

SPI_fnumber возвращает номер столбца для столбца с указанным именем.

Если параметр colname ссылается на системный столбец (например, ctid), будет возвращен соответствующий отрицательный номер столбца. Вызывающий должен провести тщательную проверку на наличие ошибки, досконально сравнивая возвращаемое значение с SPI_ERROR_NOATTRIBUTE; проверка результата по условию «меньше или равно 0» не будет корректной, если только не нужно исключить системные столбцы.

Аргументы

TupleDesc rowdesc

описание входной строки

const char * colname

имя столбца

Возвращаемое значение

Номер столбца (начиная с 1 для пользовательских столбцов) или SPI_ERROR_NOATTRIBUTE, если столбец с указанным именем не найден.

SPI_getvalue

SPI_getvalue — вернуть строковое значение указанного столбца

Синтаксис

char * SPI_getvalue(HeapTuple row, TupleDesc rowdesc, int colnumber)

Описание

SPI_getvalue возвращает строковое представление значения указанного столбца.

Результат возвращается в памяти, выделенной с помощью palloc. (Когда он станет не нужен, можно будет освободить память с помощью pfree.)

Аргументы

HeapTuple row

входная строка, подлежащая проверке

TupleDesc rowdesc

описание входной строки

int colnumber

номер столбца (отсчет начинается с 1)

Возвращаемое значение

Значение столбца или NULL, если столбец содержит NULL, значение colnumber выходит за пределы допустимого диапазона (в SPI_result устанавливается SPI_ERROR_NOATTRIBUTE) или нет доступной функции выхода (в SPI_result устанавливается SPI_ERROR_NOOUTFUNC).

SPI_getbinval

SPI_getbinval — вернуть двоичное значение указанного столбца

Синтаксис

Datum SPI_getbinval(HeapTuple row, TupleDesc rowdesc, int colnumber,
                    bool * isnull)

Описание

SPI_getbinval возвращает значение указанного столбца во внутренней форме (как тип Datum).

Эта функция не выделяет новое пространство для данных. В случае типа данных, передаваемого по ссылке, возвращаемое значение будет указателем на переданную строку.

Аргументы

HeapTuple row

входная строка, подлежащая изучению

TupleDesc rowdesc

описание входной строки

int colnumber

номер столбца (отсчет начинается с 1)

bool * isnull

флаг для значения NULL в столбце

Возвращаемое значение

Возвращается двоичное значение столбца. Если этот столбец содержит NULL, переменной, на которую указывает параметр isnull, присваивается значение true, иначе — false.

При ошибке в SPI_result устанавливается SPI_ERROR_NOATTRIBUTE.

SPI_gettype

SPI_gettype — вернуть имя типа данных указанного столбца

Синтаксис

char * SPI_gettype(TupleDesc rowdesc, int colnumber)

Описание

SPI_gettype возвращает копию имени типа данных указанного столбца. (Когда копия имени станет не нужна, можно будет освободить ее с помощью pfree.)

Аргументы

TupleDesc rowdesc

описание входной строки

int colnumber

номер столбца (отсчет начинается с 1)

Возвращаемое значение

Имя типа данных указанного столбца или NULL при ошибке. При ошибке в SPI_result устанавливается SPI_ERROR_NOATTRIBUTE.

SPI_gettypeid

SPI_gettypeid — вернуть OID типа данных указанного столбца

Синтаксис

Oid SPI_gettypeid(TupleDesc rowdesc, int colnumber)

Описание

SPI_gettypeid возвращает OID типа данных указанного столбца.

Аргументы

TupleDesc rowdesc

описание входной строки

int colnumber

номер столбца (отсчет начинается с 1)

Возвращаемое значение

OID типа данных указанного столбца или InvalidOid при ошибке. При ошибке в SPI_result устанавливается SPI_ERROR_NOATTRIBUTE.

SPI_getrelname

SPI_getrelname — вернуть имя указанного отношения

Синтаксис

char * SPI_getrelname(Relation rel)

Описание

SPI_getrelname возвращает копию имени указанного отношения. (Когда копия имени станет не нужна, можно будет освободить ее с помощью pfree.)

Аргументы

Relation rel

входное отношение

Возвращаемое значение

Имя указанного отношения.

SPI_getnspname

SPI_getnspname — вернуть пространство имен указанного отношения

Синтаксис

char * SPI_getnspname(Relation rel)

Описание

SPI_getnspname возвращает копию имени пространства имен, которому принадлежит указанное отношение (Relation). Другое название пространства имен — схема. По окончании работы со значением, возвращенным этой функцией, следует освободить его с помощью pfree.

Аргументы

Relation rel

входное отношение

Возвращаемое значение

Имя пространства имен указанного отношения.

SPI_result_code_string

SPI_result_code_string — вернуть код ошибки в виде строки

Синтаксис

const char * SPI_result_code_string(int code);

Описание

SPI_result_code_string выдает строковое представление кода результата, возвращенного различными функциями SPI или сохраненного в SPI_result.

Аргументы

int code

код результата

Возвращаемое значение

Строковое представление кода результата.

Управление памятью

SPI_palloc — выделить память в верхнем контексте исполнителя
SPI_repalloc — перераспределить память в верхнем контексте исполнителя
SPI_pfree — освободить память в верхнем контексте исполнителя
SPI_copytuple — сделать копию строки в верхнем контексте исполнителя
SPI_returntuple — подготовить кортеж к возврату в виде Datum
SPI_modifytuple — создать новую запись, заменив выбранные поля данной записи
SPI_freetuple — освободить строку, выделенную в верхнем контексте исполнителя
SPI_freetuptable — освободить набор строк, созданных SPI_execute или подобной функцией
SPI_freeplan — освободить ранее сохраненный подготовленный оператор

QHB выделяет память в контекстах памяти, предоставляющих удобный метод управления распределением памяти по многим различным местам и с различным требуемым сроком жизни этой памяти. Уничтожение контекста освобождает всю выделенную в нем память. Таким образом, нет необходимости отслеживать отдельные объекты, чтобы избежать утечек памяти; вместо этого достаточно управлять только относительно небольшим количеством контекстов. palloc и связанные функции выделяют память из «текущего» контекста.

Функция SPI_connect создает новый контекст памяти и делает его текущим. Функция SPI_finish восстанавливает контекст, который был текущим ранее, и уничтожает контекст, созданный SPI_connect. Эти действия гарантируют, что временные выделения памяти, сделанные внутри вашей функции на C/RUST, будут освобождены при выходе из этой функции, предотвращая утечки памяти.

Однако если вашей функции на C/RUST нужно вернуть объект в выделенной памяти (например, значение типа данных с передачей по ссылке), вы не можете выделить эту память, используя palloc, по крайней мере, пока вы подключены к SPI. Если вы попытаетесь, объект будет освобожден посредством SPI_finish, и ваша функция на C/RUST не будет работать надежно. Для решения этой проблемы используйте SPI_palloc, чтобы выделить память для вашего возвращаемого объекта. SPI_palloc выделяет память в «верхнем контексте исполнителя», то есть в контексте памяти, который был текущим при вызове SPI_connect и является абсолютно верным контекстом для значения, возвращаемого вашей функцией на C/RUST. Некоторые другие служебные функции, описанные в этом разделе, также возвращают объекты, созданные в верхнем контексте исполнителя.

Когда вызывается SPI_connect, создаваемый ей частный контекст функции на C/RUST становится текущим контекстом. Все выделения, сделанные функциями palloc, repalloc или служебными функциями SPI (за исключением случаев, описанных в этом разделе), выполняются в этом контексте. Когда функция на C/RUST отключается от менеджера SPI (через SPI_finish), текущим контекстом снова становится верхний контекст исполнителя, а вся память, выделенная в контексте памяти функции на C/RUST, освобождается и больше не может быть использована.

SPI_palloc

SPI_palloc — выделить память в верхнем контексте исполнителя

Синтаксис

void * SPI_palloc(Size size)

Описание

SPI_palloc выделяет память в верхнем контексте исполнителя

Эту функцию можно использовать только при подключении к SPI. В противном случае она выдает ошибку.

Аргументы

Size size

размер выделяемой памяти (в байтах)

Возвращаемое значение

указатель на новый выделенный блок памяти заданного размера

SPI_repalloc

SPI_repalloc — перераспределить память в верхнем контексте исполнителя

Синтаксис

void * SPI_repalloc(void * pointer, Size size)

Описание

SPI_repalloc изменяет размер сегмента памяти, ранее выделенного с помощью функции SPI_palloc.

Эта функция больше не отличается от обычного repalloc. Она сохранена только для обратной совместимости существующего кода.

Аргументы

void * pointer

указатель на существующее хранилище, подлежащее изменению

Size size

размер выделяемой памяти (в байтах)

Возвращаемое значение

указатель на новый блок памяти указанного размера, в который скопировано содержимое существующего блока

SPI_pfree

SPI_pfree — освободить память в верхнем контексте исполнителя

Синтаксис

void SPI_pfree(void * pointer)

Описание

SPI_pfree освобождает память, ранее выделенную с помощью функции SPI_palloc или SPI_repalloc.

Эта функция больше не отличается от обычного pfree. Она сохранена только для обратной совместимости существующего кода.

Аргументы

void * pointer

указатель на существующий блок памяти, подлежащий освобождению

SPI_copytuple

SPI_copytuple — сделать копию строки в верхнем контексте исполнителя

Синтаксис

HeapTuple SPI_copytuple(HeapTuple row)

Описание

SPI_copytuple создает копию строки в верхнем контексте исполнителя. Обычно это используется для возврата измененной строки из триггера. В функции, объявленной для возврата составного типа, используйте вместо этого SPI_returntuple.

Эту функцию можно использовать только при подключении к SPI. В противном случае она возвращает NULL и устанавливает в SPI_result значение SPI_ERROR_UNCONNECTED.

Аргументы

HeapTuple row

строка, подлежащая копированию

Возвращаемое значение

скопированная строка или NULL в случае ошибки (код ошибки см. в SPI_result)

SPI_returntuple

SPI_returntuple — подготовить кортеж к возврату в виде Datum

Синтаксис

HeapTupleHeader SPI_returntuple(HeapTuple row, TupleDesc rowdesc)

Описание

SPI_returntuple создает копию строки в верхнем контексте исполнителя, возвращая ее в виде типа Datum. Перед возвратом полученный указатель нужно лишь преобразовать в Datum посредством функции PointerGetDatum.

Эту функцию можно использовать только при подключении к SPI. В противном случае она возвращает NULL и устанавливает в SPI_result значение SPI_ERROR_UNCONNECTED.

Обратите внимание, что данный метод следует использовать для функций, которые объявлены для возврата составных типов. Для триггеров он не применяется; чтобы вернуть измененную строку в триггере, используйте функцию SPI_copytuple.

Аргументы

HeapTuple row

строка, подлежащая копированию

TupleDesc rowdesc

дескриптор для строки (передавайте каждый раз один и тот же дескриптор для наиболее эффективного кэширования)

Возвращаемое значение

HeapTupleHeader, указывающий на скопированную строку, или NULL при ошибке (код ошибки см. в SPI_result)

SPI_modifytuple

SPI_modifytuple — создать строку, заменив выбранные поля исходной строки

Синтаксис

HeapTuple SPI_modifytuple(Relation rel, HeapTuple row, int ncols,
                          int * colnum, Datum * values, const char * nulls)

Описание

SPI_modifytuple создает новую строку, подставляя новые значения для выбранных столбцов и копируя остальные столбцы исходной строки. Входная строка не изменяется. Новая строка возвращается в верхнем контексте исполнителя.

Эту функцию можно использовать только при подключении к SPI. В противном случае она возвращает NULL и устанавливает в SPI_result значение SPI_ERROR_UNCONNECTED.

Аргументы

Relation rel

Используется только как источник дескриптора строки. (Передача отношения вместо дескриптора строки является ошибкой.)

HeapTuple row

строка, подлежащая изменению

int ncols

количество столбцов, подлежащих изменению

int * colnum

массив длиной ncols, содержащий номера столбцов, подлежащих изменению (номера столбцов начинаются с 1)

Datum * values

массив длиной ncols, содержащий новые значения для указанных столбцов

const char * nulls

массив длиной ncols, описывающий, какие из новых значений равны NULL

Если параметр nulls равен NULL, то SPI_modifytuple предполагает, что ни одно новое значение не равно NULL. В противном случае каждый элемент массива nulls должен содержать ' ', если соответствующее новое значение не равно NULL, или 'n', если равно. (В последнем случае фактическое значение в соответствующем элементе values не имеет значения.) Обратите внимание, что nulls — это не текстовая строка, а массив, поэтому завершающий символ '\0' не нужен.

Возвращаемое значение

новая строка с изменениями, размещенная в верхнем контексте исполнителя или NULL при ошибке (код ошибки см. в SPI_result)

В случае ошибки в SPI_result устанавливаются следующие значения:

SPI_ERROR_ARGUMENT

если rel равен NULL, или row равен NULL, или ncols меньше или равен 0, или colnum равен NULL, или values равен NULL.

SPI_ERROR_NOATTRIBUTE

если параметр colnum содержит недопустимый номер столбца (меньше или равен 0 или больше, чем количество столбцов в параметре row)

SPI_ERROR_UNCONNECTED

если SPI неактивен

SPI_freetuple

SPI_freetuple — освободить строку, выделенную в верхнем контексте исполнителя

Синтаксис

void SPI_freetuple(HeapTuple row)

Описание

SPI_freetuple освобождает строку, ранее выделенную в верхнем контексте исполнителя.

Эта функция больше не отличается от обычной heap_freetuple. Она сохранена только для обратной совместимости существующего кода.

Аргументы

HeapTuple row

строка, подлежащая освобождению

SPI_freetuptable

SPI_freetuptable — освободить набор строк, созданный SPI_execute или подобной функцией

Синтаксис

void SPI_freetuptable(SPITupleTable * tuptable)

Описание

SPI_freetuptable освобождает набор строк, созданный предыдущим вызовом функции SPI, выполняющей команды, например SPI_execute. Поэтому данная функция часто вызывается с глобальной переменной SPI_tuptable в качестве аргумента.

Эта функция полезна, если функция на C/RUST, использующая SPI, должна выполнять несколько команд и не хочет сохранять результаты предыдущих команд до завершения. Обратите внимание, что все наборы строк будут в любом случае освобождены при вызове SPI_finish. Кроме того, если субтранзакция запускается, а затем прерывается в ходе выполнения использующей SPI функции на C/RUST, то SPI автоматически освобождает все наборы строк, созданные в рамках субтранзакции.

Аргументы

SPITupleTable * tuptable

указатель на набор строк, подлежащий освобождению, или NULL, если ничего не делать не нужно

SPI_freeplan

SPI_freeplan — освободить ранее сохраненный подготовленный оператор

Синтаксис

int SPI_freeplan(SPIPlanPtr plan)

Описание

SPI_freeplan освобождает подготовленный оператор, ранее возвращенный функцией SPI_prepare или сохраненный функцией SPI_keepplan или SPI_saveplan.

Аргументы

SPIPlanPtr plan

указатель на оператор, подлежащий освобождению

Возвращаемое значение

0 в случае успеха; SPI_ERROR_ARGUMENT, если значение параметра plan неверно или равно NULL.

Управление транзакциями

SPI_commit — зафиксировать текущую транзакцию
SPI_rollback — прервать текущую транзакцию
SPI_start_transaction — начать новую транзакцию

Невозможно выполнить команды управления транзакциями, например COMMIT и ROLLBACK через функции SPI, такие как SPI_execute. Однако существуют отдельные функции интерфейса, позволяющие управлять транзакциями через SPI.

Обычно небезопасно и нецелесообразно запускать и завершать транзакции в произвольных пользовательских функциях, вызываемых из SQL, без учета контекста, в котором они вызываются. Например, граница транзакции в середине функции, которая является частью сложного SQL-выражения, являющегося частью какой-либо другой команды SQL, скорее всего приведет к непонятным внутренним ошибкам или сбоям. Представленные здесь функции интерфейса предназначены, в первую очередь, для использования реализациями процедурных языков в целях поддержки управления транзакциями в процедурах уровня SQL, которые вызываются командой CALL (с учетом контекста ее вызова). Процедуры, реализованные на C/RUST и использующие SPI, могут реализовывать ту же логику, но подробное освещение этой темы этого выходит за рамки данной документации.

SPI_commit

SPI_commit, SPI_commit_and_chain — зафиксировать текущую транзакцию

Синтаксис

void SPI_commit(void)

void SPI_commit_and_chain(void)

Описание

SPI_commit фиксирует текущую транзакцию. Это примерно равнозначно выполнению SQL-команды COMMIT. После фиксации транзакции и перед выполнением дальнейший действий с базой данных нужно запустить новую транзакцию с помощью функции SPI_start_transaction.

SPI_commit_and_chain делает то же самое, но новая транзакция немедленно запускается с теми же характеристиками, что и только что завершенная, как при выполнении SQL-команды COMMIT AND CHAIN.

Эти функции можно выполнить, только если SPI-подключение было установлено в неатомарном режиме путем вызова функции SPI_connect_ext.

SPI_rollback

SPI\rollback, SPI_rollback_and_chain — отменить текущую транзакцию

Синтаксис

void SPI_rollback(void)

void SPI_rollback_and_chain(void)

Описание

SPI_rollback откатывает текущую транзакцию. Это примерно равнозначно выполнению SQL-команды ROLLBACK. После отката транзакции и перед выполнением дальнейший действий с базой данных нужно запустить новую транзакцию с помощью функции SPI_start_transaction.

SPI_rollback_and_chain делает то же самое, но новая транзакция немедленно запускается с теми же характеристиками, что и только что завершенная, как при выполнении SQL-команды ROLLBACK AND CHAIN.

Эти функции можно выполнить, только если SPI-подключение было установлено в неатомарном режиме путем вызова функции SPI_connect_ext.

SPI_start_transaction

SPI_start_transaction — начать новую транзакцию

Синтаксис

void SPI_start_transaction(void)

Описание

SPI_start_transaction начинает новую транзакцию. Эту функцию можно вызвать только после SPI_commit или SPI_rollback, поскольку в этот момент транзакция не активна. Обычно, когда вызывается процедура, использующая SPI, уже существует активная транзакция, поэтому попытка начать еще одну до закрытия текущей приведет к ошибке.

Эту функцию можно выполнить, только если SPI-подключение было установлено в неатомарном режиме путем вызова функции SPI_connect_ext.

Видимость изменений данных

Следующие правила регулируют видимость изменений данных в функциях, использующих SPI (или любых других функциях на C/RUST):

Во время выполнения команды SQL любые изменения данных, сделанные этой командой, невидимы для самой команды. Например, в:

INSERT INTO a SELECT * FROM a;

вставленные строки невидимы для части SELECT.

Изменения, произведенные командой В, видны всем командам, которые запускаются после нее, независимо от того, были ли эти команды запущены внутри В (во время ее выполнения) или после завершения В.

Команды, выполняемые через SPI внутри функции, вызванной командой SQL (обычной функцией или триггером), следуют одному или другому из приведенных выше правил в зависимости от флага чтения/записи, передаваемого в SPI. Команды, выполняемые в режиме «только чтение», следуют первому правилу: они не могут видеть изменения, произведенные вызывающей командой. Команды, выполняемые в режиме «чтение-запись», следуют второму правилу: они могут видеть все произведенные к этому моменту изменения.

Все стандартные процедурные языки устанавливают режим SPI «чтение-запись» в зависимости от атрибута изменчивости функции. Команды функций STABLE и IMMUTABLE выполняются в режиме «только чтение», тогда как команды функций VOLATILE выполняются в режиме «чтение-запись». Хотя авторы функций на C/RUST могут нарушить это соглашение, вряд ли это будет хорошей идеей.

Следующий раздел содержит пример, который иллюстрирует применение этих правил.

Примеры

Этот раздел содержит очень простой пример использования SPI. Функция на C/RUST execq принимает команду SQL в качестве первого аргумента и число строк в качестве второго, выполняет команду с помощью функции SPI_exec и возвращает количество строк, обработанных командой. Вы можете найти более сложные примеры для SPI в дереве исходного кода в src/test/regress/regress.c.

#include "postgres.h"

#include "executor/spi.h"
#include "utils/builtins.h"

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(execq);

Datum
execq(PG_FUNCTION_ARGS)
{
    char *command;
    int cnt;
    int ret;
    uint64 proc;

    /* Преобразовать данный текстовый объект в строку C */
    command = text_to_cstring(PG_GETARG_TEXT_PP(0));
    cnt = PG_GETARG_INT32(1);

    SPI_connect();

    ret = SPI_exec(command, cnt);

    proc = SPI_processed;

    /*
     * Если были извлечены какие-то строки, вывести их через elog(INFO).
     */
    if (ret > 0 && SPI_tuptable != NULL)
    {
        TupleDesc tupdesc = SPI_tuptable->tupdesc;
        SPITupleTable *tuptable = SPI_tuptable;
        char buf[8192];
        uint64 j;

        for (j = 0; j < proc; j++)
        {
            HeapTuple tuple = tuptable->vals[j];
            int i;

            for (i = 1, buf[0] = 0; i <= tupdesc->natts; i++)
                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %s%s",
                        SPI_getvalue(tuple, tupdesc, i),
                        (i == tupdesc->natts) ? " " : " |");
            elog(INFO, "EXECQ: %s", buf);
        }
    }

    SPI_finish();
    pfree(command);

    PG_RETURN_INT64(proc);
}

Так эта функция будет объявлена после ее компиляции в разделяемую библиотеку (подробности в разделе Компиляция и связывание динамически загружаемых функций):

CREATE FUNCTION execq(text, integer) RETURNS int8
    AS 'имя_файла'
    LANGUAGE C STRICT;

Пример сеанса:

=> SELECT execq('CREATE TABLE a (x integer)', 0);
 execq
-------
     0
(1 row)

=> INSERT INTO a VALUES (execq('INSERT INTO a VALUES (0)', 0));
INSERT 0 1
=> SELECT execq('SELECT * FROM a', 0);
INFO:  EXECQ:  0    -- вставлено функцией execq
INFO:  EXECQ:  1    -- возвращено функцией execq и вставлено верхней командой INSERT

 execq
-------
     2
(1 row)

=> SELECT execq('INSERT INTO a SELECT x + 2 FROM a', 1);
 execq
-------
     1
(1 row)

=> SELECT execq('SELECT * FROM a', 10);
INFO:  EXECQ:  0
INFO:  EXECQ:  1
INFO:  EXECQ:  2    -- 0 + 2, вставлена только одна строка — как указано

 execq
-------
     3              -- 10 — только максимальное значение, 3 — реальное число строк
(1 row)

=> DELETE FROM a;
DELETE 3
=> INSERT INTO a VALUES (execq('SELECT * FROM a', 0) + 1);
INSERT 0 1
=> SELECT * FROM a;
 x
---
 1                  -- нет строк в a (0) + 1
(1 row)

=> INSERT INTO a VALUES (execq('SELECT * FROM a', 0) + 1);
INFO:  EXECQ:  1
INSERT 0 1
=> SELECT * FROM a;
 x
---
 1
 2                  -- была только одна строка в a + 1
(2 rows)

-- Этот пример демонстрирует правило видимости изменений данных:

=> INSERT INTO a SELECT execq('SELECT * FROM a', 0) * x FROM a;
INFO:  EXECQ:  1
INFO:  EXECQ:  2
INFO:  EXECQ:  1
INFO:  EXECQ:  2
INFO:  EXECQ:  2
INSERT 0 2
=> SELECT * FROM a;
 x
---
 1
 2
 2                  -- 2 строки * 1 (x в первой строке)
 6                  -- 3 строки (2 + 1 только что вставленная) * 2 (x во второй строке)
(4 rows)               ^^^^^^
                       строки, видимые в execq() при разных вызовах

Фоновые рабочие процессы

QHB может быть расширена для запуска пользовательского кода в отдельных процессах. Такие процессы запускаются, останавливаются и контролируются QHB, что позволяет им тесно связать время жизни со статусом сервера. Эти процессы имеют возможность подключаться к области общей памяти QHB и подключаться к базам данных, они также могут запускать последовательно, несколько транзакций как обычный серверный процесс, подключенный к клиенту. Также, связываясь с libpq, они могут подключаться к серверу и вести себя как обычное клиентское приложение.

Фоновые процессы могут быть инициализированы во время запуска QHB путем включения имени модуля в shared_preload_libraries. Модуль, желающий запустить фоновый рабочий, может зарегистрировать его, вызвав RegisterBackgroundWorker( BackgroundWorker *worker ) из своего _PG_init(). Фоновые рабочие также можно запустить после запуска системы, вызвав функцию RegisterDynamicBackgroundWorker( BackgroundWorker *worker, BackgroundWorkerHandle **handle ). В отличие от RegisterBackgroundWorker, который может вызываться только из postmaster, RegisterDynamicBackgroundWorker должен вызываться из обычного бэкенда или другого фонового процесса.

Структура BackgroundWorker определяется таким образом:

typedef void (*bgworker_main_type)(Datum main_arg);
typedef struct BackgroundWorker
{
    char        bgw_name[BGW_MAXLEN];
    char        bgw_type[BGW_MAXLEN];
    int         bgw_flags;
    BgWorkerStartTime bgw_start_time;
    int         bgw_restart_time;       /* in seconds, or BGW_NEVER_RESTART */
    char        bgw_library_name[BGW_MAXLEN];
    char        bgw_function_name[BGW_MAXLEN];
    Datum       bgw_main_arg;
    char        bgw_extra[BGW_EXTRALEN];
    int         bgw_notify_pid;
} BackgroundWorker;

bgw_name и bgw_type - это строки, которые будут использоваться в сообщениях журнала, списках процессов и в подобных контекстах. bgw_type должен быть одинаковым для всех фоновых процессов одного типа, чтобы, например, можно было сгруппировать таких процессов в список процессов. bgw_name с другой стороны, может содержать дополнительную информацию о конкретном процессе. (Как правило, строка для bgw_name будет как-то идентифицировать фоновый процесс, но это не обязательно.)

bgw_flags - битовая маска или битовая маска, указывающая возможности, которые предоставляются модулю. Возможные значения:

Флаг
Назначение

BGWORKER_SHMEM_ACCESS
Запрашивает доступ к общей памяти. Рабочие, не имеющие доступа к общей памяти, не могут получить доступ ни к одной из общих структур данных QHB, таких как тяжелые или облегченные блокировки, общие буферы, или к любым пользовательским структурам данных, которые рабочий может захотеть создать и использовать.

BGWORKER_BACKEND_DATABASE_CONNECTION
Запрашивает возможность установить соединение с базой данных, с помощью которого она может позже выполнять транзакции и запросы Фоновый процесс, использующий BGWORKER_BACKEND_DATABASE_CONNECTION для подключения к базе данных, должен также подключить общую память с помощью BGWORKER_SHMEM_ACCESS, иначе запуск рабочего не удастся.

bgw_start_time - состояние сервера, в течение которого QHB должна запустить процесс - это может быть BgWorkerStart_PostmasterStart (запускаться, как только сама QHB завершила свою собственную инициализацию, процессы, запускающиеся в этот момент, не подходят для соединений с базой данных), BgWorkerStart_ConsistentState (запускаться, как только достигнуто согласованное состояние, что позволяет процессам подключаться к базам данных и выполнять запросы только для чтения) и BgWorkerStart_RecoveryFinished (запускаться, как только система BgWorkerStart_RecoveryFinished перевела систему в нормальное состояние чтения-записи). Обратите внимание, что последние два значения эквивалентны на сервере, который не является standby. Обратите внимание, что этот параметр указывает только, когда фоновые процессы должны запускаться - они не будут останавливаться при переходе в другое состояния.

bgw_restart_time - это интервал в секундах, который QHB должна ждать перед перезапуском процесса в случае его сбоя. Это может быть любое положительное значение или BGW_NEVER_RESTART, указывающее не перезапускать процесс в случае сбоя.

bgw_library_name - это имя библиотеки, в которой следует искать начальную точку входа для фонового процесса. Именованная библиотека будет динамически загружена рабочим процессом, а имя-функции- bgw_function_name будет использоваться для идентификации вызываемой функции. При загрузке функции из основного кода должно быть установлено «QHB».

bgw_function_name - это имя функции в динамически загружаемой библиотеке, которая должна использоваться в качестве начальной точки входа для нового фонового процесса.

bgw_main_arg - это аргумент типа Datum для основной функции фонового процесса. Основная функция должна принимать один аргумент типа Datum и возвращать void. bgw_main_arg будет передан в качестве аргумента. Кроме того, глобальная переменная MyBgworkerEntry указывает на копию структуры BackgroundWorker переданную во время регистрации - процесс запуска может использовать эту структуру.

bgw_extra может содержать дополнительные данные для передачи фоновому процессу. В отличие от bgw_main_arg, эти данные не передаются в качестве аргумента основной функции worker-a, но к ним можно получить доступ через MyBgworkerEntry, как обсуждалось выше.

bgw_notify_pid - это PID бэкенд-процесса QHB, которому qhbmaster должен отправить SIGUSR1 при запуске или выходе из процесса. Должен быть 0 для процессов, зарегистрированных во время запуска qhbmaster, или когда бэкенд, регистрирующий процесса, не хочет ждать запуска процесса. В противном случае его следует инициализировать как MyProcPid.

После запуска процесс может подключиться к базе данных, вызвав BackgroundWorkerInitializeConnection( char *dbname, char *username , uint32 flags) или BackgroundWorkerInitializeConnectionByOid( Oid dboid, Oid useroid, uint32 flags). Это позволяет процессу выполнять транзакции и запросы с использованием интерфейса SPI. Если dbname имеет значение NULL или dboid имеет значение InvalidOid, сеанс не связан с какой-либо конкретной базой данных, но доступны общие каталоги. Если username NULL или useroid - InvalidOid, процесс будет работать как суперпользователь, созданный во время initdb. Если в качестве flags указано BGWORKER_BYPASS_ALLOWCONN можно обойти ограничение для подключения к базам данных, не разрешая пользовательские подключения. Фоновый рабочий может вызвать только одну из этих двух функций и только один раз. Невозможно переключить базы данных.

Сигналы изначально блокируются, когда управление достигает основной функции фонового процесса, и должны быть разблокированы самим процессом - это позволяет процессу при необходимости настраивать свои обработчики сигналов. Сигналы могут быть разблокированы в новом процессе путем вызова BackgroundWorkerUnblockSignals и заблокированы путем вызова BackgroundWorkerBlockSignals .

Если bgw_restart_time для фонового процесса сконфигурировано как BGW_NEVER_RESTART, или если он завершается с кодом выхода 0 или завершается TerminateBackgroundWorker, он будет автоматически отменен главным процессом при выходе. В противном случае он будет перезапущен по истечении периода времени, настроенного с помощью bgw_restart_time, или немедленно, если qhbmaster повторно инициализирует кластер из-за сбоя бэкенда. бэкенды, которые должны временно приостановить выполнение, должны использовать паузы вместо выхода - это может быть достигнуто путем вызова WaitLatch(). Убедитесь, что при вызове этой функции установлен флаг WL_POSTMASTER_DEATH, и проверьте код завершения для быстрого завершения в аварийном случае, когда сама qhb завершилась.

Когда фоновый процесс зарегистрирован с помощью функции RegisterDynamicBackgroundWorker, сервер, выполняющий регистрацию, может получить информацию о статусе процесса. бэкенды, желающие сделать это, должны передать адрес BackgroundWorkerHandle * в качестве второго аргумента RegisterDynamicBackgroundWorker. Если процесс успешно зарегистрирован, этот указатель будет инициализирован с непрозрачным дескриптором, который впоследствии может быть передан в GetBackgroundWorkerPid( BackgroundWorkerHandle *, pid_t * ) или TerminateBackgroundWorker( BackgroundWorkerHandle * ) . GetBackgroundWorkerPid может использоваться для опроса статуса процесса: возвращаемое значение BGWH_NOT_YET_STARTED указывает, что процесс еще не был запущен qhbmaster - BGWH_STOPPED указывает, что он был запущен, но больше не работает и BGWH_STARTED указывает, что он в данный момент работает. В этом последнем случае PID также будет возвращен через второй аргумент. TerminateBackgroundWorker заставляет qhbmaster отправлять SIGTERM процессу, если он работает, и отменять его регистрацию, как только он завершится.

В некоторых случаях процесс, который регистрирует фоновый процесс, может дождаться его запуска. Это может быть достигнуто путем инициализации bgw_notify_pid для MyProcPid и последующей передачи BackgroundWorkerHandle * полученного во время регистрации, в WaitForBackgroundWorkerStartup( BackgroundWorkerHandle *handle, pid_t * ). Эта функция будет блокироваться до тех пор, пока qhbmaster не попытается запустить фоновый процесс снова, или пока qhbmaster не прекратит работу. Если фоновый процесс работает, возвращаемое значение будет BGWH_STARTED, а PID будет записан по указанному адресу. В противном случае возвращаемое значение будет BGWH_STOPPED или BGWH_POSTMASTER_DIED .

Процесс также может ожидать выключения фонового процесса, используя WaitForBackgroundWorkerShutdown( BackgroundWorkerHandle *handle ) и передавая BackgroundWorkerHandle * полученный при регистрации. Эта функция будет блокироваться до тех пор, пока не завершится фоновый процесс или qhbmaster не завершит работу. Когда фоновый процесс завершает работу, возвращаемое значение будет BGWH_STOPPED, если qhbmaster завершится, он вернет BGWH_POSTMASTER_DIED.

Если фоновый процесс отправляет асинхронные уведомления с помощью команды NOTIFY через интерфейс программирования сервера SPI, он должен явно вызывать ProcessCompletedNotifies после ProcessCompletedNotifies включающей транзакции, чтобы можно было доставлять любые уведомления. Если фоновый процесс регистрируется для получения асинхронных уведомлений с LISTEN через SPI, процесс регистрирует эти уведомления, но у него нет программного способа перехватывать и отвечать на эти уведомления.

Максимальное количество зарегистрированных фоновых процессов ограничено параметров max_worker_processes.

Процесс qhb

qhb — сервер базы данных QHB

Синтаксис

qhb [параметр...]

или

qhbmaster [параметр...]

Описание

qhb — это сервер базы данных QHB. Чтобы получить доступ к базе данных, клиентское приложение подключается (по сети или локально) к работающему экземпляру qhb. Затем экземпляр qhb запускает отдельный серверный процесс для обработки соединения.

Один экземпляр qhb всегда управляет данными только одного кластера баз данных. Кластер баз данных — это набор баз данных, который хранится в определенном месте общей файловой системы («область данных»). В системе одновременно может работать несколько экземпляров qhb, если они используют разные области данных и разные коммуникационные порты (см. ниже). Когда qhb запускается, он должен знать местоположение области данных. Местоположение должно быть указано с помощью опции -D или переменной среды PGDATA; значение по умолчанию отсутствует. Как правило, -D или PGDATA указывает непосредственно на каталог области данных, созданный qhb_bootstrap. Другие возможные шаблоны файлов описаны в главе Расположение файлов.

По умолчанию qhb запускается в приоритетном режиме и печатает сообщения журнала в стандартный поток ошибок. На практике qhb должен запускаться как фоновый процесс, возможно, во время загрузки системы.

Команда qhb также может быть вызвана в однопользовательском режиме. В основном, этот режим используется во время начального создания базы при помощи qhb_bootstrap, но иногда применяется и для отладки или аварийного восстановления. Обратите внимание, что использование однопользовательского режима не совсем подходит для полноценной отладки сервера вследствие отсутствия в нем реалистичного межпроцессного взаимодействия и блокировок. При вызове в однопользовательском режиме из командной оболочки пользователь может вводить запросы, и результаты будут выводиться на экран, но в форме, которая более полезна для разработчиков, чем для конечных пользователей. В этом режиме пользователем сеанса будет считаться пользователь с ID 1, и ему будут предоставлены неявные полномочия суперпользователя. Этому пользователю необязательно существовать на самом деле, поэтому однопользовательский режим можно использовать для ручного восстановления после некоторых видов случайного повреждения системных каталогов.

Параметры

qhb принимает ряд аргументов командной строки. За подробным рассмотрением параметров обратитесь к разделу Конфигурация сервера. Ввод большинства этих параметров можно сохранить, настроив файл конфигурации. Некоторые (безопасные) параметры также можно установить со стороны подключающегося клиента (в зависимости от приложения), чтобы они применялись только для этого сеанса. Например, если установлена переменная среды PGOPTIONS, клиенты на основе libpq передадут эту строку на сервер, который будет интерпретировать ее как параметры командной строки qhb.

Общие параметры

-B количество-буферов

Устанавливает количество общих буферов для использования процессами сервера. Значение по умолчанию для этого параметра автоматически выбирается qhb_bootstrap. Указание этого параметра равнозначно установке параметра конфигурации shared_buffers.

-c имя=значение

Устанавливает именованный параметр времени выполнения. Параметры конфигурации, поддерживаемые QHB, описаны в главе Конфигурация сервера. Большинство других параметров командной строки на самом деле являются краткими формами такого назначения параметров. Для установки нескольких параметров -c можно указывать несколько раз.

-C имя

Печатает значение именованного параметра времени выполнения и завершает работу. (Подробности см. выше в описании параметра -c.) Это можно использовать на работающем сервере, при этом будут возвращены значения из qhb.conf с изменениями, вызванными любыми параметрами в этом вызове. Он не отражает параметры, предоставленные при запуске кластера.

Этот параметр предназначен для других программ, которые взаимодействуют с экземпляром сервера (например, qhb_ctl) и запрашивают значения параметров конфигурации. Пользовательские приложения должны вместо этого использовать SHOW или представление pg_settings.

-d уровень-отладки

Устанавливает уровень отладки. Чем выше это значение, тем больше выходных данных отладки записывается в журнал сервера. Значения находятся в диапазоне от 1 до 5. Также для отдельного сеанса возможно передать -d 0, что не даст уровню серверного журнала родительского процесса QHB распространиться на него.

-D каталог-данных

Определяет расположение файловой системы файлов конфигурации базы данных. Подробную информацию см. в разделе Расположение файлов.

-e

Устанавливает формат даты по умолчанию в «European», то есть порядок вводимых значений даты вида DMY. Это также приводит к тому, что в определенных форматах вывода даты день печатается перед месяцем. Подробную информацию см. в разделе Типы даты/времени.

-F

Выключает вызовы fsync для повышения производительности, хотя и с риском повреждения данных в случае сбоя системы. Указание этого параметра равнозначно выключению параметра конфигурации fsync. Перед использованием этого параметра прочтите подробную документацию!

-h имя-хоста

Указывает IP-адрес или имя хоста, по которому QHB должен принимать подключения по TCP/IP от клиентских приложений. Значением также может быть список адресов через запятую или символ *, означающий все доступные интерфейсы. Пустое значение указывает, что не принимается ни один IP-адрес, и в этом случае подключение к серверу возможно только через сокеты домена Unix. По умолчанию принимаются подключения только к локальной рабочей станции. Указание этого параметра равнозначно установке параметра конфигурации listen_addresses.

-i

Позволяет удаленным клиентам подключаться по TCP/IP (интернет-домен). Без этого параметра принимаются только локальные подключения. Он равнозначен установке значения * у параметра listen_addresses в qhb.conf или у параметра -h. Этот параметр считается устаревшим, так как не позволяет получить доступ ко всем функциям listen_addresses. Обычно лучше задать listen_addresses напрямую.

-k каталог

Указывает каталог сокета Unix-домена, в котором QHB должен принимать подключения от клиентских приложений. Значение также может быть разделенным запятыми списком каталогов. Пустое значение указывает, что не принимается ни один сокет Unix-домена, и в этом случае подключение к серверу возможно только через сокеты TCP/IP. Значением по умолчанию обычно является /tmp, но его можно изменить во время сборки. Указание этого параметра равнозначно установке параметра конфигурации unix_socket_directories.

-l

Включает поддержку безопасных подключений с использованием SSL. Чтобы этот параметр был доступен, QHB нужно скомпилировать с поддержкой SSL.

-N максимум-соединений

Задает максимальное количество клиентских подключений, которые принимает этот сервер. Значение по умолчанию для этого параметра автоматически выбирает qhb_bootstrap. Указание этого параметра равнозначно установке параметра конфигурации max_connections.

-o дополнительные-параметры

Аргументы в стиле командной строки, указанные в дополнительных- параметрах, передаются всем процессам сервера, запущенным этим процессом QHB.

Пробелы в строке дополнительные-параметры считаются отдельными аргументами, если они не экранированы обратной косой чертой (\); для представления буквального символа обратного слеша следует написать \\. Для указания нескольких аргументов можно использовать -o несколько раз.

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

-p порт

Указывает порт TCP/IP или расширение файла локального сокета домена Unix, на котором QHB должен принимать подключения от клиентских приложений. По умолчанию используется значение переменной среды PGPORT или, если PGPORT не установлен, то по умолчанию используется значение, установленное во время компиляции (обычно 5432). Если вы указываете порт, отличный от порта по умолчанию, то все клиентские приложения должны указывать один и тот же порт, используя либо параметры командной строки, либо PGPORT.

-s

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

-S рабочая-память

Определяет объем памяти, который будет использоваться внутренними операциями сортировки и хеширования, прежде чем использовать временные файлы на диске. Описание параметра конфигурации work_mem см. в подразделе Память.

-V --version

Показать версию qhb и выйти.

--имя=значение

Устанавливает именованный параметр времени выполнения; является более короткой формой параметра -c.

--describe-config

Этот параметр выводит внутренние переменные конфигурации сервера, описания и значения по умолчанию в формате COPY с разделителями табуляции. Он предназначен в первую очередь для использования инструментами администрирования.

-? --help

Показать справку об аргументах командной строки qhb и выйти.

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

Описанные здесь параметры используются в основном для целей отладки, а в некоторых случаях для помощи в восстановлении сильно поврежденных баз данных. Не должно быть никаких причин использовать их в настройке производственной базы данных. Они перечислены здесь только для использования разработчиками системы QHB. Кроме того, эти параметры могут быть изменены или удалены в будущих версиях без предварительного уведомления.

-f { s i o b t n m h }

Запрещает использование определенных методов сканирования и объединения: s и i выключают последовательное и индексное сканирование соответственно, o, b и t выключают сканирование только по индексу, сканирование по растровому индексу и сканирование TID соответственно, а n, m и h выключают соединения с помощью вложенных циклов, слияния и хеширования соответственно.

Ни последовательное сканирование, ни соединение с вложенным циклом нельзя выключить полностью; параметры -fs и -fn просто не позволят оптимизатору использовать эти типы планов, если у того есть какая-либо другая альтернатива.

-n

Этот параметр предназначен для отладки проблем, которые приводят к аварийному завершению серверного процесса. Обычная стратегия в этой ситуации состоит в том, чтобы уведомить все другие серверные процессы, что они должны завершиться, а затем повторно инициализировать общую память и семафоры. Это связано с тем, что вызвавший ошибку серверный процесс перед завершением мог повредить некоторое разделяемое состояние. Этот параметр указывает, что qhb не будет повторно инициализировать разделяемые структуры данных. Опытный системный программист может затем использовать отладчик для проверки состояния общей памяти и семафора.

-O

Позволяет изменять структуру системных таблиц. Этот параметр используется командой qhb_bootstrap.

-P

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

-t pa[rser] | pl[anner] | e[xecutor]

Выводит статистику по времени выполнения каждого запроса, относящегося к каждому из основных системных модулей. Этот параметр нельзя использовать совместно с параметром -s.

-T

Этот параметр предназначен для отладки проблем, которые приводят к аварийному завершению серверного процесса. Обычная стратегия в этой ситуации состоит в том, чтобы уведомить все другие серверные процессы, что они должны завершиться, а затем повторно инициализировать общую память и семафоры. Это связано с тем, что вызвавший ошибку серверный процесс перед завершением мог повредить некоторое разделяемое состояние. Этот параметр указывает, что qhb остановит все остальные серверные процессы, отправив сигнал SIGSTOP, но не приведет к их завершению. Это позволит системным программистам вручную собрать дампы ядра всех процессов сервера.

-v протокол

Указывает номер версии протокола взаимодействия клиент-сервер, который будет использоваться для конкретного сеанса. Этот параметр предназначен только для внутреннего использования.

-W секунды

При запуске нового серверного процесса после проведения процедуры аутентификации возникает многосекундная задержка. Назначение этого параметра — дать возможность подключиться к серверному процессу с помощью отладчика.

Параметры для однопользовательского режима

Следующие параметры применимы только к однопользовательскому режиму (см. параграф Однопользовательский режим ниже).

--single

Выбирает однопользовательский режим. Этот аргумент должен быть первым в командной строке.

база_данных

Определяет имя базы данных, к которой открывается доступ. Этот аргумент должен быть последним в командной строке. Если он опущен, по умолчанию используется имя пользователя.

-E

Выводить все команды на стандартное устройство вывода перед их выполнением.

-j

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

-r имя_файла

Отправляет весь вывод журнала сервера в файл имя_файла. Этот параметр учитывается только в том случае, если он указан в командной строке.

Переменные среды

PGCLIENTENCODING

Кодировка символов по умолчанию, используемая клиентами. (Клиенты могут переопределить ее индивидуально.) Это значение также можно задать в файле конфигурации.

PGDATA

Местоположение каталога данных кластера по умолчанию.

PGDATESTYLE

Значение по умолчанию для параметра времени выполнения DateStyle. (Использование этой переменной среды устарело.)

PGPORT

Номер порта по умолчанию (желательно установить в файле конфигурации).

Диагностика

Сообщение об ошибке, в котором упоминается semget или shmget, скорее всего означает необходимость настроить ядро для предоставления достаточной общей памяти и семафоров. Переконфигурирование ядра можно отложить, уменьшив значение параметра shared_buffers для снижения потребления общей памяти QHB, и/или уменьшив значение параметра max_connections для снижения затрат на использование семафоров.

Сообщение об ошибке, указывающее на то, что другой сервер уже запущен, следует тщательно проверить, например, с помощью команды

$ ps ax | grep qhb

или

$ ps -ef | grep qhb

в зависимости от ОС. Если вы уверены, что конфликтующий сервер не запущен, можно удалить указанный в сообщении файл блокировки и повторить попытку.

Сообщение об ошибке, указывающее на невозможность привязки к порту, может означать, что этот порт уже используется некоторым процессом, не связанным с QHB. Также эту ошибку можно получить, если завершить работу qhb и немедленно перезапустить его, используя тот же порт; в этом случае следует просто подождать несколько секунд, пока операционная система не закроет порт, прежде чем попытаться снова. Наконец, эту ошибку можно получить, если указать номер порта, который ваша ОС считает зарезервированным. Например, многие версии Unix считают номера портов менее 1024 «доверенными» и разрешают доступ к ним только суперпользователю Unix.

Примечания

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

Если это вообще возможно, не используйте сигнал SIGKILL для остановки основного сервера qhb, иначе этот сервер не освободит системные ресурсы (например, общую память и семафоры), которые он занимал перед остановкой. Такое поведение может вызвать проблемы при запуске нового экземпляра qhb.

Для нормального завершения работы сервера qhb можно использовать сигналы SIGTERM, SIGINT или SIGQUIT. При первом сервер перед выходом будет дожидаться завершения работы всех клиентов, при втором все клиенты будут принудительно отключены, а третий приведет к немедленному выходу без корректного завершения, вследствие чего во время перезапуска выполнится процедура восстановления.

Сигнал SIGHUP перезагрузит файлы конфигурации сервера. Также возможно отправить SIGHUP на отдельный процесс сервера, но это обычно не имеет смысла.

Чтобы отменить текущий запрос, отправьте сигнал SIGINT процессу, выполняющему эту команду. Чтобы завершить внутренний процесс, отправьте SIGTERM этому процессу. См. также в разделе Функции сигнализации сервера описание функций pg_cancel_backend и pg_terminate_backend, которые являются вызываемыми SQL аналогами этих двух действий.

Сервер qhb использует SIGQUIT, чтобы приказать подчиненным серверным процессам завершиться без обычной очистки. Пользователям нельзя использовать этот сигнал. Также неразумно отправлять SIGKILL на серверный процесс — основной процесс qhb будет интерпретировать это как сбой и заставит все родственные процессы завершиться как часть стандартной процедуры восстановления после сбоя.

Ошибки

Параметр с -- не будут работать на FreeBSD или OpenBSD; используйте вместо них -c. Это ошибка ОС.

Однопользовательский режим

Чтобы запустить сервер в однопользовательском режиме, используйте команду, например

qhb --single -D /var/lib/qhb/data other-options my_database

Укажите правильный путь к каталогу базы данных с помощью -D или задайте переменную среды PGDATA. Также укажите имя конкретной базы данных, в которой вы хотите работать.

Обычно в однопользовательском режиме сервер обрабатывает символ перевода строки как окончание ввода команды; точка с запятой не является синтаксическим разделителем, как в psql. Чтобы ввести команду, занимающую несколько строк, следует перед каждым символом перевода строки, кроме последнего, использовать обратную косую черту. При этом обратная косая черта и соседний символ перевода строки удаляются из вводимой команды. Обратите внимание, что это происходит даже внутри строкового литерала или комментария.

Но если воспользоваться параметром командной строки -j, одиночный символ перевода строки не завершит ввод команды; вместо него это будет делать последовательность из точки с запятой и двух символов перевода строки. То есть для завершения команды следует ввести точку с запятой, а следом пустую строку. Обратная косая черта с последующим символом перевода строки в этом режиме не имеют специального значения. Опять же, внутри строкового литерала или комментария такая последовательность тоже не работает.

В любом из режимов ввода символ точки с запятой, который введен не сразу перед или в составе завершающей последовательности команды, воспринимается как разделитель команд. При введении завершающей последовательности команды несколько операторов, введенных с разделителями, будут выполняться как одна транзакция.

Для завершения сеанса введите символ конца файла (EOF, обычно это Ctrl+D). Если после введения последней завершающей последовательности команды вы вводили какой-либо текст, то EOF будет восприниматься как символ завершения команды и для выхода понадобится еще один EOF.

Обратите внимание, что однопользовательский режим не предоставляет продвинутых возможностей для редактирования строк (например, здесь нет истории команд). Также в однопользовательском режиме также не производится фоновая обработка, например, не выполняются автоматические контрольные точки или репликация.

Примеры

Чтобы запустить qhb в фоновом режиме, используя значения по умолчанию, введите:

$ nohup qhb >logfile 2>&1 </dev/null &

Чтобы запустить qhb с определенным портом, например 1234, введите:

$ qhb -p 1234

Чтобы подключиться к этому серверу с помощью psql, укажите этот порт с параметром -p:

$ psql -p 1234

или установите переменную среды PGPORT:

$ export PGPORT=1234
$ psql

Именованные параметры времени выполнения можно установить в любом из следующих стилей:

$ qhb -c work_mem=1234
$ qhb --work-mem=1234

Любая из этих форм переопределяет любую настройку для work_mem в qhb.conf. Обратите внимание, что подчеркивания в именах параметров в командной строке могут быть записаны как подчеркивание или тире. За исключением краткосрочных экспериментов, вероятно, лучше отредактировать параметр в qhb.conf, чем задавать его в аргументах командной строки.

См. также

qhb_bootstrap, qhb_ctl

Системные каталоги

Системные каталоги — это место, где система управления реляционными базами данных хранит метаданные схемы, такие как сведения о таблицах и столбцах, а также информацию о внутреннем бухгалтерском учете. Системные каталоги QHB — это обычные таблицы. Вы можете удалить и пересоздать их, добавить столбцы, вставить и обновить значения — и тем самым сильно испортить свою систему. В большинстве случаев не следует изменять системные каталоги вручную, для этого обычно существуют команды SQL. (Например, CREATE DATABASE вставляет строку в каталог pg_database и фактически создает базу данных на диске.) Есть некоторые исключения для особенно сложных операций, но многие из них с течением времени становятся доступны в виде команд SQL, и поэтому необходимость в прямом манипулировании системными каталогами постоянно уменьшается.

Обзор

В Таблице 1 перечислены системные каталоги. Более подробная документация по каждому каталогу приводится ниже.

Большинство системных каталогов копируются из шаблона базы данных во время ее создания и затем зависят от конкретной базы данных. Несколько каталогов физически совместно используются во всех базах данных в кластере — они отмечаются в описаниях отдельных каталогов.

Таблица 1. Системные каталоги

Имя каталога | Назначение
pg_aggregate | агрегатные функции
pg_am | методы доступа отношений
pg_amop | операторы метода доступа
pg_amproc | функции поддержки метода доступа
pg_attrdef | значения столбцов по умолчанию
pg_attribute | столбцы таблицы («атрибуты»)
pg_authid | идентификаторы авторизации (роли)
pg_auth_members | отношения членства идентификатора авторизации
pg_cast | приведения (преобразования типов данных)
pg_class | таблицы, индексы, последовательности, представления («отношения»)
pg_collation | параметры сортировки (сведения о локали)
pg_constraint | проверочные ограничения, ограничения уникальности, ограничения первичного ключа, ограничения внешнего ключа
pg_conversion | информация о перекодировках
pg_database | базы данных в этом кластере
pg_db_role_setting | параметры на уровне ролей и баз данных
pg_default_acl | права доступа по умолчанию для типов объектов
pg_depend | зависимости между объектами базы данных
pg_description | описания или комментарии для объектов базы данных
pg_enum | определения меток и значений перечислений
pg_event_trigger | триггеры событий
pg_extension | установленные расширения
pg_foreign_data_wrapper | определения оберток сторонних данных
pg_foreign_server | определения сторонних серверов
pg_foreign_table | дополнительная информация по сторонним таблицам
pg_index | дополнительная информация по индексам
pg_inherits | иерархия наследования таблиц
pg_init_privs | начальные права для объектов
pg_language | языки для написания функций
pg_largeobject | страницы данных для больших объектов
pg_largeobject_metadata | метаданные для больших объектов
pg_namespace | схемы
pg_opclass | классы операторов метода доступа
pg_operator | операторы
pg_opfamily | семейства операторов метода доступа
pg_partitioned_table | информация о ключе разбиения таблиц
pg_policy | политики защиты на уровне строк
pg_proc | функции и процедуры
pg_publication | публикации для логической репликации
pg_publication_rel | сопоставление отношений с публикациями
pg_range | информация о типах диапазонов
pg_replication_origin | зарегистрированные источники репликации
pg_rewrite | правила перезаписи запросов
pg_seclabel | метки безопасности для объектов базы данных
pg_sequence | информация о последовательностях
pg_shdepend | зависимости для общих объектов
pg_shdescription | комментарии к общим объектам
pg_shseclabel | метки безопасности для общих объектов базы данных
pg_statistic | статистика планировщика
pg_statistic_ext | расширенная статистика планировщика (определение)
pg_statistic_ext_data | расширенная статистика планировщика (собранная статистика)
pg_subscription | подписки на логическую репликацию
pg_subscription_rel | состояние отношений для подписок
pg_tablespace | табличные пространства в этом кластере базы данных
pg_transform | трансформации (преобразования типа данных в процедурный язык)
pg_trigger | триггеры
pg_ts_config | конфигурации текстового поиска
pg_ts_config_map | сопоставления маркеров конфигураций текстового поиска
pg_ts_dict | словари текстового поиска
pg_ts_parser | синтаксические анализаторы текстового поиска
pg_ts_template | шаблоны текстового поиска
pg_type | типы данных
pg_user_mapping | сопоставления пользователей для сторонних серверов

pg_aggregate

В каталоге pg_aggregate хранится информация об агрегатных функциях. Агрегатная функция — это функция, которая работает с набором значений (как правило, с одним столбцом из каждой строки, которая соответствует условию запроса) и возвращает одно значение, вычисленное из всех этих значений. Типичными агрегатными функциями являются sum, count и max. Каждая запись в pg_aggregate представляет собой дополнение записи в pg_proc. Запись в pg_proc содержит имя агрегата, типы входных и выходных данных, а также другую информацию, аналогичную имеющейся у обычных функций.

Таблица 2. Столбцы pg_aggregate

Имя
Тип
Ссылки
Описание

aggfnoid
regproc
pg_proc.oid
OID агрегатной функции в pg_proc

aggkind
char

Вид агрегатной функции: n для обычной (normal), o для сортирующей (ordered-set) или h для гипотезирующей (hypothetical-set)

aggnumdirectargs
int2

Число непосредственных (неагрегированных) аргументов сортирующей или гипотезирующей агрегатной функции, считая переменный массив аргументов за один аргумент. Если равняется pronargs, агрегатная функция должна принимать переменный массив, и этот массив описывает как агрегированные аргументы, так и конечные непосредственные аргументы. Всегда равно нулю для обычных агрегатных функций.

aggtransfn
regproc
pg_proc.oid
Функция перехода

aggfinalfn
regproc
pg_proc.oid
Функция завершения (ноль, если ее нет)

aggcombinefn
regproc
pg_proc.oid
Функция совмещения (ноль, если ее нет)

aggserialfn
regproc
pg_proc.oid
Функция сериализации (ноль, если ее нет)

aggdeserialfn
regproc
pg_proc.oid
Функция десериализации (ноль, если ее нет)

aggmtransfn
regproc
pg_proc.oid
Функция прямого перехода для режима движущегося агрегата (ноль, если ее нет)

agminvtransfn
regproc
pg_proc.oid
Функция обратного перехода для режима движущегося агрегата (ноль, если ее нет)

aggmfinalfn
regproc
pg_proc.oid
Функция завершения для режима движущегося агрегата (ноль, если ее нет)

aggfinalextra
bool

При значении true в aggfinalfn передаются дополнительные фиктивные аргументы

aggmfinalextra
bool

При значении true в aggmfinalfn передаются дополнительные фиктивные аргументы

aggfinalmodify
char

Изменяет ли aggfinalfn значение состояния перехода: r, если состояние доступно только для чтения (read), s, если aggtransfn не может быть применена после aggfinalfn, или w, если состояние перезаписывается (write).

aggmfinalmodify
char

Сходно с aggfinalmodify, но для самого aggmfinalfn

aggsortop
oid
pg_operator.oid
Связанный оператор сортировки (ноль, если его нет)

aggtranstype
oid
pg_type.oid
Тип данных внутреннего перехода (состояния) агрегатной функции

aggtransspace
int4

Приблизительный средний размер (в байтах) данных о состоянии перехода или 0 для использования оценки по умолчанию

aggmtranstype
oid
pg_type.oid
Тип данных внутреннего перехода (состояния) агрегатной функции для режима движущегося агрегата (ноль, если его нет)

aggmtransspace
int4

Приблизительный средний размер (в байтах) данных о состоянии перехода для режима движущегося агрегата или 0 для использования оценки по умолчанию

agginitval
text

Начальное значение переходного состояния. Это текстовое поле, содержащее начальное значение во внешнем строковом представлении. Если это поле имеет значение NULL, то значение состояния перехода начнется с NULL.

aggminitval
text

Начальное значение переходного состояния для режима движущегося агрегата. Это текстовое поле, содержащее начальное значение во внешнем строковом представлении. Если это поле имеет значение NULL, то значение состояния перехода начнется с NULL.

Новые агрегатные функции регистрируются с помощью команды CREATE AGGREGATE. Дополнительную информацию о написании агрегатных функций и значении функций перехода см. в разделе Пользовательские агрегаты.

pg_am

В каталоге pg_am хранится информация о методах доступа к отношениям. Для каждого метода доступа, поддерживаемого системой, существует одна строка. В настоящее время только таблицы и индексы имеют методы доступа. Требования к методам доступа к таблицам и индексам подробно рассматриваются в главах Интерфейс доступа к таблице и Интерфейс доступа по индексу соответственно.

Таблица 3. Столбцы pg_am

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
amname | name |  | Имя метода доступа
amhandler | regproc | pg_proc.oid | OID функции обработчика, ответственного за предоставление информации о методе доступа
amtype | char |  | t = таблица (table) (включая материализованные представления), i = индекс (index).

Примечание
Данные о свойствах индексных методов доступа напрямую видны только на уровне кода С. Однако запросы SQL могут проверять эти свойства с помощью функции pg_index_column_has_property() и связанных функций; см. таблицу Информационные функции системного каталога.

pg_amop

В каталоге pg_amop хранится информация об операторах, связанных с семействами операторов метода доступа. Для каждого оператора, который является членом семейства операторов, существует одна строка. Членом семейства может быть либо оператор поиска, либо оператор упорядочивания. Оператор может принадлежать нескольким семействам, но в пределах одного семейства не может находиться более чем в одной позиции поиска или позиции упорядочивания. (Допустимо, хотя и маловероятно, что оператор будет использоваться как для поиска, так и для сравнения.)

Таблица 4. Столбцы pg_amop

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
amopfamily | oid | pg_opfamily.oid | Семейство операторов, для которых эта запись предназначена
amoplefttype | oid | pg_type.oid | Тип данных левого операнда оператора
amoprighttype | oid | pg_type.oid | Тип данных правого операнда оператора
amopstrategy | int2 |  | Номер стратегии оператора
amoppurpose | char |  | Назначение оператора: s для поиска (search), о для упорядочивания (ordering)
amopopr | oid | pg_operator.oid | OID оператора
amopmethod | oid | pg_am.oid | Метод доступа по индексу, для которого предназначено семейство операторов
amopsortfamily | oid | pg_opfamily.oid | Семейство операторов B-дерева, в соответствии с которым данный оператор сортирует, если это оператор упорядочивания; 0, если это оператор поиска

Запись оператора «поиска» указывает, что индекс этого семейства операторов можно использовать для поиска всех строк, удовлетворяющих условию WHERE столбец_индекса оператор константа. Очевидно, что такой оператор должен возвращать тип boolean, а тип его левого операнда должен соответствовать типу данных столбца индекса.

Запись оператора «упорядочивания» указывает, что индекс этого семейства операторов можно отсканировать для возврата строк в порядке, представленном в предложении ORDER BY столбец_индекса оператор константа. Такой оператор может возвращать любой сортируемый тип данных, хотя, опять же, тип его левого операнда должен соответствовать типу данных столбца индекса. Точная семантика ORDER BY определяется по столбцу amopsortfamily, который должен указывать на семейство операторов B-дерева для типа, возвращаемого оператором.

Примечание
В настоящее время предполагается, что порядок сортировки для оператора упорядочивания является значением по умолчанию для указанного семейства операторов, т. е. ASC NULLS LAST. Возможно, когда-нибудь для придания большей гибкости будут добавлены дополнительные столбцы, чтобы явно указать параметры сортировки.

Поле amopmethod в записи оператора должно соответствовать полю opfmethod содержащего его семейства операторов (в данном случае включение amopmethod является преднамеренной денормализацией структуры каталога из соображений производительности). Кроме того, поля amoplefttype и amoprighttype должны соответствовать полям oprleft и oprright ссылочной записи в pg_operator.

pg_amproc

В каталоге pg_amproc хранится информация о функциях, поддерживаемых оператором и связанных с семействами операторов метода доступа. Для каждой вспомогательной функции, принадлежащей семейству операторов, существует одна строка.

Таблица 5. Столбцы pg_amproc

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
amprocfamily | oid | pg_opfamily.oid | Семейство операторов, для которых предназначается эта запись
amproclefttype | oid | pg_type.oid | Тип данных левого операнда связанного оператора
amprocrighttype | oid | pg_type.oid | Тип данных правого операнда связанного оператора
amprocnum | int2 |  | Номер вспомогательной функции
amproc | regproc | pg_proc.oid | OID этой функции

Обычная интерпретация полей amproclefttype и amprocrighttype состоит в том, что они определяют типы левого и правого операнда оператора (или операторов), который поддерживает определенная вспомогательная функция. Для некоторых методов доступа они соответствуют типу (или типам) входных данных самой вспомогательной функции, для других — нет. Существует такое понятие, как «стандартные» вспомогательные функции для индекса. Это функции, у которых amproclefttype и amprocrighttype равны opcintype класса операторов индекса.

pg_attrdef

В каталоге pg_attrdef хранятся значения столбца по умолчанию. Основная информация о столбцах находится в каталоге pg_attribute. В этом каталоге хранятся только те столбцы, для которых было явно установлено значение по умолчанию.

Таблица 6. Столбцы pg_attrdef

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
adrelid | oid | pg_class.oid | Таблица, которой принадлежит этот столбец
adnum | int2 | pg_attribute.attnum | Номер столбца
adbin | pg_node_tree |  | Значение столбца по умолчанию в формате nodeToString(). Чтобы преобразовать его в выражение SQL, воспользуйтесь функцией pg_get_expr(adbin, adrelid).

pg_attribute

В каталоге pg_attribute хранится информация о столбцах таблицы. Для каждого столбца в каждой таблице базы данных будет храниться ровно одна строка pg_attribute. (Также будут храниться записи атрибутов для индексов и всех объектов, которые имеют записи в pg_class.)

Термин «атрибут» равнозначен столбцу и используется по историческим причинам.

Таблица 7. Столбцы pg_attribute

Имя
Тип
Ссылки
Описание

attrelid
oid
pg_class.oid
Таблица, которой принадлежит этот столбец

attname
name

Имя столбца

atttypid
oid
pg_type.oid
Тип данных этого столбца (ноль для удаленного столбца)

attstattarget
int4

attstattarget управляет уровнем детализации статистики, собираемой для данного столбца при помощи команды ANALYZE. Нулевое значение указывает на то, что статистика не должна собираться. Отрицательное значение указывает на использование системного ориентира статистики по умолчанию. Точное значение положительных значений зависит от типа данных. Для скалярных типов данных attstattarget является как целевым числом «наиболее распространенных значений» для сбора, так и целевым числом создаваемых интервалов гистограммы.

attlen
int2

Копия pg_type.typlen типа этого столбца

attnum
int2

Номер столбца. Обычные столбцы нумеруются по возрастанию, начиная с 1. Системные столбцы, такие как ctid, имеют отрицательные номера (произвольные).

attndims
int4

Число размерностей, если столбец имеет тип массива; в противном случае ноль. (В настоящее время число размерностей массива не регулируется, поэтому любое ненулевое значение означает «это массив».)

attcacheof
int4

Всегда -1 в хранилище, но при загрузке в дескриптор строки в памяти это поле может быть изменено в целях кэширования смещения атрибута в строке

atttypmod
int4

В поле atttypmod записываются специфичные для определенного типа данные, предоставляемые при создании таблицы (например, максимальную длину столбца varchar). Эти данные передаются функциям ввода определенного типа и функциям ограничения длины. Для типов, которые не нуждаются в atttypmod, значение обычно равно -1.

attbyval
bool

Копия pg_type.typbyval типа этого столбца

attalign
char

Копия pg_type.typalign типа этого столбца

attstorage
char

Обычно это копия pg_type.typstorage типа этого столбца. Для типов данных, поддерживающих TOAST, этот атрибут можно изменить после создания столбца в целях управления политикой хранения.

attcompression
char

Текущий метод сжатия столбца. Обычно задано значение '\0', то есть действует метод, установленный по умолчанию (см. default_toast_compression). В других случаях: если указано значение 'p', используется алгоритм сжатия pglz, если указано 'l' — алгоритм LZ4. Это значение игнорируется, если attstorage не поддерживает сжатие.

attnotnull
bool

Представляет ограничение NOT NULL.

atthasdef
bool

Этот столбец имеет выражение по умолчанию или генерирующее выражение; в этом случае в каталоге pg_attrdef, который фактически определяет данное выражение, появится соответствующая запись. (Определить, какое это выражение: по умолчанию или генерирующее, можно, проверив attgenerated.)

atthasmissing
bool

Этот столбец имеет значение, которое используется, когда столбец полностью отсутствует в строке (это происходит, когда столбец добавляется с неизменяющимся значением DEFAULT после создания строки). Используемое фактическое значение хранится в столбце attmissingval.

attidentity
char

Пустой символ ('') указывает, что это не столбец идентификации. a = значение генерируется всегда (always), d = значение генерируется по умолчанию (by default).

attgenerated
char

Пустой символ ('') указывает, что это не генерируемый столбец. s = генерируемое значение хранится (stored). (В будущем могут быть добавлены и другие значения.)

attisdropped
bool

Этот столбец был удален и стал нерабочим. Удаленный столбец все еще физически присутствует в таблице, но игнорируется синтаксическим анализатором и поэтому не может быть доступен через SQL.

attislocal
bool

Этот столбец определяется локально в отношении. Обратите внимание, что столбец может быть локально определен и унаследован одновременно.

attinhcount
int4

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

attcollation
oid
pg_collation.oid
Заданные параметры сортировки столбца или ноль, если столбец не относится к сортируемому типу данных.

attacl
aclitem[]

Права доступа на уровне столбца, если они были предоставлены специально для этого столбца

attoptions
text[]

Параметры уровня атрибута в виде строк «ключ=значение»

attfdwoptions
text[]

Параметры внешней оболочки данных уровня атрибута в виде строк «ключ=значение»

attmissingval
anyarray

Этот столбец имеет массив из одного элемента, содержащий значение, используемое, когда столбец полностью отсутствует в строке (это происходит, когда столбец добавляется с неизменяющимся значением DEFAULT после создания строки). Значение используется, только если atthasmissing равен true. Если значение отсутствует, столбец содержит NULL.

В записи удаленного столбца в pg_attribute поле atttypid сбрасывается до нуля, но поле attlen и остальные поля, скопированные из pg_type, все еще действительны. Это необходимо, чтобы справиться с ситуацией, когда после удаления столбца удаляется и его тип данных, и поэтому соответствующей строки pg_type тоже больше нет. Поле attlen и остальные поля можно использовать для интерпретации содержимого строки таблицы.

pg_authid

В каталоге pg_authid содержится информация об идентификаторах авторизации базы данных (ролях). Роль включает в себя понятия «пользователи» и «группы». Пользователь — это просто роль с установленным флагом rolcanlogin. Любая роль (с rolcanlogin или без него) может иметь другие роли в качестве членов; см. pg_auth_members.

Поскольку этот каталог содержит пароли, он не должен быть общедоступным. Общедоступным представлением на базе pg_authid является pg_roles, в котором очищено поле пароля.

В главе Роли в базе данных содержатся подробные сведения об управлении пользователями и их правами.

Поскольку аутентификация пользователей проводится для всего кластера, pg_authid является общим каталогом для всех баз данных кластера: существует только одна копия pg_authid на кластер, а не собственная для каждой базы данных.

Таблица 8. Столбцы pg_authid

Имя | Тип | Описание
oid | oid | Идентификатор строки
rolname | name | Имя роли
rolsuper | bool | Роль имеет права суперпользователя
rolinherit | bool | Роль автоматически наследует права ролей, членом которых является
rolcreaterole | bool | Роль может создавать другие роли
rolcreatedb | bool | Роль может создавать базы данных
rolcanlogin | bool | Роль может войти в систему. То есть эта роль может быть задана в качестве начального идентификатора авторизации сеанса
rolreplication | bool | Роль является ролью репликации. Роль репликации может инициировать подключения репликации и создавать и удалять слоты репликации.
rolbypassrls | bool | Роль обходит политику защиты уровня строки; дополнительную информацию см. в разделе Политики защиты строк.
rolconnlimit | int4 | Для ролей, которые могут войти в систему, задает максимальное число одновременных подключений, которые может создать эта роль. -1 означает отсутствие ограничений.
rolpassword | text | Пароль (возможно зашифрованный); NULL, если он не задан. Формат зависит от используемой формы шифрования.
rolvaliduntil | timestamptz | Срок действия пароля (используется только для аутентификации по паролю); NULL, если срок действия не ограничен

Для пароля, зашифрованного MD5, столбец rolpassword будет начинаться со строки md5, за которой следует 32-символьный шестнадцатеричный хеш MD5. Хеш MD5 будет содержать пароль пользователя, связанный с его именем. Например, если у пользователя joe пароль xyzzy, QHB будет хранить хеш md5 строки xyzzyjoe.

Если пароль зашифрован с помощью SCRAM-SHA-256, он имеет следующий формат:

SCRAM-SHA-256$<число итераций>:<соль>$<Сохраненный_Ключ>:<Ключ_Сервера>

где соль, Сохраненный_Ключ и Ключ_Сервера кодируются в формате Base64. Этот формат совпадает с форматом, указанным в документе RFC 5803.

Пароль, который не соответствует ни одному из этих форматов, считается незашифрованным.

pg_auth_members

В каталоге pg_auth_members представлены отношения членства между ролями. Допускается любой нециклический набор отношений.

Поскольку авторизация пользователей является кластерной, pg_auth_members является общим каталогом для всех баз данных кластера: существует только одна копия pg_auth_members на кластер, а не для каждой базы данных.

Таблица 9. Столбцы pg_auth_members

Имя | Тип | Ссылки | Описание
roleid | oid | pg_authid.oid | Идентификатор роли, имеющей члена
member | oid | pg_authid.oid | Идентификатор роли, которая является членом roleid
grantor | oid | pg_authid.oid | Идентификатор роли, предоставившей это членство
admin_option | bool |  | True, если member может предоставить членство в roleid другим ролям

pg_cast

В каталоге pg_cast хранятся как встроенные, так и определяемые пользователем пути преобразования типов данных.

Следует отметить, что pg_cast содержит не все преобразования типов, которые система умеет выполнять, а только те, которые нельзя вывести из некоторого общего правила. Например, приведение между доменом и его базовым типом в pg_cast явно не представлено. Еще одним важным исключением является то, что в каталоге pg_cast явно не представлены «автоматические приведения преобразования ввода/вывода», которые выполняются, используя для преобразования в/из типа text или других строковых типов собственные функции ввода/вывода типа данных.

Таблица 10. Столбцы pg_cast

Имя
Тип
Ссылки
Описание

oid
oid

Идентификатор строки

castsource
oid
pg_type.oid
OID исходного типа данных

casttarget
oid
pg_type.oid
OID целевого типа данных

castfunc
oid
pg_proc.oid
OID функции, которая будет использоваться для выполнения этого приведения. Ноль, если для данного метода приведения функция не требуется.

castcontext
char

Указывает, в каких контекстах приведение может быть вызвано. e означает только явное приведение (explicit) с использованием синтаксиса CAST или ::. а означает неявное и явное приведение при присвоении (assignment) целевому столбцу. i означает неявное (implicitly) приведение в выражениях, а также в других случаях.

castmethod
char

Указывает, как выполняется приведение. f означает, что используется функция (function), указанная в столбце castfunc. i означает, что используются функции ввода/вывода (input/output). b означает, что типы являются двоично-совместимыми (binary-coercible), поэтому преобразование не требуется.

Функции приведения, перечисленные в pg_cast, должны всегда принимать исходный тип приведения в качестве типа первого аргумента и возвращать результат, имеющий целевой тип приведения. Функция приведения может иметь до трех аргументов. Второй аргумент, если он присутствует, должен иметь тип integer; он получает модификатор типа, связанный с целевым типом, или -1, если модификатора нет. Третий аргумент, если он присутствует, должен иметь тип boolean; он получает значение true, если приведение является явным, в противном случае — false.

Есть возможность создать в pg_cast запись, в которой исходные и целевые типы совпадают, если связанная функция принимает более одного аргумента. Такие функции представляют собой «функции приведения длины», которые добиваются того, чтобы значения типа подходили определенному значению модификатора типа.

Когда запись в pg_cast имеет различные исходный и целевой типы, а функция принимает более одного аргумента, эта запись представляет собой преобразование из одного типа в другой и применение приведения длины за один шаг. Если такая запись недоступна, приведение к типу, использующему модификатор типа, включает два шага: один для преобразования между типами данных и второй для применения модификатора.

pg_class

Каталог pg_class содержит информацию о таблицах и практически всех остальных объектах, которые имеют столбцы или иным образом похожи на таблицу. Сюда входят индексы (см. также pg_index), последовательности (см. также pg_sequence), представления, материализованные представления, составные типы и таблицы TOAST (см. описание столбца relkind). Ниже мы говорим об «отношениях», подразумевая все эти виды объектов. Не все столбцы в каталоге значимы для всех типов отношений.

Таблица 11. Столбцы pg_class

Имя
Тип
Ссылки
Описание

oid
oid

Идентификатор строки

relname
name

Имя таблицы, индекса, представления и т. д.

relnamespace
oid
pg_namespace.oid
OID пространства имен, содержащего это отношение

reltype
oid
pg_type.oid
OID типа данных, который соответствует типу строки этой таблицы, если таковой имеется; ноль для индексов, у которых нет записи в pg_type

reloftype
oid
pg_type.oid
OID базового составного типа для типизированных таблиц; ноль для всех остальных отношений

relowner
oid
pg_authid.oid
Владелец отношения

relam
oid
pg_am.oid
Если это таблица или индекс, то используемый метод доступа (куча, B-дерево, хеш и т. д.); в иных случаях ноль (ноль проставляется для последовательностей, а также для нехранимых отношений, например представлений)

relfilenode
oid

Имя файла на диске для этого отношения; ноль означает, что это «сопоставленное» отношение, для которого имя файла на диске определяется состоянием на нижнем уровне

reltablespace
oid
pg_tablespace.oid
Табличное пространство, в котором хранится это отношение. Если ноль, подразумевается табличное пространство базы данных по умолчанию. (Не имеет смысла, если отношение не имеет файла на диске.)

relpages
int4

Размер представления этой таблицы на диске в страницах (в размере BLCKSZ). Это только приблизительная оценка, используемая планировщиком. Она обновляется с помощью команд VACUUM, ANALYZE и нескольких команд DDL, таких как CREATE INDEX.

reltuples
float4

Количество активных строк в таблице. Это только приблизительная оценка, используемая планировщиком. Она обновляется с помощью команд VACUUM, ANALYZE и нескольких команд DDL, таких как CREATE INDEX. Если таблица никогда не подвергалась очистке или анализу, поле reltuples содержит -1, указывая, что количество строк неизвестно.

relallvisible
int4

Количество страниц, отмеченных как полностью видимые на карте видимости таблицы. Это только приблизительная оценка, используемая планировщиком. Она обновляется с помощью команд VACUUM, ANALYZE и нескольких команд DDL, таких как CREATE INDEX.

reltoastrelid
oid
pg_class.oid
OID таблицы TOAST, связанной с этой таблицей; ноль, если такой таблицы нет. Таблица TOAST хранит большие атрибуты «вынесенными» во вторичную таблицу.

relhasindex
bool

True, если это таблица и она имеет (или недавно имела) какие-либо индексы

relisshared
bool

True, если эта таблица является общей для всех баз данных в кластере. Только определенные системные каталоги (такие как pg_database) являются общими.

relpersistence
char

p = постоянная таблица (permanent), u = незарегистрированная таблица (unlogged), t = временная таблица (temporary)

relkind
char

r = Обычная таблица (regular), i = индекс (index), s = последовательность (sequence), t = таблица TOAST, v = представление (view), m = материализованное представление (materialized), c = составной тип (composite), f = сторонняя таблица (foreign), p = партиционированная таблица (partitioned), I = партиционированный индекс (index)

relnatts
int2

Количество пользовательских столбцов в отношении (системные столбцы не учитываются). Должно быть такое же количество соответствующих записей в pg_attribute. См. также pg_attribute.attnum.

relchecks
int2

Количество проверочных ограничений (CHECK) у таблицы; см. каталог pg_constraint

relhasrules
bool

True, если таблица имеет (или когда-то имела) правила; см. каталог pg_rewrite

relhastriggers
bool

True, если таблица имеет (или когда-то имела) триггеры; см. каталог pg_trigger

relhassubclass
bool

True, если таблица или индекс имеют (или когда-то имели) потомков в результате наследования

relrowsecurity
bool

True, если в таблице включена защита на уровне строк; см. каталог pg_policy

relforcerowsecurity
bool

True, если защита уровня строк (если она включена) будет также применяться и к владельцу таблицы; см. каталог pg_policy

relispopulated
bool

True, если отношение заполнено (это справедливо для всех отношений, кроме некоторых материализованных представлений)

relreplident
char

Столбцы, используемые для формирования «идентификации реплики» для строк: d = по умолчанию (первичный ключ, если есть), n = ничего, f = все столбцы, i = индекс со свойством indisreplident (если ранее использовавшийся индекс был удален, действует так же, как n)

relispartition
bool

True, если таблица или индекс является партицией

relrewrite
oid
pg_class.oid
Для новых отношений, записываемых во время операции DDL, которая требует перезаписи таблицы, это поле содержит OID исходного отношения; в противном случае ноль. Это состояние видимо только внутри; данное поле всегда должно содержать 0 для видимого пользователю отношения.

relfrozenxid
xid

В этой таблице все идентификаторы транзакций, предшествующие данному, были заменены на постоянный («замороженный») идентификатор транзакции. Это используется для отслеживания того, нужно ли очистить таблицу для предотвращения зацикливания идентификатора транзакции или уменьшения размера pg_xact. Ноль (InvalidTransactionId), если данное отношение не является таблицей.

relminmxid
xid

В этой таблице все идентификаторы мультитранзакций, предшествующие данному, были заменены на постоянный («замороженный») идентификатор транзакции. Это используется для отслеживания того, нужно ли очистить таблицу для предотвращения зацикливания идентификатора мультитранзакции или уменьшения размера pg_multixact. Ноль (InvalidMultiXactId), если данное отношение не является таблицей.

relacl
aclitem[]

Права доступа; дополнительную информацию см. в разделе Права

reloptions
text[]

Параметры, специфичные для методов доступа, в виде строк «ключ=значение»

relpartbound
pg_node_tree

Если таблица является партицией (см. relispartition), внутреннее представление границ партиции

Несколько логических флагов в каталоге pg_class поддерживаются не строго: они гарантированно будут установлены в true, если это правильное состояние, но не сбрасываются на false сразу же, как только условие поменяется. Например, флаг relhasindex устанавливается с помощью команды CREATE INDEX, но никогда не сбрасывается с помощью DROP INDEX. Вместо этого relhasindex сбрасывает команда VACUUM, если обнаруживает, что таблица не имеет индексов. Этот механизм позволяет избежать состояния гонки и улучшает параллельное использование.

pg_collation

В каталоге pg_collation описываются доступные правила сортировки, которые в сущности представляют собой сопоставления между именем SQL и категориями локали операционной системы. Дополнительную информацию см. в разделе Поддержка правил сортировки.

Таблица 12. Столбцы pg_collation

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
collname | name |  | Имя правила сортировки (уникальное для каждого пространства имен и кодировки)
collnamespace | oid | pg_namespace.oid | OID пространства имен, содержащего это правило сортировки
collowner | oid | pg_authid.oid | Владелец правила сортировки
collprovider | char |  | Поставщик правила сортировки: d = установленный в базе данных по умолчанию (default), c = libc, i = icu
collisdeterministic | bool |  | Является ли правило сортировки детерминированным?
collencoding | int4 |  | Кодировка, в которой применимо правило сортировки, или -1, если оно работает для любой кодировки
collcollate | name |  | LC_COLLATE для этого объекта сортировки
collctype | name |  | LC_CTYPE для этого объекта сортировки
collversion | text |  | Версия правила сортировки для конкретного поставщика. Записывается при создании правила сортировки, а затем проверяется при его использовании для обнаружения изменений в его определении, которые могут привести к повреждению данных.

Обратите внимание, что уникальными ключами в этом каталоге являются (collname, collencoding, collnamespace), а не только (collname, collnamespace). QHB обычно игнорирует все правила сортировки, которые не имеют collencoding равным либо кодировке текущей базы данных, либо -1; кроме того, создание новых записей с тем же именем, что и запись с collencoding = -1, запрещено. Поэтому для определения правила сортировки достаточно использовать полное имя SQL (схема.имя), даже если оно не является уникальным согласно определению каталога. Такое определение каталога обусловлено тем, что во время инициализации кластера qhb_bootstrap заполняет его записями для всех доступных в системе локалей, поэтому каталог должен иметь возможность хранить записи для всех кодировок, которые могут когда-либо использоваться в кластере.

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

pg_constraint

В каталоге pg_constraint хранятся проверочные и исключающие ограничения, ограничения уникальности, первичного и внешнего ключей для таблиц. (Ограничения столбцов не обрабатываются отдельно. Каждое ограничение столбца эквивалентно некоторому ограничению таблицы.) Ограничения NOT NULL представлены не здесь, а в каталоге pg_attribute.

Определяемые пользователем триггеры ограничений (созданные с помощью команды CREATE CONSTRAINT TRIGGER) также приводят к появлению записи в этой таблице.

Также здесь хранятся проверочные ограничения для доменов.

Таблица 13. Столбцы pg_constraint

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
conname | name |  | Имя ограничения (не обязательно уникальное!)
connamespace | oid | pg_namespace.oid | OID пространства имен, содержащего это ограничение
contype | char |  | c = проверочное ограничение (check), f = ограничение внешнего ключа (foreign), p = ограничение первичного ключа (primary), u = ограничение уникальности (unique), t = триггер ограничения (trigger), x = исключающее ограничение (exclusion)
condeferrable | bool |  | Является ли ограничение откладываемым?
condeferred | bool |  | Является ли ограничение отложенным по умолчанию?
convalidated | bool |  | Было ли это ограничение проверено? В настоящее время false может быть только для внешних ключей и проверочных ограничений (CHECK)
conrelid | oid | pg_class.oid | Таблица, на которую наложено это ограничение; ноль, если это не ограничение таблицы
contypid | oid | pg_type.oid | Домен, на который наложено это ограничение; ноль, если это не ограничение домена
conindid | oid | pg_class.oid | Индекс, поддерживающий это ограничение, если это ограничение уникальности, первичного ключа, внешнего ключа или исключающее ограничение; в противном случае ноль
conparentid | oid | pg_constraint.oid | Соответствующее ограничение в родительской партиционированной таблице, если это ограничение для партиции; в противном случае ноль
confrelid | oid | pg_class.oid | Если это внешний ключ, то ссылочная таблица (на которую ссылается ключ); в противном случае ноль
confupdtype | char |  | Код действия при обновлении внешнего ключа: a = нет действия (no action), r = ограничить (restrict), c = каскадное действие (cascade), n = установить значение NULL, d = поведение по умолчанию (default)
confdeltype | char |  | Код действия при удалении внешнего ключа: a = нет действия, r = ограничить, c = каскадное действие, n = установить значение NULL, d = поведение по умолчанию
confmatchtype | char |  | Тип соответствия для внешнего ключа: f = полный (full), p = частичный (partial), s = простой (simple)
conislocal | bool |  | Это ограничение определяется локально для данного отношения. Обратите внимание, что ограничение может быть локально определено и унаследовано одновременно.
coninhcount | int4 |  | Число предков прямого наследования от ограничения. Ограничение с ненулевым числом предков не может быть удалено или переименовано.
connoinherit | bool |  | Это ограничение определено локально для данного отношения и является ненаследуемым.
conkey | int2[] | pg_attribute.attnum | Для ограничения таблицы (включая внешние ключи, но не триггеры ограничений) — список столбцов с ограничением
confkey | int2[] | pg_attribute.attnum | Для внешнего ключа — список ссылочных столбцов
conpfeqop | oid[] | pg_operator.oid | Для внешнего ключа — список операторов равенства для сравнений PK = FK
conppeqop | oid[] | pg_operator.oid | Для внешнего ключа — список операторов равенства для сравнений PK = PK
conffeqop | oid[] | pg_operator.oid | Для внешнего ключа — список операторов равенства для сравнений FK = FK
conexclop | oid[] | pg_operator.oid | Для исключающего ограничения — список операторов исключения по столбцам
conbin | pg_node_tree |  | Для проверочного ограничения — внутреннее представление выражения. (Чтобы извлечь определение проверочного ограничения, рекомендуется использовать pg_get_constraintdef().)

В случае исключающего ограничения столбец conkey полезен только для элементов ограничения, которые являются простыми ссылками на столбец. Для других случаев в conkey задается ноль, и чтобы обнаружить выражение, определяющее ограничение, необходимо просмотреть связанный индекс. (Следовательно, поле conkey имеет то же содержимое, что и pg_index.indkey для индекса.)

Примечание
Поле pg_class.relchecks необходимо согласовать с количеством записей проверочного ограничения, имеющихся в этой таблице для каждого отношения.

pg_conversion

В каталоге pg_conversion описаны функции преобразования кодировки. Дополнительную информацию см. в описании команды CREATE CONVERSION.

Таблица 14. Столбцы pg_conversion

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
conname | name |  | Имя преобразования (уникальное в пределах пространства имен)
connamespace | oid | pg_namespace.oid | OID пространства имен, содержащего это преобразование
conowner | oid | pg_authid.oid | Владелец преобразования
conforencoding | int4 |  | Идентификатор исходной кодировки
contoencoding | int4 |  | Идентификатор целевой кодировки
conproc | regproc | pg_proc.oid | Функция преобразования
condefault | bool |  | True, если это преобразование по умолчанию

pg_database

В каталоге pg_database хранится информация о доступных базах данных. Базы данных создаются с помощью команды CREATE DATABASE. Дополнительную информацию о значении некоторых параметров см. в главе Управление базами данных.

В отличие от большинства системных каталогов, pg_database является общим для всех баз данных кластера: существует только одна копия pg_database на кластер, а не по одной на каждую базу данных.

Таблица 15. Столбцы pg_database

Имя
Тип
Ссылки
Описание

oid
oid

Идентификатор строки

datname
name

Имя базы данных

datdba
oid
pg_authid.oid
Владелец базы данных — как правило, пользователь, который ее создал

encoding
int4

Кодировка символов для этой базы данных (при помощи pg_encoding_to_char() можно перевести этот номер в имя кодировки)

datcollate
name

LC_COLLATE для этой базы данных

datctype
name

LC_CTYPE для этой базы данных

datistemplate
bool

Если true, то эту базу данных может клонировать любой пользователь, обладающий правом CREATEDB; если false, то клонировать ее могут только суперпользователи или владелец этой базы данных.

datallowconn
bool

Если false, то никто не может подключиться к этой базе данных. Используется для защиты базы данных template0 от изменений.

datconnlimit
int4

Задает максимальное число одновременных подключений, которые могут быть выполнены для этой базы данных. -1 означает отсутствие ограничений.

datlastsysoid
oid

Последний системный OID в базе данных; полезен, в частности, для qhb_dump

datfrozenxid
xid

В этой базе данных все идентификаторы транзакций, предшествующие данному, были заменены на постоянный («замороженный») идентификатор транзакции. Это используется для отслеживания того, нужно ли очистить таблицу для предотвращения зацикливания идентификатора транзакции или уменьшения размера pg_xact. Это минимальные значения pg_class.relfrozenxid для всех таблиц.

datminmxid
xid

В этой базе данных все идентификаторы мультитранзакций, предшествующие данному, были заменены на другой идентификатор транзакции. Это используется для отслеживания того, нужно ли очистить таблицу для предотвращения зацикливания идентификатора мультитранзакции или уменьшения размера pg_multixact. Это минимальные значения pg_class.relminmxid для всех таблиц.

dattablespace
oid
pg_tablespace.oid
Табличное пространство по умолчанию для этой базы данных. В пределах этой базы данных все таблицы, у которых pg_class.reltablespace равно нулю, будут храниться в этом табличном пространстве (в частности, там будут находиться все частные системные каталоги).

datacl
aclitem[]

Права доступа; дополнительную информацию см. в разделе Права

pg_db_role_setting

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

В отличие от большинства системных каталогов, pg_db_role_setting является общим для всех баз данных кластера: существует только одна копия pg_db_role_setting на кластер, а не по одной на каждую базу данных.

Таблица 16. Столбцы pg_db_role_setting

Имя | Тип | Ссылки | Описание
setdatabase | oid | pg_database.oid | OID базы данных, к которой применяется этот параметр, или ноль, если он не относится к конкретной базе данных
setrole | oid | pg_authid.oid | OID роли, к которой применяется этот параметр, или ноль, если он не относится к конкретной роли
setconfig | text[] |  | Значения по умолчанию для переменных конфигурации времени выполнения

pg_default_acl

В каталоге pg_default_acl хранятся права, изначально присваиваемые только что созданным объектам.

Таблица 17. Столбцы pg_default_acl

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
defaclrole | oid | pg_authid.oid | OID роли, связанной с этой записью
defaclnamespace | oid | pg_namespace.oid | OID пространства имен, связанного с этой записью, или ноль, если такового нет
defaclobjtype | char |  | Тип объекта, для которого эта запись предназначена: r = отношение (таблица, представление), S = последовательность, f = функция, T = тип, n = схема
defaclacl | aclitem[] |  | Права доступа, которые этот тип объекта должен иметь при создании

Запись в pg_default_acl показывает начальные права, которые будут присвоены объекту, принадлежащему указанному пользователю. В настоящее время существует два типа записей: «глобальные» записи с defaclnamespace = 0 и записи «схемы», ссылающиеся на конкретную схему. Если глобальная запись присутствует, то она переопределяет обычные фиксированные права по умолчанию для типа объекта. Запись схемы, если она присутствует, представляет права, которые будут добавлены к глобальным или фиксированным правам по умолчанию.

Обратите внимание, что если запись ACL в другом каталоге имеет значение NULL, то она используется для представления фиксированных прав по умолчанию для ее объекта, а не тех, что могут находиться в pg_default_acl в данный момент. Каталог pg_default_acl учитывается только при создании объекта.

pg_depend

В каталоге pg_depend записываются отношения зависимости между объектами базы данных. Эта информация позволяет командам DROP находить, какие другие объекты должны быть удалены с помощью DROP CASCADE, или предотвращать удаление в случае DROP RESTRICT.

См. также описание каталога pg_shdepend, который выполняет аналогичную функцию для зависимостей, включающих объекты, совместно используемые в кластере баз данных.

Таблица 18. Столбцы pg_depend

Имя | Тип | Ссылки | Описание
classid | oid | pg_class.oid | OID системного каталога, в котором находится зависимый объект
objid | oid | OID любого столбца | OID конкретного зависимого объекта
objsubid | int4 |  | Для столбца таблицы это номер столбца (поля objid и classid ссылаются на саму таблицу). Для всех других типов объектов этот столбец содержит ноль.
refclassid | oid | pg_class.oid | OID системного каталога, в котором находится ссылочный объект
refobjid | oid | OID любого столбца | OID конкретного ссылочного объекта
refobjsubid | int4 |  | Для столбца таблицы это номер столбца (refobjid и refclassid ссылаются на саму таблицу). Для всех других типов объектов этот столбец содержит ноль.
deptype | char |  | Код, определяющий конкретную семантику этого отношения зависимости; см. текст ниже

Во всех случаях запись в pg_depend указывает, что ссылочный объект не может быть удален без удаления зависимого объекта. Тем не менее, есть несколько подвидов зависимостей, задаваемых в поле deptype:

DEPENDENCY_NORMAL (n)
Обычное отношение между раздельно созданными объектами. Зависимый объект можно удалить, не затрагивая ссылочный объект. Последний может быть удален только путем указания CASCADE, и в этом случае зависимый объект также удаляется. Пример: столбец таблицы имеет нормальную зависимость от своего типа данных.

DEPENDENCY_AUTO (a)
Зависимый объект может быть удален отдельно от ссылочного и должен быть автоматически удален (вне зависимости от режима RESTRICT или CASCADE). Пример: именованное ограничение для таблицы становится автоматически зависимым от таблицы, поэтому оно исчезнет автоматически, если таблица будет удалена.

DEPENDENCY_INTERNAL (i)
Зависимый объект был создан как часть ссылочного и на самом деле является только частью его внутренней реализации. Удаление такого зависимого объекта напрямую при помощи DROP будет запрещено (вместо этого пользователю будет выдано сообщение о том, что необходимо вызвать DROP для ссылочного объекта). Удаление ссылочного объекта приведет к автоматическому удалению зависимого объекта без учета того, указывается ли CASCADE при удалении или нет. Если зависимый объект должен быть удален из-за зависимости от какого-либо другого удаляемого объекта, его удаление преобразуется в удаление ссылочного объекта, поэтому зависимости NORMAL и AUTO зависимого объекта ведут себя так же, как и зависимости ссылочного объекта. Пример: правило ON SELECT для представления становится внутренне зависимым от этого представления, предотвращая его удаление, пока то остается в системе. Зависимости для этого правила (например, таблицы, на которые оно ссылается) действуют так, как если бы они были зависимостями представления.

DEPENDENCY_PARTITION_PRI (P)
DEPENDENCY_PARTITION_SEC (S)
Зависимый объект был создан как часть создания ссылочного объекта и на самом деле является только частью его внутренней реализации; однако, в отличие от INTERNAL, существует более одного такого ссылочного объекта. Зависимый объект не должен удаляться, пока не будет удален хотя бы один из этих объектов; если какой-либо из них удаляется, зависимый объект должен удаляться вне зависимости от указания CASCADE. Также, в отличие от INTERNAL, удаление некоторого другого объекта, от которого зависит объект, не приводит к автоматическому удалению какого-либо объекта с привязкой к партиции. Следовательно, если удаление каскадно не доходит хотя бы до одного из этих объектов каким-то другим способом, оно не произойдет. (В большинстве случаев зависимый объект совместно использует все свои не связанные с партициями зависимости, по крайней мере, с одним объектом с привязкой к партиции, так что это ограничение не приведет к блокированию каскадного удаления.) Первичные (PRI) и вторичные (SEC) зависимости партиций ведут себя одинаково, за исключением того, что первичная зависимость предпочтительна для использования в сообщениях об ошибках — следовательно, объект, зависящий от партиции, должен иметь одну первичную зависимость партиции и одну или несколько вторичных зависимостей. Обратите внимание, что зависимости партиций создаются в дополнение к любым зависимостям, которые обычно имеются у объекта, а не вместо них. Это упрощает операции ATTACH/DETACH PARTITION: необходимо только добавить или удалить зависимости партиции. Пример: дочерний партиционированный индекс становится партиционно-зависимым как от своей партиционированной таблицы, так и от родительского партиционированного индекса, поэтому он будет удален при удалении любого из этих объектов, но не в других случаях. Зависимость от родительского индекса является первичной, так что если пользователь попытается удалить дочерний партиционированный индекс, в сообщении об ошибке будет предложено вместо этого удалить родительский индекс (а не таблицу).

DEPENDENCY_EXTENSION (е)
Зависимый объект является элементом расширения, которое является его ссылочным объектом (см. pg_extension). Зависимый объект можно удалить только с помощью команды DROP EXTENSION для ссылочного объекта. Функционально этот тип зависимости действует так же, как и зависимость INTERNAL, но он хранится отдельно для ясности и упрощения qhb_dump.

DEPENDENCY_AUTO_EXTENSION (x)
Зависимый объект не является элементом расширения, которое является его ссылочным объектом (и поэтому не должен игнорироваться qhb_dump), но он не может функционировать без расширения и должен автоматически удаляться при удалении расширения. Зависимый объект также может быть удален сам по себе. Функционально этот тип зависимости действует так же, как и зависимость AUTO, но он хранится отдельно для ясности и упрощения qhb_dump.

DEPENDENCY_PIN (p)
Зависимого объекта нет. Этот тип записи сигнализирует, что сама система зависит от ссылочного объекта, и поэтому этот объект никогда не должен быть удален. Записи этого типа создаются только программой qhb_bootstrap. Столбцы для зависимого объекта содержат нули.

Обратите внимание, что два объекта вполне могут быть связаны более чем одной записью в pg_depend. Например, дочерний партиционированный индекс будет иметь зависимость партиционного типа от связанной партиционированной таблицы и автозависимость от каждого столбца той таблицы, которую он индексирует. Такая ситуация выражает объединение семантики множественной зависимости. Зависимый объект можно удалить без указания CASCADE, если какая-либо из его зависимостей удовлетворяет его условию автоматического удаления. И наоборот, должны быть выполнены все ограничения зависимостей, определяющие, какие объекты должны быть удалены вместе.

pg_description

В каталоге pg_description хранятся дополнительные описания (комментарии) для каждого объекта базы данных. Описания можно обрабатывать с помощью команды COMMENT и просматривать с помощью команд psql \d. В начальном содержимом pg_description представлены описания многих встроенных системных объектов.

См. также описание каталога pg_shdescription, который выполняет аналогичную функцию в отношении описаний совместно используемых объектов в кластере баз данных.

Таблица 19. Столбцы pg_description

Имя | Тип | Ссылки | Описание
objoid | oid | OID любого столбца | OID объекта, к которому относится данное описание
classoid | oid | pg_class.oid | OID системного каталога, в котором находится этот объект
objsubid | int4 |  | Для комментария к столбцу таблицы это номер столбца (objoid и classoid задают саму таблицу). Для всех других типов объектов этот столбец содержит ноль.
description | text |  | Произвольный текст, который служит описанием данного объекта

pg_enum

В каталоге pg_enum содержатся записи, показывающие значения и текстовые метки для всех перечислимых типов. Внутренним представлением заданного значения перечисления в действительности является OID соответствующей ему строки в pg_enum.

Таблица 20. Столбцы pg_enum

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
enumtypid | oid | pg_type.oid | OID записи в pg_type, содержащей это значение перечисления
enumsortorder | float4 |  | Порядок сортировки этого значения перечисления внутри его перечислимого типа
enumlabel | name |  | Текстовая метка для этого значения перечисления

OID для строк в pg_enum следуют специальному правилу: четные OID гарантированно упорядочиваются аналогично порядку сортировки их перечислимого типа. То есть если два четных OID принадлежат к одному и тому же перечислимому типу, меньший OID должен иметь меньшее значение enumsortorder. Нечетные значения OID могут быть никак не связаны с порядком сортировки. Это правило во многих случаях позволяет подпрограммам сравнения перечислений обходиться без поиска в каталоге. Подпрограммы, которые создают и изменяют перечислимые типы, по возможности пытаются назначать значениям перечислений четный OID.

При создании перечислимого типа его членам присваиваются позиции по порядку сортировки 1..n. Но членам, добавляемым позже, могут присваиваться отрицательные или дробные значения enumsortorder. Единственное требование к этим значениям в том, чтобы они были правильно упорядочены и уникальны в каждом перечислимом типе.

pg_event_trigger

В каталоге pg_event_trigger хранятся триггеры событий. Дополнительную информацию см. в главе Триггеры событий.

Таблица 21. Столбцы pg_event_trigger

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
evtname | name |  | Имя триггера (должно быть уникальным)
evtevent | name |  | Определяет событие, для которого срабатывает этот триггер
evtowner | oid | pg_authid.oid | Владелец триггера события
evtfoid | oid | pg_proc.oid | Вызываемая функция
evtenabled | char |  | Задает режимы session_replication_role, в которых срабатывает триггер события. О = триггер срабатывает в режимах «origin» (источник) и «local» (локально), D = триггер выключен (disabled), R = триггер срабатывает в режиме «replica» (реплика), A = триггер срабатывает всегда (always).
evttags | text[] |  | Метки команд, для которых этот триггер будет срабатывать. Если NULL, то срабатывание этого триггера не ограничено в зависимости от тега команды.

pg_extension

В каталоге pg_extension хранится информация об установленных расширениях. Подробную информацию о расширениях см. в разделе Упаковка связанных объектов в расширение.

Таблица 22. Столбцы pg_extension

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
extname | name |  | Имя расширения
extowner | oid | pg_authid.oid | Владелец расширения
extnamespace | oid | pg_namespace.oid | Схема, содержащая экспортируемые объекты расширения
extrelocatable | bool |  | True, если расширение можно переместить в другую схему
extversion | text |  | Имя версии для расширения
extconfig | oid[] | pg_class.oid | Массив с OID regclass для таблиц(ы) конфигурации расширения или NULL, если у расширения нет таких таблиц
extcondition | text[] |  | Массив с условиями фильтра предложения WHERE для таблиц(ы) конфигурации расширения или NULL, если условий нет

Обратите внимание, что, в отличие от большинства каталогов со столбцом «namespace», столбец extnamespace не должен означать, что расширение принадлежит этой схеме. Имена расширений никогда не определяются схемой. Скорее, extnamespace указывает схему, содержащую большинство или все объекты расширения. Если extrelocatable имеет значение true, то эта схема фактически должна содержать все определяемые схемой объекты, принадлежащие данному расширению.

pg_foreign_data_wrapper

В каталоге pg_foreign_data_wrapper хранятся определения оберток сторонних данных. Обертка сторонних данных — это механизм, с помощью которого осуществляется доступ к внешним данным, находящимся на сторонних серверах.

Таблица 23. Столбцы pg_foreign_data_wrapper

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
fdwname | name |  | Имя обертки сторонних данных
fdwowner | oid | pg_authid.oid | Владелец обертки сторонних данных
fdwhandler | oid | pg_proc.oid | Ссылается на функцию-обработчик, которая отвечает за предоставление исполняемых подпрограмм для обертки сторонних данных. Ноль, если обработчик не предоставлен
fdwvalidator | oid | pg_proc.oid | Ссылается на функцию-валидатор, которая отвечает за проверку правильности параметров, заданных обертке сторонних данных, а также параметров для сторонних серверов и сопоставлений пользователей, задаваемых через эту обертку. Ноль, если валидатор не предоставлен
fdwacl | aclitem[] |  | Права доступа; дополнительную информацию см. в разделе Права
fdwoptions | text[] |  | Конкретные параметры обертки сторонних данных в виде строки «ключ=значение»

pg_foreign_server

В каталоге pg_foreign_server хранятся определения сторонних серверов. Сторонний сервер описывает источник внешних данных, например удаленный сервер. Доступ к сторонним серверам осуществляется через обертки сторонних данных.

Таблица 24. Столбцы pg_foreign_server

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
srvname | name |  | Имя стороннего сервера
srvowner | oid | pg_authid.oid | Владелец стороннего сервера
srvfdw | oid | pg_foreign_data_wrapper.oid | OID обертки сторонних данных этого стороннего сервера
srvtype | text |  | Тип сервера (необязательно)
srvversion | text |  | Версия сервера (необязательно)
srvacl | aclitem[] |  | Права доступа; дополнительную информацию см. в разделе Права
srvoptions | text[] |  | Конкретные параметры стороннего сервера в виде строки «ключ=значение»

pg_foreign_table

В каталоге pg_foreign_table содержится дополнительная информация о сторонних таблицах. Сторонняя таблица в основном представлена записью в каталоге pg_class, как и обычная таблица. Запись, соответствующая ей в pg_foreign_table, содержит информацию, относящуюся только к сторонним таблицам, а не к какому-либо другому виду отношений.

Таблица 25. Столбцы pg_foreign_table

Имя | Тип | Ссылки | Описание
ftrelid | oid | pg_class.oid | OID записи из pg_class для этой сторонней таблицы
ftserver | oid | pg_foreign_server.oid | OID стороннего сервера для этой сторонней таблицы
ftoptions | text[] |  | Параметры сторонней таблицы в виде строки «ключ=значение»

pg_index

В каталоге pg_index содержится часть информации об индексах. Остальное находится по большей части в каталоге pg_class.

Таблица 26. Столбцы pg_index

Имя
Тип
Ссылки
Описание

indexrelid
oid
pg_class.oid
OID записи в каталоге pg_class для этого индекса

indrelid
oid
pg_class.oid
OID записи в каталоге pg_class для таблицы, к которой относится этот индекс

indnatts
int2

Общее количество столбцов в индексе (повторяет pg_class.relnatts); это число включает в себя как ключевые, так и включенные атрибуты

indnkeyatts
int2

Количество ключевых столбцов в индексе, не считая каких-либо включенных столбцов, которые просто хранятся и не участвуют в семантике индекса

indisunique
bool

Если true, то это уникальный индекс

indisprimary
bool

Если true, то этот индекс представляет собой первичный ключ таблицы (в этом случае indisunique всегда должно быть true)

indisexclusion
bool

Если true, то этот индекс поддерживает исключающее ограничение

indimmediate
bool

Если true, то проверка уникальности применяется сразу после вставки (не применимо, если у indisunique значение false)

indisclustered
bool

Если true, то данная таблица последний раз кластеризовалась по этому индексу

indisvalid
bool

Если true, то индекс в настоящее время является допустимым для запросов. False означает, что индекс может быть неполным: он по-прежнему должен изменяться с помощью операций INSERT/UPDATE, но безопасно использовать его для запросов нельзя. Если индекс уникальный, свойство уникальности также не гарантируется.

indcheckxmin
bool

Если true, запросы не должны использовать индекс до тех пор, пока поле xmin этой строки pg_index находится ниже их горизонта событий (TransactionXmin), поскольку таблица может содержать разорванные цепочки HOT с видимыми запросам несовместимыми строками

indisready
bool

Если true, то индекс в настоящее время готов к вставкам. False означает, что индекс должен быть проигнорирован операциями INSERT/UPDATE.

indislive
bool

Если false, индекс находится в процессе удаления и должен игнорироваться при любой попытке использования (включая заключение о безопасности HOT)

indisreplident
bool

Если true, то этот индекс был выбран в качестве «идентификатора реплики» с помощью команды ALTER TABLE ... REPLICA IDENTITY USING INDEX ...

indkey
int2vector
pg_attribute.attnum
Это массив из значений indnatts, указывающий на столбцы таблицы, которые этот индекс обрабатывает. Например, значение параметра 1 3 означает, что первый и третий столбцы таблицы составляют записи индекса. Ключевые столбцы предшествуют неключевым (включенным) столбцам. Ноль в этом массиве указывает, что соответствующий атрибут индекса является выражением над столбцами таблицы, а не просто ссылкой на столбец.

indcollation
oidvector
pg_collation.oid
Для каждого столбца в ключе индекса (значения indnkeyatts) это поле содержит OID правила сортировки, используемого для индекса, или ноль, если столбец не относится к сортируемому типу данных.

indclass
oidvector
pg_opclass.oid
Для каждого столбца в ключе индекса (значения indnkeyatts) это поле содержит OID используемого класса операторов. Дополнительную информацию см. в описании каталога pg_opclass.

indoption
int2vector

Это массив из значений indnkeyatts, хранящих биты флагов для каждого столбца. Значение битов определяется методом доступа по индексу.

indexprs
pg_node_tree

Деревья выражений (в представлении nodeToString()) для атрибутов индекса, которые не являются простыми ссылками на столбец. Это список с одним элементом для каждой нулевой записи в indkey. NULL, если все атрибуты индекса являются простыми ссылками.

indpred
pg_node_tree

Дерево выражений (в представлении nodeToString()) для частичного индексного предиката. NULL, если это не частичный индекс.

pg_inherits

В каталог pg_inherits записывается информация об иерархиях наследования таблицы и индекса. Для каждой прямой связи «родитель-потомок» таблицы или индекса в базе данных существует одна запись. (Косвенное наследование можно определить, проследив цепочки записей.)

Таблица 27. Столбцы pg_inherits

Имя
Тип
Ссылки
Описание

inhrelid
oid
pg_class.oid
OID дочерней таблицы или индекса

inparent
oid
pg_class.oid
OID родительской таблицы или индекса

inhseqno
int4

Если у дочерней таблицы существует более одного прямого родителя (множественное наследование), это число указывает порядок, в котором должны быть упорядочены наследуемые столбцы. Отсчет начинается с 1.
У индексов не может быть множественного наследования, так как они могут наследоваться только при декларативном партиционировании.

inhdetachpending
bool

true для партиции, находящейся в процессе отсоединения; иначе — false.

pg_init_privs

В каталог pg_init_privs записывается информация об изначальных правах объектов в системе. Для каждого объекта в базе данных, имеющего нестандартный (отличный от NULL) начальный набор прав, существует одна запись.

Начальные права объектов могут задаваться либо при инициализации системы (с помощью qhb_bootstrap), либо при создании объекта в процессе выполнения команды CREATE EXTENSION, а скрипт расширения устанавливает начальные права с помощью системы GRANT. Обратите внимание, что эта система автоматически записывает права во время выполнения скрипта расширения, поэтому авторам расширения достаточно использовать в своем скрипте только команды GRANT и REVOKE, чтобы права были записаны. Столбец privtype указывает, было ли начальное право установлено программой qhb_bootstrap или в процессе выполнения команды CREATE EXTENSION.

Объекты с начальными правами, установленными qhb_bootstrap, будут иметь записи, где privtype помечается буквой 'i', тогда как у объектов с начальными правами, установленными CREATE EXTENSION, privtype будет помечаться буквой 'e'.

Таблица 28. Столбцы pg_init_privs

Имя | Тип | Ссылки | Описание
objoid | oid | OID любого столбца | OID конкретного объекта
classoid | oid | pg_class.oid | OID системного каталога, в котором находится объект
objsubid | int4 |  | Для столбца таблицы это номер столбца (objoid и classoid указывают на саму таблицу). Для всех других типов объектов этот столбец содержит ноль.
privtype | char |  | Код, определяющий тип начального права этого объекта; см. текст выше
initprivs | aclitem[] |  | Начальные права доступа; дополнительную информацию см. в разделе Права

pg_language

В каталоге pg_language регистрируются языки, на которых можно писать функции или хранимые процедуры. Дополнительную информацию об обработчиках языка см. в описании команды CREATE LANGUAGE и главе Процедурные языки.

Таблица 29. Столбцы pg_language

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
lanname | name |  | Имя языка
lanowner | oid | pg_authid.oid | Владелец языка
lanispl | bool |  | Для внутренних языков (таких как SQL) имеет значение false, для пользовательских — true. В настоящее время qhb_dump все еще использует это, чтобы определить, какие языки нужно выгружать, но в будущем этот механизм может быть заменен на другой.
lanplusted | bool |  | True, если это доверенный язык (это означает, что он предположительно не предоставляет доступ к чему-либо за пределами обычной среды выполнения SQL). Только суперпользователи могут создавать функции на недоверенных языках.
lanplcallfoid | oid | pg_proc.oid | Для не внутренних языков это значение указывает на обработчик языка — специальную функцию, ответственную за выполнение всех функций, написанных конкретно на этом языке. Ноль для внутренних языков.
laninline | oid | pg_proc.oid | Ссылка на функцию, которая отвечает за выполнение «подставляемых» анонимных блоков кода (блоков DO). Ноль, если подставляемые блоки не поддерживаются.
lanvalidator | oid | pg_proc.oid | Ссылка на функцию-валидатор языка, которая отвечает за проверку синтаксиса и правильности новых функций при их создании. Ноль, если валидатор не предоставлен.
lanacl | aclitem[] |  | Права доступа; дополнительную информацию см. в разделе Права

pg_largeobject

В каталоге pg_largeobject содержатся данные, составляющие «большие объекты». Большой объект идентифицируется OID, назначенным при его создании. Каждый большой объект разбивается на сегменты или «страницы», достаточно маленькие для удобного хранения в виде строк в pg_largeobject. Количество данных на странице определяется как LOBLKSIZE (который в настоящее время равен BLCKSZ/4, т. е. обычно 2 Кб).

Для получения списка OID больших объектов следует обратиться к pg_largeobject_metadata.

Таблица 30. Столбцы pg_largeobject

Имя | Тип | Ссылки | Описание
loid | oid | pg_largeobject_metadata.oid | Идентификатор большого объекта, включающего эту страницу
pageno | int4 |  | Номер этой страницы в большом объекте (начиная с нуля)
data | bytea |  | Непосредственно данные, хранящиеся в большом объекте. Их объем никогда не будет превышать количество байтов LOBLKSIZE, но может быть меньше.

В каждой строке pg_largeobject содержатся данные для одной страницы большого объекта, начиная со смещения (pageno * LOBLKSIZE) внутри него в байтах. Эта реализация допускает разреженное хранилище: страницы могут отсутствовать и быть короче LOBLKSIZE, даже если это не последние страницы объекта. Отсутствующие области внутри большого объекта считываются как нули.

pg_largeobject_metadata

В каталоге pg_largeobject_metadata содержатся метаданные, связанные с большими объектами. Фактические данные большого объекта хранятся в каталоге pg_largeobject.

Таблица 31. Столбцы pg_largeobject_metadata

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
lomowner | oid | pg_authid.oid | Владелец большого объекта
lomacl | aclitem[] |  | Права доступа; дополнительную информацию см. в разделе Права

pg_namespace

В каталоге pg_namespace хранятся пространства имен. Пространство имен — это структура, лежащая в основе схем SQL: каждое пространство имен без конфликтов может иметь отдельный набор отношений, типов и т. д.

Таблица 32. Столбцы pg_namespace

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
nspname | name |  | Имя пространства имен
nspowner | oid | pg_authid.oid | Владелец пространства имен
nspacl | aclitem[] |  | Права доступа; дополнительную информацию см. в разделе Права

pg_opclass

В каталоге pg_opclass определены классы операторов для методов доступа по индексу. Каждый класс операторов определяет семантику для индексных столбцов определенного типа данных и конкретного метода доступа по индексу. Класс операторов по существу определяет, что конкретное семейство операторов применимо к конкретному индексируемому типу данных столбца. Набор операторов из семейства, которые на самом деле можно использовать с индексированным столбцом, составляет те, что принимают в качестве левого операнда тип данных столбца.

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

Таблица 33. Столбцы pg_opclass

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
opcmethod | oid | pg_am.oid | Класс операторов для метода доступа по индексу
opcname | name |  | Имя этого класса операторов
opcnamespace | oid | pg_namespace.oid | Пространство имен этого класса операторов
opcowner | oid | pg_authid.oid | Владелец класса операторов
opcfamily | oid | pg_opfamily.oid | Семейство операторов, содержащее этот класс операторов
opcintype | oid | pg_type.oid | Тип данных, который индексирует этот класс операторов
opcdefault | bool |  | True, если этот класс операторов является значением по умолчанию для opcintype
opckeytype | oid | pg_type.oid | Тип данных, хранящихся в индексе, или ноль, если он совпадает с opcintype

Значение opfmethod класса операторов должно совпадать с таковым у содержащего его семейства операторов. Кроме того, в pg_opclass должно быть не более одной строки, в которой opcdefault равно true для любой заданной комбинации opcmethod и opcintype.

pg_operator

В каталоге pg_operator хранится информация об операторах. Дополнительную информацию см. в описании команды CREATE OPERATOR и разделе Пользовательские операторы.

Таблица 34. Столбцы pg_operator

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
oprname | name |  | Имя оператора
oprnamespace | oid | pg_namespace.oid | OID пространства имен, содержащего этот оператор
oprowner | oid | pg_authid.oid | Владелец оператора
oprkind | char |  | b = инфиксный оператор («both») или l = префиксный оператор («left»)
oprcanmerge | bool |  | Этот оператор поддерживает соединения слиянием
oprcanhash | bool |  | Этот оператор поддерживает хеш-соединения
oprleft | oid | pg_type.oid | Тип левого операнда (ноль для префиксного оператора)
oprright | oid | pg_type.oid | Тип правого операнда
oprresult | oid | pg_type.oid | Тип результата (ноль для еще не определенного «пустого» оператора)
oprcom | oid | pg_operator.oid | Коммутатор этого оператора (ноль, если такового нет)
oprnegate | oid | pg_operator.oid | Инвертор этого оператора (ноль, если такового нет)
oprcode | regproc | pg_proc.oid | Функция, реализующая этот оператор (ноль для еще не определенного «пустого» оператора)
oprrest | regproc | pg_proc.oid | Функция оценки избирательности ограничения для этого оператора (ноль, если таковой нет)
oprjoin | regproc | pg_proc.oid | Функция оценки избирательности соединения для этого оператора (ноль, если таковой нет)

pg_opfamily

В каталоге pg_opfamily определены семейства операторов. Каждое семейство операторов представляет собой набор операторов и связанных с ними вспомогательных процедур, реализующих семантику, заданную для конкретного метода доступа по индексу. Более того, все операторы в семействе «совместимы» в том смысле, который определяется методом доступа. Концепция семейства операторов позволяет применять с индексами операторы разного типа и рассуждать о них, используя знания о семантике метода доступа.

Семейства операторов подробно описаны в разделе Интерфейсные расширения для индексов.

Таблица 35. Столбцы pg_opfamily

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
opfmethod | oid | pg_am.oid | Метод доступа по индексу, для которого предназначено семейство операторов
opfname | name |  | Имя этого семейства операторов
opfnamespace | oid | pg_namespace.oid | Пространство имен этого семейства операторов
opfowner | oid | pg_authid.oid | Владелец этого семейства операторов

Большая часть информации, определяющей семейство операторов, находится не в строке pg_opfamily, а в связанных строках в каталогах pg_amop, pg_amproc и pg_opclass.

pg_partitioned_table

В каталоге pg_partitioned_table хранится информация о способе партиционирования таблиц.

Таблица 36. Столбцы pg_partitioned_table

Имя
Тип
Ссылки
Описание

partrelid
oid
pg_class.oid
OID записи в каталоге pg_class для партиционированной таблицы

partstrat
char

Стратегия партиционирования: h = по хешу (hash), l = по списку (list), r = по диапазону (range)

partnatts
int2

Количество столбцов в ключе разбиения

partdefid
oid
pg_class.oid
OID записи в каталоге pg_class для партиции по умолчанию этой партиционированной таблицы или ноль, если эта партиционированная таблица не имеет партиции по умолчанию.

partattrs
int2vector
pg_attribute.attnum
Это массив из значений partnatts, указывающих, какие столбцы таблицы являются частью ключа разбиения. Например, значение параметра 1 3 будет означать, что первый и третий столбцы таблицы составляют ключ разбиения. Ноль в этом массиве указывает, что соответствующий столбец ключа разбиения является выражением, а не простой ссылкой на столбец.

partclass
oidvector
pg_opclass.oid
Для каждого столбца в ключе разбиения это поле содержит OID используемых классов операторов. Дополнительную информацию см. в описании каталога pg_opclass.

partcollation
oidvector
pg_opclass.oid
Для каждого столбца в ключе разбиения это поле содержит OID правил сортировки, используемых для партиционирования, или ноль, если столбец не относится к сортируемому типу данных.

partexprs
pg_node_tree

Деревья выражений (в представлении nodeToString()) для столбцов ключа разбиения, которые не являются простыми ссылками на столбцы. Это список с одним элементом для каждой нулевой записи в partattrs. NULL, если все столбцы ключа разбиения являются простыми ссылками.

pg_policy

В каталоге pg_policy хранятся политики защиты на уровне строк для таблиц. Политика включает тип команды, к которой она применяется (возможно, это все команды), роли, к которым она применяется, выражение, добавляемое в качестве требования барьера безопасности к запросам, включающим таблицу, и выражение, добавляемое в качестве параметра WITH CHECK для запросов, которые пытаются добавить в таблицу новые записи.

Таблица 37. Столбцы pg_policy

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
polname | name |  | Имя политики
polrelid | oid | pg_class.oid | Таблица, к которой применяется политика
polcmd | char |  | Тип команды, к которой применяется политика: r для SELECT, a для INSERT, w для UPDATE, d для DELETE или * для всех
polpermissive | bool |  | Является ли эта политика разрешительной или ограничительной?
polroles | oid[] | pg_authid.oid | Роли, к которым применяется политика; ноль означает PUBLIC (которая обычно является единственным элементом массива)
polqual | pg_node_tree |  | Дерево выражений, добавляемое к требованиям барьеров безопасности для запросов, использующих данную таблицу
polwithcheck | pg_node_tree |  | Дерево выражений, добавляемое к требованиям WITH CHECK для запросов, пытающихся добавить строки в таблицу

Примечание
Политики, хранящиеся в каталоге pg_policy, применяются только тогда, когда для их таблицы задано свойство pg_class.relrowsecurity.

pg_proc

В каталоге pg_proc хранится информация о простых функциях, процедурах, агрегатных и оконных функциях (в совокупности также известных как подпрограммы). Дополнительную информацию см. в описаниях команд CREATE FUNCTION и CREATE PROCEDURE и разделе Пользовательские функции.

Если prokind указывает, что эта запись предназначена для агрегатной функции, в каталоге pg_aggregate должна быть соответствующая строка.

Таблица 38. Столбцы pg_proc

Имя
Тип
Ссылки
Описание

oid
oid

Идентификатор строки

proname
name

Имя функции

pronamespace
oid
pg_namespace.oid
OID пространства имен, содержащего эту функцию

proowner
oid
pg_authid.oid
Владелец этой функции

prolang
oid
pg_language.oid
Язык реализации или интерфейс вызова этой функции

procost
float4

Предполагаемая стоимость исполнения (в единицах cpu_operator_cost); если установлен признак proretset, это стоимость одной возвращенной строки

prorows
float4

Предполагаемое количество возвращаемых строк (ноль, если не задан proretset)

provariadic
oid
pg_type.oid
Тип данных элементов переменного массива параметров или ноль, если функция не принимает переменного числа параметров

prosupport
regproc
pg_proc.oid
Необязательная вспомогательная функция планировщика для этой функции (см. раздел Информация по оптимизации функций) или ноль, если таковой нет

prokind
char

f для нормальной функции (function), p для процедуры (procedure), a для агрегатной функции (aggregate) или w для оконной функции (window)

prosecdef
bool

Функция является определителем контекста безопасности (т. е. функцией «setuid»)

proleakproof
bool

Функция не имеет побочных эффектов. Никакая информация о ее аргументах не передается, иначе чем через возвращаемое значение. Любая функция, которая может вызвать ошибку, в зависимости от значений ее аргументов, не является герметичной.

proisstrict
bool

Функция возвращает значение NULL, если любой аргумент вызова имеет значение NULL. В этом случае функция по факту не будет вызвана вообще. Функции, которые не являются «строгими», должны быть подготовлены для обработки входных значений NULL.

proretset
bool

Функция возвращает набор (т. е. несколько значений указанного типа данных)

provolatile
char

provolatile указывает, зависит ли результат функции только от ее входных аргументов, или на него влияют внешние факторы. Для «неизменяемых» функций, которые всегда дают один и тот же результат для одних и тех же входных данных, устанавливается значение i (immutable). Для «стабильных» функций, результаты которых (для фиксированных входных аргументов) не изменяются в пределах сканирования, устанавливается значение s (stable). Для «изменчивых» функций, результаты которых могут измениться в любое время, устанавливается значение v (volatile). (Также v используется для функций с побочными эффектами, чтобы их вызовы нельзя было оптимизировать.)

proparallel
char

proparallel указывает, можно ли безопасно запускать функцию в параллельном режиме. Для функций, которые безопасно запускать в параллельном режиме без ограничений, устанавливается значение s (safe). Для функций, которые могут выполняться в параллельном режиме, но ограничены ведущим процессом группы (параллельные рабочие процессы не могут вызывать эти функции), устанавливается значение r (restricted). Для функций, которые небезопасны в параллельном режиме (наличие такой функции заставляет оптимизатор строить план последовательного выполнения запроса), устанавливается значение u (unsafe).

pronargs
int2

Количество входных аргументов

pronargdefaults
int2

Количество аргументов, имеющих значения по умолчанию

prorettype
oid
pg_type.oid
Тип данных возвращаемого значения

proargtypes
oidvector
pg_type.oid
Массив с типами данных аргументов функции. Включает только входные аргументы (включая аргументы INOUT и VARIADIC) и тем самым представляет сигнатуру вызова функции.

proallargtypes
oid[]
pg_type.oid
Массив с типами данных аргументов функции. Включает в себя все аргументы (в том числе аргументы IN и INOUT), однако если все аргументы являются IN, это поле будет иметь значение NULL. Обратите внимание, что индексация массива обычно начинается с 1, тогда как по историческим причинам proargtypes индексируется от 0.

proargmodes
char[]

Массив с режимами аргументов функции, закодированными как i для аргументов IN, о для аргументов OUT, b для аргументов INOUT, v для аргументов VARIADIC, t для аргументов TABLE. Если все аргументы являются IN, это поле будет иметь значение NULL. Обратите внимание, что индексы соответствуют позициям в proallargtypes, а не в proargtypes.

proargnames
text[]

Массив с именами аргументов функции. Аргументы без имени представляют собой пустые строки в массиве. Если ни один из аргументов не имеет имени, это поле будет иметь значение NULL. Обратите внимание, что индексы соответствуют позициям в proallargtypes, а не в proargtypes.

proargdefaults
pg_node_tree

Деревья выражений (в представлении nodeToString()) для значений по умолчанию. Это список, содержащий pronargdefaults элементов, соответствующих последним N входным аргументам (т. е. последним N позициям в proargtypes). Если ни один из аргументов не имеет значений по умолчанию, это поле будет иметь значение NULL.

protrftypes
oid[]

Массив типов данных аргументов/результатов, к которым необходимо применить преобразования (из предложения TRANSFORM объявления функции). NULL, если таковых нет.

prosrc
text

Это значение говорит обработчику функции, как ее вызвать. Это может быть фактический исходный код функции для интерпретируемых языков, символ ссылки, имя файла или что-нибудь еще, в зависимости от языка реализации/соглашения о вызовах.

probin
text

Дополнительная информация о том, как вызвать эту функцию. Интерпретация этого значения также зависит от языка.

prosqlbody
pg_node_tree

Предварительно проанализированное тело функции SQL. Используется для функций на языке SQL, когда тело функции оформлено по стандарту SQL, а не в виде строкового литерала. В других случаях — NULL.

proconfig
text[]

Локальные настройки функции для переменных конфигурации времени выполнения

proacl
aclitem[]

Права доступа; дополнительную информацию см. в разделе Права

Для скомпилированных функций, как встроенных, так и динамически загружаемых, поле prosrc содержит имя функции на языке C/RUST (связующий символ). Для функции на языке SQL prosrc содержит исходный текст функции, если та задана в виде строкового литерала, но если тело функции записано в стиле стандарта SQL, поле prosrc не применяется (обычно это пустая строка), а поле prosqlbody содержит предварительно проанализированное определение. Для всех других известных на данный момент типов языков поле prosrc содержит исходный текст функции. Поле probin используется только для динамически загружаемых функций на языке C/RUST, для которых оно задает имя файла разделяемой библиотеки, содержащей эти функции.

pg_publication

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

Таблица 39. Столбцы pg_publication

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
pubname | name |  | Имя публикации
pubname | oid | pg_authid.oid | Владелец этой публикации
puballtables | bool |  | Если true, эта публикация автоматически включает все таблицы в базе данных, в том числе те, что будут созданы в будущем.
pubinsert | bool |  | Если true, операции INSERT реплицируются для таблиц в публикации.
pubupdate | bool |  | Если true, операции UPDATE реплицируются для таблиц в публикации.
pubdelete | bool |  | Если true, операции DELETE реплицируются для таблиц в публикации.
pubtruncate | bool |  | Если true, операции TRUNCATE реплицируются для таблиц в публикации.
pubviaroot | bool |  | Если true, операции с конечной партицией реплицируются с использованием идентификатора и схемы партиционированной таблицы-родителя самого верхнего уровня, включенной в данную публикацию, а не ее собственных.

pg_publication_rel

В каталоге pg_publication_rel содержатся сопоставления между отношениями и публикациями в базе данных. Это сопоставления вида многие-ко-многим. Более понятное пользователю представление этой информации можно также посмотреть в описании pg_publication_tables.

Таблица 40. Столбцы pg_publication_rel

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
prpubid | oid | pg_publication.oid | Ссылка на публикацию
prrelid | oid | pg_class.oid | Ссылка на отношение

pg_range

В каталоге pg_range хранится информация о типах диапазонов. Это дополнительная информация к записям типов в каталоге pg_type.

Таблица 41. Столбцы pg_range

Имя | Тип | Ссылки | Описание
rngtypid | oid | pg_type.oid | OID типа диапазона
rngsubtype | oid | pg_type.oid | OID типа элемента (подтипа) данного типа диапазона
rngcollation | oid | pg_collation.oid | OID правила сортировки, используемого для сравнения диапазонов, или ноль, если такового нет
rngsubopc | oid | pg_opclass.oid | OID класса операторов подтипа, используемого для сравнения диапазонов
rngcanonic | regproc | pg_proc.oid | OID функции, преобразующей значения диапазона в каноничную форму, или ноль, если таковой нет
rngsubdiff | regproc | pg_proc.oid | OID функции, возвращающей разницу между двумя значениями элементов в виде значения double precision, или ноль, если таковой нет

Значение поля rngsubopc (плюс rngcollation, если тип элемента является сортируемым) определяет правило сортировки, используемое типом диапазона. Значение поля rngcanonic используется, когда тип элемента является дискретным. Значение поля rngsubdiff является необязательным, но его стоит задавать для повышения производительности индексов GiST с диапазонным типом.

pg_replication_origin

Каталог pg_replication_origin содержит все созданные источники репликации.

В отличие от большинства системных каталогов, pg_replication_origin является общим для всех баз данных кластера: существует только одна копия pg_replication_origin на кластер, а не по одной на каждую базу данных.

Таблица 42. Столбцы pg_replication_origin

Имя | Тип | Описание
roident | oid | Уникальный в пределах кластера идентификатор источника репликации. Никогда не должен выходить за границы системы.
roname | text | Определяемое пользователем внешнее имя источника репликации.

pg_rewrite

В каталоге pg_rewrite хранятся правила перезаписи для таблиц и представлений.

Таблица 43. Столбцы pg_rewrite

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
rulename | name |  | Имя правила
ev_class | oid | pg_class.oid | Таблица, для которой предназначено это правило
ev_type | char |  | Тип события, для которого используется это правило: 1 = SELECT, 2 = UPDATE, 3 = INSERT, 4 = DELETE
ev_enabled | char |  | Задает режимы session_replication_role, при которых срабатывает это правило. О = правило срабатывает в режимах «origin» (источник) и «local» (локально), D = правило выключено (disabled), R = правило срабатывает в режиме «replica» (реплика), A = правило срабатывает всегда (always).
is_instead | bool |  | True, если это правило INSTEAD
ev_qual | pg_node_tree |  | Дерево выражений (в форме представления nodeToString()) для условия применения правила
ev_action | pg_node_tree |  | Дерево запросов (в форме представления nodeToString()) для действия правила

Примечание
Если таблица имеет какие-либо правила в этом каталоге, поле pg_class.relhasrules должно содержать true.

pg_seclabel

В каталоге pg_seclabel хранятся метки безопасности для объектов базы данных. Метками безопасности можно управлять с помощью команды SECURITY LABEL. Более простой способ просмотра меток безопасности описан в подразделе pg_seclabels.

См. также описание каталога pg_shseclabel, который выполняет ту же функцию для меток безопасности объектов базы данных, совместно используемых в кластере баз данных.

Таблица 44. Столбцы pg_seclabel

Имя | Тип | Ссылки | Описание
objoid | oid | OID любого столбца | OID объекта, к которому относится эта метка безопасности
classoid | oid | pg_class.oid | OID системного каталога, к которому относится этот объект
objsubid | int4 |  | Для метки безопасности в столбце таблицы это номер столбца (поля objoid и classoid указывают на саму таблицу). Для всех других типов объектов этот столбец содержит ноль.
provider | text |  | Поставщик меток безопасности, связанный с этой меткой.
label | text |  | Метка безопасности, применяемая к этому объекту.

pg_sequence

В каталоге pg_sequence содержится информация о последовательностях. Некоторые сведения о последовательностях, такие как имя и схема, находятся в каталоге pg_class.

Таблица 45. Столбцы pg_sequence

Имя | Тип | Ссылки | Описание
seqrelid | oid | pg_class.oid | OID записи в pg_class для этой последовательности
seqtypid | oid | pg_type.oid | Тип данных последовательности
seqstart | int8 |  | Начальное значение последовательности
seqincrement | int8 |  | Значение приращения последовательности
seqmax | int8 |  | Максимальное значение последовательности
seqmin | int8 |  | Минимальное значение последовательности
seqcache | int8 |  | Размер кэша последовательности
seqcycle | bool |  | Зацикливается ли последовательность

pg_shdepend

В каталог pg_shdepend записываются отношения зависимости между объектами базы данных и общими объектами, такими как роли. Эта информация позволяет QHB перед попыткой удаления этих объектов убедиться в том, что они не используются.

См. также описание каталога pg_depend, который выполняет ту же функцию для зависимостей объектов в пределах одной базы данных.

В отличие от большинства системных каталогов, pg_shdepend является общим для всех баз данных кластера: существует только одна копия pg_shdepend на кластер, а не по одной на каждую базу данных.

Таблица 46. Столбцы pg_shdepend

Имя | Тип | Ссылки | Описание
dbid | oid | pg_database.oid | OID базы данных, в которой находится зависимый объект, или ноль для общего объекта или записи SHARED_DEPENDENCY_PIN
classid | oid | pg_class.oid | OID системного каталога, в котором находится зависимый объект, или ноль для записи SHARED_DEPENDENCY_PIN
objid | oid | OID любого столбца | OID конкретного зависимого объекта или ноль для записи SHARED_DEPENDENCY_PIN
objsubid | int4 |  | Для столбца таблицы это номер столбца (objid и classid указывают на саму таблицу). Для всех других типов объектов этот столбец содержит ноль.
refclassid | oid | pg_class.oid | OID системного каталога, в котором находится ссылочный объект (должен быть общим каталогом)
refobjid | oid | OID любого столбца | OID конкретного ссылочного объекта
deptype | char |  | Код, определяющий конкретную семантику этого отношения зависимости; см. текст ниже

Во всех случаях запись в pg_shdepend указывает, что ссылочный объект нельзя удалить, не удалив также зависимый объект. Однако есть несколько подвидов зависимостей, которые задаются в поле deptype:

SHARED_DEPENDENCY_OWNER (о)
Ссылочный объект (который должен быть ролью) является владельцем зависимого объекта.

SHARED_DEPENDENCY_ACL (a)
Ссылочный объект (который должен быть ролью) упоминается в списке ACL (список управления доступом (access control list), т. е. список прав) зависимого объекта. (Запись SHARED_DEPENDENCY_ACL не создается для владельца объекта, так как у владельца в любом случае будет запись SHARED_DEPENDENCY_OWNER.)

SHARED_DEPENDENCY_POLICY (r)
Ссылочный объект (который должен быть ролью) является целью для объекта зависимой политики.

SHARED_DEPENDENCY_PIN (p)
Зависимый объект отсутствует; этот тип записи служит сигналом, что сама система зависит от ссылочного объекта, и поэтому этот объект никогда не должен быть удален. Записи этого типа создаются только программой qhb_bootstrap. Столбцы для зависимого объекта у этой записи содержат нули.

SHARED_DEPENDENCY_TABLESPACE (t)
Зависимый объект (который должен быть табличным пространством) упоминается в качестве табличного пространства в отношении, у которого нет хранилища.

В будущем могут потребоваться и другие подвиды зависимостей. В частности, обратите внимание, что в текущем определении ссылочными объектами могут быть только роли и табличные пространства.

pg_shdescription

В каталоге pg_shdescription хранятся необязательные описания (комментарии) для общих объектов базы данных. Описаниями можно манипулировать с помощью команды COMMENT и просматривать с помощью команд psql \d.

См. также описание каталога pg_description, который выполняет ту же функцию для описаний объектов в пределах одной базы данных.

В отличие от большинства системных каталогов, pg_shdescription является общим для всех баз данных кластера: существует только одна копия pg_shdescription на кластер, а не по одной на каждую базу данных.

Таблица 47. Столбцы pg_shdescription

Имя | Тип | Ссылки | Описание
objoid | oid | OID любого столбца | OID объекта, к которому относится это описание
classoid | oid | pg_class.oid | OID системного каталога, к которому относится этот объект
description | text |  | Произвольный текст, который служит описанием этого объекта

pg_shseclabel

В каталоге pg_shseclabel хранятся метки безопасности на общих объектах базы данных. Метками безопасности можно управлять с помощью команды SECURITY LABEL. Более простой способ просмотра меток безопасности описан в подразделе pg_seclabels.

См. также описание каталога pg_seclabel, который выполняет ту же функцию для меток безопасности объектов в пределах одной базы данных.

В отличие от большинства системных каталогов, pg_shseclabel является общим для всех баз данных кластера: существует только одна копия pg_shseclabel на кластер, а не по одной на каждую базу данных.

Таблица 48. Столбцы pg_shseclabel

Имя | Тип | Ссылки | Описание
objoid | oid | OID любого столбца | OID объекта, к которому относится эта метка безопасности
classoid | oid | pg_class.oid | OID системного каталога, к которому относится этот объект
provider | text |  | Поставщик меток безопасности, связанный с этой меткой
label | text |  | Метка безопасности, применяемая к этому объекту

pg_statistic

В каталоге pg_statistic хранятся статистические данные о содержимом базы данных. Записи создаются при помощи команды ANALYZE и впоследствии используются планировщиком запросов. Обратите внимание, что все статистические данные по своей сути приблизительны, даже если предполагается, что они актуальны.

Обычно для каждого столбца таблицы, который был проанализирован, есть одна запись со значением stainherit = false. Если таблица имеет потомков в иерархии наследования, то создается также вторая запись со значением stainherit = true. Эта строка представляет статистику для столбца по всему дереву наследования, т. е. статистику для данных, которые вы видите с помощью команды SELECT столбец FROM таблица*, в то время как строка stainherit = false представляет собой результаты выполнения команды SELECT столбец FROM ONLY таблица.

В pg_statistic также хранятся статистические данные о значениях индексных выражений. Они описываются так, как если бы они были реальными столбцами данных; в частности, starelid ссылается на индекс. Однако для обычного столбца индекса, не содержащего выражения, запись не производится, поскольку она дублировала бы запись для нижележащего столбца таблицы. В настоящее время записи для выражений индекса всегда имеют значение stainherit = false.

Поскольку различные виды статистики подходят для различных видов данных, pg_statistic предназначен не для того, чтобы строить конкретные предположения о том, какая статистика в нем хранится. В отдельных столбцах pg_statistic приводятся только самые общие статистические данные (такие как доля значений NULL). Все остальное хранится в «слотах», представляющих собой группы связанных столбцов, содержимое которых определяется кодовым номером в одном из слотов. Более подробную информацию см. в src/include/catalog/pg_statistic.h.

Каталог pg_statistic не должен быть доступным для чтения всем, поскольку даже статистическая информация о содержимом таблицы может считаться конфиденциальной. (Например: могут представлять интерес минимальные и максимальные значения столбца зарплаты.) Доступным для чтения всеми представлением для pg_statistic является pg_stats, которое предоставляет информацию только о тех таблицах, которые может читать текущий пользователь.

Таблица 49. Столбцы pg_statistic

Имя
Тип
Ссылки
Описание

starelid
oid
pg_class.oid
Таблица или индекс, к которому принадлежит описываемый столбец

staattnum
int2
pg_attribute.attnum
Номер описываемого столбца

stainherit
bool

Если true, то в статистику включены значения в дочерних столбцах иерархии наследования, а не только значения в указанном отношении

stanullfrac
float4

Доля записей столбца, которые содержат NULL

stawidth
int4

Средняя хранимая ширина записей, отличных от NULL, в байтах

stadistinct
float4

Число различных значений данных, отличных от NULL, в столбце. Значение больше нуля — это фактическое количество различных значений. Значение меньше нуля — это множитель для количества строк в таблице, взятый с отрицательным знаком; например, столбец, в котором около 80% значений не имеют значения NULL и каждое такое значение появляется в среднем примерно два раза, может быть представлен как stadistinct = -0.4. Нулевое значение означает, что число различных значений неизвестно.

stakindN
int2

Кодовый номер, указывающий вид статистики, хранящейся в N-м «слоте» строки в pg_statistic.

staopN
oid
pg_operator.oid
Оператор, используемый для получения статистики, хранящейся в N-м «слоте». Например, для слота гистограммы это будет оператор <, определяющий порядок сортировки данных. Ноль, если для этого вида статистики не нужен оператор.

stacollN
oid
pg_collation.oid
Правило сортировки, используемое для получения статистики, хранящейся в N-м «слоте». Например, слот гистограммы для сортируемого столбца будет отображать правило сортировки, определяющее порядок сортировки данных. Ноль для несортируемых данных.

stanumbersN
float4[]

Числовая статистика соответствующего вида для N-го «слота» или NULL, если этот вид слота не содержит числовых значений

stavaluesN
anyarray

Значения данных столбца соответствующего вида для N-го «слота» или NULL, если этот вид слота не хранит никаких значений данных. Значения каждого элемента массива фактически относятся к определенному типу данных столбца или связанному типу, например, типу элемента массива, поэтому нет способа определить тип этих столбцов более конкретно, чем anyarray.

pg_statistic_ext

В каталоге pg_statistic_ext содержатся определения расширенной статистики планировщика. Каждая строка в этом каталоге соответствует объекту статистики, созданному с помощью команды CREATE STATISTICS.

Таблица 50. Столбцы pg_statistic_ext

Имя
Тип
Ссылки
Описание

oid
oid

Идентификатор строки

stxrelid
oid
pg_class.oid
Таблица, содержащая столбцы, описываемые данным объектом

stxname
name

Имя объекта статистики

stxnamespace
oid
pg_namespace.oid
OID пространства имен, содержащего этот объект статистики

stxowner
oid
pg_authid.oid
Владелец объекта статистики

stxstattarget
int4

stxstattarget определяет уровень детализации статистики, собранной для данного объекта статистики командой ANALYZE. Нулевое значение показывает, что статистику собирать не нужно. Отрицательное значение указывает использовать максимум из ориентиров статистики для целевых столбцов, если таковые заданы, или системный ориентир статистики по умолчанию. Положительное значение stxstattarget определяет ориентировочное число «наиболее частых значений», которое нужно собрать.

stxkeys
int2vector
pg_attribute.attnum
Массив номеров атрибутов, показывающий, какие столбцы таблицы охвачены этим объектом статистики; например, значение 1 3 означает, что будут покрыты первый и третий столбцы таблицы

stxkind
char[]

Массив, содержащий коды для включенных типов статистики; допустимыми значениями являются: d для статистики по количеству различных значений (n-distinct), f для статистики функциональной зависимости (functional dependency), m для статистики списка наиболее частых значений (most common values, MCV) и e для статистики выражений (expression)

stxexprs
pg_node_tree

Деревья выражений (в представлении nodeToString()) для атрибутов объекта статистики, которые не являются простыми ссылками на столбец. Это список с одним элементом для каждого выражения. NULL, если все атрибуты объекта статистики являются простыми ссылками.

Запись в pg_statistic_ext полностью заполняется во время выполнения команды CREATE STATISTICS, но фактические статистические значения при этом не вычисляются. Последующие команды ANALYZE вычисляют требуемые значения и заполняют запись в каталоге pg_statistic_ext_data.

pg_statistic_ext_data

В каталоге pg_statistic_ext_data содержатся данные для расширенной статистики планировщика, определенной в pg_statistic_ext. Каждая строка в этом каталоге соответствует объекту статистики, созданному командой CREATE STATISTICS.

Как и в случае с pg_statistic, каталог pg_statistic_ext_data не должен быть доступен для чтения всем, поскольку его содержимое может считаться конфиденциальным. (Пример: определенный интерес могут предоставлять наиболее распространенные комбинации значений в столбцах базы данных.) Доступным для всех представлением каталога pg_statistic_ext_data является pg_stats_ext (после соединения с pg_statistic_ext), которое предоставляет информацию только о тех таблицах и столбцах, которые может прочитать текущий пользователь.

Таблица 51. Столбцы pg_statistic_ext_data

Имя | Тип | Ссылки | Описание
stxoid | oid | pg_statistic_ext.oid | Расширенный статистический объект, содержащий определение для этих данных
stxdndistinct | pg_ndistinct |  | Количество различных значений, сериализованное как тип pg_ndistinct
stxddependencies | pg_dependencies |  | Статистика функциональных зависимостей, сериализованная как тип pg_dependencies
stxdmcv | pg_mcv_list |  | Статистика списка MCV (most-common values, наиболее частых значений), сериализованная как тип pg_mcv_list

pg_subscription

В каталоге pg_subscription содержатся все существующие подписки на логическую репликацию.

В отличие от большинства системных каталогов, pg_subscription является общим для всех баз данных кластера: существует только одна копия pg_subscription на кластер, а не по одной на каждую базу данных.

Обычным пользователям запрещен доступ к столбцу subconninfo, поскольку он может содержать пароли в открытом виде.

Таблица 52. Столбцы pg_subscription

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
subdbid | oid | pg_database.oid | OID базы данных, в которой находится подписка
subname | name |  | Имя подписки
subowner | oid | pg_authid.oid | Владелец подписки
subenabled | bool |  | Если true, то подписка включена и должна реплицироваться.
subbinary | bool |  | Если true, то данные для подписки будут запрашиваться у сервера в двоичном формате
substream | bool |  | Если true, то для подписки будет разрешена передача данных еще выполняющихся транзакций
subconninfo | text |  | Строка подключения к вышестоящей базе данных
subslotname | name |  | Имя слота репликации в вышестоящей базе данных (также используется при локальной репликации имени источника); значение NULL представляет имя NONE.
subsynccommit | text |  | Значение параметра synchronous_commit для рабочих процессов подписки.
subpublications | text[] |  | Массив имен публикаций, на которые оформлена подписка. Эти имена представляют публикации на публикующем сервере.

pg_subscription_rel

В каталоге pg_subscription_rel содержится состояние для каждого реплицируемого отношения в каждой подписке. Это сопоставление вида многие-ко-многим.

Этот каталог содержит только таблицы, известные подписке после выполнения команды CREATE SUBSCRIPTION или ALTER SUBSCRIPTION ... REFRESH PUBLICATION.

Таблица 53. Столбцы pg_subscription_rel

Имя | Тип | Ссылки | Описание
srsubid | oid | pg_subscription.oid | Ссылка на подписку
srrelid | oid | pg_class.oid | Ссылка на отношение
srsubstate | char |  | Код состояния: i = инициализация, d = копирование данных, f = копирование таблицы завершено, s = синхронизация выполнена, r = готовность (нормальная репликация)
srsublsn | pg_lsn |  | LSN изменения состояния на удаленной стороне, используемый для координации синхронизации при состояниях s или r; в ином случае NULL.

pg_tablespace

В каталоге pg_tablespace хранится информация о доступных табличных пространствах. Таблицы могут быть размещены в определенных табличных пространствах с целью управления структурой диска.

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

Таблица 54. Столбцы pg_tablespace

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
spcname | name |  | Имя табличного пространства
spcowner | oid | pg_authid.oid | Владелец этого табличного пространства (обычно это пользователь, который его создал)
spcacl | aclitem[] |  | Права доступа; дополнительную информацию см. в разделе Права
spcoptions | text[] |  | Параметры уровня табличного пространства в виде строк «ключ=значение»

pg_transform

В каталоге pg_transform хранится информация о преобразованиях, которые являются механизмом адаптации типов данных к процедурным языкам. Дополнительную информацию см. в описании команды CREATE TRANSFORM.

Таблица 55. Столбцы pg_transform

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
trftype | oid | pg_type.oid | OID типа данных, для которого предназначено это преобразование
trflang | oid | pg_language.oid | OID языка, для которого предназначено это преобразование
trffromsql | regproc | pg_proc.oid | OID функции, используемой при преобразовании типа данных для передачи в процедурный язык (например, параметры функции). Содержит ноль, если должно применяться поведение по умолчанию.
trftosql | regproc | pg_proc.oid | OID функции, используемой при преобразовании значений, полученных из процедурного языка (например, возвращаемых значений) к типу данных. Содержит ноль, если должно применяться поведение по умолчанию.

pg_trigger

В каталоге pg_trigger содержится информация о триггерах в таблицах и представлениях. Дополнительную информацию см. в описании команды CREATE TRIGGER.

Таблица 56. Столбцы pg_trigger

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
tgrelid | oid | pg_class.oid | Таблица, к которой относится этот триггер
tgparentid | oid | pg_trigger.oid | Родительский триггер, из которого клонируется данный (это происходит, когда партиции создаются или присоединяются к партиционированной таблице); ноль, если данный триггер — не клон
tgname | name |  | Имя триггера (должно быть уникальным среди триггеров одной таблицы)
tgfoid | oid | pg_proc.oid | Вызываемая функция
tgtype | int2 |  | Битовая маска, определяющая условия срабатывания триггера
tgenabled | char |  | Задает режимы session_replication_role, при которых срабатывает триггер. О = триггер срабатывает в режимах «origin» (источник) и «local» (локально), D = триггер выключен (disabled), R = триггер срабатывает в режиме «replica» (реплика), A = триггер срабатывает всегда (always).
tgisinternal | bool |  | True, если триггер генерируется внутри системы (обычно для принудительного применения ограничения, определенного tgconstraint)
tgconstrrelid | oid | pg_class.oid | Таблица, на которую ссылается ограничение ссылочной целостности (ноль, если триггер предназначен не для реализации ограничения ссылочной целостности)
tgconstrindid | oid | pg_class.oid | Индекс, поддерживающий ограничение уникальности, первичного ключа, ссылочной целостности или исключения (ноль, если триггер предназначен не для реализации этих типов ограничений)
tgconstraint | oid | pg_constraint.oid | Связанная с триггером запись в pg_constraint (ноль, если триггер предназначен не для ограничения)
tgdeferrable | bool |  | True, если триггер ограничения является откладываемым
tginitdeferred | bool |  | True, если триггер ограничения изначально отложен
tgnargs | int2 |  | Количество строк аргументов, переданных в триггерную функцию
tgattr | int2vector | pg_attribute.attnum | Номера столбцов, если триггер является специфичным для столбца; в противном случае — пустой массив
tgargs | bytea |  | Строки аргументов для передачи триггеру, каждая завершается NULL
tgqual | pg_node_tree |  | Дерево выражений (в представлении nodeToString()) для условия триггера WHEN или NULL, если оно отсутствует
tgoldtable | name |  | Имя предложения REFERENCING для OLD TABLE или NULL, если оно отсутствует
tgnewtable | name |  | Имя предложения REFERENCING для NEW TABLE или NULL, если оно отсутствует

В настоящее время триггеры, относящиеся к столбцам, поддерживаются только для событий UPDATE, поэтому поле tgattr актуально только для этого типа событий. Поле tgtype может также содержать биты для других типов событий, но считается, что они распространяются только на таблицы, независимо от значения поля tgattr.

Примечание
Когда поле tgconstraint имеет отличное от нуля значение, поля tgconstrrelid, tgconstrindid, tgdeferrable и tginitdeferred в основном дублируют ссылочную запись в pg_constraint. Тем не менее, неоткладываемый триггер может быть связан с откладываемым ограничением: ограничения внешнего ключа могут иметь и откладываемые, и неоткладываемые триггеры.

Примечание
Если отношение имеет какие-либо триггеры в этом каталоге, поле pg_class.relhastriggers должно иметь значение true.

pg_ts_config

В каталоге pg_ts_config содержатся записи, представляющие конфигурации текстового поиска. Конфигурация задает определенный синтаксический анализатор текстового поиска и список словарей, которые будут использоваться для каждого из типов выходных фрагментов синтаксического анализатора. Анализатор хранится в записи pg_ts_config, но сопоставление фрагмент-словарь определяется подчиненными записями в pg_ts_config_map.

Таблица 57. Столбцы pg_ts_config

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
cfgname | name |  | Имя конфигурации текстового поиска
cfgnamespace | oid | pg_namespace.oid | OID пространства имен, содержащего эту конфигурацию
cfgowner | oid | pg_authid.oid | Владелец этой конфигурации
cfgparser | oid | pg_ts_parser.oid | OID синтаксического анализатора текстового поиска для этой конфигурации

pg_ts_config_map

В каталоге pg_ts_config_map содержатся записи, показывающие, к каким словарям текстового поиска следует обращаться и в каком порядке, для каждого типа фрагмента, выдаваемого синтаксическим анализатором каждой конфигурации текстового поиска.

Таблица 58. Столбцы pg_ts_config_map

Имя | Тип | Ссылки | Описание
mapcfg | oid | pg_ts_config.oid | OID записи из pg_ts_config, которой принадлежит эта запись сопоставления
maptokentype | int4 |  | Тип фрагмента, создаваемого синтаксическим анализатором конфигурации
mapseqno | int4 |  | Порядок, в котором следует обращаться к этой записи (сначала меньшие mapseqno)
mapdict | oid | pg_ts_dict.oid | OID словаря текстового поиска, к которому нужно обращаться

pg_ts_dict

В каталоге pg_ts_dict содержатся записи, определяющие словари текстового поиска. Словарь зависит от шаблона текстового поиска, который задает все необходимые функции реализации; сам словарь предоставляет значения для настраиваемых пользователем параметров, поддерживаемых шаблоном. Такое разделение позволяет создавать словари пользователям без прав. Параметры задаются текстовой строкой dictinitoption, формат и значение которой различаются в зависимости от шаблона.

Таблица 59. Столбцы pg_ts_dict

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
dictname | name |  | Имя словаря текстового поиска
dictnamespace | oid | pg_namespace.oid | OID пространства имен, содержащего этот словарь
dictowner | oid | pg_authid.oid | Владелец этого словаря
dicttemplate | oid | pg_ts_template.oid | OID шаблона текстового поиска для этого словаря
dictinitoption | text |  | Строка параметра инициализации для этого шаблона

pg_ts_parser

В каталоге pg_ts_parser содержатся записи, определяющие синтаксические анализаторы текстового поиска. Синтаксический анализатор отвечает за разбиение входного текста на лексемы и присвоение каждой лексеме типа фрагмента. Поскольку синтаксический анализатор должен быть реализован с помощью функций на языке уровня C/RUST, создавать новые синтаксические анализаторы могут только суперпользователи базы данных.

Таблица 60. Столбцы pg_ts_parser

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
prsname | name |  | Имя синтаксического анализатора текстового поиска
prsnamespace | oid | pg_namespace.oid | OID пространства имен, содержащего этот синтаксический анализатор
prsstart | regproc | pg_proc.oid | OID функции запуска синтаксического анализатора
prstoken | regproc | pg_proc.oid | OID функции синтаксического анализатора, выдающей следующий фрагмент
prsend | regproc | pg_proc.oid | OID функции синтаксического анализатора, завершающей анализ
prsheadline | regproc | pg_proc.oid | OID функции синтаксического анализатора, выдающей заголовки (ноль, если таковой нет)
prslextype | regproc | pg_proc.oid | OID функции анализатора лексических типов

pg_ts_template

В каталоге pg_ts_template содержатся записи, определяющие шаблоны текстового поиска. Шаблон — это основа реализации для класса словарей текстового поиска. Поскольку шаблон должен быть реализован с помощью функций на языке уровня C/RUST, создавать новые шаблоны могут только суперпользователи базы данных.

Таблица 61. Столбцы pg_ts_template

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
tmplname | name |  | Имя шаблона текстового поиска
tmplnamespace | oid | pg_namespace.oid | OID пространства имен, содержащего этот шаблон
tmplinit | regproc | pg_proc.oid | OID функции инициализации этого шаблона (ноль, если таковой нет)
tmpllexize | regproc | pg_proc.oid | OID функции выделения лексем этого шаблона

pg_type

В каталоге pg_type хранится информация о типах данных. Базовые и перечислимые (скалярные) типы создаются с помощью команды CREATE TYPE, а домены — с помощью CREATE DOMAIN. Составной тип автоматически создается для каждой таблицы в базе данных для представления структуры строк таблицы. Кроме того, составные типы можно создать с помощью CREATE TYPE AS.

Таблица 62. Столбцы pg_type

Имя
Тип
Ссылки
Описание

oid
oid

Идентификатор строки

typname
name

Имя типа данных

typnamespace
oid
pg_namespace.oid
OID пространства имен, содержащего этот тип

typowner
oid
pg_authid.oid
Владелец данного типа

typlen
int2

Для типа фиксированного размера typlen — это число байтов во внутреннем представлении типа. Но для типа переменной длины typlen будет отрицательным. -1 обозначает тип «varlena» (он содержит машинное слово, указывающее длину), -2 обозначает строку в стиле C, завершающуюся значением NULL.

typbyval
bool

typbyval определяет, будут ли внутренние функции передавать значение этого типа по значению или по ссылке. typbyval предпочтительно иметь значение false, если только длина typlen не равна 1, 2 или 4 (или 8 на 64-битных машинах). Типы переменной длины всегда передаются по ссылке. Обратите внимание, что typbyval может иметь значение false, даже если длина позволит передачу по значению.

typtype
char

typtype имеет значение b для базового типа (base), c для составного типа (composite) (например, типа строки таблицы), d для домена (domain), e для перечислимого типа (enum), p для псевдотипа (pseudo) или r для типа диапазона (range). См. также описания столбцов typrelid и typbasetype.

typcategory
char

В typcategory задается произвольная классификация типов данных, которая используется синтаксическим анализатором для определения того, какие неявные приведения должны быть «предпочтительными». См. Таблицу 63.

typispreferred
bool

True, если тип является предпочтительным объектом приведения в пределах его typcategory

typisdefined
bool

True, если тип определен, false, если это запись-«заготовка» для еще не определенного типа. Когда typisdefined имеет значение false, ни на что, кроме имени типа, пространства имен и OID, нельзя полагаться.

typdelim
char

Символ, который разделяет два значения этого типа при разборе вводимых данных массива. Обратите внимание, что разделитель связан с типом данных элемента массива, а не с типом самого массива.

typrelid
oid
pg_class.oid
Если это составной тип (см. typtype), то данный столбец указывает на запись в pg_class, которая определяет соответствующую таблицу. (Для отдельно стоящего составного типа запись в pg_class на самом деле не представляет таблицу, но она все равно необходима для записей типа в pg_attribute, чтобы сослаться на них.) Для несоставных типов столбец содержит ноль.

typsubscript
regproc
pg_proc.oid
OID функции-обработчика индексации или ноль, если этот тип не поддерживает индексацию. Для «настоящих» типов массивов typsubscript = array_subscript_handler, но у других типов могут быть и другие функции-обработчики для реализации особого способа индексации.

typelem
oid
pg_type.oid
Если typelem не содержит ноль, то он указывает на другую строку в pg_type, определяя тип, возвращаемый при индексации. Если typsubscript содержит ноль, то и в этом столбце должен быть ноль. Однако typelem может содержать ноль, и когда typsubscript ненулевой, если при определении типа результата индексации обработчику не нужен typelem. Обратите внимание, что зависимость typelem подразумевает физическое включение типа элемента в этот тип, поэтому изменения DDL типа элемента могут быть ограничены присутствием типа результата.

typarray
oid
pg_type.oid
Если typarray не содержит 0, то он ссылается на другую строку в pg_type, описывающую «настоящий» тип массива, где этот тип является элементом

typinput
regproc
pg_proc.oid
Функция преобразования ввода (из текстового формата)

typoutput
regproc
pg_proc.oid
Функция преобразования вывода (в текстовый формат)

typreceive
regproc
pg_proc.oid
Функция преобразования ввода (из двоичного формата) или 0, если такой функции нет

typsend
regproc
pg_proc.oid
Функция преобразования вывода (в двоичный формат) или 0, если такой функции нет

typmodin
regproc
pg_proc.oid
Функция модификатора ввода или 0, если тип не поддерживает модификаторы

typmodout
regproc
pg_proc.oid
Функция модификатора вывода или 0 для использования стандартного формата

typanalyze
regproc
pg_proc.oid
Пользовательская функция ANALYZE или 0 для использования стандартной функции

typalign
char

typalign определяет выравнивание, необходимое при хранении значения этого типа. Это относится к хранилищу на диске, а также к большинству представлений значения внутри QHB. Когда несколько значений хранятся последовательно, например, в представлении строки данных на диске, заполнение дополнительными байтами происходит перед значением этого типа, чтобы тот начинался на указанной границе. Ссылка на выравнивание является началом первого значения в последовательности.
Возможные значения:

c = выравнивание по символам (char), т. е. выравнивание не требуется.

s = выравнивание по границе слова (short) (2 байта на большинстве процессоров).

i = выравнивание по границе целого (int) (4 байта на большинстве процессоров).

d = выравнивание по границе двойного слова (double) (8 байт на многих процессорах, но далеко не на всех).

typstorage
char

Значение typstorage для типов varlena (т. е. с typlen = -1) сообщает, подготовлен ли тип для помещения в TOAST и какова должна быть стратегия по умолчанию для атрибутов этого типа.
Возможные значения:

p (plain, простое): Значения всегда должны храниться простым образом (этот вариант используется для всех типов постоянной длины).

e (external, внешнее): Значения могут храниться во вторичном отношении «TOAST» (если в отношении это значение есть, см. столбец pg_class.reltoastrelid).

m (main, основное): Значения могут быть сжаты и храниться внутристрочно.

x (extended, расширенное): Значения могут быть сжаты и/или перемещены во вторичное отношение.

Для типов, которые могут быть помещены в TOAST, обычно применяется стратегия x. Обратите внимание, что значения m также можно переместить во вторичное хранилище, но только в крайнем случае (значения e и x перемещаются первыми).

typnotnull
bool

Поле typnotnull представляет собой ограничение «не NULL» для типа. Используется только для доменов.

typbasetype
oid
pg_type.oid
Если это домен (см. typtype), то typbasetype определяет тип, на котором он основан. Ноль, если этот тип не является доменом.

typtypmod
int4

Домены используют typtypmod, чтобы записать модификатор typmod, применяемый к их базовому типу (-1, если базовый тип не использует typtypmod). -1, если этот тип не является доменом.

typndims
int4

Значение typndims задает число размерностей массива для домена, определенного поверх массива (т. е. когда typbasetype является типом массива). Ноль для типов, отличных от доменов поверх типов массивов.

typcollation
oid
pg_collation.oid
Значение typcollation задает правило сортировки типа. Если тип не поддерживает правила сортировки, столбец содержит ноль. Базовый тип, который поддерживает правила сортировки, будет иметь ненулевое значение, как правило, DEFAULT_COLLATION_OID. Домен поверх сортируемого типа может иметь OID правила сортировки, отличающийся от такового его базового типа, если он был указан для домена.

typdefaultbin
pg_node_tree

Если поле typdefaultbin содержит не NULL, то это представление nodeToString() выражения по умолчанию для данного типа. Используется только для доменов.

typdefault
text

Поле typdefault имеет значение NULL, если с типом не связано значение по умолчанию. Если поле typdefaultbin содержит не NULL, то typdefault должно содержать понятную человеку версию выражения по умолчанию, представленного в typdefaultbin. Если typdefaultbin имеет значение NULL, а typdefault — нет, значит, в typdefault находится внешнее представление значения по умолчанию типа, которое можно передать в принадлежащую типу функцию преобразования ввода для получения константы.

typacl
aclitem[]

Права доступа; дополнительную информацию см. в разделе Права

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

В Таблице 63 перечислены определяемые системой значения typcategory. Любые будущие дополнения к этому списку также будут записаны прописными буквами ASCII. Все остальные символы ASCII зарезервированы для пользовательских категорий.

Таблица 63. Коды typcategory

Код | Категория
A | Типы массивов
B | Логические типы
C | Составные типы
D | Типы даты/времени
E | Перечислимые типы
G | Геометрические типы
I | Типы сетевых адресов
N | Числовые типы
P | Псевдотипы
R | Диапазонные типы
S | Строковые типы
T | Интервальные типы
U | Пользовательские типы
V | Типы битовых строк
X | Неизвестный тип (unknown)

pg_user_mapping

В каталоге pg_user_mapping хранятся сопоставления локальных пользователей с удаленными. Обычные пользователи не имеют доступа к этому каталогу, вместо этого им следует использовать представление pg_user_mappings.

Таблица 64. Столбцы pg_user_mapping

Имя | Тип | Ссылки | Описание
oid | oid |  | Идентификатор строки
umuser | oid | pg_authid.oid | OID сопоставляемой локальной роли или ноль, если пользовательское сопоставление является общедоступным
umserver | oid | pg_foreign_server.oid | OID внешнего сервера, содержащего это сопоставление
umoptions | text[] |  | Специальные параметры сопоставления пользователей в виде строк «ключ=значение»

Системные представления

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

Информационная схема предоставляет альтернативный набор представлений, которые перекрывают функциональные возможности системных представлений. Поскольку информационная схема является частью стандарта SQL, а описанные здесь представления специфичны для QHB, обычно лучше использовать информационную схему, если та выдает всю необходимую информацию.

В Таблице 65 перечислены системные представления, описанные в этом разделе. Более подробная документация по каждому представлению приведена ниже. Существуют некоторые дополнительные представления, обеспечивающие доступ к результатам работы сборщика статистических данных; они описаны в таблице Представления по собранной статистике.

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

Таблица 65. Системные представления

Имя представления | Назначение
pg_available_extensions | доступные расширения
pg_available_extension_versions | доступные версии расширений
pg_backend_memory_contexts | контекст памяти обслуживающего процесса
pg_config | параметры конфигурации во время компиляции
pg_cursors | открытые курсоры
pg_file_settings | сводка содержимого файла конфигурации
pg_group | группы пользователей баз данных
pg_hba_file_rules | сводка содержимого файла конфигурации аутентификации клиентов
pg_indexes | индексы
pg_locks | блокировки, удерживаемые или ожидаемые в настоящий момент
pg_matviews | материализованные представления
pg_policies | политики
pg_prepared_statements | подготовленные операторы
pg_prepared_xacts | подготовленные транзакции
pg_publication_tables | публикации и связанные с ними таблицы
pg_replication_origin_status | сведения об источниках репликации, включая ход выполнения репликации
pg_replication_slots | сведения о слоте репликации
pg_roles | роли базы данных
pg_rules | правила
pg_seclabels | метки безопасности
pg_sequences | последовательности
pg_settings | настройки параметров
pg_shadow | пользователи базы данных
pg_shmem_allocations | блоки, выделенные в общей памяти
pg_stats | статистика планировщика
pg_stats_ext | расширенная статистика планировщика
pg_stats_ext_exprs | расширенная статистика планировщика для выражений
pg_tables | таблицы
pg_timezone_abbrevs | сокращения часовых поясов
pg_timezone_names | названия часовых поясов
pg_user | пользователь базы данных
pg_user_mappings | сопоставление пользователей
pg_views | число просмотров

pg_available_extensions

Представление pg_available_extensions содержит список расширений, доступных для установки. См. также каталог pg_extension, в котором отображаются уже установленные расширения.

Таблица 66. Столбцы pg_available_extensions

Имя | Тип | Описание
name | name | Имя расширения
default_version | text | Имя версии по умолчанию или NULL, если версия не указана
installed_version | text | Текущая установленная версия расширения или NULL, если расширение не установлено
comment | text | Строка комментария из управляющего файла расширения

Представление pg_available_extensions доступно только для чтения.

pg_available_extension_versions

Представление pg_available_extension_versions содержит список конкретных версий расширений, доступных для установки. См. также каталог pg_extension, в котором отображаются уже установленные расширения.

Таблица 67. Столбцы pg_available_extension_versions

Имя | Тип | Описание
name | name | Имя расширения
version | text | Имя версии
installed | bool | True, если эта версия данного расширения уже установлена
superuser | bool | True, если это расширение разрешено устанавливать только суперпользователям (но см. trusted)
trusted | bool | True, если это расширение могут устанавливать обычные пользователи с надлежащими правами
relocatable | bool | True, если расширение можно переместить в другую схему
schema | name | Имя схемы, в которую необходимо установить расширение, или NULL, если оно частично или полностью переместимо
requires | name[] | Имена расширений, требующихся для данного, или NULL, если таковых нет
comment | text | Строка комментария из управляющего файла расширения

Представление pg_available_extension_versions доступно только для чтения.

pg_backend_memory_contexts

В представлении pg_backend_memory_contexts отображаются все контексты памяти серверного процесса, обслуживающего текущий сеанс.

Представление pg_backend_memory_contexts содержит одну строку для каждого контекста памяти.

Таблица 68. Столбцы pg_backend_memory_contexts

Имя | Тип | Описание
name | text | Имя контекста памяти
ident | text | Идентификационная информация о контексте памяти. Размер этого поля ограничен 1024 байтами
parent | text | Имя родителя этого контекста памяти
level | int4 | Расстояние от верхнего контекста памяти (TopMemoryContext) в дереве контекстов
total_bytes | int8 | Общее количество байтов, выделенное для этого контекста памяти
total_nblocks | int8 | Общее количество блоков памяти, выделенное для этого контекста памяти
free_bytes | int8 | Объем свободного пространства в байтах
free_chunks | int8 | Общее количество свободных участков памяти
used_bytes | int8 | Объем использованного пространства в байтах

По умолчанию представление pg_backend_memory_contexts могут прочитать только суперпользователями.

pg_config

Представление pg_config описывает конфигурационные параметры времени компиляции текущей установленной версии QHB. Оно предназначено, например, для использования программными пакетами, которым может понадобиться содействие QHB в поиске необходимых заголовочных файлов и библиотек.

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

Таблица 69. Столбцы pg_config

Имя | Тип | Описание
name | text | Имя параметра
setting | text | Значение параметра

pg_cursors

В представлении pg_cursors перечислены курсоры, доступные в настоящий момент. Курсоры могут быть определены несколькими способами:

через оператор DECLARE в SQL

через сообщение Bind в клиент-серверном протоколе QHB

через интерфейс программирования сервера (Server Programming Interface, SPI), как описано в разделе Функции интерфейса

Представление pg_cursors отображает курсоры, созданные любым из этих средств. Курсоры существуют только в течение транзакции, которая их определяет, если только они не были объявлены WITH HOLD. Таким образом, не удерживаемые курсоры присутствуют в представлении только до конца создавшей их транзакции.

Примечание
Курсоры используются внутри системы для реализации некоторых компонентов QHB, таких как процедурные языки. Поэтому представление pg_cursors может содержать курсоры, которые не были явно созданы пользователем.

Таблица 70. Столбцы pg_cursors

Имя | Тип | Описание
name | text | Имя курсора
statement | text | Дословная строка запроса, инициализировавшая объявления этого курсора
is_holdable | bool | True, если курсор удерживаемый (т. е. он может быть доступен после фиксации транзакции, в которой он был объявлен); иначе — false
is_binary | bool | True, если курсор был объявлен с указанием BINARY; иначе — false
is_scrollable | bool | True, если курсор прокручиваемый (т. е. он позволяет извлекать строки непоследовательным образом); иначе — false
creation_time | timestamptz | Время, когда был объявлен курсор

Представление pg_cursors доступно только для чтения.

pg_file_settings

Представление pg_file_settings выдает сводку содержимого файла (или файлов) конфигурации сервера. Для каждой записи «имя = значение», фигурирующей в этих файлах, данное представление содержит строку с примечаниями, показывающими, может ли это значение быть успешно применено. Также для проблем, не связанных с записью «имя = значение», например синтаксических ошибок в файлах, в этом представлении может появляться дополнительная строка (или строки).

Это представление полезно для проверки того, будут ли работать запланированные изменения в файлах конфигурации, или для диагностики предыдущего сбоя. Обратите внимание, что это представление сообщает о текущем содержимом файлов, а не о том, что было применено сервером в последний раз. (Для получения того состояния обычно достаточно представления pg_settings).

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

Таблица 71. Столбцы pg_file_settings

Имя | Тип | Описание
sourcefile | text | Полный путь к файлу конфигурации
sourceline | int4 | Номер строки в файле конфигурации, где появляется эта запись
seqno | int4 | Порядок, в котором обрабатываются операции (1..n)
name | text | Имя параметра конфигурации
setting | text | Значение, присваиваемое параметру
applied | bool | True, если значение может быть применено успешно
error | text | Сообщение об ошибке, указывающее, почему эта запись не может быть применена, или NULL

Если файл конфигурации содержит синтаксические ошибки или недопустимые имена параметров, сервер не будет пытаться применить какие-либо параметры из файла, а следовательно, все поля applied будут равны false. В таком случае в представлении будет одна или несколько строк со значением error, отличные от NULL, указывающие на проблему(ы). Иначе, если это возможно, будут применены отдельные параметры. Если отдельный параметр применить нельзя (например, из-за недопустимого значения или если параметр нельзя изменять после запуска сервера), для него будет записано соответствующее сообщение в поле error. Другой вариант, при котором поле applied будет содержать false, означает, что эта запись переопределяется более поздней записью для того же имени параметра; этот случай не считается ошибкой, поэтому в поле error ничего не появляется.

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

pg_group

В представлении pg_group показаны имена и члены всех ролей, отмеченных как не имеющие признака rolcanlogin, что примерно соответствует набору ролей, которые использовались как группы.

Таблица 72. Столбцы pg_group

Имя | Тип | Ссылки | Описание
groname | name | pg_authid.rolname | Имя группы
grosysid | oid | pg_authid.oid | Идентификатор этой группы
grolist | oid[] | pg_authid.oid | Массив, содержащий идентификаторы ролей в этой группе

pg_hba_file_rules

Представление pg_hba_file_rules выдает сводку содержимого файла конфигурации аутентификации клиентов, qhb_hba.conf. Для каждой непустой строки файла, не имеющей комментариев, в этом представлении содержится строка с примечаниями, показывающими, может ли это правило быть успешно применено.

Это представление может быть полезно для проверки того, будут ли работать запланированные изменения в файле конфигурации аутентификации, или для диагностики предыдущего сбоя. Обратите внимание, что это представление сообщает о текущем содержимом файла, а не о том, что было загружено сервером в последний раз.

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

Таблица 73. Столбцы pg_hba_file_rules

Имя | Тип | Описание
line_number | int4 | Номер строки этого правила в qhb_hba.conf
type | text | Тип соединения
database | text[] | Список имен баз данных, к которым применяется данное правило
user_name | text[] | Список имен пользователей и ролей, к которым применяется данное правило
address | text | Имя хоста или IP-адрес, либо одно из значений: all, samehost или *samenet, либо NULL для локальных соединений
netmask | text | Маска IP-адреса или NULL, если это неприменимо
auth_method | text | Способ аутентификации
options | text[] | Параметры, указанные для метода аутентификации, если таковые имеются
error | text | Сообщение об ошибке, указывающее, почему эта строка не может быть обработана, или NULL

Обычно строка, отражающая неверную запись, будет иметь значения только для полей line_number и error.

Более подробную информацию о конфигурации аутентификации клиентов см. в главе Аутентификация клиентского приложения.

pg_indexes

Представление pg_indexes предоставляет доступ к полезной информации о каждом индексе в базе данных.

Таблица 74. Столбцы pg_indexes

Имя | Тип | Ссылки | Описание
schemaname | name | pg_namespace.nspname | Имя схемы, содержащей таблицу и индекс
tablename | name | pg_class.relname | Имя таблицы, для которой создан индекс
indexname | name | pg_class.relname | Имя индекса
tablespace | name | pg_tablespace.spcname | Имя табличного пространства, содержащего индекс (NULL, если это пространство по умолчанию для базы данных)
indexdef | text |  | Определение индекса (реконструированная команда CREATE INDEX)

pg_locks

Представление pg_locks предоставляет доступ к информации о блокировках, удерживаемых активными процессами на сервере базы данных. Подробное рассмотрение процесса блокировки см. в главе Управление параллельным доступом.

pg_locks содержит одну строку на каждый активный блокируемый объект, запрошенный режим блокировки и соответствующий процесс. Таким образом, один и тот же блокируемый объект может появляться много раз, если его блокируют или ожидают блокировки несколько процессов. Однако объект, который в настоящее время не имеет блокировок, не будет отображаться вообще.

Существует несколько различных типов блокируемых объектов: отношения целиком (например, таблицы), отдельные страницы отношений, отдельные кортежи отношений, идентификаторы транзакций (как виртуальные, так и постоянные) и общие объекты базы данных (идентифицируемые по OID класса и OID объекта, так же как и в pg_description или pg_depend). Также в виде отдельного блокируемого объекта представляется право на расширение отношения, а также право изменения значения pg_database.datfrozenxid. Кроме того, «необязательные» блокировки могут иметь номера, значения которых определяет пользователь.

Таблица 75. Столбцы pg_locks

Имя
Тип
Ссылки
Описание

locktype
text

Тип блокируемого объекта: relation (отношение), extend (расширение), page (страница), tuple (кортеж), transactionid (идентификатор транзакции), virtualxid (виртуальный идентификатор), object (объект), userlock (пользовательская блокировка) или advisory (необязательная). (См. также таблицу Типы событий ожидания.)

database
oid
pg_database.oid
OID базы данных, в которой существует цель блокировки, или ноль, если цель является общим объектом, или NULL, если цель является идентификатором транзакции

relation
oid
pg_class.oid
OID отношения, на которое направлена блокировка, или NULL, если цель не является отношением или частью отношения

page
int4

Номер страницы, на которую направлена блокировка в пределах отношения, или NULL, если цель не является страницей отношения или кортежем

tuple
int2

Номер кортежа, на который направлена блокировка внутри страницы, или NULL, если цель не является кортежем

virtualxid
text

Виртуальный идентификатор транзакции, на которую направлена блокировка, или NULL, если цель не является виртуальным идентификатором транзакции

transactionid
xid

Идентификатор транзакции, на которую направлена блокировка, или NULL, если цель не является идентификатором транзакции

classid
oid
pg_class.oid
OID системного каталога, содержащего целевой объект блокировки, или NULL, если целевой объект не является общим объектом базы данных

objid
oid
OID любого столбца
OID целевого объекта блокировки в системном каталоге или NULL, если целевой объект не является общим объектом базы данных

objsubid
int2

Номер столбца, на который направлена блокировка (поля classid и objid определяют саму таблицу), или ноль, если целевой объект является каким-либо другим общим объектом базы данных, или NULL, если целевой объект не является общим объектом базы данных

virtualtransaction
text

Виртуальный идентификатор транзакции, которая удерживает или ожидает эту блокировку

pid
int4

Идентификатор серверного процесса (PID), удерживающего или ожидающего эту блокировку, или NULL, если блокировка удерживается подготовленной транзакцией

mode
text

Название режима блокировки, удерживаемой или ожидаемой этим процессом (см. подразделы Блокировки на уровне таблицы и Уровень изоляции Serializable)

granted
bool

True, если блокировка удерживается, false, если блокировка ожидается

fastpath
bool

True, если блокировка была получена быстрым путем, false, если она получена через главную таблицу блокировки

waitstart
timestamptz

Время, когда серверный процесс начал ожидать эту блокировку, или NULL, если блокировка получена. Обратите внимание, что после начала ожидания в этом поле может кратковременно стоять NULL, несмотря на то, что в granted будет false.

Поле granted имеет значение true в строке, представляющей блокировку, удерживаемую указанным процессом. Значение false показывает, что этот процесс в настоящее время ожидает получения данной блокировки, свидетельствуя о том, что как минимум еще один процесс удерживает или ожидает блокировку того же объекта в конфликтном режиме. Процесс ожидания будет находиться в спящем режиме до тех пор, пока не будет снята другая блокировка (или обнаружена ситуация взаимоблокировки). Один процесс может ожидать получения не более одной блокировки за один раз.

Во время выполнения транзакции серверный процесс удерживает исключительную блокировку виртуального идентификатора транзакции. Если транзакции присваивается постоянный идентификатор (что обычно происходит только в том случае, если транзакция изменяет состояние базы данных), он также удерживает монопольную блокировку на постоянном идентификаторе транзакции до ее завершения. Когда процесс считает необходимым специально дождаться окончания другой транзакции, он делает это, пытаясь получить общую блокировку идентификатора другой транзакции (виртуального или постоянного, в зависимости от ситуации). Это произойдет только тогда, когда другая транзакция завершится и освободит свои блокировки.

Хотя кортежи являются блокируемым типом объекта, информация о блокировках на уровне строк хранится на диске, а не в памяти, и поэтому блокировки на уровне строк обычно в этом представлении не отображаются. Если процесс ожидает блокировки уровня строки, он обычно отображается в представлении как ожидающий постоянного идентификатора транзакции текущего владельца этой блокировки.

Необязательные блокировки можно получить по ключам, состоящим из одного значения bigint или двух значений integer. При выведении ключа bigint его старшая часть отображается в столбце classid, младшая — в столбце objid, а objsubid равен 1. Исходное значение bigint можно собрать заново с помощью выражения (classid::bigint << 32) | objid::bigint. Ключи integer отображаются так: первый в столбце classid, второй — в столбце objid, а objsubid равен 2. Фактическое значение ключей определяет пользователь. Необязательные блокировки являются локальными для каждой базы данных, поэтому столбец database имеет для них значение.

pg_locks обеспечивает глобальное представление всех блокировок в кластере баз данных, а не только тех, которые относятся к текущей базе данных. Хотя через соединение столбцов relation и pg_class.oid можно определить заблокированные отношения, это будет правильно работать только для отношений в текущей базе данных (т. е. тех, для которых столбец database — это либо OID текущей базы данных, либо ноль).

Столбец pid можно соединить со столбцом pid представления pg_stat_activity, что позволит получить дополнительную информацию о сеансе, удерживающем или ожидающем каждую блокировку, например:

SELECT * FROM pg_locks pl LEFT JOIN pg_stat_activity psa
    ON pl.pid = psa.pid;

Кроме того, при использовании подготовленных транзакций столбец virtualtransaction можно соединить со столбцом transaction представления pg_prepared_xacts, что позволит получить дополнительную информацию об удерживающих блокировки подготовленных транзакциях. (Подготовленная транзакция не может ожидать блокировки, но она продолжает удерживать блокировки, полученные ей в процессе выполнения.) Например:

SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
    ON pl.virtualtransaction = '-1/' || ppx.transaction;

Хотя и возможно получить информацию о том, какие из процессов блокируют другие процессы (и какие именно), соединив представление pg_locks с самим собой, крайне трудно сделать это правильно во всех деталях. Такому запросу пришлось бы кодировать знания о том, какие режимы блокировки конфликтуют с другими. Хуже того, представление pg_locks не выдает информацию ни о том, какие процессы опережают другие в очереди ожидания блокировки, ни о том, какие процессы являются параллельными рабочими процессами и от от имени каких других сеансов клиента они выполняются. Чтобы определить, каким процессом (или процессами) блокируется ожидающий процесс, лучше воспользоваться функцией pg_blocking_pids() (см. таблицу Информационные функции сеанса).

Представление pg_locks отображает данные как из диспетчера обычных блокировок, так и из диспетчера предикатных блокировок, которые являются отдельными системами; кроме того, диспетчер обычных блокировок подразделяет свои блокировки на обычные и полученные быстрым путем. Полная согласованность этих данных не гарантируется. При запросе к этому представлению данные о блокировках по быстрому пути (с fastpath = true) собираются с каждого серверного процесса по очереди, без замораживания состояние всего диспетчера блокировок, поэтому существует вероятность того, что во время сбора этой информации блокировки будут получены или освобождены. Обратите внимание, однако, что эти блокировки, как известно, не должны конфликтовать ни с какими другими действующими блокировками. После того, как во все серверные процессы были сделаны запросы для получения быстрых блокировок, оставшаяся часть диспетчера обычных блокировок блокируется как единое целое, и согласованный снимок всех оставшихся блокировок собирается атомарно. После разблокирования диспетчера обычных блокировок аналогичным образом блокируется диспетчер предикатных блокировок, и все предикатные блокировки собираются атомарно. Таким образом, за исключением быстрых блокировок, каждый диспетчер блокировок будет выдавать согласованный набор результатов, но поскольку мы не блокируем оба диспетчера блокировок одновременно, блокировки могут быть получены или освобождены после опроса диспетчера обычных блокировок и до опроса диспетчера предикатных блокировки.

Блокировка диспетчера обычных и/или предикатных блокировок может некоторым образом повлиять на производительность базы данных, если очень часто обращаться к этому представлению. Эти блокировки удерживаются не дольше, чем необходимо для получения данных от диспетчеров блокировок, но это не до конца исключает возможность влияния на производительность.

pg_matviews

Представление pg_matviews обеспечивает доступ к полезной информации о каждом материализованном представлении в базе данных.

Таблица 76. Столбцы pg_matviews

Имя | Тип | Ссылки | Описание
schemaname | name | pg_namespace.nspname | Имя схемы, содержащей материализованное представление
matviewname | name | pg_class.relname | Имя материализованного представления
matviewowner | name | pg_authid.rolname | Имя владельца материализованного представления
tablespace | name | pg_tablespace.spcname | Имя табличного пространства, содержащего материализованное представление (NULL, если это пространство по умолчанию для базы данных)
hasindexes | bool |  | True, если материализованное представление имеет (или недавно имело) какие-либо индексы
ispopulated | bool |  | True, если материализованное представление в данный момент заполнено
definition | text |  | Определение материализованного представления (реконструированный запрос SELECT)

pg_policies

Представление pg_policies обеспечивает доступ к полезной информации о каждой политике защиты на уровне строк в базе данных.

Таблица 77. Столбцы pg_policies

Имя | Тип | Ссылки | Описание
schemaname | name | pg_namespace.nspname | Имя схемы, содержащей таблицу с этой политикой
tablename | name | pg_class.relname | Имя таблицы с этой политикой
policyname | name | pg_policy.polname | Имя политики
polpermissive | text |  | Является ли эта политика разрешительной или ограничительной?
roles | name[] |  | Роли, к которым применяется эта политика
cmd | text |  | Тип команды, к которой применяется эта политика
qual | text |  | Выражение, добавленное к условиям барьера безопасности для запросов, к которым применяется эта политика
with_check | text |  | Выражение, добавленное к условиям WITH CHECK для запросов, которые пытаются добавить строки в эту таблицу

pg_prepared_statements

В представлении pg_prepared_statements отображаются все подготовленные операторы, доступные в текущем сеансе. Более подробную информацию о подготовленных операторах см. в описании команды PREPARE.

pg_prepared_statements содержит отдельную строку для каждого подготовленного оператора. Строки добавляются в представление при создании нового подготовленного оператора и удаляются при освобождении подготовленного оператора (например, с помощью команды DEALLOCATE).

Таблица 78. Столбцы pg_prepared_statements

Имя
Тип
Описание

name
text
Идентификатор подготовленного оператора

statement
text
Строка запроса, отправленная клиентом для создания этого подготовленного оператора. Для подготовленных операторов, созданных посредством SQL, это команда PREPARE, инициализированная клиентом. Для подготовленных операторов, созданных посредством клиент-серверного протокола, это текст самого подготовленного оператора.

prepare_time
timestamptz
Время, в которое был создан подготовленный оператор

parameter_types
regtype[]
Ожидаемые типы параметров для подготовленного оператора в виде массива regtype. OID, соответствующий элементу этого массива, может быть получен путем приведения значения regtype к oid.

from_sql
bool
True, если подготовленный оператор был создан посредством команды SQL PREPARE; false, если оператор был подготовлен посредством клиент-серверного протокола

generic_plans
int8
Сколько раз был выбран типовой план

custom_plans
int8
Сколько раз был выбран пользовательский план

Представление pg_prepared_statements доступно только для чтения.

pg_prepared_xacts

В представлении pg_prepared_xacts отображается информация о транзакциях, которые в настоящее время подготовлены для двухфазной фиксации (подробную информацию см. в описании команды PREPARE TRANSACTION).

pg_prepared_xacts содержит отдельную строку для каждой подготовленной транзакции. Запись удаляется при фиксации или откате транзакции.

Таблица 79. Столбцы pg_prepared_xacts

Имя | Тип | Ссылки | Описание
transaction | xid |  | Числовой идентификатор подготовленной транзакции
gid | text |  | Глобальный идентификатор, присвоенный этой транзакции
prepared | timestamptz |  | Время, в которое эта транзакция была подготовлена к фиксации
owner | name | pg_authid.rolname | Имя пользователя, выполнившего эту транзакцию
database | name | pg_database.datname | Имя базы данных, в которой была выполнена эта транзакция

При обращении к представлению pg_prepared_xacts внутренние структуры данных диспетчера транзакций на мгновение блокируются, а для отображения в представлении создается копия. Это гарантирует, что представление создает согласованный набор результатов, не блокируя при этом обычные операции дольше, чем это необходимо. Тем не менее, при частых обращениях к этому представлению это может некоторым образом сказаться на производительности базы данных.

pg_publication_tables

Представление pg_publication_tables выдает информацию о сопоставлении публикаций и таблиц, которые они содержат. В отличие от нижележащего каталога pg_publication_rel, это представление расширяет список публикаций, определенных как FOR ALL TABLES, поэтому для таких публикаций будет наличествовать строка для каждой соответствующей таблицы.

Таблица 80. Столбцы pg_publication_tables

Имя | Тип | Ссылки | Описание
pubname | name | pg_publication.pubname | Имя публикации
schemaname | name | pg_namespace.nspname | Имя схемы, содержащей таблицу
tablename | name | pg_class.relname | Имя таблицы

pg_replication_origin_status

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

Таблица 81. Столбцы pg_replication_origin_status

Имя | Тип | Ссылки | Описание
local_id | oid | pg_replication_origin.roident | Внутренний идентификатор узла
external_id | text | pg_replication_origin.roname | Внешний идентификатор узла
remote_lsn | pg_lsn |  | LSN исходного узла, до которого были реплицированы данные.
local_lsn | pg_lsn |  | LSN этого узла, в котором был реплицирован remote_lsn. Используется при асинхронной фиксации для сброса записей фиксации на диск перед сохранением данных.

pg_replication_slots

Представление pg_replication_slots выдает список всех слотов репликации, которые в настоящее время существуют в кластере баз данных, а также их текущее состояние.

Таблица 82. Столбцы pg_replication_slots

Имя
Тип
Ссылки
Описание

slot_name
name

Уникальный внутрикластерный идентификатор для слота репликации

plugin
name

Базовое имя разделяемого объекта, содержащего модуль вывода, который использует этот логический слот, или NULL для физических слотов.

slot_type
text

Тип слота — physical (физический) или logical (логический)

datoid
oid
pg_database.oid
OID базы данных, с которой связан этот слот, или NULL. Только логические слоты могут быть связаны с базой данных.

database
text
pg_database.datname
Имя базы данных, с которой связан этот слот, или NULL. Только логические слоты могут быть связаны с базой данных.

temporary
bool

True, если это слот временной репликации. Временные слоты не сохраняются на диске и автоматически удаляются при ошибке или по завершении сеанса.

active
bool

True, если этот слот в настоящий момент активно используется

active_pid
int4

Идентификатор процесса сеанса, использующего этот слот, если тот в настоящий момент активно используется. NULL, если слот не используется.

xmin
xid

Старейшая транзакция, которую этот слот должен сохранить в базе данных. Команда VACUUM не сможет удалить кортежи, удаленные любой более поздней транзакцией.

catalog_xmin
xid

Старейшая транзакция, влияющая на системные каталоги, которые этот слот должен сохранить в базе данных. Команда VACUUM не сможет удалить кортежи каталога, удаленные любой более поздней транзакцией.

restart_lsn
pg_lsn

Адрес (LSN) старейшей записи в WAL, которая все еще может понадобиться пользователю этого слота и поэтому не будет автоматически удаляться во время контрольных точек, если только этот LSN не отстает более чем на max_slot_wal_keep_size от текущего. NULL, если LSN этого слота никогда не был зарезервирован.

confirmed_flush_lsn
pg_lsn

Адрес (LSN), до которого пользователь логического слота подтвердил получение данных. Данные старее этого LSN уже не доступны. NULL для физических слотов.

wal_status
text

Состояние файлов WAL, нужных данному слоту.
Возможные значения:

reserved (резерв) означает, что объем требуемых файлов вписывается в max_wal_size.

extended (превышение) означает, что предел max_wal_size превышен, но файлы все еще сохраняются, потому что вписываются в wal_keep_size или нужны слоту репликации.

unreserved (нет резерва) означает, что для слота больше не сохраняются требуемые файлы WAL и некоторые из них будут удалены при следующей контрольной точке. Это состояние может смениться на reserved или extended.

lost (потеря) означает, что какие-либо из нужных файлов WAL были удалены и данный слот уже нельзя использовать.

Последние два значения можно увидеть только при неотрицательном значении max_slot_wal_keep_size. Если в restart_lsn содержится NULL, в этом поле также будет NULL.

safe_wal_size
int8

Объем в байтах, который может быть записан в WAL, чтобы этому слоту не грозила опасность оказаться в состоянии «lost». Для потерянных слотов равен NULL, как и при max_slot_wal_keep_size равном -1.

two_phase
bool

True, если этот слот задействован для декодирования подготовленных транзакций. Всегда false для физических слотов.

pg_roles

Представление pg_roles обеспечивает доступ к информации о ролях в базе данных. Это просто доступное для всех представление pg_authid, в котором скрыто поле пароля.

Таблица 83. Столбцы pg_roles

Имя | Тип | Ссылки | Описание
rolname | name |  | Имя роли
rolsuper | bool |  | Роль имеет права суперпользователя
rolinherit | bool |  | Роль автоматически наследует права ролей, членом которых является
rolcreaterole | bool |  | Роль может создавать другие роли
rolcreatedb | bool |  | Роль может создавать базы данных
rolcanlogin | bool |  | Роль может войти в систему. То есть эта роль может быть задана в качестве начального идентификатора авторизации сеанса
rolreplication | bool |  | Роль является ролью репликации. Роль репликации может запускать соединения для репликации и создавать и удалять слоты репликации.
rolconnlimit | int4 |  | Для ролей, которые могут войти в систему, задает максимальное число одновременных подключений, которые может создать эта роль. -1 означает отсутствие ограничений.
rolpassword | text |  | Не пароль (всегда читается как ********)
rolvaliduntil | timestamptz |  | Срок действия пароля (используется только для аутентификации по паролю); NULL, если срок не ограничен
rolbypassrls | bool |  | Роль обходит любую политику защиты уровня строки; дополнительную информацию см. в разделе Политики защиты строк.
rolconfig | text[] |  | Заданные для роли значения по умолчанию переменных конфигурации времени выполнения
oid | oid | pg_authid.oid | Идентификатор роли

pg_rules

Представление pg_rules обеспечивает доступ к полезной информации о правилах перезаписи запросов.

Таблица 84. Столбцы pg_rules

Имя | Тип | Ссылки | Описание
schemaname | name | pg_namespace.nspname | Имя схемы, содержащей таблицу
tablename | name | pg_class.relname | Имя таблицы, для которой используется это правило
rulename | name | pg_rewrite.rulename | Имя правила
definition | text |  | Определение правила (реконструированная команда создания)

Из представления pg_rules исключены правила ON SELECT для представлений и материализованных представлений; их можно увидеть в pg_views и pg_matviews.

pg_seclabels

Представление pg_seclabels предоставляет информацию о метках безопасности. Это более простая для обращения версия каталога pg_seclabel.

Таблица 85. Столбцы pg_seclabels

Имя | Тип | Ссылки | Описание
objoid | oid | OID любого столбца | OID объекта, к которому относится эта метка безопасности
classoid | oid | pg_class.oid | OID системного каталога, в котором отображается этот объект
objsubid | int4 |  | Для метки безопасности в столбце таблицы это номер столбца (objoid и classoid указывают на саму таблицу). Для всех других типов объектов этот столбец содержит нуль.
objtype | text |  | Тип объекта, к которому применяется эта метка, в текстовом виде
objnamespace | oid | pg_namespace.oid | OID пространства имен для этого объекта, если применимо; иначе NULL
objname | text |  | Имя объекта, к которому применяется эта метка, в текстовом виде
provider | text | pg_seclabel.provider | Поставщик меток безопасности, связанный с этой меткой
label | text | pg_seclabel.label | Метка безопасности, применяемая к этому объекту

pg_sequences

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

Таблица 86. Столбцы pg_sequences

Имя
Тип
Ссылки
Описание

schemaname
name
pg_namespace.nspname
Имя схемы, содержащей последовательность

sequencename
name
pg_class.relname
Имя последовательности

sequenceowner
name
pg_authid.rolname
Имя владельца последовательности

data_type
regtype
pg_type.oid
Тип данных последовательности

start_value
int8

Начальное значение последовательности

min_value
int8

Минимальное значение последовательности

max_value
int8

Максимальное значение последовательности

increment_by
int8

Значение приращения последовательности

cycle
bool

Зацикливается ли последовательность

cache_size
int8

Размер кэша последовательности

last_value
int8

Последнее значение последовательности, записанное на диск. Если используется кэширование, это значение может быть больше, чем последнее значение, полученное из последовательности. NULL, если последовательность еще не была прочитана. Также содержит NULL, если текущий пользователь не имеет для последовательности прав USAGE или SELECT.

pg_settings

Представление pg_setting обеспечивает доступ к параметрам времени выполнения сервера. В сущности это альтернативный интерфейс к командам SHOW и SET. Также оно обеспечивает доступ к некоторым фактам о каждом параметре, которые нельзя получить напрямую при помощи команды SHOW, например, минимальные и максимальные значения.

Таблица 87. Столбцы pg_settings

Имя | Тип | Описание
name | text | Имя параметра конфигурации времени выполнения
setting | text | Текущее значение параметра
unit | text | Неявно подразумеваемая единица измерения параметра
category | text | Логическая группа параметра
short_desc | text | Краткое описание параметра
extra_desc | text | Дополнительное, более детальное, описание параметра
context | text | Контекст, необходимый для установки значения параметра (см. ниже)
vartype | text | Тип параметра (bool, enum, integer, real или string)
source | text | Источник текущего значения параметра
min_val | text | Минимальное допустимое значение параметра (NULL для нечисловых значений)
max_val | text | Максимально допустимое значение параметра (NULL для нечисловых значений)
enumvals | text[] | Допустимые значения параметра-перечисления (NULL для значений не перечислений)
boot_val | text | Значение параметра, принимаемое при запуске сервера, если параметр не задан иным образом
reset_val | text | Значение, к которое параметр будет сброшен командой RESET в текущем сеансе
sourcefile | text | Файл конфигурации, в котором было задано текущее значение (NULL для значений, полученных не из файлов конфигурации, или при проверке пользователем, который не является ни суперпользователем, ни членом роли pg_read_all_settings); полезно при использовании указаний include в файлах конфигурации
sourceline | int4 | Номер строки в файле конфигурации, в котором было задано текущее значение (NULL для значений, полученных не из файлов конфигурации, или при проверке пользователем, который не является ни суперпользователем, ни членом роли pg_read_all_settings).
pending_restart | bool | True, если значение было изменено в файле конфигурации, но требуется перезапуск; иначе — false.

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

internal

Эти параметры нельзя изменить непосредственно; они отражают значения, определенные внутри системы. Некоторые из них можно изменить, пересобрав сервер с другими параметрами конфигурации, или изменив параметры, передаваемые в qhb_bootstrap.

postmaster

Эти параметры можно применять только при запуске сервера, поэтому любое изменение требует его перезагрузки. Значения для этих параметров обычно хранятся в файле qhb.conf либо передаются в командной строке при запуске сервера. Разумеется, параметры любого из нижеследующих типов context также можно задать во время запуска сервера.

sighup

Изменения этих параметров можно вносить в qhb.conf без перезагрузки сервера. Если послать в qhbmaster (управляющий процесс) сигнал SIGHUP, он перечитает qhb.conf и применит изменения. Qhbmaster также перешлет сигнал SIGHUP своим дочерним процессам, чтобы все они приняли новое значение.

superuser-backend

Изменения этих параметров можно вносить в qhb.conf без перезагрузки сервера. Они также могут быть установлены для определенного сеанса в пакете запроса на подключение (например, через переменную окружения PGOPTIONS), но только если подключающийся пользователь является суперпользователем. Однако эти параметры никогда не изменяются в сеансе после его запуска. Если вы измените их внутри qhb.conf, отправьте сигнал SIGHUP в qhbmaster, чтобы тот перечитал qhb.conf. Новые значения будут влиять только на сеансы, запускаемые впоследствии.

backend

Изменения этих параметров можно вносить в qhb.conf без перезагрузки сервера. Они также могут быть установлены для определенного сеанса в пакете запроса на подключение (например, через переменную окружения PGOPTIONS); любой пользователь может внести такое изменение для своей сессии. Однако эти параметры никогда не изменяются в сеансе после его запуска. Если вы измените их внутри qhb.conf, отправьте сигнал SIGHUP в qhbmaster, чтобы тот перечитал qhb.conf. Новые значения будут влиять только на сеансы, запускаемые впоследствии.

superuser

Эти параметры можно задать из qhb.conf или в рамках сеанса с помощью команды SET, но только суперпользователи могут изменить их посредством SET. Изменения в qhb.conf будут влиять на существующие сеансы только в том случае, если в них командой SET не были заданы локальные в рамках сеанса значения.

user

Эти параметры можно задать из qhb.conf или в рамках сеанса с помощью команды SET. В рамках сеанса их локальное значение может изменить любой пользователь. Изменения в qhb.conf будут влиять на существующие сеансы только в том случае, если в них командой SET не были заданы локальные в рамках сеанса значения. .

Более подробную информацию о различных способах изменения этих параметров см. в разделе Настройка параметров.

В этом представлении специализированные параметры отображаются, только если был загружен определяющий их модуль расширения.

Это представление не допускает добавление или удаление строк, но допускает изменение. Команда UPDATE, примененная к строке pg_settings, равнозначна выполнению команды SET для этого именованного параметра. Это изменение влияет только на значение, используемое в текущем сеансе. Если команда UPDATE выполняется в рамках транзакции, которая затем прерывается, эффекты UPDATE пропадают при откате транзакции. После фиксации окружающей транзакции эти эффекты сохранятся до конца сеанса, если только не будут переопределены другой командой UPDATE или SET.

pg_shadow

В представлении pg_shadow показаны свойства всех ролей, которые помечены как rolcanlogin в pg_authid.

Такое имя («тень») обусловлено тем фактом, что эта таблица не должна быть доступна для чтения любому пользователю, поскольку она содержит пароли. pg_user — это доступное всем представление для pg_shadow, в котором стерто поле пароля.

Таблица 88. Столбцы pg_shadow

Имя | Тип | Ссылки | Описание
usename | name | pg_authid.rolname | Имя пользователя
usesysid | oid | pg_authid.oid | Идентификатор этого пользователя
usecreatedb | bool |  | Пользователь может создавать базы данных
usesuper | bool |  | Пользователь является суперпользователем
userepl | bool |  | Пользователь может инициировать потоковую репликацию и включать и выключать в системе режим резервного копирования.
usebypassrls | bool |  | Пользователь обходит любую политику защиты на уровне строк; дополнительную информацию см. в разделе Политики защиты строк.
passwd | text |  | Пароль (возможно зашифрованный); NULL, если пароля нет. Подробную информацию о том, как хранятся зашифрованные пароли, см. в описании каталога pg_authid.
valuntil | timestamptz |  | Срок действия пароля (используется только для аутентификации по паролю)
useconfig | text[] |  | Сеансовые значения по умолчанию для переменных конфигурации времени выполнения

pg_shmem_allocations

В представлении pg_shmem_allocations показаны блоки памяти, выделенные в основном сегменте общей памяти сервера. Сюда входят блоки, выделенные с использованием описанных в подразделе Общая память и легкие блокировки механизмов как для самого процесса postgres, так и для расширений.

Обратите внимание, что в этом представлении не показываются блоки, выделенные с использованием инфраструктуры динамической общей памяти.

Таблица 89. Столбцы pg_shmem_allocations

Имя | Тип | Описание
name | text | Имя блока в общей памяти. NULL для неиспользуемых блоков памяти и <anonymous> для анонимных блоков.
off | int8 | Смещение, с которого начинается выделенный блок. NULL для анонимных блоков, так как дополнительной информации о таких блоках нет.
size | int8 | Размер блока
allocated_size | int8 | Размер блока, включая выравнивающее дополнение. Для анонимных блоков информация о дополнении недоступна, поэтому значения в столбцах size и allocated_size всегда равны. Для свободной памяти объем дополнения не имеет смысла, так что эти столбцы тоже будут содержать одинаковые значения.

Анонимными считаются блоки, выделенные непосредственно функцией ShmemAlloc(), а не функцией ShmemInitStruct() или ShmemInitHash().

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

pg_stats

Представление pg_stats обеспечивает доступ к информации, хранящейся в каталоге pg_statistic. Это представление позволяет получить доступ только к строкам pg_statistic, соответствующим таблицам, которые пользователь может читать; таким образом, можно без опасений разрешить публичный доступ для чтения к этому представлению.

pg_stats также предназначено для представления информации в более удобочитаемом формате, чем нижележащий каталог, — за счет того, что схему этого представления необходимо расширять всякий раз, когда для pg_statistic определяются новые типы слотов.

Таблица 90. Столбцы pg_stats

Имя
Тип
Ссылки
Описание

schemaname
name
pg_namespace.nspname
Имя схемы, содержащей таблицу

tablename
name
pg_class.relname
Имя таблицы

attname
name
pg_attribute.attname
Имя столбца, описываемого этой строкой

inherited
bool

Если true, то данные этой строки включают значения в дочерних столбцах наследования, а не только в указанной таблице

null_frac
float4

Доля записей столбцов, имеющих значение NULL

avg_width
int4

Средняя ширина записей столбца в байтах

n_distinct
float4

Если больше нуля, то показывает примерное число различных значений в столбце. Если меньше нуля, то модуль числа показывает количество различных значений, разделенное на число строк. (Отрицательная форма используется, когда ANALYZE считает, что число различных значений, вероятно, будет увеличиваться по мере роста таблицы; положительная форма используется, когда считается, что столбец имеет фиксированное число возможных значений.) Например, значение -1 указывает на уникальный столбец, в котором число различных значений совпадает с числом строк.

most_common_vals
anyarray

Список наиболее распространенных значений в столбце. (NULL, если значения, встречающиеся чаще других, не обнаруживаются.)

most_common_freqs
float4[]

Список частот наиболее распространенных значений, т. е. количество вхождений каждого значения, деленное на общее количество строк. (NULL, если most_common_vals содержит NULL.)

histogram_bounds
anyarray

Список значений, разделяющих значения столбца на группы примерно одинаковой популяции. Значения most_common_vals, если они присутствует, исключаются из этого расчета гистограммы. (Этот столбец содержит NULL, если тип данных столбца не имеет оператора < или если список most_common_vals охватывает все популяции.)

correlation
float4

Статистическая корреляция между физическим порядком строк и логическим порядком значений столбцов. Диапазон допустимых значений: от -1 до +1. Когда значение близко к -1 или +1, сканирование индекса по столбцу будет считаться дешевле, чем когда оно близко к нулю, вследствие уменьшения случайного доступа к диску. (Столбец имеет значение NULL, если тип данных столбца не имеет оператора <.)

most_common_elems
anyarray

Список ненулевых значений элементов, наиболее часто появляющихся в пределах значений столбца. (NULL для скалярных типов.)

most_common_elem_freqs
float4[]

Список частот наиболее распространенных значений элементов, т. е. доля строк, содержащих хотя бы один экземпляр данного значения. За частотами каждого элемента следуют два или три дополнительных значения; это минимум и максимум предыдущих частот каждого элемента и (необязательно) частота элементов NULL. (NULL, когда most_common_elems содержит NULL.)

elem_count_histogram
float4[]

Гистограмма подсчетов различных значений элементов, отличных от NULL, в пределах значений столбца, за которой следует среднее количество различных элементов, отличных от NULL. (NULL для скалярных типов.)

Максимальным числом записей в полях массива можно управлять на уровне столбцов с помощью команды ALTER TABLE SET STATISTICS или глобально, задавая параметр времени выполнения default_statistics_target.

pg_stats_ext

Представление pg_stats_ext обеспечивает доступ к информации, хранящейся в каталогах pg_statistic_ext и pg_statistic_ext_data. Это представление дает доступ только к тем строкам pg_statistic_ext и pg_statistic_ext_data, которые соответствуют таблицам, которые пользователь может читать; таким образом, можно без опасений разрешить публичный доступ на чтение к этому представлению.

pg_stats_ext также предназначено для представления информации в более удобочитаемом формате, чем нижележащие каталоги, — за счет того, что схему этого представления необходимо расширять всякий раз, когда в pg_statistic_ext добавляются новые типы расширенной статистики.

Таблица 91. Столбцы pg_stats_ext

Имя
Тип
Ссылки
Описание

schemaname
name
pg_namespace.nspname
Имя схемы, содержащей таблицу

tablename
name
pg_class.relname
Имя таблицы

statistics_schemaname
name
pg_namespace.nspname
Имя схемы, содержащей расширенную статистику

statistics_name
name
pg_statistic_ext.stxname
Имя расширенной статистики

statistics_owner
oid
pg_authid.oid
Владелец расширенной статистики

attnames
name[]
pg_attribute.attname
Имена столбцов, для которых определена расширенная статистика

exprs
text[]

Выражения, включенные в объект расширенной статистики

kinds
text[]

Типы расширенной статистики, включенные для этой записи

n_distinct
pg_ndistinct

Количество различных комбинаций значений столбцов. Если больше нуля, то показывает примерное число различных значений в комбинации. Если меньше нуля, то модуль числа показывает количество различных значений, разделенное на число строк. (Отрицательная форма используется, когда ANALYZE считает, что число различных значений, вероятно, будет увеличиваться по мере роста таблицы; положительная форма используется, когда считается, что столбец имеет фиксированное число возможных значений.) Например, значение -1 указывает на уникальный столбец, в котором число различных значений совпадает с числом строк.

dependencies
pg_dependencies

Статистика для функциональной зависимости

most_common_vals
text[]

Список наиболее распространенных комбинаций значений в столбцах. (NULL, если комбинации, встречающиеся чаще других, не обнаруживаются.)

most_common_val_nulls
bool[]

Список флагов NULL для наиболее распространенных комбинаций значений. (NULL, если most_common_vals содержит NULL)

most_common_freqs
float8[]

Список частот наиболее распространенных комбинаций, т. е. количество вхождений каждой, деленное на общее количество строк. (NULL, если most_common_vals содержит NULL)

most_common_base_freqs
float8[]

Список базовых частот наиболее распространенных комбинаций, т. е. произведение частот отдельных значений. (NULL, если most_common_vals содержит NULL)

Максимальным числом записей в полях массива можно управлять на уровне столбцов с помощью команды ALTER TABLE SET STATISTICS или глобально, задав параметр времени выполнения default_statistics_target.

pg_stats_ext_exprs

Представление pg_stats_ext_exprs обеспечивает доступ к информации обо всех выражениях, включенных в объекты расширенной статистики, сочетая данные, хранящиеся в каталогах pg_statistic_ext и pg_statistic_ext_data. Это представление дает доступ только к тем строкам pg_statistic_ext и pg_statistic_ext_data, что соответствуют таблицам, которые пользователю разрешено читать, а потому можно без опасений открыть общий доступ на чтение этого представления.

Кроме того, pg_stats_ext_exprs специально разработано для подачи информации в более удобочитаемом виде, чем нижележащие каталоги — ценой того, что его схему необходимо расширять всякий раз, когда изменяется структура статистики в каталоге pg_statistic_ext.

Таблица 92. Столбцы pg_stats_ext_exprs

Имя
Тип
Ссылки
Описание

schemaname
name
pg_namespace.nspname
Имя схемы, содержащей таблицу

tablename
name
pg_class.relname
Имя таблицы, для которой определен объект статистики

statistics_schemaname
name
pg_namespace.nspname
Имя схемы, содержащей объект расширенной статистики

statistics_name
name
pg_statistic_ext.stxname
Имя объекта расширенной статистики

statistics_owner
name
pg_authid.rolname
Владелец объекта расширенной статистики

expr
text

Выражение, включенное в объект расширенной статистики

null_frac
float4

Доля записей выражения, содержащих NULL

avg_width
int4

Средний размер записей выражения в байтах

n_distinct
float4

Число больше нуля отражает примерное количество различных значений в выражении. Если число меньше нуля, его модуль отражает количество различных значений, деленное на количество строк. (Отрицательная форма используется, когда ANALYZE полагает, что количество различных значений, скорее всего, будет расти по мере роста таблицы; положительная форма используется, когда в выражении, по-видимому, будет фиксированное количество возможных значений.) Например, -1 указывает на выражение, в котором количество различных значений совпадает с количеством строк.

most_common_vals
anyarray

Список наиболее распространенных значений в выражении. (NULL, если не находятся значения, встречающиеся чаще других.)

most_common_freqs
float4[]

Список частот наиболее распространенных значений, т. е. число их вхождений, разделенное на общее число строк. (NULL, когда most_common_vals содержит NULL.)

histogram_bounds
anyarray

Список значений, разделяющих значения выражения на группы примерно одинакового заполнения. Значения в most_common_vals, если таковые имеются, исключаются из вычисления этой гистограммы. (Это выражение содержит NULL, если для типа данных выражения отсутствует оператор < или если в списке most_common_vals находится вся популяция.)

correlation
float4

Статистическая корреляция между физическим порядком строк и логическим порядком значений выражения. Диапазон допустимых значений: от -1 до +1. Когда значение близко к -1 или +1, сканирование индекса по выражению будет считаться дешевле, чем когда это значение близко к нулю, вследствие уменьшения случайного доступа к диску. (Это выражение содержит NULL, если для типа данных выражения отсутствует оператор <.)

most_common_elems
anyarray

Список элементов, отличных от NULL, наболее часто встречающихся в значениях выражения. (NULL для скалярных типов.)

most_common_elem_freqs
float4[]

Список частот наиболее распространенных элементов, т. е. доля строк, содержащих как минимум один экземпляр данного значения. За частотами по элементам следуют два или три дополнительных значения: минимум и максимум предшествующих частот по элементам и, при необходимости, частота элементов NULL. (NULL, когда most_common_elems содержит NULL.)

elem_count_histogram
float4[]

Гистограмма количеств различных элементов, отличных от NULL, в значениях этого выражения, за которой следует среднее количество элементов, отличных от NULL. (NULL для скалярных типов.)

Максимальным количеством записей в полях-массивах можно управлять на уровне столбцов с помощью команды ALTER TABLE SET STATISTICS или глобально, установив параметр времени выполнения default_statistics_target.

pg_tables

Представление pg_tables обеспечивает доступ к полезной информации о каждой таблице в базе данных.

Таблица 93. Столбцы pg_tables

Имя | Тип | Ссылки | Описание
schemaname | name | pg_namespace.nspname | Имя схемы, содержащей таблицу
tablename | name | pg_class.relname | Имя таблицы
tableowner | name | pg_authid.rolname | Имя владельца таблицы
tablespace | name | pg_tablespace.spcname | Имя табличного пространства, содержащего таблицу (NULL, если это пространство по умолчанию для базы данных)
hasindexes | bool | pg_class.relhasindex | True, если таблица имеет (или недавно имела) какие-либо индексы
hasrules | bool | pg_class.relhasrules | True, если таблица имеет (или когда-то имела) правила
hastriggers | bool | pg_class.relhastriggers | True, если таблица имеет (или когда-то имела) триггеры
rowsecurity | bool | pg_class.relrowsecurity | True, если в таблице включена защита уровня строк

pg_timezone_abbrevs

Представление pg_timezone_abbrevs содержит список сокращений часовых поясов, которые в настоящее время верно распознаются функциями ввода даты/времени. Содержимое этого представления меняется при изменении параметра времени выполнения timezone_abbreviations.

Таблица 94. Столбцы pg_timezone_abbrevs

Имя | Тип | Описание
abbrev | text | Аббревиатура часового пояса
utc_offset | interval | Смещение от UTC (положительное значение означает к востоку от Гринвича)
is_dst | bool | True, если это аббревиатура задает летнее время

Хотя большинство сокращений часовых поясов представляют собой фиксированные смещения от UTC, в некоторых поясах значение смещения менялось в ходе истории (подробную информацию см. в разделе Файлы конфигурации даты/времени). В таких случаях это представление отображает их текущее значение.

pg_timezone_names

Представление pg_timezone_names содержит список имен часовых поясов, которые распознаются командой SET TIMEZONE, а также связанные с ними сокращения, смещения UTC и статус летнего времени. (Технически QHB не использует UTC, потому что високосные секунды не обрабатываются). В отличие от сокращений, показанных в pg_timezone_abbrevs, многие из этих имен подразумевают набор правил перехода на летнее время. Поэтому связанная информация изменяется при пересечении локальных границ перехода на летнее время. Отображаемая информация вычисляется на основе текущего значения параметра CURRENT_TIMESTAMP.

Таблица 95. Столбцы pg_timezone_names

Имя | Тип | Описание
name | text | Название часового пояса
abbrev | text | Аббревиатура часового пояса
utc_offset | interval | Смещение от UTC (положительное значение означает к востоку от Гринвича)
is_dst | bool | True, если в данный момент установлено летнее время

pg_user

Представление pg_user обеспечивает доступ к информации о пользователях базы данных. Это просто общедоступное представление каталога pg_shadow, в котором скрыто поле пароля.

Таблица 96. Столбцы pg_user

Имя | Тип | Описание
usename | name | Имя пользователя
usesysid | oid | Идентификатор этого пользователя
usecreatedb | bool | Пользователь может создавать базы данных
usesuper | bool | Пользователь является суперпользователем
userepl | bool | Пользователь может инициировать потоковую репликацию и включать и выключать режим резервного копирования в системе.
usebypassrls | bool | Пользователь обходит каждую политику защиты уровня строк; дополнительную информацию см. в разделе Политики защиты строк.
passwd | text | Не пароль (всегда читается как ********)
valuntil | timestamptz | Срок действия пароля (используется только при аутентификации по паролю)
useconfig | text[] | Значения по умолчанию сеанса для переменных конфигурации времени выполнения

pg_user_mappings

Представление pg_user_mappings обеспечивает доступ к информации о сопоставлениях пользователей. По сути это общедоступное представление каталога pg_user_mapping, которое не показывает поле параметров, если у пользователя нет прав на его использование.

Таблица 97. Столбцы pg_user_mappings

Имя | Тип | Ссылки | Описание
umid | oid | pg_user_mapping.oid | OID сопоставления пользователей
srvid | oid | pg_foreign_server.oid | OID стороннего сервера, содержащего это сопоставление
srvname | name | pg_foreign_server.srvname | Имя стороннего сервера
umuser | oid | pg_authid.oid | OID сопоставляемой локальной роли; 0, если сопоставление пользователей задается для всех
usename | name |  | Имя локального пользователя для сопоставления
umoptions | text[] |  | Специфические параметры сопоставления пользователей в виде строк «ключ=значение»

Чтобы защитить информацию о пароле, сохраненную в качестве параметра сопоставления пользователей, столбец umoptions будет считываться как NULL, если не выполняется одно из следующих условий:

текущий пользователь является сопоставляемым пользователем, который владеет сервером или имеет для него право USAGE

текущий пользователь является владельцем сервера и прочитывается сопоставление для PUBLIC

текущий пользователь является суперпользователем

pg_views

Представление pg_views обеспечивает доступ к полезной информации о каждом представлении в базе данных.

Таблица 98. Столбцы pg_views

Имя | Тип | Ссылки | Описание
schemaname | name | pg_namespace.nspname | Имя схемы, содержащей представление
viewname | name | pg_class.relname | Имя представления
viewowner | name | pg_authid.rolname | Имя владельца представления
definition | text |  | Определение представления (реконструированный запрос SELECT)

Определение интерфейса метода доступа к таблице

В этой главе описывается интерфейс между основной системой QHB и методами доступа к таблицам (access method), которые управляют хранилищем для таблиц. Основная система мало что знает об этих методах доступа, кроме того, что описано в этой главе. Поэтому можно разработать новые типы методов доступа, написав дополнительный код.

Каждый метод доступа к таблице описывается строкой в системном каталоге pg_am. Запись в pg_am задает имя и функцию-обработчик (handler function) для метода доступа к таблице. Эти записи могут быть созданы и удалены с помощью команд SQL CREATE ACCESS METHOD и DROP ACCESS METHOD.

Функция обработчика метода доступа к таблице должна быть объявлена, чтобы принять один аргумент типа internal и чтобы вернуть псевдо-тип table_am_handler. Аргумент является фиктивным значением, которое необходимо для предотвращения вызова функции непосредственно из команд SQL. Результатом работы функции должен быть указатель на структуру типа TableAmRoutine, который содержит все необходимое для использования метода доступа к таблице. Возвращаемое значение должно иметь время жизни самого сервера, что обычно достигается путем определения его как static const переменной в глобальной области видимости. Структура TableAmRoutine, также называемая API-структурой метода доступа, определяет поведение метода доступа с помощью функций обратного вызова. Эти обратные вызовы являются указателями на обычные C-функции и не являются видимыми или вызываемыми на уровне SQL. Все обратные вызовы и их поведение определяются в структуре TableAmRoutine (с комментариями внутри структуры, определяющими требования к обратным вызовам). Большинство обратных вызовов имеют оберточные функции, которые документируются для пользователя (а не исполнителя) метода доступа к таблице. Для получения дополнительной информации обратитесь к файлу src/include/access/tableam.h.

Для реализации метода доступа исполнителю, как правило, требуется реализовать определенный для этого метода тип слота таблицы кортежей (см. src/include/executor/tuptable.h), который позволяет реализации иметь ссылки на кортежи метода доступа и получать доступ к столбцам кортежа.

В настоящее время способ, которым метод доступа фактически хранит данные, довольно неограничен. Например, можно, но не обязательно, использовать общий буферный кэш. В случае его использования, вероятно, имеет смысл использовать стандартную структуру страницы QHB, как описано в разделе Внутренняя структура страницы базы данных.

Одно довольно большое ограничение API метода доступа к таблице заключается в том, что в настоящее время, если метод доступа хочет поддерживать модификации и/или индексы, то необходимо, чтобы каждый кортеж имел идентификатор кортежа (TID), состоящий из номера блока и номера элемента (см. также раздел Внутренняя структура страницы базы данных). Нет строгой необходимости в том, чтобы части TIDs имели то же значение, которое они, например, имеют для кучи, но если требуется опциональная поддержка битового сканирования, тогда номер блока должен обеспечивать локальность.

Для обработки аварийного завершения метод доступа может использовать WAL или же пользовательские механизмы. Если выбран параметр WAL, можно использовать Общие записи WAL или реализовать новый тип записей WAL. Общие записи WAL просты, но требуют большего объема данных. Реализация нового типа записи WAL в настоящее время требует внесения изменений в основной код (в частности, src/include/access/rmgrlist.h).

Для реализации транзакционной поддержки таким образом, чтобы различные методы доступа к таблицам были доступны в рамках одной транзакции, вероятно, необходимо тесно интегрироваться с механизмом в src/backend/access/transam/xlog.c.

Любой разработчик нового способа доступа к таблице может обратиться к существующей реализации кучи, которая находится в src/backend/access/heap/ heapam_handler.c.

Определение интерфейса для индексных методов доступа

В этой главе описывается интерфейс между ядром QHB и методами доступа к индексам, которые управляют отдельными типами индексов. Ядро системы ничего не знает об индексах, кроме того, что описано здесь, поэтому можно разработать совершенно новые типы индексов, написав дополнительный код.

Все индексы в QHB - это то, что технически известно как вторичные индексы, то есть индекс физически отделен от файла таблицы, который он описывает. Каждый индекс хранится как собственное физическое отношение и описывается записью в каталоге pg_class. Содержание индекса полностью находится под контролем его метода доступа к индексу. На практике все методы доступа к индексам разделяют индексы на страницы стандартного размера, чтобы они могли использовать обычный диспетчер хранилища и диспетчер буферов для доступа к содержимому индекса. (Все существующие методы доступа к индексам, кроме того, используют стандартный макет страницы, описанный в разделе Внутренняя структура страницы базы данных, и большинство используют тот же формат для заголовков индексных записей, но эти решения не являются обязательными для метода доступа).

Индекс это средство эффективного сопоставления некоторых значений ключей с идентификаторами записей (TID) версий строк (записей) в родительской таблице индекса. TID состоит из номера блока и номера записи внутри этого блока (см. раздел Внутренняя структура страницы базы данных). Этой информации достаточно для извлечения определенной версии строки из таблицы. Индексы непосредственно не знают, что при использовании MVCC, может существовать несколько версий одной и той же логической строки; для индекса каждая запись является независимым объектом, который требует своей собственной записи в индексе. Таким образом, обновление строки всегда создает новые записи для всех индексов, в которых участвует запись, даже если значения ключей не изменились. (HOT оптимизация обновления записей является исключением из этого утверждения, но индексы не имеют с ней дело). Индексные записи для мертвых записей в таблице удаляются во время вакуума вместе с удалением самих мертвых записей.

Базовая структура API для индексов

Каждый метод доступа к индексу описывается строкой в системном каталоге pg_am. Запись в pg_am содержит имя и функцию-обработчик метода доступа к индексу. Эти записи могут быть созданы и удалены с помощью SQL команд CREATE ACCESS METHOD и DROP ACCESS METHOD.

Функция-обработчик индексного метода доступа должна принимать один аргумент типа internal и возвращать псевдо-тип index_am_handler. Аргумент является фиктивным значением, предназначенным для предотвращения вызова функций обработчика непосредственно из команд SQL. Результатом работы функции должна быть структура типа IndexAmRoutine, размещенная в куче с помощью palloc и содержащая всё, что ядро должно знать, чтобы использовать метод доступа к индексу. Структура IndexAmRoutine, также называемая API-структурой метода доступа, включает поля, задающие различные фиксированные свойства метода доступа, например, возможность поддержки многоколоночных индексов. Что еще более важно, она содержит указатели на функции, выполняющие всю реальную работу для индексного метода доступа. Эти вспомогательные функции являются простыми функциями на C и не видны и не вызываются на уровне SQL. Вспомогательные функции описаны в разделе Функции доступа к индексу .

Структура IndexAmRoutine определяется таким образом:

typedef struct IndexAmRoutine
{
    NodeTag     type;

    /*
     * Total number of strategies (operators) by which we can traverse/search
     * this AM.  Zero if AM does not have a fixed set of strategy assignments.
     */
    uint16      amstrategies;
    /* total number of support functions that this AM uses */
    uint16      amsupport;
    /* does AM support ORDER BY indexed column's value? */
    bool        amcanorder;
    /* does AM support ORDER BY result of an operator on indexed column? */
    bool        amcanorderbyop;
    /* does AM support backward scanning? */
    bool        amcanbackward;
    /* does AM support UNIQUE indexes? */
    bool        amcanunique;
    /* does AM support multi-column indexes? */
    bool        amcanmulticol;
    /* does AM require scans to have a constraint on the first index column? */
    bool        amoptionalkey;
    /* does AM handle ScalarArrayOpExpr quals? */
    bool        amsearcharray;
    /* does AM handle IS NULL/IS NOT NULL quals? */
    bool        amsearchnulls;
    /* can index storage data type differ from column data type? */
    bool        amstorage;
    /* can an index of this type be clustered on? */
    bool        amclusterable;
    /* does AM handle predicate locks? */
    bool        ampredlocks;
    /* does AM support parallel scan? */
    bool        amcanparallel;
    /* does AM support columns included with clause INCLUDE? */
    bool        amcaninclude;
    /* type of data stored in index, or InvalidOid if variable */
    Oid         amkeytype;

    /* interface functions */
    ambuild_function ambuild;
    ambuildempty_function ambuildempty;
    aminsert_function aminsert;
    ambulkdelete_function ambulkdelete;
    amvacuumcleanup_function amvacuumcleanup;
    amcanreturn_function amcanreturn;   /* can be NULL */
    amcostestimate_function amcostestimate;
    amoptions_function amoptions;
    amproperty_function amproperty;     /* can be NULL */
    ambuildphasename_function ambuildphasename;   /* can be NULL */
    amvalidate_function amvalidate;
    ambeginscan_function ambeginscan;
    amrescan_function amrescan;
    amgettuple_function amgettuple;     /* can be NULL */
    amgetbitmap_function amgetbitmap;   /* can be NULL */
    amendscan_function amendscan;
    ammarkpos_function ammarkpos;       /* can be NULL */
    amrestrpos_function amrestrpos;     /* can be NULL */

    /* interface functions to support parallel index scans */
    amestimateparallelscan_function amestimateparallelscan;    /* can be NULL */
    aminitparallelscan_function aminitparallelscan;    /* can be NULL */
    amparallelrescan_function amparallelrescan;    /* can be NULL */
} IndexAmRoutine;

Чтобы быть полезным, метод доступа к индексу также должен иметь одно или несколько семейств операторов и классов операторов, определенных в pg_opfamily, pg_opclass, pg_amop и pg_amproc. Эти записи позволяют планировщику определить, какие виды запросов могут использоваться с индексами данного метода доступа. Семейства операторов и классы описаны в Интерфейсные расширения для индексов, который является необходимым материалом для чтения этой главы.

Индивидуальный индекс определяется записью в pg_class, описывающей его как физическое отношение, плюс запись в pg_index, показывающей логическое содержимое индекса, то есть набор столбцов входящих в индекс, и семантику этих столбцов, то есть связь с классами операторов. Столбцы индекса (ключевые значения) могут быть либо простыми столбцами таблицы, для которой строится индекс, либо выражениями над строками этой таблицы. Обычно для индексного метода доступа не имеет значения, откуда берутся значения ключей индекса (ему всегда передаются предварительно вычисленные значения ключей), но очень важна информация о классе оператора в pg_index. Обе эти записи каталога могут быть доступны в составе структуры данных Relation, которая передается всем операциям с индексом.

Некоторые из полей флагов структуры IndexAmRoutine имеют нетривиальное назначение. Требования, предъявляемые к amcanunique, обсуждаются в разделе Проверка уникальности индекса. Флаг amcanmulticol утверждает, что метод доступа поддерживает многоколоночные индексы, в то время как amoptionalkey указывает, что метод доступа разрешает сканирование, когда для первого столбца индекса не задано ограничение. Когда amcanmulticol равен false, amoptionalkey, говорит, что метод доступа поддерживает сканирование с полным перебором без каких-либо ограничительных условий. Методы доступа, поддерживающие несколько столбцов в индексе, должны поддерживать сканирования, позволяющие пропускать ограничения для любого или всех столбцов после первого; однако им разрешено требовать, чтобы использовались ограничения для первого столбца индекса, об этом сигнализирует флаг amoptionalkey равный false. Одна из причин, по которой метод доступа может установить значение amoptionalkey равным false, если он не индексирует нулевые значения. Поскольку большинство индексируемых операторов являются строгими и, следовательно, не могут возвращать true, если поле содержит NULL, на первый взгляд кажется правильным не хранить записи индекса для нулевых значений: они никогда не могут быть возвращены при сканировании индекса. Однако этот аргумент неверен, если проверка индекса не содержит условия ограничения для данного столбца индекса. На практике это означает, что индексы, у которых amoptionalkey равным true, должен индексировать значения NULL, так как планировщик может решить использовать такой индекс без ключей сканирования вообще. Связанное с этим ограничение заключается в том, что метод доступа к индексам, поддерживающий несколько столбцов индекса, должен поддерживать индексирование NULL значений в столбцах после первого, поскольку планировщик будет предполагать, что индекс может использоваться для запросов, которые не ограничивают эти столбцы. Например, рассмотрим индекс на (a, b) и запрос с WHERE a = 4. Система будет считать, что индекс может быть использован для сканирования строк с помощью а = 4, что неверно, если индекс пропускает строки, где b - это null. Однако допустимо пропускать строки, в которых первый индексированный столбец имеет значение NULL. Метод доступа к индексу, позволяющий индексировать значения NULL, также может установить значение amsearchnulls в true, указывая, что он поддерживает предложения IS NULL и IS NOT NULL в условиях поиска.

Функции доступа к индексу

Функции построения и обслуживания индекса

Функции построения и обслуживания индекса, которые должен предоставить метод доступа к индексу в структуре IndexAmRoutine:

ambuild

IndexBuildResult *
ambuild (Relation heapRelation,
         Relation indexRelation,
         IndexInfo *indexInfo);

Создает новый индекс. Отношение индекса физически создано, но является пустым. Метод должен заполнить отношение любыми фиксированными данными, необходимыми для собственного функционирования, а также записями для всех строк, уже существующих в таблице. Обычно функция ambuild будет вызывать table_index_build_scan() для сканирования таблицы на наличие существующих записей и вычисления ключей, которые необходимо вставить в индекс. Функция должна возвращать структуру, размещенную в куче с помощью palloc и содержащую статистику о новом индексе.

ambuildempty

void
ambuildempty (Relation indexRelation);

Создает пустой индекс и записывает его в слой инициализации (INIT_FORKNUM) данного отношения. Этот метод вызывается только для нежурналируемых индексов. Пустой индекс, записанный в слой инициализации, будет скопирован поверх основного слоя отношения при каждом перезапуске сервера.

aminsert

bool
aminsert (Relation indexRelation,
          Datum *values,
          bool *isnull,
          ItemPointer heap_tid,
          Relation heapRelation,
          IndexUniqueCheck checkUnique,
          IndexInfo *indexInfo);

Вставляет новую запись в существующий индекс. Входные массивы values и isnull задают значения ключей для индексирования, heap_tid - это TID для индексации. Если метод доступа поддерживает уникальные индексы (его флаг amcanunique равен true), тогда аргумент checkUnique указывает выполняется или нет проверка уникальности. Это зависит от того, является ли ограничение уникальности откладываемым; смотрите раздел Проверка уникальности индекса для получения подробной информации. Обычно метод доступа требует только параметр heapRelation при выполнении проверки уникальности (так как должен будет заглянуть в таблицу, чтобы проверить живучесть записи).

Логическое значение результата функции является значимым только тогда, когда значение параметра checkUnique является UNIQUE_CHECK_PARTIAL. В этом случае true означает, что новая запись является уникальной, тогда как false означает, что она может быть неуникальной (и отложенная проверка уникальности должна быть запланирована). В других случаях рекомендуется всегда возвращать false.

Некоторые индексы могут индексировать не все записи. Если запись не будет индексироваться, параметр aminsert надо просто вернуть, ничего не делая.

Если индексный метод доступа хочет кэшировать данные через последовательные вставки индекса в инструкции SQL, он может выделить пространство в indexInfo->ii_Context и хранить указатель на данные внутри indexInfo->ii_AmCache (который изначально будет равен нулю).

ambulkdelete

IndexBulkDeleteResult *
ambulkdelete (IndexVacuumInfo *info,
              IndexBulkDeleteResult *stats,
              IndexBulkDeleteCallback callback,
              void *callback_state);

Удаляет запись(и) из индекса. Это операция "массового удаления", которая должна быть реализована путем сканирования всего индекса и проверки каждой записи, чтобы определить, следует ли ее удалить. Для каждой записи нужно вызвать функцию обратного вызова: callback( __TID__, callback_state) и проверить возвращаемое значение типа bool. Если возвращаемое значение равно true, запись необходимо удалить из индекса. Метод ambulkdelete должен возвращать либо NULL, либо структуру, размещенную в куче с помощью palloc и содержащую статистику о результатах операции удаления. Можно вернуть NULL, если индекс не был изменен в процессе операции VACUUM, в противном случае должна быть возвращена корректная статистика.

Из-за ограничения maintenance_work_mem, функцию ambulkdelete, возможно, потребуется вызывать несколько раз, когда требуется удалить много записей. Аргумент stats является результатом предыдущего вызова для этого индекса (он равен NULL для первого вызова в рамках операции VACUUM). Это позволяет методу доступа накапливать статистику по всей операции. Как правило, ambulkdelete будет изменять и возвращать ту же структуру, если переданная stats не является нулем.

amvacuumcleanup

IndexBulkDeleteResult *
amvacuumcleanup (IndexVacuumInfo *info,
                 IndexBulkDeleteResult *stats);

Выполнить очистку после операции VACUUM (до этого могло быть 0 или больше вызовов ambulkdelete). Функция не должна делать ничего, кроме возврата статистики, но может выполнить массовую очистку, такую как удаление пустых страниц индекса. Параметр stats содержит результат последнего вызова метода ambulkdelete, или NULL, если ambulkdelete не был вызван, потому что никакие записи не нужно было удалять. Результат функции является структурой, размещенной в куче с помощью palloc. Содержащаяся в нем статистика будет использована для обновления pg_class и выводится операцией VACUUM, если задан ключ VERBOSE. Метод может вернуть NULL, если индекс не изменялся во время операции VACUUM, в противном случае должна возвращаться корректная статистика..

Метод amvacuumcleanup также будет вызван по завершении операции ANALYZE. В этом случае параметр stats всегда имеет значение NULL, и любое возвращаемое значение игнорируется. Этот случай можно отличить проверкой info->analyze_only. Рекомендуется, чтобы в таком вызове метод доступа не делал ничего, кроме очистки после вставки, и это только в рабочем процессе autovacuum.

amcanreturn

bool
amcanreturn (Relation indexRelation, int attno);

Проверяет, поддерживает ли индекс сканирование только по индексу для данного столбца, возвращая его значение из индексной записи в виде структуры IndexTuple. Нумерация атрибутов начинается с 1, т.е. параметр attno для первого столбца должен быть равен 1. Метод возвращает true, если поддерживается, иначе false. Если метод доступа вообще не поддерживает сканирование только по индексу, то поле amcanreturn в его структуре IndexAmRoutine можно установить в значение NULL.

amcostestimate

void
amcostestimate (PlannerInfo *root,
                IndexPath *path,
                double loop_count,
                Cost *indexStartupCost,
                Cost *indexTotalCost,
                Selectivity *indexSelectivity,
                double *indexCorrelation,
                double *indexPages);

Оценивает стоимость на сканирование индекса. Эта функция полностью описана в разделе Функции оценки стоимости индекса ниже.

amoptions

bytea *
amoptions (ArrayType *reloptions,
           bool validate);

Разбирает и анализирует массив reloptions. Метод вызывается только, когда для индекса существует ненулевой массив reloptions. Аргумент reloptions это текстовый массив, содержащий записи форме name=value. Функция должна сформировать bytea значение, которое будет скопировано в поле rd_options записи индекса в relcache. Содержимое bytea определяется самим методом доступа; большинство стандартных методов доступа используют структуру StdRdOptions. Когда аргумент validate равен true, функция должна выдавать соответствующее сообщение об ошибке, если какой-либо из параметров в массиве reloptions не распознан или имеет недопустимое значение; если validate имеет значение false, недопустимые значения должны молча игнорироваться. (validate имеет значение false при загрузке уже сохраненных параметров из pg_catalog; недопустимая запись может быть найдена только в том случае, если метод доступа изменил свои правила для опций, и в этом случае игнорирование устаревших записей является уместным). Можно вернуть значение NULL, если требуется поведение по умолчанию.

amproperty

bool
amproperty (Oid index_oid, int attno,
            IndexAMProperty prop, const char *propname,
            bool *res, bool *isnull);

Метод amproperty позволяет методу доступа переопределить поведение по умолчанию pg_index_column_has_property и связанных функций. Если метод доступа не имеет никакого специального поведения для запросов свойств индекса, то поле amproperty в структуре IndexAmRoutine можно установить в значение NULL. В противном случае, метод amproperty будет вызван с аргументами index_oid и attno равными нулю для pg_indexam_has_property, или с валидным значением в index_oid и attno больше нуля для pg_index_column_has_property. Атрибут prop является значением enum, идентифицирующим проверяемое свойство, в то время как атрибут propname является исходной строкой имени свойства. Если ядро не распознает имя свойства, то параметр prop будет содержать AMPROP_UNKNOWN. Методы доступа могут определять имена настраиваемых свойств путем проверки propname на соответствие (используйте pg_strcasecmp, чтобы проверить соответствие единообразно с основным кодом); для имен, известных ядру базы данных, лучше проверить параметр prop. Если метод amproperty возвращает true, значит он определил результат проверки свойства. В этом случае он должен установить значение res или значение *isnull в true, чтобы вернуть NULL. (Обе указанные переменные инициализируются в false перед вызовом). Если метод amproperty возвращает false, тогда ядро базы данных продолжит свою обычную логику для определения результата проверки свойства.

Методы доступа, поддерживающие операторы сортировки, должны реализовывать проверку свойства AMPROP_DISTANCE_ORDERABLE, так как ядро не знает, как это сделать, и вернет значение NULL. Также стоит реализовать проверку AMPROP_RETURNABLE, если это можно сделать дешевле, чем открыть индекс и вызвать метод amcanreturn, что является поведением ядра базы данных по умолчанию. Поведение по умолчанию должно быть удовлетворительным для всех других стандартных свойств.

ambuildphasename

char *
ambuildphasename (int64 phasenum);

Возвращает текстовое имя заданного номера фазы сборки. Номера фаз-это те, которые сообщаются во время построения индекса через вызов pgstat_progress_update_param. Названия фаз затем доступны во view pg_stat_progress_create_index.

amvalidate

bool
amvalidate (Oid opclassoid);

Проверяет записи каталога для указанного класса операторов, насколько метод доступа может сделать это. Например, может убедится, что реализованы всех обязательные функций в методе доступа. Метод amvalidate должен вернуть false, если opclass является недопустимым. О проблемах следует сообщать с помощью вызова ereport.

Функции поддержки сканирования

Цель индекса, конечно же, заключается в поддержке сканирования для записей, соответствующих индексируемому условию WHERE, часто называемым квалификатором или ключом сканирования. Семантика индексного сканирования более подробно описана в разделе Индексное сканирование ниже. Метод доступа к индексу может поддерживать "простые" индексные сканирования, bitmap индексные сканирования или оба. Связанные со сканированием функции, которые должен или может предоставить метод доступа к индексам:

ambeginscan

IndexScanDesc
ambeginscan (Relation indexRelation,
             int nkeys,
             int norderbys);

Выполняет подготовку к сканированию индекса. Параметры nkeys и norderbys указывают количество операторов сравнения и сортировки, которые будут использоваться в сканировании; это может быть полезно для целей выделения памяти. Обратите внимание, что фактические значения ключей сканирования еще не предоставлены. Результатом работы должна быть структура, выделенная в куче с помощью palloc. В связи с особенностями реализации, метод доступа к индексу должен создать эту структуру путем вызова функции RelationGetIndexScan(). В большинстве случаев ambeginscan мало что делает, кроме данного вызова и, возможно, получения блокировок; интересные части запуска сканирования индекса находятся в функции amrescan.

amrescan

void
amrescan (IndexScanDesc scan,
          ScanKey keys,
          int nkeys,
          ScanKey orderbys,
          int norderbys);

Запускает или перезапускает сканирование индекса, возможно, с новыми ключами сканирования. (Для перезапуска с использованием ранее переданных ключей параметры keys и/или orderbys устанавливаются в NULL). Обратите внимание, что не допускается, чтобы количество ключей или операторов сортировки было больше, чем то, что передано ambeginscan. На практике функция перезапуска используется, когда в соединении со вложенным циклом выбирается новая внешняя запись и, поэтому, требуется новое сравнение с ключом, но структура, содержащая состояние сканирования, остается прежней.

amgettuple

boolean
amgettuple (IndexScanDesc scan,
            ScanDirection direction);

Выбирает следующую запись в заданном сканировании, двигаясь в указанном направлении (вперед или назад в индексе). Возвращает true, если запись получена, false, если не осталось совпадающих записей. Если запись найдена, ее TID сохраняется в структуре сканирования. Обратите внимание, что "успех" означает только то, что индекс содержит запись, соответствующую ключам сканирования, а не то, что запись обязательно все еще существует в таблице или пройдет тест видимости в снимке вызывающего объекта. В случае успеха, функция amgettuple должна также установить scan->xs_recheck в true или false. False означает, что запись индекса обязательно соответствует ключам сканирования. True означает, что это не обязательно, и условия, представленные ключами сканирования, должны быть повторно проверены после извлечения записи из таблицы. Это условие поддерживает операторы индекса c ”потерями". Обратите внимание, что повторная проверка будет распространяться только на условия сканирования; частичный индексный предикат (если таковой имеется) никогда не перепроверяется кодом, который вызывал amgettuple.

Если индекс поддерживает сканирование только по индексу (т.е. функция amcanreturn возвращает true для соответствующего свойства), тогда в случае успешного сканирования, метод доступа также должен проверить значение scan->xs_want_itup, и оно равно true, метод доступа обязан вернуть исходные индексированные данные для записи индекса. Данные могут быть возвращены в виде указателя IndexTuple, сохраненного в scan->xs_itup, с дескриптором записи scan->xs_itupdesc; или в форме указателя HeapTuple, сохраненного в scan->xs_hitup, с дескриптором записи scan->xs_hitupdesc. (Последний формат следует использовать при возврате данных, которые могут не вписываться в IndexTuple). В любом случае, управление данными, на которые ссылается указатель, является ответственностью метода доступа. Данные должны оставаться хорошими по крайней мере до следующего вызова amgettuple, amrescan, или amendscan.

Функция amgettuple должна быть реализована только в том случае, если метод доступа поддерживает “простые” индексные сканирования. Если это не так, то поле amgettuple в структуре IndexAmRoutine необходимо установить в значение NULL.

amgetbitmap

int64
amgetbitmap (IndexScanDesc scan,
             TIDBitmap *tbm);

Извлекает все записи в данном сканировании и добавляет их в TIDBitmap, переданной на вход (то есть добавляет TID найденных записей к уже находящимся в битовой карте). Функция возвращает количество извлеченных записей (это может быть только приблизительное значение, например, некоторые методы доступа не обнаруживают дубликаты). При вставке идентификаторов записей в коллекцию, amgettbitmap может сигнализировать, что для некоторых записей требуется повторная проверка условий сканирования. Это аналогично выходному параметру xs_recheck функции amgettuple. Примечание: в текущей реализации поддержка этой функции объединена с поддержкой хранилища с потерями самой битовой карты, поэтому вызывающий код повторно проверяет и условия сканирования, и частичный индексный предикат (если таковой имеется) для записей, помеченных для повторной проверки. Однако это не всегда может быть правдой. Функции amgettbitmap и amgettuple нельзя использовать в одном и том же сканировании индекса; есть и другие ограничения при использовании amgettbitmap, более подробно они описаны в разделе Индексное сканирование .

Функция amgettbitmap должна быть реализована только в том случае, если метод доступа поддерживает “bitmap” сканирование индекса. Если это не так, то поле amgettbitmap в его структуре IndexAmRoutine необходимо установить в значение NULL.

amendscan

void
amendscan (IndexScanDesc scan);

Завершает сканирование и освобождает ресурсы. Сама структура сканирования не должна быть освобождена, но любые блокировки и закрепления, полученные внутри метода доступа, должны быть освобождены, а также любая другая память, выделенная методом ambeginscan и другими функциями поддержки сканирования.

ammarkpos

void
ammarkpos (IndexScanDesc scan);

Отмечает текущее положение сканирования. Метод доступа должен поддерживать только одно запоминание позиции на сканирование.

Функция ammarkpos должна быть реализована только в том случае, если метод доступа поддерживает упорядоченные сканирования. Если это не так, то поле ammarkpos в его структуре IndexAmRoutine можно установить в значение NULL.

amrestrpos

void
amrestrpos (IndexScanDesc scan);

Восстанавливает сканирование до самой последней отмеченной позиции.

функция amrestrpos должна быть реализована только в том случае, если метод доступа поддерживает упорядоченные сканирования. Если это не так, то поле amrestrpos в его структуре IndexAmRoutine можно установить в значение NULL.

Функции поддержки параллельного сканирования

В дополнение к поддержке обычных индексных сканирований, некоторые типы индексов могут поддерживать параллельные индексные сканирования, позволяющие нескольким бэкендам совместно выполнять сканирования индекса. Метод доступа к индексам должен организовать все так, чтобы каждый взаимодействующий процесс возвращал подмножество записей, полученных обычным, непараллельным сканированием индекса, но таким образом, чтобы объединение этих подмножеств было равно набору записей, которые будут возвращены обычным, непараллельным сканированием индекса. Кроме того, хотя не требуется никакого глобального упорядочения записей, возвращаемых параллельным сканированием, порядок внутри этих подмножеств записей, возвращаемых в каждом взаимодействующем процессе, должен соответствовать запрошенному порядку. Для поддержки параллельного сканирования индексов могут быть реализованы следующие функции:

amestimateparallelscan

Size
amestimateparallelscan (void);

Оценивает и возвращает количество байт в динамической общей памяти, которое необходимо методу доступа для выполнения параллельного сканирования. (Это число является дополнением, а не заменой, объема памяти, необходимого для хранения независимой от метода доступа данных структуры ParallelIndexScanDescData).

Нет необходимости реализовывать эту функцию для методов доступа, которые не поддерживают параллельное сканирование или для которых требуется нулевое количество дополнительных байт памяти.

aminitparallelscan

void
aminitparallelscan (void *target);

Эта функция вызывается для инициализации динамической общей памяти в начале параллельного сканирования. Параметр target будет указывать, по крайней мере, на количество байт, возвращенных ранее функцией amestimateparallelscan, и эта функция может использовать данное количество памяти для хранения любых данных, которые ей требуются.

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

amparallelrescan

void
amparallelrescan (IndexScanDesc scan);

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

Индексное сканирование

В индексном сканировании метод доступа отвечает за возврат TID всех записей, соответствующих ключам сканирования. Метод доступа не участвует в фактической выборке этих записей из родительской таблицы индекса, а также в определении того, проходят ли они проверку видимости сканирования в снимке данных или другие условия.

Ключ сканирования-это внутреннее представление условия WHERE в форме index_key operator constant, где ключ индекса является одним из столбцов индекса, а оператор-одним из членов семейства операторов, связанных с этим столбцом индекса. Сканирование индекса имеет ноль или более ключей сканирования, связанных через И, то есть ожидается, что возвращенные записи, удовлетворяют всем указанным условиям.

Метод доступа может сообщить, что индекс является индексом с потерями или требует повторных проверок для конкретного запроса. Это означает, что сканирование индекса вернет все записи, удовлетворяющие ключу сканирования, а также, возможно, дополнительные записи, которые не удовлетворяют. Часть ядра базы данных, отвечающая за индексное сканирование, затем снова применит условия индекса к записи из таблицы, чтобы проверить, действительно ли она должна быть выбрана. Если опция повторной проверки не задана, сканирование индекса должно возвращать набор данных, полученный от метода доступа.

Обратите внимание, что метод доступа обязан убедиться, что он правильно находит все и только удовлетворяющие всем заданным ключам сканирования записи. Кроме того, ядро базы данных просто передаст все условия WHERE, соответствующие ключам индекса, и семейства операторов, без какого-либо семантического анализа на избыточность или противоречивость. Например, в выражении WHERE x>4 AND x>14, где x является столбцом в B-tree индексе, только в функции amrescan функции B-tree индекса можно понять, что первый ключ сканирования является избыточным и может быть отброшен. Необходимая степень предварительной обработки в функции amrescan зависит от степени, которая требуется методу доступа к индексу, чтобы уменьшить ключи сканирования до ”нормализованной" формы.

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

Методы доступа, всегда возвращающие записи в порядке хранения данных в индексе (например, btree), должны установить amcanorder в true. В настоящее время такие методы доступа должны использовать btree-совместимые стратегии для их операторов равенства и сортировки.

Методы доступа, поддерживающие операторы сортировки должны установить amcanorderbyop в true. Это указывает на то, что индекс способен возвращать записи в порядке, удовлетворяющем ORDER BY index_key operator constant. Модификаторы сканирования этой формы могут быть переданы в функцию amrescan как описано ранее.

Функция amgettuple имеет аргумент direction, который может принимать значения или ForwardScanDirection (обычный случай), или BackwardScanDirection. Если первый вызов после amrescan получит BackwardScanDirection, тогда набор совпадающих индексных записей должен сканироваться назад-вперед, а не в обычном направлении вперед-назад, поэтому методу amgettuple необходимо вернуть последнюю совпадающую запись в индексе, а не первую, как это обычно делается. (Это будет происходить только для методов доступа, установивших флаг amcanorder в true). После первого вызова, функции amgettuple необходимо быть готовым к дальнейшему сканированию в любом направлении от последней возвращенной записи. (Но если флаг amcanbackward имеет значение false, все последующие вызовы будут иметь то же направление, что и первый).

Методы доступа, поддерживающие упорядоченное сканирование, должны поддерживать "маркировку" позиции в сканировании и последующий возврат к отмеченной позиции. Одна и та же позиция может быть восстановлена несколько раз. Однако, необходимо сохранять только одно положение на сканирование; новый вызов функции ammarkpos переопределяет ранее отмеченную позицию. Методам доступа, не поддерживающим упорядоченное сканирование, не нужно реализовывать функции ammarkpos и amrestrpos внутри IndexAmRoutine; вместо этого установите эти указатели в значение NULL.

Текущее положение сканирования и отмеченная позиция (если такая есть) должны оставаться в согласованном состоянии во время одновременных вставок или удалений из индекса. Это нормально, если только что вставленная запись не возвращается сканированием, которое обнаружило бы запись, если бы она существовала, когда сканирование началось. Так же нормально для сканирования вернуть такую запись при повторном сканировании или возврате, даже если она не была возвращена в первый раз. Аналогично, одновременное удаление может быть отражено или не отражено в результатах сканирования. Важно, чтобы вставки или удаления не приводили к пропуску или к дублированию возвращаемых записей, которые не были вставлены или удалены.

Если индекс хранит исходные значения индексированных данных (а не некоторое их представление с потерями), полезно поддерживать сканирование только по индексу, в котором индекс возвращает фактические данные, а не только TID записи в таблице. Это позволит избежать ввода-вывода только в том случае, если карта видимости показывает, что TID находится на полностью видимой странице; в противном случае требуется прочитать запись из таблицы, чтобы проверить видимость согласно MVCC. Но это уже не касается метода доступа.

Вместо использования функции amgettuple, сканирование индекса можно выполнить с помощью вызова amgettbitmap, чтобы получить все записи в одном вызове. Это может быть заметно более эффективным, чем amgettuple, потому что это позволяет избежать циклов блокировки/разблокировки в рамках метода доступа. В принципе, вызов amgettbitmap должен иметь такие же последствия, как и последовательные вызовы amgettuple, но есть несколько ограничений, чтобы упростить дело. Прежде всего, amgettbitmap возвращает все записи сразу и маркировка или восстановление позиции сканирования не поддерживается. Во-вторых, записи возвращаются в виде битовой карты, которая не имеет никакой сортировки, поэтому amgettbitmap не принимает аргумент direction. (Операторы сортировки также никогда не будут предоставлены для такого сканирования). Кроме того, не предусмотрено использование только индексных сканирований с помощью amgettbitmap, так как нет никакого способа вернуть содержимое индексных записей. В-третьих, amgettbitmap не гарантирует никакой блокировки возвращенных записей, с последствиями, указанными в разделе Вопросы блокировки индекса .

Обратите внимание, что для метода доступа разрешено реализовать только amgettbitmap и не реализовывать amgettuple или наоборот, если его внутренняя реализация не подходит для одного API или другого.

Вопросы блокировки индекса

Методы доступа к индексам должны обрабатывать одновременные обновления индекса несколькими процессами. Ядро QHB получает AccessShareLock на индекс во время сканирования индекса и RowExclusiveLock при обновлении индекса (включая обычный VACUUM). Поскольку эти типы блокировок не конфликтуют, метод доступа отвечает за обработку любых более мелких блокировок, которые могут потребоваться. Исключительная блокировка индекса целиком может потребоваться только во время его создания, уничтожения или REINDEX .

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

Помимо собственных внутренних требований к согласованности индекса, параллельные обновления создают проблемы согласованности между родительской таблицей и индексом. Поскольку QHB отделяет доступ и обновления таблицы от доступа и обновления индекса, существуют окна, в которых индекс может быть несовместим с таблицей. Эта проблема решается с помощью следующих правил:

Новая запись в таблице создается перед созданием для нее записи в индексе. (Поэтому одновременное сканирование индекса, скорее всего, не сможет увидеть запись в таблице. Это нормально, потому что читатель индекса не заинтересован в получении незафиксированной строки в любом случае. Для более детальной информации смотрите раздел Проверка уникальности индекса).

Когда запись удаляется из таблицы (с помощью VACUUM), все ее индексные записи должны быть удалены первыми.

Сканирование индекса должно закреплять страницу индекса, содержащую элемент, возвращенный последним вызовом amgettuple, и функция ambulkdelete не может удалить записи со страниц, закрепленных другими бэкендами. Необходимость в этом правиле объясняется ниже.

Без третьего правила читатель индекса может видеть запись индекса непосредственно перед ее удалением VACUUM, а затем получить соответствующую запись из таблицы после того, как она была удалена VACUUM. Это не создает никаких серьезных проблем, если этот номер элемента еще не используется (после очистки), когда читатель достигает его, так как пустой слот элемента будет игнорироваться при извлечении записей из таблицы. Но что делать, если третий сервер уже повторно использовал данный слот элемента для чего-то другого? При использовании моментального снимка, совместимого с MVCC, нет никаких проблем, потому что новый пользователь слота наверняка будет слишком новым, чтобы пройти тест видимости для моментального снимка. Однако с моментальным снимком, не совместимым с MVCC (например SnapshotAny), возможна ситуация, когда будет принята и возвращена строка, которая фактически не соответствует ключам сканирования. Можно защититься от этого сценария, требуя, перепроверки ключей сканирования для записи из таблицы во всех случаях, но это слишком дорого. Вместо этого используется закрепление страницы индекса в качестве прокси, как указание, что читатель все еще может быть “в процессе” обращение от записи индекса к соответствующей записи в таблице. Создание таких блоков функцией ambulkdelete гарантирует, что VACUUM не сможет удалить запись из таблицы до того, как читатель закончит работать с ней. Это решение не требует больших затрат во время выполнения и добавляет накладные расходы на блокировку только в редких случаях, когда на самом деле существует конфликт.

Такое решение требует, чтобы сканирование индекса было “синхронным”: необходимо прочитать запись из таблицы сразу после сканирования соответствующей записи индекса. Это дорого по ряду причин. "Асинхронное" сканирование, при котором собирается много TID из индекса, a только через некоторое время выполняется чтение таблицы, требует гораздо меньше накладных расходов на блокировку индекса и позволяет более эффективно реализовать доступ к таблице. В соответствии с приведенным выше анализом, синхронный подход необходимо использовать для не-MVCC-совместимых снимков, а асинхронное сканирование применимо для запроса, использующего снимок MVCC.

В функции amgetbitmap, метод доступа не сохраняет защелок индексных страниц для возвращенных записей. Поэтому безопасно использовать такие сканирования только с MVCC-совместимыми моментальными снимками.

Когда флаг ampredlocks не установлен, любое сканирование, использующее этот метод доступа к индексу в сериализуемой транзакции, получит неблокирующую блокировку предиката на весь индекс. Это приведет к возникновению конфликта чтения и записи при вставке любой записи в этот индекс в параллельной сериализуемой транзакции. Если определенные шаблоны конфликтов чтения и записи обнаруживаются среди набора параллельных сериализуемых транзакций, одна из этих транзакций может быть отменена для защиты целостности данных. Когда флаг установлен, это указывает, что метод доступа к индексу реализует более мелкую блокировку предиката, которая будет стремиться к уменьшению частоты таких отмен транзакций.

Проверка уникальности индекса

QHB реализует ограничения уникальности SQL через использование уникальных индексов, которые являются индексами, запрещающими несколько записей с одинаковыми ключами. Метод доступа, поддерживающий эту функцию, устанавливает для флага amcanunique значение true. (В настоящее время его поддерживают только B-tree индексы). Столбцы, перечисленные в предложении INCLUDE не учитываются при обеспечении уникальности.

Из-за MVCC необходимо всегда разрешать наличие повторяющихся записей в индексе: записи могут ссылаться на последовательные версии одной логической строки. Поведение, которое требуется добиться, заключается в том, что ни один снимок MVCC не может включать две строки с одинаковыми ключами индекса. Это разбивается на следующие случаи, которые необходимо проверить при вставке новой строки в уникальный индекс:

Если конфликтующая валидная строка была удалена текущей транзакцией, это нормально. (В частности, поскольку обновление всегда удаляет старую версию строки перед вставкой новой версии, это позволит обновить строку без изменения ключа).

Если конфликтующая строка была вставлена пока еще незафиксированной транзакцией, необходимо дождаться, завершения этой транзакции. Если она откатывается назад, тогда нет никакого конфликта. Если транзакция фиксируется, не удаляя конфликтующую строку снова, существует нарушение уникальности. (На практике просто выполняется ожидание другой транзакции, а затем повторение проверки видимости).

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

Кроме того, непосредственно перед сообщением о нарушении уникальности в соответствии с вышеуказанными правилами, метод доступа должен повторно проверить жизнеспособность вставляемой строки. Если запись мертва, то не нужно сообщать о каких-либо нарушениях. (Этот случай не может произойти во время обычного сценария вставки строки, только что созданной текущей транзакцией. Однако, это может произойти при выполнении CREATE UNIQUE INDEX CONCURRENTLY.)

Метод доступа к индексу обязан выполнять данные тесты сам, а это означает, что он должен проверить в таблице состояние фиксации любой строки, которая имеет дубликат ключа в соответствии с содержимым индекса. Это, без сомнения, некрасиво и не модульно, но это позволяет избежать избыточной работы: если бы ядро базы данных делало отдельную проверку, то поиск конфликтующей строки в индексе был бы фактически повторен в процессе поиска места для вставки новой записи индекса строки. Более того, нет очевидного способа избежать гонок, если только проверка конфликта не является неотъемлемой частью вставки новой записи индекса.

Если ограничение уникальности является отложенным, возникает дополнительная сложность: нужно иметь возможность вставлять запись индекса для новой строки, но откладывать любую ошибку нарушения уникальности до конца оператора или даже позже. Чтобы избежать ненужных повторных поисков индекса, метод доступа к индексу должен выполнить предварительную проверку уникальности во время начальной вставки. Если проверка показывает, что нет конфликтующей живой записи, дальнейшие проверки не проводятся. В противном случае планируется повторная проверка, когда придет время применить ограничение. Если во время повторной проверки и вставленный запись, и какая-то другая запись с тем же ключом являются живыми, то необходимо сообщить об ошибке. (Обратите внимание, что в данном случае “живая” фактически означает “в HOT цепочке в индексе существует живая запись”). Чтобы реализовать повторные проверки, в функцию aminsert передается параметр checkUnique, имеющий одно из следующих значений:

UNIQUE_CHECK_NO указывает, что проверка уникальности не должна выполняться (это не уникальный индекс).

UNIQUE_CHECK_YES указывает, что этот уникальный индекс без отложенной проверки, и проверка уникальности должна быть выполнена немедленно, как описано выше.

UNIQUE_CHECK_PARTIAL указывает, что ограничение уникальности является отложенным. QHB будет использовать этот режим для вставки каждой строки в индекс. Метод доступа должен разрешать повторяющиеся записи в индексе и сообщать о любых потенциальных дубликатах, возвращая false из aminsert. Для каждой строки, для которой возвращается false, будет запланирована отложенная повторная проверка.

Метод доступа должен идентифицировать любые строки, которые могут нарушить ограничение уникальности. Ложные срабатывания не являются ошибкой. Это позволяет выполнить проверку, не дожидаясь завершения других транзакций; конфликты, сообщенные здесь, не рассматриваются как ошибки и будут перепроверены позже, к этому времени они могут больше не быть конфликтами.

UNIQUE_CHECK_EXISTING указывает, что это отложенная повторная проверка строки, отмеченной как потенциальное нарушение уникальности. Хотя это реализуется путем вызова aminsert, метод доступа не должен вставлять новую запись индекса в этом случае. Запись индекса уже присутствует. Вместо этого метод доступа должен проверить, есть ли другая "живая" запись в индексе. Если это так, и если целевая строка также все еще активна, сообщить об ошибке.

Рекомендуется, чтобы в вызове UNIQUE_CHECK_EXISTING, метод доступа далее проверял, что целевая строка, действительно, имеет существующую запись в индексе, и сообщает об ошибке, если ее нет. Это хорошая идея, потому что значения индексной записи, переданной в aminsert, будут пересчитаны. Если определение индекса включает в себя функции, которые на самом деле не являются неизменяемыми, возможна проверка неправильной области индекса. Верификация того, что целевая строка найдена при повторной проверке, убеждается, что сканируются те же значения записи, которые использовались в исходной вставке.

Функции оценки стоимости индекса

Функция amcostestimate предоставляет информацию, описывающую возможное сканирование индекса, включая списки Where и ORDER BY, которые были определены для использования с индексом. Метод должен возвращать оценку стоимости доступа к индексу и селективность предложений WHERE (то есть доли строк родительской таблицы, которые будут получены во время сканирования индекса). Для простых случаев почти вся работа оценщика затрат может быть выполнена путем вызова стандартных подпрограмм в оптимизаторе; смысл наличия функции amcostestimate заключается в том, чтобы позволить методу доступа к индексам предоставлять специфические знания о конкретных типах индексов, если это может улучшить стандартные оценки.

Каждая функция amcostestimate должна иметь следующие параметры:

void
amcostestimate (PlannerInfo *root,
                IndexPath *path,
                double loop_count,
                Cost *indexStartupCost,
                Cost *indexTotalCost,
                Selectivity *indexSelectivity,
                double *indexCorrelation,
                double *indexPages);

Первые три параметра являются входными данными:

root

Информация планировщика об обрабатываемом запросе.

path

Рассматривается путь доступа к индексу. Все поля, кроме indexTotalCost и indexSelectivity являются валидными.

loop_count

Число повторений сканирования индекса, которое должно быть учтено в оценке стоимости. Значение обычно будет больше единицы при рассмотрении параметризованного сканирования для использования внутри соединения с помощью вложенных запросов. Обратите внимание, что оценка стоимости все еще должна быть только для одного сканирования; большое значение loop_count означает, что его можно учесть для планирования кэширования при нескольких сканированиях.

Последние пять параметров являются выходными данными по ссылке:

indexStartupCost

Значение стоимость начальной обработки индекса

indexTotalCost

Общая стоимость обработки индекса

indexSelectivity

Индекс селективности

indexCorrelation

Коэффициент корреляции между порядком сканирования индекса и порядком базовой таблицы

indexPages

Число листовых страниц индекса

Обратите внимание, что функции оценки затрат необходимо писать на C/RUST, а не на SQL или любом другом доступном процедурном языке, поскольку они должны иметь доступ к внутренним структурам данных планировщика/оптимизатора.

Стоимость доступа к индексу должна быть рассчитана с использованием параметров, используемых src/backend/optimizer/path/costsize.c: последовательная выборка дискового блока имеет стоимость seq_page_cost, произвольная выборка имеет стоимость random_page_cost, и стоимость обработки одной строки индекса обычно следует принимать как cpu_index_tuple_cost. Кроме того, следует использовать соответствующее умножение на cpu_operator_cost для любых операторов сравнения, вызываемых во время обработки индекса (особенно вычисление самих условий индекса).

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

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

В indexSelectivity должно быть записано значение предполагаемой доли строк родительской таблицы, которая будет получена во время сканирования индекса. В случае запроса с потерями, как правило, значение будет выше, чем доля строк, которые фактически удовлетворяют условиям сравнения.

В indexCorrelation следует записать корреляцию (в диапазоне от -1,0 до 1,0) между порядком индекса и порядком таблицы. Это используется для корректировки оценки стоимости извлечения строк из родительской таблицы.

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

Когда loop_count больше единицы, возвращаемые числа должны быть средними, ожидаемыми для любого одного сканирования индекса.

Оценка стоимости

Типичный оценщик стоимости будет действовать следующим образом:

Оценивает и возвращает долю строк родительской таблицы, которые будут посещены на основе заданных условий сравнения. При отсутствии каких-либо специфических знаний для данного типа индекса нужно использовать стандартную функцию оптимизатора clauselist_selectivity():

*indexSelectivity = clauselist_selectivity(root, path->indexquals,
                                           path->indexinfo->rel->relid,
                                           JOIN_INNER, NULL);

Оценивает количество строк индекса, которые будут посещены во время сканирования. Для многих типов индексов это то же самое, что indexSelectivity, умноженное на число строк в индексе, но может быть и больше. (Обратите внимание, что размер индекса в страницах и строках доступен из входного параметра path->indexinfo).

Оценивает количество индексных страниц, которые будут прочитаны во время сканирования. Это может быть просто indexSelectivity умноженное на размер индекса в страницах.

Вычисляет стоимость доступа к индексу. Универсальный оценщик может сделать это так:

/*
 * Our generic assumption is that the index pages will be read
 * sequentially, so they cost seq_page_cost each, not random_page_cost.
 * Also, we charge for evaluation of the indexquals at each index row.
 * All the costs are assumed to be paid incrementally during the scan.
 */
cost_qual_eval(&index_qual_cost, path->indexquals, root);
*indexStartupCost = index_qual_cost.startup;
*indexTotalCost = seq_page_cost * numIndexPages +
    (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples;

Однако вышеизложенное не учитывает амортизацию операций чтения индекса при повторных сканированиях индекса.

Оценивает коэффициент корреляции. Для простого упорядоченного индекса по одному полю значение можно получить из pg_statistic. Если корреляция неизвестна, то консервативная оценка равна нулю (без корреляции).

Примеры функций оценки стоимости можно найти в разделе src/backend/utils/adt/selfuncs.c.

Общие записи WAL

Хотя все встроенные модули, использующие упреждающую запись, поддерживают свои собственные типы записей WAL, существует также общий тип записи WAL, который описывает изменения страниц в общем виде. Это полезно для расширений, предоставляющих пользовательские методы доступа, поскольку они не могут зарегистрировать свои собственные процедуры восстановления через WAL.

API для построения универсальных записей WAL определяется в access/generic_xlog.h.

Чтобы выполнить обновление данных защищённых WAL с помощью универсального средства записи WAL, выполните следующие действия:

state = GenericXLogStart(relation) - начать построение общей записи WAL для данного отношения.

page = GenericXLogRegisterBuffer(state, buffer, flags) - зарегистрируйте буфер, который будет изменен в текущей общей записи WAL. Эта функция возвращает указатель на временную копию страницы буфера, где должны быть сделаны изменения. (Не изменяйте содержимое буфера напрямую). Третий аргумент-это битовая маска флагов, применимая к данной операции. В настоящее время единственным таким флагом является GENERIC_XLOG_FULL_IMAGE, что означает, что в запись WAL должен быть включены не конкретные изменения, а полностраничный образ. Обычно этот флаг устанавливается, если страница является новой или была полностью переписана. GenericXLogRegisterBuffer можно повторить, если при помощи журналированного действия необходимо изменить несколько страниц.

Примените изменения к образам страниц, полученным на предыдущем шаге.

GenericXLogFinish(state) - примените изменения к буферам и выдайте общую запись WAL.

Построение записи WAL может быть отменено между любыми из вышеперечисленных шагов путем вызова GenericXLogAbort(state). Это приведет к отмене всех изменений в копии изображения страницы.

Пожалуйста, обратите внимание на следующие моменты при использовании общего средства записи WAL:

Никакие прямые модификации буферов не допускаются! Все изменения должны быть сделаны в копиях, полученных от GenericXLogRegisterBuffer(). Другими словами, код, который делает универсальные записи WAL, никогда не должен вызывать BufferGetPage(). Тем не менее, это остается обязанностью вызывающей стороны закрепить/открепить (pin/unpin) и заблокировать/разблокировать (lock/unlock) буферы в нужный момент. Исключительная блокировка должна быть удержана на каждом целевом буфере перед вызовом GenericXLogRegisterBuffer() до тех пор, пока не будет вызван GenericXLogFinish().

Регистрация буферов (шаг 2) и модификация изображений страниц (шаг 3) могут быть вызваны в любой последовательности. Имейте в виду, что буферы должны быть зарегистрированы в том же порядке, в котором блокировки должны быть получены на них во время воспроизведения.

Максимальное число буферов, которые могут быть зарегистрированы для универсальной записи WAL является MAX_GENERIC_XLOG_PAGES. При превышении этого предела будет выдана ошибка.

Generic WAL предполагает, что изменяемые страницы имеют стандартную компоновку, и в частности, что между ними нет полезных данных pd_lower и pd_upper.

Так как вы изменяете копии буферных страниц, GenericXLogStart() не запускает критическую секцию. Таким образом, вы можете безопасно выполнять выделение памяти, возврат ошибок и т. д. между GenericXLogStart() и GenericXLogFinish(). Единственная критическая секция присутствует внутри GenericXLogFinish(), но находясь внутри неё нет необходимости беспокоиться о вызове GenericXLogAbort() во время выхода из-за ошибки.

GenericXLogFinish() заботится о маркировке грязных буферов и настройке их LSNs. Вам не нужно делать это явно.

Для незарегистрированных отношений все работает одинаково, за исключением того, что не порождается фактическая запись WAL. Таким образом, обычно нет необходимости выполнять явные проверки незарегистрированных отношений.

Универсальная функция восстановления по Wal будет получать эксклюзивные блокировки для буферов в том же порядке, в котором они были зарегистрированы. После повторного внесения всех изменений, блокировки будут освобождены в том же порядке.

Если GENERIC_XLOG_FULL_IMAGE не задается для зарегистрированного буфера, общая запись WAL содержит разницу между старым и новым образами страниц. Эта разность основана на байтовом сравнении. Это не очень компактно для случая перемещения данных в пределах страницы и может быть улучшено в будущем.

Физическое хранилище базы данных

В этой главе представлен обзор формата физического хранилища, используемого базами данных QHB.

Структура файлов базы данных

В этом разделе описывается формат хранения на уровне файлов и каталогов.

Файлы конфигурации и данных, используемые СУБД, обычно хранятся в одном каталоге называемом PGDATA (аналогично имени переменной среды). Обычно PGDATA располагается по следующему пути: /var/lib/qhb/data. Несколько кластеров, управляемых различными экземплярами сервера, могут существовать на одной и той же машине.

Каталог PGDATA содержит несколько подкаталогов и управляющих файлов, как показано в таблице 1. В дополнение к этим необходимым элементам, файлы конфигурации кластера qhb.conf, qhb_hba.conf, и qhb_ident.conf традиционно хранятся в PGDATA, хотя можно разместить их и в другом месте.

Таблица 1. Содержание PGDATA

Файл/каталог
Описание

QHB_VERSION
Файл, содержащий основной номер версии QHB

base
Каталог в котором содержатся подкаталоги для каждой базы данных

current_logfiles
Файл содержащий пути файлов для ведения журнала логирования

global
Подкаталог, содержащий таблицы общие для кластера, такие как pg_database

pg_commit_ts
Подкаталог, содержащий данные временной метки фиксации транзакций

pg_dynshmem
Подкаталог, содержащий файлы, используемые динамической подсистемой общей памяти

pg_logical
Подкаталог, содержащий статусные данные для логического декодирования

pg_multixact
Подкаталог, содержащий данные о состоянии мультитранзакций (используется для общих блокировок строк)

pg_notify
Подкаталог, содержащий данные о состоянии LISTEN/NOTIFY

pg_replslot
Подкаталог, содержащий данные слота репликации

pg_serial
Подкаталог, содержащий информацию о совершенных сериализуемых транзакциях

pg_snapshots
Подкаталог, содержащий экспортированные снимки

pg_stat
Подкаталог, содержащий постоянные файлы для подсистемы статистики

pg_stat_tmp
Подкаталог, содержащий временные файлы для подсистемы статистики

pg_subtrans
Подкаталог, содержащий данные о состоянии подтранзакции

pg_tblspc
Подкаталог, содержащий символьные ссылки на табличные пространства

pg_twophase
Подкаталог, содержащий файлы состояний для подготовленных транзакций

pg_wal
Подкаталог, содержащий файлы WAL (Журнал упреждающей записи)

pg_xact
Подкаталог, содержащий данные о состоянии фиксации транзакций

qhb.auto.conf
Файл, используемый для хранения параметров конфигурации, которые задаются ALTER SYSTEM

postmaster.opts
Файл, записывающий параметры командной строки, с которыми сервер был запущен в последний раз

qhbmaster.pid
Файл блокировки, записывающий текущий идентификатор процесса postmaster (PID), путь к каталогу данных кластера, метку времени запуска postmaster, номер порта, путь к каталогу сокета Unix-домена, первый допустимый адрес listen_address (IP-адрес или * или пустой, если не прослушивает TCP), и идентификатор сегмента общей памяти (этот файл не присутствует после завершения работы сервера)

Для каждой базы данных в кластере существует подкаталог внутри PGDATA/base, названный по номеру OID базы данных в pg_database. Этот подкаталог по умолчанию используется для файлов базы данных; в частности, там хранятся ее системные каталоги.

Обратите внимание, что в следующих разделах описано поведение встроенного устройства кучи метод доступа к таблице и методы доступа к встроенному индексу. Поскольку QHB является расширяемой системой, другие методы доступа могут работать по-другому.

Каждая таблица и индекс хранятся в отдельном файле. Для обычных отношений эти файлы называются по номеру таблицы или индекса filenode, который можно найти в pg_class.relfilenode. Но для временных отношений имя файла имеет вид tBBB_FFF, где BBB является идентификатором внутреннего сервера, который создал файл, и FFF является номером filenode. В любом случае, помимо основного файла (слоя), каждая таблица и индекс имеют карту свободного пространства (см. раздел Карта свободного пространства), в которой хранится информация о свободном пространстве, доступном в отношении. Карта свободного пространства хранится в файле с именем с номером filenode с суффиксом _fsm. Таблицы также имеют карту видимости, хранящуюся в файле с суффиксом _vm для отслеживания страниц без "мертвых" кортежей. Карта видимости описана далее в разделе Карта видимости. Незарегистрированные таблицы и индексы имеют третий файл, известный как слой инициализации с суффиксом \init (см. раздел Слой инициализации).

!!!! Предупреждение

Обратите внимание, что хотя filenode таблицы часто совпадает с ее OID, это не обязательно так. Некоторые операции, такие как усечение, переиндексация, кластеризация и некоторые формы ALTER TABLE, могут изменить filenode, сохраняя OID. Избегайте предположения, что filenode и table OID-это одно и то же. Кроме того, для некоторых системных каталогов, в частности pg_class, pg_class.relfilenode содержит ноль. Фактическое число filenode этих каталогов хранится в структуре данных более низкого уровня и может быть получен с помощью pg_relation_filenode() функция.

Если объем таблицы или индекса превышает 1 гб, он делится на сегменты размером в гигабайт. Имя файла первого сегмента совпадает с именем filenode; последующие сегменты называются filenode.1, filenode.2, и т.д. Это позволяет избежать проблем на платформах, имеющих ограничения по размеру файлов. (На самом деле, 1 гб-это только размер сегмента по умолчанию. Размер сегмента можно регулировать с помощью опции конфигурации --with-segsize при сборки QHB). В теории, для карт свободного пространства и карт видимости также может потребоваться несколько сегментов, хотя на практике это вряд ли произойдет.

Таблица, содержащая столбцы с потенциально большими записями, будет иметь связанную с ней таблицу TOAST, которая используется для внешнего хранения полей, которые слишком велики, чтобы хранить их непосредственно в строках таблицы. pg_class.reltoastrelid связывает таблицу с ее TOAST таблицами, если таковые имеются. Дополнительную информацию смотрите в разделе TOAST.

Содержание таблиц и индексов рассматривается далее в разделе Внутренняя структура страницы базы данных.

Табличные пространства делают устройство базы данных более сложным. Каждое пользовательское табличное пространство имеет символическую ссылку внутри каталога PGDATA/pg_tblspc, который указывает на физический каталог табличного пространства (т.е. расположение, указанное в команде CREATE TABLESPACE табличного пространства). Эта символьная ссылка названа по номеру OID табличного пространства. Внутри физического каталога табличных пространств есть подкаталог с именем, которое зависит от версии сервера QHB. (Этот подкаталог необходим для того, чтобы последовательные версии базы данных могли использовать одни и те же значение для CREATE TABLESPACE без конфликтов). В подкаталоге для конкретной версии для каждой базы данных, которая имеет элементы в табличном пространстве, существует подкаталог названный по номеру OID базы данных. Таблицы и индексы хранятся в этом каталоге, используя схему именования filenode. Табличное пространство pg_default недоступно через pg_tblspc, но находится в PGDATA/base. Точно так же, как pg_global табличное пространство недоступно через pg_tblspc, но находится в PGDATA/global.

pg_relation_filepath() функция показывает весь путь (относительно PGDATA) любого отношения. Это часто полезно в качестве альтернативы для запоминания многих из вышеперечисленных правил. Но имейте в виду, что эта функция просто дает название первого сегмента основного слоя отношения — вам может понадобиться добавить номер сегмента и/или _fsm, _vm, или _init чтобы найти все файлы, связанные с этим отношением.

Временные файлы (для таких операций как сортировка данных большего размера, чем может поместиться в памяти) создаются внутри PGDATA/base/pgsql_tmp, или в подкаталоге табличного пространства внутри pgsql_tmp, если табличное пространство отличается от pg_default. Имя временного файла имеет вид pgsql_tmpPPP.NNN, где PPP это PID владеющего бэкенда и NNN идентифицирует различные временные файлы этого бэкенда.

TOAST

В этом разделе представлен обзор TOAST (The Oversized-Attribute Storage Technique, Методика хранения сверхбольших атрибутов).

QHB использует фиксированный размер страницы (обычно 8 кб) и не позволяет кортежам занимать несколько страниц. Поэтому невозможно хранить очень большие значения полей непосредственно. Чтобы преодолеть это ограничение, большие значения полей сжимаются и/или разбиваются на несколько физических строк. Это происходит прозрачно для пользователя, только с небольшим влиянием на большую часть внутреннего кода. Этот прием называют TOAST. Инфраструктура TOAST также используется для улучшения обработки больших значений данных в памяти.

Только некоторые типы данных поддерживают TOAST — нет необходимости накладывать накладные расходы на типы данных, которые не могут создавать большие значения полей. Для поддержки TOAST тип данных должен иметь представление переменной длины (varlena), в котором обычно первое четырехбайтовое слово содержит общую длину значения в байтах (включая сам размер). TOAST не ограничивает остальную часть представления типа данных. Специальные представления работают путем изменения или интерпретации этого начального слова длины. Поэтому функции уровня C/RUST, использующие тип данных, поддерживающий TOAST, должны быть аккуратны с тем, как они обрабатывают входные значения TOAST: входные данные могут фактически не состоять из четырехбайтового слова длины и содержимого до тех пор, пока они не будут детализированы. (Обычно это делается путем вызова PG_DETOAST_DATUM прежде, чем делать что-либо с входным значением, но в некоторых случаях возможны более эффективные подходы. Дополнительную информацию смотрите в разделе Особенности TOAST).

TOAST использует два бита из длины слова varlena (биты старшего порядка на big-endian архитектурах и биты младшего порядка на little-endian архитектурах), тем самым ограничивая логический размер любого значения типа данных с поддержкой TOAST до 1 гб (2³⁰ - 1 байт). Когда оба бита равны нулю, значение является обычным значением соответствующего типа данных, а оставшиеся биты длины содержат общий размер типа данных (включая размер типа длины) в байтах. Когда выставлен бит самого старшего или самого младшего порядка, значение имеет только однобайтовый заголовок вместо обычного четырехбайтового заголовка, а оставшиеся биты этого байта дают общий размер типа данных (включая байт длины) в байтах. Этот подход предоставляет экономичное хранение значений размером менее 127 байт, но при этом позволяет увеличить тип данных до 1 гб при необходимости. Значения с однобайтовыми заголовками не выровнены по какой-либо определенной границе, тогда как значения с четырехбайтовыми заголовками выровнены по крайней мере по четырехбайтовой границе; это отсутствие выравнивания обеспечивает дополнительную экономию пространства, что значительно для коротких значений. В частном случае, если все оставшиеся биты однобайтового заголовка равны нулю (что было бы невозможно для самодостаточной длины), значение является указателем на исходные данные с несколькими возможными альтернативами, как описано ниже. Тип и размер такого а TOAST-указателя определяется кодом, хранящимся во втором байте данных. Наконец, когда бит самого старшего или самого младшего порядка равен нулю, а соседний бит установлен, то содержимое данных сжато и должно быть распаковано перед использованием. В этом случае оставшиеся биты четырехбайтового слова длины дают общий размер сжатых данных, а не исходные данные. Обратите внимание, что сжатие также возможно для данных разделенных на несколько страниц. Это определяется не заголовком varlena, а содержимым по TOAST-указателю.

Как уже упоминалось, существует несколько типов данных для TOAST указателя. Наиболее распространенный тип — это указатель на разделенные по страницам данные, хранящиеся в таблице TOAST. Эта таблица отделена от таблицы, содержащая TOAST-указатель на данные, но связана с ней. Эта информация указателя на диске создается с помощью кода управления TOAST (в access/heap/tuptoaster.c) когда кортеж, который будет храниться на диске, слишком велик, чтобы быть сохраненным как есть. Более подробная информация приводится в разделе Внешнее хранилище TOAST на диске. Кроме того, TOAST-указатель на данные может содержать указатель на исходящие данные, которые отображаются в другом месте в памяти. Такие данные обязательно временны и никогда не появляются на диске, но они очень полезны для предотвращения копирования и избыточной обработки больших значений данных. Более подробная информация приводится в разделе Внешнее хранилище TOAST в памяти.

Метод сжатия, используемый как для одностраничных, так и для многостраничных данных, является довольно простой и очень быстрой реализацией алгоритма из семейства методов сжатия LZ, реализация представлена в src/common/pg_lzcompress.c.

Внешнее хранилище TOAST на диске

Если любой из столбцов таблицы поддерживает TOAST, таблица будет иметь связанную таблицу TOAST, OID которой хранится в записи таблицы pg_class.reltoastrelid. Значения TOAST на диске хранятся в таблице TOAST, как описано ниже более подробно.

Разделенные значения делятся (если используется сжатие) на порции длиной не более чем TOAST_MAX_CHUNK_SIZE байт (по умолчанию это значение выбрано таким образом, что четыре строки chunk поместятся на странице, что составляет около 2000 байт). Каждый фрагмент хранится в виде отдельной строки в таблице TOAST, принадлежащей таблице-владельцу. Каждая таблица TOAST имеет столбцы chunk_id (OID, определяющий конкретное TOAST значение), chunk_seq (порядковый номер для фрагмента в пределах его значения), и chunk_data (фактические данные фрагмента). Уникальный индекс на chunk_id и chunk_seq обеспечивает быстрое доступ к значениям. Таким образом, указатель на данные, представляющий собой устаревшее значение на диске, должен хранить OID таблицы TOAST, в которой нужно искать, и OID конкретного значения (его chunk_id). Для удобства, указатели на данные также хранят логический размер данных (исходная несжатая длина) и физический сохраненный размер (различен, если сжатие было применено). Учитывая байты заголовка varlena, общий размер TOAST-указателя на диске составляет 18 байт независимо от фактического размера представленного значения.

Код для обработки TOAST запускается только тогда, когда значение строки, подлежащее хранению в таблице, больше, чем TOAST_TUPLE_THRESHOLD байт (обычно 2 кб). Код TOAST будет сжимать и/или перемещать значения полей из строки до тех пор, пока значение строки не станет короче, чем TOAST_TUPLE_TARGET байт (по умолчанию тоже 2 кб) или до тех пор, пока объем строки будет невозможно уменьшить. Во время операции UPDATE значения неизмененных полей обычно сохраняются как есть; поэтому обновление строки с значениями разделенных на несколько страниц не несет никаких дополнительных затрат, если ни одно из таких значений не изменилось.

Код для обработки TOAST определяет четыре различных стратегии для хранения столбцов с поддержкой TOAST на диске:

PLAIN предотвращает сжатие или хранение вне строки; кроме того, он выключает использование однобайтовых заголовков для типов varlena. Это единственная возможная стратегия для столбцов типов данных, не допускающих TOAST.

EXTENDED позволяет как сжатие, так и хранение вне строки. Это значение по умолчанию для большинства типов данных с поддержкой TOAST. Сначала будет предпринята попытка сжатия, затем хранение вне строки, если строка все еще слишком велика.

EXTERNAL позволяет хранить вне строки, но не сжимать. Использование EXTERNAL сделает операции с подстрокой в больших text и bytea столбцах быстрее (в ущерб большему требованию к памяти), потому что такие операции оптимизированы для выборки только необходимых частей вне строки, когда оно не сжато.

MAIN позволяет сжатие, но не вне хранилища. (На самом деле, для таких столбцов по-прежнему будет выполняться автономное хранение, но только в крайнем случае, когда нет другого способа сделать строку достаточно маленькой, чтобы поместиться на странице).

Каждый тип данных поддерживающий TOAST задает стратегию по умолчанию для столбцов этого типа данных. Также стратегию для указанного столбца таблицы можно изменить с помощью ALTER TABLE ... SET STORAGE.

TOAST_TUPLE_TARGET можно настроить для каждой таблицы с помощью ALTER TABLE ... SET (toast_tuple_target = N)

По сравнению с более простым подходом, который позволяет значениям строк пересекать границы страницы, эта схема имеет ряд преимуществ. Предполагая, что запросы обычно имеют сравнения с относительно маленькими значениями ключа, большая часть работы будет выполнена с использованием записи основной строки. Большие значения TOAST-атрибутов будут извлечены только (если они вообще выбраны) во время отправки результата клиенту. Таким образом, основная таблица намного меньше, а значит больше строк помещается в общий буферный кэш, чем могло быть без использования внешнего хранения атрибутов. Данные для сортировки также уменьшаются, и сортировка чаще всего будет выполняться полностью в памяти. Небольшой тест показал, что в таблице, которая содержит типичные HTML-страницы и их URL-адреса, хранится примерно в два раза меньший объем необработанных данных, включая таблицу TOAST, и что основная таблица содержит только около 10% всех данных (URL-адреса и некоторые небольшие HTML-страницы). При этом не было никакой разницы во времени выполнения по сравнению с таблицей без TOAST атрибутов, в которой все HTML-страницы были сокращены до 7 КБ, для соответствия.

Внешнее хранилище TOAST в памяти

TOAST-указатели могут указывать на данные, которые не находятся на диске, а находятся в другом месте в памяти текущего серверного процесса. Такие указатели, очевидно, не могут быть долговечными, но они тем не менее полезны. В настоящее время существует два вида: указатели на косвенные (indirect) данные и указатели на расширенные (expanded) данные.

Косвенный указатель TOAST указывает на значение varlena, хранящееся где-то в памяти. Изначально он был создан просто как доказательство концепции, но в настоящее время он используется во время логического декодирования, чтобы избежать потенциального создания физических кортежей более 1 гб (что может произойти при объединении всех разделенных полей в один кортеж). У такого способа весьма ограниченное применение: владелец такого указателя самостоятельно должен учитывать, что ссылаемые данные существуют до тех пор, пока существует указатель.

Расширенные TOAST-указатели полезны для сложных типов данных, представление которых на диске не особенно подходит для вычислительных целей. Например, стандартное представление varlena массива включает информацию о размерности, битовый массив с null-значениями, если таковые имеются и далее все значения по порядку. Когда сам тип элемента имеет переменную длину, единственный способ найти N-ный элемент — это просмотреть все предыдущие элементы. Такое представление подходит для хранения на диске из-за его компактности, но для вычислений с массивом гораздо лучше иметь “расширенное” или “деконструированное” представление, в котором определены все начальные местоположения элементов. Механизм TOAST-указателя поддерживает эту потребность, позволяя данным, переданным по ссылке, указывать либо на стандартное значение varlena (представленного на диске), либо на TOAST-указатель, который указывает на расширенное представление где-то в памяти. Детали этого расширенного представления зависят от типа данных, тем не менее он должен иметь стандартный заголовок и соответствовать другим требованиям API, приведенным в src/include/utils/expandeddatum.h. функции уровня C/RUST, работающие с типом данных, могут выбрать для обработки любое представление. Функции, которые не знают о расширенном представлении, а просто применяют PG_DETOAST_DATUM к их входам будет, прозрачно получают обычное представление varlena; поэтому поддержка расширенного представления может вводиться постепенно, по одной функции за раз.

Указатели TOAST на расширенные значения далее разбиваются на указатели для чтения-записи и только для чтения. Представление с указанием на объект является одинаковым в любом случае, но функция, получающая указатель на чтение и запись, может изменять указанное значение на месте, тогда как функция, получающая указатель только на чтение, не должна этого делать; она должна сначала создать копию, если она хочет сделать измененную версию значения. Это различие и некоторые связанные с ним соглашения позволяют избежать ненужного копирования расширенных значений во время выполнения запроса.

Для всех типов указателей TOAST в памяти код управления TOAST гарантирует, что никакие данные указателя не может оказаться на диске. Указатели TOAST в памяти автоматически расширяются до обычных линейных значений varlena перед хранением, и затем, возможно, преобразуются в указатели TOAST на диске, если содержащийся кортеж был слишком большим.

Карта свободного пространства

Каждое отношение кучи и индекса, за исключением хеш-индексов, имеет карту свободного пространства (FSM) для отслеживания доступного пространства в отношении. Она хранится вместе с основными данными отношения в отдельном слое отношений, названной по номеру filenode отношения вместе с _fsm суффиксом. Например, если filenode отношения равен 12345, то FSM хранится в файле с именем 12345_fsm, в том же каталоге, что и основной файл отношения.

Карта свободного пространства организована в виде дерева страниц FSM. Страницы FSM нижнего уровня хранят свободное пространство, доступное на каждой странице кучи (или индекса), используя один байт для представления каждой такой страницы. Верхние уровни агрегируют информацию с нижних уровней.

Внутри каждой страницы FSM находится двоичное дерево, хранящееся в массиве с одним байтом на узел. Каждый конечный узел представляет собой страницу кучи или страницу FSM более низкого уровня. В каждом не-листовом узле хранится максимум из его дочерних значений. Поэтому максимальное значение всех листовых узлах хранится в корне.

Посмотрите src/backend/storage/freespace/README для получения более подробной информации о том, как структурирован FSM, и как он обновляется и ищется. Модуль pg_freespacemap может использоваться для изучения информации, хранящейся на картах свободного пространства.

Карта видимости

Каждое отношение кучи имеет карту видимости (VM), чтобы отслеживать, какие страницы содержат только кортежи, которые, как известно, видны для всех активных транзакций; она также отслеживает, какие страницы содержат только замороженные кортежи. Она хранится вместе с основными данными отношения в отдельном слое отношений, названной по номеру filenode отношения вместе с _vm суффиксом. Например, если filenode отношения равен 12345, то VM хранится в файле с именем 12345_vm, в том же каталоге, что и основной файл отношения. Обратите внимание, что индексы не имеют карту видимости.

Карта видимости хранит два бита на странице кучи. Первый бит, если он установлен, указывает, что страница является полностью видимой, или другими словами, что страница не содержит никаких кортежей, которые необходимо очистить. Эта информация также может использоваться сканированиями по индексу для ответа на запросы, с использованием только кортежа индекса. Второй бит, если он установлен, означает, что все Кортежи на странице были заморожены. Это означает, что даже вакуум с защитой от переполнения счетчика транзакции не должен повторно просматривать страницу.

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

Модуль pg_visibility можно использовать для изучения информации, хранящейся в карте видимости.

Слой инициализации

Каждая нежурналируемая таблица и каждый индекс нежурналируемой таблицы имеют слой инициализации. Слой инициализации — это пустая таблица или индекс соответствующего типа. Когда нежурналируемая таблица должна быть сброшена к пустому состоянию из-за сбоя, слой инициализации копируется поверх основного слоя, а все другие слои стираются (они будут автоматически воссозданы по мере необходимости).

Внутренняя структура страницы базы данных

В этом разделе представлен обзор формата страницы, используемого в таблицах и индексах QHB¹. Последовательности и таблицы TOAST представляются так же, как обычная таблица.

В следующем объяснении предполагается, что байт содержит 8 битов. Кроме того, термин элемент относится к значению данных, которое хранится на странице. В таблице элемент является строкой; в индексе элемент является записью индекса.

Каждая таблица и индекс хранятся в виде массива страниц фиксированного размера (обычно 8 кб, хотя при компиляции сервера можно выбрать другой размер страницы). В таблице все страницы логически эквивалентны, поэтому определенный элемент (строка) может храниться на любой странице. В индексах первая страница обычно зарезервирована как метастраница, содержащая управляющую информацию, и в индексе могут быть разные типы страниц, в зависимости от метода доступа к индексу.

В таблице 2 показана общая компоновка страницы. На каждой странице есть 5 частей.

Таблица 2. Общая Структура Страницы

Предмет | Описание
PageHeaderData | 24 байта длиной. Содержит общую информацию о странице, включая указатели на свободное места.
ItemIdData | Массив идентификаторов элементов, указывающих на фактические элементы. Каждая запись является парой (смещение,длина). 4 байта на элемент.
Free space | Свободное пространство. Новые идентификаторы элементов выделяются с начала этой области, новые элементы — с конца.
Items | Собственно сами элементы.
Special space | Специальная область для методов индексного доступа. Различные методы хранят различные данные. Пустое поле для обычных таблиц.

Первые 24 байта каждой страницы состоят из заголовка страницы (PageHeaderData). Его формат подробно описан в таблице 3. Первое поле отслеживает самую последнюю запись WAL, связанную с этой страницей. Второе поле содержит контрольную сумму страницы, если контрольные суммы включены. Далее идет 2-байтовое поле, содержащее биты флагов. Далее следуют три 2-байтных целочисленных поля (pd_lower, pd_upper, и pd_special). Они содержат смещения в байтах от начала страницы до начала свободного пространства, до конца свободного пространства и до начала специального пространства. Следующие 2 байта заголовка страницы, pd_pagesize_version, содержат размер страницы и индикатор версии.

Размер страницы в основном присутствует только в качестве перекрестной проверки; нет поддержки страниц разных размеров при установке. Последнее поле служит подсказкой для того, будет ли полезно очистить страницу: он отслеживает самый старый XMAX на странице, из тех что не был очищен.

Таблица 3. Структура PageHeaderData

Поле | Тип | Длина | Описание
pd_lsn | PageXLogRecPtr | 8 байт | LSN: следующий байт после последнего байта WAL-записи для последнего изменения на этой странице
pd_checksum | uint16 | 2 байта | Контрольная сумма страницы
pd_flags | uint16 | 2 байта | Биты флагов
pd_lower | LocationIndex | 2 байта | Смещение до начала свободного места
pd_upper | LocationIndex | 2 байта | Смещение до конца свободного пространства
pd_special | LocationIndex | 2 байта | Смещение до начала специального пространства
pd_pagesize_version | uint16 | 2 байта | Размер страницы и номер версии макета информация
pd_prune_xid | TransactionId | 4 байта | Самый старый неочищенный XMAX на странице, или ноль, если отсутствует

Все подробности можно узнать в разделе src/include/storage/bufpage.h.

После заголовка страницы идут идентификаторы элементов (ItemIdData), каждый из которых требует четыре байта. Идентификатор элемента содержит смещение в байтах до начала элемента, его длину в байтах и несколько битов атрибутов, влияющих на его интерпретацию. Новые идентификаторы элементов выделяются по мере необходимости с начала свободного пространства. Количество присутствующих идентификаторов элементов можно определить, посмотрев на pd_lower, который увеличивается при выделении нового идентификатора. Поскольку идентификатор элемента никогда не перемещается до тех пор, пока он не будет освобожден, его индекс можно использовать в долгосрочной основе для ссылки на элемент, даже если сам элемент перемещается по странице для сжатия свободного пространства. На самом деле, каждый указатель на элемент (ItemPointer, также известный как CTID) созданный QHB состоит из номера страницы и индекса идентификатора элемента.

Сами элементы хранятся в пространстве, выделенном с конца свободного пространства. Точная структура варьируется в зависимости от того, что должна содержать таблица. В таблицах и последовательностях используется структура с именем HeapTupleHeaderData, описанная ниже.

Последний раздел - это "специальный раздел", который может содержать все, что метод доступа желает сохранить. Например, B-дерево хранит ссылки на левый и правый родственные элементы страницы, а также некоторые другие данные, относящиеся к структуре индекса. Обычные таблицы вообще не используют специальный раздел (указывается при настройке pd_special для выравнивания размера страницы).

На рис. 1 показано, как эти части располагаются на странице.

Рис. 1. Структура страницы

Расположение Строк Таблицы

Все строки таблицы структурированы аналогичным образом. Существует заголовок фиксированного размера (занимающий 23 байта на большинстве машинах), за которым следует необязательное битовый массив с null-элементами, необязательное поле с идентификатором объекта и пользовательские данные. Заголовок подробно описан в таблице 4. Сами же пользовательские данные (столбцы строки) начинаются со смещения, указанного в t_hoff, который всегда должен быть кратен MAXALIGN для платформы. Битовый массив с null-элементами присутствует только в том случае, если установлен бит HEAP_HASNULL в t_infomask. Если он присутствует, то он начинается сразу после фиксированного заголовка и занимает достаточно байт, чтобы иметь один бит на столбец данных (т.е. количество битов, равное количеству атрибутов в столбце). t_infomask2). В этом списке битов выставленный бит указывает, что элемент не null, пустой - null. Когда битовый массив отсутствует, все столбцы считаются не null. Идентификатор объекта присутствует только в том случае, если установлен бит HEAP_HASOID_OLD в t_infomask. Если присутствует, то он появляется непосредственно перед смещением t_hoff. Чтобы сделать t_hoff кратным MAXALIGN между битовым массивом и идентификатором объекта добавляется отступ произвольного размера. (Это в свою очередь гарантирует, что идентификатор объекта соответствующим образом выровнен.)

Таблица 4. Структура Heaptupleheaderdata

Поле | Тип | Длина | Описание
t_xmin | TransactionId | 4 байта | значение XID при вставке
t_xmax | TransactionId | 4 байта | значение XID при удалении
t_cid | CommandId | 4 байта | значение CID при вставке и/или удалении (пересекается с t_xvac)
t_xvac | TransactionId | 4 байта | XID для операции вакуума при перемещении версии строки
t_ctid | ItemPointerData | 6 байт | TID текущий или более новой версии строки
t_infomask2 | uint16 | 2 байта | количество атрибутов плюс различные флаговые биты
t_infomask | uint16 | 2 байта | различные флаговые биты
t_hoff | uint8 | 1-байтовый | смещение до данных пользователя

Все подробности можно узнать в разделе src/include/access/htup_details.h.

Интерпретация фактических данных может быть выполнена только с помощью информации, полученной из других таблиц, в основном pg_attribute. Ключевые значения, необходимые для определения местоположения полей: attlen и attalign. Доступ к произвольному атрибуту возможен только тогда, когда все поля имеют фиксированную длину и нет нулевых значений. Все эти особенности определены в функциях heap_getattr, fastgetattr и heap_getsysattr.

Чтобы прочитать данные, необходимо прочитать каждый атрибут по очереди. Первым делом нужно проверить, является ли поле NULL в соответствии с битовым массивом null-значений. Если это так, то элемент отсутствует в данных. После этого необходимо убедиться, что есть правильное выравнивание. Если поле имеет фиксированную ширину, то все байты помещаются как есть. Если это поле переменной длины (attlen = -1), то это немного сложнее. Все типы данных переменной длины имеют общую структуру заголовка struct varlena, который включает в себя общую длину сохраненного значения и некоторые флаговые биты. В зависимости от флагов, данные могут быть либо встроенными, либо хранится отдельно в таблице TOAST; они также могут быть сжаты (см. раздел TOAST).

¹

На самом деле, использование этого формата страницы не требуется ни для методов доступа к таблице, ни для методов доступа к индексу. Метод heap доступа к таблице всегда использует этот формат. Все существующие методы индексирования также используют базовый формат, но данные, хранящиеся на мета-страницах индекса, обычно не следуют правилам компоновки элементов.

Как планировщик использует статистику

Эта глава основана на материалах, описанных в разделах Использование EXPLAIN и Статистика, используемая планировщиком, чтобы описать некоторые детали о том, как планировщик использует системную статистику для оценки количества строк, которые может возвратить каждая часть запроса. Это существенная часть процесса планирования, обеспечивающая большую часть информации для расчета стоимости запроса.

Цель этой главы состоит не в том, чтобы подробно документировать код, а в том, чтобы представить обзор того, как он работает. Это, возможно, сгладит кривую обучения для тех, кто впоследствии захочет изучить код.

Примеры оценок строк

В приведенных ниже примерах используются таблицы в базе данных регрессионного теста QHB. Обратите также внимание, что поскольку ANALYZE использует случайную выборку при создании статистики, результаты немного изменятся после нового анализа.

Начнем с очень простого запроса:

EXPLAIN SELECT * FROM tenk1;

                         QUERY PLAN
-------------------------------------------------------------
 Seq Scan on tenk1  (cost=0.00..458.00 rows=10000 width=244)

Как планировщик определяет размер (мощность) таблицы tenk1 рассматривается в разделе Статистика, используемая планировщиком, но повторяется здесь для полноты картины. Количество страниц и строк находится в поле pg_class:

SELECT relpages, reltuples FROM pg_class WHERE relname = 'tenk1';

 relpages | reltuples
----------+-----------
      358 |     10000

Эти числа являются текущими по состоянию на последний вызов VACUUM или ANALYZE в таблице. Затем планировщик выбирает фактическое количество страниц в таблице (это дешевая операция, не требующая сканирования таблицы). Если это значение отличается от relpages тогда reltuples пропорционально изменяется, чтобы получить текущую оценку количества строк. В приведенном выше примере значение параметра relpages является актуальным, поэтому оценка строк такая же, как reltuples.

Давайте перейдем к примеру с диапазоном в условии WHERE:

EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 1000;

                                   QUERY PLAN
--------------------------------------------------------------------------------
 Bitmap Heap Scan on tenk1  (cost=24.06..394.64 rows=1007 width=244)
   Recheck Cond: (unique1 < 1000)
   ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..23.80 rows=1007 width=0)
         Index Cond: (unique1 < 1000)

Планировщик проверяет условие WHERE и ищет функцию селективности (оценка размера выборки) для оператора < в pg_operator. Это проводится в колонке oprrest, и запись в этом случае является scalarltsel. scalarltsel функция извлекает гистограмму для unique1 из pg_statistic. Для запросов выполняемых вручную удобнее искать информацию в более простом pg_stats:

SELECT histogram_bounds FROM pg_stats
WHERE tablename='tenk1' AND attname='unique1';

                   histogram_bounds
------------------------------------------------------
 {0,993,1997,3050,4040,5036,5957,7057,8029,9016,9995}

Далее обрабатывается доля гистограммы в пределах 1000. Это и есть селективность. Гистограмма делит диапазон на равные частотные сегменты, поэтому все, что нам нужно сделать, это найти группу, в которой находится наше значение, и подсчитать пропорциональную долю и все предыдущие группы. Значение 1000 явно находится во второй группе (993-1997). Предполагая линейное распределение значений внутри каждой группы, мы можем рассчитать селективность как:

selectivity = (1 + (1000 - bucket[2].min)/(bucket[2].max - bucket[2].min))/num_buckets
            = (1 + (1000 - 993)/(1997 - 993))/10
            = 0.100697

то есть одна целая группа плюс пропорциональная доля второй, разделенная на количество групп. Конечное число строк теперь может быть рассчитано как произведение селективности и мощности tenk1:

rows = rel_cardinality * selectivity
     = 10000 * 0.100697
     = 1007  (rounding off)

Далее рассмотрим пример с условием равенства в WHERE:

EXPLAIN SELECT * FROM tenk1 WHERE stringu1 = 'CRAAAA';

                        QUERY PLAN
----------------------------------------------------------
 Seq Scan on tenk1  (cost=0.00..483.00 rows=30 width=244)
   Filter: (stringu1 = 'CRAAAA'::name)

Опять же, планировщик изучает условие в WHERE и ищет функцию селективности для =, который является eqsel. Для оценки равенства гистограмма не полезна; вместо этого для определения селективности используется список наиболее распространенных значений (most common value - MCV). Давайте посмотрим на MCV, с некоторыми дополнительными столбцами, которые будут полезны позже:

SELECT null_frac, n_distinct, most_common_vals, most_common_freqs FROM pg_stats
WHERE tablename='tenk1' AND attname='stringu1';

null_frac         | 0
n_distinct        | 676
most_common_vals  | {EJAAAA,BBAAAA,CRAAAA,FCAAAA,FEAAAA,GSAAAA,JOAAAA,MCAAAA,NAAAAA,WGAAAA}
most_common_freqs | {0.00333333,0.003,0.003,0.003,0.003,0.003,0.003,0.003,0.003,0.003}

Селективность определяется записью соответствующей записью CRAAAA в списке наиболее распространенных частот (MCFs):

selectivity = mcf[3]
            = 0.003

Как и прежде, расчетное число строк — это просто произведение селективности с кардинальностью tenk1:

rows = 10000 * 0.003
     = 30

Теперь рассмотрим тот же запрос, но с константой, которая не находится в списке MCV:

EXPLAIN SELECT * FROM tenk1 WHERE stringu1 = 'xxx';

                        QUERY PLAN
----------------------------------------------------------
 Seq Scan on tenk1  (cost=0.00..483.00 rows=15 width=244)
   Filter: (stringu1 = 'xxx'::name)

Это совсем другая проблема: как оценить селективность, когда значение не находится в списке MCV. Для решения можно использовать знание о том, что искомое значение не находится в списке вместе со всем значениями частот для MCV:

selectivity = (1 - sum(mvf))/(num_distinct - num_mcv)
            = (1 - (0.00333333 + 0.003 + 0.003 + 0.003 + 0.003 + 0.003 +
                    0.003 + 0.003 + 0.003 + 0.003))/(676 - 10)
            = 0.0014559

Нужно сложить все частоты для MCV и вычесть их из единицы, а затем разделить на количество других различных значений. Это равнозначно, что часть столбца, которая не является ни одним из MCV, равномерно распределена между всеми другими различными значениями. Обратите внимание, что нет никаких нулевых значений, поэтому нам не нужно беспокоиться о них (в противном случае мы бы вычитали нулевую дробь из числителя). Приблизительное число строк затем вычисляется как обычно:

rows = 10000 * 0.0014559
     = 15  (rounding off)

В предыдущем примере с условием unique1 < 1000 было упрощением того, что действительно делает scalarltsel. Теперь, когда мы увидели пример использования MCVs, мы можем описать процесс более детально. Этот пример был верен, поскольку unique1 это уникальный столбец, а значит у него нет MCV (очевидно, что никакое значение не встречается чаще, чем любое другое значение). Для неуникального столбца обычно существуют гистограмма и список MCV, и гистограмма не включает значения из списка MCV. Это сделано для более точной оценки. В этой ситуации scalarltsel непосредственно применяет данное условие (например, "<1000") к каждому значению списка MCV и суммирует частоты MCV, для которых условие является истинным. Это дает точную оценку селективности для той части таблицы, которая содержит значения MCVs. Аналогичным образом используется гистограмма для оценки селективности в той части таблицы, которая не содержит значения MCVs, а затем эти два числа складываются для оценки общей селективности. Например, рассмотрим

EXPLAIN SELECT * FROM tenk1 WHERE stringu1 < 'IAAAAA';

                         QUERY PLAN
------------------------------------------------------------
 Seq Scan on tenk1  (cost=0.00..483.00 rows=3077 width=244)
   Filter: (stringu1 < 'IAAAAA'::name)

Мы уже видели информацию MCV для stringu1, и вот его гистограмма:

SELECT histogram_bounds FROM pg_stats
WHERE tablename='tenk1' AND attname='stringu1';

                                histogram_bounds
--------------------------------------------------------------------------------
 {AAAAAA,CQAAAA,FRAAAA,IBAAAA,KRAAAA,NFAAAA,PSAAAA,SGAAAA,VAAAAA,XLAAAA,ZZAAAA}

Проверяя список MCV, мы обнаруживаем, что условие stringu1 удовлетворяется первыми шестью записями, а не последними четырьмя, поэтому селективность для значений из множества MCV определяется как

selectivity = sum(relevant mvfs)
            = 0.00333333 + 0.003 + 0.003 + 0.003 + 0.003 + 0.003
            = 0.01833333

Суммирование всех MCFs также говорит нам, что общая доля значений из множества MCVs, составляет 0.03033333, и поэтому доля значений, представленная гистограммой, составляет 0.96966667 (опять же, нет NULL-значений, иначе мы должны были бы их здесь исключить). Видно, что значение IAAAAA расположено почти в конце третьей группы гистограммы. Используя довольно грубые предположения о частоте различных символов, планировщик приходит к оценке 0,298387 для части значений из гистограммы, которая меньше, чем IAAAAA. Затем мы объединяем оценки для MCV и не-MCV значений:

selectivity = mcv_selectivity + histogram_selectivity * histogram_fraction
            = 0.01833333 + 0.298387 * 0.96966667
            = 0.307669

rows        = 10000 * 0.307669
            = 3077  (rounding off)

В этом конкретном примере коррекция полученная из списка MCV незначительна, потому что распределение столбцов на самом деле довольно плоское (статистика, показывающая эти конкретные значения как более распространенные, чем другие, в основном из-за ошибки выборки). В более типичном случае, когда некоторые значения являются значительно более распространенными, чем другие, этот сложный процесс повышает точность, поскольку селективность для MVC найдена точно.

Теперь рассмотрим случай с несколькими условиями в WHERE:

EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 1000 AND stringu1 = 'xxx';

                                   QUERY PLAN
--------------------------------------------------------------------------------
 Bitmap Heap Scan on tenk1  (cost=23.80..396.91 rows=1 width=244)
   Recheck Cond: (unique1 < 1000)
   Filter: (stringu1 = 'xxx'::name)
   ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..23.80 rows=1007 width=0)
         Index Cond: (unique1 < 1000)

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

selectivity = selectivity(unique1 < 1000) * selectivity(stringu1 = 'xxx')
            = 0.100697 * 0.0014559
            = 0.0001466

rows        = 10000 * 0.0001466
            = 1  (rounding off)

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

Наконец, мы рассмотрим запрос, который включает в себя соединение таблиц:

EXPLAIN SELECT * FROM tenk1 t1, tenk2 t2
WHERE t1.unique1 < 50 AND t1.unique2 = t2.unique2;

                                      QUERY PLAN
--------------------------------------------------------------------------------------
 Nested Loop  (cost=4.64..456.23 rows=50 width=488)
   ->  Bitmap Heap Scan on tenk1 t1  (cost=4.64..142.17 rows=50 width=244)
         Recheck Cond: (unique1 < 50)
         ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..4.63 rows=50 width=0)
               Index Cond: (unique1 < 50)
   ->  Index Scan using tenk2_unique2 on tenk2 t2  (cost=0.00..6.27 rows=1 width=244)
         Index Cond: (unique2 = t1.unique2)

Ограничение на tenk1, unique1< 50, вычисляется перед циклом соединения. Это обрабатывается аналогично предыдущему примеру с диапазоном. На этот раз значение 50 попадает в первую корзину гистограммы unique1:

selectivity = (0 + (50 - bucket[1].min)/(bucket[1].max - bucket[1].min))/num_buckets
            = (0 + (50 - 0)/(993 - 0))/10
            = 0.005035

rows        = 10000 * 0.005035
            = 50  (rounding off)

Ограничение для соединения является t2.unique2 = t1.unique2. Оператор — это уже разобранный =, однако функцию селективности (eqjoinsel.eqjoinsel) находится в столбце oprjoin таблицы pg_operator. Эта функция ищет статистическую информацию для обоих tenk2 и tenk1:

SELECT tablename, null_frac,n_distinct, most_common_vals FROM pg_stats
WHERE tablename IN ('tenk1', 'tenk2') AND attname='unique2';

tablename  | null_frac | n_distinct | most_common_vals
-----------+-----------+------------+------------------
 tenk1     |         0 |         -1 |
 tenk2     |         0 |         -1 |

В этом случае отсутствует информация MCV для unique2 поскольку все значения выглядят уникальными, а значит используется алгоритм, который полагается только на число различных значений для обоих отношений вместе с их нулевыми фракциями:

selectivity = (1 - null_frac1) * (1 - null_frac2) * min(1/num_distinct1, 1/num_distinct2)
            = (1 - 0) * (1 - 0) / max(10000, 10000)
            = 0.0001

В этом случае это определяется как вычитание доли null элеменов из единицы для каждого из отношений и деление на максимальное число различных значений. Число строк, которые будет получено при соединении, вычисляется как мощность декартова произведения двух входных данных, умноженная на селективность:

rows = (outer_cardinality * inner_cardinality) * selectivity
     = (50 * 10000) * 0.0001
     = 50

Если бы существовали списки MCV для двух столбцов, eqjoinsel использовал бы прямое сравнение списков MCV для определения селективности соединения для той части значений, что содержится в MCVs. Оценка для остальной части данных следует по такому же подходу.

Обратите внимание, что мы показали inner_cardinality как 10000, то есть неизмененный размер tenk2. При проверке выходных данных EXPLAIN может показаться, что оценка соединяемых строк исходит из 50 * 1, то есть число внешних строк умножается на предполагаемое число строк, полученных каждым внутренним индексным сканированием на tenk2. Но это не так: размер отношения соединения оценивается до того, как был рассмотрен какой-либо конкретный план соединения. Если все работает хорошо, то два способа оценки размера соединения дадут примерно один и тот же результат, но из-за ошибки округления и других факторов они иногда значительно расходятся.

Для тех, кто заинтересован в более подробной информации, оценка размера таблицы (перед любым условием WHERE) делается в src/backend/ optimizer/util/plancat.с. Общая логика для селективности условий находится в src/backend/optimizer/path/clausesel.с. Специфичные для операторов функции селективности можно найти внутри src/backend/utils/adt/selfuncs.с.

Примеры многомерной статистики

Функциональная зависимость

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

CREATE TABLE t (a INT, b INT);
INSERT INTO t SELECT i % 100, i % 100 FROM generate_series(1, 10000) s(i);
ANALYZE t;

Как объяснено в разделе Статистика, используемая планировщиком, планировщик может определить размер таблицы t используя количество страниц и строк, полученных из pg_class:

SELECT relpages, reltuples FROM pg_class WHERE relname = 't';

 relpages | reltuples
----------+-----------
       45 |     10000

Распределение данных очень простое; в каждом столбце есть только 100 различных значений распределенных равномерно.

В следующем примере показан результат оценки условия WHERE для столбца a:

EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1;
                                 QUERY PLAN
-------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..170.00 rows=100 width=8) (actual rows=100 loops=1)
   Filter: (a = 1)
   Rows Removed by Filter: 9900

Планировщик изучает условие и определяет, что селективность равна 1% Сравнивая эту оценку и фактическое число строк, мы видим, что оценка является очень точной (фактической, так как таблица очень мала). При использования столбца b в условии WHERE генерируется идентичный план. Но обратите внимание, что произойдет, если мы применим одно и то же условие к обоим столбцам, объединив их с AND:

EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 1;
                                 QUERY PLAN
-----------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..195.00 rows=1 width=8) (actual rows=100 loops=1)
   Filter: ((a = 1) AND (b = 1))
   Rows Removed by Filter: 9900

Планировщик оценивает селективность для каждого условия индивидуально, приходя к тем же самым оценкам 1%, что и выше. Затем он предполагает, что условия независимы, и поэтому он умножает их селективность, производя окончательную оценку селективности равной всего 0,01%. Это является значительной недооценкой, поскольку фактическое число строк, соответствующих условиям (100), на два порядка выше.

Эта проблема может быть решена путем создания объекта статистики, который укажет ANALYZE вычислить многомерную статистику функциональной зависимости по двум столбцам:

CREATE STATISTICS stts (dependencies) ON a, b FROM t;
ANALYZE t;
EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 1;
                                  QUERY PLAN
-------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..195.00 rows=100 width=8) (actual rows=100 loops=1)
   Filter: ((a = 1) AND (b = 1))
   Rows Removed by Filter: 9900

Подсчет групп многомерных значений

Аналогичная проблема возникает с оценкой мощности множеств нескольких столбцов, таких как число групп, которые будут генерироваться выражением GROUP BY. Когда GROUP BY передан один столбец, оценка различных значений (которые можно увидеть в значении количества строк, возвращаемых узлом HashAggregate) очень точна:

EXPLAIN (ANALYZE, TIMING OFF) SELECT COUNT(*) FROM t GROUP BY a;
                                       QUERY PLAN
-----------------------------------------------------------------------------------------
 HashAggregate  (cost=195.00..196.00 rows=100 width=12) (actual rows=100 loops=1)
   Group Key: a
   ->  Seq Scan on t  (cost=0.00..145.00 rows=10000 width=4) (actual rows=10000 loops=1)

Но без многомерной статистики оценка количества групп в запросе с двумя столбцами в GROUP BY, в частности в следующем примере, отклоняется на порядок:

EXPLAIN (ANALYZE, TIMING OFF) SELECT COUNT(*) FROM t GROUP BY a, b;
                                       QUERY PLAN
--------------------------------------------------------------------------------------------
 HashAggregate  (cost=220.00..230.00 rows=1000 width=16) (actual rows=100 loops=1)
   Group Key: a, b
   ->  Seq Scan on t  (cost=0.00..145.00 rows=10000 width=8) (actual rows=10000 loops=1)

При переопределении объекта статистики для включения подсчета групп для двух столбцов оценка значительно улучшается:

DROP STATISTICS stts;
CREATE STATISTICS stts (dependencies, ndistinct) ON a, b FROM t;
ANALYZE t;
EXPLAIN (ANALYZE, TIMING OFF) SELECT COUNT(*) FROM t GROUP BY a, b;
                                       QUERY PLAN
--------------------------------------------------------------------------------------------
 HashAggregate  (cost=220.00..221.00 rows=100 width=16) (actual rows=100 loops=1)
   Group Key: a, b
   ->  Seq Scan on t  (cost=0.00..145.00 rows=10000 width=8) (actual rows=10000 loops=1)

MCV списки

Как пояснялось в разделе ранее, функциональные зависимости являются очень дешевым и эффективным классом статистики, но у них есть основное ограничение в глобальном характере (отслеживается зависимости на уровне столбцов, а не между отдельными значениями столбцов).

Этот раздел вводит многомерный вариант множеств MCV (наиболее распространенных значений), расширение статистики по столбцам, описанной в разделе, с примерами оценок строк. Этот вариант решают проблему путем хранения отдельных значений, но это, естественно, более дорого, как с точки зрения построения статистики в ходе ANALYZE, так и времени затрачиваемом на хранение и планирование.

Давайте еще раз посмотрим на запрос из раздела функциональной зависимости, но на этот раз со списком MCV, созданным на том же наборе столбцов (не забудьте удалить функциональные зависимости, чтобы убедиться, что планировщик использует только новую статистику).

DROP STATISTICS stts;
CREATE STATISTICS stts2 (mcv) ON a, b FROM t;
ANALYZE t;
EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 1;
                                   QUERY PLAN
-------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..195.00 rows=100 width=8) (actual rows=100 loops=1)
   Filter: ((a = 1) AND (b = 1))
   Rows Removed by Filter: 9900

Оценка так же точна, как и с функциональными зависимостями, в основном благодаря тому, что таблица довольно мала и имеет простое распределение с небольшим количеством различных значений. Прежде чем рассматривать второй запрос, который не так хорошо работают функциональные зависимости, давайте немного рассмотрим список MCV.

Проверка списка MCV возможна с помощью функции pg_mcv_list_items.

SELECT m.* FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid),
                pg_mcv_list_items(stxdmcv) m WHERE stxname = 'stts2';
 index |  values  | nulls | frequency | base_frequency
-------+----------+-------+-----------+----------------
     0 | {0, 0}   | {f,f} |      0.01 |         0.0001
     1 | {1, 1}   | {f,f} |      0.01 |         0.0001
   ...
    49 | {49, 49} | {f,f} |      0.01 |         0.0001
    50 | {50, 50} | {f,f} |      0.01 |         0.0001
   ...
    97 | {97, 97} | {f,f} |      0.01 |         0.0001
    98 | {98, 98} | {f,f} |      0.01 |         0.0001
    99 | {99, 99} | {f,f} |      0.01 |         0.0001
(100 rows)

Это подтверждает, что в двух столбцах есть 100 различных комбинаций, и все они примерно равновероятны (частота 1% для каждого из них). base_frequency — это частота, вычисляемая из статистики по каждому столбцу независимо от статистики по нескольким столбцам. Если бы в любом из столбцов были какие-либо нулевые значения, это было бы указано в столбце nulls.

При оценке селективности планировщик применяет все условия для элементов в списке MCV, а затем суммирует частоты эквивалентных элементов. За подробностями можно обратиться к функции mcv_clauselist_selectivity в файле src/backend/статистика/mcv.с.

По сравнению с функциональными зависимостями, списки MCV имеют два основных преимущества. Во-первых, в списке хранятся фактические значения, что позволяет решить, какие комбинации совместимы.

EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 10;
                                 QUERY PLAN
---------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..195.00 rows=1 width=8) (actual rows=0 loops=1)
   Filter: ((a = 1) AND (b = 10))
   Rows Removed by Filter: 10000

Во-вторых, списки MCV обрабатывают больше видов условий, а не только условия равенства как функциональные зависимости. Например, взгляните на следующий запрос с диапазоном значений для той же таблицы:

EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a <= 49 AND b > 49;
                                QUERY PLAN
---------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..195.00 rows=1 width=8) (actual rows=0 loops=1)
   Filter: ((a <= 49) AND (b > 49))
   Rows Removed by Filter: 10000

Планировщик статистики и безопасности

Доступ к таблице pg_statistic ограничивается суперпользователями, так что обычные пользователи не могут узнать о содержании таблиц других пользователей из него. Некоторые функции оценки селективности будут использовать предоставленный пользователем оператор (либо оператор, появляющийся в запросе, либо связанный с ним оператор) для анализа сохраненной статистики. Например, чтобы определить, применимо ли сохраненное MCV, оценщик селективности должен будет выполнить соответствующее оператор = для сравнения константы в запросе с сохраненным значением. Таким образом, данные в pg_statistic потенциально могут передаваться пользовательским операторам. Правильно созданный оператор может намеренно пропускать переданные операнды (например, регистрируя их или записывая в другую таблицу), или случайно пропускать их, показывая их значения в сообщениях об ошибках, в обоих случаях возможно получить данные из pg_statistic для пользователя, который не должен видеть его.

Чтобы предотвратить это, к всем встроенным функциям оценки селективности применяется следующее. При планировании запроса, чтобы иметь возможность использовать сохраненную статистику, текущий пользователь должен либо иметь привилегию SELECT на таблицу или вовлеченные столбцы, или используемый оператор должны быть LEAKPROOF (точнее, функция, на которой основан оператор). Если нет, то оценщик селективности будет вести себя так, как если бы никакой статистики не было доступно, и планировщик будет продолжать использовать предположения по умолчанию.

Если пользователь не имеет необходимых прав доступа к таблице или столбцам, то во многих случаях запрос в конечном итоге получит ошибку прав доступа, и в этом случае такой механизм на практике невидим. Но если пользователь читает из представления с барьером безопасности, то планировщик может захотеть проверить статистику базовой таблицы, которая в противном случае недоступна для пользователя. В этом случае оператор должен быть leak-proof, иначе статистика не будет использоваться. Единственное в чем могут быть наблюдаемые отличия это неоптимальность плана. Если кто-то подозревает, что это так, можно попробовать запустить запрос от имени более привилегированного пользователя, чтобы увидеть, выбирается ли другой план.

Это ограничение применяется только в тех случаях, когда планировщику необходимо выполнить оператор, определенный пользователем, для одного или нескольких значений из pg_statistic. Таким образом, планировщику разрешается использовать общую статистическую информацию, такую как доля нулевых значений или количество различных значений в столбце, независимо от прав доступа.

Содержащиеся в сторонних расширениях функции оценки селективности, которые потенциально работают со статистикой с пользовательскими операторами, должны следовать тем же правилам безопасности. Обратитесь к исходному коду QHB для получения подробных деталей.

Асинхронный пул соединений QCP

Асинхронный пул соединений (Quantum Connection Pool, QCP) обеспечивает подключение и поддержку связи с удаленными клиентами.

Общий принцип работы

QCP принимает входящие подключения от удаленных клиентов по адресу listening_address (по умолчанию — 0.0.0.0:8080) и проксирует их серверам баз данных, перечисленных в настройке servers. Соединения до серверов устанавливаются (и завершаются) автоматически, по мере необходимости.

В зависимости от режима работы, задаваемого параметром relay_mode, проксирование данных от клиентов к серверам осуществляется одним из следующих способов:

relay_mode: Session: при первом обращении к серверу клиенту выделяется уникальное соединение до базы данных, которое возвращается в пул только при отключении клиента.

relay_mode: Smart: при обращении к серверу клиенту выделяется уникальное соединение до базы данных, которое возвращается в пул тогда и только тогда, когда в ответе от сервера будет стоять флаг Idle (см. раздел Форматы сообщений, сообщение ReadyForQuery).

С полным описанием настроек можно ознакомиться в примере конфигурационного файла qcp/config-example.yaml.

Запуск и работа

Запуск осуществляется с помощью утилиты qcp (qcp --help для списка опций), остановка — утилитой qcp-ctrl (qcp-ctrl --help для списка опций). Например, чтобы остановить запущенный экземпляр QCP, необходимо выполнить команду qcp-ctrl quit.

Вывод логов в процессе работы QCP контролируется настройкой log_output (см. qcp/config-example.yaml), при этом уровень логирования задается настройкой log_level (подробнее про уровни логирования см. в разделе LevelFilter). Например, чтобы выводить логи уровня Info и выше в файл /tmp/qcp.log, необходимо задать следующие настройки в файле конфигурации:

log_level: Info
log_output:
    file: /tmp/qcp.log

Потребление памяти

При старте программы единовременно выделяется количество памяти, указанное в разделе arena файла конфигурации:

# Конфигурация памяти (опционально)
arena:
    chunk_size: 66560  # Размер одного блока памяти, в байтах

    # Общее количество потребляемой памяти можно указать либо используя параметр
    # «количество блоков памяти»:
    chunks_count: 3150 # Количество таких блоков
    # либо указав общее количество напрямую:
    total_size: 3.1 GB # Поддерживаются суффиксы B, KB, MB, GB

Модуль прямой загрузки данных QDL

Модуль прямой загрузки данных (Quantum Data Loader, QDL) — утилита, позволяющая осуществить загрузку из файла формата CSV в таблицу формата QHB согласно конфигурации. Скорость работы существенно превосходит INSERT и COPY за счет использования оптимизированного многопоточного кода, отсутствия блокировок таблицы и обхода транзакционного ядра.

Поддерживаемые типы полей

Типы | Названия
Целочисленные | smallint, int2, integer, int4, int, bigint, int8
Числовые с плавающей запятой | float4, real, float8, double precision
Последовательности | smallserial, serial, bigserial
Текстовые | character, char, varchar, character varying, text
Числа с указанной точностью | decimal, numeric, dec
Логические | bool, boolean
Идентификаторы | uuid, oid
Временные | timestamp, date
Бинарные | bytea

Примечание
В текущей версии QDL допускается использование значений NaN (Not a Number) в таких типах данных, как float (числовые с плавающей запятой) и numeric (числа с указанной точностью). В дополнение к обычным числовым значениям и NaN, float также имеют специальные значения: Infinity и -Infinity. Обратите внимание, что все значения, находящиеся в файле формата CSV, не заключаются в кавычки.

Идентификаторы UUID записываются в виде последовательности шестнадцатеричных цифр в нижнем регистре, разделенных знаками минуса на несколько групп, в таком порядке: группа из 8 цифр, за ней три группы из 4 цифр и наконец группа из 12 цифр, что в сумме составляет 32 цифры. Пример UUID в этом стандартном виде: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11. Временные типы поддерживают такие специальные значения как infinity, -infinity и epoch.

Синтаксис

    qdl [флаги] [параметры] [субкоманды]

Флаги

-h
--help
Выводит справочную информацию об аргументах командной строки qdl.

-v
--verbose
Выводит отладочную информацию qdl.

-V
--version
Выводит информацию о версии qdl.

Параметры

-c
--config <файл конфигурации>
Путь к файлу конфигурации qdl.

-d
--data <файл данных>
Путь к исходным данным. Используйте '-', чтобы читать из stdin.

Субкоманды

create_table
Генерирует SQL-скрипт для создания таблицы.

help
Выводит список доступных субкоманд или справку по заданным субкомандам.

insert_values
Заполняет файл с таблицей значениями.

validate
Проверяет правильность файла CSV с данными.

Команда create_table

Генерирует SQL-скрипт, создающий таблицу СУБД согласно конфигурации, а также заполняет ее первыми тремя кортежами CSV-таблицы. Это требуется для создания структуры таблицы, которая будет заполнена данными. Также это позволяет нам получить пути расположения файлов и их названия (OID, требуемый для генерации командой insert_values).

    qdl --config <файл конфигурации> --data <файл данных> create_table [флаги]

Флаги

-h
--help
Выводит справочную информацию об аргументах командной строки create_table.

-V
--version
Выводит информацию о версии create_table.

Примечание
В формате CSV все символы являются значимыми. Значение в таблице, дополненное пробелами или любыми другими символами, кроме разделителя, будет включать эти символы. Это может приводить к ошибкам при заполнении таблицы данными из системы, дополняющей строки CSV пробельными символами до некоторой фиксированной ширины. В случае возникновения такой проблемы необходимо обработать файл CSV и удалить из него замыкающие пробельные символы, прежде чем загружать данные из него в QHB.

Пример использования

qdl --config config.yml \
    --data data.csv \
    create_table

Команда insert_values

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

Параметр --out-dir <выходной каталог> является обязательным для ввода из командной строки.

Также рекомендуется предварительно удостовериться, что версия компоновки страницы QHB равна 4. Сделать это можно, например, при помощи расширения pageinspect::page_header.

    qdl --config <файл конфигурации> --data <файл данных> insert_values [параметры] --out-dir <выходной каталог> [флаги]

Флаги

-h
--help
Выводит справочную информацию об аргументах командной строки insert_values.

-V
--version
Выводит информацию о версии insert_values.

ПАРАМЕТРЫ

-C
--cdc-info <выходной каталог для cdc.info>
Выходной каталог для qdl-cdc.info файла (корень PGDATA).

-o
--out-dir <выходной каталог>
Выходной каталог.

-r
--ram-usage-percent <процент используемой оперативной памяти>
Процент оперативной памяти, после которой запускается сборщик мусора для внутренних буферов (от 1 до 99; значение по умолчанию — 80).

Пример использования

qdl --config config.yml \
    --data data.csv \
    insert_values \
    --out-dir output/

Команда validate

Проверяет логическую целостность CSV-файла и согласованность структуры данных с файлом конфигурации, сигнализируя об ошибках соответствующим кодом завершения.

    qdl --config <файл конфигурации> --data <файл данных> validate [флаги]

Флаги

-h
--help
Выводит справочную информацию об аргументах командной строки validate.

-V
--version
Выводит информацию о версии validate.

Пример использования

qdl --config config.yml \
    --data data.csv \
    validate

Файл конфигурации

Файл конфигурации имеет следующие разделы:

general — базовая конфигурация приложения. Содержит поля:

threads (целочисленное) количество потоков-обработчиков сырых данных.

chunk_size — (целочисленное, необязательное) размер внутреннего буфера передачи данных;
данный параметр может меняться с целью оптимизации в нестандартных аппаратных конфигурациях.

data_source — выбор источника данных. В данной версии единственным поддерживаемым форматом является csv и содержит поля:

delimiter — разделитель столбцов для CSV.

double_quote — если true то две кавычки подряд считаются как одна, экранированная кавычка.

escape — Символ, используемый для экранирования.

comment — Символ, отмечающий начало комментария.

output — конфигурация выходных данных. Содержит поля:

segment_size — (целочисленное, необязательное) максимальное количество страниц в сегменте базы данных.

oid — (целочисленное) идентификатор основной таблицы.

toast_oid — (целочисленное, необязательное) идентификатор toast таблицы.

table — конфигурация таблицы. Содержит поля:

name — (строковое) имя таблицы.

fields — (повторяющиеся field) описание каждого из полей (например, field_1 : varchar и необязательная пометка [nullable]).

Пример файла конфигурации

# Конфигурация системы:
general:
    # Количество потоков, выделяемых для анализа и сериализации строк.
    # Обратите внимание, что помимо этих потоков обязательно выделяются еще два:
    # поток чтения и поток записи в файл.
    threads: 2

# Конфигурация исходных данных:
data_source:
    csv:
        # Разделитель столбцов для CSV:
        delimiter: ";"

# Конфигурация получаемых данных:
output:
    # Идентификатор объекта. Определяет целочисленный идентификатор для группы файлов целевой таблицы.
    oid: 16385
    # Идентификатор таблицы для хранения больших данных
    toast_oid: 16386

# Конфигурация целевой таблицы:
table:
    # Имя таблицы. Требуется для *generate_sql*, чтобы задать имя создаваемой таблицы.
    name: "t_qdl"
    # Колонки в формате "имя" : "тип"
    fields:
        id : integer
        social_id : integer [nullable]
        first_name : varchar
        last_name : varchar
        city : varchar (30)
        profession : varchar (30) [nullable]
        salary : double precision
        description : varchar [nullable]

Сценарий использования

ВНИМАНИЕ!
Во избежание проблем с правами доступа, все последующие операции требуется проводить от имени пользователя, который запускает экземпляр базы данных.

ШАГ 0 (необязательный):

Проверить CSV файл на согласованность с конфигурацией. Это может сэкономить значительное количество времени в случаях, когда конвертируется CSV-файл большого объема, так как если в ходе работы функции insert_values будет обнаружена ошибка, то ее выполнение придется повторить после исправления причин ее возникновения.

qdl --config config.yml --data data.csv validate

ШАГ 1:

Сгенерировать SQL-скрипт для создания таблицы, в которую планируется загрузить данные, а впоследствии использовать ее в СУБД. Также этот скрипт получает OID (идентификатор объекта; потребуется для файла конфигурации insert_values) и пути файлов базы данных. Эти операции могут быть произведены вручную, но во избежание возможных ошибок рекомендуется использовать скрипт.

qdl --config config.yml --data data.csv create_table

ШАГ 2:

Исполнить SQL-скрипт. OID, полученный в ходе выполнения, требуется разместить в файле конфигурации config.yml.

ШАГ 3:

Загрузить таблицу. Этот процесс может занять значительное время, в зависимости от объема загружаемых данных. Потоки, выполняющие чтение/конвертацию/запись, имеют определенные названия; это позволит системному администратору оценить загруженность каждого отдельно взятого потока ядра и определить их количеством таким образом, чтобы добиться оптимальной производительности. Также нужно заметить, что внутренние буфера qdl не имеют ограничения размера, а значит, при очень медленной записи на диск возможна ситуация, когда эти буферы займут всю оперативную память.

qdl --config config.yml --data data.csv insert_values --out-dir ./output_dir/

Важно!
Если вы используете CDC для создания инкрементальных резервных копий, то следует указать опцию -C, --cdc-info <выходной каталог для cdc.info> для генерирования информационного файла для CDC, чтобы сохранить консистентность данных в резервных копиях. Этот файл должен лежать в корне PGDATA. После перемещения этого файла в корень необходимо вызвать SELECT qcdc_attach_info() для применения изменений. После этого этот файл удалится автоматически. Если вы используете QDL несколько раз, то опция -C, --cdc-info <выходной каталог для cdc.info> должна совпадать для всех запусков, иначе резервная копия будет неконсистентной, либо выполняйте SELECT qcdc_attach_info() каждый раз после загрузки данных в базу!

ШАГ 4:

Заменить файлы СУБД сгенерированными при помощи qdl — копируем файлы при помощи команд операционной системы. После этого нужно выполнить переподключение базы данных на вновь сгенерированные файлы; для этого можно выполнить перезагрузку СУБД или выполнить

SELECT qhb_drop_rel_cache('имя_таблицы');

Примечание
Для чтения из stdin нужно использовать вертикальную черту (|). Примеры использования команд:

Команда create_table:

cat data.csv | qdl --config config.yml --data - create_table

Команда validate:

cat data.csv | qdl --verbose --config config.yml --data - validate

Команда insert_values:

cat data.csv | qdl --config config.yml --data - insert_values --out-dir output/

Вызов справочной информации:

qdl -h

Пример использования QDL

Общие замечания

QDL (Quantum Data Loader) — дополнительный модуль, который не входит в основную поставку QHB и устанавливается отдельно. Эта утилита позволяет на основе заданного описания структуры таблицы и соответствующих данных в CSV-файле сформировать в многопоточном режиме файл таблицы, минуя обычные механизмы базы данных. Полученный файл можно скопировать в качестве файла таблицы в каталог базы данных.

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

Вариант использования QDL в случае загрузки данных в новую партицию имеющейся таблицы

Допустим, имеется таблица, партиционированная по диапазонам дат. Пусть каждый диапазон охватывает один год. В качестве упрощенного примера возьмем таблицу, у которой имеется три поля c типами timestamp, int и text. Пусть на данный момент имеется две партиции с данными за 2019 и 2020 годы, и необходимо максимально быстро загрузить данные за 2021 год. Сделаем это с использованием QDL.

Шаг 1. Формирование данных тестовой таблицы

-- Партиционированная таблица qdl_test
DROP TABLE IF EXISTS qdl_test;
CREATE TABLE qdl_test (
    timestamp_value timestamp not null,
    int_value       int not null,
    text_value      text
) PARTITION BY RANGE (timestamp_value);

-- Тестовые данные за 2019 год
DROP TABLE IF EXISTS qdl_test_y2019;
CREATE TABLE qdl_test_y2019 PARTITION OF qdl_test
   FOR VALUES FROM ('2019-01-01') TO ('2020-01-01');

INSERT INTO qdl_test_y2019
SELECT to_timestamp(extract('epoch' FROM to_timestamp('2019-01-01','yyyy-mm-dd')) + random()*60*60*24*365)::timestamp AS timestamp_value,
       round(random()*1000000)::int AS int_value,
       substr(md5(random()::text), 0, 30) AS text_value
  FROM generate_series(1,1000);

-- Тестовые данные за 2020 год
DROP TABLE IF EXISTS qdl_test_y2020;
CREATE TABLE qdl_test_y2020 PARTITION OF qdl_test
   FOR VALUES FROM ('2020-01-01') TO ('2021-01-01');

INSERT INTO qdl_test_y2020
SELECT to_timestamp(extract('epoch' from to_timestamp('2020-01-01','yyyy-mm-dd')) + random()*60*60*24*365)::timestamp AS timestamp_value,
       round(random()*1000000)::int AS int_value,
       substr(md5(random()::text), 0, 30) AS text_value
  FROM generate_series(1,1000);

Шаг 2. Генерация данных для загрузки в виде CSV-файла

В целях тестирования сгенерируем данные за 2021 год в файле /tmp/qdl_data.csv, используя возможности команды COPY. В качестве символа разделителя выберем точку с запятой (;). Этот же символ необходимо будет использовать в файле конфигурации QDL.

Примечание
На этапе загрузки данных в QDL подразумевается, что первая строка файла данных содержит заголовок с названиями полей, поэтому при выгрузке в CSV команде COPY необходимо добавить параметр header true. Иначе при загрузке вместо заголовка будет пропущена первая строка с данными, что недопустимо.

COPY (SELECT to_timestamp(extract('epoch' from to_timestamp('2021-01-01','yyyy-mm-dd')) + random()*60*60*24*365)::timestamp AS timestamp_value
           , round(random()*1000000) AS int_value
           , substr(md5(random()::text), 0, 30) AS text_value
        FROM generate_series(1,1000))
  TO '/tmp/qdl_data.csv'
  WITH (format csv, delimiter ';', header true);

Шаг 3. Создание конфигурационного файла /tmp/config.yml, содержащего описание загружаемой таблицы для QDL

Следует отметить, что на первом этапе параметр oid в секции output не играет роли, поэтому его значение можно оставить нулевым. Важным здесь является символ-разделитель (delimiter), описание таблицы и ее столбцов (параметры партиции table). Если значение столбца может быть равным NULL, после наименования типа нужно добавить ключевое слово [nullable].

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

В данном примере файл конфигурации /tmp/config.yml выглядит следующим образом:

# Конфигурация системы:
general:
    # Количество потоков, выделяемых для разбора и сериализации строк.
    # Следует обратить внимание, что помимо этих потоков обязательно выделяются еще два:
    # поток чтения и поток записи в файл.
    threads: 2

# Конфигурация исходных данных:
data_source:
    csv:
        # Разделитель столбцов для CSV:
        delimiter: ";"

# Конфигурация получаемых данных:
output:
    # Идентификатор объекта. Определяет целочисленный идентификатор для группы файлов целевой таблицы.
    # Не важен до выполнения этапа создания таблицы
    oid: 0
    # Идентификатор таблицы для хранения больших данных
    toast_oid: 0

# Конфигурация целевой таблицы:
table:
    # Имя таблицы. Требуется для generate_sql, чтобы задать имя создаваемой таблицы.
    name: "qdl_test_y2021"
    # Столбцы в формате "имя": "тип"
    fields:
        timestamp_value : timestamp
        int_value : integer
        text_value : text [nullable]

Шаг 4. Проверка целостности описания таблицы и данных в CSV-файле

Это необязательный шаг, но его желательно выполнить, чтобы не терять результаты работы по формированию файла таблицы, если формат данных в какой-то строке окажется неверным. В этом случае загрузку данных придется запустить с самого начала.

Следующая команда проверяет логическую целостность CSV-файла и согласованность структуры данных с файлом конфигурации, сигнализируя об ошибках соответствующим кодом завершения.

/usr/bin/qdl validate --config /tmp/config.yml --data /tmp/qdl_data.csv --verbose

В случае успешной проверки выводится сообщение примерно такого содержания:

[20210319_140056][qdl::validate_csv][DEBUG] /tmp/qdl_data.csv is valid csv file

Шаг 5. Получение и применение скрипта создания таблицы и вывода значения OID

/usr/bin/qdl create_table --config /tmp/config.yml --data /tmp/qdl_data.csv > /tmp/table_script.sql

Вывод результата работы команды выглядит следующим образом:

CREATE TABLE qdl_test_y2021("timestamp_value" timestamp NOT NULL, "int_value" integer NOT NULL, "text_value" text);
CHECKPOINT;
-- Location of the main table
SELECT pg_relation_filepath('qdl_test_y2021');
-- Location of the toast table
SELECT pg_relation_filepath('pg_toast.pg_toast_' || (SELECT oid FROM pg_class WHERE relname = 'qdl_test_y2021'));

Скрипт нужно выполнить в базе данных. SELECT запросы отображают путь относительно каталога кластера баз данных. Первый выведет путь к основной таблице. Второй выведет путь к toast таблице, если она существует.

Шаг 6. Формирование файла таблицы

Это основная и самая ресурсоемкая операция. Важно выбрать эффективное значение для количества параллельных потоков (параметр threads в партиции general), участвующих в формировании файла таблицы. Это значение будет зависеть от количества процессоров на используемой машине, а также от возможностей дисковой системы. Относительно большое значение потоков может усилить конкуренцию за диски, и возможный выигрыш может быть нивелирован.

Значение OID таблицы нужно прописать в файле /tmp/config.yml в качестве значения параметра oid в секции output. Допустим, в приведенном скрипте в результате первого SELECT запроса функция pg_relation_filepath вывела значение base/13676/16463, тогда для OID таблицы пропишем значение 16463. Имя сформированного в результате файла таблицы будет соответствовать этому параметру. Аналогично прописываем toast_oid, если он был получен.

В результате работы утилиты в директории --out-dir появятся файлы с заданными OID в качестве имени, в приведенном примере это будет файл 16463. В случае, если размер данных окажется более 1 Гб, создадутся дополнительные файлы с соответствующими расширениями (16463.1, 816463.2 и т. д.).

/usr/bin/qdl insert_values --config /tmp/config.yml --data /tmp/qdl_data.csv --out-dir /tmp

Шаг 7. Копирование полученного файла средствами ОС в директорию базы данных

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

cp /tmp/16463 <путь к каталогу данных>/base/13676/16463

Шаг 8. Сброс данных таблицы

Этот шаг необходим для того, чтобы закэшированные данные таблицы сбросились.

SELECT qhb_drop_rel_cache('qdl_test_y2021');

Шаг 9. Проверка данных таблицы

Можно убедиться, что данные доступны как на основном сервере, так и на реплике.

SELECT * FROM qdl_test_y2021 LIMIT 10;
SELECT count(*) FROM  qdl_test_y2021; -- при желании можно убедиться в соответствии количеству строк в файле

Теперь при необходимости можно построить требуемые индексы.

Важно!
Следует отметить, что загрузка данных в обход стандартных механизмов работы базы данных приведет к тому, что при восстановлении базы из бэкапа и при применении архивных журналов данные в таблицах, загруженных через QDL, не восстановятся. При выполнении массовой загрузки данных через QDL желательно сразу после этого выполнить полный бэкап базы данных.

Шаг 10. Включение данных в партиционированную таблицу

Сделаем таблицу qdl_test_y2021 частью таблицы qdl_test, превратив ее в партицию и добавив ограничения по диапазону дат.

ALTER TABLE qdl_test ATTACH PARTITION qdl_test_y2021
  FOR VALUES FROM ('2021-01-01') TO ('2022-01-01');

При этом на сервере реплики уже будет иметься заранее подготовленная таблица qdl_test_y2021, и команда успешно выполнится.

Теперь при запросе данных 2021 года в таблице qdl_test используется обращение к новой партиции.

qhb=# explain select count(*) from qdl_test where timestamp_value >= to_timestamp('2021-01-01','YYYY-MM_DD');
                                           QUERY PLAN
-------------------------------------------------------------------------------------------------
 Aggregate  (cost=79.79..79.80 rows=1 width=8)
   ->  Append  (cost=0.00..78.84 rows=379 width=0)
         Subplans Removed: 2
         ->  Seq Scan on qdl_test_y2021  (cost=0.00..26.95 rows=377 width=0)
               Filter: (timestamp_value >= to_timestamp('2021-01-01'::text, 'YYYY-MM_DD'::text))
(5 rows)

В плане запроса отражено обращение к партиции qdl_test_y2021.

Замечания, касающиеся физической репликации

При использовании физической репликации необходимо иметь в виду, что данные таблицы на главном сервере формируются без генерации журналов предзаписи. Данные, скопированные в виде готового файла, не попадут автоматически через репликацию на реплики. Поэтому необходимо также скопировать файл таблицы в соответствующую директорию на репликах. Если этого не сделать, на реплики попадет только пустая таблица, созданная на шаге 5.

Необходимо также учитывать, что репликация может осуществляться с задержкой, связанной либо с переключением файлов журналов предзаписи (если не используется потоковая репликация), либо со специально настроенным отставанием реплики по времени, которое применяется в целях защиты от случайной потери данных на основном сервере.

Особенности выполнения изменений на физических репликах нужно учитывать на следующих шагах:

Шаг 5. Команды скрипта выполнятся на репликах автоматически, возможно, с некоторой задержкой.
Шаг 7. Необходимо скопировать подготовленный файл таблицы также в соответствующий каталог данных на серверах реплик. Этот шаг на репликах нужно выполнить после того, как на них будут созданы таблицы.
Шаг 8. Необходимо выполнить приведенный запрос по сбросу данных также и на серверах реплик.
Шаг 9. Репликация индексов выполнится автоматически. Однако необходимо, чтобы к моменту завершения репликации индексов на серверах реплик были выполнены все предыдущие шаги (5, 7 и 8) и таблица была готова к чтению данных через доступ по индексам.
Шаг 10. Скрипт выполнится на репликах автоматически, возможно, с некоторой задержкой. Желательно убедиться в успешном выполнении операции на репликах.

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

Нередко возникает необходимость загрузить объемные данные в новую таблицу, изменения в которой должны передаваться с помощью логической репликации в некое отдельное хранилище данных. QDL можно успешно использовать и в этой ситуации. В данном случае приведен простой пример обычной таблицы, который, тем не менее, демонстрирует все характерные нюансы.

Предварительные замечания

В случае логической репликации термины «основной сервер» и «реплика» используются относительно реплицируемой таблицы. Логическая репликация подразумевает наличие на стороне реплики копии реплицируемой таблицы. При инициализации логической репликации все данные таблицы на стороне основного сервера копируются целиком стандартными встроенными средствами. Чтобы избежать копирования всего объема загружаемых данных с основного сервера на реплику, можно инициализировать логическую репликацию с пустыми таблицами, заменив после этого содержимое таблиц как на основном сервере, так и на реплике. На стороне реплики таблица будет иметь в общем случае свой OID, поэтому файл с данными при копировании нужно будет переименовать соответствующим образом. Затем необходимо сбросить закэшированные данные таблицы как на основном сервере, так и на реплике. Далее репликация изменений будет выполняться в обычном порядке. Разумеется, до окончания выполнения работ по настройке репликации и копированию данных таблица на основном сервере не должна обновляться.

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

Шаг 1. Создание CSV-файла с данными для тестирования

В целях тестирования сгенерируем данные в файле /tmp/qdl_log_repl.csv, используя возможности команды COPY. В качестве символа разделителя выберем точку с запятой (;). Этот же символ необходимо будет использовать в файле конфигурации QDL.

Примечание
На этапе загрузки данных в QDL подразумевается, что первая строка файла данных содержит заголовок с названиями полей, поэтому при выгрузке в CSV команде COPY необходимо добавить параметр header true. Иначе при загрузке вместо заголовка будет пропущена первая строка с данными, что недопустимо.

Воспользуемся тем же скриптом, что и в прошлом примере.

COPY (SELECT to_timestamp(extract('epoch' FROM to_timestamp('2021-01-01','yyyy-mm-dd')) + random()*60*60*24*365)::timestamp AS timestamp_value
           , round(random()*1000000) AS int_value
           , substr(md5(random()::text), 0, 30) AS text_value
        FROM generate_series(1,1000))
  TO '/tmp/qdl_log_repl.csv'
  WITH (format csv, delimiter ';', header true);

Шаг 2. Создание конфигурационного файла /tmp/config_qdl_log_repl.yml, содержащего описание загружаемой таблицы для QDL

Следует отметить, что на первом этапе параметр oid в секции output не играет роли, поэтому его значение можно оставить нулевым. Важным здесь является символ-разделитель (delimiter), описание таблицы и ее столбцов (параметры партиции table). Если значение столбца может быть равным NULL, после наименования типа нужно добавить ключевое слово [nullable].

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

В данном примере файл конфигурации /tmp/config_qdl_log_repl.yml выглядит следующим образом:

# Конфигурация системы:
general:
    # Количество потоков, выделяемых для разбора и сериализации строк.
    # Следует обратить внимание, что помимо этих потоков обязательно выделяются еще два:
    # поток чтения и поток записи в файл.
    threads: 2

# Конфигурация исходных данных:
data_source:
    csv:
        # Разделитель столбцов для CSV:
        delimiter: ";"

# Конфигурация получаемых данных:
output:
    # Идентификатор объекта. Определяет целочисленный идентификатор для группы файлов целевой таблицы.
    # Не важен до выполнения этапа создания таблицы
    oid: 0
    # Идентификатор таблицы для хранения больших данных
    toast_oid: 0

# Конфигурация целевой таблицы:
table:
    # Имя таблицы. Требуется для generate_sql, чтобы задать имя создаваемой таблицы.
    name: "qdl_log_repl"
    # Столбцы в формате "имя": "тип"
    fields:
        timestamp_value : timestamp
        int_value : integer
        text_value : text [nullable]

Шаг 3. Проверка целостности описания таблицы и данных в CSV-файле

Это необязательный шаг, но его желательно выполнить для того, чтобы не терять результаты работы по формированию файла таблицы, если формат данных в какой-то строке окажется неверным. В этом случае загрузку данных придется запустить с самого начала.

Следующая команда проверяет логическую целостность CSV-файла и согласованность структуры данных с файлом конфигурации, сигнализируя об ошибках соответствующим кодом завершения.

/usr/bin/qdl validate --config /tmp/config_qdl_log_repl.yml --data /tmp/qdl_data.csv --verbose

В случае успешной проверки выводится сообщение примерно такого содержания:

[20210319_140056][qdl::validate_csv][DEBUG] /tmp/config_qdl_log_repl.csv is valid csv file

Шаг 4. Получение и применение скрипта создания таблицы и вывода значений OID

/usr/bin/qdl create_table --config /tmp/config_qdl_log_repl.yml --data /tmp/qdl_log_repl.csv > /tmp/table_script.sql

Вывод результата работы команды выглядит следующим образом:

CREATE TABLE qdl_log_repl("timestamp_value" timestamp NOT NULL, "int_value" integer NOT NULL, "text_value" text);
CHECKPOINT;
-- Location of the main table
SELECT pg_relation_filepath('qdl_test_y2021');
-- Location of the toast table
SELECT pg_relation_filepath('pg_toast.pg_toast_' || (SELECT oid FROM pg_class WHERE relname = 'qdl_log_repl'));

Скрипт нужно выполнить и на основном сервере, и на реплике.

Первый SELECT запрос скрипта выведет путь к таблице относительно каталога кластера баз данных. Обратите внимание, что на каждой базе данных выведутся свои значения функции pg_relation_filepath с номером OID таблицы в конце. Этот номер нужно будет использовать в параметре oid партиции output конфигурационного файла QDL.

На этом же шаге следует удалить все строки из таблицы qdl_log_repl на основном сервере и на реплике:

DELETE FROM qdl_log_repl;

При инициализации репликации строки таблицы копируются с основного сервера на реплику; в данном случае этот шаг фактически будет пропущен.

Шаг 5. Настройка логической репликации

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

На основном сервере выполните следующий скрипт:

SELECT pg_create_logical_replication_slot('test_slot','pgoutput');
CREATE PUBLICATION test_pub FOR TABLE qdl_log_repl;

На реплике замените в тексте следующей команды <имя_пользователя> на действительное имя пользователя в базе основного сервера и выполните ее.

CREATE SUBSCRIPTION test_sub CONNECTION 'port=5432 user=<имя_пользователя> dbname=qhb' PUBLICATION test_pub WITH (create_slot = false, slot_name = test_slot);

Шаг 6. Формирование файла таблицы

Это основная и самая ресурсоемкая операция. Важно выбрать эффективное значение для количества параллельных потоков (параметр threads партиции general), участвующих в формировании файла таблицы. Это значение будет зависеть от количества процессоров на используемой машине, а также от возможностей дисковой системы. Относительно большое значение потоков может усилить конкуренцию за диски, и возможный выигрыш может быть нивелирован.

Номер OID таблицы нужно прописать в файле /tmp/config_qdl_log_repl.yml в качестве значения параметра oid в секции output. Допустим, в приведенном скрипте в результате последнего запроса функция pg_relation_filepath вывела на основном сервере значение base/13676/17101, а на реплике значение base/13676/18203. Тогда для основного сервера пропишем в качестве параметра oid значение 17101. Имя сформированного в результате файла таблицы будет соответствовать этому параметру.

Значение на реплике будет отличаться. При копировании необходимо будет переименовать файл так, чтобы имя соответствовало номеру OID на реплике. Если не указать правильное значение, в лучшем случае файл будет просто лишним, а в худшем можно случайно перезаписать уже существующую таблицу с номером OID, совпадающим с номером OID таблицы на основном сервере.

Далее необходимо выполнить следующий скрипт:

/usr/bin/qdl insert_values --config /tmp/config.yml --data /tmp/qdl_data.csv --out-dir /tmp

В результате работы утилиты в директории --out-dir появится файл с заданным OID в качестве имени; в приведенном примере это будет файл 17101. В случае, если размер данных окажется более 1 Гб, создадутся дополнительные файлы с соответствующими расширениями (17101.1, 17101.2 и т. д.).

Шаг 7. Копирование полученного файла средствами OS в директорию базы данных на основном сервере и реплике

На основном сервере команда копирования может выглядеть приблизительно следующим образом:

cp /tmp/16463 <путь к каталогу данных на основном сервере>/base/13676/17101

На реплике имя файла отличается (OID базы данных также может быть другим, но в этом примере не изменен):

cp /tmp/16463 <путь к каталогу данных на реплике>/base/13676/18203

Шаг 8. Сброс данных таблицы

Этот шаг необходим для того, чтобы закэшированные данные таблицы сбросились. Необходимо выполнить запрос как на основном сервере, так и на реплике.

SELECT qhb_drop_rel_cache('qdl_log_repl');

Шаг 9. Проверка данных таблицы

Можно убедиться, что данные доступны как на главном сервере, так и на сервере реплики.

SELECT * FROM qdl_log_repl limit 10;
SELECT count(*) FROM  qdl_log_repl; -- при желании можно убедиться в соответствии количеству строк в файле

При необходимости теперь можно построить требуемые индексы.

Важно!
Следует отметить, что загрузка данных в обход стандартных механизмов работы базы данных приводит к тому, что при восстановлении базы из бэкапа и при применении архивных журналов данные в таблицах, загруженных через QDL, не восстанавливаются. При выполнении массовой загрузки данных через QDL желательно сразу после этого выполнить полный бэкап базы. Чтобы этот бэкап не был дополнительным, желательно согласовать политику выполнения массовой загрузки данных с политикой выполнения бэкапов.

Примечание
QBackup имеет встроенные механизмы поддержки QDL. Таблицы, модифицированные с его помощью, будут учтены в резервной копии, если при команде insert_values был указан параметр -C, --cdc-info <out-dir> (при использовании CDC в первом режиме изменения будут обнаружены и без указания данной опции). Этот параметр генерирует информационный файл, который должен лежать в корне PGDATA. Подробную информацию см. в разделе Сценарий использования QDL, шаг 3, пункт ВАЖНО.

Заключение

Используя QDL, можно эффективно загружать в базу объемные данные, в том числе в случае использования физической или логической репликации. Тестирование показывает, что общее время загрузки данных с помощью QDL и время копирования полученного файла в 3,5 раза меньше по сравнению с продолжительностью загрузки тех же данных с помощью команды COPY.

Приведенный подход можно использовать в хранилищах и витринах данных, где время загрузки новых данных является критичным показателем, а данные, как правило, поступают из других источников.

Менеджер кэша дисковых блоков TARQ

Менеджер кэша дисковых блоков — самобалансирующийся менеджер кэша дисковых блоков с автоматической компенсацией нагрузки на дисковую систему.

Алгоритм вытеснения из кэша нужен для работы в среде, где в целях ускорения операций с диском часть быстродействующей памяти используется для размещения наиболее часто востребованных блоков. Данный алгоритм служит для определения блока, который можно выбросить с наименьшими потерями для общей производительности (т. е. такого блока, который будет использоваться не так часто и/или интенсивно как остальные).

QHB может запускаться с усовершенствованным алгоритмом вытеснения, называемым TARQ.

Алгоритм предполагает поддержку двух списков страниц, L1 и L2. Максимальная длина обоих списков составляет 2c, где c — размер кэша в страницах. Оба списка формируются в стиле LRU. При перемещении в кэш страницы, номер которой отсутствует в обоих списках, этот номер заносится в начало списка L1. При обращении к странице, номер которой фигурирует в одном из списков, этот номер переносится в начало списка L2. Важной особенностью алгоритма является то, что только в начале каждого из списков (в подсписках T1 и T2) располагаются номера страниц, находящихся в кэше; таким образом поддерживается история страниц, недавно вытесненных из кэша. Страница для замещения выбирается из конца подсписка T1 или T2 в зависимости от значения параметра p, определяющего текущую допустимую длину подсписка T1, а тем самым, и длину T2. Адаптивность алгоритма состоит в том, что значение p изменяется в зависимости от вида рабочей нагрузки.

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

use_qhb_cache (boolean)

Логический параметр. При установке значения в true будет использоваться новая версия кэша.

qhb_cache_size (integer)

Общий размер буферного кэша (при включении use_qhb_cache=true значение shared_buffers не используется).

shared_buffers_partitions (integer)

Размер фрагментов кэша (партиций; не имеют отношения к партициям таблицы). Обращение к каждому фрагменту происходит независимо от остальных. Слишком большой размер приведет к возрастанию конкуренции за блокировки, слишком маленький может привести к задержкам, если в партиции не окажется пригодных к вытеснению блоков (все грязные или занятые фоновым процессом).

Рекомендуемое значение: 128.

tarq_cache.touch_queue_ignore (integer)

Процент заполнения фрагмента кэша при котором обращения начинают приводить к операциям балансировки. Может принимать значения от 1 до 100 (целые числа). Для незаполненного или заполненного частично фрагмента балансировок не требуется.

Рекомендуемое значение: 50.

Ребалансировка — дорогая операция, в нагруженной среде к некоторым буферам (содержимое системных таблиц, словари, последовательности) может быть много тысяч обращений в секунду, существенно не влияющих на общий баланс. Для сглаживания можно использовать следующие параметры:

tarq_cache.touch_window (integer)

Время в секундах. Все обращения в течение этого периода считаются одним обращением.

Рекомендуемое значение: 3.

tarq_cache.touch_threshold (integer)

Количество обращений к буферу, по достижении которого происходит операция ребалансировки.

Рекомендуемое значение: 5.

Дамп и загрузка содержимого менеджера кэша дисковых блоков UMCA

Экземпляр QHB может работать достаточно долгое время. В процессе он накапливает данные о загруженных дисковых блоках. Если по каким-то причинам появится необходимость остановить его работу, то эти данные будут потеряны. В этом случае после запуска новому экземпляру придется заново собирать эти данные.

Модуль Universal Memory Cache Analyzer, UMCA позволяет сохранять содержимое менеджера кэша дисковых блоков в файловой системе, а затем загружать его обратно, тем самым восстанавливая прежнее состояние. За счет этого время на прогрев экземпляра не затрачивается. (Подробное описание менеджера кэша дисковых блоков см. в разделе Менеджер кэша дисковых блоков TARQ)

Внимание! Функции UMCA работают, только если в конфигурационном файле qhb.conf параметр use_qhb_cache установлен в значение true, иначе вызовы функций будут завершаться ошибкой.

Создание дампа

Для создания дампов используется встроенная SQL-функция qhb_dump_disk_cache. Она копирует содержимое менеджера кэша дисковых блоков и затем сохраняет его в файле. В качестве результата возвращается количество сохраненных блоков. При вызове необходимо указать тип дампа (регистр символов не имеет значения). Ниже представлена соотносительная таблица всех типов дампов и затрагиваемых списков страниц.

Тип дампа | Затрагиваемые списки
FULL | B1, B2, L1, L2
HOT | L2
COLD | L1
SHADOW | B1, B2

Ниже представлен пример вызова функции qhb_dump_disk_cache, в результате которого создается полный дамп содержимого менеджера кэша дисковых блоков. Если ошибок нет, значит, функция выполнилась успешно.

SELECT qhb_dump_disk_cache('FULL');

Все дампы сохраняются в подкаталоге qhb-disk-cache-dump каталога базы данных. Файл имеет имя, состоящее из типа дампа (см. таблицу выше) и текущего UTC-времени.

Сохранение дампа в файл с произвольным именем

Функция qhb_dump_disk_cache имеет форму со вторым параметром, позволяющим дополнительно передать имя файла, под которым будет сохранен создаваемый дамп. Ниже представлен пример вызова функции qhb_dump_disk_cache, в результате которого будет создан дамп горячих блоков с именем test.dump.

SELECT qhb_dump_disk_cache('HOT','old.dump');

Загрузка дампа

Для загрузки из дампов используется встроенная SQL-функция qhb_load_disk_cache. Она загружает данные из предварительно созданного дампа. В качестве результата возвращается количество загруженных блоков. При вызове необходимо указать название или абсолютный путь к дампу. Ниже представлен пример вызова функции qhb_load_disk_cache, в результате выполнения которого будут загружены блоки. Если ошибок нет, значит, функция выполнилась успешно.

SELECT qhb_load_disk_cache('2020-12-03_12:50:21.dump');

В примере выше указано название дампа, поэтому функция будет искать его в стандартном подкаталоге хранения дампов менеджера кэша дисковых блоков. Также можно указать полный путь к дампу, как это сделано в примере ниже.

SELECT qhb_load_disk_cache('/opt/qhb-data/qhb-disk-cache-dumps/2020-12-03_12:50:21.dump');

Загрузка последнего дампа

Функция qhb_load_disk_cache имеет форму без параметров; в этом случае функция будет искать последний измененный файл, доступный для чтения. Ниже представлен пример вызова функции qhb_load_disk_cache без параметров.

SELECT qhb_load_disk_cache();

Если в директории есть несколько дампов, с помощью команды touch можно обновить время изменения дампа, тогда он будет считаться последним.

touch new-last.dump

Просмотр файлов дампа

Дистрибутив QHB содержит утилиту qhb_disk_cache_dump_viewer, которая позволяет посмотреть содержимое дампов менеджера кэша дисковых блоков. Ниже представлен пример использования этой утилиты.

qhb_disk_cache_dump_viewer 2020-12-03_15:34:53.dump

Сортировка вывода

Утилита qhb_disk_cache_dump_viewer имеет параметр сортировки вывода --sort-by (сокращенно -s). По умолчанию включена сортировка по номеру блока. С помощью значений, описанных в таблице ниже, можно указать, по какому столбцу сортировать вывод.

Значение параметра --sort-by | Описание
ForkNum | сортирует вывод по столбцу fork_num
BlockNum | сортирует вывод по столбцу block_num
SpcNode | сортирует вывод по столбцу spc_node
DbNode | сортирует вывод по столбцу db_node
RelNode | сортирует вывод по столбцу rel_node

Ниже представлен пример вывода утилиты qhb_disk_cache_dump_viewer.

GEN      ID: [ fork_num | block_num | spc_node | db_node | rel_node ]
L1        1: [        0 |         0 |     1663 |   12674 |     2615 ]
L1        2: [        0 |         5 |     1663 |   12674 |     2617 ]
L1        3: [        0 |         1 |     1663 |   12674 |     2654 ]
L1      129: [        2 |         0 |     1663 |   12674 |     3456 ]
L1      130: [        0 |        42 |     1663 |   12674 |     2838 ]
L1      257: [        0 |         1 |     1663 |   12674 |     1259 ]
L1      258: [        0 |         2 |     1663 |   12674 |     2610 ]
L1      259: [        0 |         3 |     1663 |   12674 |     2696 ]

Получение размера дампа в блоках

Утилита qhb_disk_cache_dump_viewer имеет параметр отображения размера дампа в блоках --size (сокращенно -S). Этот параметр несовместим с параметром --sort-by.

Ниже представлен пример вывода qhb_disk_cache_dump_viewer с параметром --size.

B1: 0
B2: 0
L1: 159
L2: 0
Total: 159

Параметр HOLDMEM и дополнительные кэши дисковых блоков

Внимание!
В релизе QHB 1.5.0 эта функциональность является экспериментальной, её использование в инсталляциях на продуктивной среде не рекомендовано.

В QHB для ускорения работы с таблицами имеется возможность создавать их особым параметром HOLDMEM, который указывает, где они будут размещаться. Этот параметр может иметь три значения: OFF, POSSIBLY и ONLY.

Синтаксис:

CREATE TABLE cats (id int) WITH (HOLDMEM = OFF);
-- то же самое, что и CREATE TABLE cats (id int);

CREATE TABLE dogs (id int) WITH (HOLDMEM = POSSIBLY);

CREATE UNLOGGED TABLE cows (id int) WITH (HOLDMEM = ONLY);

Примечание
Обратите внимание, что таблицы с HOLDMEM = ONLY должны быть созданы с параметром UNLOGGED.

Значения HOLDMEM

OFF — это обычные таблицы. При создании таблицы этот параметр можно опустить. Обычно он нужен, когда требуется изменить свойство таблицы.

ALTER TABLE dogs SET (HOLDMEM = OFF);

POSSIBLY — это таблицы, которые по возможности находятся в памяти.

ALTER TABLE dogs SET (HOLDMEM = POSSIBLY);

ONLY — это таблицы, которые всегда находятся в памяти.

ALTER TABLE dogs SET (HOLDMEM = ONLY);

Примечание!
Все таблицы с параметром HOLDMEM = ONLY должны быть UNLOGGED.
Отдельным запросом сделайте таблицу UNLOGGED:

ALTER TABLE dogs set UNLOGGED;

а затем

ALTER TABLE dogs SET (HOLDMEM = ONLY);

HOLDMEM = POSSIBLY

Таблицы, созданные с параметром HOLDMEM = POSSIBLY, предназначены для хранения данных, к которым требуется быстрый доступ. Для них используется отдельный кэш LRU, за счет простоты которого достигается быстродействие при обращении к нему во время поиска страницы. Однако алгоритм LRU имеет меньше возможностей настройки, чем TARQ, поэтому его использование нужно в особых случаях, например, при работе с данными, к которым требуется частое обращение.

Примечание
Все таблицы с параметром HOLDMEM = ONLY должны быть UNLOGGED.
Отдельным запросом сделайте таблицу UNLOGGED:
ALTER TABLE dogs set UNLOGGED;
а затем
ALTER TABLE dogs SET (HOLDMEM = ONLY);

HOLDMEM = ONLY

Таблицы, созданные с параметром HOLDMEM = ONLY, предназначены для хранения данных, к которым требуется максимально быстрый доступ.

Однако нужно знать некоторые особенности при работе с ним:

Так как все данные хранятся в памяти, переполнение этого буфера приведет к ошибке. Также при сбое или экстренном завершении данные не могут быть и не будут восстановлены.

Не поддерживается механизм TOAST.

а) При создании таблицы всем колонкам будет принудительно установлен режим хранения PLAIN.

б) У существующей таблицы нельзя изменить атрибут хранения на HOLDMEM = ONLY, если есть соответствующая TOAST таблица. Предварительно необходимо выключить TOAST с помощью команды ALTER TABLE имя_таблицы ALTER COLUMN имя_столбца SET STORAGE PLAIN. Обратите внимание, что при изменении режима хранения существующая таблица TOAST не будет удалена. На TOAST таблицу параметр HOLDMEM = ONLY не действует.

в) При установке атрибута хранения HOLDMEM = POSSIBLY или HOLDMEM = OFF после HOLDMEM = ONLY автоматическое включение поддержки TOAST не производится. При возникновении потребности в такой поддержке ее необходимо включить вручную с помощью команды ALTER TABLE имя_таблицы ALTER COLUMN имя_столбца SET STORAGE.

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

use_possible_buffer (boolean)

Логический параметр. При установке значения в TRUE будет использоваться этот кэш.

qhb_possible_buffers_size (integer)

Общий размер буферного кэша.

shared_buffers_partitions (integer)

Используется общий параметр с TARQ - размер фрагментов кэша (партиций; не имеют отношения к партициям таблицы).

Обращение к каждому фрагменту происходит независимо от остальных. Слишком большой размер партиций приведет к возрастанию конкуренции за блокировки, слишком маленький может привести к задержкам, если в партиции не окажется пригодных к вытеснению блоков (все «грязные» или занятые фоновым процессом).

Рекомендуемое значение: 128.

use_qhb_onlymem_cache (boolean)

Логический параметр. При установке значения в TRUE будет использоваться этот кэш.

qhb_onlymem_cache_size (integer)

Общий размер буферного кэша.

Совместимость

Параметр HOLDMEM, так же как и значения OFF, POSSIBLY и ONLY, реализованные в QHB, является расширением стандарта SQL.

См. также

ALTER TABLE

Таблицы APPEND_ONLY

В QHB для ускорения работы с таблицами имеется возможность создавать их с особым параметром APPEND_ONLY. Этот параметр предназначен для таблиц, с которыми не производятся модификации, например журналы, данные с датчиков и т. п. В такие таблицы можно только добавлять записи, но делается это с максимальной скоростью, так как не выполняется полноценного MVCC анализа.

Для этого варианта хранилища характерны также следующие свойства:

Отсутствует необходимость в автоочистке.

Поддерживаются все типы индексов.

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

Не поддерживается механизм TOAST.

Создание таблицы с APPEND_ONLY выполняется запросом:

CREATE TABLE имя_таблицы (...) USING APPEND_ONLY;

Также для ускорения работы с таблицей возможно создание таблицы с параметром HOLDMEM. Для этого необходимо при создании таблицы указать параметр UNLOGGED и добавить WITH (HOLDMEM = ONLY). Запрос на создание такой таблицы выглядит так:

CREATE UNLOGGED TABLE имя_таблицы (...) USING APPEND_ONLY WITH (HOLDMEM = ONLY)

Сервер метрик

Сервер метрик используется в инфраструктуре QHB для сбора, агрегации и пересылки метрик в систему мониторинга Graphite. Список и особенности формирования метрик QHB содержатся в разделе Метрики QHB.

Тема установки и настройки Graphite выходит за рамки данной документации. Пожалуйста, обратитесь к документации Graphite.

Сервер метрик должен быть установлен и настроен на каждой машине, где работают компоненты QHB (сам сервер баз данных или QCP)/ Подробную информацию см. в разделе Установка.

Настройка сервера метрик

Пример файла конфигурации устанавливается по пути /etc/metricsd/config-example.yaml.

Для работы сервера необходимо скопировать его в /etc/metricsd/config.yaml и подправить необходимые параметры. Особого внимания требует секция aggregation → backends:

  # Конфигурация серверов. Должен быть сконфигурирован хотя бы один сервер.
  backends:
    # Конфигурация сервера graphite
    - graphite:
      # Адрес терминала TCP Graphite для текстового протокола. Порт по умолчанию: 2003.
      # Доступен только протокол TCP, поэтому если Graphite не принимает подключение к этому порту, это вызовет ошибку!
      address: "graphite:2003"
      # Префикс, добавляемый к имени каждой метрики. Необязательный параметр; по умолчанию — пустая строка.
      prefix: ""
      # Время ожидания подключения. Необязательный параметр; по умолчанию — 30 секунд.
      connection_timeout: "30 sec"
      # Время ожидания отправки данных. Необязательный параметр; по умолчанию — 5 секунд.
      send_timeout: "5 sec"

    # Конфигурация CSV-бэкенда (в случае необходимости копирования данных о метриках в CSV-файлы)
    - csv:
      # Каталог, содержащий CSV-файлы.
      directory: "/var/lib/qhb/csv_create"
      # Системный идентификатор инстанса QHB.
      qhb_instance: "instance_name"
      # Интервал переключения CSV-файлов (опционально).
      rotation_age: 1 h
      # Настройка времени следующего переключения после запуска (опционально).
      rotation_offset: 2022-03-01T18:00:00

Исправьте параметр address на реальный адрес сервера Graphite в вашей сети. Также рекомендуется изменить значение параметра prefix на, например, имя машины, на которой запущен сервер. Этот префикс будет добавляться ко всем генерируемым метрикам.

Параметры конфигурации CSV-бэкенда необходимы в случае, если нужно сохранять данные метрик в CSV-файлы.
Для этого используются следующие параметры:
directory указывает место расположения CSV-файлов;
qhb_instance - системный идентификатор инстанса, в чем-то аналогичен параметру prefix, но его значение при загрузке данных метрик заполняет отдельный столбец в таблице метрик, не связанный с именем метрик. Может быть полезен при наличии нескольких инстансов QHB на одном сервере;
rotation_age - период переключения CSV-файлов. Единицы измерения можно задавать, например, в виде секунд (s), минут (m), часов (h), дней (d).
rotation_offset - параметр задает время переключения на очередной CSV-файл после запуска сервера метрик. Время указывается для часового пояса UTC. Параметр удобно использовать для выравнивания начала очередного периода, например, по началу следующего часа.

Для автоматического запуска сервера при старте системы активируйте соответствующий сервис systemd:

$ sudo systemctl enable --now metricsd.service

Встроенная очередь сообщений

Описание

Очереди сообщений представляет собой подсистему СУБД для отправки типизированных сообщений и их получения заинтересованными (подписанными) клиентами. Существует два вида очередей: персистентные и in-memory. Если последние хранят сообщения до обработки всеми подписанными клиентами исключительно в памяти, то персистентные могут сохранять данные на диске, благодаря чему данные будут доступны после перезапуска базы.

На данный момент реализована только in-memory очередь. Она хранит элементы в буфере фиксированного размера, и если тот заполнится, функция qhb_enqueue будет перезаписывать наиболее старые элементы.

Необходимые системные настройки

Так как Брокер Очередей используется выделенный системный процесс СУБД, то необходимо учитывать разрешённое максимальное количество данных процессов задаваемое системной настройкой max_worker_processes.
Следует установить значение, с учётом количества брокеров, которые будут обсуживать очереди на данном кластере QHB.

В случае, если невозможно запустить дополнительные серверные процессы, функция start_mq_broker() вернёт ошибку.

Функции для работы с очередью сообщений

Имя | Описание
start_mq_broker() | Запуск брокера очереди сообщений
stop_mq_broker() | Остановка брокера очереди сообщений
qhb_create_queue(name,typeoid) | Создание очереди указанного типа
qhb_delete_queue(name) | Удаление очереди
qhb_register(queue,subscriber,callback) | Регистрация подписчика для обработки новых сообщений
qhb_unregister(queue,subscriber) | Удаление подписчика
qhb_enqueue(queue,data) | Добавление элемента в очередь
qhb_dequeue() | Получение элемента из очереди (доступно только из подписанного обработчика)

Брокер очереди сообщений

Брокер очереди сообщений — это отдельный процесс, который предоставляет внутренний интерфейс для работы с очередью. Для того чтобы с ней работать, требуется явно запустить данный процесс с помощью функции start_mq_broker(). Помимо брокера также запускается процесс worker, который фактически вызывает переданные ему обработчики и в случае возникновения каких-либо ошибок способен перезапустится. Оба процесса реализованы как встроенные BackgroundWorker, а следовательно, их pid можно обнаружить в таблице pg_stat_activity.

Процедура обработки сообщений

Сообщения доставляются подписчикам асинхронно с помощью вызова указанной пользователем процедуры. Для того чтобы обрабатывать новые сообщения, необходимо зарегистрировать процедуру-обработчик с помощью функции qhb_register. Сама процедура может быть создана с помощью CREATE PROCEDURE и должна иметь пустое множество аргументов, к примеру:

CREATE PROCEDURE callback()
LANGUAGE plpgsql AS $$
BEGIN
    CREATE TABLE IF NOT EXISTS my_table (x TEXT);
    INSERT INTO my_table
    SELECT x FROM qhb_dequeue() as (x TEXT);
END; $$;

Элемент безусловно удаляется из очереди до вызова обработчика, даже если обработчик не вызывает qhb_dequeue. Каждый вызов qhb_dequeue в пределах одного вызова обработчика будет возвращать один и тот же результат вне зависимости от того, сколько раз она была вызвана.

Любые ошибки, возникшие в процедуре, логируются и далее никак не обрабатываются (в том числе процедура-обработчик не удаляется из списка подписчиков). Возникновение ошибки в одном из обработчиков не влияет на получение сообщений другими обработчиками.

ВАЖНО Не существует никаких ограничений для использования функций для работы с очередью в процедуре обработки сообщений. То есть в обработчике сообщений кроме получения сообщения из очереди при помощи qhb_dequeue можно также добавить сообщение в очередь, создать и / или удалить очередь, зарегистрировать или удалить подписчика и т.п. Использовать отличные от qhb_dequeue функции для работы с очередью в обработчике нужно очень внимательно и осторожно - добавление сообщения в очередь в обработчике сообщения может привести к неуправляемому поведению системы, неконтролируемым побочным эффектам и потере сообщений в очереди из-за их перезаписи. При реализации процедуры-обработчика сообщения лучшей практикой будет избегать использования каких-либо функций работы с очередью кроме qhb_dequeue.

Типизация

Очередь является строго типизированной и может содержать элементы только одного типа; данный тип явно указывается при создании очереди. К примеру, очередь с именем my_queue1 и типом int4 можно создать следующим образом:

SELECT qhb_create_queue(name := 'my_queue1', typeoid := oid)
FROM pg_type
WHERE typname = 'int4';

Список поддерживаемых типов ограничен следующим перечнем:

Класс типа | Название конкретного типа
Числовой тип | int2, int4, int8
Тип с плавающей запятой | float4, float8
Булев тип | bool
Строковый тип | text, cstring, bpchar, varchar, mvarchar
Временной тип | timestamp, timestamptz, time, timetz, date, interval

Тип char напрямую не поддерживается. Вместо него следует использовать аналогичный тип bpchar.

При использовании enqueue или dequeue с некорректным типом будет выдана соответствующая ошибка.

Поддержка транзакционности

Очередь поддерживает транзакционность СУБД: отправка сообщения в очередь через qhb_enqueue внутри транзакции выполнит саму отправку только после успешного завершения текущей транзакции. При выполнении команды ROLLBACK или необработанном исключении внутри транзакции данные в очередь доставлены не будут и далее будут игнорироваться (обработчик при этом также остается зарегистрированным).

Внутреннее состояние очереди сообщений

Состояние очереди хранится для каждой базы данных отдельно в системных таблицах qhb_queue и qhb_queue_consumer. Первая содержит список очередей и их типы, вторая содержит список подписанных обработчиков и их соответствие с очередью. Данные таблицы рекомендуется использовать только в информационных целях, поскольку их изменение непосредственно ведет к неконсистентному состоянию.

QSched

QSched — встроенный планировщик заданий для QHB.

Внимание!
В релизе QHB 1.5.0 эта функциональность является экспериментальной, её использование в инсталляциях на продуктивной среде не рекомендовано.

Описание

Пользовательские задания выполняются набором фоновых процессов, которые уникальны для каждой базы данных. Основой для работы планировщика является набор системных таблиц с префиксом pg_catalog.qsched_..., который доступен для просмотра администратору базы данных и пользователю-владельцу базы данных.

Для создания и планирования заданий предусмотрено право SCHED, которое присваивается администратором базы данных пользователям, имеющим право работать с этой базой.

Использование

Присвоение права работы с QSched

Администратор/владелец базы данных вправе наделить любого пользователя правом работы с планировщиком:

-- GRANT CONNECT ON DATABASE ... TO ...;
GRANT SCHED ON DATABASE ... TO ...;

Запуск планировщика

SELECT start_qsched_master();
-- Из процедуры будет возвращен PID планировщика для данной базы данных.

Создание задачи

Задачи создаются и планируются посредством SQL-интерфейса, предусмотренного в планировщике QSched:

-- Общая форма для примера:
SELECT qsched_job_create_{proc,block,query}('job name', 'job payload');
-- Из процедуры будет возвращен JOB_ID:Oid.

-- Пример немедленного выполнения (для отладки):
SELECT qsched_job_exec(/*job id*/ 1);

-- Конкретная функция для создания задачи с содержимым в виде запроса:
SELECT qsched_job_create_query('job name', 'SELECT 123;');
-- Из процедуры будет возвращен JOB_ID:Oid.

-- Конкретная функция для создания задачи с содержимым в виде plpgsql-блока (в данном примере — бесконечный цикл):
SELECT qsched_job_create_block('job name', 'BEGIN LOOP PERFORM pg_sleep(1); END LOOP; END;')
-- из процедуры будет возвращен JOB_ID:Oid.

-- Конкретная функция для создания задачи с содержимым в виде хранимой процедуры (существование процедуры 'proc_name' проверяется во время выполнения):
SELECT qsched_job_create_proc('job name', 'proc_name');
-- Из процедуры будет возвращен JOB_ID:Oid.

Примечания по содержимому выполняемых заданий

При создании задачи можно указать несуществующую в данный момент процедуру, имя которой будет разрешаться при выполнении (относительно текущего значения параметра search_path), либо можно указать процедуру сразу с именем схемы:

SELECT qsched_job_create_proc('job name','my_schema.my_proc');

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

-- Пример использования анонимного блока с целью фиксирования значений аргументов
-- (аргументы также могут рассчитываться внутри анонимного блока):
SELECT qsched_job_create_block('job name', $$BEGIN PERFORM my_proc('my_arg1', 'my_arg2'); END;$$);

Создание расписания

Задача планируется одним или несколькими периодическими расписаниями с cron-подобным синтаксисом:

-- Запланирует для выполнения каждые 30 секунд:
SELECT qsched_schedule_create(/*job id*/ 1, '1/30 * * * * *');

-- Включает все расписания данной задачи в набор выполняемых фоновых задач QSched:
SELECT qsched_job_enable(/*job id*/ 1);

Выключение расписаний/задач

-- Выключает все расписания для данной задачи из набора выполняемых фоновых задач QSched:
SELECT qsched_job_disable(/*job id*/ 1);

Включение и выключение конкретных расписаний

Наряду с функциями включения и отключения целостных задач qsched_job_{enable,disable}, существуют функции, которые работают с гранулярностью расписания. Так, передав идентификатор расписания в функции qsched_schedule_{enable,disable}, можно активировать или отключить конкретное расписание конкретной задачи.

Узнать идентификаторы расписаний можно в системном представлении qsched_my_jobs.

Просмотр логов планировщика

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

-- Покажет представление, основанное на системной таблице pg_catalog.qsched_job_run_status:
SELECT * FROM qsched_my_log;

Просмотр общей таблицы задач и расписаний для пользователя:

Обратившись к представлению qsched_my_jobs, пользователь может увидеть задачи и расписания, которые создавал сам, либо те, которые запланированы исполняться от его имени.

-- Выведет полную сводку по "вашим" задачам и расписаниям:
SELECT * FROM qsched_my_jobs;

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

Допустим, мы создали задачу и установили ей расписание в формате crontab:

SELECT qsched_job_create_query('job name', 'SELECT 123');
SELECT qsched_schedule_create(1, '* * * * * *');

SELECT qsched_job_enable(1);
COMMIT;

В данном случае мы активировали задачу, и, согласно расписанию, она будет выполняться каждую секунду. Но, например, мы хотим, чтобы следующее время выполнения задачи рассчитывалось динамически, после очередного успешного выполнения. Для этого SQL-интерфейс QSched предоставляет функцию qsched_schedule_dyn_override, которая для заданного расписания позволяет определить SQL-команду для расчета времени следующего выполнения:

-- Общий синтаксис:
SELECT qsched_schedule_dyn_override(/*schedule Oid*/, /*Dynamic SQL*/);

-- Пример:
SELECT qsched_schedule_dyn_override(1, $$CURRENT_TIMESTAMP + '1 day'$$);

Результат выполнения данной SQL-команды всегда будет интерпретирован как время, а точнее TIMESTAMP WITHOUT TIME ZONE.

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

Отмена динамического вычисления времени следующего старта задачи

Если вы хотите, чтобы задача не вычисляла свое время запуска динамически, согласно выражению в столбце next_fire_statement, а просто действовала по расписанию, то следует выполнить запрос с использованием функции qsched_schedule_cancel_override, передав ей идентификатор расписания:

SELECT qsched_schedule_cancel_override(/* schedule id */ 1);

Здесь для примера указан идентификатор 1, но это может быть любое значение типа Oid. Тогда ваша задача будет следовать расписанию для определения времени запуска.

Убедиться в правильности изменений можно обратившись к представлению qsched_my_jobs, задав желаемый идентификатор задачи:

SELECT next_fire_statement FROM qsched_my_jobs WHERE job_id = /*job_id*/ 1;

Выполнение произвольного SQL для задачи, завершившейся ошибкой

Допустим, мы создали задачу и ее идентификатор 123. Задача может завершиться неудачей при любом исполнении, и мы хотим настроить SQL-выражение, которое бы выполнялось процессом QSched при таком неудачном завершении:

-- Вторым аргументом указано SQL-выражение Fallback:
SELECT qsched_job_set_fallback_sql(123, 'UPDATE err_log_table SET failure = 1;');

Теперь каждый раз, когда данная задача будет завершаться неудачно, исполнитель процесса QSched будет пытаться обновить записи в таблице err_log_table с выставлением поля failure в 1. (Данная операция дана в качестве примера, можно использовать произвольное SQL-выражение.)

Если и задача завершилась неудачно, и Fallback-выражение выполнилось с ошибкой, в системных таблицах QSched данный факт отмечается и доступен для просмотра в представлении qsched_my_log:

-- Столбец double_fault означает двойную ошибку — и содержимого задачи, и Fallback-выражения.
SELECT double_fault FROM qsched_my_log;

Отмена SQL-выражения Fallback для конкретной задачи

Если вы хотите, чтобы Fallback-выражение больше не выполнялось при неудачном завершении какой-либо задачи, необходимо выполнить запрос с использованием функции qsched_job_unset_fallback_sql:

SELECT qsched_job_unset_fallback_sql(/*job id*/ 1);

Удаление и отключение ненужных задач

В данной версии поддерживается полное удаление задачи (исключая историю исполнения) с помощью функции qsched_job_delete:

-- Удаление Задачи, ее расписания, логов и историю исполнения:
SELECT qsched_job_delete(1 /*job oid*/);

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

-- Сделает все расписания для данной задачи неактивными, включая запланированные с помощю qsched_job_exec немедленные запуски:
SELECT qsched_job_disable(1 /*job oid*/);

Активировать все расписания для данной задачи обратно можно с помощью функции qsched_job_enable(/*job oid*/).

Число допустимых параллельных процессов

С помощью системной настройки qsched_parallel_slaves администратор данной базы данных может установить ограничение на число параллельных процессов QSched, выделенных под исполнение пользовательских задач.

-- Увеличить допустимое число параллельных процессов до 4:
ALTER DATABASE SET qsched_parallel_slaves TO 4;

-- После этого следует перезапустить координатор процессов:
SELECT start_qsched_master();

-- Но перед этим следует завершить текущий координатор.
-- Узнать PID можно, обратившись к дереву процессов либо к представлению pg_stat_activity.
\! kill -TERM <pid_of_qsched_master>

SHOW qsched_parallel_slaves;

Данное ограничение действует по отношению к конкретной базе данных и не распространяется на процессы-координаторы QSched Master.

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

В QSched введено стандартное для многих Enterprise-планировщиков ограничение: хотя многие задачи могут исполняться параллельно, данная конкретная задача (имеющая Oid и имя) не будет исполняться параллельно с экземпляром той же самой задачи. Это сделано для защиты от ненамеренной порчи данных, если, например, содержимое задачи модифицирует таблицы СУБД или делает какие-то другие модифицирующие действия.

Переменные уровня сессии - qhb-variables

Внимание!
В релизе QHB 1.5.0 эта функциональность является экспериментальной, её использование в инсталляциях на продуктивной среде не рекомендовано.

Описание

Qhb-variables позволяет создавать переменные, которые будут удалены по завершении сессии. Этот механизм является альтернативой временным таблицам в местах, где их производительности недостаточно. Переменные хранятся в пакетах. Существует два типа переменных: скалярные и табличные. В скалярных переменных за раз можно хранить только одно значение любого типа, в табличных же можно хранить множество строк. Переменные также подразделяются на транзакционные и нетранзакционные. Состояние первых не зависит от результата транзакции, а вторые меняют своё значение учитывая COMMIT и ROLLBACK. Тип переменной нельзя менять после создания до полного удаления (в случае транзакционной переменной до COMMIT'а).

Функции qhb-variables

Функции для работы со скалярными переменными

Функция | Возвращаемое значение | Описание
var_set(package text, variable text, value (non)anynonarray, is_transactional bool default false) | void | Установка значения переменной, в том числе обновление
var_get(package text, variable text, var_type (non)anynonarray, strict bool default true) | anynonarray | Получение значения переменной

Примеры использования

SELECT var_set('pack', 'var', 0);
 var_set
---------

(1 row)

SELECT var_get('pack', 'var', NULL::int);
 var_get
---------
       0
(1 row)

BEGIN;
SELECT var_set('pack', 'var', 'before savepoint'::text, true);
 var_set
---------

(1 row)

SAVEPOINT zero;
SELECT var_set('pack', 'var', 'after savepoint'::text, true);
 var_set
---------

(1 row)

SELECT var_get('pack', 'var', NULL::text);
     var_get
-----------------
 after savepoint
(1 row)

ROLLBACK TO zero;
SELECT var_get('pack', 'var', NULL::text);
     var_get
------------------
 before savepoint
(1 row)

Функции для работы с табличными переменными

Функция | Возвращаемое значение | Описание
var_insert(package text, variable text, r record, is_transactional bool default false) | void | Вставляет новую запись в табличную переменную. Первая колонка записи является первичным ключом. Возвращает ошибку, если запись с таким ключом уже есть или запись имеет другую сигнатуру.
var_update(package text, variable text, r record) | boolean | Обновляет запись в табличной переменной, опираясь на ключевое поле. Возвращает true, если запись была обновлена. Если сигнатура переменной отличается от предоставленной строки, то возникнет ошибка.
var_delete(package text, variable text, value anynonarray) | boolean | Удаляет запись по ключу. Возвращает true, если запись была удалена.
var_select(package text, variable text) | set of record | Возвращает текущее значение переменной.
var_select(package text, variable text, value anynonarray) | record | Возвращает текущее значение переменной по ключу.
var_select(package text, variable text, value anyarray) | set of record | Возвращает текущее значение переменной по ключам.

Примеры использования

SELECT var_insert('pac', 'var', ROW(NULL::int, 0::int));
 var_insert
------------

(1 row)

SELECT var_insert('pac', 'var', ROW(0::int, 1::int));
 var_insert
------------

(1 row)

SELECT var_insert('pac', 'var1', ROW(0::int, 2::int));
 var_insert
------------

(1 row)

SELECT var_insert('pac', 'var1', ROW(2::int, 2::int));
 var_insert
------------

(1 row)

SELECT var_select('pac', 'var');
 var_select
------------
 (,0)
 (0,1)
(2 rows)

SELECT var_select('pac', 'var1', 0);
 var_select
------------
 (0,2)
(1 row)

SELECT var_select('pac', 'var1', ARRAY[0, 1]);
 var_select
------------
 (0,2)
(1 row)

Общие функции переменных

Функция | Возвращаемое значение | Описание
var_exists(package text, variable text, is_record bool) | bool | Возвращает true, если переменная существует в данном пакете.
var_exists(package text) | bool | Возвращает true, если пакет существует.
var_remove(package text, variable text, is_record bool) | void | Удаляет переменную по имени. Если переменной или пакета не существует, то будет возвращена ошибка.
var_remove(package text) | void | Удаляет пакет со всеми переменными внутри него. Если пакета не существует, то будет возвращена ошибка.
var_free() | void | Удаляет все пакеты со всеми переменными внутри.
var_list() | table(package text, variable text, variable_type text,is_transactional bool) | Возвращает список всех пакетов и переменных с указанием их транзакционности и типа.
var_stats() | table(allocated_memory bigint) | Возвращает занимаемое место переменными. Обратите внимание, что эта функция возвращает выделенное место, которое может быть использовано не полностью. За более подробной информацией используйте pg_backend_memory_contexts()

Примечание
В одном пакете могут быть две переменные с одинаковым названием, но только при условии, что одна из них табличная, а другая скалярная. По умолчанию функции var_exists и var_remove удаляют обе переменные, если вам нужно выбрать только табличную переменную, передайте последним параметром true, для удаления только скалярной переменной передайте false. Этот параметр является опциональным.

BEGIN;
SELECT var_set('pack', 'var', 0, true);
 var_set
---------

(1 row)

SELECT var_insert('pack', 'var', ROW(0::int, 1::int), true);
 var_insert
------------

(1 row)

SELECT * FROM var_list();
 package | variable |         variable_type         | is_transactional
---------+----------+-------------------------------+------------------
 pack    | var      | Scalar(integer)               | t
 pack    | var      | Record(row(integer, integer)) | t
(2 rows)

SAVEPOINT first;
SELECT var_remove('pack', 'var', true);
 var_remove
------------

(1 row)

SELECT var_exists('pack', 'var', true);
 var_exists
------------
 f
(1 row)

SELECT var_exists('pack', 'var', false);
 var_exists
------------
 t
(1 row)

SELECT var_exists('pack', 'var');
 var_exists
------------
 t
(1 row)

SELECT var_remove('pack', 'var');
 var_remove
------------

(1 row)

SELECT var_exists('pack', 'var', false);
 var_exists
------------
 f
(1 row)

ROLLBACK TO first;
SELECT var_exists('pack', 'var');
 var_exists
------------
 t
(1 row)

Примеры использования:

BEGIN;
SELECT var_set('pack', 'var', 'non-transactional'::text);
 var_set
---------

(1 row)

SELECT var_set('pack', 'var1', 'transactional'::text, true);
 var_set
---------

(1 row)

SELECT * FROM var_list();
 package | variable | variable_type | is_transactional
---------+----------+---------------+------------------
 pack    | var      | Scalar(text)  | f
 pack    | var1     | Scalar(text)  | t
(2 rows)

SAVEPOINT first;
SELECT var_free();
 var_free
----------

(1 row)

SELECT var_stats();
 var_stats
-----------
      8192
(1 row)

SELECT * FROM var_list();
 package | variable | variable_type | is_transactional
---------+----------+---------------+------------------
(0 rows)

ROLLBACK TO first;
SELECT * FROM var_list();
 package | variable | variable_type | is_transactional
---------+----------+---------------+------------------
 pack    | var1     | Scalar(text)  | t
(1 row)

Другие примеры использования

Вставка из таблицы и деструктуризация SELECT

CREATE TABLE test_table (id int, t text);
INSERT INTO test_table VALUES (0, 'first'), (1, 'second');
SELECT var_insert('pack', 'var', test_table) FROM test_table;
 var_insert
------------

(2 rows)

SELECT var_select('pack', 'var');
 var_select
------------
 (1,second)
 (0,first)
(2 rows)

SELECT * FROM var_select('pack', 'var') AS (id int, t text);
 id |   t
----+--------
  1 | second
  0 | first
(2 rows)

Изменение типа при полном удалении переменной

SELECT var_set('pack', 'var', 1::int);
 var_set
---------

(1 row)

SELECT var_set('pack', 'var_transactional', 1::int, true);
 var_set
---------

(1 row)

SELECT * FROM var_list();
 package |     variable      |  variable_type  | is_transactional
---------+-------------------+-----------------+------------------
 pack    | var_transactional | Scalar(integer) | t
 pack    | var               | Scalar(integer) | f
(2 rows)

BEGIN;
SELECT var_free();
 var_free
----------

(1 row)

SELECT var_set('pack', 'var', 1::text); -- ok
 var_set
---------

(1 row)

-- SELECT var_set('pack', 'var_transactional', 1::text, true); -- Err
COMMIT;
SELECT var_set('pack', 'var_transactional', 1::text, true); -- Ok
 var_set
---------

(1 row)

SELECT * FROM var_list();
 package |     variable      | variable_type | is_transactional
---------+-------------------+---------------+------------------
 pack    | var_transactional | Scalar(text)  | t
 pack    | var               | Scalar(text)  | f
(2 rows)

Этот пример работает потому что переменные были безвозвратно удалены при вызове var_free.

Сохранение элементов таблицы

При вставке в переменную создаётся независимая копия данных.

CREATE TABLE test_table (id int, t text);
INSERT INTO test_table VALUES (0, 'first'), (1, 'second'), (2, 'third');
SELECT var_insert('pack', 'var', test_table) FROM test_table WHERE id < 2;
 var_insert
------------

(2 rows)

DROP TABLE test_table;
CREATE TABLE test_table AS SELECT * FROM var_select('pack', 'var') AS (id int, t text);
SELECT * FROM test_table;
 id |   t
----+--------
  1 | second
  0 | first
(2 rows)

SELECT + CURSOR

При вызове var_select, создаётся мгновенная копия данных, поэтому на результат SELECT'а var_update не повлияет. Однако, если переменная была удалена, тогда получение оставшихся данных станет невозможным.

CREATE TABLE test_table (id int, t text);
INSERT INTO test_table VALUES (0, 'first'), (1, 'second'), (2, 'third');
SELECT var_insert('pack', 'var', test_table, true) FROM test_table;
 var_insert
------------

(3 rows)

BEGIN;
DECLARE r1_cur CURSOR FOR SELECT var_select('pack', 'var');
FETCH 1 in r1_cur;
 var_select
------------
 (2,third)
(1 row)

SELECT var_remove('pack');
 var_remove
------------

(1 row)

FETCH 1 in r1_cur;
ERROR:  var_select: Existence: Variable with name `var` doesn't exist in package `pack`
ROLLBACK;
SELECT var_select('pack', 'var');
 var_select
------------
 (2,third)
 (1,second)
 (0,first)
(3 rows)

Профилирование долгих запросов

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

Подготовка

Для включения профилирования необходимо установить параметр query_profiling в настройках (либо в конфигурационном файле, либо динамически с помощью команды SET).

Для профилирования также необходима функционирующая очередь сообщений, в частности вызов select start_mq_broker();. Далее всё взаимодействие происходит с системными очередями, которые уже созданы.

Анализ информации очень чувствителен к статической информации поддерживаемой СУБД, поэтому рекомендуется использовать ANALYZE для исследуемых таблиц.

Анализ информации.

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

qhb_running_queries

qhb_dump_qep

Функция qhb_running_queries возвращает временную таблицу, которая содержит следующую информацию:

Название колонки | Тип | Описание | Комментарий
query_id | int8 | Уникальный идентификатор запроса |
backend_pid | int8 | pid postgres-бэкенда, на котором выполняется запрос | Эквивалентен значению из pg_stat_activity.pid
query_string | text | Текстовая строка запроса |
query_started | timestamptz | Время начала работы запроса |
ETC | timestamptz | Ориентировочное время окончания работы | Может быть NULL, при сложных запросах оценка может быть далека от реальности
elapsed_counter | float8 | Внутренний счетчик для отслеживания выполненной работы запроса |
estimated_total_counter | float8 | Внутреннее ожидаемое количество работы для запроса |

Функция qhb_dump_qep принимает идентификатор запроса из qhb_running_queries и отображает дерево плана запроса (query execution plan). Второй аргумент принимает строку со способом форматирования:

'full' — отображает полные значения счетчиков;

'scaled' — отображает значения счетчиков приведенные к одному порядку (отображается в поле units);

'scientific' — отображает значения счетчиков;

'percent' — отображает значения счетчиков в процентах;

'mixed_percent' — отображает значения счетчиков в процентах и абсолютное ожидаемое значение;

Функция возвращает дерево плана, эквивалентное тому, что возвращает EXPLAIN, а также статус выполнения для каждого узла. Стоит иметь ввиду, что узлы могут иметь прогресс в 100%, но тем не менее вызываться повторно увеличивая счетчик циклов (loops). Пример анализа запроса:

# select start_mq_broker();
 start_mq_broker
-----------------

(1 row)

# create table tb (x int);
CREATE TABLE
# insert into tb select generate_series(1, 50000);
INSERT 0 50000
# select a.x, count(*) from tb a, tb b group by a.x;

В другом клиенте:

# select * from qhb_running_queries();
 query_id | backend_pid |                    query_string                    |         query_started         |              ETC              |  elapsed_counter   | estimated_total_counter
----------+-------------+----------------------------------------------------+-------------------------------+-------------------------------+--------------------+-------------------------
        7 |        1064 | select a.x, count(*) from tb a, tb b group by a.x; | 2022-02-10 16:34:01.719477+03 | 2022-02-10 16:46:49.189314+03 | 2337060.9605833497 |             97739647.85
(1 row)

# select qhb_dump_qep(query_id, 'percent') from qhb_running_queries() where query_string like 'select a.x%';
                  qhb_dump_qep
-------------------------------------------------
                                                +
 ~  Agg (progress: 0.00%, loops:  0)            +
   ~  Nest Loop (progress: 5.69%, loops:  0)    +
     -> Seq Scan (progress: 6.45%, loops:  0)   +
     ~  Material (progress: 88.30%, loops: 3648)+
       ~  Seq Scan (progress: 88.32%, loops:  0)+

(1 row)

Ограничения

Реализация в текущем виде содержит ограничения:

Память для внутренних структур профилирования фиксирована и ограничена 8 МиБ, при превышении данного значения, профилирование будет игнорироваться.

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

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

Формат манифеста резервной копии

Манифест резервной копии, генерируемый утилитой qhb_basebackup, предназначен в первую очередь для того, чтобы эту копию можно было проверить с помощью qhb_verifybackup. Однако и другие программные средства также могут прочитать файл манифеста резервной копии и использовать содержащуюся там информацию в своих целях. В связи с этим в данной главе описывается формат файла манифеста.

Манифест резервной копии представляет собой документ JSON в кодировке UTF-8. (Хотя в целом документы JSON должны быть представлены в Unicode, QHB допускает использование типов данных json и jsonb в любой поддерживаемой сервером кодировке. Для манифестов резервных копий подобное исключение не делается.) Этот документ JSON всегда содержит объект; ключи, находящиеся в этом объекте, описываются в разделах ниже.

Объект верхнего уровня в манифесте копии

Документ JSON, представляющий манифест копии, содержит следующие ключи.

PostgreSQL-Backup-Manifest-Version
Связанное с этим ключом значение всегда равно целому числу 1.

Files
Связанное с этим ключом значение всегда является списком объектов, каждый из которых описывает один файл, имеющийся в резервной копии. В этом списке отсутствуют записи о файлах WAL, необходимых для использования этой копии, а также о самом манифесте. Структура каждого объекта в этом списке описана в разделе Объект файла в манифесте копии.

WAL-Ranges
Связанное с этим ключом значение всегда является списком объектов, каждый из которых описывает диапазон записей WAL, которые должны быть доступны для чтения из определенной временной шкалы, чтобы можно было воспользоваться этой копией. Подробнее структура этих объектов описана в разделе Объект диапазона WAL в манифесте копии.

Manifest-Checksum
Этот ключ всегда находится в последней строке файла манифеста резервной копии. Связанное с ним значение является контрольной суммой всех предыдущих строк, вычисленной по алгоритму SHA256. Здесь используется фиксированный метод контрольных сумм, чтобы клиенты могли провести инкрементальный синтаксический анализ манифеста. Хотя алгоритм SHA256 значительно затратнее алгоритма CRC32C, обычно манифест достаточно невелик, чтобы эти дополнительные вычисления ни на что особо не влияли.

Объект файла в манифесте копии

Это объект, описывающий один файл и содержащий либо ключ Path, либо ключ Encoded-Path. Обычно содержит ключ Path. Связанное строковое значение представляет собой путь к файлу относительно корневого каталога резервной копии. У файлов, расположенных в пользовательском табличном пространстве, первыми компонентами пути будут qhb_tblspc и OID этого табличного пространства. Если путь является строкой не в кодировке UTF-8 или пользователь хочет, чтобы закодированные пути использовались для всех файлов, то вместо ключа Path будет присутствовать ключ Encoded-Path. В нем содержатся те же данные, но закодированные в виде строки шестнадцатеричных цифр, каждая пара которых представляет один восьмибитовый байт.

Всегда присутствуют следующие два ключа:

Size
Ожидаемый размер этого файла, в виде целого числа.

Last-Modified
Время последнего изменения файла, выданное сервером в процессе создания резервной копии. В отличие от других полей, хранящихся в копии, это поле не используется утилитой qhb_verifybackup. Оно добавлено исключительно для информации.

Если резервная копия была сделана с включенным вычислением контрольных сумм файлов, будут присутствовать следующие ключи:

Checksum-Algorithm
Алгоритм, который применялся для расчета контрольной суммы этого файла. В настоящее время этот алгоритм будет одинаковым для всех файлов в манифесте резервной копии, но в будущих выпусках это может измениться. На данный момент поддерживаются алгоритмы CRC32C, SHA224, SHA256, SHA384 и SHA512.

Checksum
Контрольная сумма, подсчитанная для этого файла, которая хранится в виде последовательности шестнадцатеричных цифр, по две на каждый байт контрольной суммы.

Объект диапазона WAL в манифесте копии

Объект, описывающий диапазон WAL, всегда содержит три ключа:

Timeline
Временная шкала для этого диапазона записей WAL, в виде целого числа.

Start-LSN
Позиция LSN, с которой должно начаться воспроизведение на указанной временной шкале, чтобы эту резервную копию можно было применить для восстановления. LSN хранится в формате, принятом в QHB, т. е. в виде строки, состоящей из двух разделенных слэшем строк длиной от 1 до 8 шестнадцатеричных символов.

End-LSN
Самый первый LSN, на котором может закончиться воспроизведение на указанной временной шкале при восстановлении из резервной копии. Он хранится в том же формате, что и Start-LSN.

Обычно в манифесте описывается всего один диапазон WAL. Однако если копия была сделана с резервного сервера, переключившего временные шкалы во время ее создания, чтобы увеличить поток данных, в манифесте могут присутствовать несколько диапазонов, относящихся к разным временным шкалам. К одной временной шкале всегда будет относиться только один диапазон WAL.

Часть II. Установка и настройка

В этой части рассматриваются темы, представляющие интерес для администратора базы данных QHB. Это включает в себя установку программного обеспечения, настройку сервера, управление пользователями и базами данных, а также задачи по обслуживанию. Любой, кто запускает сервер QHB, даже для личного использования, особенно в производственной среде, должен быть знаком с темами, описанными в этой части.

Информация в этой части расположена приблизительно в том порядке, в котором её должен прочитать новый пользователь. Однако главы являются автономными и, по желанию, могут быть прочитаны независимо друг от друга в любом порядке. Информация в этой части представлена в повествовательной форме. Читателям, ищущим полное описание конкретной команды, следует обратиться к главе Команды SQL.

Первые несколько глав написаны так, чтобы их можно было понять без предварительных знаний, чтобы новые пользователи, которым необходимо настроить собственный сервер, спокойно могли начать свое исследование. Остальные же главы этой — о настройке и управлении — предполагают, что читатель уже знаком с общими принципами использования базы данных QHB. Для получения дополнительной информации, рекомендуется ознакомиться с внутренним устройством и языком SQL.

Установка

Начало работы

Создание базы данных

Доступ к базе данных

Настройка и эксплуатация сервера

Учетная запись пользователя

Создание кластера базы данных

Запуск сервера базы данных

Завершение работы сервера

Конфигурация сервера

Настройка параметров

Расположение файлов

Подключения и аутентификация

Потребление ресурсов

Журнал упреждающей записи (wal)

Репликация

Планирование запросов

Регистрация ошибок и протоколирование

Статистика во время выполнения

Автоматическая очистка

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

Управление блокировками

Совместимость с разными версиями и платформами

Обработка ошибок

Предустановленные параметры

Специализированные параметры

Параметры для разработчиков

Краткие аргументы

Советы по производительности

Использование EXPLAIN

Статистика, используемая планировщиком

Управление планировщиком с помощью явных предложений JOIN

Заполнение базы данных

Настройки снижающие прочность

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

Создание пользовательских путей сканирования

Создание пользовательских планов сканирования

Выполнение пользовательских сканирований

Аутентификация клиентского приложения

Файл pg_hba.conf

Файл сопоставлений имен пользователей

Методы аутентификации

Аутентификация trust

Аутентификация по паролю

Аутентификация GSSAPI

Аутентификация ident

Аутентификация peer

Аутентификация LDAP

Аутентификация RADIUS

Аутентификация по сертификату

Аутентификация PAM

Аутентификация BSD

Проблемы аутентификации

Роли в базе данных

Роли базы данных

Атрибуты ролей

Ролевая модель управления доступом

Удаление ролей

Роли по умолчанию

Функция безопасности

Управление базами данных

Обзор

Создание базы данных

Шаблоны баз данных

Конфигурация базы данных

Удаление базы данных

Табличные пространства

Методы выборки из таблицы

Функции поддержки метода выборки

Регулярные задачи обслуживания базы данных

Регулярная очистка

Регулярная переиндексация

Обслуживание файла журнала

Резервное копирование и восстановление

SQL-дамп

Резервное копирование на уровне файловой системы

Непрерывное архивирование и восстановление на момент времени (PITR)

Мониторинг активности базы данных

Стандартные инструменты Unix

Сборщик статистики

Просмотр блокировок

Отчеты о ходе выполнения команд

Мониторинг использования диска

Определение использования диска

Ошибка диска

Прочность и журнал упреждающей записи

Прочность

Контрольные суммы данных

Журнал упреждающей записи (WAL)

Асинхронный коммит

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

Внутреннее устройство WAL

Процедурные языки

Установка процедурных языков

Клиентские приложения QHB

clusterdb — кластеризация базы данных QHB

createdb — создать новую базу данных QHB

createuser — определить новую учетную запись пользователя QHB

dropdb — удалить базу данных QHB

dropuser — удалить учетную запись пользователя QHB

psql — QHB интерактивный терминал

qhb_amcheck — проверить одну или несколько баз данных QHB на предмет повреждения

qhb_basebackup — сделать базовую резервную копию кластера QHB

qhb_dump — извлечь базу данных QHB в файл сценария или другой архив

qhb_dumpall — извлечь кластер базы данных QHB в файл сценария

qhb_isready — проверить состояние соединения с сервером QHB

qhb_receivewal — потоковые журналы записи с сервера QHB

qhb_recvlogical — управление потоками логического декодирования QHB

qhb_restore — восстановить базу данных QHB из файла архива, созданного qhb_dump

qhb_verifybackup — проверить целостность базовой резервной копии кластера QHB

reindexdb — переиндексировать базу данных QHB

vacuumdb — сбор мусора и анализ базы данных QHB

Серверные приложения QHB

initdb - создать новый кластер базы данных QHB

qhb_archivecleanup - очистить архивные файлы QHB WAL

qhb_checksums - включить, выключить или проверить контрольные суммы данных в кластере базы данных QHB

qhb_controldata - отображать управляющую информацию кластера базы данных QHB

qhb_ctl - инициализация, запуск, остановка или управление сервером QHB

qhb_resetwal - сбросить журнал предварительной записи и другую управляющую информацию кластера базы данных QHB

qhb_rewind - синхронизировать каталог данных QHB с другим каталогом данных, который был разветвлен из него

qhb_upgrade - обновить экземпляр сервера QHB

qhb_waldump - отобразить удобочитаемый рендеринг журнала записи с опережением кластера базы данных QHB

Написание обёртки сторонних данных

Функции обёрток сторонних данных

Процедуры обратного вызова сторонних данных

Вспомогательные функции для обёртки сторонних данных

Планирование запросов на обработку сторонних данных

Блокировка строк в обёртках сторонних данных

Инструмент резервного копирования qbackup

Установка

Поддерживаемые платформы

Платформа, то есть комбинация архитектуры процессора и операционной системы, считается поддерживаемой сообществом разработчиков QHB.

Для QHB версии 1.5.0 поддерживаются:

Centos Linux 7, x86_64

Centos Linux 8, x86_64

Альт Сервер 9, x86_64

Fedora 33, x86_64

Fedora 34, x86_64

Linux Debian 9, x86_64

Astra Linux Special Edition, «Смоленск» 1.6, x86_64

в docker контейнере

Состав поставки

QHB поставляется в виде следующих пакетов

qhb-core - ядро QHB

qhb-contrib - расширения QHB

qcp - пулер соединений

qdl - средство быстрой загрузки

qbackup - инструмент резервного копирования

metricsd - сервер метрик

Установка из репозитория пакетов

Внимание!
Если вы обновляете версию QHB или мигрируете базы данных с других СУБД, обратитесь к разделу Установка с обновлением.

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

Стандартная процедура установки QHB выглядит так:

Подключение репозитория и установка пакетов

Подключите репозиторий пакетов и установите пакеты для выбранной платформы со Страницы загрузки.

Инициализация кластера базы данных

Инициализируйте кластер базы данных при помощи утилиты initdb или qhb_bootstrap. В данном примере /opt/qhb/data — расположение каталога базы данных:

Внимание!
Запуск утилит необходимо производить из под пользователя qhb, владельца экземпляра.
Или предварять запуск командой sudo -u qhb .

/usr/local/qhb/bin/initdb -D /opt/qhb/data -U qhb

или

/usr/local/qhb/bin/qhb_bootstrap -D /opt/qhb/data -U qhb

Замечание.
Использование утилиты qhb_bootstrap предпочтительно.
Утилита initdb планируется к удалению в будущих версиях QHB.

Настройка сервиса

Если необходимо, настройте сервис базы данных, см. Настройка сервиса базы данных.

Создание базы данных и конфигурирование

Следуйте инструкция по созданию и настройке базы данных, см. Начало работы.

Установка с обновлением

Обновление версии QHB и миграция баз данных с других СУБД.

При переходе с одной версии QHB к другой, иногда происходят такие изменения в структурах данных каталога СУБД, которые делают невозможным функционирование новой версии без преобразования данных старой базы.

Для такого преобразования предусмотрена специальная утилита обновления экземпляра qhb_upgrade.

Некоторые обновления не требуют запуска qhb_upgrade:

обновление пакетов из репозитория, если база данных ещё не была создана или планируется создание новой, а старая не важна,

установка QHB впервые или

миграция базы с помощью утилит qhb_dump / qhb_dumpall.

Если же планируется перенос или миграция базы данных, перед обновлением нужно обратиться утилите qhb_upgrade. Утилита может обновлять данные не только при обновлении версии QHB, но даже при миграции с PostgreSQL некоторых версий.

Некоторые минорные обновления QHB не меняют структуру данных, и их установка не требуют каких-то специальных действий. В этом случае, утилита qhb_upgrade об этом сообщит.

Начало работы

Создание базы данных

Подключение к базе данных

Создание базы данных

Первый тест, чтобы увидеть, можете ли вы получить доступ к серверу базы данных - попытаться создать базу данных. Работающий сервер QHB может управлять многими базами данных. Как правило, отдельная база данных используется для каждого проекта или для каждого пользователя.

Возможно, ваш администратор уже создал базу данных для вашего использования. В этом случае вы можете пропустить этот шаг и перейти к следующему разделу.

Чтобы создать новую базу данных, в этом примере с именем mydb, вы используете следующую команду:

createdb mydb -h localhost

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

Если вы видите сообщение, похожее на:

createdb: command not found

тогда QHB не был установлен должным образом. Либо он вообще не был установлен, либо путь поиска вашей оболочки не был настроен на его включение. Попробуйте вместо этого вызвать команду с абсолютным путем:

/usr/local/qhb/bin/createdb mydb

Ваш путь может быть другим. Обратитесь к вашему администратору или ознакомьтесь с инструкциями по установке, чтобы исправить ситуацию.

Другой ответ может быть таким:

createdb: could not connect to database qhb: could not connect to server: No such file or directory
        Is the server running locally and accepting
        connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

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

Другой ответ может быть таким:

createdb: could not connect to database qhb: FATAL:  role "joe" does not exist

где упоминается ваше имя пользователя. Это произойдет, если администратор не создал для вас учетную запись пользователя QHB. (Учетные записи пользователей QHB отличаются от учетных записей пользователей операционной системы). Если вы являетесь администратором, обратитесь к главе Роли в базе данных за помощью в создании учетных записей. Вам нужно стать пользователем операционной системы, под которой был установлен QHB (обычно qhb), чтобы создать первую учетную запись пользователя. Возможно также, что вам было присвоено имя пользователя QHB, отличающееся от имени пользователя вашей операционной системы; в этом случае вам нужно использовать ключ -U или установить окружение PGUSER, чтобы указать свое имя пользователя в QHB.

Если у вас есть учетная запись пользователя, но у нее нет прав, необходимых для создания базы данных, вы увидите следующее:

createdb: database creation failed: ERROR:  permission denied to create database

Не каждый пользователь имеет право создавать новые базы данных. Если QHB отказывается создавать базы данных для вас, ваш администратор должен предоставить вам разрешение на создание баз данных. Обратитесь к вашему администратору, если это произойдет. Если вы установили QHB самостоятельно, вы должны войти в систему в целях данного руководства под учетной записью пользователя, с которой вы запустили сервер¹.

Вы также можете создавать базы данных с другими именами. QHB позволяет создавать любое количество баз данных. Имена баз данных должны иметь алфавитный первый символ и иметь длину не более 63 байтов. Удобный выбор - создать базу данных с тем же именем, что и ваше текущее имя пользователя. Многие инструменты предполагают, что имя базы данных используется как имя по умолчанию, поэтому оно поможет вам сэкономить время при наборе текста. Чтобы создать эту базу данных, просто введите:

createdb

Если вы больше не хотите использовать свою базу данных, вы можете удалить ее. Например, если вы являетесь владельцем (создателем) базы данных mydb, вы можете уничтожить ее, используя следующую команду:

dropdb mydb

(Для этой команды имя базы данных по умолчанию не совпадает с именем учетной записи пользователя. Вы всегда должны указывать его.) Это действие физически удаляет все файлы, связанные с базой данных, и не может быть отменено, так что это должно быть сделано только с большим количеством предусмотрительности.

Больше информации по командам createdb и dropdb можно найти в соответствующих разделах.

¹

В качестве объяснения того, почему это работает: имена пользователей QHB отделены от учетных записей пользователей операционной системы. Когда вы подключаетесь к базе данных, вы можете выбрать, какое имя пользователя QHB будет подключаться; если вы этого не сделаете, по умолчанию будет использоваться то же имя, что и ваша текущая учетная запись операционной системы. Как это бывает, всегда будет учетная запись пользователя QHB, имя которой совпадает с именем пользователя операционной системы, запустившего сервер, и также бывает, что этот пользователь всегда имеет разрешение на создание баз данных. Вместо входа в систему под этим пользователем вы также можете указать опцию -U везде, чтобы выбрать имя пользователя QHB для подключения.

Доступ к базе данных

Создав базу данных, вы можете получить к ней доступ одним из следующих способов:

С помощью интерактивного терминала QHB, называемого psql, который позволяет в интерактивном режиме вводить, редактировать и выполнять команды SQL.

С использованием существующего графического инструмента внешнего интерфейса, например, pgAdmin, DBeaver или офисного пакета с поддержкой ODBC или JDBC. Эти возможности не рассматриваются в этом руководстве.

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

Возможно, вы захотите запустить psql, чтобы попробовать примеры из этого урока. Его можно запустить для базы данных mydb, набрав команду:

$ psql -h localhost -d mydb

Если вы не укажете имя базы данных, по умолчанию будет использоваться имя вашей учетной записи. Мы уже сталкивались с этой схемой в предыдущем разделе, при работе с createdb.

Внутри psql вы увидите следующее сообщение:

psql - Interactive terminal qhb (1.2.0)
Enter "\help" in order to get help

mydb(username)=#

Это будет означать, что вы являетесь суперпользователем базы данных, что наиболее вероятно, если вы установили экземпляр QHB самостоятельно. Суперпользователь имеет неограниченный доступ к базе данных; впрочем, для целей урока это не имеет значения.

Если вы столкнулись с проблемами при запуске psql, вернитесь к предыдущему разделу. Диагностика createdb и psql схожи, и если первый сработал, последний также должен сработать.

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

mydb(username)=# select version();
version
----------
QHB 1.2.0
(1 строка)

mydb(username)=# SELECT current_date;
    date
------------
 2020-10-07

mydb(username)=# SELECT 2 + 2;
 ?column?
----------
4
(1 строка)

Программа psql имеет ряд внутренних команд, которые не являются командами SQL. Они начинаются с символа обратной косой черты "\". Например, чтобы выйти из psql, введите:

mydb(username)=# \q

и psql завершит работу и вернёт вас в командную оболочку.

Все возможности psql описаны в соответствующем разделе: psql.

Настройка и эксплуатация сервера

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

Учетная запись пользователя

Как и любой серверный процесс, доступный для внешнего мира, рекомендуется запускать QHB под отдельной учетной записью пользователя. Эта учетная запись пользователя должна владеть только данными, которыми управляет сервер, и не должна использоваться совместно с другими демонами. (Например, использование пользователя nobody является плохой идеей). Не рекомендуется устанавливать исполняемые файлы, принадлежащие этому пользователю, потому что скомпрометированные системы могут затем изменить свои собственные двоичные файлы.

Чтобы добавить учетную запись пользователя Unix в вашу систему, используйте команду useradd или adduser. Имя пользователя qhb часто используется, и предполагается в этой книге, но возможно использовать другое имя, если необходимо.

Создание кластера базы данных

Прежде чем что-либо делать, вы должны инициализировать область хранения базы данных на диске. В документации это называется кластером базы данных. (В стандарте SQL используется термин кластер каталога). Кластер баз данных — это набор баз данных, который управляется одним экземпляром работающего сервера баз данных. После инициализации кластер базы данных будет содержать базу данных с именем qhb, которая является базой данных по умолчанию для использования утилитами, пользователями и сторонними приложениями. Сам сервер базы данных не требует существования базы данных qhb, но многие внешние утилиты предполагают, что она существует. Другая база данных, созданная в каждом кластере во время инициализации, называется template1. Как следует из названия, она будет использоваться в качестве шаблона для впоследствии созданных баз данных и не должна использоваться для фактической работы. (Информацию о создании новых баз данных в кластере см. в главе Управление базами данных).

С точки зрения файловой системы, кластер базы данных - это один каталог, в котором будут храниться все данные. Он называется каталогом данных или областью данных. Где хранить свои данные - полностью зависит от пользователя. Каких-либо жёстких установок не существует, хотя популярны такие места, как /var/lib/qhb/data или /u01/qhb/data. Чтобы инициализировать кластер базы данных, используйте команду qhb_bootstrap, которая устанавливается с QHB. Расположение файловой системы вашего кластера баз данных указывается с помощью опции -D, например:

qhb_bootstrap -D /var/lib/qhb/data

Обратите внимание, что вы должны выполнить эту команду при входе в учетную запись пользователя, которая описана в предыдущем разделе.

Заметка
В качестве альтернативы опции -D вы можете установить переменную окружения PGDATA..

qhb_bootstrap попытается создать указанную вами директорию, если она еще не существует. Конечно, это не удастся, если у qhb_bootstrap нет прав на запись в родительский каталог. Обычно рекомендуется, чтобы пользователь QHB владел не только каталогом данных, но и своим родительским каталогом, чтобы это не было проблемой. Если желаемый родительский каталог тоже не существует, вам сначала нужно его создать, используя привилегии суперпользователя, если каталог прародителя недоступен для записи. Так что процесс может выглядеть так:

root# mkdir /usr/local/qhb
root# chown qhb /usr/local/qhb
root# su qhb
qhb$ qhb_bootstrap -D /var/lib/qhb/data

Если работаем от root используем:

root# mkdir /var/lib/qhb/data
root# chown qhb /var/lib/qhb/data
root# sudo -u qhb /usr/local/qhb/bin/qhb_bootstrap -D /var/lib/qhb/data

qhb_bootstrap откажется запускаться, если каталог данных существует и уже содержит файлы, что предотвратит случайную перезапись существующей установки.

Поскольку каталог данных содержит все данные, хранящиеся в базе данных, важно, чтобы он был защищен от несанкционированного доступа. Поэтому qhb_bootstrap отзывает права доступа у всех, кроме пользователя QHB и, при необходимости, группы. Групповой доступ, если он включен, доступен только для чтения. Это позволяет непривилегированному пользователю в той же группе, что и владелец кластера, создавать резервную копию данных кластера или выполнять другие операции, для которых требуется только чтение.

Обратите внимание, что включение или выключение группового доступа в существующем кластере требует выключения кластера и установки соответствующего режима для всех каталогов и файлов перед перезапуском QHB. В противном случае в каталоге данных может существовать комбинация режимов. Для кластеров, которые разрешают доступ только владельцу, соответствующие режимы - 0700 для каталогов и 0600 для файлов. Для кластеров, которые также разрешают чтение по группе, соответствующие режимы - 0750 для каталогов и 0640 для файлов.

Однако, хотя содержимое каталога является безопасным, настройка аутентификации клиента по умолчанию позволяет любому локальному пользователю подключаться к базе данных и даже стать суперпользователем базы данных. Если вы не доверяете другим локальным пользователям, мы рекомендуем использовать один из параметров qhb_bootstrap -W, --pwprompt или --pwfile, чтобы назначить пароль суперпользователю базы данных. Кроме того, укажите пароль -A md5 или -A password чтобы режим проверки подлинности по умолчанию не использовался; или измените созданный файл qhb_hba.conf после запуска qhb_bootstrap, но перед первым запуском сервера. Другие разумные подходы включают использование peer аутентификации или разрешений файловой системы для ограничения соединений.

qhb_bootstrap также инициализирует локаль по умолчанию для кластера базы данных. Обычно берет используются параметры локали из среды и применяет их к инициализированной базе данных. Можно указать другую локаль для базы данных - более подробную информацию об этом можно найти в разделе Поддержка языковых стандартов. Порядок сортировки по умолчанию, используемый в конкретном кластере баз данных, устанавливается qhb_bootstrap, и, хотя вы можете создавать новые базы данных, используя другой порядок сортировки, порядок, используемый в базах данных шаблонов, которые создает qhb_bootstrap, нельзя изменить без их удаления и повторного создания. Существует также влияние на производительность при использовании локалей, отличных от C(RUST) или POSIX. Поэтому важно правильно сделать этот выбор с первого раза.

qhb_bootstrap также устанавливает кодировку набора символов по умолчанию для кластера базы данных. Обычно это следует выбирать в соответствии с настройками локали. Подробнее см. раздел Поддержка кодировок.

Языки, отличные от C\RUST полагаются на библиотеку сортировки операционной системы для упорядочения набора символов. Это контролирует порядок ключей, хранящийся в индексах. По этой причине кластер не может переключиться на несовместимую версию библиотеки сортировки, используя восстановление моментальных снимков, двоичную потоковую репликацию, другую операционную систему или обновление операционной системы.

Использование вторичных файловых систем

Многие установки создают свои кластеры базы данных в файловых системах (томах), отличных от «корневого» тома. Если вы решите сделать это, не рекомендуется пытаться использовать самый верхний каталог тома (точку монтирования) в качестве каталога для хранения данных. Рекомендуется создать каталог в каталоге для монтирования, который принадлежит пользователю QHB, а затем создать в нем каталог данных. Это позволяет избежать проблем с разрешениями, особенно для таких операций, как qhb_upgrade, а также гарантирует понятные сообщения об ошибках, если том отключен.

Файловые Системы

Как правило, любая файловая система с семантикой POSIX может использоваться для QHB. Пользователи предпочитают разные файловые системы по разным причинам, включая поддержку поставщиков, производительность и личную экспертизу. Опыт показывает, что при прочих равных условиях не следует ожидать значительных изменений в производительности или в поведении просто от переключения файловых систем или незначительных изменений конфигурации файловой системы.

NFS

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

Единственное жесткое требование для использования NFS с QHB - это монтирование файловой системы с использованием опции hard. С помощью опции hard процессы могут «зависать» на неопределенное время, если возникают проблемы с сетью, поэтому такая конфигурация потребует тщательной настройки мониторинга. Опция soft будет прерывать системные вызовы в случае сетевых проблем, но QHB не будет повторять системные вызовы, прерванные таким образом, поэтому любое такое прерывание приведет к сообщению об ошибке ввода-вывода.

Нет необходимости использовать опцию sync. Поведение опции async является достаточным, поскольку QHB делает вызовы fsync в нужное время для очистки кэшей записи, аналогично тому, как это происходит в локальной файловой системе. Однако настоятельно рекомендуется использовать параметр экспорта sync на сервере NFS в системах, где он существует. В противном случае fsync или его эквивалент на клиенте NFS не гарантируют того, что данные достигнут постоянного хранилища на сервере, что может привести к повреждению, аналогично тому которое может случится на сервере с выключенным параметром fsync. Значения по умолчанию этих параметров монтирования и экспорта различаются у разных поставщиков и версий, поэтому рекомендуется в любом случае проверить и, возможно, указать их явно, чтобы избежать двусмысленности.

В некоторых случаях к внешнему хранилищу можно получить доступ через NFS или протокол более низкого уровня, например iSCSI. В последнем случае хранилище выглядит как блочное устройство, и на нем может быть создана любая поддерживаемая файловая система. Такой подход может избавить администратора базы данных от необходимости иметь дело с некоторыми особенностями NFS, но, конечно, сложность управления удаленным хранилищем возникает на других уровнях.

Запуск сервера базы данных

Прежде чем кто-либо сможет получить доступ к базе данных, вы должны запустить сервер базы данных. Программа сервера базы данных называется qhb. Программа qhb должна знать, где найти файлы базы данных, которые она должна использовать. Это делается с помощью опции -D. Таким образом, самый простой способ запустить сервер:

qhb# qhb -D /var/lib/qhb/data

root# sudo -u qhb ./qhb -D /var/lib/qhb/data

эта команда оставит сервер на консоли. Она должна выполнятся из учётной записи пользователя qhb. Без -D сервер попытается использовать каталог данных, названный переменной окружения PGDATA. Если эта переменная также не указана, произойдет сбой.

Обычно лучше запускать qhb в фоновом режиме. Для этого используйте обычный синтаксис оболочки (shell) Unix:

$ qhb -D /var/lib/qhb/data >logfile 2>&1 &

Важно где-то хранить выходные данные сервера stdout и stderr, как показано выше для целей аудита и диагностики проблем. (См. раздел Обслуживание файла журнала для более подробного обсуждения обработки файла журнала).

Программа qhb также принимает ряд других параметров командной строки. Для получения дополнительной информации см. Справочную страницу qhb и главу Конфигурация сервера.

Этот синтаксис трудно запомнить и повторить. Поэтому программа-обертка qhb_ctl предоставляется для упрощения некоторых задач. Например:

qhb_ctl start -l logfile

запустит сервер в фоновом режиме и поместит вывод в именованный файл журнала. Параметр -D имеет то же значение, что и для qhb. qhb_ctl также может останавливать сервер.

Обычно вы хотите запустить сервер базы данных при загрузке компьютера. Сценарии автозапуска зависят от операционной системы. Некоторые из них распространяются с пакетами QHB.

Различные системы имеют разные соглашения для запуска демонов во время загрузки. Многие системы имеют файл /etc/rc.local или /etc/rc.d/rc.local. Другие используют каталоги init.d или rc.d Что бы вы ни делали, сервер должен работать под учетной записью пользователя QHB, а не под учетной записью root или любого другого пользователя. Поэтому вы, вероятно, должны формировать свои команды, используя su qhb -c '...'. Например:

su qhb -c 'qhb_ctl start -D /var/lib/qhb/data -l serverlog'

Вот еще несколько предложений для запуска :

добавьте в /etc/rc.d/rc.local или /etc/rc.local

/usr/local/qhb/bin/qhb_ctl start -l logfile -D /var/lib/qhb/data

или используйте файл contrib/start-scripts/linux в исходном дистрибутиве QHB.

при использовании systemd вы можете использовать следующий файл для systemd (например, в /etc/systemd/system/qhb.service):

[Unit]
Description=QHB database server
Documentation=man:qhb(1)

[Service]
Type=notify
User=qhb
ExecStart=/usr/local/qhb/bin/qhb -D /var/lib/qhb/data
ExecReload=/bin/kill -HUP $MAINPID
KillMode=mixed
KillSignal=SIGINT
TimeoutSec=90

[Install]
WantedBy=multi-user.target

Тщательно продумайте настройку тайм-аута. На настоящий момент системный тайм-аут по умолчанию составляет 90 секунд и завершит процесс, который не уведомит о готовности в течение этого времени. Но серверу QHB, который может выполнить восстановление после сбоя при запуске, может потребоваться гораздо больше времени, чтобы подготовиться. Предлагаемое значение 0 выключает логику тайм-аута.

Во время работы сервера его PID хранится в файле qhbmaster.pid в каталоге данных. Это используется для предотвращения запуска нескольких экземпляров сервера в одном каталоге данных, а также может использоваться для выключения сервера.

Сбои при запуске сервера

Существует несколько распространенных причин, по которым сервер может не запуститься. Проверьте файл журнала сервера или запустите его вручную (без перенаправления стандартного вывода или стандартной ошибки) и посмотрите, какие сообщения об ошибках появляются. Ниже мы объясним некоторые из наиболее распространенных сообщений об ошибках более подробно.

LOG:  could not bind IPv4 address "127.0.0.1": Address already in use
HINT:  Is another postmaster already running on port 5432? If not, wait a few seconds and retry.
FATAL:  could not create any TCP/IP sockets

Обычно это означает только то, что вы попытались запустить другой сервер на том же порту, на котором он уже запущен. Однако, если сообщение об ошибке в ядре не является Address already in use или каким-либо другим вариантом, это может быть другой проблемой. Например, попытка запустить сервер с зарезервированным номером порта может выдать что-то вроде:

$ qhb -p 777
LOG:  could not bind IPv4 address "127.0.0.1": Permission denied
HINT:  Is another postmaster already running on port 777? If not, wait a few seconds and retry.
FATAL:  could not create any TCP/IP sockets

Сообщение :

FATAL:  could not create shared memory segment: Invalid argument
DETAIL:  Failed system call was shmget(key=5440001, size=4011376640, 03600).

вероятно, означает, что ограничение вашего ядра на размер общей памяти меньше, чем рабочая область, которую пытается создать QHB (4011376640 байт в этом примере). Или это может означать, что в вашем ядре вообще не настроена поддержка общей памяти System V. В качестве временного решения можно попробовать запустить сервер с меньшим, чем обычно, числом буферов ( shared_buffers). В конечном итоге необходимо перенастроить ядро ОС, чтобы увеличить допустимый размер общей памяти. Это сообщение также может появиться при попытке запустить несколько серверов на одном компьютере, если их общее запрошенное пространство превышает ограничение ядра.

Ошибка :

FATAL:  could not create semaphores: No space left on device
DETAIL:  Failed system call was semget(5440126, 17, 03600).

не означает, что вам не хватает места на диске. Это означает, что ограничение вашего ядра на число семафоров System V меньше, чем число, которое QHB хочет создать. Как и в случае выше, можно обойти эту проблему, запустив сервер с меньшим количеством разрешенных соединений ( max_connections), но в конечном итоге необходимо увеличить ограничение ядра.

Если вы получили ошибку «illegal system call», вполне вероятно, что общая память или семафоры вообще не поддерживаются в вашем ядре. В этом случае единственный вариант - перенастроить ядро, чтобы включить эти функции.

Проблемы с клиентским подключением

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

psql: could not connect to server: Connection refused
        Is the server running on host "server.joe.com" and accepting
        TCP/IP connections on port 5432?

Это общая ошибка «Я не могу найти сервер для связи». Выглядит как ошибка при попытке установить связь TCP/IP. Распространенная ошибка - забыть включить настройки сервера для разрешения соединений по TCP/IP.

В качестве альтернативы, при попытке соединения с локальным сервером с использованием доменных сокетов Unix вы получите:

psql: could not connect to server: No such file or directory
        Is the server running locally and accepting
        connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

Последняя строка полезна для проверки того, что клиент пытается подключиться в нужное место. Если на самом деле не работает ни один сервер, сообщение об ошибке в ядре обычно будет либо « onnection refused либо No such file or directory, важно понимать, что в этом контексте Connection refused в соединении, не означает, что сервер получил ваш запрос на соединение и отклонил его. В этом случае будет выдано другое сообщение, которое указывают на более фундаментальные проблемы, такие как отсутствие сетевого подключения.

Завершение работы сервера

Есть несколько способов выключить сервер базы данных. Вы управляете типом выключения, посылая различные сигналы основному процессу qhb.

SIGTERM Это режим Smart Shutdown. После получения сигнала SIGTERM сервер запрещает новые подключения, но позволяет существующим сеансам нормально завершать свою работу. Сервер отключается только после завершения всех сеансов. Если сервер находится в режиме горячего резервного копирования, он дополнительно ожидает, пока режим резервного копирования в перестанет быть активным. При активном режиме резервного копирования новые подключения будут по-прежнему разрешены, но только для суперпользователей (это исключение позволяет суперпользователю подключаться для завершения горячего копирования). Если сервер находится в состоянии восстановления, когда запрашивается интеллектуальное отключение, восстановление и потоковая репликация будут остановлены только после завершения всех обычных сеансов.

SIGINT Это режим быстрого отключения. Сервер запрещает новые подключения и отправляет всем существующим серверным процессам сигнал SIGTERM, что заставит их прервать свои текущие транзакции и быстро завершить работу. Затем он ожидает завершения всех серверных процессов и, наконец, сам завершает работу. Если сервер находится в режиме горячего резервного копирования, режим резервного копирования будет прерван, что сделает резервную копированию бесполезной.

SIGQUIT Это режим немедленного отключения. Сервер отправит сигнал SIGQUIT всем дочерним процессам и будет ждать их завершения. Если они не прекратятся в течение 5 секунд, им будут отправлены сигналы SIGKILL. Главный процесс сервера завершается сразу же после завершения всех дочерних процессов без выполнения обычной обработки завершения работы базы данных. Это приведет к восстановлению (путем воспроизведения журнала WAL) при следующем запуске. Рекомендуется только в чрезвычайных ситуациях.

Программа qhb_ctl предоставляет удобный интерфейс для отправки этих сигналов для завершение работы сервера. Кроме того, вы можете отправить сигнал напрямую, используя kill. PID процесса qhb можно найти с помощью программы ps или из файла qhbmaster.pid в каталоге данных. Например, чтобы сделать быстрое отключение:

$ kill -INT `head -1 /var/lib/qhb/data/qhbmaster.pid`

Важно!!!
Лучше не использовать SIGKILL для выключения сервера так как в этом случае не будет выполнено освобождение сервером общей памяти и семафоров. Кроме того, SIGKILL убивает процесс qhb не позволяя ему передавать сигнал своим подпроцессам, поэтому может потребоваться также уничтожить отдельные подпроцессы вручную.

Чтобы завершить отдельный сеанс, одновременно продолжая другие сеансы, используйте pg_terminate_backend() (см. Таблицу ) или отправьте сигнал SIGTERM дочернему процессу, связанному с сеансом.

Конфигурация сервера

Существует много параметров конфигурации, которые влияют на поведение системы базы данных. В первом разделе этой главы мы расскажем, как взаимодействовать с этими параметрами. В последующих разделах подробно рассматривается каждый параметр.

Настройка параметров

Имена и значения параметров

Имена всех параметров не чувствительны к регистру. Каждый параметр принимает значение одного из пяти типов: логический, строка, целое число, число с плавающей запятой или перечисление. От типа зависит синтаксис, применяемый для установки этого параметра:

Логический: Значения могут вводиться как on, off, true, false, yes, no, 1, 0 (все без учета регистра) или любым однозначным префиксом одного из них.

Строка: Обычно значение заключается в апострофы, при этом внутренние апострофы значения дублируются. Однако если значение является простым числом или идентификатором, апострофы обычно можно опустить. (Значения, совпадающие с каким-либо ключевым словом SQL, в некоторых контекстах все же требуют заключения в апострофы.)

Число (целое или с плавающей запятой): Числовые параметры можно указывать в обычных форматах, принятых для целого числа и числа с плавающей запятой; если параметр имеет целочисленный тип, дробные значения округляются до ближайшего целого числа. Кроме того, целочисленные параметры дополнительно принимают шестнадцатеричные (с префиксом 0x) и восьмеричные (с префиксом 0) значения, но эти форматы не могут быть дробными. Разделители разрядов использовать нельзя. Кавычки требуются только для шестнадцатеричного значения.

Число с единицей измерения: Некоторые числовые параметры имеют неявную единицу измерения, поскольку описывают количество памяти или времени. Этой единицей измерения могут быть байты, килобайты, блоки (обычно восемь килобайтов), миллисекунды, секунды или минуты. При указании только числового значения для одного из таких параметров будет использована единица измерения, установленная для него по умолчанию, которую можно узнать из pg_settings.unit. Для удобства параметры можно задать, явно указав единицу измерения, например, ’120 ms’ для значения времени, которая будет преобразована в фактическую единицу измерения параметра. Обратите внимание, что для применения этой функции значение следует записывать в виде строки (с апострофами). Название единицы измерения чувствительно к регистру, а между ним и числовым значением можно ставить пробел.

Допустимые единицы памяти: B (байты), kB (килобайты), MB (мегабайты), GB (гигабайты) и TB (терабайты). Множитель для единиц памяти равен 1024, а не 1000.

Допустимые единицы времени: us (микросекунды), ms (миллисекунды), s (секунды), min (минуты), h (часы) и d (дни).

Если с единицей измерения задается дробное значение, оно будет округлено до кратного следующей меньшей единицы, если таковая имеется. Например, 30.1 GB будут преобразованы в 30822 MB, а не в 32319628902 B. Если параметр имеет целочисленный тип, после любого преобразования единицы измерения значение окончательно округляется до целого.

Перечисление: Параметры перечисляемого типа задаются так же, как и строковые параметры, но имеют ограниченный набор значений. Допустимые значения для такого параметра можно найти в pg_settings.enumvals. Значения перечислимых параметров не чувствительны к регистру.

Взаимодействие с параметрами через файл конфигурации

Самый основной способ установить эти параметры — отредактировать файл qhb.conf, который обычно хранится в каталоге данных. Копия по умолчанию устанавливается при инициализации каталога кластера базы данных. Пример того, как может выглядеть этот файл:

# Это комментарий
log_connections = yes
log_destination = 'syslog'
search_path = '"$user", public'
shared_buffers = 128MB

В каждой строке указывается один параметр. Знак равенства между именем и значением необязателен. Пробелы не играют роли (за исключением значения параметра, заключенного в апострофы), а пустые строки игнорируются. Знаки решетки (#) обозначают оставшуюся часть строки как комментарий. Значения параметров, которые не являются простыми идентификаторами или числами, должны быть заключены в апострофы. Чтобы вставить апостроф в само значение параметра, удвойте его (желательно) или добавьте перед ним обратный слэш. Если файл содержит несколько определений одного и того же параметра, то все они, кроме последнего, игнорируются.

Параметры, установленные таким образом, предоставляют значения по умолчанию для данного кластера. Эти значения будут настройками, видимыми в активных сеансах, если не будут переопределены. В следующих разделах описываются способы, которыми администратор или пользователь могут переопределить эти значения по умолчанию.

Файл конфигурации перечитывается всякий раз, когда основной процесс сервера получает сигнал SIGHUP; этот сигнал легче всего отправить, запустив qhb_ctl reload в командной строке или вызвав функцию SQL pg_reload_conf(). Основной процесс сервера передает этот сигнал всем запущенным в данный момент процессам сервера, так что существующие сеансы тоже принимают новые значения (это произойдет после того, как они завершат любую выполняемую в данный момент клиентскую команду). Кроме того, этот сигнал можно отправить напрямую одному из серверных процессов. Некоторые параметры можно установить только при запуске сервера; любые изменения их значений в файле конфигурации будут игнорироваться до перезапуска сервера. При обработке SIGHUP также игнорируются (но регистрируются) неверные значения параметров в файле конфигурации.

В дополнение к qhb.conf каталог данных QHB содержит файл qhb.auto.conf, который имеет тот же формат, что и qhb.conf, но предназначен для редактирования автоматически, а не вручную. Этот файл содержит параметры, настраиваемые командой ALTER SYSTEM. Он считывается одновременно с qhb.conf, и заданные в нем параметры действуют аналогичным образом. Параметры в qhb.auto.conf переопределяют параметры в qhb.conf.

Внешние средства также могут изменять qhb.auto.conf. Не рекомендуется делать это во время работы сервера, поскольку параллельное выполнение команды ALTER SYSTEM может перезаписать такие изменения. Такие программы могут просто добавлять новые настройки параметров в конец файла или удалять дубликаты определений и/или комментарии (как делает ALTER SYSTEM).

Системное представление pg_file_settings может быть полезно для предварительной проверки изменений в файлах конфигурации или для диагностики проблем, если сигнал SIGHUP не дает желаемых результатов.

Взаимодействие с параметрами через SQL

QHB предоставляет три команды SQL для установки параметров конфигурации по умолчанию. Уже упомянутая команда ALTER SYSTEM предоставляет средства изменения глобальных значений по умолчанию, доступные для SQL; она функционально равнозначна редактированию qhb.conf. Кроме того, есть еще две команды, которые позволяют устанавливать значения по умолчанию на уровне базы данных или роли:

Команда ALTER DATABASE позволяет переопределять глобальные параметры на уровне базы данных.

Команда ALTER ROLE позволяет пользователю переопределять как глобальные параметры, так и параметры на уровне базы данных.

Значения, установленные с помощью команд ALTER DATABASE и ALTER ROLE, применяются только при запуске нового сеанса базы данных. Они переопределяют значения, полученные из файлов конфигурации или командной строки сервера, и назначаются значениями по умолчанию до конца сеанса. Обратите внимание, что некоторые параметры нельзя изменить после запуска сервера, и поэтому их невозможно установить с помощью этих или перечисленных ниже команд.

После подключения клиента к базе данных он может воспользоваться двумя дополнительными командами SQL (и равнозначными им функциями), предоставляемыми QHB для взаимодействия с локальными параметрами конфигурации сеанса:

Команда SHOW позволяет проверить текущее значение всех параметров. Соответствующая функция SQL — current_setting(setting_name text) (см. подраздел Функции настройки конфигурации).

Команда SET позволяет изменять текущее значение тех параметров, которые могут быть установлены локально для сеанса; это не влияет на другие сеансы. Соответствующая функция SQL — set_config(setting_name, new_value, is_local) (см. подраздел Функции настройки конфигурации).

Кроме того, для просмотра и изменения локальных значений сеанса можно воспользоваться системным представлением pg_settings:

Запрос этого представления аналогичен использованию команды SHOW ALL, но выдает более подробную информацию. Этот подход также более гибкий, так как позволяет задавать условия фильтрации или объединять результат с другими отношениями.

Использование UPDATE в этом представлении, в частности, обновление столбца setting, эквивалентно выполнению команд SET. Например, команде

SET configuration_parameter TO DEFAULT;

равнозначен запрос:

UPDATE pg_settings SET setting = reset_val WHERE name = 'configuration_parameter';

Взаимодействие с параметрами через командную оболочку

Помимо установки глобальных значений по умолчанию или добавления переопределений на уровне базы данных или роли, параметры QHB можно задавать с помощью средств оболочки. Через оболочку значения параметров принимают и сервер, и клиентская библиотека libpq.

Во время запуска сервера значения параметров можно передать команде qhb посредством параметра командной строки -c. Например:

qhb -c log_connections=yes -c log_destination='syslog'

Параметры, предоставленные таким образом, переопределяют те, что были заданы в qhb.conf или командой ALTER SYSTEM, поэтому их нельзя изменить глобально без перезапуска сервера.

При запуске клиентского сеанса через libpq настройку параметров можно провести с помощью переменной среды PGOPTIONS. Значения, установленные таким образом, назначаются значениями по умолчанию до конца сеанса, но не влияют на другие сеансы. По историческим причинам формат PGOPTIONS похож на тот, что используется при запуске команды qhb; в частности, следует указывать флаг -c. Например,

env PGOPTIONS="-c geqo=off -c statement_timeout=5min" psql

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

Управление содержимым файла конфигурации

QHB предоставляет несколько функций для разбиения сложных файлов qhb.conf на вложенные файлы. Эти функции особенно полезны при управлении несколькими серверами со связанными, но не идентичными конфигурациями.

Помимо значений отдельных параметров файл qhb.conf может содержать директивы включения, благодаря которым другой файл будет прочитан и обработан, как если бы он был вставлен в данное место файла конфигурации. Эта особенность позволяет разделить файл конфигурации на физически отдельные части. Директивы включения записываются просто:

include ’имя_файла’

Если имя файла не является абсолютным путем, оно рассматривается относительно каталога, содержащего включающий файл конфигурации. Включения файлов могут быть вложенными.

Существует также директива include_if_exists, которая действует так же, как include, за исключением случаев, когда указанный файл не существует или не может быть прочитан. Обычная директива include будет считать это условием возникновения ошибки, но include_if_exists просто регистрирует сообщение и продолжает обрабатывать включающий файл конфигурации.

Файл qhb.conf также может содержать директивы include_dir, которые указывают весь каталог файлов конфигурации, который нужно включить. Они записываются так:

include_dir 'каталог'

Неабсолютные имена каталогов берутся относительно каталога, содержащего включающий файл конфигурации. В указанном каталоге будут включены только файлы, не являющиеся каталогами, с именами, заканчивающимися суффиксом .conf. Имена файлов, начинающиеся с символа «.» также игнорируется во избежание ошибок, поскольку такие файлы являются скрытыми на некоторых платформах. Множество файлов во включаемом каталоге обрабатывается в порядке их имен (в соответствии с правилами языка C, т. е. цифры идут перед буквами, а заглавные буквы — перед строчными).

Включение файлов или каталогов можно использовать для логического разделения конфигурации базы данных на части, вместо того чтобы вести один большой файл qhb.conf. Рассмотрим компанию с двумя серверами баз данных, каждый с разным объемом памяти. Скорее всего, у их конфигураций есть общие элементы, которые будут использоваться, например, для ведения журнала. Но параметры, связанные с памятью, у них будут различаться. Также у каждого сервера могут быть и специфические параметры. Один из способов справиться с этой ситуацией — разбить изменения стандартной конфигурации вашей сети на три файла. Чтобы включить их, можно добавить в конец файла qhb.conf следующую запись:

include 'shared.conf'
include 'memory.conf'
include 'server.conf'

Все системы будут иметь одинаковый shared.conf. Все серверы с определенным объемом памяти могут использовать общий файл memory.conf; один файл для всех серверов с 8 ГБ ОЗУ, другой для серверов с 16 ГБ. И, наконец, server.conf может содержать действительно специфические для сервера параметры конфигурации.

Другая возможность — создать каталог с файлами конфигурации и поместить эту информацию в файлы. Например, можно указать каталог conf.d в конце qhb.conf:

include_dir 'conf.d'

Затем можно назвать файлы в каталоге conf.d следующим образом:

00shared.conf
01memory.conf
02server.conf

Это соглашение об именах устанавливает четкий порядок загрузки этих файлов. Это важно, потому что будет использоваться только последнее значение параметра, найденное сервером при чтении файлов конфигурации. В данном примере значение, установленное в conf.d/02server.conf, переопределит значение, заданное в .d/01memory.conf.

Вместо этого можно использовать этот подход для описательного именования файлов:

00shared.conf
01memory-8GB.conf
02server-foo.conf

Такая расстановка дает уникальное имя для каждого варианта файла конфигурации. Это может помочь устранить неоднозначность, когда конфигурации нескольких серверов хранятся в одном месте, например в репозитории системы управления версиями. (Хранение файлов конфигурации базы данных в системе управления версиями — это еще одна полезная практика, которую стоит рассмотреть.)

Расположение файлов

В дополнение к уже упомянутому файлу qhb.conf QHB использует еще два редактируемых вручную файла конфигурации, которые управляют аутентификацией клиента. По умолчанию все три файла конфигурации хранятся в каталоге данных кластера баз данных. Параметры, описанные в этом разделе, позволяют размещать файлы конфигурации в другом месте. (Это может упростить управление. В частности, при хранении файлов конфигурации отдельно зачастую проще обеспечить их правильное резервное копирование.)

data_directory (string)

Задает каталог, в котором будут храниться данные. Этот параметр можно установить только при запуске сервера.

config_file (string)

Задает основной файл конфигурации сервера (обычно он называется qhb.conf). Этот параметр можно установить только в командной строке QHB.

hba_file (string)

Задает файл конфигурации для проверки подлинности по узлу (обычно он называется qhb_hba.conf). Этот параметр можно установить только при запуске сервера.

ident_file (string)

Задает файл конфигурации для сопоставлений имен пользователей (обычно он называется qhb_ident.conf). Этот параметр можно задать только при запуске сервера.

external_pid_file (string)

Задает имя дополнительного файла с идентификатором процесса (PID), который сервер должен создать для использования программами администрирования сервера. Этот параметр можно установить только при запуске сервера.

При установке по умолчанию ни один из вышеперечисленных параметров не устанавливается явно. Вместо этого каталог данных указывается параметром командной строки -D или переменной среды PGDATA, и все файлы конфигурации находятся в каталоге данных.

Если требуется сохранить файлы конфигурации не в каталоге данных, параметр командной строки QHB -D или переменная среды PGDATA должны указывать на каталог, содержащий файлы конфигурации, а в qhb.conf (или в командной строке) должен быть задан параметр data_directory, чтобы показать, где на самом деле находится каталог данных. Обратите внимание, что data_directory переопределяет путь, заданный в -D и PGDATA как расположение каталога данных, но не расположение файлов конфигурации.

При желании можно задать имена файлов конфигурации и их пути по отдельности, используя параметры config_file, hba_file и/или ident_file. config_file можно указывать только в командной строке QHB, но остальные параметры можно задать в основном файле конфигурации. Если все три параметра и data_directory установлены явно, указывать -D или PGDATA необязательно.

При установке любого из этих параметров относительный путь будет рассматриваться от каталога, в котором запущен QHB.

Подключения и аутентификация

Параметры подключения

listen_addresses (string)

Задает адреса TCP/IP, по которым сервер должен принимать подключения от клиентских приложений. Значение имеет форму разделенного запятыми списка имен хостов и/или числовых IP-адресов. Специальная запись * соответствует всем доступным IP-интерфейсам. Запись 0.0.0.0 позволяет прослушивать все адреса IPv4, а :: — все адреса IPv6. Если список пуст, сервер вообще не прослушивает ни один IP-интерфейс, и в этом случае для подключения к нему можно использовать только сокеты домена Unix. Значением по умолчанию является localhost, что позволяет устанавливать только локальные «петлевые» подключения по TCP/IP. Хотя аутентификация клиента позволяет скрупулезно управлять доступом к серверу, listen_addresses определяет, через какие именно интерфейсы будут приниматься соединения, что может помочь предотвратить злонамеренные попытки подключения через незащищенные сетевые интерфейсы. Этот параметр можно задать только при запуске сервера.

port (integer)

TCP-порт, который прослушивает сервер; по умолчанию это 5432. Обратите внимание, что этот номер порта используется для всех IP-адресов, через которые сервер принимает подключения. Этот параметр можно задать только при запуске сервера.

max_connections (integer)

Определяет максимальное количество одновременных подключений к серверу базы данных. По умолчанию обычно это 100 подключений, но может быть и меньше, если настройки ядра не будут это поддерживать (что определяется в процессе initdb). Этот параметр можно задать только при запуске сервера.

При запуске резервного сервера следует установить для этого параметра значение большее или равное значению на главном сервере. В противном случае на резервном сервере не будут разрешены запросы.

superuser_reserved_connections (integer)

Определяет количество «слотов» подключений, которые зарезервированы для суперпользователей QHB. В большинстве случаев подключения max_connections могут быть активными одновременно. Всякий раз, когда число активных одновременных подключений больше или равно max_connections минус superuser_reserved_connections, новые подключения будут приниматься только от суперпользователей, а подключения для репликации приниматься не будут.

По умолчанию резервируется три соединения. Это значение должно быть меньше значения max_connections. Этот параметр можно задать только при запуске сервера.

unix_socket_directories (string)

Задает каталог сокетов Unix-домена, через которые сервер должен принимать подключения от клиентских приложений. Можно создать несколько сокетов, перечислив несколько каталогов, разделенных запятыми. Пробелы между записями игнорируются; если в имени каталога требуются пробелы или запятые, заключите его в кавычки. Пустое значение указывает, что сервер не принимает подключения через сокеты домена Unix, и в этом случае к нему можно подключиться только по TCP/IP. Значением по умолчанию обычно является /tmp, но его можно изменить во время сборки. Этот параметр можно задать только при запуске сервера.

В дополнение к самому файлу сокета, который называется .s.PGSQL.nnnn, где nnnn — это номер порта сервера, в каждом из каталогов unix_socket_directories будет создан обычный файл с именем .s.PGSQL.nnnn.lock. Эти файлы ни в коем случае нельзя удалять вручную.

unix_socket_group (string)

Задает группу-владельца сокетов Unix-домена. (Пользователем-владельцем сокетов всегда является пользователь, запускающий сервер). В сочетании с параметром unix_socket_permissions этот параметр можно использовать в качестве дополнительного механизма управления доступом для подключений через сокеты Unix-домена. По умолчанию это пустая строка, что делает владельцем группу по умолчанию пользователя сервера. Этот параметр можно задать только при запуске сервера.

unix_socket_permissions (integer)

Задает права доступа к сокетов Unix-домена. Для этих сокетов применяется обычный набор разрешений файловой системы Unix. Ожидается, что значение параметра будет иметь числовой вид, указанный в формате, принимаемом системными функциями chmod и umask. (Для использования обычного восьмеричного формата число должно начинаться с 0 (нуля).)

Разрешения по умолчанию — 0777, при которых подключаться к сокету могут все. Разумными альтернативами являются 0770 (доступ имеет только пользователь и группа, см. также unix_socket_group) и 0700 (доступ имеет только пользователь). (Обратите внимание, что для сокетов Unix-домена имеет значение только право на запись, поэтому нет смысла устанавливать или отзывать права на чтение или выполнение.)

Этот параметр можно задать только при запуске сервера.

Этот параметр не применим к системам (особенно Solaris — а именно Solaris 10), которые полностью игнорируют разрешения для сокетов. Там схожего эффекта можно достичь, задав в параметре unix_socket_directories каталог, доступ к которому ограничен желаемой аудиторией.

bonjour (boolean)

Включает оповещение о существование сервера посредством Bonjour. По умолчанию выключен. Этот параметр можно задать только при запуске сервера.

bonjour_name (string)

Задает имя службы в среде Bonjour. Если для этого параметра задана пустая строка, '' (это значение по умолчанию), то в качестве имени службы используется имя компьютера. Этот параметр игнорируется, если сервер был скомпилирован без поддержки Bonjour. Этот параметр можно задать только при запуске сервера.

tcp_keepalives_idle (integer)

Задает время бездействия сети, по истечении которого операционная система должна отправлять клиенту сигнал TCP о поддержании активного состояния. Если это значение указано без единиц измерения, оно считается заданным в секундах. При значении 0 (по умолчанию) выбирается значение, принятое по умолчанию в операционной системе. Этот параметр поддерживается только в системах, поддерживающих TCP_KEEPIDLE или равнозначный ему параметр сокета; в других системах он должен быть равен нулю. В сеансах, подключенных через сокеты Unix-домена, этот параметр игнорируется и всегда считается равным нулю.

tcp_keepalives_interval (integer)

Задает время, по истечении которого следует повторно передать сигнал TCP о поддержании активного состояния, если получение предыдущего сигнала не было подтверждено клиентом. Если это значение указано без единиц измерения, оно считается заданным в секундах. При значении 0 (по умолчанию) выбирается значение, принятое по умолчанию в операционной системе. Этот параметр поддерживается только в системах, поддерживающих TCP_KEEPINTVL или равнозначный ему параметр сокета; в других системах он должен быть равен нулю. В сеансах, подключенных через сокеты домена Unix, этот параметр игнорируется и всегда считается равным нулю.

tcp_keepalives_count (integer)

Задает количество сигналов TCP о поддержании активного состояния, которые могут быть потеряны до того, как соединение сервера с клиентом будет считаться прерванным. При значении 0 (по умолчанию) выбирается значение, принятое по умолчанию в операционной системе. Этот параметр поддерживается только в системах, поддерживающих TCP_KEEPCNT или равнозначный ему параметр сокета; в других системах он должен быть равен нулю. В сеансах, подключенных через сокеты домена Unix, этот параметр игнорируется и всегда считается равным нулю.

tcp_user_timeout (integer)

Задает время, в течение которого передаваемые данные могут оставаться неподтвержденными, прежде чем TCP-соединение будет принудительно закрыто. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. При значении 0 (по умолчанию) выбирается значение, принятое по умолчанию в операционной системе. Этот параметр поддерживается только в системах, поддерживающих TCP_USER_TIMEOUT или равнозначный ему параметр сокета; в других системах он должен быть равен нулю. В сеансах, подключенных через сокеты домена Unix, этот параметр игнорируется и всегда считается равным нулю.

Аутентификация

authentication_timeout (integer)

Максимальное время, за которое должна завершиться аутентификация клиента. Если потенциальный клиент не завершил протокол аутентификации за это время, сервер закрывает соединение. Это не дает зависшим при подключении клиентам занимать соединение неограниченно долго. Если это значение указано без единиц измерения, оно считается заданным в секундах. Значение по умолчанию составляет одну минуту (1m). Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

password_encryption (enum)

Если в CREATE ROLE или ALTER ROLE указан пароль, этот параметр определяет алгоритм, которым тот будет зашифрован. Значением по умолчанию является md5, то есть пароль сохраняется в виде хеша MD5 (также в качестве псевдонима для md5 допускается значение on). При значении scram-sha-256 пароль зашифруется алгоритмом SCRAM-SHA-256.

Обратите внимание, что старые клиенты могут не поддерживать механизм аутентификации SCRAM и, следовательно, не будут работать с паролями, зашифрованными алгоритмом SCRAM-SHA-256.

krb_server_keyfile (string)

Задает путь к файлу ключей Kerberos для данного сервера. Если в качестве значения задается пустая строка, то используется значение по умолчанию, зависящее от системы. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

krb_caseins_users (boolean)

Определяет, должны ли имена пользователей GSSAPI обрабатываться без учета регистра. По умолчанию установлено значение off (регистр учитывается). Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

db_user_namespace (boolean)

Этот параметр позволяет относить имена пользователей к базам данных. По умолчанию установлено значение off (выключен). Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Если этот параметр включен, имена пользователей следует создавать в виде имя_пользователя@база_данных. Когда подключающийся клиент передает имя_пользователя, к нему добавляются @ и имя базы данных, и сервер ищет пользователя по этому специфическому для базы данных имени. Обратите внимание, что при создании в среде SQL пользователей с именами, содержащими @, потребуется заключать эти имена в кавычки.

Если этот параметр включен, вы все равно можете создавать обычных глобальных пользователей. Для этого достаточно добавить @ при указании имени пользователя в клиенте, например joe@. Символ @ будет удален раньше, чем сервер найдет имя пользователя.

db_user_namespace вызывает расхождения в представлении имени пользователя на стороне клиента и сервера. Проверка подлинности всегда выполняется с именем на стороне сервера, поэтому методы аутентификации должны настраиваться по серверному имени пользователя, а не клиентскому. Поскольку метод аутентификации md5 использует имя пользователя в качестве соли как на стороне клиента, так и на стороне сервера, при включенном параметре db_user_namespace этот метод использовать нельзя.

SSL

ssl (boolean)

Разрешает подключения SSL. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Значение по умолчанию — off (выключен).

ssl_ca_file (string)

Задает имя файла, содержащего сертификаты центров сертификации (ЦС) для SSL- сервера. Относительные пути рассматриваются от каталога данных. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. По умолчанию этот параметр пуст, то есть файл ЦС не загружается, и проверка клиентских сертификатов не выполняется.

ssl_cert_file (string)

Задает имя файла, содержащего сертификат SSL-сервера. Относительные пути рассматриваются от каталога данных. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Значение по умолчанию — server.crt.

ssl_crl_file (string)

Задает имя файла, содержащего список отзыва сертификатов (CRL, Certificate Revocation List) для SSL-сервера. Относительные пути рассматриваются от каталога данных. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. По умолчанию этот параметр пуст, то есть файл CRL не загружается.

ssl_key_file (string)

Задает имя файла, содержащего закрытый ключ SSL-сервера. Относительные пути рассматриваются от каталога данных. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Значение по умолчанию — server.key.

ssl_ciphers (string)

Задает список наборов шифров SSL, которые разрешено использовать для SSL- соединений. Синтаксис этого параметра и список поддерживаемых значений можно найти на странице руководства по шифрам в пакете OpenSSL. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Значение по умолчанию — HIGH:MEDIUM:+3DES:!aNULL. Обычно это разумный выбор, если у вас нет особых требований к безопасности.

Объяснение значения по умолчанию:

-   *HIGH* — наборы шифров, в которых используются шифры из группы HIGH
    (например, AES, Camellia, 3DES)

-   *MEDIUM* — наборы шифров, в которых используются шифры из группы MEDIUM
    (например, RC4, SEED)

-   *+3DES* — порядок OpenSSL по умолчанию для HIGH проблематичен, потому
    что он ставит 3DES выше, чем AES128. Это неправильно, поскольку 3DES
    менее безопасен, чем AES128, и работает гораздо медленнее. Включение
    *+3DES* меняет этот порядок, размещая 3DES после всех других шифров
    групп HIGH и MEDIUM.

-   *!aNULL* — выключает наборы анонимных шифров, не требующие
    аутентификации. Такие наборы уязвимы для атак через посредника, и
    поэтому не должны использоваться.

Доступные наборы шифров и их свойства зависят от версии OpenSSL. Чтобы увидеть фактическую информацию о них для текущей установленной версии OpenSSL, используйте команду openssl ciphers -v 'HIGH:MEDIUM:+3DES:!aNULL'. Обратите внимание, что этот список фильтруется во время выполнения, в зависимости от типа ключа сервера.

ssl_prefer_server_ciphers (boolean)

Определяет, следует ли отдавать предпочтение шифрам SSL-сервера, а не клиента. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Значение по умолчанию — on (включен).

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

ssl_ecdh_curve (string)

Задает имя кривой для использования при обмене ключами ECDH. Эту кривую должны поддерживать все подключающиеся клиенты. Это необязательно должна быть та же кривая, с которой был получен ключ эллиптической кривой сервера. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Значение по умолчанию — prime256v1.

Имена OpenSSL для наиболее распространенных кривых: prime256v1 (NIST P-256), secp384r1 (NIST P-384), secp521r1 (NIST P-521). Полный список доступных кривых можно получить с помощью команды openssl ecparam -list_curves. Однако не все из них пригодны для TLS.

ssl_min_protocol_version (enum)

Задает минимальную версию протокола SSL/TLS, которую можно использовать. Допустимые на данный момент значения: TLSv1, TLSv1.1, TLSv1.2, TLSv1.3. Старые версии библиотеки OpenSSL поддерживают не все значения; при выборе неподдерживаемой версии возникнет ошибка. Версии протокола до TLS 1.0, а именно SSL v.2 и v.3, в любом случае не работают.

Значение по умолчанию — TLSv1, в основном для поддержки старых версий библиотеки OpenSSL. Возможно, имеет смысл установить более высокое значение, если все применяемые у вас программные компоненты могут поддерживать более новые версии протокола.

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

ssl_max_protocol_version (enum)

Задает максимальную версию протокола SSL/TLS, которую можно использовать. Допустимые значения такие же, как у параметра ssl_min_protocol_version, плюс пустая строка, означающая, что допускается любая версия протокола. По умолчанию разрешена любая версия. Установка максимальной версии протокола полезна прежде всего для тестирования или в случаях, когда у некоторых компонентов возникают проблемы при работе с более новым протоколом.

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

ssl_dh_params_file (string)

Задает имя файла, содержащего параметры алгоритма Диффи-Хеллмана, используемые для так называемого эфемерного семейства DH-шифров SSL. По умолчанию этот параметр пуст, то есть используются параметры DH, скомпилированные по умолчанию. Применение нестандартных параметров DH повышает защиту, если злоумышленнику удается взломать хорошо известные скомпилированные параметры DH. Собственный файл с параметрами DH можно создать с помощью команды openssl dhparam -out dhparams.pem 2048.

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

ssl_passphrase_command (string)

Задает внешнюю команду, которая будет вызываться, когда потребуется получить парольную фразу для расшифровки SSL-файла, например закрытого ключа. По умолчанию этот параметр пуст, то есть используется встроенный механизм запроса.

Эта команда должна вывести парольную фразу на устройство стандартного вывода и завершиться с кодом 0. В значении параметра %p заменяется строкой приглашения. (Напишите %%, чтобы вывести литерал %). Обратите внимание, что строка приглашения, вероятно, будет содержать пробелы, поэтому потребуется заключить ее в кавычки. Если в конце имеется один перевод строки, он будет удален при выводе.

На самом деле команда не обязана запрашивать у пользователя парольную фразу. Она может прочитать ее из файла, получить из системной связки ключей или другими подобными способами. Ответственность за надлежащую безопасность выбранного механизма несет пользователь.

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

ssl_passphrase_command_supports_reload (boolean)

Этот параметр определяет, будет ли заданная параметром ssl_passphrase_command команда запроса пароль также вызываться при перезагрузке конфигурации, если для файла ключа нужна парольная фраза. Если этот параметр выключен (по умолчанию), то ssl_passphrase_command будет игнорироваться при перезагрузке, и конфигурация SSL не будет перезагружаться, если требуется парольная фраза. Этот параметр подходит для команды, которой для ввода пароля требуется терминал TTY, который может быть недоступен во время работы сервера. Включение этого параметра может быть целесообразно, если, например, пароль считывается из файла.

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Потребление ресурсов

Память

shared_buffers (integer)

Задает объем памяти, который будет использовать сервер базы данных для буферов общей памяти. Значение по умолчанию обычно составляет 128 мегабайт (128MB), но может быть и меньше, если параметры ядра не будут его поддерживать (это определяется в процессе initdb). Значение этого параметра не должно быть меньше 128 килобайт. Однако для поддержания хорошей производительности обычно требуются значения гораздо выше минимальных. Если это значение указано без единиц измерения, оно считается заданным в блоках, размер которых равен BLCKSZ байтов (обычно это 8 КБ). (Значения BLCKSZ, отличные от принятых по умолчанию, изменяют минимально допустимое значение.) Этот параметр можно задать только при запуске сервера.

Если у вас есть выделенный сервер базы данных с объемом ОЗУ 1 ГБ или более, разумное начальное значение для shared_buffers составляет 25% от объема памяти в вашей системе. Существуют варианты рабочей нагрузки, при которых эффективны еще большие значения shared_buffers, но поскольку QHB также использует и кэш операционной системы, выделение для shared_buffers более 40% ОЗУ вряд ли повысит эффективность. При увеличении значения shared_buffers обычно требуется соответствующее увеличение max_wal_size, чтобы растянуть процесс записи большого объема новых или измененных данных на более длительный период времени.

В системах с объемом ОЗУ меньше 1 ГБ целесообразно установить меньший процент ОЗУ, чтобы оставить достаточно места для операционной системы.

huge_pages (enum)

Определяет, будут ли запрашиваться огромные страницы из основной области общей памяти. Допустимые значения: try (по умолчанию), on и off. Если в параметре huge_pages установлено try, сервер будет пытаться запросить огромные страницы, но в случае сбоя вернется к поведению по умолчанию. При значении on, если получить огромные страницы не удастся, сервер не запустится. При значении off огромные страницы запрашиваться не будут.

В настоящее время этот параметр поддерживается только в Linux. Во всех других системах значение try игнорируется.

Использование огромных страниц приводит к уменьшению таблиц страниц и сокращению затрат времени ЦП на управление памятью, что повышает производительность.

Обратите внимание, что этот параметр влияет только на основную область общей памяти. Операционные системы, такие как Linux, FreeBSD и Illumos, также могут автоматически использовать огромные страницы (также называемые «суперстраницами» или «большими» страницами) для обычного выделения памяти без явного запроса со стороны QHB. В Linux это называется «прозрачными огромными страницами» (ТНР, Transparent Huge Pages). Известно, что эта функция может привести к снижению производительности QHB для некоторых пользователей в некоторых версиях Linux, поэтому использовать ее в настоящее время не рекомендуется (в отличие от явного использования huge_pages).

temp_buffers (integer)

Задает максимальный объем памяти, выделяемый для временных буферов в каждом сеансе базы данных. Это существующие исключительно в рамках сеанса буферы, используемые только для доступа к временным таблицам. Если это значение указано без единиц измерения, оно считается заданным в блоках, размер которых равен BLCKSZ байтов (обычно это 8 КБ). По умолчанию используется восемь мегабайтов (8MB). (Если BLCKSZ не равен 8 КБ, значение по умолчанию масштабируется пропорционально ему). Этот параметр можно менять в отдельном сеансе, но только до первого обращения к временным таблицам; последующие попытки изменить его значение не будут влиять на этот сеанс.

Сеанс будет выделять временные буферы по мере необходимости до предела, заданного temp_buffers. При установке большого значения в сеансах, которым на самом деле не нужно много временных буферов, память расходуется только на дескриптор буфера, который занимает 64 байта, за приращение в temp_buffers. Однако если буфер действительно используется, он будет дополнительно занимать 8192 байта (или, в общем случае, BLCKSZ байтов).

max_prepared_transactions (integer)

Задает максимальное количество транзакций, которые могут одновременно находиться в «подготовленном» состоянии (см. PREPARE TRANSACTION). Установка этого параметра в ноль (по умолчанию) выключает функцию подготовленных транзакций. Этот параметр можно задать только при запуске сервера.

Если вы не планируете использовать подготовленные транзакции, следует установить этот параметр на ноль, чтобы предотвратить случайное создание подготовленных транзакций. Если же вы используете подготовленные транзакции, имеет смысл, чтобы max_prepared_transactions был не меньше, чем max_connections, чтобы можно было подготовить транзакцию в каждом сеансе.

При запуске резервного сервера следует установить для этого параметра значение большее или равное значению на главном сервере. В противном случае на резервном сервере не будут разрешены запросы.

work_mem (integer)

Задает максимальный объем памяти, который будет использоваться в операциях при обработке запроса (например, для сортировки или хеш-таблиц) перед записью во временные файлы на диске. Если это значение указано без единиц измерения, оно считается заданным в килобайтах. Значение по умолчанию составляет четыре мегабайта (4MB). Обратите внимание, что в сложном запросе могут параллельно выполняться несколько операций сортировки или хеширования, причем указанный в этом значении объем памяти может использоваться в каждой операции, прежде чем та начнет записывать данные во временные файлы. Кроме того, такие операции могут выполняться одновременно в нескольких запущенных сеансах. Следовательно, общая используемая память может многократно превышать значение параметра work_mem; это необходимо учитывать при выборе значения. Операции сортировки применяются для ORDER BY, DISTINCT и объединений слиянием. Хеш-таблицы применяются в хеш-соединениях, агрегировании по хешу и обработке подзапросов IN с использованием хеша.

maintenance_work_mem (integer)

Задает максимальный объем памяти, который будет использоваться операциями обслуживания, такими как VACUUM, CREATE INDEX и ALTER TABLE ADD FOREIGN KEY. Если это значение указано без единиц измерения, оно считается заданным в килобайтах. Значение по умолчанию составляет 64 мегабайта (64MB). Поскольку в один момент времени сеансом базы данных может быть выполнена только одна из этих операций, и обычно они не выполняются параллельно, можно без опасений сделать это значение значительно выше, чем work_mem. Увеличение этого параметра может повысить производительность операций очистки и восстановления дампов базы данных.

Обратите внимание, что при запуске операции автовакуума этот объем памяти может быть выделен до autovacuum_max_workers раз, поэтому постарайтесь не делать значение по умолчанию слишком большим. Возможно, лучше будет управлять выделяемым для автовакуума объемом памяти отдельно, с помощью параметра autovacuum_work_mem.

autovacuum_work_mem (integer)

Задает максимальный объем памяти, который будет использоваться каждым рабочим процессом автовакуума. Если это значение указано без единиц измерения, оно считается заданным в килобайтах. По умолчанию используется значение -1, указывающее, что вместо этого параметра следует использовать значение maintenance_work_mem. Этот параметр не влияет на поведение команды VACUUM при запуске в других контекстах. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

max_stack_depth (integer)

Задает максимальную безопасную глубину стека исполнения сервера. Идеальным значением для этого параметра является фактический предел размера стека, установленный ядром (который задается командой ulimit -s или ее локальным эквивалентом), за вычетом запаса прочности примерно в мегабайт. Запас прочности необходим, потому что глубина стека проверяется не в каждой подпрограмме на сервере, а только в ключевых потенциально рекурсивных подпрограммах. Если это значение указано без единиц измерения, оно считается заданным в килобайтах. Значение по умолчанию составляет два мегабайта (2MB), что дает небольшой запас и вряд ли приведет к сбою. Однако этого может быть недостаточно для выполнения сложных функций. Только суперпользователи могут изменять этот параметр.

Если установить max_stack_depth выше фактического лимита ядра, то функция с неограниченной рекурсией может вызвать сбой отдельного процесса сервера. На платформах, где QHB может определить предел, установленный ядром, сервер не допустит, чтобы для этой переменной было задано небезопасное значение. Однако не все платформы предоставляют подобную информацию, поэтому при выборе значения рекомендуется соблюдать осторожность.

shared_memory_type (enum)

Определяет реализацию разделяемой памяти, которую сервер должен использовать при работе с основной областью разделяемой памяти, содержащей общие буферы QHB и другие общие данные. Возможные значения: mmap (для выделения анонимных блоков разделяемой памяти с помощью mmap) и sysv (для выделения разделяемой памяти System V с помощью shmget). Не все варианты поддерживаются на разных платформах; первый из поддерживаемых данной платформой вариантов становится для нее реализацией по умолчанию. Использовать sysv, которая нигде не выбирается по умолчанию, в принципе не рекомендуется, потому что с ней для выделения большого объема памяти обычно требуются нестандартные настройки ядра.

dynamic_shared_memory_type (enum)

Определяет реализацию динамической общей памяти, которую должен использовать сервер. Возможные значения: posix (для выделения общей памяти POSIX с помощью shm_open), sysv (для выделения общей памяти System V с помощью shmget) и mmap (для эмуляции общей памяти посредством отображения в памяти файлов, хранящихся в каталоге данных). Не все варианты поддерживаются на разных платформах; первый из поддерживаемых данной платформой вариантов становится для нее реализацией по умолчанию. Использовать mmap, которая нигде не выбирается по умолчанию, в принципе не рекомендуется, поскольку операционная система может многократно записывать измененные страницы на диск, увеличивая нагрузку на системные потоки ввода-вывода; однако это может быть полезно при отладке, когда каталог pg_dynshmem хранится на RAM-диске или когда другие реализации общей памяти недоступны.

Диск

temp_file_limit (integer)

Задает максимальный объем дискового пространства, который один процесс может использовать для временных файлов, например при сортировке и хешировании или при сохранении удерживаемого курсора. Транзакция, пытающаяся превысить этот лимит, будет отменена. Если это значение указано без единиц измерения, оно считается заданным в килобайтах. Значение -1 (по умолчанию) означает отсутствие ограничений. Только суперпользователи могут изменять этот параметр.

Этот параметр ограничивает общий объем, занимаемый в текущий момент всеми временными файлами, используемыми данным процессом QHB. Следует отметить, что в этом ограничении не учитывается дисковое пространство, занимаемое явными временными таблицами; ограничивается только объем временных файлов, используемых за кадром при выполнении запросов.

Использование ресурсов ядра

max_files_per_process (integer)

Задает максимальное количество одновременно открытых файлов, разрешенное для каждого подпроцесса сервера. По умолчанию это тысяча файлов. Если ядро устанавливает безопасный лимит для каждого процесса, об этом параметре можно не беспокоиться. Но на некоторых платформах (особенно в большинстве систем BSD) ядро позволяет отдельным процессам открывать гораздо больше файлов, чем фактически может поддерживать система в случае, если столько же файлов одновременно открыло бы сразу несколько процессов. Если вы столкнетесь с ошибками «Too many open files» (Слишком много открытых файлов), попробуйте уменьшить значение этого параметра. Этот параметр можно задать только при запуске сервера.

Задержка очистки по стоимости

Во время выполнения команд VACUUM и ANALYZE система ведет внутренний счетчик, который фиксирует оценочную стоимость различных выполняемых операций ввода- вывода. Когда накопленная стоимость достигает предела (указанного в параметре vacuum_cost_limit), процесс, выполняющий операцию, на некоторое время перейдет в спящий режим, как указано в параметре vacuum_cost_delay. Затем он сбросит счетчик и продолжит выполнять операцию.

Цель этой функции — позволить администраторам уменьшить влияние процессов ввода/ вывода этих команд на параллельную работу базы данных. Во многих ситуациях быстрое завершение команд обслуживания, таких как VACUUM и ANALYZE, не имеет значения; однако, как правило, очень важно, чтобы эти команды не оказывали значительного влияния на способность системы выполнять другие операции с базой данных. Администраторы могут этого добиться с помощью задержки очистки по стоимости.

Эта функция по умолчанию выключена для команд VACUUM, введенных вручную. Чтобы ее включить, установите для переменной vacuum_cost_delay ненулевое значение.

vacuum_cost_delay (floating point)

Время, в течение которого процесс будет находиться в спящем режиме после превышения предела стоимости. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. Значение по умолчанию равно нулю, то есть функция задержки очистки по стоимости выключена. Положительные значения включают очистку, интенсивность которой зависит от стоимости.

При настройке интенсивности такой очистки подходящие значения для vacuum_cost_delay обычно довольно малы, вплоть до 1 миллисекунды и менее. Несмотря на то, что в этом параметре можно задать значения в долях миллисекунды, на старых платформах такие задержки могут быть неточными. На таких платформах увеличение интенсивности VACUUM по сравнению с уровнем, получаемым при задержке в 1 мс, потребует изменения других параметров стоимости очистки. Тем не менее, стоит сохранять столь малое значение vacuum_cost_delay, какое ваша платформа в состоянии измерить; большие задержки не будут полезны.

vacuum_cost_page_hit (integer)

Ориентировочная стоимость очистки буфера в общем буферном кэше. Это подразумевает стоимость блокировки пула буферов, поиска в общей хеш-таблице и сканирования содержимого страницы. Значение по умолчанию — 1.

vacuum_cost_page_miss (integer)

Ориентировочная стоимость очистки буфера, который нужно прочитать с диска. Это подразумевает попытку заблокировать пул буферов, поиск в общей хеш-таблице, чтение нужного блока с диска и сканирование его содержимого. Значение по умолчанию — 10.

vacuum_cost_page_dirty (integer)

Ориентировочная стоимость очистки, изменяющей блок, который до этого был чистым. Это подразумевает дополнительную стоимость ввода/вывода, необходимого для повторной записи измененного блока на диск. Значение по умолчанию — 20.

vacuum_cost_limit (integer)

Накопленная стоимость, по достижении которой процесс очистки перейдет в спящий режим. Значение по умолчанию — 200.

Примечание
Существуют определенные операции, которые устанавливают критические блокировки и поэтому должны завершаться как можно быстрее. Во время таких операций задержка очистки по стоимости не проводится, поэтому накопленная за это время стоимость может намного превышать указанный предел. Во избежание ненужных длительных задержек в таких случаях, фактическая задержка рассчитывается как vacuum_cost_delay * accumulated_balance / vacuum_cost_limit и ограничивается максимумом vacuum_cost_delay * 4.

Фоновая запись

Среди прочих существует отдельный серверный процесс, называемый фоновой записью, задача которого — производить запись «грязных» (новых или измененных) общих буферов. Когда количество чистых общих буферов считается недостаточным, этот процесс записывает некоторые грязные буферы в файловую систему и помечает их как чистые. Это снижает вероятность того, что серверным процессам, занимающимся запросами пользователя, не удастся найти чистые буферы и придется записывать грязные буферы самостоятельно. Тем не менее, процесс фоновой записи вызывает увеличение общей нагрузки на систему ввода/вывода, поскольку он может записывать неоднократно изменяемую страницу несколько раз, в то время как без него эту страницу можно было бы записать всего один раз за интервал контрольной точки. Параметры, рассматриваемые в этом подразделе, позволяют настроить поведение фоновой записи для локальных нужд.

bgwriter_delay (integer)

Задает задержку между раундами активности процесса фоновой записи. В каждом раунде этот процесс производит запись некоторого количества грязных буферов (это настраивается следующими параметрами). Затем он засыпает на время bgwriter_delay, после чего все повторяется. Когда в пуле нет грязных буферов, процесс засыпает на более долгий срок, независимо от значения параметра bgwriter_delay. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. Значение по умолчанию — 200 миллисекунд (200ms). Обратите внимание, что во многих системах эффективное разрешение задержек сна составляет 10 миллисекунд; если задать в bgwriter_delay значение, не кратное 10, на практике можно получить такой же результат, что и со следующим за ним значением, кратным 10. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

bgwriter_lru_maxpages (integer)

Максимальное количество буферов, которое процесс фоновой записи будет записывать в каждом раунде. Установка этого значения в ноль выключает фоновую запись. (Обратите внимание, что это не затрагивает контрольные точки, которые управляются отдельным выделенным вспомогательным процессом.) Значение по умолчанию — 100 буферов. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

bgwriter_lru_multiplier (floating point)

Количество грязных буферов, записанных в каждом раунде, основано на количестве новых буферов, которое понадобилось серверным процессам во время последних раундов. Для расчета предварительной потребности в буферах для следующего раунда средняя потребность за последнее время умножается на значение bgwriter_lru_multiplier. Грязные буферы будут записываются до тех пор, пока количество чистых, пригодных для повторного использования буферов не достигнет целевого значения. (Однако в каждом раунде будет записано не более bgwriter_lru_maxpages буферов.) Таким образом, значение этого параметра, равное 1.0, воплощает политику «точно в срок», при которой записывается именно то количество буферов, которое, предполагается необходимым. Увеличение этого значения обеспечивает некоторую страховку от всплесков потребностей, тогда как уменьшение преднамеренно оставляет некоторый объем записи для серверных процессов. Значение по умолчанию — 2.0. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

bgwriter_flush_after (integer)

Всякий раз, когда процесс фоновой записи записывает больше заданного объема данных, сервер пытается заставить ОС произвести запись этих данных в нижележащее хранилище. Это ограничивает количество «грязных» данных в страничном кэше ядра, снижая вероятность зависаний при выполнении fsync в конце контрольной точки или когда ОС выполняет обратную запись большими пакетами в фоновом режиме. Зачастую это приводит к значительному снижению задержки транзакций, но также бывают случаи (особенно когда объем рабочей нагрузки больше shared_buffers, но меньше страничного кэша), когда производительность может снизиться. Этот параметр может действовать не на всех платформах. Если это значение указано без единиц измерения, оно считается заданным в блоках, размер которых равен BLCKSZ байтов (обычно это 8 КБ). Допустимый диапазон: от 0 (что выключает принудительную обратную запись) до 2 MB. Значение по умолчанию равно 512kB в Linux и 0 в других ОС. (Если BLCKSZ отличен от 8 КБ, значение по умолчанию и максимальное значение масштабируются пропорционально ему.) Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

С маленькими значениями bgwriter_lru_maxpages и bgwriter_lru_multiplier снижается дополнительная нагрузка на систему ввода/вывода, вызываемая процессом фоновой записи, но повышается вероятность того, что серверным процессам придется самим производить записи, что замедлит интерактивные запросы.

Асинхронное поведение

effective_io_concurrency (integer)

Задает количество параллельных операций ввода/вывода на диске, которые, как ожидает QHB, могут выполняться одновременно. Увеличение этого значения приведет к увеличению числа операций ввода/вывода, которые QHB попытается запустить параллельно в каждом отдельном сеансе. Допустимый диапазон: от 1 до 1000; ноль выключает выполнение асинхронных запросов ввода/вывода. В настоящее время этот параметр влияет только на сканирование по битовой карте.

Для магнитных носителей хорошей начальной точкой для этого параметра является количество отдельных дисков, содержащих массив с чередованием RAID 0 или зеркальный массив RAID 1, в котором размещена база данных. (Для RAID 5 диск четности следует исключить.) Однако если база данных часто обрабатывает несколько запросов, выполняемых в параллельных сеансах, для полной загрузки дискового массива может хватить и небольших значений. Более высокое значение приведет лишь к дополнительной нагрузке на ЦП. Диски SSD и другие виды хранилища в памяти часто могут обрабатывать множество параллельных запросов, поэтому наилучшим значением могут быть сотни.

Асинхронный ввод/вывод зависит от эффективности функции posix_fadvise, которая отсутствует в некоторых операционных системах. В случае ее отсутствия установка этого параметра в любое значение, отличное от нуля, приведет к ошибке. В некоторых операционных системах (например, Solaris) эта функция имеется, но в действительности ничего не делает.

В системах, где это поддерживается, по умолчанию установлено значение 1, в остальных — 0. Это значение можно переопределить для таблиц в определенном табличном пространстве, установив одноименный параметр табличного пространства (см. ALTER TABLESPACE).

maintenance_io_concurrency (integer)

Этот параметр схож с effective_io_concurrency, но используется в операциях обслуживания, которые выполняются во многих клиентских сеансах.

В системах, где это поддерживается, по умолчанию установлено значение 1, в остальных — 0. Это значение можно переопределить для таблиц в определенном табличном пространстве, установив одноименный параметр табличного пространства (см. ALTER TABLESPACE).

max_worker_processes (integer)

Задает максимальное количество фоновых процессов, которые может поддерживать система. Этот параметр можно задать только при запуске сервера. Значение по умолчанию — 8.

При запуске резервного сервера следует установить для этого параметра значение большее или равное значению на главном сервере. В противном случае на резервном сервере не будут разрешены запросы.

При изменении этого значения также имеет смысл скорректировать параметры max_parallel_workers, max_parallel_maintenance_workers и max_parallel_workers_per_gather.

max_parallel_workers_per_gather (integer)

Задает максимальное количество рабочих процессов, которое может запустить один узел Gather или Gather Merge. Параллельные рабочие процессы берутся из пула процессов, созданного параметром max_worker_processes и ограниченного параметром max_parallel_workers. Обратите внимание, что запрошенное количество рабочих процессов в действительности может быть недоступно во время выполнения. Если это произойдет, план будет выполняться с меньшим количеством рабочих процессов, чем ожидалось, что может быть неэффективно. Значение по умолчанию — 2. Установка этого значения в 0 выключает параллельное выполнение запросов.

Обратите внимание, что параллельные запросы могут потреблять значительно больше ресурсов, чем непараллельные, потому что каждый рабочий процесс является совершенно отдельным процессом, который оказывает на систему примерно такое же влияние, что и дополнительный пользовательский сеанс. Это следует учитывать при выборе значения этого параметра, а также при настройке других параметров, управляющих использованием ресурсов, например work_mem. Ограничения ресурсов, такие как work_mem, применяются индивидуально к каждому рабочему процессу, что означает, что общая нагрузка для всех процессов может оказаться намного выше, чем она была бы для отдельного процесса в обычной ситуации. Например, параллельный запрос с использованием 4 рабочих процессов может задействовать в 5 раз больше времени процессора, объема памяти, пропускной способности ввода/вывода и т. д., чем запрос, который вообще не использует рабочие процессы.

max_parallel_maintenance_workers (integer)

Задает максимальное количество параллельных рабочих процессов, которое может запустить одна служебная команда. В настоящее время единственными параллельными служебными командами, которые поддерживают использование параллельных рабочих процессов, являются CREATE INDEX (и только при построении индекса B-дерева) и VACUUM без указания FULL. Параллельные рабочие процессы берутся из пула процессов, созданного параметром max_worker_processes и ограниченного параметром max_parallel_workers. Обратите внимание, что запрошенное количество рабочих процессов в действительности может быть недоступно во время выполнения. Если это произойдет, служебная операция будет выполняться с меньшим количеством рабочих процессов, чем ожидалось. Значение по умолчанию — 2. Установка этого значения в 0 выключает использование параллельных рабочих процессов служебными командами.

Обратите внимание, что параллельные служебные команды не должны потреблять значительно больше памяти, чем равнозначные непараллельные операции. Эта стратегия отличается от стратегии параллельного запроса, где ограничения ресурсов обычно применяются для каждого рабочего процесса. Для параллельных служебных команд ограничение ресурса maintenance_work_mem считается применяемым ко всей служебной команде, независимо от количества параллельных рабочих процессов. Тем не менее, параллельные служебные команды все равно могут значительно больше нагружать процессор и канал ввода/вывода.

max_parallel_workers (integer)

Задает максимальное количество рабочих процессов, которое система может поддерживать для параллельных операций. Значение по умолчанию — 8. При увеличении или уменьшении этого значения также имеет смысл скорректировать параметры max_parallel_maintenance_workers и max_parallel_workers_per_gather. Также обратите внимание, что если установить для этого параметра значение, превышающее max_worker_processes, это не будет иметь никакого эффекта, поскольку параллельные рабочие процессы берутся из пула рабочих процессов, ограничиваемого этим параметром.

backend_flush_after (integer)

Всякий раз, когда одним обслуживающим процессом записывается больше заданного объема данных, сервер пытается заставить ОС произвести запись этих данных в нижележащее хранилище. Это ограничивает количество «грязных» данных в страничном кэше ядра, снижая вероятность зависаний при выполнении fsync в конце контрольной точки или когда ОС выполняет обратную запись большими пакетами в фоновом режиме. Зачастую это приводит к значительному снижению задержки транзакций, но также бывают случаи (особенно когда объем рабочей нагрузки больше shared_buffers, но меньше страничного кэша ОС), когда производительность может снизиться. Этот параметр может действовать не на всех платформах. Если это значение указано без единиц измерения, оно считается заданным в блоках, размер которых равен BLCKSZ байтов (обычно это 8 КБ). Допустимый диапазон: от 0 (что выключает принудительную обратную запись) до 2 MB. Значение по умолчанию — 0, т. е. принудительная обратная запись отсутствует. (Если BLCKSZ отличен от 8 КБ, значение по умолчанию и максимальное значение масштабируются пропорционально ему.)

old_snapshot_threshold (integer)

Задает минимальный период времени, в течение которого можно использовать снимок состояния для запроса без риска возникновения ошибки «снимок слишком старый». Данные, потерявшие актуальность и находящиеся в этом состоянии дольше этого периода, могут быть вычищены. Это может помочь предотвратить «разбухание» снимков, которые остаются задействованными долгое время. Во избежание неверных результатов из-за очистки данных, которые в противном случае были бы видны в снимке, генерируется ошибка, если снимок старше этого предела и используется для чтения страницы, измененной с момента его создания.

Если это значение указано без единиц измерения, оно считается заданным в минутах. Значение -1 (по умолчанию) выключает эту функцию, фактически делая предельный срок снимков бесконечным. Этот параметр можно задать только при запуске сервера.

Полезные для производственной работы значения могут варьироваться от нескольких часов до нескольких дней. Малые значения (например, 0 или 1min) допускаются только потому, что иногда они могут быть полезны при тестировании. Несмотря на то, что допустимо значение до 60d (60 дней), имейте в виду, что при многих рабочих нагрузках критичное «разбухание» или зацикливание идентификаторов транзакций может происходить в гораздо меньшие сроки.

Когда эта функция включена, освобожденное пространство в конце отношения нельзя отдать операционной системе, так как при этом может быть удалена информация, необходимая для выявления условия «снимок слишком стар». Все пространство, выделенное отношению, остается связанным с ним и повторно используется только в нем, пока не будет явно освобождено (например, командой VACUUM FULL).

Этот параметр не дает гарантии, что данная ошибка будет генерироваться при всех возможных обстоятельствах. В действительности, если можно получить верные результаты, например, из курсора, который материализовал результирующий набор, ошибка не будет выдана, даже если нижележащие строки в ссылочной таблице были вычищены. Некоторые таблицы, например системные каталоги, невозможно безопасно очистить досрочно, поэтому данный параметр на них не повлияет. Для таких таблиц этот параметр не уменьшает раздувание, но и не чреват ошибкой «снимок слишком стар» при сканировании.

Журнал упреждающей записи (WAL)

Для получения дополнительной информации о настройке этих параметров см. раздел Конфигурация WAL.

Настройки

wal_level (enum)

wal_level определяет, сколько информации записывается в WAL. Значение по умолчанию — replica, при котором в журнал записывается достаточно данных для поддержки архивирования WAL и репликации, включая выполнение запросов только на чтение на резервном сервере. Значение minimal удаляет всю информацию, кроме необходимой для восстановления после сбоя или аварийного отключения. Наконец, logical добавляет информацию, необходимую для поддержки логического декодирования. Каждый последующий уровень включает информацию, регистрируемую на всех уровнях ниже его. Этот параметр можно задать только при запуске сервера.

На уровне minimal WAL-протоколирование некоторых массовых операций можно пропустить без риска потери данных, что позволяет значительно ускорить эти операции (см. раздел Выключить архивацию WAL и потоковую репликацию). Такая оптимизация возможна в следующих операциях:

CREATE TABLE AS

CREATE INDEX

CLUSTER

COPY с таблицами, которые были созданы или усечены в одной транзакции

Но минимальный журнал не содержит достаточно информации для восстановления данных из базовой резервной копии и журналов WAL, поэтому для включения архивирования WAL (archive_mode) и потоковой репликации необходимо установить уровень replica или более высокий.

На уровне logical протоколируется та же информация, что и на уровне replica, а также информация, необходимая для извлечения из WAL наборов логических изменений. Установка уровня logical увеличит объем WAL, особенно если многие таблицы сконфигурированы с параметром REPLICA IDENTITY FULL и выполняется множество команды UPDATE и DELETE.

fsync (boolean)

Если этот параметр включен, сервер QHB попытается добиться, чтобы изменения были физически записаны на диск, выполняя системные вызовы fsync() или иные схожие методы (см. wal_sync_method). Это гарантирует, что кластер баз данных сможет вернуться в согласованное состояние после сбоя операционной системы или оборудования.

Хотя выключение fsync часто повышает производительность, это может привести к неустранимому повреждению данных в случае отключения питания или сбоя системы. Поэтому рекомендуется выключать fsync только в том случае, если вы легко можете восстановить всю базу данных из внешнего источника.

К примерам безопасных условий для выключения fsync относятся начальная загрузка нового кластера баз данных из файла резервной копии, использование кластера баз данных для обработки пакета данных, после которой база данных будет удалена и создана заново, или задействование клона базы данных только для чтения, который часто пересоздается и не используется для отработки отказа. Высококачественное оборудование само по себе не является достаточным основанием для выключения fsync.

Для надежного восстановления при смене значения fsync с off на on необходимо принудительно перенести все измененные буферы из ядра в стабильное хранилище. Это можно сделать, когда кластер остановлен или когда режим fsync включен, запустив команду initdb --sync-only или команду sync, размонтировав файловую систему или перезагрузив сервер.

Во многих ситуациях выключение synchronous_commit для некритичных транзакций может обеспечить больший потенциальный выигрыш в производительности, чем выключение fsync, без сопутствующего риска повреждения данных.

Параметр fsync можно задать только в файле qhb.conf или в командной строке сервера. Если вы выключите этот параметр, возможно, также имеет смысл выключить параметр full_page_writes.

synchronous_commit (enum)

Определяет, после завершения какого этапа обработки WAL сервер базы данных вернет клиенту сообщение об успешном выполнении операции. Допустимые значения: remote_apply (применено удаленно), on (включено, значение по умолчанию), remote_write (записано удаленно), local (локально) и off (выключено).

Если значение synchronous_standby_names не задано, для данного параметра имеют смысл только значения on и off; при значениях remote_apply, remote_write и local уровень синхронизации будет тот же, что и с вариантом on. Локальное поведение всех режимов, отличных от off, заключается в ожидании локального сброса WAL на диск. В режиме off из-за отсутствия ожидания возможна задержка между моментом, когда клиенту сообщается об успешном выполнении, и моментом, когда транзакция действительно гарантированно защищена от сбоя сервера. (Максимальная задержка равна тройному значению wal_writer_delay.) В отличие от fsync, значение off этого параметра не создает риска несогласованности базы данных: сбой операционной системы или базы данных может привести к потере некоторых последних предположительно зафиксированных транзакций, но состояние базы данных будет таким же, как если бы эти транзакции были прерваны обычным образом. Поэтому выключение параметра synchronous_commit может быть полезной альтернативой выключению fsync, когда производительность важнее точной уверенности в прочности транзакции. Дополнительную информацию см. в разделе Асинхронный коммит.

Если значение synchronous_standby_names не пустое, synchronous_commit также определяет, будет ли сервер при фиксировании транзакций ожидать, пока их записи WAL будут обработаны на резервном сервере (или серверах).

Если задано значение remote_apply, фиксирование не завершится, пока ответы от текущих синхронных резервных серверов не укажут, что они получили запись о фиксировании транзакции, применили эту транзакцию, так что она стала видимой для запросов на резервном сервере (или серверах), а также сохранили ее в стабильном хранилище. Это вызовет куда большую задержку фиксирования, нежели предыдущие параметры, поскольку нужно будет дождаться воспроизведения WAL. Если задано значение on, фиксирование не завершится, пока ответы от текущих синхронных резервных серверов не укажут, что они получили запись о фиксировании транзакции и сохранили ее в стабильном хранилище. Это гарантирует, что транзакция не будет потеряна, если только хранилище базы данных не будет повреждено и на основном, и на всех синхронных резервных серверах. Если задано значение remote_write, фиксирование не завершится, пока ответы от текущих синхронных резервных серверов не укажут, что они получили запись о фиксировании транзакции и сохранили ее в своих файловых системах. Этот вариант гарантирует сохранение данных даже в случае сбоя резервного сервера QHB, но не в случае сбоя на уровне его операционной системы, поскольку данные могут не достичь стабильного хранилища на этом сервере. Со значением local фиксирование завершится после локального сброса данных на диск, не дожидаясь репликации. Обычно этот вариант нежелателен при синхронной репликации, но он предоставлен для полноты.

Этот параметр можно изменить в любое время; поведение каждой конкретной транзакции определяется значением, действующим на момент ее фиксирования. Таким образом, есть возможность и смысл, чтобы некоторые транзакции фиксировались синхронно, а другие — асинхронно. Например, чтобы одна транзакция с несколькими состояниями фиксировалась асинхронно, когда по умолчанию задано противоположное значение, выполните в этой транзакции команду SET LOCAL synchronous_commit TO OFF.

Характеристики различных значений synchronous_commit приведены в таблице 1.

Таблица 1. Режимы synchronous_commit

Значение synchronous_commit | Стабильная локальная фиксация | Стабильная фиксация на резервном сервере после сбоя QHB | Стабильная фиксация на резервном сервере после сбоя ОС | Согласованность запросов на резервном сервере
remote_apply | * | * | * | *
on | * | * | * |
remote_write | * | * |  |
local | * |  |  |
off |  |  |  |

wal_sync_method (enum)

Метод, используемый для принудительного сохранения изменений WAL на диск. Если режим fsync выключен, то этот параметр не учитывается, поскольку принудительные сохранения файла WAL вообще не будут проводиться. Возможные значения этого параметра:

open_datasync (записать файлы WAL функцией open() с параметром O_DSYNC)

fdatasync (вызывать fdatasync() при каждом фиксировании)

fsync (вызывать fsync() при каждом фиксировании)

fsync_writethrough (вызывать fsync() при каждом фиксировании, форсируя вызывая сквозную запись любого дискового кэша)

open_sync (записать файлы WAL функцией open() с параметром O_SYNC)

Варианты open_* также применяют флаг O_DIRECT если он доступен. Не все эти методы доступны на всех платформах. По умолчанию выбирается первый метод из приведенного выше списка, который поддерживается платформой, за исключением того, что в Linux значением по умолчанию является fdatasync. Выбираемый по умолчанию вариант не обязательно идеален; для создания отказоустойчивой конфигурации или достижения оптимальной производительности может потребоваться изменить это значение или другие аспекты конфигурации вашей системы. Эти аспекты рассматриваются в разделе Прочность. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

full_page_writes (boolean)

Когда этот параметр включен, сервер QHB записывает в WAL все содержимое каждой страницы диска при первом изменении этой страницы после контрольной точки. Это необходимо, поскольку запись страницы, которая выполнялась во время сбоя операционной системы, может оказаться завершена только частично, что приводит к появлению на диске страницы, содержащей смесь старых и новых данных. Информации об изменениях на уровне строк, обычно хранящейся в WAL, будет недостаточно для полного воссоздания такой страницы при восстановлении после сбоя. Сохранение образа полной страницы гарантирует, что ее можно восстановить корректно, но ценой будет увеличение объема данных, которые должны быть записаны в WAL. (Поскольку воспроизведение WAL всегда начинается с контрольной точки, достаточно сделать это при первом изменении каждой страницы после контрольной точки. Поэтому одним из способов снижения затрат на запись полных страниц является увеличение интервалов контрольных точек.)

Выключение этого параметра ускоряет обычные операции, но может привести к неисправимому или незаметному повреждению данных после сбоя системы. Поскольку риски при этом схожи с рисками при выключении fsync, хотя и в меньшей степени, его следует выключать только при тех же обстоятельствах, которые рекомендованы для вышеприведенного параметра.

Выключение этого параметра не влияет на применение архивирования WAL для восстановления состояния на определенный момент времени (PITR) (см. раздел Непрерывное архивирование и восстановление на момент времени (PITR)).

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Значение по умолчанию — on (включено).

wal_log_hints (boolean)

Когда этот параметр имеет значение on (включен), сервер QHB записывает в WAL все содержимое каждой страницы диска при первом изменении этой страницы после контрольной точки, даже при второстепенных изменениях так называемых вспомогательных битов.

Если включен расчет контрольных сумм данных, изменения вспомогательных битов всегда регистрируются в WAL, и этот параметр игнорируется. С помощью этого параметра можно проверить, насколько больше информации будет запротоколировано в WAL, если в вашей базе данных будет включен расчет контрольных сумм данных.

Этот параметр можно задать только при запуске сервера. Значение по умолчанию — off (выключен).

wal_compression (boolean)

Когда этот параметр имеет значение on (включен), сервер QHB сжимает образ полной страницы, записанный в WAL, когда включен параметр full_page_writes включен или при создании базовой резервной копии. Сжатый образ страницы будет распакован во время воспроизведения WAL. Значение по умолчанию — off (выключен). Только суперпользователи могут изменять этот параметр.

Включение этого параметра может уменьшить объем WAL, не повышая риск неустранимого повреждения данных, но ценой некоторой дополнительной нагрузки на процессор, связанной со сжатием данных при записи в WAL и их разворачиванием при воспроизведении WAL.

wal_init_zero (boolean)

Если этот параметр имеет значение on (по умолчанию), вновь создаваемые файлы WAL заполняются нулями. В некоторых файловых системах благодаря этому пространство выделяется заранее, до того, как потребуется делать записи WAL. Однако в файловых системах, работающих по принципу копирования при записи (COW, Copy-On-Write) этот метод может не давать никаких преимуществ, поэтому данный параметр позволяет пропустить ненужную в таком случае операцию. Со значением off (выключен) при создании файла WAL записывается только последний байт, чтобы файл имел желаемый размер.

wal_recycle (boolean)

Если этот параметр имеет значение on (по умолчанию), файлы WAL переименовываются и используются повторно, что избавляет от необходимости создавать новые файлы. В файловых системах с COW, возможно, будет быстрее создать новые файлы, поэтому данный параметр позволяет выключить это поведение.

wal_buffers (integer)

Объем общей памяти, используемой для данных WAL, которые еще не были записаны на диск. Значение -1 (по умолчанию) задает размер, равный 1/32 (около 3%) от shared_buffers, но не меньше 64 КБ и не больше, чем размер одного сегмента WAL, обычно 16 МБ. Это значение можно задать вручную, если выбираемое автоматически слишком велико или мало, но любое положительное значение меньше 32 КБ будет восприниматься как 32 КБ. Если это значение указано без единиц измерения, оно считается заданным в блоках WAL, размер которых равен XLOG_BLCKSZ байтов (обычно это 8 КБ). Этот параметр можно задать только при запуске сервера.

Содержимое буферов WAL записывается на диск при каждом фиксировании транзакции, поэтому очень большие значения вряд ли обеспечат значительное преимущество. Однако значение как минимум в несколько мегабайт может повысить производительность при записи на загруженном сервере, где одновременно множество клиентов фиксируют транзакции. Автонастройка, заданная по умолчанию (-1), в большинстве случаев должна давать удовлетворительные результаты.

wal_writer_delay (integer)

Определяет, с какой периодичностью процесс записи WAL будет сбрасывать WAL на диск. После каждого сброса WAL он засыпает на время, заданное wal_writer_delay, но может пробудиться раньше, если произойдет асинхронное фиксирование транзакции. Если предыдущий сброс произошел раньше, чем закончился период, указанный в wal_writer_delay, и полученный после этого объем WAL не достиг значения wal_writer_flush_after, то WAL только передается ОС, но не сбрасывается на диск. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. Значение по умолчанию — 200 миллисекунд (200ms). Обратите внимание, что во многих системах эффективное разрешение таймера сна составляет 10 миллисекунд; если задать в wal_writer_delay значение, не кратное 10, на практике можно получить такой же результат, что и со следующим за ним значением, кратным 10. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

wal_writer_flush_after (integer)

Определяет, при каком объеме процесс записи WAL будет сбрасывать WAL на диск. Если предыдущий сброс произошел раньше, чем закончился период, указанный в wal_writer_delay, и полученный после этого объем WAL не достиг значения wal_writer_flush_after, то WAL только передается ОС, но не сбрасывается на диск. Если значение wal_writer_flush_after равно 0, то данные WAL всегда сбрасываются на диск немедленно. Если это значение указано без единиц измерения, оно считается заданным в блоках WAL, размер которых равен XLOG_BLCKSZ байтов (обычно это 8 КБ). Значение по умолчанию — 1 МБ. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

wal_after_error_sleep_delay (integer)

Определяет период в микросекундах, на который процесс сброса WAL останавливается после того, как произойдёт ошибка записи на диск. Большие значения этого периода (не более одной секунды) позволяют избежать чрезмерного заполнения журнала, но могут приводить к критическим задержкам работы высоконагруженных систем. Возможные значения - от 0 (отсутствие задержек) до 1000000 (1 секунда). Значение по умолчанию - 100, или 100 микросекунд. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Изменение значения потребует перезапуска сервера.

commit_delay (integer)

Параметр commit_delay добавляет паузу перед началом сброса данных WAL. Это может увеличить быстродействие при групповом фиксировании, позволяя зафиксировать большее число транзакций за один сброс WAL, если загрузка системы достаточно высока и за заданное время успевают подготовиться к фиксированию другие транзакции. Однако это также увеличивает задержку вплоть до commit_delay при каждом сбросе WAL. Поскольку в паузе нет смысла, если никакие другие транзакции не готовы к фиксированию, задержка происходит только в том случае, если на момент запуска сброса WAL активно не менее commit_siblings других транзакций. Кроме того, эти задержки не происходят при выключенном fsync. Если это значение указано без единиц измерения, оно считается заданным в микросекундах. По умолчанию значение commit_delay равно нулю (без задержки). Только суперпользователи могут изменять этот параметр.

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

commit_siblings (integer)

Минимальное число одновременных открытых транзакций, которое требуется для добавления задержки commit_delay. Чем больше это значение, тем выше вероятность того, что как минимум еще одна транзакция окажется готова к фиксированию за время задержки. По умолчанию это пять транзакций.

Контрольные точки

checkpoint_timeout (integer)

Максимальное время между автоматическими контрольными точками WAL. Если это значение указано без единиц измерения, оно считается заданным в секундах. Допустимый диапазон составляет от 30 секунд до одного дня. Значение по умолчанию — пять минут (5min). Увеличение этого параметра может привести к увеличению времени, необходимого для восстановления после сбоя. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

checkpoint_completion_target (floating point)

Задает целевое время завершения процедуры контрольной точки, как долю от общего времени между контрольными точками. Значение по умолчанию — 0.5. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

checkpoint_flush_after (integer)

Всякий раз, когда во время выполнения контрольной точки записывается больше заданного объема данных, сервер пытается заставить ОС произвести запись этих данных в нижележащее хранилище. Это ограничивает количество «грязных» данных в страничном кэше ядра, снижая вероятность зависаний при выполнении fsync в конце контрольной точки или когда ОС выполняет обратную запись большими пакетами в фоновом режиме. Зачастую это приводит к значительному снижению задержки транзакций, но также бывают случаи (особенно когда объем рабочей нагрузки больше shared_buffers, но меньше страничного кэша), когда производительность может снизиться. Этот параметр может действовать не на всех платформах. Если это значение указано без единиц измерения, оно считается заданным в блоках, размер которых равен BLCKSZ байтов (обычно это 8 КБ). Допустимый диапазон: от 0 (что выключает принудительную обратную запись) до 2 MB. Значение по умолчанию равно 256kB в Linux и 0 в других ОС. (Если BLCKSZ отличен от 8 КБ, значение по умолчанию и максимальное значение масштабируются пропорционально ему.) Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

checkpoint_warning (integer)

Записать сообщение в журнал сервера, если контрольные точки, вызванные заполнением файлов сегментов WAL, выполняются быстрее, чем через заданное время (что говорит о том, что нужно увеличить max_wal_size). Если это значение указано без единиц измерения, оно считается заданным в секундах. Значение по умолчанию — 30 секунд (30s). При нуле это предупреждение выключается. Если checkpoint_timeout меньше, чем checkpoint_warning, предупреждения генерироваться не будут. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

max_wal_size (integer)

Максимальный размер, до которого может вырасти WAL во время автоматических контрольных точек. Это мягкий предел; размер WAL может превышать max_wal_size при особых обстоятельствах, например, при большой нагрузке, сбое в archive_command или большом значении wal_keep_size. Если это значение указано без единиц измерения, оно считается заданным в мегабайтах. Значение по умолчанию — 1 ГБ. Увеличение этого параметра может привести к увеличению времени, необходимого для восстановления после сбоя. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

min_wal_size (integer)

Пока WAL занимает на диске меньше объема, заданного в этом параметре, старые файлы WAL в контрольных точках всегда перерабатываются для дальнейшего использования, а не удаляются. Это позволяет зарезервировать достаточно пространства для WAL, чтобы справиться с пиками его использования, например, при выполнении больших пакетных заданий. Если это значение указано без единиц измерения, оно считается заданным в мегабайтах. Значение по умолчанию — 80 МБ. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Архивирование

archive_mode (enum)

Когда параметр archive_mode включен, заполненные сегменты WAL отправляются в архивное хранилище с помощью команды archive_command. Помимо значения off, выключающего архивацию, есть еще два: on (включен) и always (всегда). В нормальных условиях между этими двумя режимами нет никакой разницы, но в режиме always архивирование WAL активно также во время восстановления из архива и при работе с резервным сервером. В режиме always все файлы, восстановленные из архива или переданные с потоковой репликацией, будут заархивированы (снова).

Переменные archive_mode и archive_command разделены, чтобы archive_command можно было изменять, не выходя из режима архивирования. Этот параметр можно задать только при запуске сервера. archive_mode нельзя включить, если в параметре wal_level задано значение minimal.

archive_command (string)

Это команда локальной оболочки, выполняемая для архивирования завершенного сегмента файла WAL. Любое вхождение %p в этой строке заменяется путем к файлу, который нужно заархивировать, а любое вхождение %f — только именем этого файла. (Путь задается относительно рабочего каталога сервера, т. е. каталога данных кластера.) Чтобы вставить в команду символ %, его нужно записать как %%. Важно, чтобы команда возвращала нулевой статус выхода, только если она завершается успешно. Подробную информацию см. в подразделе Настройка архивирования WAL.

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Он игнорируется, если параметр archive_mode не был включен при запуске сервера. Если значение archive_command — пустая строка (по умолчанию), а archive_mode включен, архивирование WAL временно выключается, но сервер продолжает накапливать файлы сегментов WAL в ожидании, что команда будет вскоре предоставлена. Если в качестве команды archive_command задать команду, которая ничего не делает, но возвращает значение true (успешное завершение), например /bin/true, архивирование по сути выключается, но при этом разрывается цепочка файлов WAL, необходимых для восстановления архива, поэтому такой вариант следует использовать только в исключительных случаях.

archive_timeout (integer)

Команда archive_command вызывается только для завершенных сегментов WAL. Поэтому, если ваш сервер генерирует небольшой трафик WAL (или запись данных чередуется с периодами простоя), от завершения транзакции до ее безопасного сохранения в архивном хранилище может пройти довольно много времени. Для ограничения времени существования неархивированных данных, можно задать значение archive_timeout, чтобы сервер периодически переключался на новый файл сегмента WAL. Когда этот параметр больше нуля, сервер будет переключаться на новый файл сегмента, если с момента последнего переключения на новый файл прошел заданный промежуток времени и наблюдалась какая-то активность базы данных, пусть даже это была одна контрольная точка (контрольные точки пропускаются, если нет активности базы данных). Обратите внимание, что архивные файлы, которые из-за принудительного переключения закрываются досрочно, все равно будут иметь тот же размер, что и полностью заполненные. Поэтому устанавливать для archive_timeout очень маленькое значение неразумно — это приведет к переполнению архивного хранилища. Обычно имеет смысл задавать значение около минуты. Если вы хотите, чтобы данные копировались с главного сервера быстрее, следует подумать о переходе с архивирования на потоковую репликацию. Если это значение указано без единиц измерения, оно считается заданным в секундах. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Восстановление из архива

В этом разделе описываются параметры, которые применяются только в процессе восстановления. Они должны быть сброшены для любой последующей операции восстановления.

Под «восстановлением» понимается как использование сервера в качестве резервного, так и выполнение целевого восстановления данных. Как правило, резервный сервер используется для обеспечения высокой доступности и/или масштабируемости чтения, тогда как целевое восстановление используется в случае потери данных.

Чтобы запустить сервер в режиме резервного, создайте в каталоге данных файл с именем standby.signal. Сервер перейдет к восстановлению, но не остановится по достижении конца заархивированного WAL, а продолжит пытаться осуществить восстановление дальше, подключившись для этого к передающему серверу, используя параметры в primary_conninfo, и/или получив новые сегменты WAL с помощью команды restore_command. Для данного режима представляют интерес параметры, описанные в этом разделе и подразделе Резервные серверы. Параметры, описанные в подразделе Целевая точка восстановления, также будут действовать, но в этом режиме они, как правило, бесполезны.

Чтобы запустить сервер в режиме целевого восстановления, создайте в каталоге данных файл с именем recovery.signal. В случае одновременного создания файлов standby.signal и recovery.signal приоритет имеет режим резервного сервера. Режим целевого восстановления завершается после полного воспроизведения заархивированного WAL или при достижении recovery_target (целевой точки). В этом режиме используются параметры, описанные в этом разделе и подразделе Целевая точка восстановления.

restore_command (string)

Команда локальной оболочки, выполняемая для извлечения заархивированного сегмента набора файлов WAL. Этот параметр необходим для восстановления из архива, но необязателен для потоковой репликации. Любое вхождение %f в этой строке заменяется именем файла, извлекаемого из архива, а любое вхождение %p — путем назначения на сервере. (Путь указывается относительно текущего рабочего каталога, т. е. каталога данных кластера.) Любое вхождение %r заменяется именем файла, содержащего последнюю действительную точку перезапуска. Это самый ранний файл, который необходимо хранить, чтобы обеспечить возможность запуска восстановления, поэтому эту информацию можно использовать для усечения архива до минимального размера, необходимого для поддержки перезапуска. %r обычно используется только в конфигурациях с теплым резервированием. Чтобы вставить в команду символ %, его нужно записать как %%.

Важно, чтобы команда возвращала нулевой статус выхода, только если она завершается успешно. У команды будут запрошены имена файлов, которых нет в архиве; в этом случае она должна возвращать ненулевой статус. Пример:

restore_command = 'cp /mnt/server/archivedir/%f "%p"'

Если команда была прервана сигналом (отличным от SIGTERM, который используется как часть отключения сервера базы данных) или ошибкой со стороны оболочки (например, если команда не найдена), то процесс восстановления будет остановлен и сервер не запустится.

Этот параметр можно задать только при запуске сервера.

archive_cleanup_command (string)

Этот необязательный параметр задает команду оболочки, которая будет выполняться при каждой точке перезапуска. Назначение параметра archive_cleanup_command — предоставить механизм для очистки от старых архивных файлов WAL, которые больше не нужны резервному серверу. Любое вхождение %r заменяется именем файла, содержащего последнюю действительную точку перезапуска. Это самый ранний файл, который необходимо хранить, чтобы обеспечить возможность запуска восстановления, поэтому все более старые файлы можно безопасно удалить. Эту информацию можно использовать для усечения архива до минимального размера, необходимого для поддержки перезапуска. Модуль qhb_archivecleanup часто используется в качестве archive_cleanup_command в конфигурациях с одним резервным сервером, например:

archive_cleanup_command = 'qhb-archivecleanup /mnt/server/archivedir %r'

Однако обратите внимание, что если несколько резервных серверов восстанавливаются из одного и того же архивного каталога, следует проследить за тем, чтобы файлы WAL удалялись только после того, как ими воспользуются все серверы. archive_cleanup_command часто используется в конфигурациях с теплым резервированием. Чтобы вставить в команду символ %, его нужно записать как %%.

Если команда возвращает ненулевой статус выхода, в журнал будет записано сообщение с предупреждением. Если же команда была прервана сигналом или ошибкой со стороны оболочки (например, если команда не найдена), возникнет фатальная ошибка.

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

recovery_end_command (string)

Этот параметр задает команду оболочки, которая будет выполнена только один раз в конце процесса восстановления. Этот параметр не является обязательным. Назначение параметра recovery_end_command — предоставить механизм очистки после репликации или восстановления. Любое вхождение %r заменяется именем файла, содержащего последнюю действительную точку перезапуска, аналогично параметру archive_cleanup_command.

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

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Целевая точка восстановления

По умолчанию восстановление производится вплоть до конца журнала WAL. Чтобы остановить процесс восстановления в более ранней точке, можно использовать один из следующих параметров: recovery_target, recovery_target_lsn, recovery_target_name, recovery_target_time или recovery_target_xid. Если в файле конфигурации указано более одного из них, возникнет ошибка. Эти параметры можно задать только при запуске сервера.

recovery_target = ’immediate’

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

Технически это строковый параметр, но в данный момент допустимым является только значение ’immediate’.

recovery_target_name (string)

Этот параметр задает именованную точку восстановления (созданную с помощью pg_create_restore_point()), до которой будет производиться восстановление.

recovery_target_time (timestamp)

Этот параметр задает метку времени, до которой будет производиться восстановление. Точная точка остановки также зависит от значения параметра recovery_target_inclusive.

Значение этого параметра задается в том же формате, что принимается типом данных timestamp with time zone, за исключением того, что в нем нельзя использовать аббревиатуру часового пояса (если только переменная timezone_abbreviations не была установлена в файле конфигурации выше). Поэтому рекомендуется задавать числовое смещение от UTC или записывать название часового пояса полностью, например Europe/Helsinki (но не EEST).

recovery_target_xid (string)

Этот параметр задает идентификатор транзакции, до которого будет производиться восстановление. Имейте в виду, что значения идентификаторов отражают последовательность именно старта транзакций, а выполняться они могут и в другом порядке. Будут восстановлены все транзакции, совершенные до указанной (и, возможно, включая ее). Точная точка остановки также зависит от значения параметра recovery_target_inclusive.

recovery_target_lsn (pg_lsn)

Этот параметр задает номер LSN позиции в журнале упреждающей записи, до которого будет производиться восстановление. Точная точка остановки также зависит от значения параметра recovery_target_inclusive. Этот параметр принимает значение системного типа данных pg_lsn.

Следующие параметры уточняют целевую точку восстановления и определяют, что будет происходить при ее достижении:

recovery_target_inclusive (boolean)

Указывает, следует ли останавливаться сразу после заданной целевой точки восстановления (on) или непосредственно перед ней (off). Применяется, когда заданы recovery_target_lsn, recovery_target_time или recovery_target_xid. Этот параметр определяет, будут ли восстановлены транзакции, у которых позиция в WAL (LSN), время фиксации или идентификатор в точности совпадают с соответствующим заданным значением. Значение по умолчанию — on.

recovery_target_timeline (string)

Указывает точную линию времени для восстановления. Значение этого параметра может задаваться числовым идентификатором временной шкалы или специальным значением. При значении current восстанавливается та линия времени, которая действовала при создании базовой резервной копии. При значении latest восстанавливается последняя линия времени, найденная в архиве, что полезно для резервного сервера. Значение по умолчанию — latest.

Обычно этот параметр требуется задавать только в сложных ситуациях повторного восстановления, когда необходимо вернуться к состоянию, которое само было достигнуто после восстановления на момент времени. Это рассматривается в подразделе Линии времени.

recovery_target_action (enum)

Указывает, какое действие должен предпринять сервер после достижения целевой точки восстановления. Вариант по умолчанию — pause, означающий, что восстановление будет приостановлено. Вариант promote означает, что процесс восстановления завершится и сервер начнет принимать подключения. Наконец, при варианте shutdown сервер остановится по достижении целевой точки восстановления.

Предполагается, что вариант pause позволит выполнить запросы к базе данных и проверить, является ли эта целевая точка восстановления наиболее желаемой. Для снятия с паузы можно воспользоваться функцией pg_wal_replay_resume() (см. таблицу Функции управления восстановлением), которая приведет к завершению восстановления. Если эта целевая точка не является желаемой, нужно выключить сервер, установить параметры на более позднюю цель и перезапустить сервер для продолжения восстановления.

Вариант shutdown полезен для получения готового экземпляра СУБД в желаемой точке воспроизведения. Данный экземпляр по-прежнему сможет воспроизводить дополнительные записи WAL (и на самом деле при следующем запуске должен будет воспроизводить записи WAL с момента последней контрольной точки).

Обратите внимание, что поскольку recovery.signal не удаляется, если в recovery_target_action задано значение shutdown, последующий запуск закончится немедленным завершением работы, если только не изменить конфигурацию или не удалить файл recovery.signal вручную.

Этот параметр не действует, если целевая точка восстановления не установлена. Если не включен режим hot_standby, вариант pause будет действовать так же, как shutdown.

Репликация

Следующие параметры управляют поведением встроенной функции потоковой репликации. При этом одни серверы становятся главными, другие — резервными. Главные серверы могут передавать данные, тогда как резервные всегда принимают реплицированные данные. Однако когда настроена каскадная репликация резервные серверы могут не только получать, но и передавать данные. Эти параметры в основном относятся к передающим и резервным серверам, хотя некоторые из них имеют смысл только для главного сервера. При необходимости все эти параметры могут быть разными в рамках одного кластера.

Передающие серверы

Эти параметры можно установить на любом сервере, который должен передавать данные репликации на один или несколько резервных серверов. Главный сервер всегда является передающим, поэтому на нем эти параметры должны задаваться всегда. Роль и значение этих параметров не меняются после того, как резервный сервер становится главным.

max_wal_senders (integer)

Задает максимально допустимое число одновременных подключений резервных серверов или клиентов потокового базового резервного копирования (т. е. максимальное число одновременно работающих процессов передачи WAL). Значение по умолчанию — 10. При значении 0 репликация выключается. При неожиданном отключении клиента потоковой передачи потерянный слот подключения может игнорироваться вплоть до истечения тайм-аута, поэтому значение этого параметра должно быть немного выше максимально допустимого числа ожидаемых клиентов, чтобы отключившиеся клиенты могли немедленно восстановить соединение. Этот параметр можно задать только при запуске сервера. Кроме того, чтобы к данному серверу могли подключаться резервные, уровень параметра wal_level должен быть replica или выше.

При запуске резервного сервера следует установить для этого параметра значение большее или равное значению на главном сервере. В противном случае на резервном сервере не будут разрешены запросы.

max_replication_slots (integer)

Задает максимальное число слотов репликации, которое сможет поддерживать сервер. Значение по умолчанию — 10. Этот параметр можно задать только при запуске сервера. Если заданное значение будет меньше, чем число существующих в настоящее время слотов репликации, сервер не запустится. Кроме того, чтобы к данному серверу могли подключаться резервные, уровень параметра wal_level должен быть replica или выше.

На стороне подписчика определяет, сколько источников репликации можно отслеживать одновременно, по сути ограничивая количество подписок на логическую репликацию, которые можно создать на сервере. Если заданное значение будет меньше, чем текущее число отслеживаемых источников репликации (показываемое в pg_replication_origin_status, а не в pg_replication_origin), сервер не запустится.

wal_keep_size (integer)

Задает минимальный объем прошлых сегментов файла журнала, который будет сохраняться в каталоге pg_wal, на случай, если резервному серверу понадобится выбрать их для потоковой репликации. Если резервный сервер, подключенный к передающему, отстает больше чем на wal_keep_size мегабайт, передающий сервер может удалить сегменты WAL, все еще нужные резервному, и в этом случае соединение репликации прервется. В результате этого впоследствии также будут прерваны зависимые соединения. (Однако резервный сервер может восстановиться, выбрав этот сегмент из архива, если осуществляется архивация WAL.)

Этот параметр задает только минимальный объем сегментов, который будет сохраняться в каталоге pg_wal; для архивации WAL или для восстановления с момента контрольной точки системе может потребоваться сохранить больше сегментов. Если wal_keep_size равен нулю (это значение по умолчанию), система не сохраняет никакие дополнительные сегменты для резервных серверов, поэтому число доступных для них старых сегментов WAL зависит от положения предыдущей контрольной точки и состояния архивации WAL. Если это значение задается без единиц измерения, оно считается заданным в мегабайтах. Задать этот параметр можно только в файле qhb.conf или в командной строке при запуске сервера.

max_slot_wal_keep_size (integer)

Задает максимальный объем файлов WAL, который может оставаться в каталоге pg_wal для слотов репликации после выполнения контрольной точки. Со значением max_slot_wal_keep_size, равным -1 (по умолчанию), для слотов репликации может сохраняться неограниченный объем файлов WAL. При неотрицательном значении, если позиция restart_lsn для слота репликации отстает от текущего LSN более чем на заданный объем, использующий этот слот ведомый сервер может лишиться возможности продолжить репликацию вследствие удаления нужных ему файлов WAL. Доступность WAL для слотов репликации показывается в представлении pg_replication_slots.

wal_sender_timeout (integer)

Задает промежуток времени, по истечении которого прерываются неактивные соединения репликации. Это помогает передающему серверу обнаружить сбой резервного сервера или разрывы сети. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. Значение по умолчанию — 60 секунд. При значении 0 механизм тайм-аута выключается.

Если узлы кластера распределены по нескольким географическим местоположениям, его управление можно сделать более гибким, используя разные значения в разных расположениях. Маленькие значения помогают быстрее обнаружить потерю резервного сервера, подключенного через сеть с низкими задержками, а большие позволяют точнее определить состояние удаленного резервного сервера, соединение с которым характеризуется большими задержками.

track_commit_timestamp (boolean)

Включает запись времени фиксации транзакций. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Значение по умолчанию — off (выключено).

Главный сервер

Эти параметры можно задать на главном/основном сервере, который должен передавать данные репликации на один или несколько резервных серверов. Обратите внимание, что помимо этих параметров на главном сервере должен быть надлежащим образом установлен wal_level, а также может быть включено архивирование WAL (см. подраздел Архивирование). Значения этих параметров на резервных серверах не важны, хотя их можно задать там заранее, на случай, если понадобится сделать резервный сервер главным.

synchronous_standby_names (string)

Задает список резервных серверов, которые могут поддерживать синхронную репликацию. Активных синхронных резервных серверов может быть один или несколько; транзакциям, ожидающим фиксации, будет позволено завершиться только после того, как эти резервные серверы подтвердят получение своих данных. Синхронными резервными серверами будут те, имена которых указаны в этом списке и которые в настоящее время подключены и передают данные в режиме реального времени (что показывает характеристика streaming в представлении pg_stat_replication). Указание более одного синхронного резервного сервера может обеспечить очень высокую доступность и защиту от потери данных.

Именем резервного сервера в этом контексте является значение строки application_name резервного сервера, задаваемое в его свойствах подключения. В случае осуществления физической репликации его следует задавать в строке primary_conninfo; по умолчанию это значение параметра cluster_name, если оно задано, иначе — walreceiver. Для логической репликации его можно задать в свойствах подключения подписки; по умолчанию это имя подписки. Чтобы узнать, как задать его для других потребителей потока репликации, ознакомьтесь с их документацией.

Этот параметр задает список резервных серверов, используя любую из следующих форм:

[FIRST] число_синхронных ( имя_резервного [, ...] )
ANY число_синхронных ( имя_резервного [, ...] )
имя_резервного [, ...]

где число_синхронных — это число синхронных резервных серверов, от которых транзакции должны дождаться ответов, а имя_резервного — это имя резервного сервера. FIRST и ANY указывают способ выбора синхронных резервных из перечисленных серверов.

Ключевое слово FIRST в сочетании с числом_синхронных задает синхронную репликацию согласно приоритетам, и транзакции не фиксируются, пока их записи WAL не будут реплицированы на число_синхронных синхронных резервных серверов, выбранных, исходя из их приоритетности. Например, если задать FIRST 3 (s1, s2, s3, s4), то каждая фиксация будет происходить только после получения ответов от трех наиболее приоритетных из резервных серверов s1, s2, s3 и s4. Резервные серверы, имена которых стоят в списке выше, имеют более высокий приоритет и будут рассматриваться как синхронные. Серверы, следующие в списке за ними, считаются потенциальными синхронными. Если какой-либо из текущих синхронных резервных серверов по какой-то причине отключится, он будет немедленно заменен следующим резервным сервером с наивысшим приоритетом. Ключевое слово FIRST является необязательным.

Ключевое слово ANY в сочетании с числом_синхронных задает синхронную репликацию согласно кворума, и транзакции не фиксируются, пока их записи WAL не будут реплицированы как минимум на число_синхронных перечисленных резервных серверов. Например, если задать ANY 3 (s1, s2, s3, s4), то каждая фиксация будет происходить сразу после получения ответов от как минимум трех из резервных серверов s1, s2, s3 и s4.

Ключевые слова FIRST и ANY нечувствительны к регистру. Если эти же слова используются в качестве имени резервного сервера, его имя_резервного следует заключить в кавычки.

Третья форма является устаревшей, но до сих пор поддерживается QHB. Она равнозначна первой форме с FIRST и числом_синхронных, равным 1. Например, FIRST 1 (s1, s2) и s1, s2 означают одно и то же: в качестве синхронного резервного сервера выбирается либо s1, либо s2.

Специальному элементу * соответствует имя любого резервного сервера.

Не существует механизма, обеспечивающего уникальность имен резервных серверов. В случае дублирования более приоритетным будет считаться один из резервных серверов с подходящим именем, хотя какой именно, не определено.

Примечание
Каждое имя_резервного должно задаваться в форме действительного идентификатора SQL, кроме *. При необходимости его можно заключать в кавычки. Но обратите внимание, что имена_резервного сравниваются с именами приложений резервных серверов без учета регистра, независимо от того, заключены они в кавычки или нет.

Если не задать в этом параметре имена синхронных резервных серверов, то синхронная репликация не включится и фиксируемые транзакции не будут ждать репликации. Это конфигурация по умолчанию. Даже когда синхронная репликация включена, отдельные транзакции можно настроить так, чтобы они не ожидали репликации, задав для параметра synchronous_commit значение local или off.

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

vacuum_defer_cleanup_age (integer)

Задает число транзакций, на которое будут откладывать очистку неиспользуемых версий строк команда VACUUM и изменения HOT. По умолчанию это число равно нулю, то есть неиспользуемые версии строк могут быть удалены сразу, как только перестанут быть видимыми для любой открытой транзакции. Можно установить ненулевое значение на основном сервере, который работает с серверами горячего резерва. Это дает больше времени для выполнения запросов на резервном сервере без возникновения конфликтов из-за ранней очистки строк. Тем не менее, поскольку это значение определяется числом записывающих транзакций, выполняющихся на основном сервере, трудно предсказать, сколько дополнительного времени получат резервные серверы. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

В качестве альтернативы использованию этого параметра следует также рассмотреть возможность настройки hot_standby_feedback на резервных серверах.

Этот параметр не предотвращает очистку неиспользуемых строк, которые достигли возраста, заданного параметром old_snapshot_threshold.

Резервные серверы

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

primary_conninfo (string)

Задает строку подключения, которая будет использоваться резервным сервером для соединения с передающим. Если в этой строке не указан никакой параметр, то проверяется соответствующая переменная среды. Если переменная среды также не установлена, используются значения по умолчанию.

В строке подключения должно задаваться имя хоста (или адрес) передающего сервера, а также номер порта, если он не совпадает с номером по умолчанию для резервного сервера. Также указывается имя пользователя, соответствующее роли с требуемыми правами на передающем сервере. Кроме того, если передающий сервер требует аутентификации по паролю, следует дополнительно задать пароль. Его можно указать в строке primary_conninfo или в отдельном файле ~/.pgpass на резервном сервере (для базы данных с именем replication). Имя базы данных в строке primary_conninfo указывать не нужно.

Этот параметр можно задать только при запуске сервера. Он действует, только если сервер работает в резервном режиме.

primary_slot_name (string)

Дополнительно задает существующий слот репликации, который будет использоваться при подключении к передающему серверу посредством потоковой репликации для управления удалением ресурсов в вышестоящем узле. Этот параметр можно задать только при запуске сервера. Он действует, только если задан параметр primary_conninfo.

promote_trigger_file (string)

Задает триггерный файл, присутствие которого завершает восстановление на резервном сервере. Даже если это значение не задано, резервный сервер все равно можно назначить главным с помощью команды qhb_ctl promote или вызова функции pg_promote. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

hot_standby (boolean)

Определяет, можно ли будет подключаться к серверу и выполнять запросы в процессе восстановления. Значение по умолчанию — on (подключения разрешены). Этот параметр можно задать только при запуске сервера. Он действует только во время восстановления архива или в режиме резервного сервера.

max_standby_archive_delay (integer)

В режиме горячего резерва этот параметр определяет, как долго резервный сервер должен ждать, прежде чем отменять свои запросы, конфликтующие с готовыми к применению записями WAL. Задержка max_standby_archive_delay применяется, когда данные WAL считываются из архива (то есть не являются текущими). Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. Значение по умолчанию — 30 секунд. При значении -1 клиент может ждать завершения конфликтующих запросов неограниченное время. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Обратите внимание, что max_standby_archive_delay — это не максимальное время, которое отводится на выполнение каждому запросу; скорее это максимальное общее время, за которое должны применяться данные из одного сегмента WAL. Таким образом, если один запрос привел к значительной задержке, у последующих конфликтующих запросов отсрочка будет гораздо меньше.

max_standby_streaming_delay (integer)

В режиме горячего резерва этот параметр определяет, как долго резервный сервер должен ждать, прежде чем отменять свои запросы, конфликтующие с готовыми к применению записями WAL. Задержка max_standby_streaming_delay применяется, когда данные WAL поступают при потоковой репликации. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. Значение по умолчанию — 30 секунд. При значении -1 клиент может ждать завершения конфликтующих запросов неограниченное время. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Обратите внимание, что max_standby_streaming_delay — это не максимальное время, которое отводится на выполнение каждому запросу; скорее это максимальное общее время, за которое должны применяться данные из одного сегмента WAL после получения от главного сервера. Таким образом, если один запрос привел к значительной задержке, у последующих конфликтующих запросов будет гораздо меньше времени, пока резервный сервер снова не догонит главный.

wal_receiver_status_interval (integer)

Задает минимальную частоту, с которой процесс, принимающий WAL на резервном сервере, будет отправлять информацию о ходе выполнения репликации на главный или вышестоящий резервный сервер, где эту информацию можно будет увидеть в представлении pg_stat_replication. В сообщении резервного сервера будут передаваться сведения о последней позиции, которую он записал в журнал предзаписи, о последней позиции, которую он сохранил на диск, и о последней позиции, которую он применил. Значение этого параметра — максимальный интервал между отчетами. Сообщения отправляются при каждом изменении позиции записи или сохранения или, по крайней мере, с частотой, указанной в этом параметре. Таким образом, последняя переданная позиция может немного отставать от фактической. Если это значение указано без единиц измерения, оно считается заданным в секундах. Значение по умолчанию — 10 секунд. При нулевом значении передача сообщений о ходе выполнения репликации полностью выключается. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

hot_standby_feedback (boolean)

Определяет, будет ли сервер горячего резерва передавать главному или вышестоящему резервному серверу отчет о запросах, выполняющихся на нем в данный момент. Этот параметр позволяет исключить необходимость отмены запросов, вызванную очисткой записей, но при некоторых рабочих нагрузках может вызвать раздувание базы данных на главном сервере. Сообщения с отчетом о запросах будут отправляться не чаще, чем один раз за интервал, задаваемый параметром wal_receiver_status_interval. Значение по умолчанию — off (выключено). Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Если используется каскадная репликация, сообщения о запросах передаются выше, пока в конце концов не достигнут главного сервера. Промежуточные резервные серверы эту информацию больше никак не используют.

Этот параметр не переопределяет поведение old_snapshot_threshold на главном сервере; снимок на резервном сервере, возраст которого превышает порог, заданный на главном, может стать недействительным, что приведет к отмене транзакций на резервном сервере. Это связано с тем, что параметр old_snapshot_threshold предназначен для указания абсолютного ограничения времени, в течение которого могут накапливаться неиспользуемые строки, которое иначе было бы нарушено из-за конфигурации резервного сервера.

wal_receiver_timeout (integer)

Задает промежуток времени, по истечении которого прерываются неактивные соединения репликации. Это позволяет принимающему резервному серверу обнаружить сбой главного сервера или разрыв сети. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. Значение по умолчанию — 60 секунд. При значении 0 механизм тайм-аута выключается. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

wal_retrieve_retry_interval (integer)

Определяет, как долго резервный сервер должен ждать поступления данных WAL из любых источников (потоковая репликация, локальный pg_wal или архив WAL), прежде чем снова попытаться получить данные WAL. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. Значение по умолчанию — 5 секунд. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Этот параметр полезен в конфигурациях, где узлу в процессе восстановления необходимо контролировать время ожидания новых данных WAL. Например, при восстановлении архива можно ускорить реакцию при обнаружении нового файла WAL, уменьшив значение этого параметра. В системе с низкой активностью WAL увеличение этого параметра уменьшает количество запросов, необходимых для доступа к архивам WAL, что полезно, например, в облачных средах, где учитывается число обращений к инфраструктуре.

recovery_min_apply_delay (integer)

По умолчанию резервный сервер восстанавливает записи WAL с передающего сервера в максимально сжатые сроки. Может быть полезно задавать задержку при копировании этих данных, получая тем самым возможность исправить ошибки, связанные с потерей данных. Этот параметр позволяет отложить восстановление на указанное количество времени. Например, если установить для этого параметра значение 5min, резервный сервер будет воспроизводить фиксацию каждой транзакции не раньше, чем через 5 минут (согласно его системным часам) после времени фиксации, сообщенного главным сервером. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. Значение по умолчанию — ноль, то есть задержка не добавляется.

Бывает так, что задержка репликации между серверами превышает значение этого параметра, и в этом случае дополнительная задержка не добавляется. Обратите внимание, что задержка рассчитывается как разница между отметкой времени, записанной в WAL на главном сервере, и текущим временем на резервном сервере. Задержки передачи из-за запаздывания в сети или каскадной репликации могут значительно сократить фактическое время ожидания. Если системные часы на главном и резервном серверах не синхронизированы, это может привести к применению записей ранее ожидаемого; но это не столь важно, потому что полезные значения этого параметра намного выше обычной разницы во времени между серверами.

Задержка происходит только в записях WAL для фиксаций транзакций. Все прочие записи воспроизводятся максимально быстро, что не представляет проблемы, поскольку правила видимости MVCC гарантируют, что эффекты этих записей не будут видны до применения соответствующей записи о фиксации транзакции.

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

Этот параметр предназначен для использования с развертываниями потоковой репликации; однако если он задан, он будет учитываться во всех случаях, кроме восстановления после сбоя. Заданная этим параметром задержка будет влиять и на параметр hot_standby_feedback, что может привести к раздуванию базы данных на главном сервере; использовать эти параметры одновременно следует
с осторожностью.

Предупреждение!

Этот параметр влияет на синхронную репликацию, когда для synchronous_commit задано значение remote_apply; каждой COMMIT придется ожидать применения.

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Подписчики

Эти параметры управляют поведением подписчика логической репликации. На публикующем сервере их значения не играют роли.

Обратите внимание, что параметры конфигурации wal_receiver_timeout, wal_receiver_status_interval и wal_retrieve_retry_interval влияют на рабочие процессы логической репликации.

max_logical_replication_workers (int)

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

Рабочие процессы логической репликации берутся из пула, заданного параметром max_worker_processes.

Значение по умолчанию — 4. Этот параметр можно задать только при запуске сервера.

max_sync_workers_per_subscription (integer)

Максимально допустимое число рабочих процессов, выполняющих синхронизацию, на одну подписку. Этот параметр управляет степенью параллелизма копии исходных данных во время инициализации подписки или при добавлении новых таблиц.

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

Рабочие процессы синхронизации берутся из пула, заданного параметром max_logical_replication_workers*.

Значение по умолчанию — 2. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Планирование запросов

Конфигурация метода планирования

Эти параметры конфигурации предоставляют грубый метод воздействия на планы запросов, выбранные оптимизатором запросов. Если выбранный оптимизатором стандартный план конкретного запроса, оказался не оптимален, в качестве временного решения можно использовать один из этих параметров конфигурации, чтобы заставить оптимизатор выбрать другой план. К более оптимальным способам улучшения качества выбираемых оптимизатором планов относятся корректировка констант стоимости для планировщика (см. подраздел Константы стоимости для планировщика), выполнение ANALYZE вручную, увеличение значения параметра конфигурации default_statistics_target, а также увеличение объема статистики, собираемой для отдельных столбцов, с помощью команды ALTER TABLE SET STATISTICS.

enable_bitmapscan (boolean)

Включает или выключает использование планировщиком запросов планов сканирования по битовой карте. Значение по умолчанию — on (включено).

enable_gathermerge (boolean)

Включает или выключает использование планировщиком запросов планов слияния посредством сбора. Значение по умолчанию — on (включено).

enable_hashagg (boolean)

Включает или выключает использование планировщиком запросов планов агрегирования по хешу. Значение по умолчанию — on (включено).

enable_hashjoin (boolean)

Включает или выключает использование планировщиком запросов планов соединения по хешу. Значение по умолчанию — on (включено).

enable_indexscan (boolean)

Включает или выключает использование планировщиком запросов планов сканирования по индексу. Значение по умолчанию — on (включено).

enable_indexonlyscan (boolean)

Включает или выключает использование планировщиком запросов планов сканирования только по индексу (см. раздел Сканирование только по индексу и покрывающие индексы). Значение по умолчанию — on (включено).

enable_material (boolean)

Включает или выключает использование планировщиком запросов материализации. Полностью убрать материализацию невозможно, но выключение этого параметра не позволит планировщику вставлять узлы материализации, за исключением случаев, когда это требуется для правильности. Значение по умолчанию — on (включено).

enable_mergejoin (boolean)

Включает или выключает использование планировщиком запросов планов соединения слиянием. Значение по умолчанию — on (включено).

enable_nestloop (boolean)

Включает или выключает использование планировщиком запросов планов соединения с вложенными циклами. Полностью убрать соединения с вложенными циклами невозможно, но выключение этого параметра не позволит планировщику использовать этот метод, если доступны другие. Значение по умолчанию — on (включено).

enable_parallel_append (boolean)

Включает или выключает использование планировщиком запросов планов с распараллеливанием добавления данных. Значение по умолчанию — on (включено).

enable_parallel_hash (boolean)

Включает или выключает использование в планировщике запросов планов соединения по хешу с распараллеливанием хеширования. Не действует, если не включены планы соединения по хешу. Значение по умолчанию — on (включено).

enable_partition_pruning (boolean)

Включает или выключает в планировщике запросов возможность исключать партиции партиционированной таблиц из планов запросов. Также контролирует способность планировщика генерировать планы запросов, позволяющие исполнителю пропускать (игнорировать) партиции во время выполнения запроса. Значение по умолчанию — on (включено). Подробную информацию см. в подразделе Сокращение партиций.

enable_partitionwise_join (boolean)

Включает или выключает использование планировщиком запросов соединения с учетом партиционирования, что позволяет выполнять соединение партиционированных таблиц путем соединения соответствующих партиций. В настоящее время соединение с учетом партиционирования применяется только в том случае, если в условия соединения входят все ключи разбиения, которые при этом должны быть одного типа данных и иметь точно совпадающие наборы дочерних партиций. Поскольку для планирования соединения с учетом партиционирования может понадобиться гораздо больше процессорного времени и памяти, по умолчанию этот параметр выключен (off).

enable_partitionwise_aggregate (boolean)

Включает или выключает использование планировщиком запросов группировки или агрегирования с учетом партиционирования, что позволяет выполнять группировку или агрегирование в партиционированных таблицах, по отдельности для каждой партиции. Если предложение GROUP BY не включает ключи разбиения, на уровне партиций может быть выполнено только частичное агрегирование, а позже должна быть выполнена итоговая обработка. Поскольку для планирования группировки или агрегирования с учетом партиционирования может понадобиться гораздо больше процессорного времени и памяти, по умолчанию этот параметр выключен (off).

enable_seqscan (boolean)

Включает или выключает использование планировщиком запросов планов последовательного сканирования. Полностью убрать последовательное сканирование невозможно, но выключение этого параметра не позволит планировщику использовать этот метод, если доступны другие. Значение по умолчанию — on (включено).

enable_sort (boolean)

Включает или выключает использование планировщиком запросов шагов с явной сортировкой. Полностью убрать явную сортировку невозможно, но выключение этого параметра не позволит планировщику использовать этот метод, если доступны другие. Значение по умолчанию — on (включено).

enable_tidscan (boolean)

Включает или выключает использование планировщиком запросов планов сканирования TID. Значение по умолчанию — on (включено).

Константы стоимости для планировщика

Переменные стоимости, описанные в этом разделе, измеряются в произвольном масштабе. Важны только их относительные значения, поэтому умножение или деление всех переменных на один коэффициент не приведет к изменению выбора планировщика. По умолчанию эти переменные стоимости определяются исходя из стоимости последовательных выборок страниц; то есть seq_page_cost обычно задается равной 1.0 а другие переменные стоимости определяются относительно нее. При желании можно использовать другой масштаб, например, фактическое время выполнения запросов в миллисекундах на конкретной машине.

Примечание
К сожалению, нет четко определенного метода определения идеальных значений для переменных стоимости. Лучше всего рассматривать их как средние показатели для всего набора запросов, которые получит конкретная СУБД. Это означает, что менять их на основании всего нескольких экспериментов очень рискованно.

seq_page_cost (floating point)

Задает для планировщика стоимость выборки одной страницы с диска, которая выполняется в серии последовательных выборок. Значение по умолчанию — 1.0. Это значение можно переопределить для таблиц и индексов в определенном табличном пространстве, установив одноименный параметр табличного пространства (см. ALTER TABLESPACE).

random_page_cost (floating point)

Задает для планировщика стоимость непоследовательной выборки одной страницы с диска. Значение по умолчанию — 4.0. Это значение можно переопределить для таблиц и индексов в определенном табличном пространстве, установив одноименный параметр табличного пространства (см. ALTER TABLESPACE).

Уменьшение этого значения относительно seq_page_cost приведет к тому, что система предпочтет сканирование по индексу; при его увеличении сканирование по индексу станет выглядеть более затратным. Также оба эти значения можно увеличивать или уменьшать одновременно, тем самым изменяя стоимость операций ввода/вывода на диск относительно стоимости процессорных операций, которая определяется следующими параметрами.

Произвольный доступ к механическому дисковому хранилищу обычно гораздо дороже (более чем в 4 раза), чем последовательный. Однако по умолчанию используется более низкое значение (4.0), поскольку предполагается, что большая часть данных при произвольных обращениях к диску, например при чтении индекса, окажется в кэше. Значение по умолчанию можно рассматривать как моделирование ситуации, когда произвольный доступ в 40 раз медленнее последовательного, и при этом ожидается, что 90% операций произвольного чтения будут кэшироваться.

Если вы считаете, что для вашей рабочей нагрузки 90% является неверным допущением, вы можете увеличить параметр random_page_cost, чтобы он лучше отражал реальную стоимость произвольного чтения из хранилища. Соответственно, если ваши данные могут полностью поместиться в кэше, например, когда объем базы данных меньше общего объема памяти сервера, целесообразно будет уменьшить значение random_page_cost. Хранилище, у которого стоимость произвольного чтения низкая относительно последовательного, например, у твердотельных накопителей, также лучше будет смоделировать с более низким значением random_page_cost, например, 1.1.

Примечание
Хотя система позволит вам установить random_page_cost меньше, чем seq_page_cost, это лишено физического смысла. Однако сделать их равными имеет смысл, если база данных полностью кэшируется в ОЗУ, поскольку в этом случае непоследовательное обращение к страницам не вызовет дополнительных издержек. Кроме того, в сильно кэшированной базе данных оба этих параметра необходимо снизить относительно параметров ЦП, поскольку стоимость извлечения страницы, уже находящейся в ОЗУ, намного меньше, чем обычно.

cpu_tuple_cost (floating point)

Задает для планировщика стоимость обработки каждой строки при выполнении запроса. Значение по умолчанию — 0.01.

cpu_index_tuple_cost (floating point)

Задает для планировщика стоимость обработки каждой записи индекса при сканировании индекса. Значение по умолчанию — 0.005.

cpu_operator_cost (floating point)

Задает для планировщика стоимость обработки каждой команды или функции, выполняемых при запросе. Значение по умолчанию — 0.0025.

parallel_setup_cost (floating point)

Задает для планировщика стоимость запуска параллельных рабочих процессов. Значение по умолчанию — 1000.

parallel_tuple_cost (floating point)

Задает для планировщика стоимость переноса одного кортежа из параллельного рабочего процесса в другой процесс. Значение по умолчанию — 0.1.

min_parallel_table_scan_size (integer)

Задает минимальный объем данных таблицы, подлежащий сканированию, при котором можно применить параллельное сканирование. При параллельном последовательном сканировании объем сканируемых данных таблицы всегда равен объему таблицы, но при использовании индексов этот объем обычно бывает меньше. Если это значение указано без единиц измерения, оно считается заданным в блоках, размер которых равен BLCKSZ байтов (обычно это 8 КБ). Значение по умолчанию — 8 мегабайт (8MB).

min_parallel_index_scan_size (integer)

Задает минимальный объем данных индекса, подлежащий сканированию, при котором можно применить параллельное сканирование. Обратите внимание, что параллельное сканирование индекса обычно не затрагивает весь индекс; это число страниц, которое, по мнению планировщика, будет действительно затронуто при сканировании. Если это значение указано без единиц измерения, оно считается заданным в блоках, размер которых равен BLCKSZ байтов (обычно это 8 КБ). Значение по умолчанию — 512 килобайт (512kB).

effective_cache_size (integer)

Определяет предположение планировщика об эффективном размере дискового кэша, доступном для одного запроса. Это предположение учитывается при оценке стоимости использования индекса; чем выше это значение, тем больше вероятность того, что будет применено сканирование по индексу, чем ниже, тем больше вероятность того, что будет применено последовательное сканирование. При установке этого параметра следует учитывать как объем общих буферов QHB, так и процент дискового кэша ядра, который будут занимать файлы данных QHB, хотя некоторые данные могут находиться и там, и там. Также примите во внимание ожидаемое количество параллельных запросов к разным таблицам, так как им придется совместно использовать доступное пространство. Этот параметр не влияет на размер общей памяти, выделяемой QHB, и не задает размер резервируемого дискового кэша ядра; он используется только для ориентировочной оценки. Кроме того, система не предполагает, что данные могут оставаться в дисковом кэше между запросами. Если это значение указано без единиц измерения, оно считается заданным в блоках, размер которых равен BLCKSZ байтов (обычно это 8 КБ). Значение по умолчанию — 4 гигабайта (4GB). (Если BLCKSZ не равен 8 КБ, значение по умолчанию масштабируется пропорционально ему).

jit_above_cost (floating point)

Задает предел стоимости запроса, при превышении которого активируется JIT-компиляция, если она включена. Применение JIT занимает время при планировании, но может ускорить выполнение запроса. При значении -1 JIT-компиляция выключается. Значение по умолчанию — 100000.

jit_inline_above_cost (floating point)

Задает предел стоимости запроса, при превышении которого в процессе JIT-компиляции возможно встраивание функций и операторов. Встраивание увеличивает время планирования, но может ускорить выполнение. Присваивать этому параметру значение меньше, чем jit_above_cost, не имеет смысла. При значении -1 встраивание выключается. Значение по умолчанию — 500000.

jit_optimize_above_cost (floating point)

Задает предел стоимости запроса, при превышении которого в процессе JIT-компиляции будут применяться дорогостоящие оптимизации. Такие оптимизации увеличивают время планирования, но могут ускорить выполнение. Присваивать этому параметру значение меньше, чем jit_above_cost, не имеет смысла, а значение больше, чем jit_inline_above_cost, вряд ли даст положительный эффект. При значении -1 дорогостоящие оптимизации выключаются. Значение по умолчанию — 500000.

Генетический оптимизатор запросов

Генетический оптимизатор запросов (Genetic Query Optimizer, GEQO) — это алгоритм, который выполняет планирование запросов с помощью эвристического поиска. Это сокращает время планирования сложных запросов (тех, в которых соединяется множество отношений) за счет создания планов, иногда уступающих по качеству планам, выявляемым обычным алгоритмом полного перебора.

geqo (boolean)

Включает или выключает генетическую оптимизацию запросов. По умолчанию она включена. В производственной среде ее лучше не выключать; более детальное управление GEQO дает переменная geqo_threshold.

geqo_threshold (integer)

Задает минимальное число элементов во FROM, при котором для планирования запросов будет использоваться генетический оптимизатор запросов. (Обратите внимание, что конструкция FULL OUTER JOIN считается одним элементом FROM). Значение по умолчанию — 12. Для более простых запросов, как правило, лучше использовать обычный планировщик, выполняющий полный перебор, но для запросов со многими таблицами полный перебор занимает слишком много времени, зачастую больше, чем тратится на выполнение неоптимального плана. Таким образом, ограничение размера запроса является удобным способом управления GEQO.

geqo_effort (integer)

Управляет балансом между временем планирования и качеством плана запроса в GEQO. Эта переменная должна задаваться целым числом в диапазоне от 1 до 10. Значение по умолчанию — пять. Значения выше этого увеличивают время, затрачиваемое на планирование запросов, но при этом повышают вероятность выбора эффективного плана запросов.

В действительности параметр geqo_effort сам по себе ничего не делает; он используется только для вычисления значений по умолчанию для других переменных, влияющих на поведение GEQO (они описаны ниже). При желании эти параметры можно задать вручную.

geqo_pool_size (integer)

Управляет размером пула, используемого GEQO, то есть числом особей в генетической популяции. Это число должно быть не меньше двух, а полезные значения обычно лежат в диапазоне от 100 до 1000. Если оно равно нулю (значение по умолчанию), то подходящее число выбирается, исходя из значения geqo_effort и количества таблиц в запросе.

geqo_generations (integer)

Управляет количеством поколений, используемых GEQO, то есть числом итераций этого алгоритма. Это число должно быть не меньше одного, а полезные значения лежат в том же диапазоне, что и размер пула. Если оно равно нулю (значение по умолчанию), то подходящее число выбирается, исходя из значения geqo_pool_size.

geqo_selection_bias (floating point)

Управляет смещением выбора, используемым GEQO. Смещение выбора — это избирательное давление в популяции. Допустимые значения лежат в диапазоне от 1.50 до 2.00 (это значение по умолчанию).

geqo_seed (floating point)

Управляет начальным значением генератора случайных чисел, используемого GEQO для выбора случайных путей в пространстве поиска порядка соединений. Значение может варьироваться от нуля (по умолчанию) до единицы. Изменение значения меняет набор анализируемых путей соединения и может привести к тому, что будет найден либо более, либо менее оптимальный путь.

Другие параметры планировщика

default_statistics_target (integer)

Задает целевой показатель статистики по умолчанию для столбцов таблицы, у которых командой ALTER TABLE SET STATISTICS не заданы целевые значения. Большие значения увеличивают время, необходимое для выполнения ANALYZE, но при этом могут улучшить качество оценок планировщика. Значение по умолчанию — 100. Дополнительную информацию об использовании статистики планировщиком запросов QHB см. в разделе Статистика, используемая планировщиком.

constraint_exclusion (enum)

Управляет использованием в планировщике запросов ограничений таблицы для оптимизации запросов. Допустимые значения constraint_exclusion: on (включено) — проверять ограничения для всех таблиц, off (выключено) — никогда не проверять ограничения и partition (партиции) — проверять ограничения только для дочерних таблиц и подзапросов UNION ALL. По умолчанию установлен вариант partition. Он часто применяется с традиционными деревьями наследования для улучшения производительности.

Когда данный параметр разрешает это для конкретной таблицы, планировщик сравнивает условия запроса с ограничениями CHECK этой таблицы и не сканирует ее, если условия противоречат ограничениям. Например:

CREATE TABLE parent(key integer, ...);
CREATE TABLE child1000(check (key between 1000 and 1999)) INHERITS(parent);
CREATE TABLE child2000(check (key between 2000 and 2999)) INHERITS(parent);
...
SELECT * FROM parent WHERE key = 2400;

При включенном исключении по ограничению команда SELECTвообще не будет сканировать таблицу child1000, тем самым улучшая производительность.

В настоящее время исключение по ограничению включено по умолчанию только для сценариев, часто используемых для реализации разбиения таблиц через деревья наследования. Включение его для всех таблиц приведет к дополнительным издержкам на планирование, довольно заметным при простых запросах и чаще всего не дающим никакого выигрыша. Если у вас нет таблиц, партиционированных через традиционное наследование, имеет смысл полностью выключить это ограничение. (Обратите внимание, что похожая возможность для партиционированных таблиц управляется отдельным параметром enable_partition_pruning.)

Дополнительную информацию об использовании исключения по ограничению для реализации разбиения см. в подразделе Партиционирование и исключение по ограничению.

cursor_tuple_fraction (floating point)

Задает для планировщика оценку процента строк, которые будут получены через курсор. Значение по умолчанию — 0.1 (10%). При уменьшении значения этого параметра планировщик будет склонен использовать для курсоров планы «быстрого запуска», которые будут быстро извлекать первые несколько строк, хотя для извлечения всех строк потребуется много времени. При увеличении значения акцент сместится в сторону оптимизации общего расчетного времени запроса. При максимальном значении, равном 1,0 (100%), работа с курсорами планируется так же, как и обычные запросы, с учетом только общего расчетного времени, а не скорости доставки первых строк.

from_collapse_limit (integer)

Задает максимальное число элементов в итоговом списке FROM, до которого планировщик будет объединять подзапросы с запросами верхнего уровня. При меньших значениях сокращается время планирования, но планы запросов могут стать менее эффективными. Значение по умолчанию — восемь. Дополнительную информацию см. в разделе Управление планировщиком с помощью явных предложений JOIN.

Если сделать это значение равным geqo_threshold или больше, то может включиться планировщик GEQO и в результате будут получены неоптимальные планы. См. подраздел Генетический оптимизатор запросов.

jit (boolean)

Определяет, может ли QHB задействовать JIT-компиляцию, если та поддерживается. Значение по умолчанию — on (включено).

join_collapse_limit (integer)

Задает максимальное число элементов в списке FROM, до достижения которого планировщик будет переписывать в него явные конструкции JOIN (кроме FULL JOIN). При меньших значениях сокращается время планирования, но планы запросов могут стать менее эффективными.

По умолчанию значение этой переменной равно значению from_collapse_limit, что приемлемо в большинстве случаев. При значении, равном 1, порядок явных предложений JOIN меняться не будет. Таким образом, явно заданный в запросе порядок соединений определит фактический порядок, в котором будут соединяться отношения. Поскольку планировщик запросов не всегда выбирает оптимальный порядок соединений, опытные пользователи могут временно задать для этой переменной значение 1, а затем явно указать желаемый порядок соединения. Дополнительную информацию см. в разделе Управление планировщиком с помощью явных предложений JOIN.

Если сделать это значение равным geqo_threshold или больше, то может включиться планировщик GEQO и в результате будут получены неоптимальные планы. См. подраздел Генетический оптимизатор запросов.

parallel_leader_participation (boolean)

Позволяет ведущему процессу выполнять план запроса в узлах ниже Gather и Gather Merge, не дожидаясь рабочих процессов. Значение по умолчанию — on (включено). Значение off (выключено) снижает вероятность блокировки рабочих процессов в случае, если ведущий процесс будет читать кортежи недостаточно быстро, но требует, чтобы ведущий процесс дожидался запуска рабочих процессов, прежде чем выдавать первые кортежи. Степень увеличения или снижения производительности в результате действий ведущего процесса зависит от вида плана, числа рабочих процессов и продолжительности запроса.

force_parallel_mode (enum)

Позволяет использовать параллельные запросы в целях тестирования, даже когда от этого не ожидается никакого увеличения производительности. Допустимые значения параметра force_parallel_mode: off (использовать параллельный режим, только когда ожидается увеличение производительности), on (принудительно распараллеливать все запросы, для которых это считается безопасным) и regress (как on, но с дополнительными изменениями поведения, описанными ниже).

Говоря точнее, при значении on узел Gather добавляется на самый верх любого плана запроса, для которого это безопасно, так что это запрос выполняется внутри параллельного рабочего процесса. Даже если параллельный рабочий процесс недоступен или его нельзя использовать, такие операции, как, запуск субтранзакции, которые были бы запрещены в контексте параллельного запроса, будут запрещены, если только планировщик не решит, что это приведет к ошибке запроса. Если при включении этого параметра возникают ошибки или выдаются неожиданные результаты, вероятно, некоторые функции, используемые в этом запросе, следует пометить как PARALLEL UNSAFE (или, возможно, PARALLEL RESTRICTED).

Значение regress действует так же, как и on, но с некоторыми дополнительными особенностями, предназначенными для облегчения автоматического регрессионного тестирования. Обычно сообщения от параллельных рабочих процессов включают в себя строку контекста, указывающую на это, но значение regress подавляет эту строку, так что выходные данные получаются такими же, как при непараллельном выполнении. Кроме того, узлы Gather, добавляемые в планы с помощью этого параметра, скрыты в выводе EXPLAIN, поэтому выходные данные соответствуют тем, что были бы получены при значении off (выключено).

plan_cache_mode (enum)

Подготовленные операторы (явно подготовленные или неявно сгенерированные, например, в PL/pgSQL) могут выполняться с использованием специализированных или общих планов. Специализированные планы создаются заново для каждого выполнения с определенным набором значений параметров, в то время как общие планы не зависят от значений параметров и могут использоваться повторно. Таким образом, общий план экономит время планирования, но может быть неэффективным, если идеальный план во многом зависит от значений параметров. Выбор между этими параметрами обычно производится автоматически, но его можно переопределить с помощью параметра plan_cache_mode. Допустимые значения: auto (по умолчанию), force_custom_plan (принудительно использовать специализированные планы) и force_generic_plan (принудительно использовать общие планы). Этот параметр учитывается при выполнении кэшированного плана, а не при его подготовке. Дополнительную информацию см. в описании команды PREPARE.

Регистрация ошибок и протоколирование

Куда протоколировать

log_destination (string)

QHB поддерживает несколько методов протоколирования сообщений сервера, включая stderr, csvlog и syslog. Значение этого параметра указывается в виде списка желаемых расположений журнала, разделенных запятыми. По умолчанию сообщения сервера протоколируются только в stderr. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Если в log_destination включено значение csvlog, то записи журнала выводятся в формате CSV (значения, разделенные запятыми), что удобно для загрузки журналов в программы. Подробную информацию см. в подразделе Использование вывода журнала в формате CSV. Для генерации вывода журнала в формате CSV должен быть включен logging_collector.

Если включено указание stderr или csvlog, то создается файл current_logfiles для записи местоположения файла (или файлов) журнала, который в данный момент используется сборщиком сообщений для соответствующего назначения. Это обеспечивает удобный способ поиска журналов, используемых в данный момент экземпляром сервера. Вот пример содержимого такого файла:

stderr log/qhb.log
csvlog log/qhb.csv

current_logfiles воссоздается, когда при прокрутке создается новый файл журнала и когда повторно загружается log_destination. Он удаляется, когда в log_destination не задается ни stderr, ни csvlog и когда сборщик сообщений выключен.

Примечание
В большинстве систем Unix потребуется изменить конфигурацию демона syslog, чтобы использовать вариант syslog для log_destination. QHB может протоколировать с помощью syslog-программ (facility) с LOCAL0 по LOCAL7 (см. syslog_facility), но на большинстве платформ конфигурация syslog по умолчанию не учитывает сообщения такого типа. Чтобы это работало, понадобится добавить в конфигурацию демона syslog что-то вроде этого: local0.* /var/log/qhb.

logging_collector (boolean)

Этот параметр включает сборщик сообщений, который является фоновым процессом, собирающим сообщения, отправленные в stderr, и перенаправляющим их в файлы журнала. Такой подход зачастую более полезен, чем запись в syslog, поскольку некоторые типы сообщений могут не отображаться в выходных данных syslog. (Один из типичных примеров — сообщения об ошибках динамического компоновщика; другой — сообщения об ошибках в скриптах типа archive_command). Этот параметр можно задать только при запуске сервера.

Примечание
Можно обойтись без сборщика сообщений и просто протоколировать в stderr; сообщения журнала будут отправляться туда, куда направлен поток серверного stderr. Однако этот метод подходит только для небольших объемов протоколирования, поскольку не предоставляет удобного способа ротации файлов журналов. Кроме того, на некоторых платформах выключение сборщика сообщений может привести к потере или искажению выходных данных журнала, так как несколько процессов, одновременно пишущих в один файл журнала, могут перезаписывать сообщения друг друга.

Примечание
Сборщик сообщений спроектирован так, чтобы никогда не терять сообщения. Это означает, что при крайне высокой нагрузке серверные процессы могут быть заблокированы при попытке отправить дополнительные сообщения, если сборщик отстал. В то же время syslog предпочитает удалять сообщения, если не может их записать, то есть в таких случаях часть сообщений может потеряться, но остальная часть системы не будет блокироваться.

log_directory (string)

При включенном logging_collector этот параметр определяет каталог, в котором будут создаваться файлы журнала. Его можно задавать как абсолютный путь или относительный от каталога данных кластера. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Значение по умолчанию — log.

log_filename (string)

При включенном logging_collector этот параметр задает имена создаваемых файлов журнала. Значение рассматривается как шаблон функции strftime, поэтому в нем можно использовать спецификаторы % для включения в имена файлов даты и времени. (Обратите внимание, что при наличии зависящих от часового пояса спецификаторов % вычисления будут выполняться в поясе, заданном в log_timezone). Поддерживаемые спецификаторы % аналогичны тем, что перечислены в описании strftime спецификации Open Group. Обратите внимание, что системная функция strftime напрямую не используется, поэтому нестандартные, специфичные для платформы расширения не работают. Значение по умолчанию — qhb-%Y-%m-%d_%H%M%S.log.

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

Если в log_destination включен вывод в формате CSV, то к имени файла журнала с меткой времени будет добавлено расширение .csv. (Если log_filename оканчивается на .log, то это расширение заменяется на .csv.)

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

log_file_mode (integer)

В системах Unix при включенном logging_collector этот параметр задает права доступа к файлам журнала. Значение параметра должно быть числовым, введенным в формате, принимаемом системными командами chmod и umask. (Для обычного восьмеричного формата число должно начинаться с 0 (ноль).)

Право доступа по умолчанию — 0600, т. е. читать или писать в файлы журнала может только владелец сервера. Другое часто используемое полезное значение — 0640, позволяющее читать файлы членам группы владельца. Однако обратите внимание, что для установки такого значения нужно перенести каталог для хранения файлов журнала (log_directory) за пределы каталога данных кластера. В любом случае неразумно открывать для всех доступ на чтение файлов журналов, поскольку они могут содержать конфиденциальные данные.

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

log_rotation_age (integer)

При включенном logging_collector этот параметр определяет максимальное время использования отдельного файла журнала, после которого будет создан новый файл. Если это значение указано без единиц измерения, оно считается заданным в минутах. Значение по умолчанию — 24 часа. При нулевом значении повременное создание новых файлов журнала выключается. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

log_rotation_size (integer)

При включенном logging_collector этот параметр определяет максимальный размер отдельного файла журнала. По достижении этого размера будет создан новый файл. Если это значение указано без единиц измерения, оно считается заданным в килобайтах. Значение по умолчанию — 10 мегабайт. При нулевом значении поразмерное создание новых файлов журнала выключается. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

log_truncate_on_rotation (boolean)

При включенном logging_collector этот параметр заставит QHB усекать (перезаписывать) существующие файлы журнала с одинаковым именем, а не дописывать в них. Однако усечение будет происходить только при открытии нового файла вследствие ротации по времени, а не при запуске сервера или ротации по размеру. При выключенном параметре в любом случае будет происходить добавление записей в уже существующий файл. Например, использование этого параметра в сочетании с log_filename равным qhb-%H.log приведет к генерации 24-часовых файлов журнала, которые будут циклически перезаписываться. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Пример: для хранения файлов журнала в течение 7 дней, по одному файлу на каждый день с именами вида server_log.Mon, server_log.Tue и т. д., и автоматической перезаписью файлов журнала прошлой недели на эту неделю, нужно установить log_filename в server_log.%a, log_truncate_on_rotation в on и log_rotation_age в 1440.

Пример: для хранения файлов журнала в течение 24 часов, по одному файлу на каждый час, но также с возможностью переключения файла раньше этого срока при превышении размера 1 ГБ, нужно установить log_filename в server_log.%H%M, log_truncate_on_rotation в on, log_rotation_age в 60 и log_rotation_size в 1000000. Добавление %M в log_filename позволит при ротации по размеру указать имя файла, отличное от исходного имени файла данного часа.

syslog_facility (enum)

При включенном протоколировании в syslog этот параметр определяет значение «facility» (syslog-программы), которая будет использоваться. Допустимые значения: LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7; значение по умолчанию — LOCAL0. Подробную информацию см. в документации на системный демон syslog. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

syslog_ident (string)

При включенном протоколировании в syslog этот параметр определяет имя программы, которое будет использоваться в журналах syslog для идентификации сообщений QHB. Имя по умолчанию — qhb. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

syslog_sequence_numbers (boolean)

При включенном протоколировании в syslog и включении этого параметра (по умолчанию), все сообщения будут предваряться последовательно возрастающими номерами (например, [2]). Это позволяет обойти подавление повторов «--- последнее сообщение повторилось N раз ---», которое по умолчанию осуществляется во многих реализациях syslog. В более современных реализациях syslog подавление повторных сообщений можно настроить (например, в rsyslog есть директива $RepeatedMsgReduction), так что это может оказаться необязательно. Кроме того, если вам действительно нужно, чтобы повторы подавлялись, этот параметр можно выключить.

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

syslog_split_messages (boolean)

При включенном протоколировании в syslog этот параметр определяет способ передачи сообщений. Если этот параметр включен (по умолчанию), сообщения разделяются на строки, а длинные строки разбиваются так, чтобы уложиться в 1024 байта, что является типичным ограничением размера для традиционных реализаций syslog. Когда он выключен, сообщения сервера QHB передаются в службу syslog как есть, и она должна сама корректно справляться с потенциально громоздкими сообщениями.

Если в итоге syslog протоколирует в текстовый файл, результат в любом случае будет одинаковым, и лучше оставить этот параметр включенным, поскольку большинство реализаций syslog либо не могут обрабатывать большие сообщения, либо требуют для этого специальной настройки. Но если в итоге syslog записывает данные в какую-то другую среду, возможно, будет необходимо или более полезно сохранять логическую целостность сообщений.

Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

event_source (string)

При включенном протоколировании в журнал событий этот параметр определяет имя программы, которое будет использоваться в нем для идентификации сообщений QHB. Имя по умолчанию — QHB. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Когда протоколировать

log_min_messages (enum)

Управляет уровнем сообщений, записываемых в журнал сервера. Допустимые значения: DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, INFO, NOTICE, WARNING, ERROR, LOG, FATAL и PANIC. Каждый из перечисленных уровней включает в себя все уровни, идущие после него. Чем дальше в этом списке уровень сообщения, тем меньше сообщений отправляется в журнал. Значение по умолчанию — WARNING. Обратите внимание, что здесь у LOG другая позиция, нежели в параметре client_min_messages. Только суперпользователи могут изменять этот параметр.

log_min_error_statement (enum)

Управляет тем, какие команды SQL, вызвавшие ошибку, записываются в журнал сервера. Текущая команда SQL вносится в запись журнала, если сообщение имеет указанный уровень важности

или выше. Допустимые значения: DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, INFO, NOTICE, WARNING, ERROR, LOG, FATAL и PANIC. По умолчанию установлено ERROR, то есть в журнал будут записываться команды, завершившиеся сообщением с уровнем важности ERROR, LOG, FATAL и PANIC. Чтобы действительно выключить запись команд с ошибками, установите для этого параметра значение PANIC. Только суперпользователи могут изменять этот параметр.

log_min_duration_statement (integer)

Инициирует запись в журнал продолжительность выполнения каждой завершенной команды, если та работала в течение указанного времени или дольше. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. При нулевом значении записывается продолжительность выполнения всех команд. Значение -1 (по умолчанию) выключает запись продолжительности выполнения команд. Например, если установить значение 250ms, в журнал будут записаны все команды SQL, выполняющиеся 250 миллисекунд и дольше или более. С помощью этого параметра можно отслеживать неоптимизированные запросы в приложениях. Только суперпользователи могут изменять этот параметр.

Для клиентов, использующих расширенный протокол запросов, будет записываться продолжительность каждой фазы (разбор, связывание и выполнение).

Примечание
При использовании этого параметра совместно с log_statement текст команд, которые записываются вследствие использования log_statement, не будет дублироваться в сообщении о продолжительности выполнения. Если вы не пользуетесь syslog, рекомендуется включить в log_line_prefix идентификатор процесса или сеанса, чтобы с их помощью можно было связать текст команды с сообщением о продолжительности выполнения, которое появится позже.

log_transaction_sample_rate (real)

Задает долю транзакций, команды из которых будут записываться в журнал помимо команд, записываемых по другим причинам. Этот параметр действует на все новые транзакции, независимо от продолжительности выполнения ее команд. Значение по умолчанию — 0, то есть команды из транзакций дополнительно не записываются. При значении 1 записываются все команды из всех транзакций. Параметр log_transaction_sample_rate полезен для отслеживания выборки транзакции. Только суперпользователи могут изменять этот параметр.

Примечание
Как и все параметры, управляющие протоколированием команд, этот параметр может значительно увеличить издержки.

В Таблице 2 поясняются уровни важности сообщений, используемые в QHB. Также здесь показано, как эти уровни переводятся в системные при отправке выходных данных журнала в syslog.

Таблица 2. Уровни важности сообщений

Важность | Использование | syslog
DEBUG1..DEBUG5 | Предоставляет более подробную информацию для разработчиков. Чем больше номер, тем подробнее. | DEBUG
INFO | Предоставляет неявно запрошенную пользователем информацию, например, вывод команды VACUUM VERBOSE. | INFO
NOTICE | Предоставляет информацию, которая может быть полезна пользователям, например, уведомление об усечении длинных идентификаторов. | NOTICE
WARNING | Предоставляет предупреждения о возможных проблемах, например, COMMIT вне блока транзакций. | NOTICE
ERROR | Сообщает об ошибке, из-за которой прервана текущая команда. | WARNING
LOG | Сообщает информацию, интересующую администраторов, например, выполнение контрольных точек. | INFO
FATAL | Сообщает об ошибке, из-за которой прерван текущий сеанс. | ERR
PANIC | Сообщает об ошибке, из-за которой прерваны все сеансы базы данных. | CRIT

Что протоколировать

application_name (string)

Параметр application_name может быть любой строкой длиной не более NAMEDATALEN символов (64 символа при стандартной сборке). Обычно устанавливается приложением при подключении к серверу. Значение (имя) будет отображено в представлении pg_stat_activity и включено в записи журнала при использовании формата CSV. Также его можно добавить в записи журнала в обычных форматах посредством параметра log_line_prefix. Значение application_name может содержать только печатные символы ASCII. Прочие символы будут заменены на вопросительные знаки (?).

debug_print_parse (boolean)

debug_print_rewritten (boolean)

debug_print_plan (boolean)

Эти параметры включают вывод различных данных по отладке. При этом выводится результирующее дерево разбора, данные механизма переписывания запросов или план выполнения для каждого выполненного запроса соответственно. Эти сообщения имеют уровень LOG, поэтому по умолчанию они появляются в журнале сервера, но не отправляются клиенту. Это можно изменить, настроив client_min_messages и/или log_min_messages. По умолчанию эти параметры выключены.

debug_pretty_print (boolean)

Этот параметр включает выравнивание сообщений, созданных debug_print_parse, debug_print_rewritten или debug_print_plan. В результате сообщения становятся более удобочитаемыми, но гораздо более длинными, чем при «компактном» формате, используемом, когда debug_pretty_print выключен. По умолчанию включен.

log_checkpoints (boolean)

Включает протоколирование выполнения контрольных точек и точек перезапуска сервера. Помимо этого в сообщения включается некоторая статистическая информация, в том числе количество записанных буферов и время, потраченное на их запись. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. По умолчанию выключен.

log_connections (boolean)

Включает протоколирование всех попыток подключения к серверу, а также успешного завершения аутентификации клиента. Только суперпользователи могут изменять этот параметр, причем только в начале сеанса. Значение по умолчанию — off (выключен).

Примечание
Некоторые программы-клиенты, например psql, осуществляют две попытки подключения, при первой попытке определяя, требуется ли пароль, поэтому дублирование сообщения «connection received» не обязательно указывает на наличие проблемы.

log_disconnections (boolean)

Включает протоколирование всех завершений сеанса. В журнал выводится примерно та же информация, что и с log_connections, плюс продолжительность сеанса. Только суперпользователи могут изменять этот параметр, причем только в начале сеанса. Значение по умолчанию — off (выключен).

log_duration (boolean)

Включает протоколирование продолжительности всех выполненных команд. Значение по умолчанию — off (выключен). Только суперпользователи могут изменять этот параметр.

Для клиентов, использующих расширенный протокол запросов, будет записываться продолжительность каждой фазы (разбор, связывание и выполнение).

Примечание
Разница между включением log_duration и установкой нулевого значения для log_min_duration_statement заключается в том, что при превышении значения log_min_duration_statement протоколируется текст запроса, а при включении этого параметра — нет. Таким образом, если log_duration установлен в on (включен) и у log_min_duration_statement положительное значение, протоколируется продолжительность для всех команд, но текст запроса добавляется только для команд с продолжительностью, превышающей пороговое значение. Такое поведение может быть полезным при сборе статистики в условиях высокой нагрузки.

log_error_verbosity (enum)

Управляет количеством подробной информации, записываемой в журнал сервера для каждого протоколируемого сообщения. Допустимые значения: TERSE, DEFAULT и VERBOSE, каждое из которых добавляет дополнительные поля к отображаемым сообщениям. TERSE исключает из сообщений об ошибке поля DETAIL, HINT, QUERY и CONTEXT. VERBOSE включает в сообщение код ошибки SQLSTATE, а также имя файла с исходным кодом, имя функции и номер строки, которая вызвала ошибку. Только суперпользователи могут изменять этот параметр.

log_hostname (boolean)

По умолчанию в сообщениях журнала подключений отображается только IP-адрес подключившегося хоста. При включении этого параметра дополнительно будет протоколироваться и имя хоста. Обратите внимание, что, в зависимости от настройки разрешения имен вашего хоста, это может привести к значительному снижению производительности. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

log_line_prefix (string)

Это строка в стиле функции printf, которая выводится в начале каждой строки журнала. С символов % начинаются «управляющие последовательности», которые заменяются информацией о состоянии, как описано ниже. Нераспознанные последовательности игнорируются. Остальные символы напрямую копируются в строку журнала. Некоторые управляющие последовательности распознаются только сеансовыми процессами и будут игнорироваться фоновыми процессами, например основным процессом сервера. Информацию о состоянии можно выровнять влево или вправо, указав число после % и перед этой строкой. При отрицательном значении информация о состоянии будет дополнена пробелами справа до минимально допустимой ширины, а при положительном — слева. Заполнение может быть полезно для облегчения восприятия человеком файлов журнала. Этот параметр можно установить только в файле qhb.conf или в командной строке сервера. По умолчанию установлено значение ’%m [%p] ’, при котором протоколируется метка времени и идентификатор процесса.

Последовательность | Действие | Только для сеансового процесса
%a | Имя приложения | да
%u | Имя пользователя | да
%d | Имя базы данных | да
%r | Имя удаленного хоста или IP-адрес, а также номер удаленного порта | да
%h | Имя удаленного хоста или IP-адрес | да
%p | Идентификатор процесса | нет
%t | Метка времени без миллисекунд | нет
%m | Метка времени с миллисекундами | нет
%n | Отметка времени с миллисекундами (в виде времени Unix) | нет
%i | Тег команды: тип текущей команды сеанса | да
%e | Код ошибки SQLSTATE | нет
%c | Идентификатор сеанса: см. ниже | нет
%l | Номер строки журнала для каждого сеанса или процесса, начиная с 1 | нет
%s | Метка времени начала процесса | нет
%v | Виртуальный идентификатор транзакции (backendID/localXID) | нет
%x | Идентификатор транзакции (0, если не присвоен) | нет
%q | Не выводит никаких данных, но указывает несеансовым процессам остановиться в этой точке строки; игнорируется сеансовыми процессами | нет
%% | Выводит символ % | нет

Тип обслуживающего процесса соответствует столбцу backend_type в представлении pg_stat_activity, но в журнале могут присутствовать и другие типы, которые не показываются в этом представлении.

Последовательность %c выводит квазиуникальный идентификатор сеанса, состоящий из двух 4-байтных шестнадцатеричных чисел (без начальных нулей), разделенных точкой. Числа — это время начала процесса и идентификатор процесса, поэтому %c также можно использовать как способ экономии места для вывода этих значений. Например, чтобы сгенерировать идентификатор сеанса из pg_stat_activity, используйте этот запрос:

SELECT to_hex(trunc(EXTRACT(EPOCH FROM backend_start))::integer) || '.' ||
     to_hex(pid)
FROM pg_stat_activity;

Совет
При установке непустого значение для log_line_prefix лучше сделать его последним символом пробел, чтобы визуально отделить от остальной части строки журнала. Также можно использовать знаки пунктуации.

Совет
Syslog создает собственную метку времени и информацию об идентификаторе процесса, так что, вероятно, нет смысла использовать эти управляющие последовательности при протоколировании в syslog.

Совет
Последовательность %q полезна при включении информации, доступной только в контексте сеанса (обслуживающего процесса), например имени пользователя или базы данных. Например: log_line_prefix = '%m [%p] %q%u@%d/%a '

log_lock_waits (boolean)

Определяет, должно ли создаваться сообщение журнала, когда сеанс ожидает получения блокировки дольше, чем задано в deadlock_timeout. Это помогает определить, не является ли ожидание блокировок причиной низкой производительности. Значение по умолчанию — off (выключен). Только суперпользователи могут изменять этот параметр.

log_statement (enum)

Управляет тем, какие команды SQL протоколируются в журнал. Допустимые значения: none (выключен), ddl, mod и all (все команды). ddl протоколирует все команды определения данных, такие как CREATE, ALTER и DROP. mod протоколирует все команды ddl, а также команды, изменяющие данные, такие как INSERT, UPDATE, DELETE, TRUNCATE и COPY FROM. PREPARE, EXECUTE и EXPLAIN ANALYZE также протоколируются, если содержащие их команды имеют соответствующий тип. Для клиентов, использующих расширенный протокол запросов, запись в журнал происходит на фазе выполнения и содержит значения параметров связывания (с дублированием всех имеющихся апострофов).

Значение по умолчанию — none. Только суперпользователи могут изменять этот параметр.

Примечание
Команды, содержащие простые синтаксические ошибки, не протоколируются даже при log_statement = all, поскольку сообщение журнала создается только после выполнения стандартного разбора для определения типа команды. В случае расширенного протокола запросов этот параметр также не записывает команды, завершившиеся неуспехом до фазы выполнения (т. е. во время разбора или планирования запроса). Для записи таких команд установите параметр log_min_error_statement в значение ERROR (или ниже).

log_replication_commands (boolean)

Включает протоколирование всех команд репликации журнал сервера. Значение по умолчанию — off (выключено). Только суперпользователи могут изменять этот параметр.

log_temp_files (integer)

Управляет протоколированием имен и размеров временных файлов. Временные файлы могут создаваться для сортировки, хеширования и временного хранения результатов запросов. Если этот параметр включен, запись журнала создается для каждого временного файла при его удалении. При нулевом значении записывается информация обо всех временных файлах, а при положительных — только о тех, размер которых не меньше заданной величины. Если это значение указано без единиц измерения, оно считается заданным в килобайтах. Значение по умолчанию — -1, т. е. запись выключена. Только суперпользователи могут изменять этот параметр.

log_timezone (string)

Задает часовой пояс, используемый для меток времени при записи в журнал сервера. В отличие от TimeZone, это значение одинаково для всех баз данных кластер, поэтому в сообщениях от всех сеансов метки времени будут согласованы. Встроенное значение по умолчанию — GMT, но обычно оно переопределяется в qhb.conf; initdb установит там значение, соответствующее системному окружению. Дополнительную информацию см. в подразделе Часовые пояса. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Использование вывода журнала в формате CSV

Добавление csvlog в список log_destination обеспечивает удобный способ импорта файлов журнала в таблицу базы данных. Этот параметр генерирует строки журнала в формате значений, разделенных запятыми (формат CSV), со следующими столбцами: метка времени с миллисекундами; имя пользователя; имя базы данных; идентификатор процесса; хост клиента:номер порта; идентификатор сеанса; номер строки для каждого сеанса; тег команды; время начала сеанса; виртуальный идентификатор транзакции; обычный идентификатор транзакции; уровень важности ошибки; код ошибки SQLSTATE; сообщение об ошибке; подробности к сообщению об ошибке; подсказка; внутренний запрос, который привел к ошибке (если есть); номер символа внутреннего запроса, где произошла ошибка; контекст ошибки; пользовательский запрос, который привел к ошибке (если есть и включен log_min_error_statement); номер символа в пользовательском запросе, где произошла ошибка; расположение ошибки в исходном коде QHB (если у log_error_verbosity задано verbose) и имя приложения. Вот пример определения таблицы для хранения вывода журнала в формате CSV:

CREATE TABLE postgres_log
(
  log_time timestamp(3) with time zone,
  user_name text,
  database_name text,
  process_id integer,
  connection_from text,
  session_id text,
  session_line_num bigint,
  command_tag text,
  session_start_time timestamp with time zone,
  virtual_transaction_id text,
  transaction_id bigint,
  error_severity text,
  sql_state_code text,
  message text,
  detail text,
  hint text,
  internal_query text,
  internal_query_pos integer,
  context text,
  query text,
  query_pos integer,
  location text,
  application_name text,
  PRIMARY KEY (session_id, session_line_num)
);

Для импорта файл журнала в такую таблицу используйте команду COPY FROM:

COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;

Для упрощения импорта файлов журналов в формате CSV необходимо сделать следующее:

Установите для log_filename и log_rotation_age значения, обеспечивающие согласованную и предсказуемую схему именования файлов журнала. Это позволит предсказать, каким будет имя файла, и узнать, когда конкретный файл журнала будет завершен и, следовательно, готов к импорту.

Установите для log_rotation_size значение 0, чтобы выключить поразмерную ротацию файлов, поскольку это затрудняет прогнозирование имени файла журнала.

Установите для log_truncate_on_rotation значение on, чтобы старые данные журнала не смешивались с новыми в одном файле.

Приведенное выше определение таблицы содержит спецификацию первичного ключа. Это полезно для защиты от случайного повторного импорта информации. Команда COPY фиксирует все импортируемые данные за один раз, поэтому любая ошибка приведет к сбою всего импорта. Если сначала импортировать неполный файл журнала, а затем снова импортировать его после заполнения, нарушение первичного ключа приведет к сбою импорта. Поэтому, прежде чем импортировать, дождитесь завершения записи и закрытия журнала. Эта процедура также защитит от случайного импорта неполной (частично написанной) строки, что также приведет к сбою команды COPY.

Заголовок процесса

Эти параметры определяют, как изменяются заголовки процессов сервера. Заголовки процессов обычно просматриваются с помощью таких программ, как ps. Дополнительную информацию см. в разделе Стандартные инструменты Unix.

cluster_name (string)

Задает имя, которое идентифицирует этот кластер базы данных (экземпляр сервера) для различных целей. Имя кластера появляется в заголовке у всех процессов сервера в этом кластере. Кроме того, это имя является именем приложения по умолчанию при подключении резервного сервера (см. synchronous_standby_names).

Это имя может быть любой строкой длиной не более NAMEDATALEN символов (64 символа в стандартной сборке). В значении cluster_name можно использовать только печатные символы ASCII. Все остальные символы будут заменены на вопросительные знаки (?). Если для этого параметра задана пустая строка '' (по умолчанию), то никакое имя не отображается. Этот параметр можно задать только при запуске сервера.

update_process_title (boolean)

Позволяет изменять заголовок процесса каждый раз, когда сервер получает новую команду SQL. На большинстве платформ этот параметр по умолчанию включен. Только суперпользователи могут изменять этот параметр.

Статистика во время выполнения

Сборщик статистики запросов и индексов

Эти параметры управляют функциями сбора статистики на уровне сервера. Когда сбор статистики включен, доступ к собранным данным можно получить через семейство системных представлений pg._stat и pg._statio. Дополнительную информацию см. в главе Мониторинг активности базы данных.

track_activities (boolean)

Включает сбор информации о текущих командах, выполняемых во всех сеансах, а также время начала выполнения этих команд. По умолчанию этот параметр включен. Обратите внимание, что даже при включенном параметре эта информация видна не всем пользователям, а только суперпользователям и пользователю-владельцу сеанса, о котором сообщается, поэтому это не должно представлять угрозу безопасности. Только суперпользователи могут изменять этот параметр.

track_activity_query_size (integer)

Задает объем памяти, зарезервированный для хранения текста текущих команд, выполняемых во всех активных сеансах, для поля pg_stat_activity.query. Если это значение указано без единиц измерения, оно считается заданным в байтах. Значение по умолчанию — 1024 байта. Этот параметр можно задать только при запуске сервера.

track_counts (boolean)

Включает сбор статистики активности базы данных. По умолчанию этот параметр включен, поскольку собранная информация требуется процессу «Автовакуум». Только суперпользователи могут изменять этот параметр.

track_io_timing (boolean)

Включает замер времени операций ввода-вывода базы данных. По умолчанию этот параметр выключен, поскольку он будет периодически запрашивать у операционной системы текущее время, что может привести к значительному замедлению работы на некоторых платформах. Для измерения затрат времени в вашей системе можно воспользоваться утилитой pg_test_timing. Информация о времени ввода/вывода отображается в представлении pg_stat_database, в выводе EXPLAIN при использовании параметра BUFFERS и в представлении pg_stat_statements. Только суперпользователи могут изменять этот параметр.

track_functions (enum)

Включает подсчет вызовов функций и времени их выполнения. При значении pl подсчет будет вестись только для функций процедурного языка, при all — также для функций на языках SQL и C/RUST. Значение по умолчанию — none, т. е. сбор статистики по функциям выключен. Только суперпользователи могут изменять этот параметр.

Примечание
Функции на языке SQL, которые достаточно просты для «встраивания» в вызывающий запрос, отслеживаться не будут, независимо от этого параметра.

stats_temp_directory (string)

Задает каталог для хранения временных данных статистики. Это может быть путь относительно каталога данных или абсолютный путь. Значение по умолчанию — pg_stat_tmp. Если разместить каталог в файловой системе в ОЗУ, это снизит аппаратные требования к вводу-выводу и может привести к повышению производительности. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Мониторинг статистики

compute_query_id (enum)

Включает вычисление идентификатора запроса в ядре. Идентификаторы запроса могут отображаться в представлении pg_stat_activity при использовании команды EXPLAIN или записываться в журнал, если были сконфигурированы посредством параметра log_line_prefix. Также идентификаторы запросов должны вычисляться для расширения pg_stat_statements. Обратите внимание, что если метод вычисления идентификатора запроса в ядре является неприемлемым, в качестве альтернативы можно использовать внешний модуль. В таком случае вычисление в ядре должно быть всегда выключено. Допустимые значения: off (всегда выключено), on (всегда включено) и auto, которое позволяет модулям, например pg_stat_statements, включать это вычисление автоматически Значение по умолчанию — auto.

Примечание
Чтобы гарантированно вычислялся и отображался только один идентификатор запроса, вычисляющее его расширение должно выдавать ошибку, если идентификатор запроса уже был вычислен.

log_statement_stats (boolean)

log_parser_stats (boolean)

log_planner_stats (boolean)

log_executor_stats (boolean)

Эти параметры включают вывод статистики по производительности соответствующего модуля в журнал сервера. Это грубый инструмент профилирования, похожий на функцию getrusage() операционной системы Unix. log_statement_stats включает вывод общей статистики по командам, в то время как остальные параметры включают вывод статистики по модулям. log_statement_stats нельзя включать одновременно с параметрами для модулей. Все эти параметры по умолчанию выключены. Только суперпользователи могут изменять эти параметры.

Автоматическая очистка

Эти параметры управляют поведением функции автоочистки. Дополнительную информацию см. в подразделе Процесс «Автовакуум». Обратите внимание, что многие из этих параметров могут быть переопределены на уровне таблицы; см. подраздел Параметры хранения.

autovacuum (boolean)

Управляет состоянием процесса запуска автоочистки. По умолчанию этот процесс включен, однако для работы автоочистки также необходимо включить track_counts. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Тем не менее, автоочистку можно выключить для отдельных таблиц, изменив их параметры хранения.

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

log_autovacuum_min_duration (integer)

Задает время выполнения действий автоочистки, при превышении которого данные об этом протоколируются в журнал. При нулевом значении записываются все действия автоочистки. При значении -1 (по умолчанию) запись действий автоочистки выключается. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. Например, если задать значение 250ms, будут записываться все операции автоматической очистки и анализа, которые выполняются 250 мс или дольше. Кроме того, если для этого параметра установлено любое значение, отличное от -1, в журнал будет записано сообщение при пропуске действия автоочистки из-за конфликтующей блокировки или параллельного удаления отношения. Включение этого параметра может быть полезным для отслеживания активности автоочистки. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Тем не менее, его значение можно переопределить для отдельных таблиц, изменив их параметры хранения.

autovacuum_max_workers (integer)

Задает максимальное количество процессов автоочистки (кроме, собственно, ее запуска), которые могут выполняться одновременно. По умолчанию это количество равно трем. Этот параметр можно задать только при запуске сервера.

autovacuum_naptime (integer)

Задает минимальную задержку между запусками автоочистки в любой отдельно взятой базе данных. В каждом раунде процесс «Автовакуум» проверяет базу данных и при необходимости выдает команды VACUUM и ANALYZE для таблиц в этой базе. Если это значение указано без единиц измерения, оно считается заданным в секундах. Значение по умолчанию — одна минута (1min). Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

autovacuum_vacuum_threshold (integer)

Задает минимальное количество обновленных или удаленных кортежей, требующееся для запуска команды VACUUM в отдельно взятой таблице. Значение по умолчанию — 50 кортежей. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Тем не менее, его значение можно переопределить для отдельных таблиц, изменив их параметры хранения.

autovacuum_vacuum_insert_threshold (integer)

Задает количество добавленных кортежей, требующееся для запуска команды VACUUM в отдельно взятой таблице. Значение по умолчанию — 100 кортежей. При значении -1 запуск команды VACUUM не будет зависеть от количества добавленных кортежей. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Тем не менее, его значение можно переопределить для отдельных таблиц, изменив их параметры хранения.

autovacuum_analyze_threshold (integer)

Задает минимальное количество добавленных, обновленных или удаленных кортежей, требующееся для запуска команды ANALYZE в любой отдельно взятой таблице. Значение по умолчанию — 50 кортежей. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Тем не менее, его значение можно переопределить для отдельных таблиц, изменив их параметры хранения.

autovacuum_vacuum_scale_factor (floating point)

Задает долю от размера таблицы, добавляемую к autovacuum_vacuum_threshold при принятии решения о запуске команды VACUUM. Значение по умолчанию — 0.2 (20% от размера таблицы). Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Тем не менее, его значение можно переопределить для отдельных таблиц, изменив их параметры хранения.

autovacuum_vacuum_insert_scale_factor (floating point)

Задает долю от размера таблицы, добавляемую к autovacuum_vacuum_insert_threshold при принятии решения о запуске команды VACUUM. Значение по умолчанию — 0.2 (20% от размера таблицы). Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Тем не менее, его значение можно переопределить для отдельных таблиц, изменив их параметры хранения.

autovacuum_analyze_scale_factor (floating point)

Задает долю от размера таблицы, добавляемую к autovacuum_analyze_threshold при принятии решения о запуске команды ANALYZE. Значение по умолчанию — 0.1 (10% от размера таблицы). Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Тем не менее, его значение можно переопределить для отдельных таблиц, изменив их параметры хранения.

autovacuum_freeze_max_age (integer)

Задает максимальный возраст (в транзакциях), которого может достичь поле pg_class.relfrozenxid некоторой таблицы, прежде чем будет запущена операция VACUUM для предотвращения зацикливания идентификаторов транзакций в этой таблице. Обратите внимание, что система будет запускать процессы автоочистки для предотвращения зацикливания, даже если в остальном автоочистка выключена.

Очистка также позволяет удалять старые файлы из подкаталога pg_xact, поэтому значение по умолчанию относительно невелико — 200 миллионов транзакций. Этот параметр можно задать только при запуске сервера. Тем не менее, его значение можно уменьшить для отдельных таблиц, изменив их параметры хранения. Дополнительную информацию см. в подразделе Предотвращение ошибок зацикливания идентификатора транзакции.

autovacuum_multixact_freeze_max_age (integer)

Задает максимальный возраст (в мультитранзакциях), которого может достичь поле pg_class.relminmxidнекоторой таблицы, прежде чем будет запущена операция VACUUM для предотвращения зацикливания идентификаторов мультитранзакций в этой таблице. Обратите внимание, что система будет запускать процессы автоочистки для предотвращения зацикливания, даже если в остальном автоочистка выключена.

Очистка мультитранзакций также позволяет удалять старые файлы из подкаталогов pg_multixact/members и pg_multixact/offsets, поэтому значение по умолчанию относительно невелико — 400 миллионов мультитранзакций. Этот параметр можно задать только при запуске сервера. Тем не менее, его можно уменьшить для отдельных таблиц, изменив их параметры хранения. Дополнительную информацию см. в подразделе Мультитранзакции и зацикливание.

autovacuum_vacuum_cost_delay (floating point)

Задает задержку при превышении предела стоимости, которая будет использоваться при автоматических операциях VACUUM. При значении -1 будет применена обычная задержка vacuum_cost_delay. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. Значение по умолчанию — 2 миллисекунды. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Тем не менее, его значение можно переопределить для отдельных таблиц, изменив их параметры хранения.

autovacuum_vacuum_cost_limit (integer)

Указывает предельное значение стоимости, которое будет использоваться при автоматических операциях VACUUM. При значении -1 (по умолчанию), будет применена обычная задержка vacuum_cost_limit. Обратите внимание, что если запущенных рабочих процессов автоочистки больше одного, это значение пропорционально распределяется между ними, так что суммарный предел стоимости для всех рабочих процессов не превышает значения этой переменной. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера. Тем не менее, его значение можно переопределить для отдельных таблиц, изменив их параметры хранения.

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

Поведение команд

client_min_messages (enum)

Управляет уровнем сообщений, отправляемых клиенту. Допустимые значения: DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, LOG, NOTICE, WARNING и ERROR. Каждый из перечисленных уровней включает в себя все уровни, идущие после него. Чем дальше в этом списке уровень сообщения, тем меньше сообщений отправляется клиенту. Значение по умолчанию — NOTICE. Обратите внимание, что здесь у LOG другая позиция, нежели в параметре log_min_messages.

Сообщения уровня INFO отправляются клиенту всегда.

search_path (string)

Эта переменная задает порядок, в котором просматриваются схемы, когда к объекту поиска (таблице, типу данных, функцию и т. д.) обращаются просто по имени, без указания схемы. Если объекты с одинаковыми именами находятся в разных схемах, используется тот, который был найден первым в пути поиска. На объект, который не входит ни в одну из схем в пути поиска, можно обратиться только по полному имени (с точкой), с указанием содержащей его схемы.

Значением search_path должен быть список имен схем, разделенных запятыми. Любое имя, которое не соответствует существующей схеме или принадлежит схеме, для которой у пользователя нет права USAGE, игнорируется.

Если список содержит специальное имя $user, то вместо него подставляется схема с именем, возвращаемым функцией CURRENT_USER, если такая схема существует и у пользователя есть для нее право USAGE. (В противном случае имя $user игнорируется).

Схема системных каталогов, pg_catalog, просматривается всегда, независимо от того, упоминается она в пути или нет. Если ее имя указано в пути, она будет просматриваться в заданном порядке. Если pg_catalog в пути отсутствует, она будет просматриваться перед всеми остальными элементами пути.

Аналогично всегда просматривается схема временных таблиц текущего сеанса, pg_temp_nnn, если она существует. Ее можно явно указать в пути поиска с помощью псевдонима pg_temp. Если эта схема в пути отсутствует, она будет просматриваться первой (даже до pg_catalog). Однако во временной схеме производится только поиск отношений (таблиц, представлений, последовательностей и т. д.) и типов данных, но не функций или операторов.

Когда объекты создаются без указания конкретной целевой схемы, они помещаются в первую подходящую схему, указанную в search_path. Если путь поиска пуст, выдается ошибка.

Значение по умолчанию для этого параметра — "$user", public. При таком значении поддерживается совместное использование базы данных (когда пользователи не имеют личных схем и все используют схему public), использование личных схем и комбинации обоих вариантов. Другие результаты можно получить, изменяя значение пути поиска по умолчанию, либо глобально, либо отдельно для каждого пользователя.

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

Текущее действующее значение пути поиска можно проверить с помощью функции SQL current_schemas (см. раздел Системные информационные функции и операторы). Результат будет несколько отличаться от значения в search_path, поскольку current_schemas показывает, как были преобразованы элементы, находящиеся в search_path.

row_security (boolean)

Эта переменная определяет, будет ли выдаваться ошибка при применении политики защиты строк. При значении on (включено) политики применяются в обычном режиме. При значении off (выключено) запросы, ограниченные хотя бы одной политикой, будут выдавать ошибку. Значение по умолчанию — on. Задавать значение off имеет смысл, когда ограниченная видимость строки может привести к некорректным результатам; например, qhb_dump задает это значение по умолчанию. Эта переменная не влияет на роли, которые обходят все политики защиты строк, а именно, на суперпользователей и роли с атрибутом BYPASSRLS.

Дополнительную информацию о политиках защиты строк см. в описании команды CREATE POLICY.

default_table_access_method (string)

Этот параметр задает метод доступа к таблице по умолчанию, который используется при создании таблиц или материализованных представлений, если тот не указан явно в команде CREATE, либо при выполнении команды SELECT ... INTO, которая не позволяет явно задать метод доступа. Значение по умолчанию — heap.

default_tablespace (string)

Эта переменная задает табличное пространство по умолчанию, в котором будут создаваться объекты (таблицы и индексы), когда в команде CREATE табличное пространство не указано явно.

Ее значением является либо имя табличного пространства, либо пустая строка, подразумевающая использование табличного пространства по умолчанию для текущей базы данных. Если это значение не соответствует имени какого-либо существующего табличного пространства, QHB автоматически будет использовать табличное пространство по умолчанию для текущей базы данных. Если указано не табличное пространство по умолчанию, у пользователя должно быть право CREATE для него, иначе попытки создания объектов не увенчаются успехом.

Эта переменная не используется для временных таблиц; вместо этого для них используется temp_tablespaces.

Кроме того, эта переменная не используется при создании баз данных. По умолчанию новая база данных наследует настройки по выбору табличного пространства от базы- шаблона, из которой она копируется.

Дополнительную информацию о табличных пространствах см. в разделе Табличные пространства.

default_toast_compression (enum)

Эта переменная задает метод сжатия TOAST по умолчанию для значений сжимаемых столбцов. (Это можно переопределить для отдельных столбцов, установив параметр столбца COMPRESSION в CREATE TABLE или ALTER TABLE.) Поддерживаемые методы сжатия: pglz и lz4 (если QHB была скомпилирована с параметром --with-lz4). Значение по умолчанию — pglz.

temp_tablespaces (string)

Эта переменная задает табличные пространства, в которых будут создаваться временные объекты (временные таблицы и индексы для временных таблиц), когда в команде CREATE табличное пространство не указано явно. Также в этих табличных пространствах создаются временные файлы для таких целей, как сортировка больших наборов данных.

Ее значением является список имен табличных пространств. Когда в этом списке содержится более одного имени, QHB выбирает оттуда случайный элемент каждый раз при создании временного объекта; однако последующие создаваемые внутри транзакции временные объекты помещаются в табличные пространства, выбираемые из списка последовательно. Если выбранный элемент списка представляет собой пустую строку, QHB автоматически будет использовать табличное пространство по умолчанию для текущей базы данных.

Когда temp_tablespaces задается интерактивно, указание несуществующего табличного пространства является ошибкой, равно как и указание табличного пространства, для которого у пользователя нет права CREATE. Однако при использовании значения, заданного ранее несуществующие табличные пространства и табличные пространства, для которых у пользователя нет права CREATE, игнорируются. В частности, это правило применяется в отношении значения, заданного в qhb.conf.

Значение по умолчанию — пустая строка, поэтому все временные объекты создаются в табличном пространстве по умолчанию текущей базы данных.

См. также default_tablespace .

check_function_bodies (boolean)

Обычно этот параметр включен. Если задать значение off (выключен), выключается проверка строки с телом функции во время выполнения команды CREATE FUNCTION. Выключение проверки позволяет избежать побочных эффектов процесса проверки и избежать ложных срабатываний из-за таких проблем, как ссылки вперед. Этому параметру нужно присваивать значение off перед загрузкой функций от имени других пользователей; qhb_dump делает это автоматически.

default_transaction_isolation (enum)

Для каждой транзакции в SQL устанавливается один из следующих уровней изоляции: «read uncommitted», «read committed», «repeatable read» или «serializable». Этот параметр определяет уровень изоляции, который будет устанавливаться по умолчанию для новых транзакций. Значение по умолчанию — «read committed».

Дополнительную информацию см. в главе Параллельный контроль и описании команды SET TRANSACTION.

default_transaction_read_only (boolean)

Транзакция SQL в режиме «только чтение» не может изменять временные таблицы. Этот параметр определяет, будут ли новые транзакции иметь по умолчанию статус «только чтение». Значение по умолчанию — off (разрешено чтение/запись).

Дополнительную информацию см. в описании команды SET TRANSACTION.

default_transaction_deferrable (boolean)

Откладываемая транзакция SQL в режиме «только чтение», выполняющаяся на уровне изоляции serializable, может быть задержана, прежде чем ей будет разрешено возобновить работу. Однако как только она начнет выполняться, для обеспечения сериализуемости не потребуется никаких дополнительных затрат, поэтому у кода сериализации не будет причин прерывать ее из-за параллельных изменений, поэтому эта особенность подходит для длительных транзакций в режиме «только чтение».

Этот параметр определяет, будут ли новые транзакции по умолчанию откладываемыми. В настоящее время он не влияет на транзакции в режиме «чтение/запись» или с уровнем изоляции ниже serializable. Значение по умолчанию — off (выключен).

Дополнительную информацию см. в описании команды SET TRANSACTION.

transaction_isolation (enum)

Данный параметр отражает уровень изоляции текущей транзакции. В начале каждой транзакции ему присваивается текущее значение default_transaction_isolation. Последующая попытка изменить значение этого параметра равнозначна команде SET TRANSACTION.

transaction_read_only (boolean)

Данный параметр отражает статус режима «только чтение» для текущей транзакции. В начале каждой транзакции ему присваивается текущее значение default_transaction_read_only. Последующая попытка изменить значение этого параметра равнозначна команде SET TRANSACTION.

transaction_deferrable (boolean)

Данный параметр отражает статус «откладываемости» для текущей транзакции. В начале каждой транзакции ему присваивается текущее значение default_transaction_deferrable. Последующая попытка изменить значение этого параметра равнозначна команде SET TRANSACTION.

session_replication_role (enum)

Управляет срабатыванием связанных с репликацией триггеров и правил в текущем сеансе. Установка этой переменной требует наличия прав суперпользователя и приводит к сбросу всех ранее кэшированных планов запросов. Ее допустимые значения: origin (по умолчанию), replica и local.

Предполагается, что системы логической репликации будут устанавливать для этого параметра значение replica, применяя реплицированные изменения. В результате триггеры и правила (у которых не менялась конфигурация по умолчанию) не будут срабатывать в репликах. Дополнительную информацию см. в описании предложений ENABLE TRIGGER и ENABLE RULE команды ALTER TABLE.

Внутри QHB значения origin и local воспринимаются как равнозначные. Сторонние системы репликации могут использовать эти два значения для своих внутренних целей, например, обозначая с помощью local сеанс, изменения в котором не должны реплицироваться.

Поскольку внешние ключи реализованы в виде триггеров, присвоение этому параметру значения replica также выключает все проверки внешних ключей, что может привести к нарушению согласованности данных в случае неправильного использования.

statement_timeout (integer)

Любая команда, выполняющаяся дольше заданного в этом параметре времени, прерывается. Если в log_min_error_statement установлено значение ERROR или ниже, команда, прервавшаяся по времени, также будет записана в журнал. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. При нулевом значении (по умолчанию) это прерывание по времени выключается.

Время ожидания отсчитывается с момента поступления команды на сервер до завершения ее выполнения. В расширенном протоколе запросов время ожидания начинает отсчет с момента поступления любого сообщения, связанного с запросом (разбор, связывание, выполнение, описание), и обнуляется при завершении обработки сообщения о выполнении или синхронизации.

Задавать значение statement_timeout в qhb.conf не рекомендуется, поскольку это повлияет на все сеансы.

lock_timeout (integer)

Любая команда, ожидающая дольше заданного в этом параметре времени при получении блокировки таблицы, индекса, строки или другого объекта базы данных, прерывается. Это ограничение по времени применяется к каждой попытке получения блокировки по отдельности. Ограничение применяется как к явным запросам блокировки (например, к LOCK TABLE или SELECT FOR UPDATE без NOWAIT), так и к блокировкам, получаемым неявно. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. При нулевом значении (по умолчанию) это прерывание по времени выключается.

В отличие от statement_timeout, это прерывание по времени может происходить только при ожидании блокировок. Обратите внимание, что при ненулевом значении statement_timeout бессмысленно устанавливать для lock_timeout такое же или большее значение, поскольку прерывание по времени для команды всегда срабатывает первым. Если в log_min_error_statement установлено значение ERROR или ниже, команда, прервавшаяся по времени, будет записана в журнал.

Задавать значение lock_timeout в qhb.conf не рекомендуется, поскольку это повлияет на все сеансы.

idle_in_transaction_session_timeout (integer)

Любой сеанс с открытой транзакцией, которая простаивала дольше заданного в этом параметре времени, завершается. Это позволяет снять любые блокировки, удерживаемые этим сеансом, и повторно использовать слот подключения. Кроме того, это позволяет очистить кортежи, видимые только для этой транзакции. Более подробную информацию см. в разделе Регулярная очистка.

Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. При нулевом значении (по умолчанию) это завершение по времени выключается.

vacuum_freeze_table_age (integer)

Если поле pg_class.relfrozenxid таблицы достигает возраста, заданного в этом параметре, VACUUM будет выполнять агрессивное сканирование. Агрессивное сканирование отличается от обычного выполнения VACUUM тем, что проводится на каждой странице, которая может содержать незамороженные XID или MXID, а не только на тех, которые могут содержать мертвые кортежи. Значение по умолчанию — 150 миллионов транзакций. Хотя пользователи могут задать любое значение в диапазоне от нуля до двух миллиардов, VACUUM будет молча ограничивать действующее значение до предела, равного 95% от autovacuum_freeze_max_age, чтобы периодически запускаемая вручную команда VACUUM имела шанс выполниться до того, как для таблицы будет запущена автоочистка для предотвращения зацикливания. Дополнительную информацию см. в подразделе Предотвращение ошибок зацикливания идентификатора транзакции.

vacuum_freeze_min_age (integer)

Задает возрастную границу (в транзакциях), которая должна использоваться в команде VACUUM при принятии решения о заморозке версий строк в процессе сканирования таблицы. Значение по умолчанию — 50 миллионов транзакций. Хотя пользователи могут задать любое значение в диапазоне от нуля до одного миллиарда, VACUUM будет молча ограничивать действующее значение до предела, равного половине значения autovacuum_freeze_max_age, чтобы перерывы между принудительными автоочистками не были неоправданно короткими. Дополнительную информацию см. в подразделе Предотвращение ошибок зацикливания идентификатора транзакции.

vacuum_multixact_freeze_table_age (integer)

Если поле pg_class.relminmxid таблицы достигает возраста, заданного в этом параметре, VACUUM будет выполнять агрессивное сканирование. Агрессивное сканирование отличается от обычного выполнения VACUUM тем, что проводится на каждой странице, которая может содержать незамороженные XID или MXID, а не только на тех, которые могут содержать мертвые кортежи. Значение по умолчанию — 150 миллионов транзакций. Хотя пользователи могут задать любое значение в диапазоне от нуля до двух миллиардов, VACUUM будет молча ограничивать действующее значение до предела, равного 95% от autovacuum_multixact_freeze_max_age, чтобы периодически запускаемая вручную команда VACUUM имела шанс выполниться до того, как для таблицы будет запущена автоочистка для предотвращения зацикливания. Дополнительную информацию см. в подразделе Мультитранзакции и зацикливание.

vacuum_multixact_freeze_min_age (integer)

Задает возрастную границу (в мультитранзакциях), которая должна использоваться в команде VACUUM при принятии решения о замене идентификатора мультитранзакции более новым идентификатором транзакции или мультитранзакции в процессе сканирования таблицы. Значение по умолчанию — 5 миллионов мультитранзакций. Хотя пользователи могут задать любое значение в диапазоне от нуля до одного миллиарда, VACUUM будет молча ограничивать действующее значение до предела, равного половине значения autovacuum_multixact_freeze_max_age, чтобы перерывы между принудительными автоочистками не были неоправданно короткими. Дополнительную информацию см. в подразделе Мультитранзакции и зацикливание.

vacuum_cleanup_index_scale_factor (floating point)

Задает процент от общего числа кортежей кучи, подсчитанных при предыдущем сборе статистики, который можно вставить без необходимости сканирования индекса на этапе очистки при выполнении команды VACUUM. В настоящее время этот параметр применяется только к индексам B-деревьям.

Если из кучи не было удалено ни одного кортежа, индексы B-деревья все равно будут сканироваться на этапе очистки VACUUM, когда выполняется хотя бы одно из следующих условий: статистика индексов устарела или индекс содержит удаленные страницы, которые при очистке могут быть переработаны для повторного использования. Статистика индексов считается устаревшей, если число вновь вставленных кортежей превышает процент vacuum_cleanup_index_scale_factor от общего числа кортежей в куче, обнаруженных при предыдущем сборе статистики. Общее число кортежей кучи хранится в мета-странице индекса. Обратите внимание, что эти данные попадут в нее только после того, как VACUUM не выявит ни одного мертвого кортежа, поэтому сканирование индекса B-дерева на этапе очистки может быть пропущено, только если мертвые кортежи не будут обнаружены на втором и последующих циклах VACUUM.

Для этого параметра можно задавать значение от 0 до 10000000000. Когда в vacuum_cleanup_index_scale_factor установлено значение 0, сканирование индекса во время очистки VACUUM не пропускается никогда. Значение по умолчанию — 0.1.

bytea_output (enum)

Устанавливает формат вывода для значений типа bytea. Допустимые значения: hex (по умолчанию) и escape (традиционный формат QHB). Дополнительную информацию см. в разделе Двоичные типы данных. На входе данные типа bytea всегда воспринимаются в обоих форматах, независимо от этого параметра.

xmlbinary (enum)

Устанавливает способ кодирования двоичных значений в XML. Это применимо, например, когда значения типа bytea конвертируются в XML с помощью функций xmlelement или xmlforest. Допустимые значения, определенные в стандарте XML-схем: base64 и hex. Значение по умолчанию — base64. Дополнительную информацию о функциях для работы с XML, см. в разделе Функции XML.

Фактический выбор здесь в основном дело вкуса, ограничения могут накладываться только клиентскими приложениями. Оба метода поддерживают все возможные значения, хотя в шестнадцатеричной кодировке результат будет несколько объемнее, чем в кодировке base64.

xmloption (enum)

Устанавливает подразумеваемый по умолчанию режим преобразования между значениями XML и символьными строками. Описание этого преобразования см. в разделе Тип XML. Допустимые значения: DOCUMENT и CONTENT. Значение по умолчанию — CONTENT.

Согласно стандарту SQL этот параметр задается командой

SET XML OPTION { DOCUMENT | CONTENT };

Этот синтаксис также поддерживается в QHB.

gin_pending_list_limit (integer)

Устанавливает максимальный размер очереди записей индекса GIN, который используется, когда включен режим fastupdate. Если размер очереди превышает этот предел, она очищается путем массового переноса записей в основную структуру данных индекса GIN. Если это значение указано без единиц измерения, оно считается заданным в килобайтах. Значение по умолчанию — четыре мегабайта (4MB). Этот параметр можно переопределить для отдельных индексов GIN, изменив их параметры хранения. Дополнительную информацию см. в подразделе Быстрое обновление GIN и разделе Советы и рекомендации по применению GIN.

Языковые стандарты и форматирование

DateStyle (string)

Устанавливает формат отображения значений даты и времени, а также правила интерпретации неоднозначных значений вводимых дат. По историческим причинам эта переменная содержит два независимых компонента: спецификацию выходного формата (ISO, Postgres, SQL или German) и спецификацию, касающуюся порядка год(Y)/ месяц(M)/день(D) при вводе/выводе (DMY, MDY или YMD). Эти компоненты можно задавать вместе или по отдельности. Ключевые слова Euro и European являются синонимами для DMY; ключевые слова US, NonEuro и NonEuropean являются синонимами для MDY. Дополнительную информацию см. в разделе Типы даты/времени. Встроенное значение по умолчанию — ISO, MDY, но initdb инициализирует файл конфигурации со значением, соответствующим поведению выбранной локали lc_time.

IntervalStyle (enum)

Устанавливает формат отображения для значений интервалов. В формате sql_standard интервал будет выводиться в виде, соответствующем стандарту SQL (+Y-M +D +H:MM:SS). В формате postgres (используемом по умолчанию) интервал будет выводиться в виде Y years M mons D days HH:MM:SS. В формате postgres_verbose интервал будет выводиться в виде @ Y years M mons D days H hours M mins S secs. В формате iso_8601 интервал будет выводиться в виде, соответствующем временному интервалу «формат с кодами», определенному в разделе 4.4.3.2 стандарта ISO 8601.

Параметр IntervalStyle также влияет на интерпретацию неоднозначных вводимых интервалов. Дополнительную информацию см. в подразделе Ввод интервалов.

TimeZone (string)

Устанавливает часовой пояс для отображения и интерпретации меток времени. Встроенное значение по умолчанию — GMT, но обычно оно переопределяется в qhb.conf; initdb установит в нем значение, соответствующее системному окружению. Дополнительную информацию см. в подразделе Часовые пояса.

timezone_abbreviations (string)

Устанавливает набор сокращений часовых поясов, которые будут приниматься сервером при вводе даты и времени. Значение по умолчанию — ’Default’, которое представляет собой набор сокращений, принятых в большинстве стран мира; также допускаются значения ’Australia’ и ’India’. Кроме того, для конкретной инсталляции можно определить и другие наборы.

extra_float_digits (integer)

Этот параметр корректирует количество цифр, используемых для текстового вывода чисел с плавающей запятой, включая значения типов float4, float8, а также геометрических типов.

При значении 1 (по умолчанию) или выше числа с плавающей запятой выводятся в кратчайшем точном формате; см. подраздел Типы с плавающей запятой. Фактическое количество генерируемых цифр зависит только от выводимого числа, а не от значения этого параметра. Для чисел типа float8 требуется не более 17 цифр, а для чисел типа float4 — не более 9. Этот формат является и быстрым, и точным, при правильном прочтении в точности сохраняя исходное двоичное число с плавающей запятой. В целях исторической совместимости этот параметр принимает значения до 3 включительно.

Если его значение равно нулю или отрицательно, то выводимое число округляется до заданной десятичной точности. Применяемая точность определяется как стандартное количество цифр для типа (FLT_DIG или DBL_DIG, в зависимости от ситуации), уменьшенное на значение этого параметра. (Например, при значении -1 числа типа float4 будут выводиться округленными до 5 значащих цифр, а числа типа float8 — до 14). Преобразование в этот формат происходит медленнее и не сохраняет все биты двоичного числа с плавающей запятой, но может быть более удобным для восприятия человеком. Дополнительную информацию о поведении этого параметра см. в подразделе Типы с плавающей запятой.

client_encoding (string)

Устанавливает кодировку (набор символов) на стороне клиента. По умолчанию используется кодировка базы данных. Наборы символов, поддерживаемые сервером QHB, описаны в подразделе Поддерживаемые кодировки.

lc_messages (string)

Устанавливает язык, на котором отображаются сообщения. Допустимые значения зависят от системы; дополнительную информацию см. в разделе Поддержка языковых стандартов. Если для этой переменной задана пустая строка (по умолчанию), то значение наследуется от среды выполнения сервера системно-зависимым способом.

В некоторых системах эта категория локали отсутствует. Эту переменную все равно можно задать, но действовать она не будет. Кроме того, есть вероятность, что переведенных сообщений на заданном языке не существует. В этом случае сообщения по-прежнему будут отображаться на английском.

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

lc_monetary (string)

Устанавливает языковой стандарт для форматирования денежных сумм, например, с использованием функций семейства to_char. Допустимые значения зависят от системы; дополнительную информацию см. в разделе Поддержка языковых стандартов. Если для этой переменной задана пустая строка (по умолчанию), то значение наследуется от среды выполнения сервера системно-зависимым способом.

lc_numeric (string)

Устанавливает языковой стандарт для форматирования чисел, например, с использованием функций семейства to_char. Допустимые значения зависят от системы; дополнительную информацию см. в разделе Поддержка языковых стандартов. Если для этой переменной задана пустая строка (по умолчанию), то значение наследуется от среды выполнения сервера системно-зависимым способом.

lc_time (string)

Устанавливает языковой стандарт для форматирования даты и времени, например, с использованием функций семейства to_char. Допустимые значения зависят от системы; дополнительную информацию см. в разделе Поддержка языковых стандартов. Если для этой переменной задана пустая строка (по умолчанию), то значение наследуется от среды выполнения сервера системно-зависимым способом.

default_text_search_config (string)

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

Встроенное значение по умолчанию — pg_catalog.simple, но initdb инициализирует файл конфигурации со значением, соответствующим выбранной локали lc_ctype, если получится определить ее конфигурацию.

Предварительная загрузка разделяемых библиотек

Для настройки предварительной загрузки на сервер разделяемых библиотек доступно несколько параметров, позволяющих подключить дополнительные функции или повысить производительность. Например, при значении ’$libdir/mylib’ в память будет загружена mylib.so (или на некоторых платформах mylib.sl) из стандартного каталога библиотек данной инсталляции. Различия между этими параметрами заключаются в том, когда они вступают в силу и какие права требуются для их изменения.

Таким образом можно предварительно загрузить библиотеки на процедурных языках QHB, обычно используя синтаксис ’$libdir/plXXX’ где XXX — это имя языка: pgsql, perl, tcl или python.

Только разделяемые библиотеки, предназначенные специально для использования с QHB, могут быть загружены таким образом. Каждая библиотека, поддерживаемая QHB, имеет «магический блок», который проверяется на совместимость. Поэтому загрузить таким способом библиотеки не для QHB нельзя. Для этого можно воспользоваться средствами операционной системы, например LD_PRELOAD.

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

local_preload_libraries (string)

Эта переменная задает одну или несколько разделяемых библиотек, которые будут предварительно загружаться при установке соединения. Она содержит разделенный запятыми список имен библиотек, где каждое имя должно восприниматься командой LOAD. Пробелы между именами игнорируются; если требуется включить в имя пробелы или запятые, заключите его в кавычки. Заданное значение этого параметра действует только в начале соединения. Последующие изменения ни на что не влияют. Если указанная в списке библиотека не найдена, попытка подключения завершится неудачно.

Этот параметр может задать любой пользователь. Из-за этого загружать можно только библиотеки, находящиеся в подкаталоге plugins стандартного каталога библиотек данной инсталляции. (Ответственность за то, чтобы там находились только «безопасные» библиотеки, несет администратор.) В local_preload_libraries этот каталог можно задать явно (например, $libdir/plugins/mylib), или просто задать имя библиотеки — mylib (оно будет воспринято как $libdir/plugins/mylib).

Цель этой функциональности — позволить пользователям, не имеющим специальных прав, загружать отладочные библиотеки или библиотеки для оценки производительности в конкретные сеансы, обходясь без явной команды LOAD. Для этой цели данный параметр обычно устанавливается в переменной среды PGOPTIONS на клиенте или с помощью команды ALTER ROLE SET.

Однако обычно этот параметр не следует использовать, если только модуль не предназначен специально для такой загрузки обычными пользователями. В качестве альтернативы рекомендуется рассмотреть session_preload_libraries (см. ниже).

session_preload_libraries (string)

Эта переменная задает одну или несколько разделяемых библиотек, которые будут предварительно загружаться при установке соединения. Она содержит разделенный запятыми список имен библиотек, где каждое имя должно восприниматься командой LOAD. Пробелы между именами игнорируются; если требуется включить в имя пробелы или запятые, заключите его в кавычки. Заданное значение этого параметра действует только в начале соединения. Последующие изменения ни на что не влияют. Если указанная в списке библиотека не найдена, попытка подключения завершится неудачно. Этот параметр могут изменить только суперпользователи.

Цель этой функциональности — позволить загружать отладочные библиотеки или библиотеки для оценки производительности в конкретные сеансы, обходясь без явной команды LOAD. Например, можно загрузить модуль auto_explain

во всех сеансах пользователя с заданным именем, установив этот параметр с помощью команды ALTER ROLE SET. Кроме того, этот параметр можно менять без перезапуска сервера (но изменения вступают в силу только при запуске нового сеанса), поэтому таким образом проще подгружать новые модули, даже если это нужно сделать для всех сеансов.

В отличие от shared_preload_libraries (см. ниже), загрузка библиотеки при запуске сеанса, а не при первом использовании не дает большого выигрыша в производительности. Однако при использовании пула соединений некоторое преимущество возникает.

shared_preload_libraries (string)

Эта переменная задает одну или несколько разделяемых библиотек, которые будут предварительно загружаться при запуске сервера. Она содержит разделенный запятыми список имен библиотек, где каждое имя должно восприниматься командой LOAD. Пробелы между именами игнорируются; если требуется включить в имя пробелы или запятые, заключите его в кавычки. Этот параметр можно задать только при запуске сервера. Если указанная в списке библиотека не будет найдена, сервер не запустится.

Некоторые библиотеки должны выполнять определенные операции, которые могут иметь место только при запуске процесса postmaster, например выделение общей памяти, резервирование легких блокировок или запуск фоновых рабочих процессов. Такие библиотеки должны быть загружены при запуске сервера посредством этого параметра. За подробной информацией обратитесь к документации библиотек.

Также можно предварительно загрузить и другие библиотеки. Предварительная загрузка позволяет избавиться от задержки, возникающей при первом использовании библиотеки. Однако при этом может немного увеличиться время запуска каждого нового серверного процесса, даже если тот никогда не будет использовать эту библиотеку. Поэтому этот параметр рекомендуется применять только для библиотек, которые будут использоваться в большинстве сеансов. Кроме того, для изменения этого параметра требуется перезапуск сервера, поэтому он не подходит например, для краткосрочных задач отладки. Для подобных целей лучше применить параметр session_preload_libraries.

jit_provider (string)

Эта переменная представляет собой имя библиотеки провайдера JIT, которая будет использоваться. Значение по умолчанию — llvmjit. Этот параметр можно задать только при запуске сервера.

Если указывается несуществующая библиотека, JIT будет недоступен, но ошибки не возникнет. Это позволяет устанавливать поддержку JIT отдельно от основного пакета QHB.

Другие параметры по умолчанию

dynamic_library_path (string)

Если необходимо открыть динамически загружаемый модуль и его имя, заданное в команде CREATE FUNCTION или LOAD, не содержит имени каталога (т. е. в имени нет слэша), система будет искать требуемый файл в этом пути.

Значением dynamic_library_path должен быть список абсолютных путей к каталогам, разделенных двоеточием. Если элемент списка начинается со специальной строки $libdir, вместо нее подставляется указанный при компиляции каталог пакета библиотек QHB; сюда устанавливаются модули, предоставляемые стандартным дистрибутивом QHB. (Чтобы узнать имя этого каталога, выполните pg_config --pkglibdir.) Например:

dynamic_library_path = '/usr/local/qhb/lib:/home/my_project/lib:$libdir'

Значение по умолчанию для этого параметра — ’$libdir’. Если в качестве значения задана пустая строка, автоматический поиск по заданному пути выключается.

Суперпользователи могут изменить этот параметр в процессе работы, но изменение, выполненное таким образом, будет действовать только до завершения клиентского соединения, поэтому этот метод следует оставить для целей разработки. Для прочих целей этот параметр рекомендуется задавать в файле конфигурации qhb.conf.

gin_fuzzy_search_limit (integer)

Мягкий верхний предел для размера набора, возвращаемого при сканировании индексов GIN. Дополнительную информацию см. в разделе Советы и рекомендации по применению GIN.

Управление блокировками

deadlock_timeout (integer)

Это время ожидания блокировки, по истечении которого будет произведена проверка состояния взаимоблокировки. Проверка на взаимоблокировку является относительно дорогой, поэтому сервер не выполняет ее каждый раз при ожидании блокировки. Мы с оптимизмом предполагаем, что взаимоблокировки не часто случаются в производственных приложениях, и просто ждем некоторое время, прежде чем проверять наличие взаимоблокировок. Увеличение значения этого параметра сокращает время, затрачиваемое на ненужные проверки взаимоблокировок, но замедляет передачу сообщений о реальных взаимоблокировках. Если это значение указано без единиц измерения, оно считается заданным в миллисекундах. Значение по умолчанию — одна секунда (1s), что, наиболее близко к минимальному значению, которое желательно применять на практике. На сильно загруженном сервере его можно увеличить. В идеале значение этого параметра должно превышать типичное время транзакции, чтобы повысить вероятность того, что блокировка будет снята раньше, чем ожидающая транзакция решит проверить состояние взаимоблокировки. Только суперпользователи могут изменять этот параметр.

Когда включен параметр log_lock_waits, этот параметр также определяет время ожидания, по истечении которого в журнал сервера будет записано сообщение об ожидании блокировки. Если вы пытаетесь исследовать задержки, вызванные блокировками, имеет смысл задать параметру deadlock_timeout более низкое значение, чем обычно.

max_locks_per_transaction (integer)

Общая таблица блокировок отслеживает блокировки для max_locks_per_transaction * (max_connections + max_prepared_transactions) объектов (например, таблиц); таким образом, в любой момент времени может быть заблокировано не более этого количества различных объектов. Этот параметр управляет средним количеством блокировок объектов, выделяемым для каждой транзакции; отдельные транзакции могут заблокировать больше объектов, если все блокировки помещаются в таблице. Это не количество строк, которые могут быть заблокированы; их количество не ограничено. Значение по умолчанию, 64, как показало время, является достаточным, но может возникнуть необходимость его увеличить, если запросы обращаются ко множеству различных таблиц в одной транзакции, как например, запрос к родительской таблице со множеством потомков. Этот параметр можно задать только при запуске сервера.

При запуске резервного сервера следует установить для этого параметра значение большее или равное значению на главном сервере. В противном случае на резервном сервере не будут разрешены запросы.

max_pred_locks_per_transaction (integer)

Общая таблица предикатных блокировок отслеживает блокировки для max_pred_locks_per_transaction * (max_connections + max_prepared_transactions) объектов (например, таблиц); таким образом, в любой момент времени может быть заблокировано не более этого количества различных объектов. Этот параметр управляет средним количеством блокировок объектов, выделяемым для каждой транзакции; отдельные транзакции могут заблокировать больше объектов, если все блокировки помещаются в таблице. Это не количество строк, которые могут быть заблокированы; их количество не ограничено. Значение по умолчанию, 64, как показала практика, вполне удовлетворительно, но может возникнуть необходимость его увеличить, если клиенты обращаются ко множеству различных таблиц в одной сериализуемой транзакции. Этот параметр можно задать только при запуске сервера.

max_pred_locks_per_relation (integer)

Этот параметр определяет, сколько страниц или кортежей одного отношения может быть предикатно заблокировано, прежде чем блокировка будет распространена на все отношение. Значения, большие или равные нулю, означают абсолютный предел, а при отрицательном значении предел будет равен значению max_pred_locks_per_transaction, деленному на модуль этого значения. По умолчанию установлено значение -2, при котором сохраняется поведение, наблюдавшееся в предыдущих версиях QHB. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

max_pred_locks_per_page (integer)

Этот параметр определяет, сколько строк на одной странице может быть предикатно заблокировано, прежде чем блокировка будет распространена на всю страницу. Значение по умолчанию — 2. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Совместимость с разными версиями и платформами

Предыдущие версии QHB

array_nulls (boolean)

Этот параметр определяет, будет ли синтаксический анализатор вводимого массива распознавать строку NULL без кавычек как элемент массива, равный NULL. Значение по умолчанию — on (включено), что позволяет вводить элементы массива, содержащие NULL. Для обратной совместимости с приложениями, требующими для этой цели введения NULL в кавычках, эту переменную можно выключить (задав значение off).

Обратите внимание, что массивы, содержащие NULL, можно создавать, даже если эта переменная имеет значение off.

backslash_quote (enum)

Этот параметр определяет, можно ли будет представить знак апострофа в строковом литерале в виде \'. В стандарте SQL представлен другой, предпочитаемый способ представления знака апострофа путем его дублирования (''), но исторически QHB также принимал \'. Однако использование варианта \' создает риски безопасности, потому что в некоторых клиентских кодировках в наборе символов существуют многобайтовые символы, последний байт которых численно равнозначен коду ASCII \. Если код на стороне клиента выполнит экранирование некорректно, это может привести к атаке SQL-инъекцией. Этот риск можно предотвратить, заставив сервер отклонять запросы, в которых апостроф экранируется обратным слэшем. Допустимые значения параметра backslash_quote: on (всегда принимать \'), off (всегда отклонять) и safe_encoding (принимать только в том случае, если клиентская кодировка не допускает наличия кода ASCII \ в многобайтовых символах). Значение по умолчанию — safe_encoding.

Обратите внимание, что в строковом литерале, соответствующем стандарту, знаки \ в любом случае обозначают просто \. Этот параметр влияет только на восприятие нестандартных литералов, включая синтаксис с экранированием строки (E’...’).

escape_string_warning (boolean)

Когда этот параметр включен, выдается предупреждение, если в обычном строковом литерале (с синтаксисом ’...’) появляется обратный слэш (\), а параметр standard_conforming_strings выключен. Значение по умолчанию — on (включен).

Приложения, которые желают использовать обратный слэш в качестве управляющего символа, необходимо модифицировать для использования синтаксиса с экранированными строками (E’...’), поскольку по умолчанию теперь в обычных строках обратный слэш воспринимается как обычный символ, в соответствии со стандартом SQL. Включение этой переменной помогает найти код, нуждающийся в изменении.

lo_compat_privileges (boolean)

Если задать для этой переменной значение on (включено), то новые проверки прав выключатся в целях совместимости с предыдущими версиями QHB. Значение по умолчанию — off (выключено). Только суперпользователи могут изменять этот параметр.

Установка этой переменной выключает не все проверки безопасности, связанные с большими объектами.

operator_precedence_warning (boolean)

Когда этот параметр включен, синтаксический анализатор будет выдавать предупреждение для любой конструкции, которая могла бы изменить поведение в результате изменения приоритетов операторов в новых версиях QHB. Проверка того, не сломалось ли что-то в результате изменения приоритета, полезна для аудита приложений; но данный параметр не стоит держать включенным постоянно, поскольку предупреждения будут выдаваться и тогда, когда код совершенно корректный и соответствует стандарту SQL. Значение по умолчанию — off (выключено).

Дополнительную информацию см. в подразделе Приоритеты операторов.

quote_all_identifiers (boolean)

Этот параметр принудительно заключает в кавычки все идентификаторы, даже если это не ключевые слова (в настоящее время), при получении SQL из базы данных. Это затрагивает вывод EXPLAIN а также результаты функций типа pg_get_viewdef. См. также описание параметра --quote-all-identifiers команд qhb_dump и qhb_dumpall.

standard_conforming_strings (boolean)

Этот параметр определяет, будут ли обычные строковые литералы (’...’) воспринимать обратный слэш буквально, как указано в стандарте SQL. Значение по умолчанию — on (включено). Приложения могут проверять этот параметр, чтобы определить, как будут обрабатываться строковые литералы. Также наличие этого параметра может рассматриваться как признак того, что поддерживается синтаксис экранируемых строк (E’...’). Этот синтаксис (подраздел Строковые константы с экранированием в стиле C) следует использовать, если приложению нужно, чтобы обратные слэши воспринимались как спецсимволы.

synchronize_seqscans (boolean)

Этот параметр позволяет синхронизировать обращения при последовательном сканировании больших таблиц, чтобы эти обращения считывали один блок примерно в одно и то же время и тем самым разделяли рабочую нагрузку на ввод-вывод данных. Когда этот параметр включен, сканирование может начаться в середине таблицы, чтобы синхронизироваться с уже выполняющимся сканированием, а достигнув конца, «завернуться» к началу, чтобы обработать оставшиеся строки. Это может привести к непредсказуемым изменениям порядка строк, возвращаемых запросами, в которых нет предложения ORDER BY. При значении off (выключен) последовательное сканирование всегда начинается с начала таблицы. Значение по умолчанию — on.

Совместимость с разными платформами и клиентами

transform_null_equals (boolean)

Когда этот параметр включен, выражения вида выражение = NULL (или NULL = выражение) обрабатываются как выражение IS NULL, то есть они возвращают true (истинны), если выражение дает значение NULL, и false (ложны) в противном случае. Согласно спецификации SQL, сравнение выражение = NULL должно всегда возвращать NULL (неизвестное значение). Поэтому по умолчанию этот параметр выключен (установлен в off).

Однако отфильтрованные формы в Microsoft Access генерируют запросы, использующие выражение = NULL для проверки на значения NULL, поэтому, если вы пользуетесь этим интерфейсом для доступа к базе данных, имеет смысл включить этот параметр. Поскольку выражения вида выражение = NULL всегда возвращают значение NULL (согласно интерпретации стандарта SQL), они не очень полезны и не часто появляются в обычных приложениях, поэтому на практике от этого параметра вреда немного. Но новые пользователи часто путаются в семантике выражений, содержащих значения NULL, поэтому по умолчанию этот параметр выключен.

Обратите внимание, что этот параметр влияет только на точную форму сравнения = NULL, но не на другие операторы сравнения или другие выражения, которые в вычислительном отношении равнозначны некоторому выражению с оператором равенства (например, IN). Таким образом, этот параметр не может быть универсальным решением проблем плохого программирования.

Сопутствующую информацию см. в разделе Функции и операторы сравнения.

Обработка ошибок

exit_on_error (boolean)

Если этот параметр включен, любая ошибка приведет к прерыванию текущего сеанса. По умолчанию он выключен, так что только сеанс будет прерываться только при критических ошибках.

restart_after_crash (boolean)

Если этот параметр включен (значение on, установлено по умолчанию), QHB будет автоматически перезагружаться после сбоя серверного процесса. Обычно это лучший способ обеспечить максимальную доступность базы данных. Однако в некоторых обстоятельствах, например, когда QHB активизируется кластерным ПО, такую перезагрузку лучше выключить, чтобы кластерное ПО могло получить контроль и выполнить действия, которые сочтет целесообразными.

data_sync_retry (boolean)

Если этот параметр выключен (значение off, установлено по умолчанию), QHB будет выдавать ошибку уровня PANIC в случае неудачи при попытке сохранить файлы с измененными данными в файловую систему. Это приведет к сбою сервера базы данных. Этот параметр можно задать только при запуске сервера.

В некоторых операционных системах состояние данных в кэше страниц ядра после ошибки обратной записи неизвестно. В некоторых случаях эти данные могут полностью пропасть, и становится опасно повторять попытку записи: вторая попытка может быть объявлена успешной, тогда как в действительности данные были утеряны. В подобных обстоятельствах единственный способ избежать потери данных — после любого сбоя восстанавливать их из WAL, предпочтительно перед этим выяснив основную причину сбоя и заменив неисправное оборудование.

При включении этого параметра QHB все так же будет выдавать ошибку, но при этом продолжит работу, чтобы иметь возможность повторить операцию сохранения данных на более поздней контрольной точке. Включать его следует только после выяснения того, что именно операционная система делает с данными в буфере при ошибке обратной записи.

Предустановленные параметры

Следующие «параметры» доступны только для чтения и определяются при компиляции или установке QHB. В связи с этим они были исключены из примера файла qhb.conf. Эти параметры сообщают о различных аспектах поведения QHB, которые могут представлять интерес для определенных приложений, в частности, административных интерфейсов.

block_size (integer)

Сообщает размер блока на диске. Он определяется значением BLCKSZ при сборке сервера. Значение по умолчанию — 8192 байта. От значения block_size зависят некоторые другие переменные конфигурации (например, shared_buffers). Информацию об этом можно найти в разделе Потребление ресурсов.

data_checksums (boolean)

Сообщает, включено ли в этом кластере применение контрольных сумм для проверки данных. Дополнительную информацию см. в описании data-checksums.

data_directory_mode (integer)

В системах Unix этот параметр сообщает о разрешениях для каталога данных (data_directory), определенных при запуске. Дополнительную информацию см. в описании group-access.

debug_assertions (boolean)

Сообщает, был ли QHB собран с включенными операторами подтверждения отсутствия ошибок. Это происходит, если при сборке QHB определяется макрос USE_ASSERT_CHECKING (например, посредством configure с параметром --enable-cassert). По умолчанию QHB собирается без операторов подтверждения.

integer_datetimes (boolean)

Сообщает, был ли QHB собран с поддержкой даты и времени в 64-битных целых. Значение по умолчанию — on (включено).

lc_collate (string)

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

lc_ctype (string)

Сообщает языковой стандарт, определяющий классификацию символов. Дополнительную информацию см. в разделе Поддержка языковых стандартов. Это значение определяется при создании базы данных. Обычно оно совпадает со значением lc_collate, но для специальных приложений может быть установлено по-другому.

max_function_args (integer)

Сообщает максимальное количество аргументов функции. Оно определяется значением константы FUNC_MAX_ARGS при сборке сервера. Значение по умолчанию — 100 аргументов.

max_identifier_length (integer)

Сообщает максимальную длину идентификатора. Она определяется числом на единицу меньше значения NAMEDATALEN при сборке сервера. По умолчанию значение константы NAMEDATALEN равно 64; следовательно значение max_identifier_length по умолчанию — 63 байта, но в многобайтовых кодировках число символов будет меньше.

max_index_keys (integer)

Сообщает максимальное количество ключей индекса. Оно определяется значением константы INDEX_MAX_KEYS при сборке сервера. Значение по умолчанию — 32 ключа.

segment_size (integer)

Сообщает количество блоков (страниц), которые можно сохранить в сегменте файла. Оно определяется значением константы RELSEG_SIZE при сборке сервера. Максимальный размер сегмента в байтах равен произведению segment_size и block_size; по умолчанию это 1 ГБ.

server_encoding (string)

Сообщает кодировку базы данных (набор символов). Она определяется при создании базы данных. Обычно клиенты должно интересовать только значение client_encoding.

server_version (string)

Сообщает номер версии сервера. Он определяется значением константы PG_VERSION при сборке сервера.

server_version_num (integer)

Сообщает номер версии сервера в виде целого числа. Он определяется значением константы PG_VERSION_NUM при сборке сервера.

ssl_library (string)

Сообщает имя библиотеки SSL, с которой был собран этот сервер QHB (даже если на данный момент SSL для данного экземпляра не настроен или не используется), например OpenSSL, либо выводит пустую строку, если сборка проводилась без библиотеки.

wal_block_size (integer)

Сообщает размер блока WAL на диске. Он определяется значением константы XLOG_BLCKSZ при сборке сервера. Значение по умолчанию — 8192 байта.

wal_segment_size (integer)

Сообщает размер сегментов журнала упреждающей записи. Значение по умолчанию — 16 МБ. Дополнительную информацию см. в разделе Конфигурация WAL.

Специализированные параметры

Эта функция была разработана для того, чтобы дополнительные модули (например, процедурные языки) могли добавлять параметры, обычно не известные QHB. Это позволяет настраивать модули расширения стандартными способами.

Имена специализированных параметров состоят из двух частей: имя расширения, затем, после точки, собственно имя параметра, по аналогии с полными именами в SQL. Например: plpgsql.variable_conflict.

Поскольку установка специализированных параметров может потребоваться в процессах, не загрузивших соответствующий модуль расширения, QHB примет значения для любого имени параметра, состоящего из двух частей. Такие переменные рассматриваются как заполнители и не действуют до тех пор, пока не будет загружен модуль, который их определяет. Когда модуль расширения загружается, он добавляет свои определения переменных, преобразует любые значения заполнителей в соответствии с этими определениями и выдает предупреждения для любых нераспознанных заполнителей, начинающихся с его имени расширения.

Параметры для разработчиков

Следующие параметры предназначены для работы с исходным кодом QHB, а в некоторых случаях могут помочь восстановить сильно поврежденные базы данных. Не должно быть никаких причин использовать их в производственной базе данных, поэтому они были исключены из примера файла qhb.conf. Обратите внимание, что для работы со многими из этих параметров требуются специальные флаги компиляции исходного кода.

allow_system_table_mods (boolean)

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

ignore_system_indexes (boolean)

Включает игнорирование системных индексов при чтении системных таблиц (но при этом индексы все равно будут меняться при изменении этих таблиц). Это полезно для восстановления системы при поврежденных системных индексах. Этот параметр нельзя изменить после запуска сеанса.

post_auth_delay (integer)

Задает время задержки при запуске нового серверного процесса после проведения им процедуры аутентификации. Этот параметр предназначен для того, чтобы разработчики имели возможность присоединить отладчик к серверному процессу в целях выявления нарушений аутентификации. Если это значение указано без единиц измерения, оно считается заданным в секундах. При нулевом значении (по умолчанию) задержка выключается. Этот параметр нельзя изменить после запуска сеанса.

pre_auth_delay (integer)

Задает время задержки сразу после ветвления нового серверного процесса, до проведения им процедуры аутентификации. Этот параметр предназначен для того, чтобы разработчики имели возможность присоединить отладчик к серверному процессу в целях выявления нарушений аутентификации. Если это значение указано без единиц измерения, оно считается заданным в секундах. При нулевом значении (по умолчанию) задержка выключается. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

trace_notify (boolean)

Включает выведение очень подробной отладочной информации при выполнении команд LISTEN и NOTIFY. Чтобы отправлять эти выходные данные клиенту или в журнал сервера, параметр client_min_messages или log_min_messages соответственно должен иметь значение DEBUG1 или ниже.

trace_recovery_messages (enum)

Включает запись в журнал отладочной информации, связанной с восстановлением, которая иначе не протоколируется. Этот параметр позволяет пользователю переопределить обычное значение log_min_messages, но только для определенных сообщений. Он предназначен для использования при отладке режима горячего резерва. Допустимые значения: DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1 и LOG. Значение по умолчанию, LOG, никак не влияет на решения о протоколировании. С другими значениями связанные с восстановлением отладочные сообщения, имеющие заданный приоритет или выше, записываются так, как если бы имели приоритет LOG; при обычных значениях log_min_messages это подразумевает их обязательную передачу в журнал сервера. Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

trace_sort (boolean)

Если этот параметр включен, то во время операций сортировки будет выводиться информация об использовании ресурсов. Этот параметр доступен, только если при компиляции QHB был определен макрос TRACE_SORT. (Хотя в настоящее время TRACE_SORT определен по умолчанию.)

trace_locks (boolean)

Если этот параметр включен, будет выводиться информация об использовании блокировок. Эта информация включает тип операции блокировки, тип самой блокировки и уникальный идентификатор заблокированного или разблокированного объекта. Также включены битовые маски для типов блокировки, уже полученных для этого объекта, а также для типов блокировки, ожидающих его. Кроме того, выводится количество полученных и ожидающих блокировок для каждого типа блокировки, а также их общее число. Далее показан пример вывода в файл журнала:

LOG:  LockAcquire: new: lock(0xb7acd844) id(24688,24696,0,0,0,1)
    grantMask(0) req(0,0,0,0,0,0,0)=0 grant(0,0,0,0,0,0,0)=0
    wait(0) type(AccessShareLock)
LOG:  GrantLock: lock(0xb7acd844) id(24688,24696,0,0,0,1)
    grantMask(2) req(1,0,0,0,0,0,0)=1 grant(1,0,0,0,0,0,0)=1
    wait(0) type(AccessShareLock)
LOG:  UnGrantLock: updated: lock(0xb7acd844) id(24688,24696,0,0,0,1)
    grantMask(0) req(0,0,0,0,0,0,0)=0 grant(0,0,0,0,0,0,0)=0
    wait(0) type(AccessShareLock)
LOG:  CleanUpLock: deleting: lock(0xb7acd844) id(24688,24696,0,0,0,1)
    grantMask(0) req(0,0,0,0,0,0,0)=0 grant(0,0,0,0,0,0,0)=0
    wait(0) type(INVALID)

Подробную информацию о структуре выводимой информации можно найти в src/include/storage/lock.h

Этот параметр доступен, только если при компиляции QHB был определен макрос LOCK_DEBUG.

trace_lwlocks (boolean)

Если этот параметр включен, будет выводиться информация об использовании легковесных блокировок. Такие блокировки предназначены главным образом для обеспечения взаимоисключающего доступа к структурам данных общей памяти.

Этот параметр доступен, только если при компиляции QHB был определен макрос LOCK_DEBUG.

trace_userlocks (boolean)

Если этот параметр включен, будет выводиться информация об использовании пользовательских блокировок. Она выводится в той же форме, что и с trace_locks, но для рекомендательных блокировок.

Этот параметр доступен, только если при компиляции QHB был определен макрос LOCK_DEBUG.

trace_lock_oidmin (integer)

Если этот параметр установлен, блокировки для таблиц ниже заданного OID трассироваться не будут (это используется, чтобы исключить выведение информации по системным таблицам).

Этот параметр доступен, только если при компиляции QHB был определен макрос LOCK_DEBUG.

trace_lock_table (integer)

Обязательно трассировать блокировки для таблицы с заданным OID.

Этот параметр доступен, только если при компиляции QHB был определен макрос LOCK_DEBUG.

debug_deadlocks (boolean)

Если этот параметр установлен, по истечении времени ожидания взаимоблокировки будет выводиться информация обо всех текущих блокировках.

Этот параметр доступен, только если при компиляции QHB был определен макрос LOCK_DEBUG.

log_btree_build_stats (boolean)

Если этот параметр установлен, будет протоколироваться статистика использования системных ресурсов (память и ЦП) при различных операциях с B-деревом.

Этот параметр доступен, только если при компиляции QHB был определен макрос BTREE_BUILD_STATS.

wal_consistency_checking (string)

Этот параметр предназначен для проверки ошибок в процедурах воспроизведения WAL. Когда этот параметр включен, в запись WAL добавляются полностраничные изображения всех изменяемых вместе с ней буферов. Если эта запись впоследствии воспроизводится, система сначала применяет каждую запись, а затем проверяет, соответствуют ли измененные этой записью буферы сохраненным образам. В некоторых случаях (например, во вспомогательных битах) незначительные изменения допустимы и будут игнорироваться. Любые неожиданные различия приведут к фатальной ошибке, и восстановление прекратится.

Значение этого параметра по умолчанию, пустая строка, выключает эту функцию. Его значением может быть all для проверки всех записей или список имен менеджеров ресурсов через запятую для проверки только записей, поступающих от этих менеджеров. В настоящее время поддерживаются следующие менеджеры ресурсов: heap, heap2, btree, hash, gin, gist, sequence, spgist, brin и generic. Только суперпользователи могут изменять этот параметр.

wal_debug (boolean)

Если этот параметр включен, будет выводиться отладочная информация, связанная с WAL. Этот параметр доступен, только если при компиляции QHB был определен макрос WAL_DEBUG.

ignore_checksum_failure (boolean)

Этот параметр действует, только если включен data-checksums.

При обнаружении ошибки контрольной суммы во время чтения QHB обычно сообщает об ошибке, прерывая текущую транзакцию. Если для параметра ignore_checksum_failure задано значение on (включено), система игнорирует ошибку (но все равно выдает предупреждение) и продолжает обработку. Такое поведение может привести к сбою, распространению или сокрытию повреждения данных или другим серьезным проблемам. Однако оно же позволяет обойти ошибку и извлечь неповрежденные кортежи, которые все еще могут находиться в таблице, если заголовок блока адекватен. Если заголовок поврежден, будет выдана ошибка, даже если этот параметр включен. Значение по умолчанию — off (выключено), и изменить его может только суперпользователь.

zero_damaged_pages (boolean)

При обнаружении поврежденного заголовка страницы QHB обычно сообщает об ошибке, прерывая текущую транзакцию. Если для параметра zero_damaged_pages задано значение on (включено), система вместо этого выдаст предупреждение, обнулит поврежденную страницу в памяти и продолжит обработку. Такое поведение разрушит данные, а именно все строки на поврежденной странице. Однако оно же позволяет обойти ошибку и извлечь строки из любых неповрежденных страниц, которые могут находиться в таблице. Это полезно для восстановления данных, поврежденных из-за аппаратной или программной ошибки. Обычно его стоит включать, только если не осталось никакой другой надежды восстановить данные с поврежденных страниц таблицы. Обнуленные страницы не сохраняются на диск, поэтому прежде чем снова выключить этот параметр, рекомендуется пересоздать такие таблицы или индексы. Значение по умолчанию — off (выключено), и изменить его может только суперпользователь.

jit_debugging_support (boolean)

Если LLVM обладает необходимой функциональностью, регистрировать сгенерированные функции в GDB. Это облегчает отладку. Значение по умолчанию — off. Этот параметр можно задать только при запуске сервера.

jit_dump_bitcode (boolean)

Записывает сгенерированный LLVM IR-код в файловую систему, в каталог data_directory. Это полезно только для работы с внутренними компонентами реализации JIT. Значение по умолчанию — off (выключено). Этот параметр может изменить только суперпользователь.

jit_expressions (boolean)

Определяет, скомпилированы ли выражения JIT, когда JIT-компиляция активирована

. Значение по умолчанию — *on*

(включено).

jit_profiling_support (boolean)

Если LLVM обладает необходимой функциональностью, выдавать данные, необходимые perf для профилирования функций, сгенерированных JIT. Выходные файлы записываются в $HOME/.debug/jit/; удалять их при желании должен сам пользователь. Значение по умолчанию — off (выключено). Этот параметр можно задать только при запуске сервера.

jit_tuple_deforming (boolean)

Определяет, будет ли JIT-компилироваться преобразование кортежей, когда JIT-компиляция активирована. Значение по умолчанию — on (включено).

Краткие аргументы

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

Таблица 2. Ключ краткого аргумента

Краткий аргумент | Эквивалент
-B x | shared_buffers = x
-d x | log_min_messages = DEBUGx
-e | datestyle = euro
-fb, -fb, -fh, -fm, -fn, -fo, -fs, -ft | enable_bitmapscan = off, enable_hashjoin = off, enable_indexscan = off, enable_mergejoin = off, enable_nestloop = off, enable_indexonlyscan = off, enable_seqscan = off, enable_tidscan = off
-F | fsync = off
-h x | listen_addresses = x
-i | listen_addresses = '*'
-k x | unix_socket_directories = x
-l | ssl = on
-N x | max_connections = x
-O | allow_system_table_mods = on
-p x | port = x
-P | ignore_system_indexes = on
-s | log_statement_stats = on
-S x | work_mem = x
-tpa, -tpl, -te | log_parser_stats = on, log_planner_stats = on, log_executor_stats = on, log_executor_stats = on
-W x | post_auth_delay = x

Параметры модулей и расширений, специфичных для QHB

Менеджер кэша дисковых блоков TARQ

use_qhb_cache (boolean)

Логический параметр. При установке значения в true будет использоваться новая версия кэша.

qhb_cache_size (integer)

Общий размер буферного кэша (при включении use_qhb_cache=true значение shared_buffers не используется).

shared_buffers_partitions (integer)

Размер фрагментов кэша (партиций; не имеют отношения к партициям таблицы). Обращение к каждому фрагменту происходит независимо от остальных. Слишком большой размер приведет к возрастанию конкуренции за блокировки, слишком маленький может привести к задержкам, если в партиции не окажется пригодных к вытеснению блоков (все грязные или занятые фоновым процессом).

Рекомендуемое значение: 128.

tarq_cache.touch_queue_ignore (integer)

Процент заполнения фрагмента кэша при котором обращения начинают приводить к операциям балансировки. Может принимать значения от 1 до 100 (целые числа). Для незаполненного или заполненного частично фрагмента балансировок не требуется.

Рекомендуемое значение: 50.

Ребалансировка — дорогая операция, в нагруженной среде к некоторым буферам (содержимое системных таблиц, словари, последовательности) может быть много тысяч обращений в секунду, существенно не влияющих на общий баланс. Для сглаживания можно использовать следующие параметры:

tarq_cache.touch_window (integer)

Время в секундах. Все обращения в течение этого периода считаются одним обращением.

Рекомендуемое значение: 3.

tarq_cache.touch_threshold (integer)

Количество обращений к буферу, по достижении которого происходит операция ребалансировки.

Рекомендуемое значение: 5.

Параметр HOLDMEM и дополнительные кэши дисковых блоков

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

use_possible_buffer (boolean)

Логический параметр. При установке значения в TRUE будет использоваться этот кэш.

qhb_possible_buffers_size (integer)

Общий размер буферного кэша.

shared_buffers_partitions (integer)

Используется общий параметр с TARQ - размер фрагментов кэша (партиций; не имеют отношения к партициям таблицы).

Обращение к каждому фрагменту происходит независимо от остальных. Слишком большой размер партиций приведет к возрастанию конкуренции за блокировки, слишком маленький может привести к задержкам, если в партиции не окажется пригодных к вытеснению блоков (все «грязные» или занятые фоновым процессом).

Рекомендуемое значение: 128.

use_qhb_onlymem_cache (boolean)

Логический параметр. При установке значения в TRUE будет использоваться этот кэш.

qhb_onlymem_cache_size (integer)

Общий размер буферного кэша.

Внешнее хранение двоичных данных Rbytea

rbytea.filesystem_storage_path (string)

Определение каталога (точки монтирования файловой системы / тома) для сохранения образов данных.

Двоичные данные будут сохраняться в данном каталоге сервера. Для каждой базы будет создаваться свой подкаталог (по oid базы), а внутри него, во множестве подкаталогов, — собственно файлы с данными.

Имена вложенных каталогов от каталога базы данных до файла будут составлять uuid типа rbytea, а расширение файла — номер транзакции, в которой данные впервые появились в системе.

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

По умолчанию, если параметр опущен или пуст, каталогом для сохранения двоичных данных назначается '<каталог базы данных>/rbytea'.

shared_preload_libraries (string)

Загрузка разделяемой библиотеки при старте QHB.

Данный параметр обеспечивает загрузку разделяемой библиотеки при старте QHB и инициализацию фонового процесса для очистки устаревших образов. В противном случае автоочистка устаревших образов производится не будет.

Необходимо добавить значение 'librbytea'.

Примечание
Если параметр shared_preload_libraries уже содержит указание на загрузку других библиотек, нужно не перезаписать его значение, а добавить через разделитель librbytea.

rbytea.worker_restart_time (integer)

Интервал запуска фонового процесса очистки.

Фоновый процесс не работает постоянно. Поскольку данные

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

По умолчанию параметр устанавливается в значение 86400 секунд (сутки).

rbytea.databases_for_vacuuming (string)

Задание баз данных для фонового процесса очистки.

Параметр указывает, какие базы данных подлежат очистке. Указываются названия баз данных через запятую. Главный процесс базы данных будет запускать столько фоновых процессов очистки, сколько баз данных перечислено в данном параметре.

По умолчанию, параметр устанавливается в значение qhb.

rbytea.filesystem_qss_mode (integer)

Включение фонового шифрования.

Если в системе доступно фоновое шифрование при записи на диск, данный параметр позволяет зашифровывать также и двоичные данные rbytea. Для шифрования параметр необходимо установить в значение 1.

При зашифровании данные дополняются (выравниваются) до границы 16-байтных блоков, поэтому функции len(rbytea) и len_full(rbytea) для одних и тех же данных могут возвращать разное значение. А функции md5store(rbytea) и sha256store(rbytea) подсчитывают контрольные суммы для зашифрованных данных, дополненных до границы 16-байтного блока.

Вопросы смены ключа шифрования должны решаться администратором базы данных.

По умолчанию этот параметр устанавливается в значение 0.

2B: Поддержка решений 1С

Конфигурационные параметры немедленного обновления статистики

online_analyze.enable (boolean)

Включение/выключение функции немедленного анализа.

По умолчанию, параметр устанавливается в значение false.

online_analyze.verbose (boolean)

Включение/выключение возможности записи в лог при сборе статистики (аналогично команде SQL ANALYZE VERBOSE).

По умолчанию, параметр устанавливается в значение false.

online_analyze.table_type (enum)

Указывает тип таблиц, для которых следует применять немедленный анализ. Возможные варианты значений: all (все), persistent (постоянные), temporary (временные), none (никакие).

По умолчанию, параметр устанавливается в значение temporary.

online_analyze.exclude_tables (string)

Указывает список таблиц, исключаемых из немедленного анализа. Например, "имя_таблицы_1, имя_таблицы_2 ..."**.

По умолчанию, параметр устанавливается в пустое значение ('').

online_analyze.include_tables (string)

Указывает список таблиц, подлежащих немедленному анализу (этот параметр перекрывает online_analyze.exclude_tables).

По умолчанию, параметр устанавливается в пустое значение ('').

Примечание
Значения этих двух параметров записываются в виде списка имен таблиц через запятую.

online_analyze.local_tracking (boolean)

Включение/выключение хранения статистики временных таблиц в локальном кэше.

По умолчанию, параметр устанавливается в значение false

online_analyze.capacity_threshold (integer)

Указывает максимальное число временных таблиц, сохраняемых в локальном кэше.

По умолчанию, параметр устанавливается в значение 100000.

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

online_analyze.scale_factor (floating point)

Указывает процент измененных строк от всей таблицы, при котором начинается немедленный анализ.

По умолчанию, параметр устанавливается в значение 0.1.

online_analyze.threshold (integer)

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

По умолчанию, параметр устанавливается в значение 50.

online_analyze.lower_limit (integer)

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

По умолчанию, параметр устанавливается в значение 0.

online_analyze.min_interval (integer)

Минимальный интервал времени между вызовами команды ANALYZE для конкретной таблицы (в миллисекундах).

По умолчанию, параметр устанавливается в значение 10000.

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

plantuner.disable_index (string)

Указывает список индексов, которые нельзя использовать.

plantuner.enable_index (string)

Указывает список индексов, которые все-таки можно использовать (этот параметр перекрывает plantuner.disable_index).

plantuner.only_index (string)

Указывает точный список индексов, которые можно использовать (этот параметр перекрывает plantuner.disable_index и plantuner.enable_index).

Примечание
Значения этих трех параметров записываются в виде списка имен индексов через запятую.

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

plantuner.fix_empty_table (boolean)

Этот параметр меняет оценку планировщиком совсем пустых таблиц (только что созданных или опустошенных при помощи команды TRUNCATE). Если значение равно true, то QHB планирует в предположении, что эти таблицы так и будут оставаться пустыми. При значении false QHB по умолчанию будет считать, что таблицы содержат некоторое количество записей (обычно 20).

Советы по производительности

На производительность запроса могут влиять многие факторы. Некоторые из них могут контролироваться пользователем, в то время как другие имеют основополагающее значение для базовой структуры системы. В этой главе даются некоторые советы по пониманию и настройке производительности QHB.

Создание демонстрационных таблиц

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

CREATE TABLE tenk1 (
	unique1		int4,
	unique2		int4,
	two			int4,
	four		int4,
	ten			int4,
	twenty		int4,
	hundred		int4,
	thousand	int4,
	twothousand	int4,
	fivethous	int4,
	tenthous	int4,
	odd			int4,
	even		int4,
	stringu1	name,
	stringu2	name,
	string4		name
);

CREATE TABLE tenk2 (
	unique1 	int4,
	unique2 	int4,
	two 	 	int4,
	four 		int4,
	ten			int4,
	twenty 		int4,
	hundred 	int4,
	thousand 	int4,
	twothousand int4,
	fivethous 	int4,
	tenthous	int4,
	odd			int4,
	even		int4,
	stringu1	name,
	stringu2	name,
	string4		name
);

CREATE TABLE onek (
	unique1		int4,
	unique2		int4,
	two			int4,
	four		int4,
	ten			int4,
	twenty		int4,
	hundred		int4,
	thousand	int4,
	twothousand	int4,
	fivethous	int4,
	tenthous	int4,
	odd			int4,
	even		int4,
	stringu1	name,
	stringu2	name,
	string4		name
);

Для заполнения используйте SQL команды из приложения Данные для анализа производительности

ВНИМАНИЕ!!!
Приведённый выше файл, содержащий демонстрационные данные для анализа производительности, довольно большого размера и открытие его для просмотра и попытка выполнить, находящиеся в нём команды при помощи графических утилит может занять значительное время и закончится ошибкой поэтому рекомендуется скачать файл на диск и использовать утилиту psql для загрузки данных из интерфейса командной строки.

После загрузки данных необходимо выполнить заполнение таблицы и создание необходимых индексов при помощи команд :

INSERT INTO tenk2 SELECT * FROM tenk1;

CREATE INDEX tenk1_unique1 ON tenk1 USING btree(unique1 int4_ops);

CREATE INDEX tenk1_unique2 ON tenk1 USING btree(unique2 int4_ops);

CREATE INDEX tenk1_hundred ON tenk1 USING btree(hundred int4_ops);

CREATE INDEX tenk1_thous_tenthous ON tenk1 (thousand, tenthous);

CREATE INDEX tenk2_unique1 ON tenk2 USING btree(unique1 int4_ops);

CREATE INDEX tenk2_unique2 ON tenk2 USING btree(unique2 int4_ops);

CREATE INDEX tenk2_hundred ON tenk2 USING btree(hundred int4_ops);

CREATE INDEX onek_unique1 ON onek USING btree(unique1 int4_ops);

CREATE INDEX IF NOT EXISTS onek_unique1 ON onek USING btree(unique1 int4_ops);

CREATE INDEX IF NOT EXISTS ON onek USING btree(unique1 int4_ops);

CREATE INDEX onek_unique2 ON onek USING btree(unique2 int4_ops);

CREATE INDEX onek_hundred ON onek USING btree(hundred int4_ops);

CREATE INDEX onek_stringu1 ON onek USING btree(stringu1 name_ops);

Теперь необходим провести очистку таблиц при помощи команд :

vacuum analyze tenk1;

vacuum analyze tenk2;

vacuum analyze onek;

Таблицы созданы и заполнены

Выполняя примеры запросов приведённые ниже в этом разделе вы сможете получить аналогичные результаты, но ваши предполагаемые оценки затрат (COST) и количества строк могут незначительно отличаться, потому что статистика ANALYZE является случайной выборкой, а не точной, и поскольку оценки затрат по своей природе в некоторой степени зависят от платформы.

Использование EXPLAIN

QHB строит план запросов для каждого запроса, который получен от пользователя. Выбор правильного плана, соответствующего структуре запроса и свойствам данных, абсолютно необходим для хорошей производительности, поэтому в систему входит комплексный планировщик, который пытается выбрать хорошие планы. Вы можете использовать команду EXPLAIN, чтобы увидеть, какой план запроса создает планировщик для любого запроса. Чтение планов - это искусство, которое требует некоторого опыта, чтобы овладеть им, и в этом разделе описаны только основы.

В примерах используется стандартный текстовый формат EXPLAIN, который компактен и удобен для чтения человеком. Если вы хотите передать выходные данные EXPLAIN в программу для дальнейшего анализа, вы должны использовать вместо этого один из машиночитаемых форматов вывода (XML, JSON или YAML).

EXPLAIN Основы

Структура плана запроса представляет собой дерево узлов плана. Узлы на самом нижнем уровне дерева являются узлами сканирования: они возвращают необработанные строки из таблицы. Существуют разные типы узлов сканирования для разных методов доступа к таблице: последовательное сканирование (sequential scans), индексное сканирование (index scans) и индексное сканирование битовых карт (bitmap index scans). В части SQL запроса после FROM есть также источники строк, не являющиеся таблицами, такие как предложения VALUES и функции, возвращающие наборы, которые имеют свои собственные типы узлов сканирования. Если запрос требует объединения, агрегирования, сортировки или других операций с необработанными строками, то над узлами сканирования будут дополнительные узлы для выполнения этих операций. Опять же, как правило, существует несколько возможных способов выполнения этих операций, поэтому и здесь могут появляться разные типы узлов. Вывод EXPLAIN содержит одну строку для каждого узла в дереве плана, показывая базовый тип узла плюс оценки затрат, которые планировщик сделал для выполнения этого узла плана. Могут появиться дополнительные строки с отступом от итоговой строки узла, чтобы показать дополнительные свойства узла. Самая первая строка (итоговая строка для самого верхнего узла) содержит приблизительную общую стоимость выполнения плана (COST) - именно это число планировщик стремится свести к минимуму.

Вот простой пример, чтобы показать, как выглядит результат:

EXPLAIN SELECT * FROM tenk1;

                         QUERY PLAN
-------------------------------------------------------------
 Seq Scan on tenk1  (cost=0.00..445.00 rows=10000 width=244)

Поскольку в этом запросе нет WHERE, необходимо просматривать все строки таблицы, поэтому планировщик решил использовать простой план последовательного сканирования (Seq Scan). Числа после (слева направо):

cost=0.00..445.00 rows=10000 width=244
Ориентировочная стоимость запуска. Это время, затрачиваемое до начала фазы вывода, например, время на выполнение сортировки в узле сортировки.

cost=0.00..445.00 rows=10000 width=244
Ориентировочная общая стоимость. Предполагается, что узел плана выполнен до конца, то есть извлечены все доступные строки. На практике родительский узел может прервать чтение всех доступных строк (см. Пример LIMIT ниже).

cost=0.00..445.00 rows=10000 width=244
Расчетное количество строк, выводимых этим узлом плана. Опять же, предполагается, что узел полностью выполнился.

cost=0.00..445.00 rows=10000 width=244
Расчетная средняя "ширина" строк, выводимых этим узлом плана (в байтах).

Затраты измеряются в произвольных единицах, определяемых параметрами затрат планировщика (см. раздел Константы стоимости для планировщика). Как правило затрат выборки чтение страниц с диска то есть параметр seq_page_cost обычно устанавливается равным 1.0, а другие параметры стоимости устанавливаются относительно него. Примеры в этом разделе выполняются с параметрами стоимости по умолчанию.

Важно понимать, что стоимость узла верхнего уровня включает стоимость всех его дочерних узлов. Также важно понимать, что стоимость отражает только то, что интересует планировщик. В частности, стоимость не учитывает время, потраченное на передачу строк результатов клиенту, что может быть важным фактором в реальном окружении, но планировщик игнорирует это, потому что он не может скорректировать эту величину, изменив план. (Считается, что каждый правильный план будет выводить один и тот же набор строк).

Значение rows немного сложнее, потому что это не количество строк, обработанных или отсканированных узлом плана, а число, выдаваемое узлом. Это число часто меньше, чем число сканируемых строк, в результате фильтрации по любым условиям WHERE условия, которые применяются на узле. В идеальном случае оценка строк верхнего уровня будет приблизительно соответствовать количеству строк, фактически возвращенных, обновленных или удаленных запросом.

Возвращаясь к нашему примеру:

EXPLAIN SELECT * FROM tenk1;

                         QUERY PLAN
-------------------------------------------------------------
 Seq Scan on tenk1  (cost=0.00..445.00 rows=10000 width=244)

Эти числа получены очень просто. Если вы делаете:

SELECT relpages, reltuples FROM pg_class WHERE relname = 'tenk1';

вы обнаружите, что tenk1 имеет 345 страниц на диске и 10000 строк. Ориентировочная стоимость рассчитывается как (disk pages read * seq_page_cost ) + (rows scanned * cpu_tuple_cost ). По умолчанию seq_page_cost равно 1,0, а cpu_tuple_cost равно 0,01, поэтому предполагаемая стоимость составляет (445 * 1,0) + (10000 * 0,01) = 445.

Теперь давайте изменим запрос, добавив условие WHERE :

EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 7000;

                         QUERY PLAN
------------------------------------------------------------
 Seq Scan on tenk1  (cost=0.00..470.00 rows=7000 width=244)
   Filter: (unique1 < 7000)
(2 rows)

Обратите внимание, что вывод EXPLAIN показывает WHERE, применяемое в качестве условия Filter, присоединённого к узлу плана Seq Scan. Это означает, что узел плана проверяет условие для каждой сканируемой строки и выводит только те, которые подходят под условие. Оценка количества выходных строк была уменьшена из-за WHERE. Тем не менее,все равно придется просмотреть все 10000 строк, поэтому стоимость не снизилась; на самом деле он немного увеличилась (на 10000 * cpu_operator_cost, если точно), чтобы отразить дополнительное время ЦП, затраченное на проверку условия WHERE .

Фактическое количество строк, которое выберет этот запрос, равно 7000, но оценка количества rows только приблизительная. Если вы попытаетесь повторить этот эксперимент, вы, вероятно, получите немного другую оценку - более того, он может меняться после каждой команды ANALYZE, потому что статистика, полученная с помощью ANALYZE для таблицы, берется из случайной выборки строк.

Теперь давайте сделаем условие более ограничительным:

EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100;

                                  QUERY PLAN
------------------------------------------------------------------------------
 Bitmap Heap Scan on tenk1  (cost=5.06..224.98 rows=100 width=244)
   Recheck Cond: (unique1 < 100)
   ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..5.04 rows=100 width=0)
         Index Cond: (unique1 < 100)
(4 rows)

Здесь планировщик решил использовать двухэтапный план: дочерний узел плана просматривает индекс, чтобы найти местоположение строк в страницах таблицы, соответствующих условию индекса, а затем родительский узел плана фактически выбирает эти строки из самой таблицы. Извлечение строк по отдельности намного дороже, чем их последовательное чтение, но поскольку не все страницы таблицы нужно считывать, это все же дешевле, чем последовательное сканирование. (Причина использования двух уровней плана состоит в том, что узел верхнего плана сортирует местоположения строк, идентифицированные индексом, в некоторый физический порядок перед их чтением, чтобы минимизировать стоимость отдельных выборок. Bitmap Index Scan, упомянутый в имени узла, - это и есть механизм, который выполняет сортировку).

Теперь давайте добавим еще одно условие к WHERE :

EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND stringu1 = 'xxx';

                                  QUERY PLAN
------------------------------------------------------------------------------
 Bitmap Heap Scan on tenk1  (cost=5.04..225.20 rows=1 width=244)
   Recheck Cond: (unique1 < 100)
   Filter: (stringu1 = 'xxx'::name)
   ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..5.04 rows=100 width=0)
         Index Cond: (unique1 < 100)
(5 rows)

Добавленное условие stringu1 = 'xxx' уменьшает оценку количества выходных строк, но не стоимость, потому что нам все еще приходится считывать тот же набор строк. Обратите внимание, что столбец stringu1 нельзя применять для использования индекса, поскольку этот индекс только для столбца unique1. Вместо этого он применяется в качестве фильтра для строк, извлекаемых индексом. Таким образом, стоимость фактически немного увеличилась, чтобы отразить эту дополнительную проверку.

В некоторых случаях планировщик предпочтет « простой » план сканирования индекса Index Scan :

EXPLAIN SELECT * FROM tenk1 WHERE unique1 = 42;

                                 QUERY PLAN
-----------------------------------------------------------------------------
 Index Scan using tenk1_unique1 on tenk1  (cost=0.29..8.30 rows=1 width=244)
   Index Cond: (unique1 = 42)
(2 rows)

В этом плане строки таблицы выбираются при помощи индекса, что делает их еще более дорогими для чтения, но их так мало, что дополнительные затраты на сортировку местоположений строк не изменят производительность. Чаще всего план такого типа используется для запросов, которые выбирают только одну строку. Он также часто используется для запросов, которые имеют условие ORDER BY, соответствующее порядку индекса, потому что тогда не требуется никакого дополнительного шага сортировки для выполнения сортировки указанной в ORDER BY .

Если в нескольких столбцах, на которые есть ссылки в WHERE, имеются отдельные индексы, планировщик может выбрать использование комбинации индексов AND или OR:

EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000;

                                     QUERY PLAN
-------------------------------------------------------------------------------------
 Bitmap Heap Scan on tenk1  (cost=25.07..60.11 rows=10 width=244)
   Recheck Cond: ((unique1 < 100) AND (unique2 > 9000))
   ->  BitmapAnd  (cost=25.07..25.07 rows=10 width=0)
         ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..5.04 rows=100 width=0)
               Index Cond: (unique1 < 100)
         ->  Bitmap Index Scan on tenk1_unique2  (cost=0.00..19.78 rows=999 width=0)
               Index Cond: (unique2 > 9000)
(7 rows)

Такой план требует чтения из обоих индексов, поэтому он не обязательно даст выигрыш по сравнению с использованием только одного индекса и обработкой другого условия как фильтра. Если вы измените соответствующие диапазоны, вы увидите, что план изменится соответствующим образом.

Вот пример, показывающий влияние LIMIT :

EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;
                                     QUERY PLAN
-------------------------------------------------------------------------------------
 Limit  (cost=0.29..14.28 rows=2 width=244)
   ->  Index Scan using tenk1_unique2 on tenk1  (cost=0.29..70.27 rows=10 width=244)
         Index Cond: (unique2 > 9000)
         Filter: (unique1 < 100)
(4 rows)

Это тот же запрос, что и выше, но мы добавили LIMIT чтобы не все строки были извлечены, и планировщик передумал, это делать. Обратите внимание, что общая стоимость и количество строк узла сканирования индекса (Index Scan) отображаются так, как если бы он был выполнен до конца. Однако ожидается, что узел Limit остановится после получения только одной пятой из этих строк, поэтому его общая стоимость составляет всего одну пятую, и это фактическая оценочная стоимость запроса. Этот план предпочтительнее, чем добавление узла Limit к предыдущему плану, потому что Limit не мог избежать начальной стоимости Bitmap Index Scan, поэтому общая стоимость, при таком подходе, будет примерно 14 единиц.

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

EXPLAIN SELECT *
FROM tenk1 t1, tenk2 t2
WHERE t1.unique1 < 10 AND t1.unique2 = t2.unique2;

                                      QUERY PLAN
--------------------------------------------------------------------------------------
 Nested Loop  (cost=4.65..118.50 rows=10 width=488)
   ->  Bitmap Heap Scan on tenk1 t1  (cost=4.36..39.38 rows=10 width=244)
         Recheck Cond: (unique1 < 10)
         ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..4.36 rows=10 width=0)
               Index Cond: (unique1 < 10)
   ->  Index Scan using tenk2_unique2 on tenk2 t2  (cost=0.29..7.90 rows=1 width=244)
         Index Cond: (unique2 = t1.unique2)
(7 rows)

В этом плане у нас есть узел соединения с вложенным циклом (Nested Loop) с двумя дочерними сканированиями таблиц (tenk1 t1 и tenk2 t2) в качестве входных. Отступ строки сводки узла отражает структуру дерева плана. Первый, или «внешний», дочерний объект объединения - это Bitmap Index Scan, похожее на то, что мы видели ранее. Его стоимость и количество строк такие же, как мы получили бы из SELECT ... WHERE unique1 < 10 потому что мы применяем WHERE unique1 <> 10 для этого узла. Предложение t1.unique2 = t2.unique2 пока не актуально, поэтому оно не влияет на количество строк внешнего сканирования. Узел соединения с вложенным циклом (Nested Loop) будет запускать свой второй или «внутренний» дочерний элемент один раз для каждой строки, полученной из внешнего дочернего элемента. Значения столбцов из текущего внешней строки могут быть включены во внутреннее сканирование; здесь доступно значение t1.unique2 из внешней строки, поэтому мы получаем план и затраты, аналогичные тем, что мы видели выше для простого случая SELECT ... WHERE t2.unique2 = constant. (Предполагаемая стоимость на самом деле немного ниже, чем было показано выше, в результате кэширования, которое, как ожидается, произойдет во время повторного сканирования индекса в t2 ). Затем стоимость узла цикла устанавливается на основе стоимости внешнего сканирование, плюс одно повторение внутреннего сканирования для каждой внешней строки (это 10 * 7,90), а также немного процессорного времени для обработки соединения.

В этом примере число выходных строк в соединении совпадает с произведением количества строк в двух сканированиях, но это почти всегда не так, потому что могут быть дополнительные WHERE которые упоминают обе таблицы и поэтому могут применяться только в точке соединения, а не для входного сканирования. Вот пример:

EXPLAIN SELECT *
FROM tenk1 t1, tenk2 t2
WHERE t1.unique1 < 10 AND t2.unique2 < 10 AND t1.hundred < t2.hundred;

                                         QUERY PLAN
---------------------------------------------------------------------------------------------
 Nested Loop  (cost=4.65..49.36 rows=33 width=488)
   Join Filter: (t1.hundred < t2.hundred)
   ->  Bitmap Heap Scan on tenk1 t1  (cost=4.36..39.38 rows=10 width=244)
         Recheck Cond: (unique1 < 10)
         ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..4.36 rows=10 width=0)
               Index Cond: (unique1 < 10)
   ->  Materialize  (cost=0.29..8.51 rows=10 width=244)
         ->  Index Scan using tenk2_unique2 on tenk2 t2  (cost=0.29..8.46 rows=10 width=244)
               Index Cond: (unique2 < 10)
(9 rows)

Условие t1.hundred < t2.hundred не может быть проверено в индексе tenk2_unique2, поэтому оно применяется на узле соединения. Это уменьшает приблизительное количество выходных строк узла соединения, но не изменяет ни сканирование в дочерних узлах.

Обратите внимание, что здесь планировщик решил «материализовать» внутреннее отношение объединения, поместив поверх него узел плана Materialize. Это означает, что сканирование индекса t2 будет выполнено только один раз, даже если узел соединения с вложенным циклом должен прочитать эти данные десять раз, по одному разу для каждой строки из внешнего отношения. Узел Materialise сохраняет данные в памяти при чтении, а затем возвращает данные из памяти при каждом последующем проходе.

При работе с внешними объединениями вы можете увидеть узлы плана соединения с присоединенными фильтром объединения Join Filter . Условия фильтра объединения взяты из предложения ON внешнего соединения, поэтому строка, которая не соответствует условию фильтра соединения, все равно может быть выдана как строка с нулевым расширением.

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

EXPLAIN SELECT *
FROM tenk1 t1, tenk2 t2
WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2;

                                       QUERY PLAN
------------------------------------------------------------------------------------------
 Hash Join  (cost=226.23..709.73 rows=100 width=488)
   Hash Cond: (t2.unique2 = t1.unique2)
   ->  Seq Scan on tenk2 t2  (cost=0.00..445.00 rows=10000 width=244)
   ->  Hash  (cost=224.98..224.98 rows=100 width=244)
         ->  Bitmap Heap Scan on tenk1 t1  (cost=5.06..224.98 rows=100 width=244)
               Recheck Cond: (unique1 < 100)
               ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..5.04 rows=100 width=0)
                     Index Cond: (unique1 < 100)
(8 rows)

Здесь планировщик выбрал использование хеш-соединения, в котором строки одной таблицы заносятся в хеш-таблицу в памяти, после чего сканируется другая таблица и проверяется на совпадение каждой строки в хеш-таблице. Снова обратите внимание, как отступ отражает структуру плана: Bitmap Heap Scan on tenk1 является входом для узла Hash, который построил хеш-таблицу для tenk1 Затем он возвращается в узел Hash Join, который читает строки из своего внешнего дочернего плана и ищет в каждой хеш-таблице.

Другой возможный тип объединения - это соединение слиянием, показанное здесь:

EXPLAIN SELECT *
FROM tenk1 t1, onek t2
WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2;

                                        QUERY PLAN
------------------------------------------------------------------------------------------
 Merge Join  (cost=95.11..164.54 rows=10 width=488)
   Merge Cond: (t1.unique2 = t2.unique2)
   ->  Index Scan using tenk1_unique2 on tenk1 t1  (cost=0.29..643.28 rows=100 width=244)
         Filter: (unique1 < 100)
   ->  Sort  (cost=94.83..97.33 rows=1000 width=244)
         Sort Key: t2.unique2
         ->  Seq Scan on onek t2  (cost=0.00..45.00 rows=1000 width=244)
(7 rows)

Объединение слиянием (Merge Join) требует, чтобы его входные данные были отсортированы по ключам объединения. В этом плане данные tenk1 сортируются с использованием сканирования индекса для обхода строк в правильном порядке, но для onek предпочтительнее последовательное сканирование (Seq Scan) и сортировка (Sort), поскольку в этой таблице нужно посетить еще много строк. (Последовательное сканирование-сортировка часто превосходит сканирование индекса для сортировки множества строк из-за непоследовательного доступа к диску, необходимого для сканирования индекса).

Один из способов посмотреть на варианты планов - заставить планировщика игнорировать любую стратегию, которая, по его мнению, была самой дешевой, используя флаги включения / выключения, описанные в разделе Конфигурация метода планирования. (Это грубый, но полезный инструмент см. также раздел Управление планировщиком с помощью явных предложений JOIN). Например, если мы не уверены, что последовательное сканирование и сортировка - лучший способ справиться с таблицей onek в предыдущем примере, мы можем попробовать

SET enable_sort = off;

EXPLAIN SELECT *
FROM tenk1 t1, onek t2
WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2;

                                        QUERY PLAN
------------------------------------------------------------------------------------------
 Hash Join  (cost=226.23..275.08 rows=10 width=488)
   Hash Cond: (t2.unique2 = t1.unique2)
   ->  Seq Scan on onek t2  (cost=0.00..45.00 rows=1000 width=244)
   ->  Hash  (cost=224.98..224.98 rows=100 width=244)
         ->  Bitmap Heap Scan on tenk1 t1  (cost=5.06..224.98 rows=100 width=244)
               Recheck Cond: (unique1 < 100)
               ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..5.04 rows=100 width=0)
                     Index Cond: (unique1 < 100)
(8 rows)

что показывает, что планировщик считает, что сортировка onek помощью сканирования по индексу примерно на 12% дороже, чем последовательное сканирование и сортировка. Конечно, следующий вопрос - правильно ли это? Мы можем исследовать это, используя EXPLAIN ANALYZE, как описано ниже.

EXPLAIN ANALYZE

Можно проверить точность оценок планировщика, используя опцию EXPLAIN ’s ANALYZE. С помощью этой опции EXPLAIN фактически выполняет запрос, а затем отображает истинное количество строк и истинное время выполнения, накопленные в каждом узле плана, а также те же оценки, которые показывает обычный EXPLAIN. Например, мы можем получить такой результат:

EXPLAIN ANALYZE SELECT *
FROM tenk1 t1, tenk2 t2
WHERE t1.unique1 < 10 AND t1.unique2 = t2.unique2;

                                                          QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=4.65..118.50 rows=10 width=488) (actual time=0.108..0.406 rows=10 loops=1)
   ->  Bitmap Heap Scan on tenk1 t1  (cost=4.36..39.38 rows=10 width=244) (actual time=0.061..0.135 rows=10 loops=1)
         Recheck Cond: (unique1 < 10)
         Heap Blocks: exact=10
         ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..4.36 rows=10 width=0) (actual time=0.038..0.038 rows=10 loops=1)
               Index Cond: (unique1 < 10)
   ->  Index Scan using tenk2_unique2 on tenk2 t2  (cost=0.29..7.90 rows=1 width=244) (actual time=0.022..0.023 rows=1 loops=10)
         Index Cond: (unique2 = t1.unique2)
 Planning Time: 0.953 ms
 Execution Time: 0.517 ms
(10 rows)

Обратите внимание, что значения «фактического времени» actual time приведены в миллисекундах реального времени, тогда как оценки cost выражены в произвольных единицах; поэтому они вряд ли совпадают. Обычно наиболее важно искать, достаточно ли приблизительное число строк соответствует действительности. В этом примере все оценки были точными, но на практике это довольно необычно.

В некоторых планах запросов узел подплана может выполняться более одного раза. Например, сканирование внутреннего индекса будет выполняться один раз для каждой внешней строки в приведенном выше плане вложенного цикла. В таких случаях значение цикла сообщает об общем количестве выполнений узла, а показанные фактические значения времени и строк представляют собой средние значения для каждого выполнения. Это сделано для того, чтобы сделать числа сопоставимыми с тем, как отображаются оценки затрат. Умножьте на значение цикла, чтобы получить общее время, фактически потраченное на узел. В приведенном выше примере мы потратили 0,220 миллисекунды на выполнение сканирования индекса на tenk2 .

В некоторых случаях EXPLAIN ANALYZE показывает дополнительную статистику выполнения помимо времени выполнения узла плана и количества строк. Например, узлы Sort и Hash предоставляют дополнительную информацию:

EXPLAIN ANALYZE SELECT *
FROM tenk1 t1, tenk2 t2
WHERE t1.unique1 < 100 AND t1.unique2 = t2.unique2 ORDER BY t1.fivethous;

                                                                 QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=713.05..713.30 rows=100 width=488) (actual time=4.184..4.195 rows=100 loops=1)
   Sort Key: t1.fivethous
   Sort Method: quicksort  Memory: 77kB
   ->  Hash Join  (cost=226.23..709.73 rows=100 width=488) (actual time=0.387..4.019 rows=100 loops=1)
         Hash Cond: (t2.unique2 = t1.unique2)
         ->  Seq Scan on tenk2 t2  (cost=0.00..445.00 rows=10000 width=244) (actual time=0.011..1.688 rows=10000 loops=1)
         ->  Hash  (cost=224.98..224.98 rows=100 width=244) (actual time=0.350..0.350 rows=100 loops=1)
               Buckets: 1024  Batches: 1  Memory Usage: 35kB
               ->  Bitmap Heap Scan on tenk1 t1  (cost=5.06..224.98 rows=100 width=244) (actual time=0.039..0.300 rows=100 loops=1)
                     Recheck Cond: (unique1 < 100)
                     Heap Blocks: exact=90
                     ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..5.04 rows=100 width=0) (actual time=0.022..0.022 rows=100 loops=1)
                           Index Cond: (unique1 < 100)
 Planning Time: 1.542 ms
 Execution Time: 4.327 ms
(15 rows)

Узел Sort показывает используемый метод сортировки (в частности, была ли сортировка в памяти или на диске) и необходимый объем памяти или дискового пространства. Узел Hash показывает количество хеш-партиций и пакетов, а также пиковый объем памяти, используемый для хеш-таблицы. (Если количество пакетов превышает единицу, также будет задействовано использование дискового пространства, но это не показано).

Другой тип дополнительной информации - это количество строк, удаленных условием фильтра:

EXPLAIN ANALYZE SELECT * FROM tenk1 WHERE ten < 7;

                                                QUERY PLAN
----------------------------------------------------------------------------------------------------------
 Seq Scan on tenk1  (cost=0.00..470.00 rows=7000 width=244) (actual time=0.038..42.547 rows=7000 loops=1)
   Filter: (ten < 7)
   Rows Removed by Filter: 3000
 Planning Time: 0.176 ms
 Execution Time: 43.762 ms
(5 rows)

Эти подсчеты могут быть особенно полезны для условий фильтрации, применяемых в узлах соединения. Строка «Rows Removed» появляется только тогда, когда хотя бы одна отсканированная строка или пара возможных соединений в случае узла соединения отклонена условием фильтрации.

У EXPLAIN есть опция BUFFERS которую можно использовать с ANALYZE для получения еще большей статистики времени выполнения:

EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000;

                                                           QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on tenk1  (cost=25.07..60.11 rows=10 width=244) (actual time=0.964..1.033 rows=10 loops=1)
   Recheck Cond: ((unique1 < 100) AND (unique2 > 9000))
   Heap Blocks: exact=10
   Buffers: shared hit=14 read=3
   ->  BitmapAnd  (cost=25.07..25.07 rows=10 width=0) (actual time=0.942..0.943 rows=0 loops=1)
         Buffers: shared hit=4 read=3
         ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..5.04 rows=100 width=0) (actual time=0.056..0.056 rows=100 loops=1)
               Index Cond: (unique1 < 100)
               Buffers: shared hit=2
         ->  Bitmap Index Scan on tenk1_unique2  (cost=0.00..19.78 rows=999 width=0) (actual time=0.863..0.863 rows=999 loops=1)
               Index Cond: (unique2 > 9000)
               Buffers: shared hit=2 read=3
 Planning Time: 0.400 ms
 Execution Time: 1.113 ms
(14 rows)

Число BUFFERS помогает определить, какие части запроса наиболее интенсивны при вводе-выводе.

Имейте в виду, что поскольку EXPLAIN ANALYZE фактически выполняет запрос, любые побочные эффекты будут происходить как и при обычном выполнении запроса, даже если любые результаты, которые может выдать запрос, будут отброшены и вместо них выводится EXPLAIN. Если вы хотите проанализировать запрос на изменение данных без изменения таблиц, вы можете откатить команду позже, например:

BEGIN;

EXPLAIN ANALYZE UPDATE tenk1 SET hundred = hundred + 1 WHERE unique1 < 100;

                                                           QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
 Update on tenk1  (cost=5.07..229.46 rows=101 width=250) (actual time=14.628..14.628 rows=0 loops=1)
   ->  Bitmap Heap Scan on tenk1  (cost=5.07..229.46 rows=101 width=250) (actual time=0.101..0.439 rows=100 loops=1)
         Recheck Cond: (unique1 < 100)
         ->  Bitmap Index Scan on tenk1_unique1  (cost=0.00..5.04 rows=101 width=0) (actual time=0.043..0.043 rows=100 loops=1)
               Index Cond: (unique1 < 100)
 Planning time: 0.079 ms
 Execution time: 14.727 ms

ROLLBACK;

Как видно из этого примера, когда запрос представляет собой команду INSERT, UPDATE или DELETE, фактическая работа по изменению данных таблицы выполняется узлом плана INSERT, UPDATE или DELETE. Узлы плана под этим узлом выполняют поиск строк и / или вычисляют новые данные. Итак, выше мы видим тот же Bitmap Index Scan, который мы уже видели, и его выходные данные передаются в узел update, в котором хранятся обновленные строки. Стоит отметить, что хотя узел, модифицирующий данные, может выполнятся значительное время (в этом примере он занимает основную долю времени) выполнения, планировщик в настоящее время ничего не добавляет к оценкам затрат для учета этой работы. Это связано с тем, что выполняемая работа одинакова для каждого правильного плана запроса, поэтому она не влияет на решения по планированию.

Предостережения

Существует два основных случая, когда время выполнения, измеренное с помощью EXPLAIN ANALYZE может отличаться от нормального выполнения того же запроса. Во-первых, поскольку выходные строки не доставляются клиенту, затраты на передачу по сети и затраты на преобразование ввода / вывода не включены. Во-вторых, накладные расходы на измерения, добавленные EXPLAIN ANALYZE могут быть значительными, особенно на машинах с медленными системными вызовами операционной системы gettimeofday().

Результаты EXPLAIN не следует экстраполировать на ситуации, сильно отличающиеся от тех, которые вы фактически проверяете, например, нельзя предполагать, что результаты на таблице с крошечным количеством тестовых данных применимы к большим таблицам. Оценки стоимости планировщика не являются линейными, и поэтому он может выбрать другой план для таблицы большего или меньшего размера. Граничным условием, например, является то, что для таблицы, занимающей только одну страницу диска, почти всегда выбирается план с последовательным сканированием, независимо от того, доступны индексы или нет. Планировщик понимает, что для обработки таблицы в любом случае потребуется одно чтение страницы на диске, поэтому нет смысла тратить дополнительные операции чтения страницы для просмотра индекса.

Есть случаи, когда фактические и оценочные значения не совпадают, но на самом деле все в порядке. Один из таких случаев возникает, когда выполнение узла плана останавливается с помощью LIMIT или аналогичного эффекта. Например, в запросе LIMIT мы использовали ранее,

EXPLAIN ANALYZE SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;
                                                          QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.29..14.28 rows=2 width=244) (actual time=1.196..1.321 rows=2 loops=1)
   ->  Index Scan using tenk1_unique2 on tenk1  (cost=0.29..70.27 rows=10 width=244) (actual time=1.194..1.319 rows=2 loops=1)
         Index Cond: (unique2 > 9000)
         Filter: (unique1 < 100)
         Rows Removed by Filter: 287
 Planning Time: 7.903 ms
 Execution Time: 1.557 ms
(7 rows)

оценка стоимости и количества строк для узла Index Scan отображаются так, как если бы он был выполнен до конца. Но в действительности узел Limit прекратил запрашивать строки после того, как он получил две строки, поэтому фактическое количество строк составляет только 2, а время выполнения меньше, чем можно предположить в оценке затрат. Это не ошибка оценки, а только несоответствие в способе отображения оценок и истинных значений.

Объединения слиянием (Merge Join) также имеют артефакты измерения, которые могут запутать. Объединение слиянием прекратит чтение одного ввода, если оно исчерпало другой ввод, а следующее значение ключа в одном входе больше, чем значение последнего ключа другого ввода; в таком случае больше не может быть совпадений, поэтому нет необходимости сканировать оставшуюся часть первого ввода. Это приводит к тому, что не выполняется полное чтение одного дочернего элемента, с результатами, подобными упомянутым для LIMIT. Кроме того, если внешний (первый) дочерний элемент содержит строки с дублирующимися значениями ключа, внутренний (второй) дочерний объект резервируется и повторно сканируется для части его строк, соответствующей этому значению ключа. EXPLAIN ANALYZE считает эти повторные исключения одних и тех же внутренних рядов, как если бы они были настоящими дополнительными рядами. Когда существует много внешних дубликатов, сообщаемое фактическое число строк для внутреннего дочернего узла плана может быть значительно больше, чем число строк, которые фактически находятся во внутреннем отношении.

BitmapAnd и BitmapOr всегда сообщают, что их фактическое число строк равно нулю из-за ограничений реализации.

Обычно EXPLAIN отображает каждый узел плана, созданный планировщиком. Однако в некоторых случаях исполнитель (executor) может определить, что определенные узлы не нужно выполнять, поскольку они не могут создавать строки, основываясь на значениях параметров, которые были недоступны во время планирования. (В настоящее время это может происходить только для дочерних узлов узла Append или MergeAppend, который сканирует таблицу с партициями). Когда это происходит, эти узлы плана исключаются из вывода EXPLAIN, и вместо этого появляется аннотация Subplans Removed: N.

Статистика, используемая планировщиком

Статистика по одному столбцу

Как мы видели в предыдущем разделе, планировщик запросов должен оценить количество строк, извлеченных запросом, чтобы сделать правильный выбор плана запроса. В этом разделе приводится краткий обзор статистики, которую система использует для этих оценок.

Одним из компонентов статистики - общее количество записей в каждой таблице и индексе, а также количество дисковых блоков, занимаемых каждой таблицей и индексом. Эта информация хранится в таблице pg_class, в столбцах reltuples и relpages. Мы можем посмотреть на это с помощью запросов, похожих на этот:

SELECT relname, relkind, reltuples, relpages
FROM pg_class
WHERE relname LIKE 'tenk1%';

       relname        | relkind | reltuples | relpages
----------------------+---------+-----------+----------
 tenk1                | r       |     10000 |      345
 tenk1_hundred        | i       |     10000 |       30
 tenk1_thous_tenthous | i       |     10000 |       30
 tenk1_unique1        | i       |     10000 |       30
 tenk1_unique2        | i       |     10000 |       30
(5 rows)

Здесь мы можем видеть, что tenk1 содержит 10000 строк, как и его индексы, но индексы (что неудивительно) намного меньше таблицы.

По соображениям эффективности reltuples и relpages не обновляются на лету, и поэтому они обычно содержат несколько устаревших значений. Они обновляются с помощью VACUUM, ANALYZE и нескольких команд DDL, таких как CREATE INDEX. Операция VACUUM или ANALYZE, которая не сканирует всю таблицу (что обычно имеет место), будет постепенно обновлять число reltuples на основе части таблицы, которую она просканировала, что приведет к приблизительному значению. В любом случае планировщик будет масштабировать значения, которые он находит в pg_class чтобы соответствовать текущему размеру физической таблицы, таким образом получая более точное приближение.

Большинство запросов извлекают только часть строк в таблице из-за WHERE которые ограничивают строки, подлежащие проверке. Таким образом, планировщик должен оценить селективность WHERE, то есть долю строк, соответствующих каждому условию в WHERE. Информация, используемая для этой задачи, хранится в системном каталоге pg_statistic. Записи в pg_statistic обновляются командами ANALYZE и VACUUM ANALYZE и всегда являются приблизительными, даже если они обновлены.

Вместо непосредственного просмотра pg_statistic лучше взглянуть на его представление pg_stats при ручном анализе статистики. pg_stats предназначен для более удобного чтения. Кроме того, pg_stats доступен для чтения всем, тогда как pg_statistic доступен для чтения только суперпользователю. (Это не позволяет непривилегированным пользователям узнавать что-либо о содержимом таблиц других людей из статистики. Представление pg_stats ограничено отображением только строк о таблицах, которые может прочитать текущий пользователь).

Объем информации, хранимой в pg_statistic функцией ANALYZE, в частности максимальное количество записей в массивах most_common_vals и histogram_bounds для каждого столбца, можно установить для каждого столбца с помощью команды ALTER TABLE SET STATISTICS или глобально, установив переменной конфигурации default_statistics_target. Предел по умолчанию в настоящее время составляет 100 записей. Повышение лимита может позволить сделать более точные оценки планировщика, особенно для столбцов с нерегулярным распределением данных, за счет того, что они потребляют больше места в pg_statistic и немного больше времени для вычисления оценок. И наоборот, нижний предел может быть достаточным для столбцов с простым распределением данных.

Более подробную информацию об использовании статистики планировщиком можно найти в главе Как планировщик использует статистику.

Расширенная статистика

Обычно медленные запросы выполняются по плохим планы выполнения, поскольку несколько столбцов, используемых в предложениях запросов, коррелированы. Планировщик обычно предполагает, что несколько условий не зависят друг от друга, предположение, которое не выполняется, когда значения столбцов коррелируют между собой. Регулярные статистические данные из-за того, что они собраны для отдельных столбцов не могут собирать какие-либо данные о корреляции между столбцами. Однако у QHB есть возможность вычислять многомерную статистику, которая может собирать такую информацию.

Поскольку число возможных комбинаций столбцов очень велико, нецелесообразно автоматически рассчитывать многомерную статистику. Вместо этого можно создавать расширенные объекты статистики, которые чаще всего называют просто объектами статистики, чтобы дать серверу команду получать статистику по интересующим наборам столбцов.

Объекты статистики создаются с помощью команды CREATE STATISTICS . Создание такого объекта просто создает запись каталога, обозначающую интерес к статистике. Сбор фактических данных выполняется ANALYZE (либо ручной командой, либо в фоне при выполнении автоматических процессов). Собранные значения можно просмотреть в каталоге pg_statistic_ext_data.

ANALYZE вычисляет расширенную статистику на основе той же выборки строк таблицы, которая используется для вычисления обычной статистики из одного столбца. Поскольку размер выборки увеличивается за счет увеличения цели статистики для таблицы или любого из ее столбцов (как описано в предыдущем разделе), большая цель статистики обычно приводит к более точной расширенной статистике, а также к большему времени, затрачиваемому на ее вычисление.

В следующих подразделах описываются виды расширенной статистики, которые в настоящее время поддерживаются.

Функциональные зависимости

Самый простой вид расширенной статистики отслеживает функциональные зависимости, понятие, используемое в определениях нормальных форм базы данных. Мы говорим, что столбец b функционально зависит от столбца a если знание значения a достаточно для определения значения b, то есть нет двух строк, имеющих одинаковое значение a но разные значения b. В полностью нормализованной базе данных функциональные зависимости должны существовать только от первичных ключей и суперключей. Однако на практике многие наборы данных не полностью нормализованы по разным причинам - преднамеренная денормализация по причинам производительности является распространенным примером. Даже в полностью нормализованной базе данных между некоторыми столбцами может быть частичная корреляция, которая может быть выражена как частичная функциональная зависимость.

Наличие функциональных зависимостей напрямую влияет на точность оценок в определенных запросах. Если запрос содержит условия как для независимого, так и для зависимого столбца (столбцов), условия для зависимых столбцов не уменьшают размер результата, но без знания функциональной зависимости планировщик запросов будет предполагать, что условия независимы, что приводит к занижению размера результата.

Чтобы сообщить планировщику о функциональных зависимостях, ANALYZE может собирать измерения зависимости между столбцами. Оценка степени зависимости между всеми наборами столбцов была бы чрезмерно дорогой, поэтому сбор данных ограничивается теми группами столбцов, которые появляются вместе в статистическом объекте, определенном с помощью параметра dependencies. Желательно создавать статистику с dependencies только для групп столбцов, которые сильно коррелированы, чтобы избежать ненужных накладных расходов как при ANALYZE и при дальнейшем планировании запросов.

Вот пример сбора статистики функциональной зависимости:

CREATE STATISTICS stts (dependencies) ON city, zip FROM zipcodes;

ANALYZE zipcodes;

SELECT stxname, stxkeys, stxddependencies
  FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid)
  WHERE stxname = 'stts';
 stxname | stxkeys |             stxddependencies
---------+---------+------------------------------------------
 stts    | 1 5     | {"1 => 5": 1.000000, "5 => 1": 0.423130}
(1 row)

Здесь видно, что столбец 1 (почтовый индекс) полностью определяет столбец 5 (город), поэтому коэффициент равен 1,0, тогда как город определяет почтовый индекс только в 42% случаев, что означает, что существует много городов (58%), которые представлены более чем одним почтовым индексом.

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

Ограничения функциональных зависимостей

Функциональные зависимости в настоящее время применяются только при рассмотрении простых условий равенства, которые сравнивают столбцы с постоянными значениями. Они не используются для улучшения оценок условий равенства, сравнивающих два столбца или сравнения столбца с выражением, ни для предложений диапазона, LIKE или любого другого типа условия.

При оценке с использованием функциональных зависимостей планировщик предполагает, что условия на задействованных столбцах являются совместимыми и, следовательно, избыточными. Если они несовместимы, правильная оценка будет равна нулю строк, но эта возможность не рассматривается. Например, учитывая запрос как

SELECT * FROM zipcodes WHERE city = 'San Francisco' AND zip = '94105';

планировщик игнорирует пункт о city как не изменяющий избирательность, что является правильным. Тем не менее, он будет делать то же самое предположение о

SELECT * FROM zipcodes WHERE city = 'San Francisco' AND zip = '90210';

даже если на самом деле будет ноль строк, удовлетворяющих этому запросу. Однако статистика функциональной зависимости не дает достаточно информации, чтобы сделать вывод, что это так.

Во многих практических ситуациях это предположение обычно выполняется; например, в приложении может быть графический интерфейс, который позволяет выбирать только совместимые значения города и почтового индекса для использования в запросе. Но если это не так, функциональные зависимости не могут быть приемлемым вариантом.

Многовариантное N-мерное число уникальных значений

Статистика по одному столбцу хранит количество различных значений в каждом столбце. Оценки количества различных значений при объединении нескольких столбцов (например, для GROUP BY a, b ) часто неверны, когда у планировщика есть только статистические данные из одного столбца, что приводит к выбору плохих планов.

Чтобы улучшить такие оценки, ANALYZE может собирать n-различных статистических данных для групп столбцов. Как и прежде, это нецелесообразно делать для каждой возможной группировки столбцов, поэтому данные собираются только для тех групп столбцов, которые появляются вместе в объекте статистики, определенном с помощью опции ndistinct. Данные будут собираться для каждой возможной комбинации двух или более столбцов из набора перечисленных столбцов.

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

CREATE STATISTICS stts2 (ndistinct) ON city, state, zip FROM zipcodes;

ANALYZE zipcodes;

SELECT stxkeys AS k, stxdndistinct AS nd
  FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid)
  WHERE stxname = 'stts2';
-[ RECORD 1 ]--------------------------------------------------------
k  | 1 2 5
nd | {"1, 2": 33178, "1, 5": 33178, "2, 5": 27435, "1, 2, 5": 33178}
(1 row)

Это означает, что существует три комбинации столбцов, которые имеют 33178 различных значений: почтовый индекс и состояние; Почтовый индекс и город; и почтовый индекс, город и штат (ожидается, что все они равны, учитывая, что только один почтовый индекс уникален в этой таблице). С другой стороны, комбинация города и штата имеет только 27435 различных значений.

Целесообразно создавать ndistinct объекты статистики только по тем комбинациям столбцов, которые фактически используются для группировки и для которых неправильная оценка количества групп приводит к плохим планам. В противном случае расчёты статистики внутри ANALYZE будут напрасны.

Многомерные списки MCV

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

Чтобы улучшить такие оценки, ANALYZE может собирать списки MCV по комбинациям столбцов. Подобно функциональным зависимостям и n-мерным уникальным значениям, это нецелесообразно делать для каждой возможной группировки столбцов. Тем более для MCV, поскольку список MCV (в отличие от функциональных зависимостей и n-различных коэффициентов) хранит общие значения столбцов. Таким образом, данные собираются только для тех групп столбцов, которые появляются вместе в объекте статистики, определенном с опцией mcv .

Продолжая предыдущий пример, список MCV для таблицы почтовых индексов может выглядеть следующим образом (в отличие от более простых типов статистики, для проверки содержимого MCV требуется функция):

CREATE STATISTICS stts3 (mcv) ON city, state FROM zipcodes;

ANALYZE zipcodes;

SELECT m.* FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid),
                pg_mcv_list_items(stxdmcv) m WHERE stxname = 'stts3';

 index |         values         | nulls | frequency | base_frequency
-------+------------------------+-------+-----------+----------------
     0 | {Washington, DC}       | {f,f} |  0.003467 |        2.7e-05
     1 | {Apo, AE}              | {f,f} |  0.003067 |        1.9e-05
     2 | {Houston, TX}          | {f,f} |  0.002167 |       0.000133
     3 | {El Paso, TX}          | {f,f} |     0.002 |       0.000113
     4 | {New York, NY}         | {f,f} |  0.001967 |       0.000114
     5 | {Atlanta, GA}          | {f,f} |  0.001633 |        3.3e-05
     6 | {Sacramento, CA}       | {f,f} |  0.001433 |        7.8e-05
     7 | {Miami, FL}            | {f,f} |    0.0014 |          6e-05
     8 | {Dallas, TX}           | {f,f} |  0.001367 |        8.8e-05
     9 | {Chicago, IL}          | {f,f} |  0.001333 |        5.1e-05
   ...
(99 rows)

Это указывает на то, что наиболее распространенной комбинацией города и штата является Вашингтон, округ Колумбия, с фактической частотой (в выборке) около 0,35%. Базовая частота комбинации (рассчитанная по простым частотам на столбец) составляет всего 0,0027%, что приводит к заниженным оценкам на два порядка.

Желательно создавать объекты статистики MCV только на комбинациях столбцов, которые фактически используются в условиях вместе, и для которых неправильная оценка количества групп приводит к плохим планам. В противном случае расчёты статистики внутри ANALYZE будут напрасны.

Управление планировщиком с помощью явных предложений JOIN

Можно в определенной степени управлять планировщиком запросов, используя явный синтаксис JOIN. Чтобы понять, почему это важно, нам сначала нужно немного предыстории.

В простом запросе с соединением, таком как:

SELECT * FROM a, b, c WHERE a.id = b.id AND b.ref = c.id;

Планировщик может соединять таблицы в любом порядке. Например, он может сгенерировать план запроса, который соединяет A с B, используя условие WHERE a.id = b.id, а затем присоединить C к этой объединенной таблице, используя другое условие WHERE. Или он может соединить B с C, а затем присоединить A к этому результату. Или он может соединить A с C, а затем соединить их с B - но это будет неэффективно, поскольку должно быть сформировано полное декартово произведение A и C, поскольку в WHERE нет применимого условия, позволяющего оптимизировать объединение., (Все объединения в исполнителе происходят между двумя входными таблицами, поэтому необходимо создать результат одним или другим из этих способов). Важным моментом является то, что эти различные возможности объединения дают семантически эквивалентные результаты, но могут иметь очень разные затраты на выполнение. Поэтому планировщик изучит все из них, чтобы попытаться найти наиболее эффективный план запроса.

Когда в запросе используются только две или три таблицы, не о чем беспокоиться. Но количество возможных порядков соединения растет по экспоненте по мере увеличения количества таблиц. После десяти или более входных таблиц более нецелесообразно проводить исчерпывающий поиск всех возможностей, и даже для шести или семи таблиц планирование может занять очень много времени. Когда входных таблиц слишком много, планировщик QHB переключается с полного поиска на генетический вероятностный поиск с ограниченным числом возможностей. (Порог переключения задается параметром времени выполнения geqo_threshold). Генетический поиск занимает меньше времени, но он не обязательно находит наилучший возможный план.

Когда запрос включает внешние объединения, у планировщика имеется меньше свободы, чем для простых (внутренних) объединений. Например, рассмотрим:

SELECT * FROM a LEFT JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);

Хотя ограничения этого запроса внешне похожи на предыдущий пример, семантика отличается, потому что для каждой строки A, которая не имеет совпадающей строки в соединении B и C, должна быть выделена строка. Поэтому у планировщика нет выбора порядка соединения здесь : он должен соединить B с C и затем присоединить A к этому результату. Соответственно, этот запрос занимает меньше времени для планирования, чем предыдущий запрос. В других случаях планировщик может определить, что более одного варианта соединения является достаточным. Например, учитывая:

SELECT * FROM a LEFT JOIN b ON (a.bid = b.id) LEFT JOIN c ON (a.cid = c.id);

сначала можно присоединить A к B или C. В настоящее время только FULL JOIN полностью ограничивает порядок соединения. Большинство практических случаев, связанных с LEFT JOIN или RIGHT JOIN могут быть в некоторой степени перестроены.

Явный синтаксис внутреннего соединения ( INNER JOIN, CROSS JOIN или JOIN ) семантически аналогичен перечислению входных отношений в FROM, поэтому он не ограничивает порядок соединения.

Несмотря на то, что большинство видов JOIN не полностью ограничивают порядок соединения, можно поручить планировщику запросов QHB обрабатывать все предложения JOIN как ограничивающие порядок соединения. Например, эти три запроса логически эквивалентны:

SELECT * FROM a, b, c WHERE a.id = b.id AND b.ref = c.id;
SELECT * FROM a CROSS JOIN b CROSS JOIN c WHERE a.id = b.id AND b.ref = c.id;
SELECT * FROM a JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);

Но если мы скажем планировщику построить план для JOIN, то для второго и третьего плана потребуется меньше времени, чем для первого. Этот эффект не имеет большого смысла только для трех таблиц, но он может помочь для запросов в которых много таблиц.

Чтобы планировщик следовал порядку соединения, установленному явными соединениями JOIN, установите для параметра времени выполнения join_collapse_limit значение 1. (Другие возможные значения обсуждаются ниже).

Вам не нужно полностью ограничивать порядок соединения, чтобы сократить время поиска, потому что можно использовать операторы JOIN в элементах простого списка FROM. Например, рассмотрим:

SELECT * FROM a CROSS JOIN b, c, d, e WHERE ...;

Значение параметра join_collapse_limit = 1 заставляет планировщика соединять A с B, прежде чем соединять их с другими таблицами, но не ограничивает его выбор в противном случае. В этом примере количество возможных способов объединения уменьшается в 5 раз.

Ограничение таким способом поиска вариантов для планировщика является полезным методом как для сокращения времени планирования, так и для "направления" планировщика к хорошему плану запросов. Если планировщик выбирает неправильный порядок соединения по умолчанию, вы можете заставить его выбрать лучший порядок с помощью синтаксиса JOIN - при условии, что вы знаете лучший порядок.

Тесно связанной проблемой, которая влияет на время планирования, является объединение подзапросов в их родительский запрос. Например, рассмотрим:

SELECT *
FROM x, y,
    (SELECT * FROM a, b, c WHERE something) AS ss
WHERE somethingelse;

Эта ситуация может возникнуть из-за использования представления, содержащего соединение - SELECT будет вставлено вместо ссылки на представление, в результате чего будет получен запрос, аналогичный приведенному выше. Обычно планировщик будет пытаться свернуть подзапрос в родительский, получив:

SELECT * FROM x, y, a, b, c WHERE something AND somethingelse;

Это обычно приводит к лучшему плану, чем планирование подзапроса отдельно. (Например, внешние условия WHERE могут быть такими, что при присоединении X к A сначала удаляются многие строки из A, что позволяет избежать необходимости формировать полный логический вывод подзапроса), но в то же время мы увеличили время планирования - здесь мы имеем проблему пятистороннего соединения, заменяющую две отдельные проблемы трехстороннего соединения. Из-за экспоненциального роста числа возможностей соединения это имеет большое значение. Планировщик старается не зацикливаться на огромных проблемах поиска объединений, не сворачивая подзапрос, если в родительском запросе больше таблиц чем указано в параметре from_collapse_limit. Вы можете соотнести время планирования с качеством плана, настроив уменьшив или увеличив этот параметр времени выполнения.

from_collapse_limit и join_collapse_limit имеют похожие имена, потому что они выполняют почти одно и то же: один контролирует, когда планировщик "сгладит" подзапросы, а другой контролирует, когда он "сгладит" явные объединения. Обычно вы либо устанавливаете join_collapse_limit равным from_collapse_limit (чтобы явные объединения и подзапросы действовали аналогично), либо устанавливаете join_collapse_limit 1 (если вы хотите управлять порядком соединения с помощью явных указаний). Но вы можете установить их по-разному, если вы пытаетесь настроить компромисс между временем планирования и временем выполнения.

Заполнение базы данных

Может потребоваться вставить большой объем данных при первом заполнении базы данных. Этот раздел содержит некоторые предложения о том, как сделать этот процесс максимально эффективным.

Выключить автокоммит

При использовании нескольких INSERT выключите автокоммит и просто сделайте один коммит в конце. (В обычном SQL это означает команду BEGIN в начале и COMMIT в конце. Некоторые клиентские библиотеки могут делать это во внутренней реализации, и в этом случае вам нужно убедиться, что библиотека делает это, только когда вы этого хотите). Дополнительным преимуществом выполнения всех вставок в одной транзакции является то, что если при вставке одной строки произошел сбой, вставка всех строк, вставленных до этой точки, будет откатываться, поэтому вы не получите базу данных с частично загруженными данными.

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

Используйте команду COPY, чтобы загрузить все строки в одной команде, вместо использования серии команд INSERT. Команда COPY оптимизирована для загрузки большого количества строк; он менее гибок, чем INSERT, но значительно снижает накладные расходы при большой загрузке данных. Поскольку COPY - это одна команда, нет необходимости выключать автокоммит, если вы используете этот метод для заполнения таблицы.

Если вы не можете использовать COPY, это может помочь использовать PREPARE для создания подготовленного оператора INSERT, а затем использовать EXECUTE столько раз, сколько требуется. Это позволяет избежать некоторых накладных расходов, связанных с многократным анализом и планированием INSERT. Различные интерфейсы предоставляют эту возможность по-разному; ищите « подготовленные заявления » в документации интерфейса.

Обратите внимание, что загрузка большого количества строк с помощью COPY почти всегда быстрее, чем с помощью INSERT, даже если используется PREPARE и несколько вставок объединяются в одну транзакцию.

COPY быстрее всего используется в той же транзакции, что и предыдущая команда CREATE TABLE или TRUNCATE. В таких случаях не нужно писать в WAL, потому что в случае ошибки файлы, содержащие вновь загруженные данные, все равно будут удалены. Однако это соображение применимо только в том случае, если wal_level minimal для однораздельных таблиц, так как в противном случае все команды должны писать WAL.

Удалить индексы

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

Если вы добавляете большие объемы данных в существующую таблицу, может быть выгодно удалить индексы, загрузить таблицу, а затем воссоздать индексы. Конечно, производительность базы данных для других пользователей может снизиться во время отсутствия индексов. Следует проявить осторожность, прежде чем отбрасывать уникальный индекс, поскольку проверка ошибок, предоставляемая ограничением уникальности, будет потеряна, пока индекс отсутствует.

Удалить ограничения внешнего ключа

Как и в случае с индексами, ограничение внешнего ключа может быть "массово" проверено более эффективно, чем строка за строкой. Поэтому может быть полезно удалить ограничения внешнего ключа, загрузить данные и заново создать ограничения. Опять же, существует компромисс между скоростью загрузки данных и потерей проверки ошибок, в то время как ограничение отсутствует.

Более того, когда вы загружаете данные в таблицу с существующими ограничениями внешнего ключа, каждая новая строка требует записи в списке ожидающих событий триггера на сервере (так как это запуск триггера, который проверяет ограничение внешнего ключа строки). Загрузка многих миллионов строк может привести к переполнению очереди событий триггера доступной памяти, что приведет к недопустимому обмену или даже полному отказу команды. Поэтому может быть необходимо, а не просто желательно удалить и повторно применить внешние ключи при загрузке больших объемов данных. Если временное удаление ограничения неприемлемо, единственным другим выходом может быть разделение операции загрузки на более мелкие транзакции.

Увеличьте maintenance_work_mem

Временное увеличение параметра конфигурации maintenance_work_mem при загрузке больших объемов данных может привести к повышению производительности. Это поможет ускорить выполнение команд CREATE INDEX и ALTER TABLE ADD FOREIGN KEY. Это не будет иметь большого значения для самой COPY, поэтому этот совет полезен только при использовании одного или обоих из перечисленных методов.

Увеличить max_wal_size

Временное увеличение переменной конфигурации max_wal_size также может ускорить загрузку больших объемов данных. Это связано с тем, что загрузка большого количества данных в QHB приводит к тому, что контрольные точки появляются чаще, чем обычная частота контрольных точек (указывается в переменной конфигурации checkpoint_timeout). Всякий раз, когда возникает контрольная точка, все грязные страницы должны быть сброшены на диск. Временное увеличение max_wal_size во время массовой загрузки данных позволяет уменьшить количество необходимых контрольных точек.

Выключить архивацию WAL и потоковую репликацию

При загрузке больших объемов данных в установку, которая использует архивирование WAL или потоковую репликацию, может быть быстрее создать новую базовую резервную копию после завершения загрузки, чем обрабатывать большой объем добавочных данных WAL. Чтобы предотвратить добавочное ведение журнала WAL при загрузке, выключите архивацию и потоковую репликацию, установив для wal_level значение minimal, для параметра archive_mode off, а для max_wal_senders - ноль. Но учтите, что изменение этих настроек требует перезагрузки сервера.

Помимо сокращения времени обработки данных WAL архиватором или отправителем WAL, выполнение этого на самом деле ускорит выполнение определенных команд, поскольку они вообще не предназначены для записи WAL, если wal_level minimal. (Они могут гарантировать безопасность при fsync дешевле, выполнив fsync в конце, чем написав WAL). Это относится к следующим командам:

CREATE TABLE AS SELECT

CREATE INDEX (и варианты, такие как ALTER TABLE ADD PRIMARY KEY )

ALTER TABLE SET TABLESPACE

CLUSTER

COPY FROM, когда целевая таблица была создана или усечена ранее в той же транзакции

Запустите ANALYZE

Всякий раз, когда вы значительно изменили распределение данных в таблице, настоятельно рекомендуется использовать ANALYZE. Изменение распределения включает в себя в том числе и массовую загрузку больших объемов данных в таблицу. Запуск ANALYZE (или VACUUM ANALYZE ) гарантирует, что планировщик будет иметь актуальную статистику о таблице. При отсутствии статистики или устаревшей статистики планировщик может принимать неправильные решения во время планирования запросов, что приведёт к низкой производительности для любых таблиц с неточной или несуществующей статистикой. Обратите внимание, что если фоновый процесс autovacuum включен, он может автоматически запустить ANALYZE, см. разделы Обновление статистики планировщика и Процесс «Автовакуум» для получения дополнительной информации.

Некоторые заметки о qhb_dump

Сценарии дампа, сгенерированные qhb_dump, автоматически применяют несколько, но не все, из приведенных выше рекомендаций. Чтобы загрузить дамп qhb_dump как можно быстрее, вам нужно сделать несколько дополнительных действий вручную. (Обратите внимание, что эти пункты применяются при восстановлении дампа, а не при его создании. Те же пункты применяются при загрузке текстового дампа с помощью psql или использовании qhb_restore для загрузки из файла архива qhb_dump).

По умолчанию qhb_dump использует COPY, а когда он генерирует полный дамп схемы и данных, следует осторожно загружать данные перед созданием индексов и внешних ключей. Таким образом, в этом случае несколько рекомендаций обрабатываются автоматически. Что вам остается сделать, так это:

Установить соответствующие (т. max_wal_size, чем обычно) значения для maintenance_work_mem и max_wal_size .

Если используется архивация WAL или потоковая репликация, рассмотреть возможность их выключения во время восстановления. Для этого установить для параметра archive_mode значение off, для wal_level значение minimal, а для max_wal_senders - ноль, прежде чем загружать дамп. После этого установить для них правильные значения и создать новую базовую резервную копию.

Поэкспериментируйте с режимами параллельного дампа и восстановления qhb_dump и qhb_restore и найдите оптимальное количество одновременных заданий для использования. Параллельное сохранение и восстановление с помощью опции -j должен обеспечить значительно более высокую производительность по сравнению с последовательным режимом.

Подумайте, должен ли весь дамп быть восстановлен как одна транзакция. Для этого передайте параметр командной строки -1 или --single-transaction в psql или qhb_restore. При использовании этого режима даже самые маленькие ошибки будут отменять все восстановление, возможно, отбрасывая многие часы обработки. В зависимости от того, насколько взаимосвязаны данные, это может показаться предпочтительным или нет. Команды COPY будут выполняться быстрее, если вы используете одну транзакцию и выключили архивирование WAL.

Если на сервере базы данных доступно несколько процессоров, попробуйте использовать параметр --jobs в qhb_restore. Это позволяет одновременно загружать данные и создавать индексы.

Запустите ANALYZE после всех операций.

Дамп только для данных будет по-прежнему использовать COPY, но он не удаляет и не воссоздает индексы и обычно не касается внешних ключей¹. Таким образом, при загрузке дампа только для данных, вы можете удалить и воссоздать индексы и внешние ключи, если вы хотите использовать эти методы. По-прежнему полезно увеличивать max_wal_size при загрузке данных, но не беспокойтесь об увеличении maintenance_work_mem; скорее, вы сделаете это при ручном воссоздании индексов и внешних ключей впоследствии. И не забудьте ANALYZE когда вы закончите; см. разделы Обновление статистики планировщика и Процесс «Автовакуум» для получения дополнительной информации.

Настройки снижающие прочность

Прочность - это функция базы данных, которая гарантирует запись совершенных транзакций, даже если сервер выходит из строя или теряет питание. Однако прочность добавляет значительную нагрузку на базу данных, поэтому, если ваш сайт не требует такой гарантии, QHB может быть настроена для намного быстрой работы. Ниже приведены изменения конфигурации, которые можно внести для повышения производительности в таких случаях. За исключением отмеченного ниже, прочность все еще гарантируется в случае сбоя программного обеспечения базы данных - только внезапное прекращение работы операционной системы создает риск потери или повреждения данных при использовании этих настроек.

Поместите каталог данных кластера базы данных в файловую систему с поддержкой памяти (то есть RAM- диск). Это исключает все операции ввода-вывода на диске базы данных, но ограничивает объем хранения данных до объема доступной памяти (и, возможно, подкачки).

Выключите fsync - нет необходимости сбрасывать данные на диск.

Выключите synchronous_commit - может не потребоваться форсировать запись WAL на диск при каждом коммите. Этот параметр создает риск потери транзакции (но не повреждения данных) в случае сбоя базы данных.

Выключите full_page_writes - нет необходимости принимать меры против частичной записи страницы.

Увеличьте max_wal_size и checkpoint_timeout - это уменьшает частоту контрольных точек, но увеличивает требования к хранилищу /pg_wal.

Создавайте unlogged tables, чтобы избежать записи WAL, хотя это сделает таблицы не защищенными от сбоев.

¹

Вы можете получить эффект выключения внешних ключей, используя опцию --disable-triggers но понимайте, что это устраняет, а не просто откладывает, проверку внешнего ключа, и поэтому можно вставлять неверные данные, если вы используете эту опцию.

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

QHB поддерживает набор экспериментальных объектов, которые позволяют расширять модули путем добавления новых типов сканирования в систему. В отличие от оболочки внешних данных, которая отвечает только за то, как сканировать свои собственные внешние таблицы, пользовательский провайдер сканирования может предоставить альтернативный метод сканирования любого отношения в системе. Как правило, мотивация для написания пользовательского провайдера сканирования будет состоять в том, чтобы разрешить использование некоторой оптимизации, не поддерживаемой основной системой, такой как кэширование или некоторая форма аппаратного ускорения. В этой главе описывается, как создать новый пользовательский провайдер сканирования.

Реализация нового типа пользовательского сканирования - это трехэтапный процесс. Во-первых, во время планирования необходимо сгенерировать пути доступа, представляющие собой сканирование с использованием предложенной стратегии. Во-вторых, если один из этих путей доступа выбран планировщиком в качестве оптимальной стратегии для сканирования конкретного отношения, путь доступа должен быть преобразован в план. Наконец, должна быть предусмотрена возможность выполнения плана и получения тех же результатов, которые были бы получены для любого другого пути доступа, нацеленного на то же отношение.

Создание пользовательских путей сканирования

Пользовательский провайдер сканирования обычно добавляет пути для базового отношения, устанавливая следующий обработчик, который вызывается после того, как основной код создал все пути доступа, которые он может для отношения (за исключением путей Gather, которые создаются после этого вызова, чтобы они могли использовать частичные пути, добавленные обработчиком):

typedef void (*set_rel_pathlist_hook_type) (PlannerInfo *root,
                                            RelOptInfo *rel,
                                            Index rti,
                                            RangeTblEntry *rte);
extern PGDLLIMPORT set_rel_pathlist_hook_type set_rel_pathlist_hook;

Хотя функция подключения может использоваться для проверки, изменения или удаления путей, созданных основной системой, пользовательский провайдер сканирования обычно ограничивается созданием CustomPath объектов и добавление их к rel с помощью add_path. провайдер пользовательского сканирования отвечает за инициализацию CustomPath объекта, который объявляется следующим образом:

typedef struct CustomPath
{
    Path      path;
    uint32    flags;
    List     *custom_paths;
    List     *custom_private;
    const CustomPathMethods *methods;
} CustomPath;

path необходимо инициализировать как и для любого другого пути, включая оценку количества строк, начальную и общую стоимость, а также порядок сортировки, предоставляемый этим путем. flags это битовая маска, которая должна включать в себя CUSTOMPATH_SUPPORT_BACKWARD_SCAN если пользовательский путь может поддерживать обратную проверку и CUSTOMPATH_SUPPORT_MARK_RESTORE если он может поддерживать пометку и восстановление. Обе возможности являются необязательными. Необязательный параметр custom_paths это список Path узлов, используемых этим узлом пользовательского пути; они будут преобразованы в Plan узлы планировщиком. custom_private может использоваться для хранения собственных данных пользовательского пути. Собственные данные должны храниться в форме, которая может быть обработана с помощью nodeToString, так что отладочные процедуры, которые пытаются напечатать пользовательский путь, будут работать как задумано. methods должен указывать на объект (обычно статически выделенный), реализующий необходимые методы пользовательского пути, из которых в настоящее время существует только один.

Пользовательский провайдер сканирования также может предоставить пути соединения. Так же, как и для базовых отношений, такой путь должен давать тот же результат, что и при соединении, которое он заменяет. Для этого провайдер соединения должен установить следующий обработчик, а затем в ней создать CustomPath путь(и) для связи соединения.

typedef void (* set_join_pathlist_hook_type) (PlannerInfo * root,
                                             RelOptInfo * joinrel,
                                             RelOptInfo * externalrel,
                                             RelOptInfo * innerrel,
                                             JoinType jointype,
                                             JoinPathExtraData * extra);
extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook;

Этот обработчик будет вызываться повторно для одного и того же отношения соединения, с различными комбинациями внутренних и внешних отношений; это ответственность обработчика, чтобы минимизировать лишнюю работу.

Обратные вызовы пользовательских путей сканирования

Plan *(*PlanCustomPath) (PlannerInfo *root,
                         RelOptInfo *rel,
                         CustomPath *best_path,
                         List *tlist,
                         List *clauses,
                         List *custom_plans);

Преобразует пользовательский путь в готовый план. Возвращаемое значение обычно будет CustomScan объектом, который аллоцируется и инициализируется обратным вызовом. Смотрите раздел Создание пользовательских планов сканирования для получения более подробной информации.

Создание пользовательских планов сканирования

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

typedef struct CustomScan
{
    Scan      scan;
    uint32    flags;
    List     *custom_plans;
    List     *custom_exprs;
    List     *custom_private;
    List     *custom_scan_tlist;
    Bitmapset *custom_relids;
    const CustomScanMethods *methods;
} CustomScan;

scan необходимо инициализировать, как и для любого другого сканирования, включая расчетные затраты, списки целевых объектов, и т.д. Битовая маска flags имеет тот же смысл, что и в CustomPath. custom_plans может использоваться для хранения дочерних Plan узлов. custom_exprs следует использовать для хранения деревьев выражений, которые необходимо будет исправить с помощью setrefs.c и subselect.c, в то время как custom_private следует использовать для хранения других личных данных, которые используются только самим провайдером пользовательского сканирования. custom_scan_tlist может иметь значение NIL при сканировании базового отношения, указывающее, что пользовательское сканирование возвращает кортежи сканирования, соответствующие типу строки базового отношения. В противном случае это целевой список, описывающий фактические кортежи сканирования. custom_scan_tlist должен быть указан для соединений, а также может быть предоставлено для сканирования, если пользовательский провайдер сканирования может вычислить некоторые выражения без переменных. custom_relids устанавливается ядром и задается для набора отношений (индексов таблицы диапазонов), которые обрабатывает этот узел сканирования; за исключением случаев, когда это сканирование заменяет соединение, оно будет иметь только один элемент. methods необходимо указать на объект (обычно статически выделенный), реализующий необходимые пользовательские методы сканирования, которые более подробно описаны ниже.

Когда CustomScan сканирует отношение, scan.scanrelid должен быть индексом таблицы диапазонов сканируемой таблицы. Когда он заменяет соединение, scan.scanrelid должен быть нулевым.

Деревья плана должны иметь возможность быть продублированы с помощью copyObject, поэтому все данные, хранящиеся в "пользовательских" полях, должны состоять из узлов, которые эта функция может обрабатывать. Кроме того, настраиваемые провайдеры сканирования не могут заменить более крупную структуру, которая встраивает CustomScan для самой структуры, как это было бы возможно для CustomPath или CustomScanState.

Обратный вызов пользовательского плана сканирования

Node *(*CreateCustomScanState) (CustomScan *cscan);

Выделяет CustomScanState для переданного объекта CustomScan. Фактический размер выделенной памяти часто будет больше, чем требуется для обычного CustomScanState, потому что многие провайдеры захотят встроить этот объект в качестве поля более крупной структуры. Возвращаемое значение должно иметь метку узла и методы установите соответствующим образом, но другие поля должны быть оставлены как нули на этом этапе; после базовой инициализации в ExecInitCustomScan, будет вызван BeginCustomScan обработчику, для передачи управления провайдеру пользовательского сканирования.

Выполнение пользовательских сканирований

Когда CustomScan выполняется, его состояние исполнения находится в CustomScanState, который определен следующим образом:

typedef struct CustomScanState
{
    ScanState ss;
    uint32    flags;
    const CustomExecMethods *methods;
} CustomScanState;

ss инициализируется как и для любого другого состояния сканирования, за исключением того, что сканирование выполняется для соединения, а не для базового отношения, ss.ss_currentRelation остается NULL. flags — это битовая маска аналогичная соответствующему полю в CustomPath и CustomScan. methods должен указывать на объект (обычно статически выделенный), реализующий требуемые пользовательские методы состояния сканирования, которые более подробно описаны ниже. Как правило, CustomScanState, который не обязан поддерживать copyObject, на самом деле будет являться частью большей структуры.

Обратные вызовы выполнения пользовательского сканирования

void (*BeginCustomScan) (CustomScanState *node,
                         EState *estate,
                         int eflags);

Инициализирует переданный объект CustomScanState. Стандартные поля инициализируются с помощью ExecInitCustomScan, но любые собственные поля должны быть инициализированы в этом месте.

TupleTableSlot *(*ExecCustomScan) (CustomScanState *node);

Выбирает следующий кортеж для сканирования. Если какие-либо кортежи остаются, функция заполняет ps_ResultTupleSlot следующим кортежем в текущем направлении сканирования, а затем возвращает слот кортежа. Если кортежи закончились, должен быть возвращён NULL или пустой слот.

void (*EndCustomScan) (CustomScanState *node);

Очищает данные, связанные с CustomScanState. Этот метод является обязательным, но он ничего не обязан делать, если нет никаких связанных данных или он будет очищен автоматически.

void (*ReScanCustomScan) (CustomScanState *node);

Смещение текущего указателя сканирование в начало и подготовка к повторному сканированию отношения.

void (*MarkPosCustomScan) (CustomScanState *node);

Сохраняет текущее положение сканирования, чтобы впоследствии его можно было восстановить с помощью обработчика RestrPosCustomScan. Этот обратный вызов является необязательным и должен быть предоставлен только в том случае, если установлен флаг CUSTOMPATH_SUPPORT_MARK_RESTORE.

void (*RestrPosCustomScan) (CustomScanState *node);

Восстанавливает предыдущую позицию сканирования, сохраненную с помощью обработчика MarkPosCustomScan. Этот обратный вызов является необязательным и должен быть предоставлен только в том случае, если установлен флаг CUSTOMPATH_SUPPORT_MARK_RESTORE.

Size (*EstimateDSMCustomScan) (CustomScanState *node,
                               ParallelContext *pcxt);

Оценивает объем общей динамической памяти, который потребуется для параллельной работы. Значение может быть больше, чем фактически использованная сумма, но она не должна быть меньше. Возвращаемое значение представлено в байтах. Этот обратный вызов является необязательным и должен быть предоставлен только в том случае, если этот пользовательский провайдер сканирования поддерживает параллельное выполнение.

void (*InitializeDSMCustomScan) (CustomScanState *node,
                                 ParallelContext *pcxt,
                                 void *coordinate);

Инициализирует общую динамическую память, которая необходима для параллельной работы. coordinate указывает на общую область памяти размером равным возвращаемым значением EstimateDSMCustomScan. Этот обратный вызов является необязательным и должен быть предоставлен только в том случае, если этот пользовательский провайдер сканирования поддерживает параллельное выполнение.

void (*ReInitializeDSMCustomScan) (CustomScanState *node,
                                   ParallelContext *pcxt,
                                   void *coordinate);

Повторно инициализирует общую динамическую память, необходимую для параллельной работы, когда узел пользовательского плана сканирования будет повторно сканироваться. Этот обратный вызов является необязательным и должен быть предоставлен только в том случае, если этот пользовательский провайдер сканирования поддерживает параллельное выполнение. Рекомендуемая практика заключается в том, что этот обратный вызов сбрасывает только общее состояние, в то время как обработчик ReScanCustomScan сбрасывает только локальное состояние. В настоящее время этот обратный вызов будет вызван перед вызовом ReScanCustomScan но лучше всего не полагаться на этот порядок.

void (*InitializeWorkerCustomScan) (CustomScanState *node,
                                    shm_toc *toc,
                                    void *coordinate);

Инициализирует локальное состояние параллельного процесса на основе общего состояния, заданного лидером во время выполнения InitializeDSMCustomScan. Этот обратный вызов является необязательным и должен быть предоставлен только в том случае, если этот пользовательский провайдер сканирования поддерживает параллельное выполнение.

void (*ShutdownCustomScan) (CustomScanState *node);

Высвобождает ресурсы, когда ожидается, что узел не будет выполнен до завершения. Функция вызывается не во всех случаях; иногда, EndCustomScan может вызываться без предшествующего вызова ShutdownCustomScan. Поскольку сегмент DSM, используемый параллельным запросом, уничтожается сразу после вызова этого обратного вызова, этот метод следует реализовать пользовательским провайдерам сканирования, которые хотят предпринять некоторые действия до исчезновения сегмента DSM.

void (*ExplainCustomScan) (CustomScanState *node,
                           List *ancestors,
                           ExplainState *es);

Выводит дополнительную информацию для EXPLAIN узла пользовательского плана сканирования. Этот обратный вызов является необязательным. Общие данные, хранящиеся в ScanState, такие как целевой список и отношение сканирования, будут показаны даже без этого обратного вызова, но обратный вызов позволяет отображать дополнительную информацию о внутреннем состоянии.

Аутентификация клиентского приложения

Содержание

Файл pg_hba.conf

Файл сопоставления имен пользователей

Методы аутентификации

Аутентификация trust

Аутентификация по паролю

Аутентификация GSSAPI

Аутентификация ident

Аутентификация peer

Аутентификация LDAP

Аутентификация RADIUS

Аутентификация по сертификату

Аутентификация BSD

Проблемы аутентификации

При подключении к серверу базы данных клиентское приложение указывает, под каким именем пользователя базы данных QHB оно хочет подключиться, почти так же, как при обычном входе пользователя в компьютер с Unix. В среде SQL по имени пользователя базы данных определяется, какие у него есть права доступа к объектам базы данных; более подробную информацию см. в главе Роли в базе данных. Таким образом, крайне важно указать, к каким базам данных пользователь может подключаться.

Примечание
Как объясняется в главе Роли в базе данных, QHB на самом деле управляет правами посредством так называемых «ролей». Соответственно, в этой главе под термином пользователь базы данных подразумевается «роль с правом LOGIN».

Аутентификация — это процесс, посредством которого сервер базы данных идентифицирует клиента, а в более широком смысле определяет, разрешено ли клиентскому приложению (или пользователю, запустившему это приложение) подключиться с указанным именем пользователя.

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

Имена пользователей базы данных QHB логически отделены от имен пользователей операционной системы, в которой запущен сервер. Если у всех пользователей определенного сервера есть учетные записи в операционной системе сервера, имеет смысл назначить им точно такие же имена пользователей базы данных. Однако у сервера, принимающего удаленные подключения, может быть много пользователей базы данных, у которых нет локальной учетной записи в операционной системе, и в таких случаях в соответствии между именами пользователей базы данных и именами пользователей ОС нет необходимости.

Файл pg_hba.conf

Описание

Аутентификация клиентов управляется файлом конфигурации, который традиционно называется pg_hba.conf и хранится в каталоге данных кластера баз данных. (HBA расшифровывается как host-based authentication — аутентификация по узлу.) Стандартный файл pg_hba.conf устанавливается при инициализации каталога данных утилитой initdb. Однако файл конфигурации аутентификации можно разместить в любом другом месте; см. параметр конфигурации hba_file.

Общий формат файла pg_hba.conf — набор записей, по одной на строку. Пустые строки игнорируются, как и любой текст после знака отбивки комментария (#). Запись можно продолжить на следующей строке, завершив текущую строку обратным слэшем. (Обратный слэш является спецсимволом только в конце строки.) Запись состоит из ряда полей, разделенных пробелами и/или табуляциями. Поля могут содержать пробелы, если значение поля заключено в кавычки. Если в кавычки берется одно из ключевых слов в поле базы данных, пользователя или адреса (например, all или replication), это слово теряет свое особое значение и просто обозначает базу данных, пользователя или хост с этим именем. Обратный слэш означает перенос строки даже в тексте в кавычках или комментариях.

В каждой записи указывается тип соединения, диапазон IP-адресов клиента (если это важно для типа соединения), имя базы данных, имя пользователя и метод аутентификации, который будет использован для подключения в соответствии с этими параметрами. Первая же запись с соответствующим типом соединения, адресом клиента, требуемой базой данных и именем пользователя используется для аутентификации. Процедуры «передачи управления вниз» или «резервирования» отсутствуют: если выбрана запись и аутентификация провалилась, последующие записи не рассматриваются. Если ни одна запись не подходит, в доступе будет отказано.

Форматы записи и значения полей

Запись может иметь несколько форматов:

local         база_данных  пользователь  метод-аутентификации [параметры-аутентификации]
host          база_данных  пользователь  адрес     метод-аутентификации  [параметры-аутентификации]
hostssl       база_данных  пользователь  адрес     метод-аутентификации  [параметры-аутентификации]
hostnossl     база_данных  пользователь  адрес     метод-аутентификации  [параметры-аутентификации]
hostgssenc    база_данных  пользователь  адрес     метод-аутентификации  [параметры-аутентификации]
hostnogssenc  база_данных  пользователь  адрес     метод-аутентификации  [параметры-аутентификации]
host          база_данных  пользователь  IP-адрес  IP-маска      метод-аутентификации  [параметры-аутентификации]
hostssl       база_данных  пользователь  IP-адрес  IP-маска      метод-аутентификации  [параметры-аутентификации]
hostnossl     база_данных  пользователь  IP-адрес  IP-маска      метод-аутентификации  [параметры-аутентификации]
hostgssenc    база_данных  пользователь  IP-адрес  IP-маска      метод-аутентификации  [параметры-аутентификации]
hostnogssenc  база_данных  пользователь  IP-адрес  IP-маска      метод-аутентификации  [параметры-аутентификации]

Эти поля имеют следующие значения:

local
Эта запись соответствует попыткам подключения через сокеты домена Unix. Без записи этого типа подключения через сокеты домена Unix не допускаются.

host
Эта запись соответствует попыткам подключения по TCP/IP. Записи host соответствуют попыткам подключения с SSL или без SSL, а также попыткам подключения с криптографической защитой GSSAPI или без GSSAPI.

Примечание
Удаленные подключения по TCP/IP будут невозможны, если сервер запущен без соответствующего значения для параметра конфигурации listen_addresses, поскольку по умолчанию система принимает подключения по TCP/IP только для локального loopback-адреса localhost.

hostssl
Эта запись соответствует попыткам подключения по TCP/IP, но только когда это подключение устанавливается с шифрованием SSL.

Чтобы воспользоваться этим параметром, сервер должен быть собран с поддержкой SSL. Более того, SSL должен быть включен параметром конфигурации ssl. В противном случае запись hostssl игнорируется, не считая вывода предупреждения о том, что ей не будут соответствовать никакие подключения.

hostnossl
У этого типа записей поведение противоположно hostssl; ему соответствуют только попытки подключения по TCP/IP без шифрования SSL.

hostgssenc
Эта запись соответствует попыткам подключения по TCP/IP, но только когда это подключение устанавливается с шифрованием GSSAPI.

Чтобы воспользоваться этим параметром, сервер должен быть собран с поддержкой GSSAPI. В противном случае запись hostgssenc игнорируется, не считая вывода предупреждения о том, что ей не будут соответствовать никакие подключения.

hostnogssenc
У этого типа записей поведение противоположно hostgssenc; ему соответствуют только попытки подключения по TCP/IP без шифрования GSSAPI.

база_данных
Определяет, какому имени (или именам) базы данных соответствует эта запись. Значение all указывает, что она соответствует всем базам данных. Значение sameuser указывает, что эта запись соответствует, если имя запрашиваемой базы данных совпадает с именем запрашиваемого пользователя. Значение samerole указывает, что запрашиваемый пользователь должен быть членом роли с тем же именем, что и у запрашиваемой базы данных. (samegroup — это устаревший, но все еще принимаемый вариант значения samerole.) Суперпользователи считаются членами роли в контексте samerole, только если они явно входят в такую роль, непосредственно или опосредованно; просто быть суперпользователем для этого недостаточно. Значение replication указывает, что эта запись соответствует, если запрашивается подключение физической репликации, однако при этом не соответствует подключениям логической репликации. Обратите внимание, что для подключения физической репликации не указывается никакая конкретная база данных, тогда как для подключений логической репликации она должна указываться. Любое другое значение воспринимается как имя конкретной базы данных QHB. Можно задать несколько имен баз данных, разделив их запятыми. Кроме того, можно задать отдельный файл с именами баз данных, написав имя этого файла после знака @.

пользователь
Определяет, какому имени (или именам) пользователя базы данных соответствует эта запись. Значение all указывает, что она соответствует всем пользователям. Любое другое значение воспринимается либо как имя конкретного пользователя базы данных, либо как имя группы, если написать его после знака +. (Напомним, что в QHB нет никакой разницы между пользователями и группами; знак + фактически означает «соответствует любым ролям, которые непосредственно или опосредованно являются членами этой роли», тогда как имя без знака + соответствует только этой конкретной роли.) По этой причине суперпользователь рассматривается как член роли, только если он явно является членом этой роли, непосредственно или опосредованно; просто быть суперпользователем для этого недостаточно. Можно задать несколько имен пользователей, разделив их запятыми. Кроме того, можно задать отдельный файл с именами пользователей, написав имя этого файла после знака @.

адрес
Задает адрес (или адреса) клиентской машины, которым соответствует эта запись. Это поле может содержать либо имя хоста, либо диапазон IP-адресов, либо одно из упомянутых ниже ключевых слов.

Диапазон IP-адресов указывается посредством стандартного цифрового представления для начального адреса диапазона, дополненного слэшем (/) и длиной маски CIDR. Длина маски определяет количество старших битов клиентского IP-адреса, которые должны соответствовать. Биты, находящиеся правее, в заданном IP-адресе должны быть нулевыми. Между IP-адресом, знаком / и длиной маски CIDR не должно быть пробелов.

Типичные примеры диапазона адресов IPv4, заданных таким образом: 172.20.143.89/32 для одного хоста, 172.20.143.0/24 for для небольшой и 10.6.0.0/16 для более крупной сети. Диапазон адресов IPv6 может выглядеть как ::1/128 для одного хоста (это loopback-адрес IPv6) или как fe80::7a31:c1ff:0000:0000/96 для небольшой сети. 0.0.0.0/0 представляет все адреса IPv4, а ::0/0 — все адреса IPv6. Чтобы указать один хост, используйте длину маски 32 для IPv4 или 128 для IPv6. Не опускайте замыкающие нули в сетевом адресе.

Запись, сделанная в формате IPv4, будет соответствовать только подключениям по IPv4, а запись в формате IPv6 — только подключениям по IPv6, даже если представленный адрес находится в диапазоне IPv4-в-IPv6. Обратите внимание, что записи в формате IPv6 не будут приниматься, если системная библиотека C не поддерживает адреса IPv6.

Также можно написать значение all для соответствия записи любому адресу IP, samehost для соответствия любым IP-адресам данного сервера или samenet для соответствия любому адресу любой подсети, к которой сервер подключен напрямую.

Если задано имя хоста (все, что не является диапазоном адресов IP или специальным ключевым словом, воспринимается как имя хоста), это имя сравнивается с результатом обратного преобразования имени IP-адреса клиента (например, обратного DNS-поиска, если используется DNS). При сравнении имен хостов регистр не учитывается. Если имена совпали, выполняется прямое преобразование имени хоста (например, прямой DNS-поиск) для проверки, совпадает ли какой-либо из разрешенных адресов с IP-адресом клиента. Если двусторонняя проверка пройдена, запись считается соответствующей хосту. (Имя хоста в файле pg_hba.conf должно быть тем, что возвращается при преобразовании IP-адреса клиента в имя, иначе строка не будет соответствовать узлу. Некоторые базы имен хостов позволяют связать один IP-адрес с несколькими именами хостов, но операционная система при запросе на разрешение IP-адреса вернет только одно имя.)

Указание имени хоста, начинающееся с точки (.), соответствует суффиксу актуального имени хоста. Так, .example.com будет соответствовать foo.example.com (а не только example.com).

Когда в pg_hba.conf указываются имена хостов, следует добиться того, чтобы разрешение имен выполнялось достаточно быстро. Для этого может быть полезно установить локальный кэш разрешения имен, например, nscd. Также можно включить параметр конфигурации log_hostname, чтобы видеть в журнале имя хоста клиента вместо IP-адреса.

Эти поля не применимы к записям local.

Примечание
Иногда пользователи задаются вопросом, почему имена хостов обрабатываются таким сложным, на первый взгляд, способом, с разрешениями двух имен, включая обратный поиск IP-адреса клиента. Это усложняет использование этой функциональности в случае, если обратная DNS-запись клиента не установлена или выдает какое-нибудь нежелательное имя хоста. Это сделано, в первую очередь, из соображений эффективности: при таком подходе попытка подключения требует максимум два поиска разрешения, один обратный и один прямой. Если есть проблема разрешения с каким-то адресом, она становится проблемой только этого клиента. Гипотетическая альтернативная реализация, при которой выполнялись бы только прямые поиски, могла бы разрешать все имена хостов, упомянутые в pg_hba.conf во время каждой попытки подключения. Но если перечислено много имен, этот процесс был бы довольно медленным. И в случае наличия проблемы разрешения у одного имени хоста, это становится общей проблемой.

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

Обратите внимание, что это поведение согласуется с другими популярными реализациями управления доступом на основе имен хостов, такими как Apache HTTP Server и TCP Wrappers.

IP-адрес
IP-маска
Эти два поля можно использовать как альтернативу записи IP-адрес/длина-маски. Вместо того, чтобы указывать длину маски, в отдельном столбце указывается сама маска. Например, 255.0.0.0 представляет собой маску CIDR для IPv4 длиной 8 бит, а 255.255.255.255 представляет маску CIDR длиной 32 бита.

Эти поля не применимы к записям local.

метод-аутентификации
Задает метод аутентификации, который будет использован, когда подключение соответствует этой записи. Варианты выбора перечислены ниже; подробности см. в разделе Методы аутентификации.

trust
Разрешить безусловное подключение. Этот метод позволяет любому, кто может подключиться к серверу базы данных QHB, войти под любым желаемым пользователем QHB без необходимости вводить пароль или какой-либо другой аутентификации. Подробности см. в подразделе Аутентификация trust.

reject
Отклонить безусловное подключение. Это полезно для «отфильтровывания» некоторых хостов из группы, например, строка reject может заблокировать подключение от определенного хоста, тогда как следующая строка позволит подключиться остальным хостам в той же сети.

scram-sha-256
Выполнить аутентификацию SCRAM-SHA-256 для проверки пароля пользователя. Подробности см. в подразделе Аутентификация по паролю.

md5
Выполнить аутентификацию SCRAM-SHA-256 или MD5 для проверки пароля пользователя. Подробности см. в подразделе Аутентификация по паролю.

password
Потребовать для аутентификации введения клиентом незашифрованного пароля. Поскольку пароль посылается открытым текстом через сеть, такой метод нельзя использовать в недоверенных сетях. Подробности см. в подразделе Аутентификация по паролю.

gss
Использовать GSSAPI для аутентификации пользователя. Этот метод доступен только для подключений по TCP/IP. Подробности см. в подразделе Аутентификация GSSAPI. Его можно применять в сочетании с шифрованием GSSAPI.

ident
Получить имя пользователя операционной системы клиента, связываясь с сервером ident, и проверить, соответствует ли оно имени пользователя запрашиваемой базы данных. Аутентификацию ident можно использовать только при подключениях по TCP/IP. Если указать этот метод для локальных подключений, вместо него будет применяться аутентификация peer. Подробности см. в подразделе Аутентификация ident.

peer
Получить имя пользователя операционной системы клиента из операционной системы и проверить, соответствует ли оно имени пользователя запрашиваемой базы данных. Доступно только для локальных подключений. Подробности см. в подразделе Аутентификация peer.

ldap
Провести аутентификацию, используя сервер LDAP. Подробности см. в подразделе Аутентификация LDAP.

radius
Провести аутентификацию, используя сервер RADIUS. Подробности см. в подразделе Аутентификация RADIUS.

cert
Провести аутентификацию, используя клиентские сертификаты SSL. Подробности см. в подразделе Аутентификация по сертификату.

pam
Провести аутентификацию, используя службу подключаемых модулей аутентификации (Pluggable Authentication Modules, PAM), предоставляемую операционной системой. Подробности см. в подразделе Аутентификация PAM.

bsd
Провести аутентификацию, используя службу аутентификации BSD, предоставляемую операционной системой. Подробности см. в подразделе Аутентификация BSD.
.

параметры-аутентификации
После поля метод-аутентификации может идти поле (или поля) вида имя=значение, задающее параметры метода аутентификации. Подробнее о том, какие параметры доступны для различных методов аутентификации, рассказывается ниже.

Кроме перечисленных ниже параметров, относящихся к различным методам, есть один общий параметр аутентификации clientcert, который можно задать в любой записи hostssl. Этот параметр может принимать значения verify-ca и verify-full. В обоих случаях требуется, чтобы клиент предоставил действительный (доверенный) сертификат SSL, но вариант verify-full дополнительно требует, чтобы значение cn (Common Name, общее имя) в сертификате соответствовало имени пользователя или применимому сопоставлению. Это поведение схоже с методом аутентификации cert (см. подраздел Аутентификация по сертификату), но позволяет связать проверку сертификатов клиента с любым методом аутентификации, поддерживающим записи hostssl.

В любой записи, где используется аутентификация по сертификату клиента (т. е. в записи с методом аутентификации cert или с параметром clientcert), в параметре clientname можно указать, какая часть учетных данных в сертификате клиента будет сопоставляться при проверке. Этот параметр может принимать одно из двух значений. Если задано значение clientname=CN (это значение по умолчанию), имя пользователя сопоставляется с полем сертификата Common Name (CN). Если задано значение clientname=DN, имя пользователя сопоставляется со всем отличительным именем, Distinguished Name (DN), в сертификате. Этот параметр, вероятно, лучше всего применять в сочетании с сопоставлением пользователя. Сравнение с DN выполняется в формате RFC 2253. Чтобы просмотреть в этом формате поле DN сертификата клиента, выполните

openssl x509 -in myclient.crt -noout --subject -nameopt RFC2253 | sed "s/^subject=//"

Использовать этот параметр следует с осторожностью, особенно когда для сопоставлений с DN применяются регулярные выражения.
.

Файлы, включенные в запись конструкциями, начинающимися с @, читаются как список имен, разделенных пробелами или запятыми. Комментарии предваряются знаками #, как и в pg_hba.conf, и вложенные конструкции с @ допустимы. Если имя файла, начинающееся с @, не является абсолютным путем, оно считается относящимся к каталогу, содержащему ссылающийся файл.

Поскольку записи в pg_hba.conf просматриваются последовательно для каждой попытки подключения, порядок записей имеет большое значение. Обычно более ранние записи содержат жесткие параметры соответствия для подключения и более гибкие методы аутентификации, тогда как в более поздних записях параметры соответствия менее требовательны, а методы аутентификации более строгие. Например, некто желает использовать аутентификацию trust для локальных подключений по TCP/IP, но при этом запрашивать пароль для удаленных подключений по TCP/IP. В этом случае запись, задающая аутентификацию trust для подключений с адреса 127.0.0.1 должна предшествовать записи, задающей аутентификацию по пароль для более широкого диапазона клиентских IP-адресов.

Файл pg_hba.conf прочитывается при запуске системы, а также при получении основным серверным процессом сигнала SIGHUP. При редактировании этого файла во время работы системы необходимо послать сигнал процессу postmaster (воспользовавшись pg_ctl reload, вызвав функцию SQL pg_reload_conf() или выполнив kill -HUP), чтобы он заново прочел файл.

Системное представление pg_hba_file_rules может быть полезно для предварительной проверки изменений в файле pg_hba.conf или для диагностики проблем, если загрузка этого файла не привела к желаемым результатам. Строки в этом представлении, содержащие в поле error не NULL, указывают на проблемы в соответствующих строках файла.

Совет
Чтобы подключиться к определенной базе данных, пользователь должен не только пройти проверки файла pg_hba.conf, но и иметь право CONNECT для этой базы данных. Если требуется ограничить доступ к каким-либо базам данных для определенных пользователей, как правило, этим проще управлять, предоставляя/ отзывая право CONNECT, нежели устанавливая правила в записях pg_hba.conf.

Некоторые примеры записей файла pg_hba.conf показаны в Примере 1 ниже. Более подробную информацию о различных методах аутентификации см. в следующем разделе.

Пример 1. Пример записей pg_hba.conf

# Позволить любому пользователю локальной системы подключаться к любой базе данных,
# используя любое имя пользователя баз данных, через сокеты домена Unix
# (по умолчанию для локальных подключений).
#
# TYPE  DATABASE        USER            ADDRESS                 METHOD
  local all             all                                     trust

# То же самое, но для локальных подключений loopback по TCP/IP.
#
# TYPE  DATABASE        USER            ADDRESS                 METHOD
  host  all             all             127.0.0.1/32            trust

# То же, что и в предыдущей строке, но с указанием сетевой маски в отдельном столбце
#
# TYPE  DATABASE        USER            IP-ADDRESS      IP-MASK             METHOD
  host  all             all             127.0.0.1       255.255.255.255     trust

# То же самое для IPv6.
#
# TYPE  DATABASE        USER            ADDRESS                 METHOD
  host  all             all             ::1/128                 trust

# То же самое, но с использованием имени хоста (обычно охватывает и IPv4, и IPv6).
#
# TYPE  DATABASE        USER            ADDRESS                 METHOD
  host  all             all             localhost               trust

# Позволить любому пользователю любого узла с IP-адресом 192.168.93.x подключаться
# к базе данных «qhb» с тем же именем пользователя, которое сообщает для данного
# подключения ident (как правило, имя пользователя операционной системы).
#
# TYPE  DATABASE        USER            ADDRESS                 METHOD
  host  qhb             all             192.168.93.0/24         ident

# Позволить любому пользователю узла 192.168.12.10 подключаться к базе данных
# «qhb», если передается правильный пароль пользователя.
#
# TYPE  DATABASE        USER            ADDRESS                 METHOD
  host  qhb             all             192.168.12.10/32        scram-sha-256

# Позволить любому пользователю хостов в домене example.com подключаться к
# любой базе данных, если передается правильный пароль пользователя.
#
# Для большинства пользователей требуется аутентификация SCRAM, за исключением
# пользователя 'mike', который использует более старый клиент, не поддерживающий
# аутентификацию SCRAM.
#
# TYPE  DATABASE        USER            ADDRESS                 METHOD
  host  all             mike            .example.com            md5
  host  all             all             .example.com            scram-sha-256

# В случае отсутствия предшествующих строчек с «host», эти три строки откажут
# в любых подключениях с 192.168.54.1 (поскольку эта запись будет сопоставлена
# первой), но разрешат подключения с шифрованием GSSAPI с любых других адресов
# интернета. С нулевой маской ни один бит из IP-адреса узла не учитывается,
# так что этой строке соответствует любой узел. Подключения без GSSAPI (которые
# «опускаются» до третьей строки, поскольку записи "hostgssenc" соответствуют
# только подключения с GSSAPI) разрешены, но только с адреса 192.168.12.10.
#
# TYPE       DATABASE        USER            ADDRESS                 METHOD
  host       all             all             192.168.54.1/32         reject
  hostgssenc all             all             0.0.0.0/0               gss
  host       all             all             192.168.12.10/32        gss

# Позволить пользователям с узлов 192.168.x.x подключаться к любой базе данных,
# если они проходят проверку ident.  Если же ident говорит, к примеру, что это
# пользователь "bryanh" и он запрашивает подключение как пользователь QHB "guest1",
# подключение будет разрешено, если в pg_ident.conf есть запись с сопоставлением
# "omicron", разрешающим пользователю "bryanh" подключаться как "guest1".
#
# TYPE  DATABASE        USER            ADDRESS                 METHOD
  host  all             all             192.168.0.0/16          ident map=omicron

# Если для локальных подключений предусмотрены только эти три строки, они позволят
# локальным пользователям подключаться только к их собственным базам данных (базам
# данных с именами, совпадающими с их именами пользователей баз данных), за
# исключением администраторов и членов роли "support", которые могут подключаться
# ко всем базам данных. Список имен администраторов содержится в файле
# $PGDATA/admins. Пароли требуются в любом случае.
#
# TYPE   DATABASE         USER              ADDRESS               METHOD
  local  sameuser         all                                     md5
  local  all              @admins                                 md5
  local  all              +support                                md5

# Последние две строки выше можно объединить в одну:
  local  all              @admins,+support                        md5

# В столбце DATABASE также можно указывать списки и имена файлов:
  local  db1,db2,@demodbs all                                     md5

Файл сопоставлений имен пользователей

При использовании внешней системы аутентификации, например Ident или GSSAPI, имя пользователя операционной системы, устанавливающего подключение, может не совпадать с целевым именем пользователя (роли) базы данных. В этом случае можно применить сопоставление имен пользователей ОС и пользователей базы данных. Чтобы воспользоваться этим сопоставлением, следует указать map=имя-сопоставления в поле параметров в файле pg_hba.conf. Этот параметр поддерживается для всех методов аутентификации, принимающих внешние имена пользователей. Поскольку для разных подключений могут понадобиться разные сопоставления, имя применяемого сопоставления задается в параметре имя-сопоставления в pg_hba.conf для каждого отдельного подключения.

Сопоставления имен пользователей определяются в файле сопоставления ident, который по умолчанию называется pg_ident.conf и хранится в каталоге данных кластера. (Однако этот файл можно поместить и в другое место; см. описание параметра конфигурации ident_file.) Файл сопоставления ident содержит строки общего вида:

имя-сопоставления имя-пользователя-ос имя-пользователя-бд

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

Нет никаких ограничений касательно количества пользователей баз данных, на которые может ссылаться пользователь операционной системы, и наоборот. В связи с этим записи в файле сопоставления скорее имеют смысл «этому пользователю операционной системы разрешено подключаться под этим именем пользователя базы данных», нежели подразумевают, что эти имена пользователей равнозначны. Подключение будет разрешено, если в файле сопоставления есть запись, соединяющая имя пользователя, полученное от внешней системы аутентификации, с именем пользователя базы данных, под которым этот пользователь хочет подключиться.

Если поле имя-пользователя-ос начинается со слэша (/), оставшаяся часть поля рассматривается как регулярное выражение. (Более подробную информацию о синтаксисе регулярных выражений QHB см. в подразделе Подробное описание регулярных выражений.) Регулярное выражение может включать в себя одно обобщение или заключенное в скобки вложенное выражение, на которое можно сослаться в поле имя-пользователя-бд, написав \1 (обратный слэш-один). Это позволяет сопоставить несколько имен пользователей в одной строке, что особенно полезно для простых синтаксических замен. Например, эти записи

mymap   /^(.*)@mydomain\.com$      \1
mymap   /^(.*)@otherdomain\.com$   guest

удалят доменную часть для пользователей, чье имя пользователя системы оканчивается на @mydomain.com, и позволят всем пользователям, чье имя пользователя системы оканчивается на @otherdomain.com, подключиться как guest.

Совет
Помните, что по умолчанию регулярное выражение может совпасть только с частью строки. Разумным выходом будет использовать символов ^ и $, как показано в примере выше, чтобы обеспечить совпадение со всем именем пользователя ОС.

Файл pg_ident.conf прочитывается при запуске системы, а также при получении основным серверным процессом сигнала SIGHUP. При редактировании этого файла во время работы системы необходимо послать сигнал процессу postmaster (воспользовавшись pg_ctl reload, вызвав функцию SQL pg_reload_conf() или выполнив kill -HUP), чтобы он заново прочел файл.

Файл pg_ident.conf, который можно использовать в сочетании с файлом pg_hba.conf из Примера 1, показан в Примере 2. В этом примере любому, выполнившему вход в систему на компьютере в сети 192.168 под именем пользователя операционной системы, отличным от bryanh, ann или robert, будет отказано в доступе. Пользователь ОС Unix robert получит доступ, только когда попытается подключиться как пользователь QHB bob, но не как robert или какой-либо другой пользователь. Пользователь ann сможет подключиться только как ann. Пользователь bryanh сможет подключиться как bryanh или как guest1.

Пример 2. Пример файла pg_ident.conf

# MAPNAME       SYSTEM-USERNAME         PG-USERNAME

omicron         bryanh                  bryanh
omicron         ann                     ann
# у пользователя БД bob на этих компьютерах имя пользователя robert
omicron         robert                  bob
# bryanh также может подключаться как guest1
omicron         bryanh                  guest1

Методы аутентификации

QHB предоставляет к использованию разнообразные методы аутентификации пользователей:

Для локальных подключений обычно рекомендуется аутентификация peer, хотя в некоторых обстоятельствах может быть достаточно и аутентификации trust. Для удаленных подключений самой простой будет аутентификация по паролю. Все прочие варианты требуют участия некоторой внешней инфраструктуры безопасности (обычно это сервер аутентификации или центр сертификации, выдающий сертификаты SSL) или поддерживаются не на всех платформах.

Аутентификация trust

Когда указан метода аутентификации trust, QHB предполагает, что любой подключающийся к серверу авторизован для доступа к базе данных, независимо от указанного имени пользователя базы данных (даже если это имя суперпользователя). Разумеется, ограничения, прописанные в столбцах database и user, продолжают действовать. Этот метод следует применять только в том случае, когда на уровне операционной системы обеспечена адекватная защита подключений к серверу.

Аутентификация trust оптимальна и очень удобна для локальных подключений на однопользовательской рабочей станции. Обычно само по себе этот метод не подходит для машин с несколькими пользователями. Однако его можно использовать даже на многопользовательской машине, если ограничить доступ к серверному файлу сокета домена Unix посредством разрешений файловой системы. Для этого нужно установить параметры конфигурации unix_socket_permissions (и, возможно, unix_socket_group) как описано в подразделе Подключения и аутентификация. Или можно установить параметр конфигурации unix_socket_directories, чтобы разместить файл сокета в должным образом защищенном каталоге.

Установка разрешений на уровне файловой системы помогает только в случае подключения через сокеты Unix. На локальные подключения по TCP/IP ограничения файловой системы не действуют. Таким образом, если вы хотите использовать разрешения файловой системы для обеспечения локальной безопасности, уберите строку host ... 127.0.0.1 ... из файла pg_hba.conf или смените метод аутентификации.

Аутентификация trust подойдет для подключений по TCP/IP, только если вы доверяете каждому пользователю на каждом компьютере, который получил разрешение на подключение к серверу строками файла pg_hba.conf, указывающими метод trust. Не рекомендуется использовать trust для любых подключений по TCP/IP, отличных от localhost (127.0.0.1).

Аутентификация по паролю

Существует несколько методов аутентификации по паролю. Эти методы работают сходным образом, но различаются тем, как пароли пользователей хранятся на сервере и как предоставленный клиентом пароль передается через соединение.

scram-sha-256

С методом scram-sha-256 выполняется аутентификация SCRAM-SHA-256, как описано в RFC 7677. Она производится по схеме запрос-ответ, предотвращающей перехват паролей через недоверенные соединения и поддерживающей хранение паролей на сервере в криптографически хешированной форме, которая считается безопасной.

Это наиболее безопасный из предоставляемых на данный момент методов, но он не поддерживается старыми клиентскими библиотеками.

md5

В методе md5 применяется нестандартный и менее безопасный механизм запрос- ответ. Он предотвращает перехват паролей и не допускает хранение паролей на сервере в виде простого текста, но не обеспечивает защиту, если злоумышленнику удается похитить хеши паролей с сервера. Кроме того, в наши дни алгоритм хеширования MD5 уже не считается способным защитить от целенаправленных атак.

Метод md5 несовместим с функциональностью db_user_namespace.

Для облегчения перехода от метода md5 к более новому методу SCRAM, если в качестве метода аутентификации в файле pg_hba.conf указан md5, но пароль пользователя на сервере зашифрован для SCRAM (см. ниже), то будет автоматически выполняться аутентификация на основе SCRAM.

password

С методом password пароль передается открытым текстом и поэтому является уязвимым для атак с перехватом паролей. По возможности его всегда следует избегать. Однако если подключение защищено шифрованием SSL, то метод password может быть безопасен. (Хотя при использовании SSL, возможно, лучшим выбором будет аутентификация по сертификату SSL.)

Пароли баз данных QHB отделены от паролей пользователей операционной системы. Пароли всех пользователей базы данных хранятся в системном каталоге pg_authid. Управлять паролями можно либо с помощью команд SQL CREATE ROLE и ALTER ROLE, например, CREATE ROLE foo WITH LOGIN PASSWORD 'secret', либо с помощью команды psql \password. Если пароль для пользователя не задан, вместо него хранится NULL, и пройти аутентификацию по паролю этот пользователь не сможет.

Доступность различных методов аутентификации по паролю зависит от того, как пароли пользователей шифруются на сервере (или, если точнее, хешируются). Это определяется параметром конфигурации password_encryption в момент назначения пароля. Если пароль шифруется алгоритмом scram-sha-256, его можно будет использовать для методов аутентификации scram-sha-256 и password (но в последнем случае пароль будет передаваться открытым текстом). При этом в случае указания метода аутентификации md5 произойдет автоматическое переключение на использование метода scram-sha-256, как описано выше, так что этот вариант тоже будет работать. Если пароль шифруется алгоритмом md5, то его можно будет использовать только для методов аутентификации md5 и password (опять же, в последнем случае пароль будет передаваться открытым текстом). Чтобы проверить хранящиеся в данный момент в базе данных хеши паролей, обратитесь к системному каталогу pg_authid.

Чтобы перевести существующую инсталляцию с md5 на scram-sha-256, сперва убедитесь о том, что все клиентские библиотеки были обновлены до версий, поддерживающих SCRAM, задайте password_encryption = 'scram-sha-256' в qhb.conf, заставьте всех пользователей сменить свои пароли, а затем поменяйте указание метода аутентификации в pg_hba.conf на scram-sha-256.

Аутентификация GSSAPI

GSSAPI — это принятый в качестве отраслевого стандарта протокол для безопасной аутентификации, определенный в RFC 2743. QHB поддерживает применение GSSAPI для аутентификации, шифрования соединений, а также для аутентификации с шифрованием. GSSAPI предоставляет автоматическую аутентификацию (система единого входа) для систем, которые ее поддерживают. Сама по себе аутентификация безопасна, но данные, передаваемые через подключение к базе данных, будут кодироваться, только если используется шифрование GSSAPI или SSL.

Поддержку GSSAPI следует включать при сборке QHB; дополнительную информацию см. в главе Установка.

При работе с Kerberos GSSAPI использует стандартные имена субъектов-служб (аутентификационные идентификаторы) в формате имя_службы/ имя_хоста@область. Имя субъекта-службы, используемое конкретной инсталляцией, не закодировано в сервере QHB ни в каком виде; оно задается в файле keytab, который сервер прочитывает для определения его идентификатора. Если в файле keytab перечислено несколько субъектов-служб, сервер выберет любой из них. В качестве области сервера задается предпочитаемая область, указанная в доступном серверу файле (или файлах) конфигурации Kerberos.

Клиент должен знать имя субъекта-службы сервера, к которому он намерен подключиться. Обычно в качестве элемента имя_службы субъекта службы указывается qhb, но в параметре подключения krbsrvname в libpq можно выбрать и другое значение. В элементе имя_хоста задается полное имя узла, к которому будет подключаться libpq. В элементе область задается предпочитаемая область, указанная в доступном клиенту файле (или файлах) конфигурации Kerberos.

У клиента также должно быть имя субъекта-службы для его собственного идентификатора (и он должен иметь действительный билет для этого субъекта- службы). Чтобы использовать GSSAPI для аутентификации, субъект-службы клиента должен быть связан с именем пользователя базы данных QHB. Для сопоставления имен субъектов-служб с именами пользователей можно воспользоваться файлом конфигурации pg_ident.conf; например, pgusername@realm можно сопоставить с простым pgusername. Как вариант, можно использовать полное имя субъекта-службы username@realm в качестве имени роли в QHB без какого-либо сопоставления.

QHB также поддерживает сопоставление субъектов-служб клиентов с именами пользователей путем простого удаления элемента области из имени субъекта- службы. Этот метод оставлен для обратной совместимости и настоятельно не рекомендуется к использованию, поскольку при этом невозможно различить разных пользователей с одинаковыми именами, но приходящих из разных областей. Чтобы включить этот режим, установите для include_realm значение 0. В простых инсталляциях с одной областью исключение области в сочетании с установкой параметра krb_realm (который позволяет ограничить область субъекта-службы значением, заданным в параметре krb_realm) все же будет безопасным, но менее эффективным подходом по сравнению с явным сопоставлением в pg_ident.conf.

Расположение файла keytab на сервере задается параметром конфигурации krb_server_keyfile. По соображениям безопасности рекомендуется использовать отдельный файл keytab специально для сервера QHB, а не разрешать серверу читать системный файл keytab. Сделайте так, чтобы пользователь, от имени которого запущен сервер QHB, имел право на чтение этого серверного файла keytab (право на запись давать не стоит). (См. также раздел Учетная запись пользователя.)

Файл keytab генерируется с использованием ПО; подробную информацию см. в документации Kerberos. Следующий пример показывает, как его сгенерировать с помощью программы kadmin в MIT-совместимых реализациях Kerberos 5:

kadmin% addprinc -randkey postgres/server.my.domain.org
kadmin% ktadd -k krb5.keytab postgres/server.my.domain.org

Для метода аутентификации GSSAPI поддерживаются следующие параметры аутентификации:

include_realm
При значении 0 перед проведением сопоставления имен пользователей из субъекта- службы аутентифицированного пользователя убирается область (см. раздел Файл сопоставления имен пользователей). Этот вариант не рекомендуется и поддерживается в основном для обратной совместимости, поскольку он небезопасен в средах с несколькими областями, если только дополнительно не установить krb_realm. Рекомендуется оставить в include_realm значение по умолчанию (1) и задать в pg_ident.conf явное сопоставление для преобразования имен субъектов-служб в имена пользователей QHB.

map
Разрешает сопоставление субъектов-служб с именами пользователей базы данных. Подробную информацию см. в разделе Файл сопоставления имен пользователей. Для субъекта-службы GSSAPI/Kerberos, такого как username@EXAMPLE.COM (или более редкого username/hostbased@EXAMPLE.COM), именем пользователя в сопоставлении будет username@EXAMPLE.COM (или username/hostbased@EXAMPLE.COM соответственно), если include_realm не равно 0; иначе имя системного пользователя в сопоставлении будет username (или username/hostbased).

krb_realm
Задает область, с которой будут сопоставляться имена субъектов-служб пользователей. Если этот параметр установлен, подключаться смогут только пользователи из этой области. Если он не установлен, подключаться смогут пользователи из любой области, в зависимости от выполняемого сопоставления имен пользователей.

Помимо этих параметров, которые могут отличаться в разных записях файла pg_hba.conf, существует параметр конфигурации уровня сервера krb_caseins_users. Если он равен true, субъекты-службы пользователей сопоставляются с записями пользователей без учета регистра. Если установлен параметр krb_realm, области тоже сопоставляются без учета регистра.

Аутентификация ident

Метод аутентификации ident работает, получая имя пользователя операционной системы клиента от сервера Ident и используя его в качестве разрешенного имени пользователя базы данных (с возможным сопоставлением имен пользователей). Этот метод поддерживается только для подключений по TCP/IP.

Примечание
При указании ident для локального подключения (не TCP/IP), вместо него будет использован метод подключения peer (см. подраздел Аутентификация peer).

Для метода ident поддерживаются следующие параметры конфигурации:

map
Позволяет сопоставить имена пользователей системы и базы данных. Подробную информацию см. в разделе Файл сопоставления имен пользователей.

«Протокол идентификации» (Ident) описан в RFC 1413. Практически каждая Unix- подобная операционная системы поставляется с сервером Ident, по умолчанию прослушивающим TCP-порт 113. Базовая функциональность этого сервера — отвечать на вопросы вроде «Какой пользователь инициировал подключение, которое идет через твой порт X и подключается к моему порту Y?». Поскольку после установления физического подключения QHB знает и X, и Y, она может опрашивать сервер Ident на хосте клиентского подключения и теоретически может определять пользователя операционной системы при каждом подключении.

Недостаток этой процедуры состоит в том, что она зависит от интеграции с клиентом: если компьютер клиента недоверенный или взломанный, злоумышленник может запустить практически любую программу на порту 113 и вернуть любое имя пользователя по своему выбору. Таким образом, этот метод аутентификации подходит только для закрытых сетей, где каждый компьютер клиента находится под строгим контролем и где администраторы операционных систем и баз данных работают в тесном контакте. Другими словами, вы должны доверять компьютеру, на котором работает сервер Ident. Относитесь серьезно к предупреждению:

Протокол идентификации не предназначен для использования в качестве протокола авторизации или управления доступом.

--RFC 1413

У некоторых серверов Ident есть нестандартная функциональность, позволяющая зашифровывать возвращаемое имя пользователя, используя ключ, который известен только администратору исходного компьютера. Эту функциональность нельзя использовать с QHB, так как у QHB нет никакой возможности расшифровать возвращаемую строку и получить фактическое имя пользователя.

Аутентификация peer

Метод аутентификации peer работает, получая имя пользователя операционной системы клиента из ядра и используя его в качестве разрешенного имени пользователя базы данных (с возможным сопоставлением имен пользователей). Этот метод поддерживается только для локальных подключений.

Для метода peer поддерживаются следующие параметры конфигурации:

map
Позволяет сопоставить имена пользователей системы и базы данных. Подробную информацию см. в разделе Файл сопоставления имен пользователей.

Аутентификация peer доступна только в операционных системах, предоставляющих функцию getpeereid(), параметр сокета SO_PEERCRED или схожие механизмы. В настоящее время это Linux, большинство разновидностей BSD, включая macOS, и Solaris.

Аутентификация LDAP

Этот метод аутентификации работает сходным с методом password образом, за исключением того, что он использует LDAP в качестве метода подтверждения пароля. LDAP используется только для подтверждения пар имя пользователя/пароль. Поэтому пользователь должен уже существовать в базе данных до того, как для аутентификации можно будет использовать LDAP.

Аутентификация LDAP может работать в двух режимах. В первом режиме, который мы назовем простым связыванием, сервер связывается с отличительным именем, составленным в виде префикс имя_пользователя суффикс. Как правило, параметр префикс используется для указания cn= или DOMAIN\ в среде Active Directory. Параметр суффикс используется для указания оставшейся части DN в среде, отличной от Active Directory.

Во втором режиме, который мы назовем поиск+связывание, сервер сначала связывается с каталогом LDAP с постоянным именем пользователя и паролем, указанными в ldapbinddn и ldapbindpasswd, и выполняет поиск этого пользователя, пытающегося подключиться к базе данных. Если пользователь и пароль не определены, сервер пытается связаться с каталогом анонимно. Поиск осуществляется в поддереве ldapbasedn с попыткой подобрать точное соответствие атрибуту, заданному в ldapsearchattribute. Как только при поиске находится пользователь, сервер разрывает подключение и заново связывается с каталогом уже как этот пользователь, используя указанный клиентом пароль, чтобы удостовериться, что эта учетная запись корректна. Этот же режим применяется в схемах аутентификации LDAP в другом программном обеспечении, например, в mod_authnz_ldap и pam_ldap в Apache. Этот режим дает гораздо больше гибкости в выборе расположения объектов пользователей в каталоге, но при этом требует дважды подключаться к серверу LDAP.

В обоих режимах используются следующие параметры конфигурации:

ldapserver
Имена или IP-адреса серверов LDAP для подключения. Можно указать несколько серверов, разделяя их пробелами.

ldapport
Номер порта для подключения к серверу LDAP. Если порт не указан, будет использован установленный по умолчанию порт библиотеки LDAP.

ldapscheme
Значение ldaps выбирает протокол LDAPS. Это нестандартный способ использования LDAP поверх SSL, поддерживаемый некоторыми реализациями сервера LDAP. Альтернативный вариант см. в описании параметра ldaptls.

ldaptls
При значении 1 в соединении QHB с сервером LDAP применяется шифрование TLS. При этом используется операция StartTLS, описанная в RFC 4513. Альтернативный вариант см. в описании параметра ldapscheme.

Обратите внимание, что при использовании ldapscheme или ldaptls шифруется только трафик между сервером QHB и сервером LDAP. Соединение между сервером QHB и клиентом QHB останется незашифрованным, если только и для него не применяется SSL.

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

ldapprefix
Эта строка подставляется перед именем пользователя во время формирования DN для связывания при аутентификации в режиме простого связывания.

ldapsuffix
Эта строка подставляется после имени пользователя во время формирования DN для связывания при аутентификации в режиме простого связывания.

Следующие параметры используются только в режиме поиск+связывание:

ldapbasedn
Корневая папка DN, с которой начинается поиск пользователя при аутентификации в режиме поиск+связывание.

ldapbinddn
DN пользователя для связывания с каталогом при выполнении поиска в ходе аутентификации в режиме поиск+связывание.

ldapbindpasswd
Пароль пользователя для связывания с каталогом при выполнении поиска в ходе аутентификации в режиме поиск+связывание.

ldapsearchattribute
Атрибут для сравнения с именем пользователя при выполнении поиска в ходе аутентификации в режиме поиск+связывание. Если атрибут не указан, будет использован атрибут uid.

ldapsearchfilter
Фильтр поиска, используемый при аутентификации в режиме поиск+связывание. Вхождения $username в нем будут заменяться именем пользователя. Это позволяет задавать более гибкие фильтры поиска, чем ldapsearchattribute.

ldapurl
URL-адрес LDAP согласно стандарту RFC 4516. Это альтернативный способ записи некоторых других параметров LDAP в более компактной и стандартной форме. Формат адреса выглядит так:

ldap[s]://хост[:порт]/basedn[?[атрибут][?[область_действия][?[фильтр]]]]

Элемент область_действия должен иметь значение base, one или sub, обычно последнее. (Значение по умолчанию, base, обычно не очень полезно при таком применении.) В элементе атрибут можно назначить один атрибут; в этом случае он используется как значение параметра ldapsearchattribute. Если атрибут не указан, в качестве значения параметра ldapsearchfilter можно использовать фильтр.

Схема URL-адреса ldaps выбирает для установления LDAP-подключений поверх SSL метод LDAPS, что равнозначно указанию ldapscheme=ldaps. Для применения шифрованных LDAP-подключений с операцией StartTLS используйте обычную схему URL-адреса ldap и в дополнение к параметру ldapurl укажите ldaptls.

Для неанонимных связываний ldapbinddn и ldapbindpasswd следует указывать как отдельные параметры.

В настоящее время URL-адреса LDAP поддерживаются только с OpenLDAP.
.

Не стоит путать параметры конфигурации для режима простого связывания с параметрами для режима поиск+связывание, это будет ошибкой.

В режиме поиск+связывание поиск может выполняться либо по одному атрибуту, заданному в ldapsearchattribute, либо по произвольному фильтру поиска, заданному в ldapsearchfilter. Указание ldapsearchattribute=foo равнозначно указанию ldapsearchfilter="(foo=$username)". Если ни один параметр не указан, по умолчанию подразумевается ldapsearchattribute=uid.

Если QHB была скомпилирована с OpenLDAP в качестве клиентской библиотеки LDAP, параметр ldapserver может быть опущен. В этом случае за списком имен хостов и портов сервер, согласно стандарту RFC 2782, обращается в DNS, к SRV-записи с именем _ldap._tcp.DOMAIN, где DOMAIN извлекается из ldapbasedn.

Пример конфигурации LDAP для простого связывания:

host ... ldap ldapserver=ldap.example.net ldapprefix="cn=" ldapsuffix=", dc=example, dc=net"

Когда запрашивается подключение к серверу базы данных в качестве пользователя базы данных someuser, QHB пытается связаться с сервером LDAP, используя DN cn=someuser, dc=example, dc=net и пароль, предоставленный клиентом. Если это подключение удалось, то доступ к базе данных будет открыт.

Пример конфигурации для режима поиск+связывание:

host ... ldap ldapserver=ldap.example.net ldapbasedn="dc=example, dc=net" ldapsearchattribute=uid

Когда запрашивается подключение к серверу базы данных в качестве пользователя базы данных someuser, QHB пытается связаться с сервером LDAP анонимно (поскольку параметр ldapbinddn не был задан), выполняет поиск для (uid=someuser) под указанным DN базы. Если запись найдена, QHB пытается связаться с использованием этой найденной информации и пароля, предоставленного клиентом. Если это второе подключение удалось, то доступ к базе данных будет открыт.

Пример той же конфигурации для режима поиск+связывание, записанной в виде URL-адреса:

host ... ldap ldapurl="ldap://ldap.example.net/dc=example,dc=net?uid?sub"

Такой же формат URL-адреса используется и другим программным обеспечением, поддерживающим аутентификацию по протоколу LDAP, так что эту конфигурацию будет легче использовать совместно.

Пример конфигурации для режима поиск+связывание, в котором ldapsearchfilter используется вместо ldapsearchattribute для прохождения аутентификации по ID или почтовому адресу пользователя:

host ... ldap ldapserver=ldap.example.net ldapbasedn="dc=example, dc=net" ldapsearchfilter="(|(uid=$username)(mail=$username))"

Пример конфигурации для режима поиск+связывание, в котором используется поиск SRV-записи в DNS для получения имен хостов и портов службы LDAP в домене example.net:

host ... ldap ldapbasedn="dc=example,dc=net"

Совет
Поскольку LDAP нередко использует для разделения различных частей DN запятые и пробелы, зачастую при конфигурировании параметров LDAP возникает необходимость заключать их значения в кавычки, как показано в примерах выше.

Аутентификация RADIUS

Этот метод аутентификации работает сходным с методом password образом, за исключением того, что он использует RADIUS в качестве метода подтверждения пароля. RADIUS используется только для подтверждения пар имя пользователя/пароль. Поэтому пользователь должен уже существовать в базе данных до того, как для аутентификации можно будет использовать RADIUS.

При использовании аутентификации RADIUS настроенному серверу RADIUS посылается сообщение с запросом доступа. Это запрос типа Authenticate Only (только аутентификация), который включает в себя элементы user name (имя пользователя), password (зашифрованный пароль) и NAS Identifier (идентификатор NAS). Запрос шифруется с использованием общего с сервером секрета. Сервер RADIUS отвечает на этот запрос либо Access Accept (Доступ разрешен), либо Access Reject (В доступе отказано). Система ведения учета для RADIUS отсутствует.

Можно указать несколько серверов RADIUS, тогда они будут перебираться по очереди. Если от любого из серверов будет получен отрицательный ответ, произойдет сбой аутентификации.Если ответ не будет получен, последует попытка подключения к следующему серверу в списке. Чтобы указать несколько серверов, следует разделить их имена запятыми и заключить список в кавычки. При этом все остальные параметры RADIUS тоже можно задать в списках через запятую, чтобы каждый сервер получил отдельное значение. Также их можно задавать одним значением, тогда оно будет применяться ко всем серверам.

Для метода RADIUS поддерживаются следующие параметры конфигурации:

radiusservers
DNS-имена или IP-адреса серверов RADIUS для подключения. Это обязательный параметр.

radiussecrets
Общие секреты, используемые при защищенном обращении к серверам RADIUS. Значение этого параметра должно быть одинаковым на серверах QHB и RADIUS. Рекомендуется использовать стоку как минимум из 16 символов. Это обязательный параметр.

Примечание
Вектор шифрования будет криптографически эффективен только в том случае, если QHB собрана с поддержкой OpenSSL. В остальных случаях передача данных серверу RADIUS следует считать замаскированной, но не защищенной, поэтому при необходимости нужно применить дополнительные меры безопасности.

radiusports
Номера портов для подключения к серверам RADIUS. Если никакой порт не задан, будет использован порт RADIUS по умолчанию (1812).

radiusidentifiers
Сроки, используемые в запросах RADIUS как NAS Identifier. Этот параметр можно использовать, например, для обозначения того, к какому кластеру баз данных пытается подключиться пользователь, что может быть полезно для выбора соответствующей политики на сервере RADIUS. Если никакой идентификатор не задан, по умолчанию будет использован postgresql.

Если в значение параметра RADIUS необходимо добавить запятую или пробел, это можно сделать, заключив это значение в кавычки, но это неудобно, поскольку теперь потребуется два уровня кавычек. Например, так добавляются пробелы в строки секретов RADIUS:

host ... radius radiusservers="server1,server2" radiussecrets="""secret one"",""secret two"""

Аутентификация по сертификату

В данном методе аутентификации используются клиентские сертификаты SSL, поэтому он применим только для подключений SSL. При использовании этого метода аутентификации сервер запросит у клиента предъявления действительного и доверенного сертификата. Пароль у клиента запрашиваться не будет. Атрибут cn (Обычное имя) сертификата сравнивается с запрашиваемым именем пользователя базы данных, и если они совпадают, вход будет разрешен. Если cn отличается от имени пользователя базы данных, то можно использовать сопоставление имен пользователей.

Для аутентификации по сертификату SSL поддерживаются следующие параметры конфигурации:

map
Позволяет сопоставить имена пользователей системы и базы данных. Подробную информацию см. в разделе Файл сопоставления имен пользователей.

Указывать параметр clientcert для аутентификации cert излишне, поскольку метод cert по сути является методом trust с параметром clientcert=verify-full.

Аутентификация PAM

Этот метод аутентификации работает сходным с методом password образом, за исключением того, что он использует PAM (Pluggable Authentication Modules, подключаемые модули аутентификации) в качестве механизма аутентификации. По умолчанию имя службы PAM — postgresql . PAM используется только для подтверждения пар имя пользователя/пароль и, при необходимости, имени или IP-адреса подключенного удаленного хоста. Поэтому пользователь должен уже существовать в базе данных до того, как для аутентификации можно будет использовать PAM. Более подробную информацию о PAM см. на странице описания Linux-PAM.

Для метода PAM поддерживаются следующие параметры конфигурации:

pamservice
Имя службы PAM.

pam_use_hostname
Определяет, предоставляется ли модулям PAM через элемент PAM_RHOST IP-адрес или имя удаленного хоста. По умолчанию используется IP-адрес. При значении 1 вместо него используется разрешенное имя хоста. Разрешение имени хоста может привести к задержкам при подключении. (Большинство конфигураций PAM не пользуются этой информацией, так что этот параметр необходим, только если была создана специальная конфигурация PAM, в которой он используется.)

Примечание
Если PAM настроен для чтения /etc/shadow, произойдет сбой аутентификации, потому что сервер QHB запущен не пользователем root. Однако это не имеет значения, когда PAM настроен для использования LDAP или других методов аутентификации.

Аутентификация BSD

Этот метод аутентификации работает сходным с методом password образом, за исключением того, что он использует аутентификацию BSD для подтверждения пароля. Аутентификация BSD используется только для подтверждения пар имя пользователя/ пароль. Поэтому пользователь должен уже существовать в базе данных до того, как для аутентификации можно будет использовать BSD. Инфраструктура аутентификации BSD в настоящее время доступна только в OpenBSD.

Для аутентификации BSD в QHB применяется тип входа auth- postgresql и проверка с классом входа postgresql, если это определено в login.conf. По умолчанию этот класс входа не существует и QHB использует стандартный класс входа.

Примечание
Для применения аутентификации BSD необходимо сначала добавить учетную запись пользователя QHB (то есть пользователя операционной системы, запускающего сервер) в группу auth. Группа auth существует в системах OpenBSD по умолчанию.

Проблемы аутентификации

Сбои и другие проблемы с аутентификацией обычно проявляют себя посредством сообщений об ошибках, подобных следующему:

FATAL:  no pg_hba.conf entry for host "123.123.123.123", user "andym", database "testdb"

Это сообщение вы, скорее всего, получите, если сможете связаться с сервером, но он не захочет с вами общаться. В сообщении содержится предположение, что сервер отказал в подключении, потому что не может найти соответствующую запись в своем файле конфигурации pg_hba.conf.

FATAL:  password authentication failed for user "andym"

Сообщения вроде этого показывают, что вы связались с сервером и он готов общаться с вами, но только после того, как вы пройдете авторизацию по методу, указанному в файле pg_hba.conf. Проверьте вводимый вами пароль либо настройки Kerberos или Ident, если в сообщении упоминается один из этих типов аутентификации.

FATAL:  user "andym" does not exist

Указанное имя пользователя базы данных не найдено.

FATAL:  database "testdb" does not exist

База данных, к которой вы пытаетесь подключиться, не существует. Обратите внимание, что если вы не указываете имя базы данных, по умолчанию берется имя пользователя базы данных, что вполне может привести к ошибке.

Совет
В журнале сервера может содержаться больше информации по сбою аутентификации, чем в выдаваемых клиенту сообщениях. Если вам не до конца понятны причины сбоя, проверьте журнал сервера.

Роли в базе данных

QHB управляет правами доступа к базе данных, используя концепцию ролей. Роль может рассматриваться как пользователь базы данных или группа пользователей базы данных, в зависимости от того, как установлена роль. Роли могут владеть объектами базы данных (например, таблицами и функциями) и могут назначать права для этих объектов другим ролям, чтобы контролировать, кто имеет доступ к каким объектам. Кроме того, можно предоставить членство в роли другой роли, что позволяет роли участника использовать права, назначенные другой роли.

Понятие ролей объединяет понятия «пользователи» и «группы». Любая роль может выступать в роли пользователя, группы или обоих.

В этой главе описывается, как создавать и управлять ролями. Более подробную информацию о влиянии привилегий ролей на различные объекты базы данных можно найти в разделе Права.

Роли базы данных

Роли базы данных концептуально полностью отделены от пользователей операционной системы. На практике может быть удобно ввести соответствие, но это не обязательно. Роли базы данных являются глобальными для установленного экземпляра базы данных (а не для отдельной базы данных). Чтобы создать роль, используйте команду SQL CREATE ROLE:

CREATE ROLE name;

name соответствует правилам для идентификаторов SQL: без украшений без специальных символов или в двойных кавычках. (На практике обычно требуется добавить в команду дополнительные параметры, такие как LOGIN. Подробнее см. ниже) Чтобы удалить существующую роль, используйте аналогичную команду DROP ROLE:

DROP ROLE name;

Для удобства программы createuser и dropuser предоставляются в качестве оберток вокруг этих команд SQL, которые можно вызывать из командной строки оболочки:

createuser name
dropuser name

Чтобы определить набор существующих ролей, изучите системный каталог pg_roles, например

SELECT rolname FROM pg_roles;

Метакоманда \du программы psql также полезна для просмотра существующих ролей.

Для начальной загрузки системы базы данных, недавно инициализированная система всегда содержит одну предопределенную роль. Эта роль всегда является «суперпользователем», и по умолчанию (если она не изменена при запуске initdb) она будет иметь то же имя, что и пользователь операционной системы, который инициализировал экземпляр базы данных. Обычно эта роль будет называться qhb. Чтобы создать больше ролей, сначала необходимо подключиться c этой начальной ролью к БД.

Каждое соединение с сервером базы данных выполняется с использованием имени определенной роли, и эта роль определяет начальные права доступа для команд, выполненных в этом соединении. Имя роли, которое будет использоваться для конкретного подключения к базе данных, указывается клиентом, который инициирует запрос на подключение в зависимости от приложения. Например, программа psql использует параметр командной строки -U чтобы указать роль для подключения. Многие приложения по умолчанию принимают имя текущего пользователя операционной системы (включая createuser и psql). Поэтому часто удобно поддерживать соответствие имен между ролями и пользователями операционной системы.

Набор ролей базы данных, к которым может подключаться данное клиентское соединение, определяется настройкой аутентификации клиента. (Таким образом, клиент не ограничен подключением в качестве роли, соответствующей пользователю операционной системы, так же, как имя пользователя не обязательно должно совпадать с его или ее настоящим именем) Поскольку идентификация роли определяет набор привилегий, доступных подключенному клиенту, важно тщательно настроить права при настройке многопользовательской среды.

Атрибуты ролей

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

Атрибут
Описание

LOGIN (вход в систему)
Только роли с атрибутом LOGIN могут использоваться в качестве начального имени роли для подключения к базе данных. Роль с атрибутом LOGIN может рассматриваться как «пользователь базы данных». Чтобы создать роль с правами входа в систему, используйте: CREATE ROLE name LOGIN; CREATE USER name; CREATE USER эквивалентен CREATE ROLE за исключением того, что CREATE USER по умолчанию включает LOGIN, а CREATE ROLE - нет)

SUPERUSER (статус суперпользователя)
Суперпользователь базы данных обходит все проверки разрешений, кроме права на вход. Это опасная привилегия, и ее не следует использовать небрежно. Лучше всего выполнять большую часть своей работы в роли, которая не является суперпользователем. Чтобы создать нового суперпользователя базы данных, используйте CREATE ROLE name SUPERUSER. Вы должны сделать это под ролью, которая уже является суперпользователем.

CREATEDB (создание базы данных)
Роль должна быть явно предоставлена разрешение на создание баз данных (за исключением суперпользователей, поскольку они обходят все проверки разрешений). Чтобы создать такую роль, используйте CREATE ROLE name CREATEDB.

CREATEROLE (создание ролей)
Роли должно быть явно предоставлено разрешение на создание большего количества ролей (за исключением суперпользователей, так как они обходят все проверки разрешений). Чтобы создать такую роль, используйте CREATE ROLE name CREATEROLE. Роль с привилегией CREATEROLE может изменять и CREATEROLE другие роли, а также предоставлять или отзывать членство в них. Однако для создания, изменения, удаления или изменения принадлежности к роли суперпользователя требуется статус суперпользователя; CREATEROLE недостаточно для этого.

REPLICATION (инициирование репликации)
Роль должна быть явно предоставлена разрешение на инициирование потоковой репликации (за исключением суперпользователей, так как они обходят все проверки разрешений). Роль, используемая для потоковой репликации, также должна иметь разрешение LOGIN. Чтобы создать такую роль, используйте CREATE ROLE name REPLICATION LOGIN.

PASSWORD (пароль)
Пароль имеет значение только в том случае, если метод аутентификации клиента требует от пользователя ввода пароля при подключении к базе данных. password и методы аутентификации md5 используют пароли. Пароли базы данных отделены от паролей операционной системы. Укажите пароль при создании роли с помощью CREATE ROLE name PASSWORD ’ string ’.

Подсказка!!!
Рекомендуется создать роль с привилегиями CREATEDB и CREATEROLE, которая не является суперпользователем, а затем использовать эту роль для всего рутинного управления базами данных и ролями. Такой подход позволяет избежать опасностей работы в качестве суперпользователя для задач, которые на самом деле этого не требуют.

Атрибуты роли могут быть изменены после создания с помощью ALTER ROLE. См. справочные страницы для команд CREATE ROLE и ALTER ROLE для получения подробной информации.

Роль также может иметь специфичные для роли значения «по умолчанию» для многих параметров конфигурации настроек сервера, описанных в главе Конфигурация сервера. Например, если по какой-то причине необходимо выключить сканирование индекса (не очень хорошая идея) при каждом подключении, можно использовать:

ALTER ROLE myname SET enable_indexscan TO off;

Это сохранит настройку (но не установит ее сразу). В последующих соединениях с этой ролью будет выглядеть, как будто SET enable_indexscan TO off был выполнен непосредственно перед началом сеанса. Вы все еще можете изменить этот параметр во время сеанса; это будет только по умолчанию. Чтобы удалить настройку по умолчанию для конкретной роли, используйте ALTER ROLE rolename RESET varname. Обратите внимание, что специфичные для роли значения по умолчанию, прикрепленные к ролям без права LOGIN, довольно бесполезны, поскольку они никогда не будут вызваны.

Ролевая модель управления доступом

Часто удобно группировать пользователей, чтобы упростить управление привилегиями: таким образом, права могут быть предоставлены или отменены для группы в целом. В QHB это достигается созданием роли, представляющей группу, а затем предоставлением членства в роли группы отдельным ролям пользователей.

Чтобы настроить групповую роль, сначала создайте роль:

CREATE ROLE name;

Обычно роль, используемая в качестве группы, не имеет атрибута LOGIN, хотя вы можете установить ее, если хотите.

Когда роль группы существует, вы можете добавлять и удалять участников, используя команды GRANT и REVOKE:

GRANT group_role TO role1, ... ;
REVOKE group_role FROM role1, ... ;

Вы можете предоставить членство и другим групповым ролям (поскольку на самом деле нет различий между групповыми ролями и не групповыми ролями). База данных не позволит вам настроить циклические циклы членства. Кроме того, не разрешено предоставлять членство в роли для PUBLIC.

Члены групповой роли могут использовать права роли двумя способами. Во-первых, каждый член группы может явно выполнить SET ROLE, чтобы временно «стать» групповой ролью. В этом состоянии сеанс базы данных имеет доступ к привилегиям роли группы, а не к исходной роли входа, и любые созданные объекты базы данных считаются принадлежащими роли группы, а не роли входа. Во-вторых, роли участников, имеющие атрибут INHERIT автоматически используют права ролей, членами которых они являются, включая любые права, унаследованные этими ролями. В качестве примера предположим, что мы сделали:

CREATE ROLE joe LOGIN INHERIT;
CREATE ROLE admin NOINHERIT;
CREATE ROLE wheel NOINHERIT;
GRANT admin TO joe;
GRANT wheel TO admin;

Сразу после подключения в качестве роли joe сеанс базы данных будет использовать права, предоставленные непосредственно joe, а также любые права, предоставленные admin, поскольку joe «наследует» права admin. Однако права, предоставленные wheel, ему недоступны, потому что, хотя joe косвенно является членом wheel, членство осуществляется через роль admin которая имеет атрибут NOINHERIT. После:

SET ROLE admin;

сеанс будет использовать только те права, которые предоставлены admin, а не те, которые предоставлены joe. После:

SET ROLE wheel;

сеанс будет использовать только те права, которые предоставлены wheel, а не те, которые предоставлены либо joe либо admin. Исходное состояние привилегий может быть восстановлено любым способом из:

SET ROLE joe;
SET ROLE NONE;
RESET ROLE;

Заметка
Команда SET ROLE всегда позволяет выбрать любую роль, в которую прямо или косвенно входит исходная роль входа. Таким образом, в приведенном выше примере нет необходимости становиться admin, прежде чем стать wheel.

Заметка
В стандарте SQL существует четкое различие между пользователями и ролями, и пользователи не наследуют права автоматически, в то время как роли делают. Такое поведение может быть получено в QHB, если для ролей, используемых в качестве ролей SQL, используется атрибут INHERIT, а для ролей, используемых в качестве пользователей SQL, - атрибут NOINHERIT. В QHB по умолчанию всем ролям предоставляет атрибут INHERIT.

Атрибуты роли LOGIN, SUPERUSER, CREATEDB и CREATEROLE могут рассматриваться как специальные права, но они никогда не наследуются, как обычные права для объектов базы данных. Вы должны на самом деле установить роль для конкретной роли, имеющей один из этих атрибутов, чтобы использовать этот атрибут. Продолжая приведенный выше пример, мы можем предоставить CREATEDB и CREATEROLE роль admin. Тогда сеанс, соединяющийся как роль joe, не будет иметь этих привилегий сразу, а только после выполнения SET ROLE admin.

Чтобы уничтожить групповую роль, используйте DROP ROLE:

DROP ROLE name;

Любое членство в групповой роли автоматически отменяется (но роли участников не затрагиваются иным образом).

Удаление ролей

Поскольку роли могут владеть объектами базы данных и могут иметь права для доступа к другим объектам, удаление роли часто является не просто вопросом быстрого DROP ROLE. Любые объекты, принадлежащие этой роли, должны быть сначала отброшены или переназначены другим владельцам; и любые разрешения, предоставленные этой роли, должны быть аннулированы.

Право собственности на объекты может передаваться по одному с помощью команд ALTER, например:

ALTER TABLE bobs_table OWNER TO alice;

В качестве альтернативы, можно использовать команду REASSIGN OWNED для переназначения владения всеми объектами, принадлежащими роли, которая должна быть удалена, другой роли. Поскольку REASSIGN OWNED не может получить доступ к объектам в других базах данных, необходимо запускать его в каждой базе данных, содержащей объекты, принадлежащие этой роли. (Обратите внимание, что первый такой REASSIGN OWNED изменит владельца любых совместно используемых между базами данных объектов, то есть баз данных или табличных пространств, которые принадлежат роли, подлежащей удалению)

Как только любые ценные объекты были переданы новым владельцам, любые оставшиеся объекты, принадлежащие подлежащей удалению роли, могут быть удалены с помощью команды DROP OWNED. Опять же, эта команда не может получить доступ к объектам в других базах данных, поэтому необходимо запускать ее в каждой базе данных, содержащей объекты, принадлежащие этой роли. Кроме того, DROP OWNED не удаляет целые базы данных или табличные пространства, поэтому это необходимо делать вручную, если роли принадлежат какие-либо базы данных или табличные пространства, которые не были переданы новым владельцам.

DROP OWNED также заботится об удалении любых привилегий, предоставленных целевой роли для объектов, которые ей не принадлежат. Поскольку REASSIGN OWNED не касается таких объектов, как правило, необходимо запустить как REASSIGN OWNED и DROP OWNED (в таком порядке!), чтобы полностью удалить зависимости роли, которую необходимо удалить.

Итого, самый общий рецепт удаления роли, которая использовалась для владения объектами:

REASSIGN OWNED BY doomed_role TO successor_role;
DROP OWNED BY doomed_role;
-- repeat the above commands in each database of the cluster
DROP ROLE doomed_role;

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

Если попытка DROP ROLE выполняется, пока зависимые объекты все еще остаются, она выдаст сообщения, определяющие, какие объекты необходимо переназначить или отбросить.

Роли по умолчанию

QHB предоставляет набор ролей по умолчанию, которые обеспечивают доступ к определенным, часто необходимым, привилегированным возможностям и информации. Администраторы могут предоставлять эти роли пользователям и/или другим ролям в их среде, предоставляя этим пользователям доступ к указанным возможностям и информации.

Роли по умолчанию описаны в таблице 7.1. Обратите внимание, что конкретные разрешения для каждой роли по умолчанию могут измениться в будущем при добавлении дополнительных возможностей. Администраторы должны отслеживать заметки о выпуске на предмет изменений.

Таблица 7.1. Роли по умолчанию

Роль | Разрешенный доступ
pg_read_all_settings | Прочитайте все переменные конфигурации, даже те, которые обычно видны только суперпользователям.
pg_read_all_stats | Прочитайте все представления pg_stat_ * и используйте различные статистические расширения, даже те, которые обычно видны только суперпользователям.
pg_stat_scan_tables | Выполните функции мониторинга, которые могут блокировать ACCESS SHARE для таблиц, возможно, в течение длительного времени.
pg_monitor | Читать / выполнять различные виды мониторинга и функции. Эта роль является членом групп pg_read_all_settings, pg_read_all_stats и pg_stat_scan_tables.
pg_signal_backend | Подайте сигнал другому бэкенду, чтобы отменить запрос или завершить сеанс.
pg_read_server_files | Разрешить чтение файлов из любого места, к которому база данных может получить доступ на сервере с помощью COPY и других функций доступа к файлам.
pg_write_server_files | Разрешить запись в файлы в любом месте, к которому база данных может получить доступ на сервере с помощью COPY и других функций доступа к файлам.
pg_execute_server_program | Разрешить выполнение программ на сервере базы данных как пользователь, база данных запускается так же, как с COPY и другими функциями, которые позволяют выполнять программу на стороне сервера.

pg_monitor, pg_read_all_settings, pg_read_all_stats и pg_stat_scan_tables предназначены для того, чтобы администраторы могли легко настроить роль для мониторинга сервера базы данных. Они предоставляют набор общих привилегий, позволяющих роли читать различные полезные параметры конфигурации, статистику и другую системную информацию, обычно доступную только суперпользователям.

Роль pg_signal_backend предназначена для того, чтобы администраторы могли включать доверенные, но не суперпользовательские роли, для отправки сигналов другим бэкендам. В настоящее время эта роль позволяет отправлять сигналы для отмены запроса на другом сервере или завершения его сеанса. Однако пользователь, которому предоставлена эта роль, не может отправлять сигналы бэкенду, принадлежащему суперпользователю. См. раздел Функции сигнализации сервера.

pg_read_server_files, pg_write_server_files и pg_execute_server_program предназначены для того, чтобы администраторы могли иметь доверенные, но не суперпользовательские роли, которые могут получать доступ к файлам и запускать программы на сервере базы данных в качестве пользователя базы данных. Поскольку эти роли могут получить доступ к любому файлу в файловой системе сервера, они пропускают все проверки разрешений на уровне базы данных при непосредственном доступе к файлам и могут использоваться для получения доступа на уровне суперпользователя, поэтому при предоставлении этих ролей следует соблюдать особую осторожность.

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

Администраторы могут предоставить доступ к этим ролям пользователям с помощью команды GRANT, например:

GRANT pg_signal_backend TO admin_user;

Функция безопасности

Функции, триггеры и политики безопасности на уровне строк позволяют пользователям вставлять код на внутренний сервер, который другие пользователи могут выполнять непреднамеренно. Следовательно, эти механизмы позволяют создавать «троянских коней» относительно легко. Самая сильная защита - жесткий контроль над тем, кто может определять объекты. Там, где это невозможно, пишите запросы, относящиеся только к объектам, имеющим доверенных владельцев. Удалите из search_path общедоступную схему и любые другие схемы, которые позволяют не доверенным пользователям создавать объекты.

Функции выполняются внутри процесса внутреннего сервера с разрешениями операционной системы демона сервера баз данных. Если язык программирования, используемый для функции, допускает неконтролируемый доступ к памяти, можно изменить внутренние структуры данных сервера. Следовательно, среди прочего, такие функции могут обойти любые системы контроля доступа. Языки функций, которые разрешают такой доступ, считаются «ненадежными», а QHB позволяет только суперпользователям создавать функции, написанные на этих языках.

Управление базами данных

Каждый экземпляр работающего сервера QHB управляет одной или несколькими базами данных. Таким образом, базы данных являются высшим иерархическим уровнем для организации объектов SQL («объектов базы данных»). В этой главе описываются свойства баз данных, а также способы их создания, управления и уничтожения.

Обзор

База данных — это именованная коллекция объектов SQL («объекты базы данных»). Как правило, каждый объект базы данных (таблицы, функции и т. д.) принадлежит одной и только одной базе данных. (Однако есть несколько системных каталогов, например, pg_database, которые принадлежат всему экземпляру и доступны из каждой базы данных внутри экземпляра). Точнее, база данных представляет собой набор схем, а схемы содержат таблицы, функции и т. д. Таким образом, полная иерархия: сервер, база данных, схема, таблица (или какой-либо другой вид объекта, например функция).

При подключении к серверу базы данных клиент должен указать в своем запросе на подключение имя базы данных, к которой он хочет подключиться. Невозможно получить доступ к более чем одной базе данных для одного соединения. Тем не менее, приложение не ограничено по количеству подключений, которые оно открывает к той же или другим базам данных. Базы данных физически разделены, а управление доступом осуществляется на уровне соединения. Если один экземпляр сервера QHB предназначен для размещения проектов или пользователей, которые должны быть отдельными и по большей части не осведомленными друг о друге, поэтому рекомендуется поместить их в отдельные базы данных. Если проекты или пользователи взаимосвязаны и должны иметь возможность использовать ресурсы друг друга, они должны быть помещены в одну и ту же базу данных, но, возможно, в отдельные схемы. Схемы — это чисто логическая структура, и кто может получить доступ к тому, что управляется системой привилегий. Более подробная информация об управлении схемами находится в разделе Схемы.

Базы данных создаются с помощью команды CREATE DATABASE (см. раздел Создание базы данных) и уничтожаются с помощью команды DROP DATABASE (см. раздел Удаление базы данных). Чтобы определить набор существующих баз данных, изучите системный каталог pg_database, например

SELECT datname FROM pg_database;

Команда \l программы psql также полезна для вывода списка существующих баз данных.

Создание базы данных

Чтобы создать базу данных, сервер QHB должен быть запущен (см. раздел Запуск сервера базы данных).

Базы данных создаются с помощью команды SQL CREATE DATABASE:

CREATE DATABASE name;

где name следует обычным правилам для идентификаторов SQL. Текущая роль автоматически становится владельцем новой базы данных. Владелец базы данных имеет право удалить ее позже (что также удаляет все объекты в ней, даже если у них другой владелец).

Создание баз данных - ограниченная операция. См. раздел Атрибуты ролей для получения разрешения.

Поскольку вам необходимо подключиться к серверу базы данных, чтобы выполнить команду CREATE DATABASE, остается вопрос, как можно создать первую базу данных на любом указанном месте. Первая база данных всегда создается командой initdb при инициализации области хранения данных. (См. раздел Создание кластера базы данных). Эта база данных называется qhb. Таким образом, чтобы создать первую «обычную» базу данных, вы можете подключиться к qhb.

Вторая база данных, template1, также создается во время инициализации экземпляра базы данных. Всякий раз, когда в экземпляре создается новая база данных, template1 по существу клонируется. Это означает, что любые изменения, которые вы делаете в template1, распространяются на все впоследствии созданные базы данных. Из-за этого избегайте создания объектов в template1 если вы не хотите, чтобы они распространялись на каждую вновь созданную базу данных. Более подробная информация представлена в разделе Шаблоны баз данных.

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

createdb dbname

createdb подключается к базе данных qhb и выдает команду CREATE DATABASE, как описано выше. Справочная страница createdb содержит детали вызова. Обратите внимание, что createdb без аргументов создаст базу данных с текущим именем пользователя.

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

CREATE DATABASE dbname OWNER rolename;

из среды SQL или:

createdb -O rolename dbname

из оболочки ОС. Только суперпользователь может создавать базу данных для кого-то другого (то есть для роли, в которой вы не участвуете).

Шаблоны баз данных

CREATE DATABASE фактически работает путем копирования существующей базы данных. По умолчанию он копирует стандартную системную базу данных с именем template1. Таким образом, эта база данных является «шаблоном», из которого создаются новые базы данных. Если вы добавите объекты в template1, эти объекты будут скопированы в впоследствии созданные пользовательские базы данных. Такое поведение допускает локальные модификации стандартного набора объектов в базах данных. Например, если вы установите процедурный язык PL/Perl в template1, он автоматически будет доступен в пользовательских базах данных без каких-либо дополнительных действий при создании этих баз данных.

Существует вторая стандартная системная база данных с именем template0. Эта база данных содержит те же данные, что и исходное содержимое template1, то есть только стандартные объекты, предопределенные вашей версией QHB. template0 никогда не должен изменяться после инициализации экземпляра базы данных. CREATE DATABASE скопировав template0 вместо template1, вы можете создать «первичную» пользовательскую базу данных, которая не содержит никаких локальных дополнений из template1. Это особенно удобно при восстановлении дампа qhb_dump: сценарий дампа должен быть восстановлен в первичной базе данных, чтобы гарантировать, что каждый воссоздает правильное содержимое базы данных дампа, не конфликтуя с объектами, которые могли быть добавлены в template1 позже.

Другая распространенная причина копирования template0 вместо template1 заключается в том, что при копировании template0 можно указать новые настройки кодирования и локали, тогда как для копии template1 должны использоваться те же настройки, что и для нее. Это связано с тем, что template1 может содержать данные, специфичной кодировки или локали, а template0 как известно, нет.

Чтобы создать базу данных путем копирования template0, используйте:

CREATE DATABASE dbname TEMPLATE template0;

из среды SQL или:

createdb -T template0 dbname

из оболочки ОС

Можно создать дополнительные базы данных шаблонов, и в действительности можно скопировать любую базу данных в экземпляре, указав ее имя в качестве шаблона для CREATE DATABASE. Тем не менее, важно понимать, что это не предназначено для универсального средства «COPY DATABASE». Основным ограничением является то, что никакие другие сеансы не могут быть подключены к исходной базе данных, пока она копируется. Создание базы данных завершится ошибкой, если при ее запуске возникнет какое-либо другое соединение; во время операции копирования новые соединения с исходной базой данных будут запрещены.

В pg_database есть два полезных флага для каждой базы данных: столбцы datistemplate и datallowconn. datistemplate может быть установлен, чтобы указать, что база данных предназначена в качестве шаблона для CREATE DATABASE. Если этот флаг установлен, база данных может быть клонирована любым пользователем с привилегиями CREATEDB; если он не установлен, клонировать могут только суперпользователи и владелец базы данных. Если datallowconn имеет значение false, то новые подключения к этой базе данных не будут разрешены (но существующие сеансы не прекращаются просто путем установки флага false). База данных template0 обычно помечается как datallowconn = false чтобы предотвратить ее модификацию. И template0 и template1 всегда должны быть помечены datistemplate = true.

Заметка
template1 и template0 не имеют особого статуса, кроме того факта, что имя template1 является именем исходной базы данных по умолчанию для CREATE DATABASE. Например, можно удалить template1 и воссоздать его из template0 без каких-либо вредных последствий. Этот путь действий может быть целесообразным, если кто-то небрежно добавил кучу мусора в template1. (Чтобы удалить template1, он должен иметь pg_database.datistemplate = false).

База данных qhb также создается при инициализации экземпляра базы данных. Эта база данных является базой данных по умолчанию для пользователей и приложений для подключения. Это просто копия template1 и может быть удалена и воссоздана при необходимости.

Конфигурация базы данных

Сервер QHB предоставляет большое количество переменных конфигурации во время выполнения (см. главу Конфигурация сервера). Для многих из этих настроек вы можете установить значения по умолчанию для базы данных.

Например, если по какой-то причине вы хотите выключить оптимизатор GEQO для данной базы данных, вам обычно нужно либо выключить его для всех баз данных, либо убедиться, что каждый подключающийся клиент осторожно выдает SET geqo TO off. Чтобы установить этот параметр по умолчанию в конкретной базе данных, вы можете выполнить команду:

ALTER DATABASE mydb SET geqo TO off;

Это сохранит настройку (но не установит ее сразу). При последующих подключениях к этой базе данных это будет выглядеть так, как будто SET geqo TO off; был выполнен незадолго до начала сеанса. Обратите внимание, что пользователи все еще могут изменять этот параметр во время своих сеансов; это будет только значение по умолчанию. Чтобы отменить любую такую настройку, используйте ALTER DATABASE dbname RESET varname.

Удаление базы данных

Базы данных удаляются командой DROP DATABASE:

DROP DATABASE name;

Только владелец базы данных или суперпользователь может удалить базу данных. Удаление базы данных удаляет все объекты, которые содержались в базе данных. Уничтожение базы данных не может быть отменено.

Вы не можете выполнить команду DROP DATABASE когда подключены к удаляемой базе данных. Однако вы можете подключиться к любой другой базе данных, включая базу данных template1. template1 будет единственным вариантом для удаления последней пользовательской базы данных данного экземпляра.

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

dropdb dbname

(В отличие от createdb, удаление базы данных с текущим именем пользователя не является действием по умолчанию).

Табличные пространства

Табличные пространства в QHB позволяют администраторам баз данных определять места в файловой системе, где могут храниться файлы, представляющие объекты базы данных. После создания, на табличное пространство можно ссылаться по имени при создании объектов базы данных.

Используя табличные пространства, администратор может управлять разметкой диска установки QHB. Это полезно по крайней мере в двух случаях. Во-первых, если раздел или том, на котором был инициализирован экземпляр, исчерпал пространство и не может быть расширен, табличное пространство можно создать в другом разделе и использовать до тех пор, пока система не будет перенастроена.

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

Предупреждение!!!
Несмотря на то, что табличные пространства расположены вне основного каталога данных QHB, они являются неотъемлемой частью экземпляра базы данных и не могут рассматриваться как автономный набор файлов данных. Они зависят от метаданных, содержащихся в главном каталоге данных, и поэтому не могут быть присоединены к другому экземпляру базы данных или сохранены отдельно. Аналогичным образом, если вы потеряете табличное пространство (удаление файла, сбой диска и т. д.), экземпляр базы данных может стать нечитаемым или не сможет запуститься. Размещение табличного пространства во временной файловой системе, такой как RAM-диск, ставит под угрозу надежность всего экземпляра.

Чтобы определить табличное пространство, используйте команду CREATE TABLESPACE, например:

CREATE TABLESPACE fastspace LOCATION '/ssd1/qhb/data';

Расположение должно быть существующим пустым каталогом, который принадлежит пользователю операционной системы QHB. Все объекты, впоследствии созданные в табличном пространстве, будут храниться в файлах под этим каталогом. Расположение не должно быть в съемном или временном хранилище, поскольку экземпляр может не функционировать, если табличное пространство отсутствует или потеряно.

Заметка
Обычно нет смысла создавать более одного табличного пространства на логическую файловую систему, поскольку вы не можете контролировать расположение отдельных файлов в логической файловой системе. Однако QHB не налагает никаких подобных ограничений, и на самом деле он не знает напрямую о границах файловой системы в вашей ОС. Он просто хранит файлы в каталогах, которые вы указали для использования.

Само создание табличного пространства должно выполняться под суперпользователем базы данных, но после этого вы можете разрешить его использовать обычным пользователям базы данных. Для этого предоставьте им привилегию CREATE.

Таблицы, индексы и целые базы данных могут быть назначены конкретным табличным пространствам. Для этого пользователь с привилегией CREATE в заданном табличном пространстве должен передать имя табличного пространства в качестве параметра соответствующей команде. Например, следующая команда создает таблицу в табличном пространстве space1:

CREATE TABLE foo(i int) TABLESPACE space1;

В качестве альтернативы используйте параметр default_tablespace:

SET default_tablespace = space1;
CREATE TABLE foo(i int);

Если для default_tablespace задано значение, отличное от пустой строки, оно предоставляет неявное предложение TABLESPACE для команд CREATE TABLE и CREATE INDEX которые не имеют явной команды.

Существует также параметр temp_tablespaces, который определяет размещение временных таблиц и индексов, а также временных файлов, которые используются для таких целей, как сортировка больших наборов данных. Это может быть список имен табличных пространств, а не только одно имя, так что нагрузка, связанная с временными объектами, может быть распределена по нескольким табличным пространствам. Случайный член списка выбирается каждый раз, когда создается временный объект.

Табличное пространство, связанное с базой данных, используется для хранения системных каталогов этой базы данных. Кроме того, это табличное пространство по умолчанию, используемое для таблиц, индексов и временных файлов, созданных в базе данных, если не задано предложение TABLESPACE и никакие другие параметры не определены в default_tablespace или temp_tablespaces (в зависимости от ситуации). Если база данных создается без указания для нее табличного пространства, она использует то же самое табличное пространство, что и база данных шаблонов, из которой она копируется.

Два табличных пространства создаются автоматически при инициализации экземпляра базы данных. Пространство pg_global используется для общих системных каталогов. Пространство pg_default является табличным пространством по умолчанию для баз данных template1 и template0 (и, следовательно, будет табличным пространством по умолчанию и для других баз данных, если оно не переопределено предложением TABLESPACE в CREATE DATABASE).

После создания табличное пространство можно использовать из любой базы данных, при условии, что запрашивающий пользователь имеет достаточные привилегии. Это означает, что табличное пространство нельзя отбросить, пока не будут удалены все объекты во всех базах данных, использующих табличное пространство.

Чтобы удалить пустое табличное пространство, используйте команду DROP TABLESPACE.

Чтобы определить набор существующих табличных пространств, изучите системный каталог pg_tablespace, например

SELECT spcname FROM pg_tablespace;

Метакоманда \db программы psql также полезна для перечисления существующих табличных пространств.

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

Каталог $PGDATA/pg_tblspc содержит символические ссылки, которые указывают на каждое из встроенных табличных пространств, определенных в экземпляре. Хотя это и не рекомендуется, но можно вручную настроить макет табличного пространства, переопределив эти ссылки. Ни при каких обстоятельствах не выполняйте эту операцию во время работы сервера.

Локализация

В этой главе описываются доступные функции локализации с точки зрения администратора. QHB поддерживает два средства локализации:

Использование средств локализации операционной системы для обеспечения определяемых локалью порядка сортировки, форматирования чисел, перевода сообщений и других аспектов. Это описано в разделах Поддержка языковых стандартов и Поддержка правил сортировки.

Предоставление ряда различных кодировок для поддержки хранения текста на разных языках и обеспечения перевода кодировок между клиентом и сервером. Это описано в разделе Поддержка кодировок.

Поддержка языковых стандартов

Поддержка языковых стандартов относится к приложению, учитывающему культурные предпочтения в отношении алфавитов, сортировки, форматирования чисел и т. д. QHB использует стандартные средства локализации ISO C и POSIX, предоставляемые операционной системой сервера. Для получения дополнительной информации обратитесь к документации вашей системы.

Обзор

Поддержка языковых стандартов автоматически инициализируется при создании экземпляра базы данных с использованием initdb. По умолчанию initdb инициализирует экземпляр базы данных с настройкой локализации среды выполнения, поэтому, если ваша система уже настроена на использование языкового стандарта, который вы хотите использовать в своем экземпляре базы данных, вам больше ничего не нужно делать. Если вы хотите использовать другую локализацию (или вы не уверены, для какой локализации настроена ваша система), вы можете указать initdb, какую именно локализацию использовать, указав параметр --locale. Например:

initdb --locale=ru_RU

Этот пример для систем Unix устанавливает язык русского языка (ru), который используют в России (RU). Другие возможности могут включать en_US (американский английский) и fr_CA (французский канадский). Если для локализации можно использовать более одного набора символов, тогда спецификации могут принимать форму language_territory.codeset. Например, ru_RU.utf8 представляет русский язык (ru), на котором говорят в России (RU), с кодировкой набора символов UTF-8.

Какие локализации доступны в вашей системе, под какими именами зависит от того, что было предоставлено поставщиком операционной системы и что было установлено. В большинстве систем Unix команда locale -a предоставит список доступных локализаций. Windows использует более подробные названия German_Germany, например German_Germany или Swedish_Sweden.1252, но принципы те же.

Иногда полезно смешивать правила из нескольких языков, например, использовать правила сортировки на английском языке, но сообщения на испанском языке. Для поддержки этого существует набор подкатегорий языковых стандартов, которые контролируют только определённые аспекты правил локализации:

Категория |
LC_COLLATE | Порядок сортировки строк
LC_CTYPE | Классификация символов (Что это за буква? Каков её эквивалент в верхнем регистре?)
LC_MESSAGES | Язык сообщений
LC_MONETARY | Форматирование денежных сумм
LC_NUMERIC | Форматирование чисел
LC_TIME | Форматирование даты и времени

Имена категорий переводятся в имена параметров initdb, чтобы переопределить выбор локализации для конкретной категории. Например, чтобы установить языковой стандарт французско-канадский, но использовать правила США для форматирования валюты, используйте initdb --locale=fr_CA --lc-monetary=en_US.

Если вы хотите, чтобы система работала без поддержки локализаций, используйте специальное имя локализации C её эквивалент POSIX.

Некоторые категории локализаций должны иметь фиксированные значения при создании базы данных. Вы можете использовать разные настройки для разных баз данных, но как только база данных будет создана, вы больше не сможете их изменить. LC_COLLATE и LC_CTYPE — эти категории. Они влияют на порядок сортировки индексов, поэтому они должны оставаться фиксированными, иначе индексы в текстовых столбцах будут повреждены. (Но вы можете ослабить это ограничение, используя параметры сортировки, как описано в разделе Поддержка правил сортировки). Значения по умолчанию для этих категорий определяются при запуске initdb, и эти значения используются при создании новых баз данных, если не указано иное в команде CREATE DATABASE.

Другие категории языковых стандартов могут быть изменены в любое время путем установки параметров конфигурации сервера, которые имеют то же имя, что и категории локализаций (подробности см. в разделе Языковые стандарты и форматирование). Значения, выбранные initdb, на самом деле записываются только в файл конфигурации qhb.conf, чтобы служить значениями по умолчанию при запуске сервера. Если вы удалите эти назначения из qhb.conf, то сервер унаследует настройки из среды, в которой он выполняется.

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

Примечание
Наследование языкового стандарта от среды выполнения, означает следующее в большинстве операционных систем: для данной локализации, скажем, сортировки, следующие переменные среды анализируются в приведённом ниже порядке, пока одна из них не окажется заданной LC_ALL, LC_COLLATE (или переменная, соответствующая соответствующей категории), LANG. Если ни одна из этих переменных среды не установлена, то по умолчанию в качестве локализации используется значение C.
Некоторые библиотеки локализации сообщений также обращают внимание на переменную среды LANGUAGE которая переопределяет все остальные настройки локализации с целью установки языка сообщений. Если вы сомневаетесь, пожалуйста, обратитесь к документации вашей операционной системы, в частности к документации по gettext.

Чтобы разрешить перевод сообщений на предпочитаемый пользователем язык, во время сборки должен быть выбран NLS (configure --enable-nls). Все остальные языковые поддержки встроены автоматически.

Поведение

Настройки локализации влияют на следующие SQL-функции:

Порядок сортировки в запросах с использованием ORDER BY или стандартных операторов сравнения текстовых данных;

upper, lower и initcap функции;

операторы сортировки с образцом (регулярные выражения в стиле LIKE, SIMILAR TO и POSIX); локализации влияют на поиск без учёта регистра и на классификацию символов по регулярным выражениям;

семейство функций to_char;

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

Недостатком использования в QHB локализаций, отличных от C или POSIX является его влияние на производительность. Это замедляет обработку символов и предотвращает использование LIKE обычных индексов. По этой причине используйте локализации только в том случае, если они вам действительно нужны.

В качестве обходного пути, позволяющего QHB использовать индексы с предложениями LIKE в локализации, отличной от C, существует несколько пользовательских классов операторов. Это позволяет создать индекс, который выполняет строгое посимвольное сравнение, игнорируя правила сравнения локализаций. Обратитесь к разделу Классы операторов и семейства операторов за дополнительной информацией. Другой подход заключается в создании индексов с использованием сортировки C, как обсуждалось в разделе Поддержка правил сортировки.

Проблемы

Если поддержка языковых стандартов не работает в соответствии с приведённым выше объяснением, проверьте, правильно ли настроена поддержка языковых стандартов в вашей операционной системе. Чтобы проверить, какие языковые стандарты установлены в вашей системе, вы можете использовать команду locale -a если ваша операционная система предоставляет её.

Убедитесь, что QHB действительно использует локализацию, которая вам нужна. Параметры LC_COLLATE и LC_CTYPE определяются при создании базы данных и не могут быть изменены, кроме как путем создания новой базы данных. Другие настройки локализации, включая LC_MESSAGES и LC_MONETARY, первоначально определяются средой, в которой запущен сервер, но могут быть изменены на лету. Вы можете проверить параметры активной локализации, используя команду SHOW.

Поддержка правил сортировки

Функция правил сортировки позволяет указать порядок сортировки и классификацию символов для каждого столбца или даже для каждой операции. Это LC_COLLATE ограничение на то, что LC_COLLATE и LC_CTYPE базы данных не могут быть изменены после её создания.

Концепции

Концептуально, каждое выражение данных с возможностью сортировки имеет параметры сортировки. (Встроенные типы данных - text, varchar и char. Определяемые пользователем базовые типы также могут быть помечены как совместимые, и, конечно же, область данных для сопоставляемых типов данных может быть отсортирована.) Если выражение является ссылкой на столбец, сортировка выражения является определённой сортировкой столбца. Если выражение является константой, сопоставление является сопоставлением по умолчанию для типа данных константы. Сопоставление более сложного выражения получается из сопоставлений его входных данных, как описано ниже.

Параметры сортировки выражения могут быть параметрами сортировки «по умолчанию», что означает настройки локализации, определенные для базы данных. Кроме того, сопоставление выражения может быть неопределённым. В таких случаях операции сортировки и другие операции, которые должны знать параметры сортировки, завершатся неудачно.

Когда система базы данных должна выполнить упорядочивание или классификацию символов, она использует сопоставление входного выражения. Это происходит, например, с предложениями ORDER BY и вызовами функций или операторов, такими как <. Правила сортировки, применяемые для предложения ORDER BY, - это просто параметры сортировки сортированного ключа. Параметры сортировки, применяемые для вызова функции или оператора, получаются из аргументов, как описано ниже. В дополнение к операторам сравнения, параметры сортировки учитываются функциями, которые преобразуют буквы в нижний и верхний регистр, такие как lower, upper и initcap, с помощью операторов сортировки с образцом, to_char и связанных функций.

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

Сортировка выражения может быть явной или неявной. Это различие влияет на то, как объединяются параметры сортировки, когда в выражении появляется несколько различных параметров сортировки. Явное определение правил сортировки происходит, когда используется COLLATE. Все остальные сортировки являются неявными. Когда необходимо объединить несколько параметров сортировки, например, при вызове функции, используются следующие правила:

Если какое-либо входное выражение задано с явным сопоставлением, то все явно определённые сортировки среди входных выражений должны быть одинаковыми, в противном случае возникает ошибка. Если присутствует какое-либо явно полученное сопоставление, оно является результатом сочетания сопоставлений.

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

Если среди входных выражений существуют конфликтующие неявные параметры сортировки, то считается, что комбинация имеет неопределённый порядок сортировки. Это не ошибочное условие, если конкретная вызываемая функция не требует знания параметров сортировки, которые она должна применять. Если это произойдет, ошибка будет возникать во время выполнения.

Например, рассмотрим это определение таблицы:

CREATE TABLE test1 (
a text COLLATE "de_DE",
b text COLLATE "es_ES",
...
);

Затем в

SELECT a < 'foo' FROM test1;

< сравнение выполняется в соответствии с правилами de_DE, потому что выражение объединяет неявное сопоставление с сопоставлением по умолчанию. Но в

SELECT a < ('foo' COLLATE "fr_FR") FROM test1;

сравнение выполняется с использованием правил fr_FR, потому что явный вывод правила сортировки переопределяет неявный. Кроме того, в следующем примере

SELECT a < b FROM test1;

синтаксический анализатор не может определить, какое сопоставление применить, поскольку столбцы a и b имеют конфликтующие неявные сортировки. Поскольку оператору < необходимо знать, какое сопоставление использовать, это приведет к ошибке. Ошибка может быть устранена путём добавления явного спецификатора правила сортировки к любому входному запросу, таким образом:

SELECT a < b COLLATE "de_DE" FROM test1;

или эквивалентно

SELECT a COLLATE "de_DE" < b FROM test1;

С другой стороны, структурно похожий случай

SELECT a || b FROM test1;

не приводит к ошибке, потому что оператор || не заботится о правилах сортировок: его результат одинаков независимо от параметров сортировки.

Сортировка, назначенная объединённым входным выражениям функции или оператора, также считается применимой к результату функции или оператора, если функция или оператор выдает результат типа данных с возможностью переноса. Итак, запрос

SELECT * FROM test1 ORDER BY a || 'foo';

будет выполнен в соответствии с правилами de_DE . Но запрос:

SELECT * FROM test1 ORDER BY a || b;

приводит к ошибке, потому что даже если оператору || не нужно знать параметры сортировки, то ORDER BY это важно. Как и прежде, конфликт может быть разрешён с помощью явного спецификатора сортировки:

SELECT * FROM test1 ORDER BY a || b COLLATE "fr_FR";

Управление правилами сортировки

Правила сортировки - это объект схемы SQL, который соотносит SQL-имя на локализации, предоставляемые библиотеками, установленными в операционной системе. У определения параметров сортировки есть поставщик, который указывает, какая библиотека предоставляет данные локализации. Одно имя стандартного провайдера - libc, в котором используются локализации, предоставляемые библиотекой C операционной системы. Это локализации, которые используют большинство инструментов, предоставляемых операционной системой. Другой провайдер - icu, который использует внешнюю ICU библиотеку. Локализации ICU можно использовать только в том случае, если при создании QHB была настроена поддержка ICU.

Правило сортировки, предоставляемое libc сопоставляется с комбинацией LC_COLLATE и LC_CTYPE, как принято вызовом системной библиотеки setlocale() . (Как следует из названия, основная цель правила сортировки состоит в том, чтобы установить LC_COLLATE, который управляет порядком сортировки. Но на практике редко требуется иметь параметр LC_CTYPE, отличный от LC_COLLATE, поэтому его удобнее собирать согласно одной концепции, чем создание другой инфраструктуры для установки LC_CTYPE каждого выражения.) Кроме того, сопоставление libc связано с кодировкой набора символов (см. раздел Поддержка кодировок). Одно и то же имя правила сортировки может существовать для разных кодировок.

Объект сортировки, предоставляемый icu отображается на именованный сборщик, предоставляемый библиотекой ICU. ICU не поддерживает отдельные параметры «collate» и «ctype», поэтому они всегда одинаковы. Кроме того, параметры сортировки ICU не зависят от кодировки, поэтому в базе данных всегда есть только один параметр сортировки ICU для данного имени.

Стандартные правила сортировки

На всех платформах доступны параметры сортировки с именами default, C и POSIX. Дополнительные параметры сортировки могут быть доступны в зависимости от поддержки операционной системы. Параметры сортировки по default выбирают значения LC_COLLATE и LC_CTYPE указанные во время создания базы данных. Оба правила C и POSIX задают «традиционное поведение C», при котором только буквы ASCII от «A» до «Z» обрабатываются как буквы, а сортировка выполняется строго по значениям байтов кода символа.

Кроме того, стандартное имя сортировки SQL ucs_basic доступно для кодировки UTF8. Это эквивалентно C и сортирует по кодовой точке Unicode.

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

Если операционная система обеспечивает поддержку использования нескольких языковых стандартов в рамках одной программы (newlocale и связанных функций) или если настроена поддержка ICU, то при инициализации кластера базы данных initdb заполняет системный каталог pg_collation правилами на основе всех языковых стандартов, которые он находит в операционной системе во время инициализации.

Чтобы проверить доступные в настоящее время локализации, используйте запрос SELECT * FROM pg_collation или команду \dOS+ в psql.

Правила сортировки libc

Например, операционная система может предоставить локализацию с именем de_DE.utf8. Тогда initdb создаст сопоставление с именем de_DE.utf8 для кодировки UTF8, для которого LC_COLLATE и LC_CTYPE установлены в значение de_DE.utf8. Это также создаст сопоставление с тегом .utf8 из имени. Таким образом, вы также можете использовать параметры сортировки под именем de_DE, что не так громоздко для написания и делает имя менее зависимым от кодировки. Обратите внимание, что, тем не менее, начальный набор имен параметров сортировки зависит от платформы.

Набор параметров сортировки по умолчанию, предоставляемый libc сопоставляется непосредственно с локализациями, установленными в операционной системе, которые можно узнать с помощью команды locale -a . В случае, если требуется сопоставление libc, имеющее разные значения для LC_COLLATE и LC_CTYPE, или если в операционной системе после инициализации системы баз данных установлены новые локализации, то можно создать новое сопоставление с помощью команды CREATE COLLATION. Новые локализации операционной системы также можно массово импортировать с помощью функции pg_import_system_collations().

В любой конкретной базе данных представляют интерес только правила сортировки, использующие кодировку этой базы данных. Другие записи в pg_collation игнорируются. Таким образом, de_DE имя правила, такое как de_DE может считаться уникальным в данной базе данных, даже если оно не будет уникальным в глобальном масштабе. Рекомендуется использовать сокращенные имена параметров сортировки, так как вам придётся делать на одну вещь меньше, если вы решите перейти на другую кодировку базы данных. Однако обратите внимание, что правила default, C и POSIX могут использоваться независимо от кодировки базы данных.

QHB считает, что отдельные объекты сортировки несовместимы, даже если они имеют идентичные свойства. Так, например,

SELECT a COLLATE "C" < b COLLATE "POSIX" FROM test1;

выдаст ошибку, даже если параметры сортировки C и POSIX имеют идентичное поведение. Поэтому не рекомендуется смешивать имена сортировок с разделителями и без них.

Правила сортировки ICU

С ICU не имеет смысла перечислять все возможные названия локализаций. ICU использует определённую систему именования для локализаций, но существует гораздо больше названий локализаций, чем на самом деле разных локализаций. initdb использует API ICU для извлечения набора различных локализаций для заполнения начального набора параметров сортировки. Правила сортировки, предоставляемые ICU, создаются в среде SQL с именами в формате языкового тега BCP 47, с добавлением расширения «для частного использования» -x-icu, чтобы отличать их от языковых стандартов libc.

Вот несколько примеров правил сортировки, которые могут быть созданы:

de-x-icu

Немецкое правило сортировки, вариант по умолчанию

de-AT-x-icu

Немецкое правило сортировки для Австрии, вариант по умолчанию

(Есть также, de-DE-x-icu или de-CH-x-icu, но на момент написания статьи они эквивалентны de-x-icu).

und-x-icu (for “undefined”)

«Корневое» правило сортировки ICU. Используйте его, чтобы получить разумный порядок сортировки, не зависящий от языка.

Некоторые (менее часто используемые) кодировки не поддерживаются ICU. Когда кодировка базы данных является одной из них, записи сортировки ICU в pg_collation игнорируются. Попытка его использовать приведет к появлению ошибки в строке: «сопоставление «de-x-icu» для кодировки «WIN874» не существует».

Создание новых правил сортировки

Если стандартных и предопределённых параметров сортировки недостаточно, пользователи могут создавать свои собственные правила сортировки с помощью команды SQL CREATE COLLATION.

Стандартные и предопределённые параметры сортировки находятся в схеме pg_catalog, как и все предопределённые объекты. Пользовательские параметры сортировки должны создаваться в пользовательских схемах. Это также гарантирует, что они сохраняются pg_dump.

Правила сортировки libc

Новые правила сортировки libc могут быть созданы следующим образом:

CREATE COLLATION german (provider = libc, locale = 'de_DE');

Точные значения, приемлемые для предложения locale в этой команде, зависят от операционной системы. В Unix-подобных системах команда locale -a покажет список.

Поскольку предопределенные параметры сортировки libc уже включают в себя все параметры сортировки, определенные в операционной системе при инициализации экземпляра базы данных, нет необходимости создавать новые вручную. Причины могут быть в том случае, если желательна другая система именования (в этом случае см. раздел Копирование правил сортировки ) или если операционная система была обновлена для предоставления новых определений локализации (в этом случае см. Также pg_import_system_collations()).

Правила сортировки ICU

ICU позволяет настраивать параметры сортировки вне базового набора языка+страны, который предварительно загружен initdb. Пользователям рекомендуется определять свои собственные правила сортировки, которые используют базовые средства для соответствия поведению сортировки их требованиям. См. http://userguide.icu-project.org/locale и http://userguide.icu-project.org/collation/api для получения информации о наименовании локализации ICU. Набор допустимых имён и атрибутов зависит от конкретной версии ICU.

Вот некоторые примеры:

CREATE COLLATION "de-u-co-phonebk-x-icu" (provider = icu, locale = 'de-u-co-phonebk');
CREATE COLLATION "de-u-co-phonebk-x-icu" (provider = icu, locale = 'de@collation=phonebook');

Немецкая сортировка с типом сортировки телефонной книги

В первом примере выбирается языковой стандарт ICU с использованием
«языкового тега» формата BCP 47. Во втором примере используется традиционный синтаксис локализации, специфичный для ICU. Первый стиль предпочтительнее в дальнейшем, но он не поддерживается старыми версиями ICU.

Обратите внимание, что вы можете называть правила сортировки в среде SQL как угодно. В этом примере мы следуем стилю именования, который используют предопределенные параметры сортировки, которые, в свою очередь, также следуют BCP 47, но это не требуется для пользовательских параметров сортировки.

CREATE COLLATION "und-u-co-emoji-x-icu" (provider = icu, locale = 'und-u-co-emoji');
CREATE COLLATION "und-u-co-emoji-x-icu" (provider = icu, locale = '@collation=emoji');

Корневое правило сортировки с типом сортировки Emoji, в соответствии с Техническим стандартом Unicode #51

Заметка В традиционной системе именования локализаций ICU корневая локализация выбирается пустой строкой.

CREATE COLLATION digitslast (provider = icu, locale =
'en-u-kr-latn-digit');

CREATE COLLATION digitslast (provider = icu, locale =
'en@colReorder=latn-digit');

Сортировка цифр после латинских букв. (По умолчанию цифры перед буквами.)

CREATE COLLATION upperfirst (provider = icu, locale =
'en-u-kf-upper');

CREATE COLLATION upperfirst (provider = icu, locale =
'en@colCaseFirst=upper');

Сортировка заглавных буквы перед строчными. (По умолчанию сначала используются строчные буквы.)

CREATE COLLATION special (provider = icu, locale =
'en-u-kf-upper-kr-latn-digit');

CREATE COLLATION special (provider = icu, locale =
'en@colCaseFirst=upper;colReorder=latn-digit');

Сочетает в себе оба вышеуказанных варианта.

CREATE COLLATION numeric (provider = icu, locale = 'en-u-kn-true');

CREATE COLLATION numeric (provider = icu, locale =
'en@colNumeric=yes');

Числовой порядок сортирует последовательности цифр по их числовому значению, например: A-21 < A-123 (также известный как «естественная сортировка»).

Подробности смотрите в Техническом стандарте Unicode #35 и BCP 47. Список возможных типов сортировки (вложенный тег co) можно найти в репозитории CLDR. ICU Locale Explorer можно использовать для проверки подробностей конкретного определения локализации. Для примеров, использующих вложенные теги k* требуется ICU как минимум версии 54.

Обратите внимание, что хотя эта система позволяет создавать параметры сортировки, которые «игнорируют регистр» или «игнорируют ударения» или аналогичные (с использованием ключа ks), для того, чтобы такие параметры сортировки действовали по-настоящему без учета регистра или акцента, они также должны быть объявлены недетерминированными в CREATE COLLATION; см. раздел Недетерминированные правила сортировки. В противном случае любые строки, которые являются равными в соответствии с правилом сортировки, но не являются побайтово равными, будут отсортированы в соответствии со своими байтовыми значениями.

Заметка
По своей конструкции ICU будет принимать практически любую строку в качестве имени локализации и сопоставлять её с ближайшей локализацией, которую он может предоставить, используя процедуру, описанную в его документации. Таким образом, прямой обратной связи не будет, если спецификация правила сортировки составлена с использованием функций, которые данная установка ICU фактически не поддерживает. Поэтому рекомендуется создавать контрольные примеры на уровне приложения, чтобы проверить, что определения параметров сортировки удовлетворяют требованиям.

Копирование правил сортировки

Команду CREATE COLLATION можно также использовать для создания нового правила сортировки из существующего правила, что может быть полезно для возможности использовать независимые от операционной системы имена правил в приложениях, создавать имена для совместимости или использования правил, предоставляемых ICU в более читаемом формате.

Например:

CREATE COLLATION german FROM "de_DE";

CREATE COLLATION french FROM "fr-x-icu";

Недетерминированные правила сортировки

Правило сортировки является либо детерминированным, либо недетерминированным. Детерминированное правило использует детерминированные сравнения, что означает, что оно считает строки равными, только если они состоят из одной и той же последовательности байтов. Недетерминированное сравнение может определить строки равными, даже если они состоят из разных байтов. Типичные ситуации включают сравнение без учета регистра, сравнение без учета акцента, а также сравнение строк в различных нормальных формах Unicode. Поведение этих операций определяется поставщиком правил сравнения; флаг детерминированности только определяет, следует ли использовать байтовое сравнение для равенства. См. Также Технический стандарт Unicode 10 для получения дополнительной информации о терминологии.

Чтобы создать недетерминированный порядок сортировки, укажите для свойства CREATE COLLATION свойство deterministic = false, например:

CREATE COLLATION ndcoll (provider = icu, locale = 'und', deterministic
= false);

В этом примере стандартная сортировка Unicode будет работать недетерминированным способом. В частности, это позволило бы правильно сравнивать строки в разных нормальных формах. Более интересные примеры используют средства настройки ICU, описанные выше. Например:

CREATE COLLATION case_insensitive (provider = icu, locale =
'und-u-ks-level2', deterministic = false);

CREATE COLLATION ignore_accents (provider = icu, locale =
'und-u-ks-level1-kc-true', deterministic = false);

Все стандартные и предопределённые параметры сортировки являются детерминированными, по умолчанию все определяемые пользователем параметры сортировки являются детерминированными. Хотя недетерминированные правила сортировки дают более «правильное» поведение, особенно если учитывать всю мощь Unicode и его множество особых случаев, у них также есть некоторые недостатки. Прежде всего, их использование приводит к снижению производительности. Кроме того, некоторые операции невозможны с недетерминированными правилами, такими как операции сортировки с образцом. Поэтому их следует использовать только в тех случаях, когда они конкретно востребованы.

Совет
Для эффективной обработки текста в различных формах нормализации Unicode также можно использовать функции/выражения normalize и is normalized, позволяющие предварительно обработать или проверить строки, и не прибегать к использованию недетерминированных правил сортировки. Однако каждый подход имеет свои минусы.

Поддержка кодировок

Поддержка кодировок в QHB позволяет хранить текст в различных наборах символов (также называемых кодировками), включая однобайтовые, такие как серии ISO 8859, и многобайтовые, такие как EUC (расширенный код Unix), UTF-8 и внутренний код Mule. Все поддерживаемые кодировки могут прозрачно использоваться клиентами, но некоторые из них не поддерживаются для использования на сервере (то есть в качестве кодировки на стороне сервера). Кодировка по умолчанию выбирается при инициализации кластера баз данных QHB с помощью initdb. Ее можно переопределить при создании базы данных, что позволяет иметь несколько баз данных с разными кодировками.

Однако важным ограничением является то, что кодировка каждой базы данных должна быть совместима с параметрами локали базы данных LC_CTYPE (классификация символов) и LC_COLLATE (порядок сортировки строк). Для локали C или POSIX допустима любая кодировка, но для других локалей, предоставляемых библиотекой libc, есть только одна кодировка, которая будет работать правильно. Если у вас настроена поддержка ICU, локали, предоставляемые библиотекой ICU, можно использовать с большинством, но не со всеми кодировками на стороне сервера.

Поддерживаемые кодировки

В Таблице 1 перечислены кодировки, доступные для использования в QHB.

Таблица 1. Кодировки QHB

Имя | Описание | Язык | Поддержка на сервере | ICU? | Байтов на символ | Псевдонимы
BIG5 | Big Five | Традиционный китайский | нет | нет | 1-2 | WIN950, Windows950
EUC_CN | Расширенный код UNIX-CN | Упрощенный китайский | да | да | 1-3 |
EUC_JP | Расширенный код UNIX-JP | Японский | да | да | 1-3 |
EUC_JIS_2004 | Расширенный код UNIX-JP, JIS X 0213 | Японский | да | нет | 1-3 |
EUC_KR | Расширенный код UNIX-KR | Корейский | да | да | 1-3 |
EUC_TW | Расширенный код UNIX-TW | Традиционный китайский, тайваньский | да | да | 1-3 |
GB18030 | Национальный стандарт | Китайский язык | нет | нет | 1-4 |
GBK | Расширенный национальный стандарт | Упрощенный китайский | нет | нет | 1-2 | WIN936, Windows936
ISO_8859_5 | ISO 8859-5, ECMA 113 | Латинский/Кириллица | да | да | 1 |
ISO_8859_6 | ISO 8859-6, ECMA 114 | Латинский/Арабский | да | да | 1 |
ISO_8859_7 | ISO 8859-7, ECMA 118 | Латинский/Греческий | да | да | 1 |
ISO_8859_8 | ISO 8859-8, ECMA 121 | Латинский/Иврит | да | да | 1 |
JOHAB | JOHAB | Корейский (Хангыль) | нет | нет | 1-3 |
KOI8R | KOI 8-R | Кириллица (Русский) | да | да | 1 | KOI8
KOI8U | KOI 8-У | Кириллица (Украинский) | да | да | 1 |
LATIN1 | ISO 8859-1, ECMA 94 | Западноевропейские | да | да | 1 | ISO88591
LATIN2 | ISO 8859-2, ECMA 94 | Центральноевропейские | да | да | 1 | ISO88592
LATIN3 | ISO 8859-3, ECMA 94 | Южноевропейские | да | да | 1 | ISO88593
LATIN4 | ISO 8859-4, ECMA 94 | Североевропейские | да | да | 1 | ISO88594
LATIN5 | ISO 8859-9, ECMA 128 | Турецкий | да | да | 1 | ISO88599
LATIN6 | ISO 8859-10, ECMA 144 | Скандинавские | да | да | 1 | ISO885910
LATIN7 | ISO 8859-13 | Балтийские | да | да | 1 | ISO885913
LATIN8 | ISO 8859-14 | Кельтские | да | да | 1 | ISO885914
LATIN9 | ISO 8859-15 | LATIN1 с европейскими языками и диакритикой | да | да | 1 | ISO885915
LATIN10 | ISO 8859-16, ASRO SR 14111 | Румынский | да | нет | 1 | ISO885916
MULE_INTERNAL | Внутренний код Mule | Многоязычный редактор Emacs | да | нет | 1-4 |
SJIS | Shift JIS | Японский | нет | нет | 1-2 | Mskanji, ShiftJIS, WIN932, Windows932
SHIFT_JIS_2004 | Shift JIS, JIS X 0213 | Японский | нет | нет | 1-2 |
SQL_ASCII | не указано (см. текст) | любые | да | нет | 1 |
UHC | Унифицированный код Хангыль | Корейский | нет | нет | 1-2 | WIN949, Windows949
UTF8 | Юникод, 8 бит | все | да | да | 1-4 | Unicode
WIN866 | Windows CP866 | Кириллица | да | да | 1 | ALT
WIN874 | Windows CP874 | Тайский | да | нет | 1 |
WIN1250 | Windows CP1250 | Центральноевропейские | да | да | 1 |
WIN1251 | Windows CP1251 | Кириллица | да | да | 1 | WIN
WIN1252 | Windows CP1252 | Западноевропейские | да | да | 1 |
WIN1253 | Windows CP1253 | Греческий | да | да | 1 |
WIN1254 | Windows CP1254 | Турецкий | да | да | 1 |
WIN1255 | Windows CP1255 | Иврит | да | да | 1 |
WIN1256 | Windows CP1256 | Арабский | да | да | 1 |
WIN1257 | Windows CP1257 | Балтийские | да | да | 1 |
WIN1258 | Windows CP1258 | Вьетнамский | да | да | 1 | ABC, TCVN, TCVN5712, VSCII

Не все клиентские API поддерживают все перечисленные наборы символов. Например, драйвер QHB JDBC не поддерживает MULE_INTERNAL, LATIN6, LATIN8 и LATIN10.

Параметр SQL_ASCII ведет себя совершенно не так, как другие параметры. Когда кодировкой сервера является SQL_ASCII, сервер интерпретирует байтовые значения 0-127 в соответствии со стандартом ASCII, а байтовые значения 128-255 принимаются как неинтерпретированные символы. С установленным параметром SQL_ASCII преобразование кодировки выполняться не будет. Таким образом, этот параметр является не столько объявлением, что используется конкретная кодировка, сколько объявлением об игнорировании кодировки. В большинстве случаев, если вы работаете с любыми данными, отличными от ASCII, неразумно использовать параметр SQL_ASCII, поскольку QHB не сможет помочь вам в преобразовании или проверке символов, отличных от ASCII.

Настройка кодировки

initdb определяет набор символов (кодировку) по умолчанию для кластера QHB. Например,

initdb -E EUC_JP

устанавливает кодировку по умолчанию на EUC_JP (Расширенный код Unix для японского языка). Если вы предпочитаете полные параметры, можно использовать --encoding вместо -E. Если параметр -E или --encoding не задан, initdb пытается определить подходящую кодировку в зависимости от указанной локали или локали по умолчанию.

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

createdb -E EUC_KR -T template0 --lc-collate=ko_KR.euckr
--lc-ctype=ko_KR.euckr korean

Это создаст базу данных с именем korean, которая использует кодировку EUC_KR и локаль ko_KR. Другой способ сделать это — с помощью команды SQL:

CREATE DATABASE korean WITH ENCODING 'EUC_KR'
`LC_COLLATE`='ko_KR.euckr' `LC_CTYPE`='ko_KR.euckr'
TEMPLATE=template0;

Обратите внимание, что приведенные выше команды задают копирование базы данных template0. При копировании любой другой базы данных параметры кодировки и локали исходной базы данных изменить нельзя, поскольку это может привести к повреждению данных. Дополнительную информацию см. в разделе Шаблоны баз данных.

Кодировка базы данных хранится в системном каталоге qhb_database. Ее можно увидеть с помощью параметра psql -l или команды \l.

$ psql -l
                                         List of databases
   Name    |  Owner   | Encoding  |  Collation  |    Ctype    |          Access Privileges
-----------+----------+-----------+-------------+-------------+-------------------------------------
 clocaledb | hlinnaka | SQL_ASCII | C           | C           |
 englishdb | hlinnaka | UTF8      | en_GB.UTF8  | en_GB.UTF8  |
 japanese  | hlinnaka | UTF8      | ja_JP.UTF8  | ja_JP.UTF8  |
 korean    | hlinnaka | EUC_KR    | ko_KR.euckr | ko_KR.euckr |
 postgres  | hlinnaka | UTF8      | fi_FI.UTF8  | fi_FI.UTF8  |
 template0 | hlinnaka | UTF8      | fi_FI.UTF8  | fi_FI.UTF8  | {=c/hlinnaka,hlinnaka=CTc/hlinnaka}
 template1 | hlinnaka | UTF8      | fi_FI.UTF8  | fi_FI.UTF8  | {=c/hlinnaka,hlinnaka=CTc/hlinnaka}
(7 rows)

Важно

В большинстве современных операционных систем QHB может определить, какой набор символов подразумевается параметром LC_CTYPE, и он будет обеспечивать использование только соответствующей кодировки базы данных. В старых системах необходимо самому следить за тем, чтобы использовалась кодировка, подходящая выбранной локали. Ошибка в этой области, скорее всего, приведет к странному поведению зависящих от локали операций, таких как сортировка.

QHB позволит суперпользователям создавать базы данных с кодировкой SQL_ASCII, даже если в LC_CTYPE не установлено C или POSIX. Как отмечалось выше, SQL_ASCII не требует, чтобы данные, хранящиеся в базе данных, имели какую-либо конкретную кодировку, и поэтому этот выбор чреват отклонениями, связанными с локалью. Использование этой комбинации настроек устарело и, возможно, однажды будет полностью запрещено.

Автоматическая перекодировка между сервером и клиентом

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

Чтобы включить автоматическую перекодировку, нужно сообщить QHB набор символов (кодировку), который вы бы хотели использовать на стороне клиента. Это можно сделать несколькими способами:

Использование команды \encoding в psql. \encoding позволяет менять клиентскую кодировку «на лету». Например, для изменения кодировки на SJIS введите:

\encoding SJIS

libpq имеет функции для управления клиентской кодировкой.

Использование SET client_encoding TO. Установить клиентскую кодировку можно этой командой SQL:

SET CLIENT_ENCODING TO 'значение';

Также для этой цели можно использовать стандартные синтаксис SQL SET NAMES:

SET NAMES 'значение';

Запросить текущую клиентскую кодировку:

SHOW client_encoding;

Вернуть кодировку по умолчанию:

RESET client_encoding;

Использование PGCLIENTENCODING. Если в клиентской среде определена переменная среды PGCLIENTENCODING, заданная клиентская кодировка выбирается автоматически при подключении к серверу. (В последующем это можно переопределить с помощью любого из описанных выше методов.)

Использование переменной конфигурации client_encoding. Если установлена переменная client_encoding, заданная клиентская кодировка выбирается автоматически при подключении к серверу. (В последующем это можно переопределить с помощью любого из описанных выше методов.)

Если преобразование определенного символа невозможно — предположим, вы выбрали EUC_JP для сервера и LATIN1 для клиента, и возвращаются некоторые японские иероглифы, у которых нет представления в LATIN1 — выдается сообщение об ошибке.

Если кодировка клиента определена как SQL_ASCII, перекодировка отключается вне зависимости от кодировки сервера. (Однако если кодировка сервера отличается от SQL_ASCII, сервер все равно будет проверять входные данные на допустимость для его кодировки, поэтому конечный результат будет таким же, как если бы кодировка клиента совпадала с кодировкой сервера.) Так же, как и для сервера, использование SQL_ASCII неразумно, кроме случаев, когда данные, с которыми вы работаете, полностью соответствуют ASCII.

Доступные перекодировки

QHB допускает преобразования между любыми двумя кодировками, для которых в системном каталоге pg_conversion имеется функция перекодирования. QHB поставляется с несколькими предопределенными перекодировками, перечисленными в Таблице 2 и подробнее описанными в Таблице 3. Можно создать и новую перекодировку, воспользовавшись командой SQL CREATE CONVERSION. (Чтобы применяться для автоматических клиент-серверных перекодировок, она должна быть помечена как перекодировка «по умолчанию» для своей пары кодировок.)

Таблица 2. Встроенные клиент-серверные перекодировки

Серверная кодировка | Доступные клиентские кодировки
BIG5 | не поддерживается в качестве серверной кодировки
EUC_CN | EUC_CN, MULE_INTERNAL, UTF8
EUC_JP | EUC_JP, MULE_INTERNAL, SJIS, UTF8
EUC_JIS_2004 | EUC_JIS_2004, SHIFT_JIS_2004, UTF8
EUC_KR | EUC_KR, MULE_INTERNAL, UTF8
EUC_TW | EUC_TW, BIG5, MULE_INTERNAL, UTF8
GB18030 | не поддерживается в качестве серверной кодировки
GBK | не поддерживается в качестве серверной кодировки
ISO_8859_5 | ISO_8859_5, KOI8R, MULE_INTERNAL, UTF8, WIN866, WIN1251
ISO_8859_6 | ISO_8859_6, UTF8
ISO_8859_7 | ISO_8859_7, UTF8
ISO_8859_8 | ISO_8859_8, UTF8
JOHAB | не поддерживается в качестве серверной кодировки
KOI8R | KOI8R, ISO_8859_5, MULE_INTERNAL, UTF8, WIN866, WIN1251
KOI8U | KOI8U, UTF8
LATIN1 | LATIN1, MULE_INTERNAL, UTF8
LATIN2 | LATIN2, MULE_INTERNAL, UTF8, WIN1250
LATIN3 | LATIN3, MULE_INTERNAL, UTF8
LATIN4 | LATIN4, MULE_INTERNAL, UTF8
LATIN5 | LATIN5, UTF8
LATIN6 | LATIN6, UTF8
LATIN7 | LATIN7, UTF8
LATIN8 | LATIN8, UTF8
LATIN9 | LATIN9, UTF8
LATIN10 | LATIN10, UTF8
MULE_INTERNAL | MULE_INTERNAL, BIG5, EUC_CN, EUC_JP, EUC_KR, EUC_TW, ISO_8859_5, KOI8R, LATIN1 - LATIN4, SJIS, WIN866, WIN1250, WIN1251
SJIS | не поддерживается в качестве серверной кодировки
SHIFT_JIS_2004 | не поддерживается в качестве серверной кодировки
SQL_ASCII | любой (преобразование не будет выполнено)
UHC | не поддерживается в качестве серверной кодировки
UTF8 | все поддерживаемые кодировки
WIN866 | WIN866, ISO_8859_5, KOI8R, MULE_INTERNAL, UTF8, WIN1251
WIN874 | WIN874, UTF8
WIN1250 | WIN1250, LATIN2, MULE_INTERNAL, UTF8
WIN1251 | WIN1251, ISO_8859_5, KOI8R, MULE_INTERNAL, UTF8, WIN866
WIN1252 | WIN1252, UTF8
WIN1253 | WIN1253, UTF8
WIN1254 | WIN1254, UTF8
WIN1255 | WIN1255, UTF8
WIN1256 | WIN1256, UTF8
WIN1257 | WIN1257, UTF8
WIN1258 | WIN1258, UTF8

Таблица 3. Все встроенные перекодировки

Имя преобразования ¹ | Исходная кодировка | Целевая кодировка
big5_to_euc_tw | BIG5 | EUC_TW
big5_to_mic | BIG5 | MULE_INTERNAL
big5_to_utf8 | BIG5 | UTF8
euc_cn_to_mic | EUC_CN | MULE_INTERNAL
euc_cn_to_utf8 | EUC_CN | UTF8
euc_jp_to_mic | EUC_JP | MULE_INTERNAL
euc_jp_to_sjis | EUC_JP | SJIS
euc_jp_to_utf8 | EUC_JP | UTF8
euc_kr_to_mic | EUC_KR | MULE_INTERNAL
euc_kr_to_utf8 | EUC_KR | UTF8
euc_tw_to_big5 | EUC_TW | BIG5
euc_tw_to_mic | EUC_TW | MULE_INTERNAL
euc_tw_to_utf8 | EUC_TW | UTF8
gb18030_to_utf8 | GB18030 | UTF8
gbk_to_utf8 | GBK | UTF8
iso_8859_10_to_utf8 | LATIN6 | UTF8
iso_8859_13_to_utf8 | LATIN7 | UTF8
iso_8859_14_to_utf8 | LATIN8 | UTF8
iso_8859_15_to_utf8 | LATIN9 | UTF8
iso_8859_16_to_utf8 | LATIN10 | UTF8
iso_8859_1_to_mic | LATIN1 | MULE_INTERNAL
iso_8859_1_to_utf8 | LATIN1 | UTF8
iso_8859_2_to_mic | LATIN2 | MULE_INTERNAL
iso_8859_2_to_utf8 | LATIN2 | UTF8
iso_8859_2_to_windows_1250 | LATIN2 | WIN1250
iso_8859_3_to_mic | LATIN3 | MULE_INTERNAL
iso_8859_3_to_utf8 | LATIN3 | UTF8
iso_8859_4_to_mic | LATIN4 | MULE_INTERNAL
iso_8859_4_to_utf8 | LATIN4 | UTF8
iso_8859_5_to_koi8_r | ISO_8859_5 | KOI8R
iso_8859_5_to_mic | ISO_8859_5 | MULE_INTERNAL
iso_8859_5_to_utf8 | ISO_8859_5 | UTF8
iso_8859_5_to_windows_1251 | ISO_8859_5 | WIN1251
iso_8859_5_to_windows_866 | ISO_8859_5 | WIN866
iso_8859_6_to_utf8 | ISO_8859_6 | UTF8
iso_8859_7_to_utf8 | ISO_8859_7 | UTF8
iso_8859_8_to_utf8 | ISO_8859_8 | UTF8
iso_8859_9_to_utf8 | LATIN5 | UTF8
johab_to_utf8 | JOHAB | UTF8
koi8_r_to_iso_8859_5 | KOI8R | ISO_8859_5
koi8_r_to_mic | KOI8R | MULE_INTERNAL
koi8_r_to_utf8 | KOI8R | UTF8
koi8_r_to_windows_1251 | KOI8R | WIN1251
koi8_r_to_windows_866 | KOI8R | WIN866
koi8_u_to_utf8 | KOI8U | UTF8
mic_to_big5 | MULE_INTERNAL | BIG5
mic_to_euc_cn | MULE_INTERNAL | EUC_CN
mic_to_euc_jp | MULE_INTERNAL | EUC_JP
mic_to_euc_kr | MULE_INTERNAL | EUC_KR
mic_to_euc_tw | MULE_INTERNAL | EUC_TW
mic_to_iso_8859_1 | MULE_INTERNAL | LATIN1
mic_to_iso_8859_2 | MULE_INTERNAL | LATIN2
mic_to_iso_8859_3 | MULE_INTERNAL | LATIN3
mic_to_iso_8859_4 | MULE_INTERNAL | LATIN4
mic_to_iso_8859_5 | MULE_INTERNAL | ISO_8859_5
mic_to_koi8_r | MULE_INTERNAL | KOI8R
mic_to_sjis | MULE_INTERNAL | SJIS
mic_to_windows_1250 | MULE_INTERNAL | WIN1250
mic_to_windows_1251 | MULE_INTERNAL | WIN1251
mic_to_windows_866 | MULE_INTERNAL | WIN866
sjis_to_euc_jp | SJIS | EUC_JP
sjis_to_mic | SJIS | MULE_INTERNAL
sjis_to_utf8 | SJIS | UTF8
windows_1258_to_utf8 | WIN1258 | UTF8
uhc_to_utf8 | UHC | UTF8
utf8_to_big5 | UTF8 | BIG5
utf8_to_euc_cn | UTF8 | EUC_CN
utf8_to_euc_jp | UTF8 | EUC_JP
utf8_to_euc_kr | UTF8 | EUC_KR
utf8_to_euc_tw | UTF8 | EUC_TW
utf8_to_gb18030 | UTF8 | GB18030
utf8_to_gbk | UTF8 | GBK
utf8_to_iso_8859_1 | UTF8 | LATIN1
utf8_to_iso_8859_10 | UTF8 | LATIN6
utf8_to_iso_8859_13 | UTF8 | LATIN7
utf8_to_iso_8859_14 | UTF8 | LATIN8
utf8_to_iso_8859_15 | UTF8 | LATIN9
utf8_to_iso_8859_16 | UTF8 | LATIN10
utf8_to_iso_8859_2 | UTF8 | LATIN2
utf8_to_iso_8859_3 | UTF8 | LATIN3
utf8_to_iso_8859_4 | UTF8 | LATIN4
utf8_to_iso_8859_5 | UTF8 | ISO_8859_5
utf8_to_iso_8859_6 | UTF8 | ISO_8859_6
utf8_to_iso_8859_7 | UTF8 | ISO_8859_7
utf8_to_iso_8859_8 | UTF8 | ISO_8859_8
utf8_to_iso_8859_9 | UTF8 | LATIN5
utf8_to_johab | UTF8 | JOHAB
utf8_to_koi8_r | UTF8 | KOI8R
utf8_to_koi8_u | UTF8 | KOI8U
utf8_to_sjis | UTF8 | SJIS
utf8_to_windows_1258 | UTF8 | WIN1258
utf8_to_uhc | UTF8 | UHC
utf8_to_windows_1250 | UTF8 | WIN1250
utf8_to_windows_1251 | UTF8 | WIN1251
utf8_to_windows_1252 | UTF8 | WIN1252
utf8_to_windows_1253 | UTF8 | WIN1253
utf8_to_windows_1254 | UTF8 | WIN1254
utf8_to_windows_1255 | UTF8 | WIN1255
utf8_to_windows_1256 | UTF8 | WIN1256
utf8_to_windows_1257 | UTF8 | WIN1257
utf8_to_windows_866 | UTF8 | WIN866
utf8_to_windows_874 | UTF8 | WIN874
windows_1250_to_iso_8859_2 | WIN1250 | LATIN2
windows_1250_to_mic | WIN1250 | MULE_INTERNAL
windows_1250_to_utf8 | WIN1250 | UTF8
windows_1251_to_iso_8859_5 | WIN1251 | ISO_8859_5
windows_1251_to_koi8_r | WIN1251 | KOI8R
windows_1251_to_mic | WIN1251 | MULE_INTERNAL
windows_1251_to_utf8 | WIN1251 | UTF8
windows_1251_to_windows_866 | WIN1251 | WIN866
windows_1252_to_utf8 | WIN1252 | UTF8
windows_1256_to_utf8 | WIN1256 | UTF8
windows_866_to_iso_8859_5 | WIN866 | ISO_8859_5
windows_866_to_koi8_r | WIN866 | KOI8R
windows_866_to_mic | WIN866 | MULE_INTERNAL
windows_866_to_utf8 | WIN866 | UTF8
windows_866_to_windows_1251 | WIN866 | WIN
windows_874_to_utf8 | WIN874 | UTF8
euc_jis_2004_to_utf8 | EUC_JIS_2004 | UTF8
utf8_to_euc_jis_2004 | UTF8 | EUC_JIS_2004
shift_jis_2004_to_utf8 | SHIFT_JIS_2004 | UTF8
utf8_to_shift_jis_2004 | UTF8 | SHIFT_JIS_2004
euc_jis_2004_to_shift_jis_2004 | EUC_JIS_2004 | SHIFT_JIS_2004
shift_jis_2004_to_euc_jis_2004 | SHIFT_JIS_2004 | EUC_JIS_2004

¹

Имена перекодировок следуют стандартной схеме именования: к официальному названию исходной кодировки, в котором все не буквенно-цифровые символы заменяются подчеркиваниями, добавляется _to_, за которым следует аналогично обработанного название целевой кодировки. Таким образом, эти имена иногда отличаются от общепринятых названий, показанных в Таблице 1.

Дополнительные источники информации

Рекомендуемые источники для начала изучения различных видов систем кодирования.

CJKV Information Processing: Chinese, Japanese, Korean & Vietnamese Computing
Содержит подробные объяснения EUC_JP, EUC_CN, EUC_KR, EUC_TW.

http://www.unicode.org/
Сайт Консорциума Unicode.

RFC 3629
UTF-8 (8-битный формат преобразования UCS/Unicode) определен здесь.

Методы извлечения выборки из таблицы

В дополнение к методам BERNOULLI и SYSTEM, требуемыми стандартом SQL, реализация предложения TABLESAMPLE в QHB поддерживает пользовательские методы извлечения выборки из таблицы. Метод извлечения выборки определяет, какие строки таблицы будут выбраны, при использовании предложения TABLESAMPLE.

На уровне SQL, метод извлечения выборки представлен единственной функцией (как правило, реализованной на C или Rust) с сигнатурой

имя_метода(internal) RETURNS tsm_handler

Имя функции совпадает с именем метода, используемым в предложении TABLESAMPLE. Аргумент internal является фиктивным (всегда принимающим нулевое значение) — он предотвращает вызов этой функции непосредственно из команды SQL. Результатом функции должна быть аллоцированная palloc структура, имеющая тип TsmRoutine, который содержит указатели на вспомогательные функции для метода извлечения выборки. Вспомогательные функции описаны в разделе Вспомогательные функции метода извлечения выборки.

Кроме указателей на функций, структура TsmRoutine должна содержать следующие поля:

List *parameterTypes

Это список содержит OID типов данных параметров, принимаемых предложением TABLESAMPLE при использовании данного метода извлечения выборки. Например, у встроенных методов этот список содержит один элемент со значением FLOAT4OID, представляющий собой процент выборки. Пользовательские методы могут иметь дополнительные или иные параметры.

bool repeatable_across_queries

Значение true означает, что метод извлечения выборки возвращает идентичные выборки в последовательных запросах, если в каждом запросе предоставляются одни и те же параметры и начальное значение REPEATABLE, а содержимое таблицы не изменяется. Если же значение равно false, предложение REPEATABLE не принимается для использования с методом выборки.

bool repeatable_across

Если true, то метод извлечения выборки способен возвращать идентичные выборки в последовательных сканирования внутри одного и того же запроса (при неизменных параметрах, начальном значении и снимке). Если же значение равно false, то планировщик не будет выбирать планы запроса, требующие более одного сканирования семплированной таблицы, так как они может приводить к неконсистентным результатам запроса.

Для дополнительной информации, см. тип структуры TsmRoutine объявленный в src/include/access/tsmapi.h.

Методы извлечения выборки, входящие в стандартный дистрибутив, могут послужить хорошим примером при реализации собственных методов выборки. Код встроенных методов находится в подкаталоге src/backend/access/tablesample дерева исходного кода, а в подкаталоге contrib можно найти методы извлечения выборки расширений.

Вспомогательные функции метода извлечения выборки

Функция обработчика TSM возвращает аллоцированную palloc структуру TsmRoutine, содержащую указатели на вспомогательные функции, описанные ниже. Большинство этих функций являются обязательными, однако некоторые являются опциональными, и соответствующие им указатели могут быть NULL.

void
SampleScanGetSampleSize (PlannerInfo *root,
                         RelOptInfo *baserel,
                         List *paramexprs,
                         BlockNumber *pages,
                         double *tuples);

Эта функция вызывается во время планирования запроса. Она должна оценить количество страниц отношения, которые будут прочитаны во время сканирования выборки, и количество кортежей, которые будут выбраны при сканировании. (Например, они могут быть определены оценкой процента выборки и умножением baserel->pages и baserel->tuples на этот процент с округлением к целому.) Список paramexprs содержит выражения, являющиеся параметрами к предложению TABLESAMPLE. Рекомендуется использовать estimate_expression_value(), чтобы редуцировать эти выражения до констант, когда они используются для оценки, однако функция должна вернуть оценку и в случае, когда выражения не могут быть редуцированны. Более того, функция должна успешно вернуть оценку даже в случае, когда соответствующие значения выглядят некорректными (помните, что это только приблизительные оценки чисел, которые будут получены во время выполнения). pages и tuples являются выходными параметрами.

void
InitSampleScan (SampleScanState *node,
                int eflags);

Инициализация для выполнения узла плана SampleScan. Эта функция вызывается во время запуска исполнителя. Её полагается выполнить любую инициализацию, необходимую перед началом обработки. Узел SampleScanState уже создан, но его поле tsm_state ещё равно NULL. Функция InitSampleScan может аллоцировать с помощью palloc любое внутреннее состояние, требуемое для метода извлечения выборки, и присвоить node->tsm_state указатель на это состояние. Информация о том, какие страницы сканировать, доступна через иные поля узла SampleScanState (но обратите внимание, что дескриптор сканирования node->ss.ss_currentScanDesc ещё не установлен). eflags содержит битовые флаги, описывающие рабочий режим исполнителя для данного узла плана.

Когда (eflags & EXEC_FLAG_EXPLAIN_ONLY) истинно, сканирование фактически не будет выполняться, и функция должна выполнить только минимум, необходимый, чтобы состояние узла было корректным для EXPLAIN и EndSampleScan.

Эта функция может быть опущена (присваиванием указателю значение NULL), в этом случае функция BeginSampleScan должна выполнять всю инициализацию, необходимую методу извлечения выборки.

void
BeginSampleScan (SampleScanState *node,
                 Datum *params,
                 int nparams,
                 uint32 seed);

Начать выполнение сканирования выборки. Это функция вызывается непосредственно перед первой попыткой извлечения кортежа и может быть вызвана снова, если сканирование необходимо перезапустить. Информация о таблице, подлежащей сканированию, доступна через поля структуры node (но обратите внимание, что node->ss.ss_currentScanDesc в этот момент ещё не инициализирован). Массив params, длины nparams, содержит значения параметров переданных предложению TABLESAMPLE. Они будут иметь номер и тип, определённые списком patameterTypes метода извлечения выборки, и будут проверены на неравенство NULL. seed содержит начальное значение для всех псевдослучайных чисел, сгенерированных внутри метода. Оно представляет собой или хеш на основе значения REPEATABLE, или результат вызова random().

Эта функция может менять значения полей node->use_bulkread и node->use_pagemode. Если node->use_pagemode истинно, что по умолчанию так, то сканирование будет использовать стратегию доступа к буферу, которая поощряет переиспользование буферов. Может иметь смысл установить это поле в false, если сканирование будет выбирать лишь малую часть всех кортежей в каждой посещённой таблице. Это приведет к меньшему количеству выполняемых проверок видимости кортежей, хотя каждая из них будет стоить дороже, так как потребует больше блокировок.

Если метод извлечения выборки помечен как repeatable_across_scans, он обязан выбирать один и тот же набор кортежей во время повторного сканирования, как и во время первоначального, то есть вызов BeginSampleScan должен приводить к выбору тех же кортежей, что и раньше (если параметры TABLESPACE и начальное значение не изменились).

BlockNumber
NextSampleBlock (SampleScanState *node, BlockNumber nblocks);

Возвращает номер блока следующей сканируемой страницы, или InvalidBlockNumber если не осталось страниц для сканирования.

Эта функция может быть опущена (присваиванием указателю значение NULL), тогда основной код выполнит последовательное сканирование всего отношения. Такая проверка может использовать синхронизацию сканирования, так что метод извлечения выборки не может предполагать, что страницы отношения посещаются в одном и том же порядке при каждом сканировании.

OffsetNumber
NextSampleTuple (SampleScanState *node,
                 BlockNumber blockno,
                 OffsetNumber maxoffset);

Для данной страницы, возвращает номер смещения следующего кортежа, добавляемого в выборку, или InvalidOffsetNumber если не осталось кортежей для выборки. maxoffset является наибольшей величиной смещения на этой странице.

Примечание!!!
NextSampleTuple явно не говорится, какие номера смещения в диапазоне 1 .. maxoffset в действительности соответствуют корректным кортежам. Как правило, это не является проблемой, поскольку основной код игнорирует запросы к семплированию не существующих или скрытых кортежей, это не должно привести ни к какому смещению в выборке. Однако, если необходимо, функция может использовать node->donetuples, чтобы проверить, сколько из возвращённых кортежей были корректны и видимы.

Примечание!!!
NextSampleTuple не следует предполагать, что blockno это тот же номер страницы, что был возвращен самым последним вызовом NextSampleBlock. Он был возвращен каким-то предыдущим вызовом NextSampleBlock вызовом, однако основной код может вызывать NextSampleBlock до фактического сканирования страниц, для поддержки предварительной выборки. Допустимо предположить, что когда выборка данной страницы начнётся, все последующие вызовы вызовы NextSampleTuple ссылаются на одну и ту же страницу, до тех пор, пока не будет возвращен InvalidOffsetNumber.

void
EndSampleScan (SampleScanState *node);

Завершить сканирование и освободить ресурсы. Как правило, освобождать аллоцированную с помощью palloc память не обязательно, однако любые ресурсы, видимые извне, должны быть очищены. Эта функция может быть опущена (присваиванием указателю значение NULL) в обычном случае, когда подобных ресурсов нет.

Регулярные задачи обслуживания базы данных

QHB, как и любое программное обеспечение для баз данных, для достижения оптимальной производительности требует, чтобы определенные задачи выполнялись регулярно . Обсуждаемые здесь задачи являются обязательными, но они повторяемые и могут быть легко автоматизированы с помощью стандартных инструментов, таких как сценарии cron. Администратор базы данных отвечает за установку соответствующих сценариев и проверку их успешного выполнения.

Одной из очевидных задач обслуживания является создание резервных копий данных на регулярной основе. Без последней резервной копии у вас нет шансов на восстановление после катастрофы (сбой диска, пожар, ошибочное удаление критической таблицы и т. д.). Механизмы резервного копирования и восстановления, доступные в QHB, подробно обсуждаются в главе Резервное копирование и восстановление.

Другой основной категорией задачи обслуживания является периодическая очистка базы данных. Эта задача обсуждается в разделе Регулярная очистка. С ней тесно связана задача обновления статистики, которая будет использоваться планировщиком запросов, как описано в разделе Обновление статистики планировщика.

Другая задача, которая может потребовать периодического внимания, — это управление файлами журналов. Она обсуждается в разделе Обслуживание файла журнала.

QHB не требует значительного обслуживания по сравнению с другими системами управления базами данных. Тем не менее, надлежащее внимание к этим задачам позволит обеспечить комфортный и продуктивный опыт работы с системой.

Регулярная очистка

Базы данных QHB требуют периодического обслуживания, известного как очистка («vacuum»). Для многих установок достаточно, чтобы процесс vacuum выполнял только автоматическую очистку, как описано в разделе Процесс «Автовакуум». Возможно, вам придется настроить параметры autovacuum, описанные там, чтобы получить наилучшие результаты для вашей установки. Некоторые администраторы баз данных захотят дополнить или заменить действия автоочистки командами VACUUM с ручным управлением, которые обычно выполняются в соответствии с расписанием с помощью сценариев cron. Чтобы правильно настроить процесс vacuum с ручным управлением, важно понимать вопросы, обсуждаемые в следующих нескольких подразделах. Администраторы, которые полагаются на автоочистку, могут ознакомится с этим материалом, чтобы лучше понять как настроить autovacuum.

Основы vacuum

Команда QHB VACUUM должна обрабатывать каждую таблицу на регулярной основе по нескольким причинам:

для восстановления или повторного использования дискового пространства, занятого обновленными или удаленными строками;

для обновления статистики данных используемых планировщиком запросов QHB;

для обновления карты видимости, которая ускоряет Сканирование только по индексу и покрывающие индексы;

для защиты от потери очень старых данных из-за "закольцовывания" идентификатора транзакции или мультитранзакционного идентификатора.

Каждая из этих причин требует выполнение операций VACUUM различной частоты и объема, что будет объяснено в следующих подразделах.

Существует два варианта VACUUM: стандартный VACUUM и VACUUM FULL. VACUUM FULL может освободить больше места на диске, но работает намного медленнее. Кроме того, стандартная форма VACUUM может работать параллельно с другими операциями базы данных. (Такие команды, как SELECT, INSERT, UPDATE и DELETE, продолжат нормально функционировать, хотя вы не сможете изменять определение таблицы с помощью команд, таких как ALTER TABLE во время очистки). VACUUM FULL требует эксклюзивной блокировки на таблицы, с которыми он работает, и, следовательно, не может быть выполнен параллельно с другим использованием таблицы. Поэтому, как правило, администраторы должны стремиться использовать стандартный VACUUM и избегать VACUUM FULL.

VACUUM создает значительный объем трафика ввода-вывода, что может привести к снижению производительности других активных сеансов. Существуют параметры конфигурации, которые можно регулировать, чтобы снизить влияние фоновой очистки на производительность - см. раздел Задержка очистки по стоимости.

Восстановление дискового пространства

В QHB UPDATE или DELETE строки не сразу удаляют старую версию строки. Этот подход необходим для получения преимуществ многоверсионного управления параллелизмом: нельзя удалять версию строки, пока она потенциально может быть видна другим транзакциям. Но в конце концов устаревшая или удаленная версия строки больше не представляет интереса для какой-либо транзакции. Затем занимаемое им пространство должно стать доступно для повторного использования новыми строками, чтобы избежать неограниченного роста требований к дисковому пространству. Это достигается с помощью процесса VACUUM.

Стандартная форма VACUUM удаляет версии "мертвых" строк в таблицах и индексах и отмечает пространство, доступное для повторного использования в будущем. Однако он не вернет пространство операционной системе, за исключением особого случая, когда одна или несколько страниц в конце таблицы становятся полностью свободными, и можно легко получить эксклюзивную блокировку таблицы. Напротив, VACUUM FULL активно уплотняет таблицы, записывая полную новую версию файла таблицы без пустого пространства. Это минимизирует размер таблицы, но может занять очень много времени. Также требуется дополнительное дисковое пространство для новой копии таблицы, пока операция не завершится.

Обычно, цель очистки состоит в том, чтобы выполнять стандартный VACUUM достаточно часто, чтобы избежать необходимости запуска VACUUM FULL. Фоновый процесс autovacuum пытается работать таким образом, и фактически никогда не применяет VACUUM FULL. Идея такого подхода состоит не в том, чтобы поддерживать минимальный размер таблиц, а в том, чтобы поддерживать устойчивое равномерное использование дискового пространства: каждая таблица занимает пространство, эквивалентное ее минимальному размеру, тем не менее, много места может использоваться между очистками. Хотя VACUUM FULL можно использовать для сокращения таблицы до минимального размера и возврата дискового пространства в операционную систему, в этом нет особого смысла, если таблица в будущем снова будет расти. Таким образом, стандартные прогоны VACUUM с умеренной частотой являются лучше, чем редкие прогоны VACUUM FULL, для обработки значительно обновленных таблиц.

Некоторые администраторы предпочитают планировать VACUUM самостоятельно, например, выполняя всю работу ночью при низкой нагрузке. Сложность выполнения очистки по фиксированному расписанию состоит в том, что если в таблице неожиданно наблюдается всплеск активности обновления, она может раздуться до такой степени, что VACUUM FULL станет действительно необходим для освобождения места. Использование фонового процесса autovacuum облегчает эту задачу, поскольку фоновый процесс планирует динамическое удаление "мёртвых" строк в ответ на действия по обновлению. Неразумно полностью выключать фоновый процесс, если у вас нет стопроцентно предсказуемой рабочей нагрузки. Одним из возможных компромиссов является установка параметров фонового процесса так, чтобы он реагировал только на необычайно интенсивную деятельность по обновлению, таким образом не давая системе выйти из-под контроля, в то время как запланированные процессы VACUUM должны выполнять основную часть работы, когда нагрузка является типичной.

Для тех, кто не использует автоочистку, типичный подход - планирование VACUUM всей базы данных один раз в сутки в течение периода наименьшего использования, дополненного, по мере необходимости, более частой очисткой сильно обновленных таблиц. (В некоторых установках с чрезвычайно высокой частотой обновления самые загруженные таблицы вакуумируются каждые несколько минут). Если у вас в кластере несколько баз данных, не забудьте настроить VACUUM для каждой. Здесь может быть полезна программа vacuumdb.

Заметка
Результаты простого VACUUM могут быть неудовлетворительными, если в таблице содержится большое количество версий "мертвых" строк в результате массового обновления или удаления. Если у вас есть такая таблица и вам нужно освободить занимаемое ею избыточное дисковое пространство, вам нужно будет использовать VACUUM FULL или, альтернативно, CLUSTER или один из вариантов перезаписи таблицы ALTER TABLE. Эти команды переписывают новую копию таблицы и строят для нее новые индексы. Все эти опции требуют эксклюзивной блокировки. Обратите внимание, что они также временно используют дополнительное дисковое пространство, приблизительно равное размеру таблицы, поскольку старые копии таблицы и индексов не могут быть освобождены, пока не будут созданы новые.

Заметка
Если у вас есть таблица, все содержимое которой периодически удаляется, попробуйте сделать это с помощью TRUNCATE, а не с помощью DELETE а затем выполните VACUUM. TRUNCATE немедленно удаляет все содержимое таблицы, не требуя последующего VACUUM или VACUUM FULL для восстановления неиспользуемого дискового пространства. Недостатком является то, что строгая семантика MVCC нарушается.

Обновление статистики планировщика

Планировщик запросов QHB полагается на статистическую информацию о содержимом таблиц для создания хороших планов для запросов. Эти статистические данные собираются с помощью команды ANALYZE, которую можно вызвать отдельно или в качестве необязательного шага в VACUUM. Важно иметь достаточно точную статистику, иначе неверный выбор планов выполнения может значительно снизить производительность базы данных.

Фоновый процесс autovacuum, если он включен, будет автоматически запускать команды ANALYZE всякий раз, когда содержимое таблицы в достаточной степени изменяется . Однако администраторы могут предпочесть полагаться на запланированные вручную операции ANALYZE, особенно если известно, что активность обновления таблицы не повлияет на статистику «интересующих» планировщик столбцов (которые интенсивно используются в WHERE). Фоновый процесс autovacuum планирует ANALYZE строго в зависимости от количества вставленных или обновленных строк - он не знает, приведет ли это к значимым статистическим изменениям.

Как и в случае с VACUUM для восстановления пространства, частые обновления статистики более полезны для сильно обновленных таблиц, чем для редко обновляемых. Но даже для сильно обновленной таблицы может не потребоваться обновление статистики, если статистическое распределение данных сильно не меняется. Простое правило - подумать о том, насколько изменяются минимальное и максимальное значения столбцов в таблице. Например, столбец timestamp который содержит время обновления строки, будет иметь постоянно увеличивающееся максимальное значение при добавлении и обновлении строк; такой столбец, вероятно, будет нуждаться в более частых обновлениях статистики, чем, скажем, столбец, содержащий URL-адреса для страниц, доступных на веб-сайте. Столбец URL может получать изменения так же часто, но статистическое распределение его значений, вероятно, изменяется слабо.

Можно запустить ANALYZE для определенных таблиц и даже только для определенных столбцов таблицы, поэтому существует возможность обновлять некоторые статистические данные чаще, чем другие, если этого требует ваше приложение. На практике, однако, обычно лучше всего проанализировать всю базу данных, потому что это быстрая операция. ANALYZE использует статистически случайную выборку строк таблицы, а не читает каждую строку.

Заметка
Хотя подстройка частоты ANALYZE каждого столбца может быть и не очень продуктивной, может оказаться целесообразным выполнить для каждого столбца настройку уровня детализации статистики, собираемой ANALYZE. Столбцы, которые интенсивно используются в WHERE и имеют очень нерегулярное распределение данных, могут потребовать более детальной гистограммы данных, чем другие столбцы. См. ALTER TABLE SET STATISTICS или можно изменить значение по умолчанию для всей базы данных, используя параметр конфигурации default_statistics_target.

Также по умолчанию имеется ограниченная информация о селективности функций. Однако если вы создадите индекс по выражению, использующий вызов функции, будет собрана полезная статистика о функции, которая может значительно улучшить планы запросов, использующие индекс по выражению.

Заметка
Фоновый процесс autovacuum не запускает команды ANALYZE для внешних таблиц, так как не имеет возможности определить, как часто это может быть сделано. Если ваши запросы для правильного планирования требуют статистики внешних таблиц , рекомендуется запускать вручную команды ANALYZE для этих таблиц по расписанию.

Обновление карты видимости

VACUUM поддерживает карту видимости для каждой таблицы, чтобы отслеживать, какие страницы содержат только кортежи, которые, как известно, видны всем активным транзакциям (и всем будущим транзакциям, пока страница не будет снова изменена). Это реализовано для двух целей.

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

Во-вторых, страницы видимости позволяют QHB отвечать на некоторые запросы, используя только индекс, без ссылки на базовую таблицу. Поскольку индексы QHB не содержат информации о видимости строки, при обычном сканировании индекса строка выбирается для каждой соответствующей записи индекса, чтобы проверить, должна ли она быть видимой текущей транзакцией. С другой стороны, сканирование только по индексу сначала проверяет карту видимости. Если известно, что все кортежи на странице видны, выборка может быть пропущена. Это наиболее полезно для больших наборов данных, где карта видимости может предотвратить лишний доступ к диску. Карта видимости значительно меньше списка всех блоков, поэтому ее можно легко кэшировать, даже если блоков очень много.

Предотвращение ошибок зацикливания идентификатора транзакции

Семантика транзакции MVCC в QHB зависит от возможности сравнения номеров идентификаторов транзакций (XID): версия строки с XID вставки, превышающим XID текущей транзакции, находится «в будущем» и не должна быть видимой для текущей транзакции. Но поскольку идентификаторы транзакций имеют ограниченный размер 32 бита, кластер, работающий в течение длительного времени (более 4 миллиардов транзакций), будет подвергаться циклическому изменению идентификатора транзакции (закольцовывание, wraparound): счетчик XID обнуляется, и все транзакции, которые были в прошлом внезапно окажутся в будущем - что означает, что их значения становится невидимы. В итоге произойдёт катастрофическая потеря данных. (На самом деле данные все еще там, но это не поможет, если нет возможности их получить). Чтобы избежать этого, необходимо запустить VACUUM для каждой таблицы в каждой базе данных как минимум один раз каждые два миллиарда транзакций.

Причина, по которой периодический запуск VACUUM решает проблему, состоит в том, что VACUUM помечает строки как "замороженные" (freeze), указывая на то, что они были установлены транзакцией, зафиксированной достаточно давно, и что изменения этой транзакции будут видны для всех текущих и будущих транзакций., Нормальные XID сравниваются с использованием арифметики по модулю 2 **³²

**. Это означает, что для каждого нормального XID существует два миллиарда «более старых» и два миллиарда «более новых» значений. После создания версии строки с определенным нормальным XID версия строки будет «в прошлом» для следующих двух миллиардов транзакций, независимо от того, о каком XID идет речь. Если версия строки все еще существует после более чем двух миллиардов транзакций, она внезапно появится в будущем. Чтобы предотвратить это, QHB резервирует специальный XID, FrozenTransactionId, который не следует нормальным правилам сравнения XID и всегда считается старше любого обычного XID. Версии замороженных строк обрабатываются так, как если бы XID вставки был FrozenTransactionId, так что они будут казаться «в прошлом» для всех обычных транзакций, независимо от проблем с переносом, и поэтому такие версии строк будут действительными до тех пор, пока они не будут удалены, независимо от того, как долго они хранятся.

vacuum_freeze_min_age контролирует, какой возраст должен быть у значения XID, прежде чем строки, содержащие этот XID, будут "заморожены". Увеличение этого параметра поможет избежать ненужной работы, если строки, которые в противном случае были бы заморожены, вскоре будут снова изменены, но уменьшение этого параметра увеличивает количество транзакций, которые могут пройти, прежде чем таблицу необходимо будет снова очистить.

VACUUM использует карту видимости, чтобы определить, какие страницы таблицы необходимо сканировать. Обычно он пропускает страницы, которые не имеют версий "мертвых" строк, даже если на этих страницах все еще могут быть версии строк со старыми значениями XID. Поэтому обычные VACUUM не всегда замораживают каждую старую версию строки в таблице. Периодически VACUUM будет инициировать агрессивный вакуум, пропуская только те страницы, которые не содержат ни пустых строк, ни незамерзших значений XID или MXID. vacuum_freeze_table_age контролирует, когда VACUUM выполнит след. действия: сканируются все видимые, но не полностью замороженные страницы, если количество транзакций, прошедших с момента последнего такого сканирования, больше, чем vacuum_freeze_table_age минус vacuum_freeze_min_age. Установка 0 в vacuum_freeze_table_age заставляет VACUUM использовать эту более агрессивную стратегию для всех сканирований.

Максимальное время, в течение которого таблица может оставаться невостребованной, составляет два миллиарда транзакций минус значение vacuum_freeze_min_age во время последнего агрессивного вакуума. Если бы она оставалась невостребованной дольше, это могло бы привести к потере данных. Чтобы этого не происходило, autovacuum вызывается для любой таблицы, которая может содержать незамерзающие строки с XID старше, чем возраст, указанный в параметре конфигурации autovacuum_freeze_max_age. (Вызов произойдет, даже если автоочистка выключена).

Подразумевается, что если таблица не будет очищена VACUUM, то для ее обработки будет вызываться autovacuum примерно один раз за autovacuum_freeze_max_age минус vacuum_freeze_min_age транзакцию. Для таблиц, которые регулярно очищает VACUUM в целях освобождения пространства, это не имеет большого значения. Однако для статических таблиц (включая таблицы, для которых вызываются операции вставки, но не обновления и не удаления), нет необходимости в VACUUM для восстановления пространства, поэтому может быть полезно попытаться максимально увеличить интервал между принудительными сеансами автоочистки для очень больших статических таблиц. Очевидно, что это можно сделать, либо увеличив autovacuum_freeze_max_age либо уменьшив vacuum_freeze_min_age.

Сколько-нибудь эффективным максимальным значением vacuum_freeze_table_age является 0.95*autovacuum_freeze_max_age. Большее значение будет упираться в возможный максимум. А значение равное autovacuum_freeze_max_age вообще не имеет смысла, потому что процедура очистки для предотвращения "закольцовывания" (прохода через ноль номера транзакции) запустится к этому моменту сама. Таким образом множитель 0,95 оставляет некоторое пространство для манёвра и запуска VACUUM в ручном режиме, до момента, когда произойдёт закольцовывание. Основным правилом можно считать следующее: Параметр vacuum_freeze_table_age нужно выставить несколько ниже, чем autovacuum_freeze_max_age, оставляя промежуток для штатно-запланированного запуска VACUUM или autovacuum вызываемого нормальными процедурами DELETE и UPDATE. Таким образом, ставя значения слишком близко, возможно повлечь запуск autovacuum для устранения закольцовываний, даже если таблица была недавно очищена для целей восстановления свободного места. Установка же низких значений ведёт к агрессивному и более частому вакуумированию таблицы.

Единственный недостаток увеличения autovacuum_freeze_max_age (и в сочетании с vacuum_freeze_table_age) заключается в том, что подкаталоги pg_xact и pg_commit_ts кластера базы данных будут занимать больше места, поскольку они должны хранить статус фиксации и (если track_commit_timestamp включена) временную метку всех транзакций возрастом меньше чем autovacuum_freeze_max_age. Состояние фиксации использует два бита на транзакцию, поэтому, если для autovacuum_freeze_max_age установлено максимально допустимое значение в два миллиарда, можно ожидать, что pg_xact вырастет примерно до половины гигабайта, а pg_commit_ts - примерно до 20 ГБ. Если это малозначимо по сравнению с вашим общим размером базы данных, рекомендуется установить для autovacuum_freeze_max_age максимально допустимое значение. В противном случае установите его в зависимости от того, что вы хотите разрешить для pg_xact и pg_commit_ts. (Значение по умолчанию, 200 миллионов транзакций, означает около 50 МБ хранилища pg_xact и около 2 ГБ хранилища pg_commit_ts).

Одним из недостатков уменьшения vacuum_freeze_min_age является то, что это может привести к тому, что VACUUM сделает бесполезную работу: замораживания версии строки - пустая трата времени, если строка будет вскоре изменена (что приведет к приобретению строкой нового XID). Таким образом, настройка должна быть достаточно большой, чтобы строки не были заморожены до тех пор, пока они, скорее всего, не изменятся.

Чтобы отследить возраст самых старых незамерзших XID в базе данных, VACUUM сохраняет статистику XID в системных таблицах pg_class и pg_database. В частности, столбец relfrozenxid в строке таблицы pg_class содержит XID отсечки замораживания, который использовался последним агрессивным VACUUM для этой таблицы. Все строки, вставленные транзакциями с XID старше этого XID с отсечкой, гарантированно будут заморожены. Точно так столбец datfrozenxid строки базы данных pg_database является нижней границей незамерзших XID, появляющихся в этой базе данных, — это только минимум значений relfrozenxid каждой таблицы в базе данных. Удобный способ проверить эту информацию - выполнить такие запросы:

SELECT c.oid::regclass as table_name,
       greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind IN ('r', 'm');

SELECT datname, age(datfrozenxid) FROM pg_database;

Столбец age показывает количество транзакций от XID среза до XID текущей транзакции.

VACUUM обычно сканирует только те страницы, которые были изменены с момента последнего вакуума, но relfrozenxid может быть расширен только после сканирования каждой страницы таблицы, которая может содержать незамёрзшие XID. Это происходит, когда relfrozenxid больше, чем vacuum_freeze_table_age транзакции, когда используется опция FREEZE VACUUM, или когда все страницы, которые еще не полностью заморожены, требуют очистки для удаления версий мертвых строк. Когда VACUUM сканирует каждую страницу в таблице, которая еще не полностью заморожена, ему следует установить значение для age(relfrozenxid) чуть превышающее vacuum_freeze_min_age. Если VACUUM, устанавливающий relfrozenxid, не будет выдан на таблицу до тех пор, пока autovacuum_freeze_max_age не будет достигнут, то для таблицы будет автоматически запущена автоочистка.

Если по какой-то причине автоочистке не удастся удалить старые XID из таблицы, система начнет выдавать предупреждающие сообщения, подобные этому, когда самые старые XID базы данных достигнут десяти миллионов транзакций с точки прохода VACUUM:

WARNING:  database "mydb" must be vacuumed within 177009986 transactions
HINT:  To avoid a database shutdown, execute a database-wide VACUUM in "mydb".

(Ручной VACUUM должен решить проблему, как подсказано в сообщении, но учтите, что VACUUM должен выполняться суперпользователем, иначе он не сможет обрабатывать системные каталоги и, следовательно, не сможет продвигать datfrozenxid базы данных). Если эти предупреждения игнорируются, система завершит работу и откажется начинать любые новые транзакции, если до завершения процедуры останется менее 1 миллиона транзакций:

ERROR:  database is not accepting commands to avoid wraparound data loss in database "mydb"
HINT:  Stop the postmaster and vacuum that database in single-user mode.

Запас безопасности в 1 миллион транзакций предусмотрен, чтобы позволить администратору восстановиться без потери данных, выполнив вручную необходимые команды VACUUM. Однако, поскольку система не будет выполнять команды после перехода в режим безопасного выключения, единственный способ сделать это - остановить сервер и запустить сервер в однопользовательском режиме для выполнения VACUUM.

Мультитранзакции и зацикливание

Идентификаторы мультитранзакций используются для поддержки блокировки строк несколькими транзакциями. Поскольку в заголовке кортежа имеется только ограниченное пространство для хранения информации о блокировке, эта информация кодируется как «идентификатор множественной транзакции» или идентификатор мультитранзакции, когда существует более одной транзакции, одновременно блокирующей строку. Информация о том, какие идентификаторы транзакций включены в любой конкретный идентификатор мультитранзакции, хранится отдельно в подкаталоге pg_multixact, и в поле xmax в заголовке кортежа отображается только идентификатор мультитранзакции. Как и идентификаторы транзакций, идентификаторы мультитранзакций реализованы в виде 32-разрядного счетчика и соответствующего хранилища, что требует тщательного управления устареванием, очистки хранилища и обработки изменений при зацикливаниях. Существует отдельная область хранения, которая содержит список элементов для каждой мультитранзакции, которая также использует 32-разрядный счетчик и которым также необходимо управлять.

Всякий раз, когда VACUUM сканирует какую-либо часть таблицы, он заменяет любой обнаруженный им идентификатор мультитранзакции, который старше, чем vacuum_multixact_freeze_min_age, на другое значение, которое может быть нулевым значением, идентификатором отдельной транзакции или новым идентификатором мультитранзакции. Для каждой таблицы pg_class.relminmxid хранит самый старый из возможных идентификаторов мультитранзакций, которые все еще присутствуют в любом кортеже этой таблицы. Если это значение старше, чем vacuum_multixact_freeze_table_age, агрессивный вакуум запускается принудительно. Как обсуждалось в предыдущем разделе, агрессивный вакуум означает, что будут пропущены только те страницы, про которые известно, что они полностью заморожены. Функция mxid_age() может использоваться для pg_class.relminmxid для определения возраста.

Агрессивное сканирование VACUUM, независимо от того, что его вызывает, позволяет повысить доступность дискового пространства. В конце концов, так как все таблицы во всех базах данных сканируются и их самые старые значения мультитранзакций обновляются, пространство на диске для старых файлов мультитранзакций может быть освобождено.

В качестве защитного устройства агрессивное сканирование VACUUM будет выполняться для любой таблицы, возраст мультитранзакций которой больше, чем autovacuum_multixact_freeze_max_age. Агрессивное VACUUM сканирование также будет происходить постепенно для всех таблиц, начиная с тех, которые имеют самые старые мультитранзакции, если объем используемого пространства хранения элементов превышает 50% адресуемого пространства хранения. Оба этих вида агрессивного сканирования будут происходить даже в том случае, если автоочистка формально выключена.

Процесс «Автовакуум»

QHB имеет необязательную, но настоятельно рекомендуемую функцию, называемую Автовакуум (autovacuum), цель которой - автоматизировать выполнение команд VACUUM и ANALYZE. При включении autovacuum проверяет таблицы, в которые было вставлено, обновлено или удалено большое количество кортежей. Эти проверки используют средства сбора статистики; поэтому, autovacuum нельзя использовать, если для track_counts не установлено значение true. В конфигурации по умолчанию автоочистка включена, и соответствующие параметры конфигурации установлены соответствующим образом.

Процесс autovacuum на самом деле состоит из нескольких процессов. Существует постоянный фоновый процесс, называемый средством запуска autovacuum (autovacuum executor), который отвечает за запуск рабочих процессов autovacuum для всех баз данных. Модуль запуска будет распределять работу по времени, пытаясь запускать по одному процессу в каждой базе данных каждые autovacuum_naptime секунд. (Следовательно, если в установке имеется N баз данных, новый процесс будет запускаться каждые autovacuum_naptime/N секунд). Одновременно разрешено запускать максимум autovacuum_max_workers рабочих процессов. Если запущено больше баз данных, чем установленное значение autovacuum_max_workers, то следующая база данных будет обработана, как только закончится первый процесс. Каждый рабочий процесс будет проверять каждую таблицу в своей базе данных и выполнять VACUUM и/или ANALYZE по мере необходимости. log_autovacuum_min_duration может быть установлен для мониторинга активности процессов автоочистки.

Если несколько больших таблиц становятся доступны для вакуума за короткий промежуток времени, все процессы автоочистки, могут быть заняты очисткой в течение длительного времени. Это приведет к тому, что другие таблицы и базы данных не будут очищены до тех пор, пока процесс не станет доступным. Нет ограничений на количество процессов в одной базе данных, но процессы стараются избегать повторения уже выполненной работы. Обратите внимание, что количество работающих процессов не учитывается в рамках ограничений max_connections или superuser_reserved_connections.

Таблицы, у которых значение relfrozenxid больше, чем autovacuum_freeze_max_age старых транзакций, всегда очищаются VACUUM (это также относится к тем таблицам, чей максимальный срок замораживания был изменен с помощью параметров хранилища; см. ниже). В противном случае, если количество кортежей, устаревших с момента последнего VACUUM, превышает «порог», таблица очищается VACUUM. Порог вакуума (vacuum threshold) определяется как:

vacuum threshold = vacuum base threshold + vacuum scale factor * number of tuples

где базовый порог вакуума (vacuum base threshold) - autovacuum_vacuum_threshold, коэффициент масштабирования вакуума (vacuum scale factor)- autovacuum_vacuum_scale_factor, а число кортежей (number of tuples) - pg_class.reltuples. Количество устаревших кортежей которое получается из сборщика статистики - это примерный расчет, обновляемый каждой операцией UPDATE и DELETE. (Расчет приблизительный, потому, что некоторая информация может быть потеряна при большой нагрузке). Если значение relfrozenxid в таблице больше, чем vacuum_freeze_table_age транзакции, агрессивный вакуум выполняется для замораживания старых кортежей и продвижения relfrozenxid, в противном случае сканируются только страницы, которые были изменены с момента последнего вакуума.

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

analyze threshold = analyze base threshold + analyze scale factor * number of tuples

сравнивается с общим количеством вставленных, обновленных или удаленных кортежей со времени последнего ANALYZE.

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

Пороговые значения по умолчанию и масштабные коэффициенты взяты из qhb.conf, но их можно переопределить (и многие другие параметры управления автоочисткой) для каждой таблицы; см. Параметры хранения для получения дополнительной информации. Если параметр управляющий autovacuum был изменен с помощью параметров хранения таблицы, это значение используется при обработке этой таблицы - в противном случае используются глобальные настройки. См. раздел Автоматическая очистка для более подробной информации о глобальных настройках.

Когда работают несколько процессов autovacuum, параметры задержки затрат на автоочистку (см. раздел Задержка очистки по стоимости) «сбалансированы» между всеми работающими процессами, так что общее влияние ввода/вывода на систему одинаково независимо от количества фактически работающих процессов, Однако любые рабочие таблицы, для которых были установлены собственные параметры хранения autovacuum_vacuum_cost_delay или autovacuum_vacuum_cost_limit, не учитываются в алгоритме балансировки.

Процессы autovacuum обычно не блокируют другие команды. Если процесс попытается получить блокировку, которая конфликтует с блокировкой SHARE UPDATE EXCLUSIVE удерживаемой автоочисткой, получение блокировки прервет autovacuum. Конфликтующие режимы блокировки см. в таблице. Однако, если автоочистка работает для предотвращения закольцовывания идентификатора транзакции (т. е. имя запроса автоочистки в представлении pg_stat_activity заканчивается текстом to prevent wraparound), автоочистка автоматически не прерывается.

Предупреждение!!!
Регулярно выполняемые команды, которые получают блокировки, конфликтующие с блокировкой SHARE UPDATE EXCLUSIVE (например, ANALYZE), могут эффективно предотвращать завершение autovacuum.

Регулярная переиндексация

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

Страницы индекса B-tree, которые стали полностью пустыми, возвращаются для повторного использования. Однако все еще существует вероятность неэффективного использования пространства: если все, кроме нескольких индексных ключей на странице, были удалены, страница остается выделенной. Следовательно, шаблон использования, при котором большинство, но не все ключи в каждом диапазоне в конечном итоге будут удалены, приведет к неэффективному использованию пространства. Для таких моделей использования рекомендуется периодическая переиндексация.

Потенциал раздувания (bloat) в индексах отличных от B-tree не был достаточно исследован. Рекомендуется периодически отслеживать физический размер индекса при использовании любого типа индекса, отличного от B-tree.

Кроме того, для индексов B-tree доступ к недавно сконструированному индексу немного быстрее, чем к индексу, который много раз обновлялся, потому что логически смежные страницы обычно также физически соседствуют во вновь созданном индексе. (Это соображение не относится к индексам, не относящимся к B-tree). Возможно, есть смысл периодически переиндексировать таблицы для повышения скорости доступа.

REINDEX может использоваться легко и безопасно во всех случаях. Эта команда требует блокировки ACCESS EXCLUSIVE по умолчанию, поэтому часто предпочтительно выполнять ее с параметром CONCURRENTLY, который требует только блокировки SHARE UPDATE EXCLUSIVE.

Обслуживание файла журнала

Хорошей идеей будет сохранить куда-нибудь вывод журнала сервера базы данных, а не просто отбрасывать его через /dev/null. Вывод журнала очень важен при диагностике проблем. Однако вывод журнала имеет тенденцию к росту объемов (особенно на более высоких уровнях детализации), поэтому его нет смысла сохранять его бесконечно долго. Желательно настроить файлы журнала так, чтобы новые файлы журнала запускались, а старые удалялись через разумный период времени.

Если вы просто направите stderr процесса qhb в файл, у вас будет вывод журнала, но единственный способ обрезать файл журнала — это остановить и перезапустить сервер. Это может быть приемлемо, если вы используете QHB в среде разработки, но немногие продукционные серверы сочтут это поведение приемлемым.

Лучшим подходом является отправка вывода stderr сервера в какую-либо программу ротации журналов. Существует встроенная logging_collector ротации журналов, которую вы можете использовать, установив для параметра конфигурации logging_collector значение true в qhb.conf. Параметры управления для этой программы описаны в разделе Куда протоколировать. Вы также можете использовать этот подход для захвата данных журнала в машиночитаемом формате CSV (значения, разделенные запятыми).

В качестве альтернативы вы можете использовать внешнюю программу ротации журналов, если у вас есть такая, которую вы уже используете с другим серверным программным обеспечением. Например, инструмент rotatelogs, включенный в дистрибутив Apache, может использоваться с QHB. Один из способов сделать это - направить вывод сервера stderr в нужную программу. Если вы запускаете сервер с qhb_ctl, то stderr уже перенаправлен на stdout, поэтому вам просто нужна команда pipe, например:

qhb_ctl start | rotatelogs /var/log/pgsql_log 86400

Вы можете объединить эти подходы, настроив logrotate для сбора файлов журналов, создаваемых встроенным сборщиком журналов QHB. В этом случае сборщик журналов определяет имена и расположение файлов журналов, а logrotate периодически архивирует эти файлы. При запуске ротации журнала logrotate должен убедиться, что приложение отправляет дальнейшие выходные данные в новый файл. Обычно это делается с помощью сценария postrotate который отправляет сигнал SIGHUP приложению, которое затем повторно открывает файл журнала. В QHB вы можете запустить qhb_ctl с опцией logrotate. Когда сервер получает эту команду, сервер либо переключается на новый файл журнала, либо повторно открывает существующий файл, в зависимости от конфигурации ведения журнала (см. Раздел Куда протоколировать).

Заметка
При использовании статических имен файлов журнала сервер может не открыть файл журнала, если достигнут максимальный лимит открытых файлов в системе или произошло переполнение таблицы файлов. В этом случае сообщения журнала отправляются в старый файл до успешной ротации журнала. Если logrotate настроен на сжатие файла журнала и его удаление, сервер может потерять сообщения, зарегистрированные в этот период времени. Чтобы избежать этой проблемы, вы можете настроить сборщик журналов на динамическое назначение имен файлов журналов и использовать сценарий предварительной prerotate чтобы игнорировать открытые файлы журналов.

Другой производственный подход к управлению выводом журнала - отправить его в системный журнал и позволить системному журналу работать с ротацией файлов. Для этого установите для параметра конфигурации log_destination значение syslog (для вывода только в syslog) в qhb.conf. Затем вы можете отправить сигнал SIGHUP демону syslog всякий раз, когда вы хотите заставить его начать запись нового файла журнала. Если вы хотите автоматизировать ротацию журналов, программа logrotate может быть настроена для работы с файлами журналов на системном уровне.

Однако во многих установках системный журнал не очень надежен, особенно с большими сообщениями, он может обрезать или отбрасывать сообщения именно тогда, когда они вам нужны больше всего. Кроме того, в Linux системный журнал сбрасывает каждое сообщение на диск, что приводит к снижению производительности. (Вы можете использовать «-» в начале имени файла в файле конфигурации системного журнала, чтобы выключить синхронизацию).

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

Резервное копирование и восстановление

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

SQL-дамп

Резервное копирование на уровне файловой системы

Непрерывное архивирование

У каждого есть свои сильные и слабые стороны, которые обсуждается в следующих разделах.

SQL-дамп

Идея этого метода дампа состоит в том, чтобы создать файл с SQL-командами, которые при применении на сервере воссоздают базу данных в том же состоянии, в котором она находилась во время создания дампа. QHB предоставляет для этой цели служебную программу qhb_dump. Простейшее использование этой программы выглядит следующим образом:

qhb_dump dbname > dumpfile

qhb_dump записывает свой результат в стандартный вывод. Ниже мы увидим, когда это может быть полезно. В то время как вышеуказанная команда создаёт текстовый файл, qhb_dump может создавать файлы в других форматах, которые обеспечивают параллелизм и более детальный контроль над восстановлением объектов.

qhb_dump — это обычное клиентское приложение QHB. Это означает, что можно выполнить процедуру резервного копирования с любого удалённого хоста, который имеет доступ к базе данных. Но помните, что qhb_dump не работает с использованием специальных привилегий. В частности, требуется доступ на чтение ко всем таблицам, для которых выполняется резервное копирование, поэтому для резервного копирования всей базы данных почти всегда требуется запуск с правами суперпользователя базы данных.

Заметка
Если у вас недостаточно прав для резервного копирования всей базы данных, вы всё равно можете создавать резервные копии тех частей базы данных, к которым у вас есть доступ, используя такие параметры, как -n schema или -t table).

Чтобы указать, к какому серверу баз данных должен обращаться qhb_dump, используйте параметры командной строки -h host и -p port. Хост по умолчанию — это локальный хост или то значение, которое указано в переменной среды PGHOST. Точно так же порт по умолчанию указывается переменной окружения PGPORT или, если она не задана, значением, принятым по умолчанию.

Заметка
При компиляции сервер обычно имеет те же значения параметров по умолчанию

Как и любое другое клиентское приложение QHB, qhb_dump по умолчанию соединяется с базой данных используя имя пользователя, которое совпадает с текущим именем пользователя операционной системы. Чтобы переопределить это, либо укажите опцию -U либо установите переменную окружения PGUSER. Помните, что соединения qhb_dump подчиняются обычным механизмам аутентификации клиента.

Важное преимущество qhb_dump перед другими методами резервного копирования, описанными ниже, заключается в том, что выходные данные qhb_dump, как правило, могут загружаться в более новые версии QHB, тогда как резервные копии на уровне файлов и непрерывное архивирование являются исключительно специфичными для версии сервера. qhb_dump также является единственным методом, который будет работать при переносе базы данных на другую архитектуру компьютера, например при переходе с 32-разрядного на 64-разрядный сервер.

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

Заметка
Исключением являются те операции, которым требуется полная блокировка, например, большинство форм ALTER TABLE.

Восстановление дампа

Текстовые файлы, созданные qhb_dump, предназначены для чтения программой psql. Общая форма команды для восстановления дампа:

psql dbname < dumpfile

где dumpfile — это файл, содержащий вывод команды qhb_dump. Эта команда не будет создавать базу данных dbname, поэтому вы должны создать её самостоятельно из template0 перед выполнением psql (например, с помощью createdb -T template0 dbname). psql поддерживает параметры, аналогичные qhb_dump, для указания сервера базы данных, к которому нужно подключиться, и имени пользователя для использования (см. подробнее в psql). Дампы нетекстовых файлов восстанавливаются с помощью утилиты qhb_restore.

Перед восстановлением дампа SQL все пользователи, которым принадлежали объекты или права на них в выгруженной базе данных, уже должны существовать. Если их нет, при восстановлении не удастся воссоздать объекты с первоначальным владельцем и / или правами. (Иногда это желаемое поведение, но обычно это не так).

По умолчанию сценарий psql будет продолжать выполняться после возникновения ошибки SQL. Если запустить psql с переменной ON_ERROR_STOP установленной для изменения этого поведения, то psql завершится кодом 3, если возникает ошибка SQL:

psql --set ON_ERROR_STOP=on dbname < dumpfile

В любом случае, у вас будет только частично восстановленная база данных. В качестве альтернативы вы можете указать, что весь дамп должен быть восстановлен за одну транзакцию, поэтому восстановление будет либо полностью завершено, либо полностью отменено. Этот режим может быть указан путём передачи параметров командной строки -1 или --single-transaction в psql. При использовании этого режима помните, что даже небольшая ошибка может послужить причиной отката восстановления, которое уже выполняется в течение многих часов. Однако это все же может быть предпочтительнее, чем ручная очистка сложной базы данных после частично восстановленного дампа.

Возможность qhb_dump и psql использовать каналы ввода/вывода позволяет скопировать базу данных напрямую с одного сервера на другой, например:

qhb_dump -h host1 dbname | psql -h host2 dbname

Важно!!!
Дампы, создаваемые qhb_dump, относятся к template0. Это означает, что любые языки, процедуры и т. д., добавленные через template1, также будут выгружены qhb_dump. В результате при восстановлении, если вы используете настроенный template1, вы должны создать пустую базу данных из template0, как в примере выше.

После восстановления резервной копии целесообразно запустить ANALYZE для каждой базы данных, чтобы оптимизатор запросов имел полезную статистику. См. раздел Обновление статистики планировщика и раздел Процесс «Автовакуум» для получения дополнительной информации. Дополнительные советы о том, как эффективно загружать большие объёмы данных в QHB, см. в разделе Заполнение базы данных.

Использование qhb_dumpall

qhb_dump одновременно создаёт дамп только одной базы данных, который не содержит информацию о ролях или табличных пространствах (потому что они относятся к экземпляру, а не к базе данных). Для создания удобного дампа всего содержимого экземпляра базы данных предусмотрена программа qhb_dumpall. qhb_dumpall выполняет резервное копирование каждой базы данных в данном кластере, а также сохраняет данные для всего кластера, такие как определения ролей и табличных пространств. Простой вариант использования этой программы:

qhb_dumpall > dumpfile

Полученный дамп можно восстановить с помощью psql:

psql -f dumpfile qhb

Заметка
На самом деле вы можете указать любое существующее имя базы данных для запуска, но если вы загружаете в пустой экземпляр, обычно следует использовать qhb.

Восстановление дампа qhb_dumpall необходимо производить с правами суперпользователя, поскольку это требуется для восстановления информации о ролях и табличных пространствах. Если вы используете табличные пространства, убедитесь, что их пути в дампе соответствуют новой среде.

qhb_dumpall работает, выполняя команды для воссоздания ролей, табличных пространств и пустых баз данных, затем вызывая qhb_dump для каждой базы данных. Это означает, что хотя каждая база данных будет внутренне согласованной, снимки разных баз данных не синхронизируются.

Данные всего экземпляра могут быть выгружены отдельно, используя опцию --globals-only. Это необходимо для полного резервного копирования экземпляра при выполнении команды qhb_dump в отдельных базах данных.

Обработка больших баз данных

Некоторые операционные системы имеют ограничения по максимальному размеру файла, которые вызывают проблемы при создании больших выходных файлов qhb_dump. К счастью, qhb_dump может записывать в стандартный вывод, поэтому вы можете использовать стандартные инструменты Unix для решения этой потенциальной проблемы. Есть несколько возможных методов:

Используйте сжатые дампы. Вы можете использовать вашу предпочитаемую программу сжатия, например, gzip:

qhb_dump dbname | gzip > filename.gz

Восстановление дампа с помощью:

gunzip -c filename.gz | psql dbname

или:

cat filename.gz | gunzip | psql dbname

Команда split позволяет разбить вывод на более мелкие файлы, приемлемые по размеру для базовой файловой системы. Например, чтобы сделать куски по 1 мегабайту:

qhb_dump dbname | split -b 1m - filename

Восстановление дампа с помощью:

cat filename* | psql dbname

Используйте специальный формат дампа qhb_dump. Если QHB был собран в системе с установленной библиотекой сжатия zlib, пользовательский формат дампа будет сжимать данные по мере их записи в выходной файл. Это приведет к размеру файла дампа, аналогичному использованию gzip, но у него есть дополнительное преимущество: таблицы могут восстанавливаться выборочно. Следующая команда создаёт дамп базы данных с использованием специального формата дампа:

qhb_dump -Fc dbname > filename

Дамп специального формата не является сценарием для psql, а должен быть восстановлен с помощью qhb_restore, например:

qhb_restore -d dbname filename

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

Используйте функцию параллельного дампа qhb_dump. Чтобы ускорить дамп большой базы данных, вы можете использовать параллельный режим qhb_dump. Этот режим позволит сбросить несколько таблиц одновременно. Вы можете контролировать количество потоков с помощью параметра -j. Параллельные дампы поддерживаются только для архива в формате каталога.

qhb_dump -j num -F d -f out.dir dbname

Вы можете использовать qhb_restore -j для параллельного восстановления дампа. Это будет работать для любого архива, созданном в специальном формате или в формате каталога, независимо от того, был ли он создан с помощью qhb_dump -j.

Резервное копирование на уровне файловой системы

Альтернативная стратегия резервного копирования заключается в прямом копировании файлов, которые QHB использует для хранения данных в базе данных; Вы можете использовать любой метод для резервного копирования файловой системы; например:

tar -cf backup.tar /var/lib/qhb/data

Однако есть два ограничения, которые делают этот метод непрактичным или, по крайней мере, уступают методу qhb_dump:

Сервер базы данных должен быть выключен, чтобы получить правильную резервную копию. Промежуточные меры, такие как запрещение всех подключений, работать не будут (отчасти потому, что tar и подобные инструменты не делают атомарный снимок состояния файловой системы, но также из-за внутренней буферизации внутри сервера). Информацию об остановке сервера можно найти в разделе завершение работы сервера. Излишне говорить, что вам также необходимо выключить сервер перед восстановлением данных.

Если вы вникли в детали структуры файловой системы базы данных, у вас может возникнуть соблазн попытаться выполнить резервное копирование или восстановление только определенных отдельных таблиц или баз данных из их соответствующих файлов или каталогов. Это не будет работать, потому что информация, содержащаяся в этих файлах, не может быть использована без файлов журнала транзакций pg_xact/*, которые содержат статус фиксации всех транзакций. Файл таблицы может использоваться только с этой информацией. Конечно, также невозможно восстановить только таблицу и связанные с pg_xact данные, потому что это сделает все остальные таблицы в экземпляре базы данных бесполезными. Таким образом, резервные копии файловой системы работают только для полного резервного копирования и восстановления всего экземпляра базы данных.

Альтернативный подход к резервному копированию файловой системы заключается в создании «согласованного снимка» каталога данных, если файловая система поддерживает эту функцию (и вы уверены, что она реализована правильно). Типичная процедура - сделать «замороженный снимок» тома, содержащего базу данных, затем скопировать весь каталог данных (не только части, см. выше) из моментального снимка на устройство резервного копирования, а затем освободить замороженный снимок. Это будет работать даже во время работы сервера базы данных. Однако созданная таким образом резервная копия сохраняет файлы базы данных в таком состоянии, как если бы сервер базы данных был неправильно остановлен; поэтому, когда вы запускаете сервер базы данных с резервной копией данных, он будет считать, что предыдущий экземпляр сервера завершился аварийно, и применит данные журналов WAL. Это не проблема, однако это стоит иметь в виду.

Внимание!!!
Обязательно включите файлы WAL в свою резервную копию!

Заметка
Вы можете выполнить CHECKPOINT перед созданием снимка, чтобы сократить время восстановления.

Если ваша база данных распределена по нескольким файловым системам, то способа получить строго одновременно замороженные снимки всех томов может не оказаться. Например, если ваши файлы данных и журнал WAL находятся на разных дисках, или если табличные пространства находятся в разных файловых системах, может оказаться невозможным использование резервного копирования «моментального снимка», поскольку моментальные снимки должны быть одновременными. Внимательно прочитайте документацию по файловой системе, прежде чем доверять технологии согласованных снимков в таких ситуациях.

Если согласованные снимки невозможны, один из вариантов - отключить сервер базы данных на достаточно длительное время, чтобы скопировать все замороженные снимки. Другим вариантом является выполнение непрерывного архивирования (раздел Создание базовой резервной копии), поскольку такие резервные копии не пострадают от изменений файловой системы во время резервного копирования. Это требует работы непрерывного архивирования на время процесса резервного копирования. Восстановление выполняется с использованием непрерывного восстановления архива (См. раздел Восстановление с помощью непрерывного архива).

Другой вариант - использовать rsync для резервного копирования файловой системы. Для этого сначала нужно запустить rsync во время работы сервера базы данных, а затем завершить работу сервера базы данных на время, достаточное, чтобы выполнить rsync --checksum. (--checksum необходим, потому что rsync различает время с точностью до секунды). Второй запуск rsync отработает быстрее, чем первый, потому что ему останется относительно мало данных для передачи, и конечный результат будет согласованным, поскольку сервер был выключен. Этот метод позволяет выполнять резервное копирование файловой системы с минимальным временем простоя.

Обратите внимание, что резервная копия файловой системы обычно больше, чем дамп SQL. (Например, qhb_dump не нужно записывать содержимое индексов, только команды для их восстановления). Однако создание резервной копии файловой системы может выполняться быстрее.

Непрерывное архивирование и восстановление на момент времени (PITR)

QHB поддерживает журнал упреждающей записи (WAL) в подкаталоге pg_wal/ каталога данных экземпляра. В журнал записываются все изменения, внесённые в файлы данных базы. Этот журнал существует главным образом в целях обеспечения безопасности при сбоях: в случае сбоя системы базы данных могут быть восстановлена до состояния согласованности путём «воспроизведения» записей журнала, созданных с момента последней контрольной точки. Однако наличие журнала позволяет использовать третью стратегию резервного копирования баз данных: можно объединить резервное копирование на уровне файловой системы с резервным копированием файлов WAL. Если требуется восстановление, то восстанавливается резервная копия файловой системы, а затем воспроизводятся изменения из резервных копий файлов WAL, чтобы привести систему к актуальному состоянию. Этот подход сложнее в администрировании, чем любой из предыдущих подходов, но он имеет некоторые существенные преимущества:

не требуется идеально согласованное резервное копирование файловой системы в качестве отправной точки. Любое внутреннее несоответствие в резервной копии будет исправлено путём воспроизведения журнала (это существенно не отличается от того, что происходит во время восстановления после сбоя). Поэтому не нужна возможность создания снимков файловой системы, просто tar или аналогичный инструмент архивирования.

Поскольку можно комбинировать неограниченно длинную последовательность файлов WAL для воспроизведения, непрерывное резервное копирование может быть достигнуто простым продолжением архивирования файлов WAL. Это особенно ценно для больших баз данных, где не всегда удобно делать полное резервное копирование.

Нет необходимости воспроизводить записи WAL до самого конца. Можно остановить воспроизведение в любой момент и получить согласованный снимок базы данных на заданный момент времени. Таким образом, этот метод поддерживает восстановление на определенный момент времени: восстановление базы данных до её состояния возможно в любое время с момента создания резервной копии базы.

Если непрерывно передавать серии файлов WAL на другой компьютер, который был загружен с одними и теми же базовыми данными резервной копии, то появляется система тёплого резервирования: в любой момент можно запустить второй сервер, и у него будет практически текущая копия база данных.

Заметка
qhb_dump и qhb_dumpall не создают резервные копии на уровне файловой системы и не могут использоваться как часть решения для непрерывного архивирования. Это логические дампы, и они не содержат достаточно информации для использования при воспроизведении WAL.

Как и в случае простого метода резервного копирования файловой системы, этот метод может поддерживать только восстановление всего экземпляра базы данных, но не его части. Кроме того, для этого требуется большое архивное хранилище: базовая резервная копия может быть громоздкой, а занятая система будет генерировать много мегабайт трафика WAL, который необходимо архивировать. Тем не менее, это предпочтительный метод резервного копирования во многих ситуациях, когда требуется высокая надёжность.

Для успешного восстановления с использованием непрерывного архивирования (также называемого «оперативным резервным копированием» многими разработчиками баз данных) нужна непрерывная последовательность архивированных WAL-файлов история которой, по крайней мере, начинается со времени начала резервного копирования. Итак, для начала необходимо настроить и протестировать процедуру архивации файлов WAL, прежде чем делать первую базовую резервную копию. Соответственно, сначала рассмотрим механизм архивирования файлов WAL.

Настройка архивирования WAL

В абстрактном смысле работающая система QHB создаёт бесконечно длинную последовательность записей WAL. Система физически делит эту последовательность на файлы сегментов WAL, которые обычно имеют размер 16 МБ (хотя размер сегмента можно изменить во время создания нового кластера базы данных через параметры initdb). Сегменты получают числовые имена, которые отражают их положение в абстрактной последовательности WAL. Когда архивация WAL не используется, система обычно создаёт только несколько файлов сегментов, а затем «перезаписывает» их, меняя имена ставших ненужными файлов WAL на новые, с бо́льшими номерами. Предполагается, что файлы сегментов, содержимое которых предшествует последней контрольной точке, больше не представляют интереса и могут быть использованы заново.

При архивировании данных WAL нам нужно захватить содержимое каждого файла сегмента после его заполнения и сохранить эти данные где-то перед тем, как файл сегмента будет повторно использован. В зависимости от приложения и доступного оборудования, может быть много разных способов «сохранения данных где-то»: мы можем скопировать файлы сегментов в каталог, смонтированный NFS на другом компьютере, записать на ленточный накопитель (гарантируя, что у вас есть способ идентификации исходного имени каждого файла), или собрать вместе и записать на компакт-диски, или что-то ещё. Чтобы предоставить администратору базы данных гибкость, QHB старается не делать никаких предположений о том, как будет осуществляться архивирование. Вместо этого QHB позволяет администратору указать команду оболочки, которая будет выполняться, чтобы скопировать заполненный файл сегмента туда, куда он должен попасть. Команда может быть такой же простой, как cp, или может вызывать сложный сценарий оболочки - все зависит от администратора.

Чтобы включить архивирование WAL, установите для параметра конфигурации wal_level значение replica или выше, для параметра archive_mode - значение on и укажите команду оболочки для использования в параметре конфигурации archive_command. На практике эти настройки всегда будут помещаться в файл qhb.conf. В archive_command %p заменяется путём к файлу для архивирования, а %f заменяется только именем файла. (Путь указывается относительно текущего рабочего каталога, т. е. каталога данных экземпляра). Используйте %% если вам нужно вставить символ % в команду. Самая простая полезная команда выглядит примерно так:

archive_command = 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'  # Unix

она скопирует архивируемые сегменты WAL в каталог /mnt/server/archivedir. (Это пример, а не рекомендация, и он может работать не на всех платформах). После замены параметров %p и %f действительная команда может выглядеть следующим образом:

test ! -f /mnt/server/archivedir/00000001000000A900000065 && cp qhb_wal/00000001000000A900000065 /mnt/server/archivedir/00000001000000A900000065

Аналогичная команда будет сгенерирована для каждого нового файла, подлежащего архивированию.

Команда архивирования будет выполняться под управлением того же пользователя, на котором работает сервер QHB. Поскольку серия архивируемых файлов WAL содержит практически всё, что есть в вашей базе данных, вы должны быть уверены, что заархивированные данные защищены от посторонних глаз. Например, архивируйте в каталог, с ограниченными правами доступа на чтение для группы и других пользователей.

Важно, чтобы команда архивирования возвращала нулевой статус выхода, только если копирование произошло успешно. Получив нулевой результат, QHB предположит, что файл был успешно заархивирован, и удалит или переиспользует его. Однако ненулевой статус говорит QHB, что файл не был заархивирован и будут периодически предприниматься попытки его архивации заново, пока это не удастся.

Обычно команда архивирования должна предотвращать перезапись любого ранее существующего архивного файла. Это важная функция безопасности для сохранения целостности вашего архива в случае ошибки администратора (например, отправка вывода двух разных серверов в один и тот же каталог архива).

Рекомендуется протестировать предложенную вами команду архивирования, чтобы убедиться, что она действительно не перезаписывает существующий файл, а если это так, то возвращает ненулевой статус. Приведённый выше пример команды для Unix обеспечивает это путём включения отдельного шага test. На некоторых платформах Unix в cp есть такие переключатели, как -i, которые можно использовать для выполнения тех же операций менее явно, но вы не должны полагаться на них, не убедившись, что возвращён правильный код статуса. (В частности, GNU cp вернет нулевой код статуса, когда используется -i и целевой файл уже существует, что не является желаемым поведением).

При разработке вашей конфигурации архивации рассмотрите, что произойдёт, если команда архивирования не будет выполнена повторно, потому что какие-то обстоятельства требуют вмешательства оператора или архиву не хватает места. Например, это может произойти, если вы пишете на ленту без автоматической смены; когда лента заполняется, ничто больше не может быть заархивировано, пока лента не будет заменена. Вы должны убедиться, что о любой ошибке или запросе оператору-человеку сообщается надлежащим образом, чтобы ситуация могла быть разрешена достаточно быстро. pg_wal/ будет продолжать заполняться файлами сегментов WAL, пока ситуация не будет решена. (Если файловая система, содержащая pg_wal/ заполняется, QHB завершит работу аварийно. Никакие зафиксированные транзакции не будут потеряны, но база данных останется в автономном режиме, пока вы не освободите некоторое пространство).

Скорость команды архивации не имеет значения, пока она может соответствовать средней скорости, с которой ваш сервер генерирует данные WAL. Нормальная работа продолжается, даже если процесс архивирования немного отстаёт. Если архивирование значительно отстаёт, это увеличит объём данных, которые могут быть потеряны в случае аварии. Это также будет означать, что pg_wal/ будет содержать большое количество ещё не заархивированных файлов сегментов, которые в конечном итоге могут превысить доступное дисковое пространство. Рекомендуется следить за процессом архивации, чтобы убедиться, что он работает так, как вы рассчитываете.

При написании команды архивирования вы должны исходить из того, что имена файлов, подлежащих архивированию, могут быть длиной до 64 символов и могут содержать любую комбинацию букв ASCII, цифр и точек. Нет необходимости сохранять исходный относительный путь (%p), но необходимо сохранить имя файла (%f).

Обратите внимание, что несмотря на то, что архивация WAL позволит вам восстановить любые изменения, внесенные в данные в вашей базе данных QHB, она не восстановит изменения, внесённые в файлы конфигурации (то есть qhb.conf, qhb_hba.conf и qhb_ident.conf), так как они редактируются вручную, а не с помощью операций SQL. Возможно, вы захотите сохранить файлы конфигурации в том же месте, где будут храниться результаты ваших обычных процедур резервного копирования файловой системы. расположение файлов

Команда архивирования вызывается только для завершённых сегментов WAL. Следовательно, если ваш сервер генерирует только небольшой трафик WAL (или имеет периоды простоя, когда это происходит), может возникнуть длительная задержка между завершением транзакции и ее безопасной записью в архивное хранилище. Чтобы установить ограничение на срок хранения неархивированных данных, вы можете установить archive_timeout, чтобы заставить сервер переключаться на новый файл сегмента WAL так часто, насколько это необходимо. Обратите внимание, что архивные файлы, которые архивируются раньше из-за принудительного переключения, имеют ту же длину, что и полностью заполненные файлы. Поэтому неразумно устанавливать очень короткое значение archive_timeout — это приведёт к переполнению вашего архивного хранилища. Параметр archive_timeout установленный на минутный промежуток или около того обычно бывает корректен.

Кроме того, можно принудительно переключить сегмент с помощью pg_switch_wal если вы хотите, чтобы только что завершённая транзакция была заархивирована как можно скорее. Другие функции утилит, относящиеся к управлению WAL, перечислены в таблице 1.

Таблица 1. Функции управления резервным копированием

Имя | Тип ответа | Описание
pg_create_restore_point(name text) | pg_lsn | Создать именованную точку для выполнения восстановления*
pg_current_wal_flush_lsn() | pg_lsn | Получить текущее местоположение сброса журнала предзаписи.
pg_current_wal_insert_lsn() | pg_lsn | Получить текущее местоположение вставки журнала предзаписи.
pg_current_wal_lsn() | pg_lsn | Получить текущее местоположение записи в журнал предзаписи.
pg_start_backup(label text [, fast boolean [, exclusive boolean ] ]) | pg_lsn | Подготовление к выполнению резервному копированию*
pg_stop_backup() | pg_lsn | Завершить выполнение монопольного резервного копирования*
pg_stop_backup(exclusive boolean [, wait_for_archive boolean ]) | set of record | Завершить выполнение монопольного резервного копирования*
pg_is_in_backup() | bool | Истинно, если монопольное резервное копирование все еще выполняется.
pg_backup_start_time() | timestamp with time zone | Получить время запуска монопольного резервного копирования.
pg_switch_wal() | pg_lsn | Принудительное переключение на новый файл журнала с опережением записи*
pg_walfile_name(lsn pg_lsn) | text | Преобразовать местоположение журнала предзаписи в имя файла.
pg_walfile_name_offset(lsn pg_lsn) | text, integer | Преобразовать местоположение журнала предзаписи в имя файла и десятичное смещение байта в нём.
pg_wal_lsn_diff(lsn pg_lsn, lsn pg_lsn) | numeric | Рассчитать разницу между двумя местоположениями журнала записи.

Заметка
* - (по умолчанию ограничено суперпользователями, но другим пользователям может быть предоставлено разрешение EXECUTE для запуска функции).

Когда wal_level - minimal некоторые команды SQL оптимизированы, чтобы избежать ведения журнала WAL. Если архивация или потоковая репликация были включены во время выполнения одного из этих операторов, WAL не будет содержать достаточно информации для восстановления архива. (На восстановление после аварийного завершения это не распространяется). По этой причине wal_level может быть изменен только при запуске сервера. Тем не менее, параметр archive_command может быть изменен с перезагрузкой файла конфигурации. Если вы хотите временно остановить архивирование, один из способов сделать это - установить для archive_command пустую строку (’ ’). Это приведет к тому, что файлы WAL будут накапливаться в pg_wal/ пока не будет восстановлена рабочая команда archive_command.

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

Самый простой способ выполнить базовое резервное копирование - использовать инструмент qhb_basebackup. Он может создавать базовую резервную копию в виде обычных файлов или в виде архива tar. Альтернативой этому инструменту служит qbackup. Эта программа позволяет сохранять ваши резервные копии в структурированный каталог, при этом поддерживается инкрементальное копирование и сжатие, а также параллельный режим работы.

Если требуется больше гибкости, чем могут обеспечить эти программы, то вы также можете сделать базовую резервную копию, используя низкоуровневый API (см. раздел Создание базовой резервной копии с использованием API низкого уровня).

Нет необходимости беспокоиться о количестве времени, необходимого для создания базовой резервной копии. Однако если вы обычно запускаете сервер с выключенными full_page_writes, вы можете заметить падение производительности во время резервного копирования, так как full_page_writes включается автоматически в режиме резервного копирования.

Чтобы в последующем использовать резервную копию, вам необходимо сохранить все файлы сегментов WAL, созданные во время и после резервного копирования файловой системы. Чтобы помочь вам в этом, процесс базового резервного копирования создаёт файл истории резервного копирования, который немедленно сохраняется в области архивации WAL. Этот файл назван как первый файл сегмента WAL, который необходим для резервного копирования файловой системы. Например, если начальный файл WAL - 0000000100001234000055CD файл истории резервного копирования будет иметь имя, например, 0000000100001234000055CD.007C9330.backup. (Вторая часть имени файла обозначает точную позицию в файле WAL и обычно может игнорироваться). После того, как вы благополучно заархивировали резервную копию файловой системы и файлы сегментов WAL, использованные во время резервного копирования (как указано в истории резервного копирования файла), все архивные сегменты WAL с численно меньшими именами больше не нужны для восстановления резервной копии файловой системы и могут быть удалены. Однако вам следует подумать о том, чтобы сохранить несколько наборов резервных копий, чтобы быть абсолютно уверенным, что возможно восстановить нужные данные.

Файл истории резервного копирования — это небольшой текстовый файл. Он содержит строку метки, которую вы дали qhb_basebackup, а также начальное и конечное время и имена начального и конечного сегментов WAL, относящихся к резервной копии. Если вы использовали метку для идентификации связанного файла дампа, то заархивированного файла истории достаточно, чтобы указать, какой файл дампа нужно восстановить.

Поскольку вы должны хранить все архивные файлы WAL начиная с первой базовой резервной копии, интервал между повторными резервными копированиями копиями обычно следует выбирать исходя из того, сколько памяти вы хотите потратить на архивные файлы WAL. Вам также следует учитывать, сколько времени вы готовы потратить на восстановление, ведь система должна будет воспроизвести все эти сегменты WAL, а это может занять некоторое время, если прошло много времени с момента последнего резервного копирования базы.

Создание базовой резервной копии с использованием API низкого уровня

Процедура создания базовой резервной копии с использованием низкоуровневых API-интерфейсов содержит на несколько шагов больше, чем метод qhb_basebackup или qbackup, но относительно проста. Очень важно, чтобы эти шаги выполнялись последовательно, и чтобы каждый шаг был успешен перед переходом к следующему шагу.

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

Создание немонопольной резервной копии на низком уровне

Немонопольное резервное копирование низкого уровня позволяет запускать другие параллельные резервные копии (через API-интерфейс или qhb_basebackup).

Убедитесь, что архивация WAL включена и работает.

Подключитесь к серверу (не имеет значения, к какой базе данных) как пользователь с правами на запуск pg_start_backup (суперпользователь или пользователь, которому предоставлена привилегия EXECUTE для функции) и выполните команду:

SELECT pg_start_backup('label', false, false);

где label - любая строка, которую вы хотите использовать в качестве идентификатора этой операции резервного копирования. Соединение, вызывающее pg_start_backup должно поддерживаться до конца резервного копирования, иначе резервное копирование будет автоматически прервано.

По умолчанию pg_start_backup может занять много времени до завершения. Это связано с тем, что он выполняет контрольную точку, а операции ввода-вывода, требуемые для этого, распределяются в интервале времени, равного по умолчанию половине вашего интервала между контрольными точками (см. параметр конфигурации checkpoint_completion_target в разделе Контрольные точки). Обычно это то, что и требуется, потому что это минимизирует влияние на обработку запросов. Если вы хотите начать резервное копирование как можно скорее, измените второй параметр на true, который немедленно выдаст контрольную точку с использованием максимально возможного количества операций ввода-вывода.

Третий параметр false указывает pg_start_backup инициировать немонопольную базовую резервную копию.

Выполните резервное копирование, используя любой удобный инструмент для резервного копирования файловой системы, такой как tar или cpio (не qhb_dump или qhb_dumpall). Нет необходимости останавливать нормальную работу базы данных, пока вы делаете это. См. раздел Резервное копирование каталога данных о том, какие нюансы следует учитывать при выполнении резервного копирования.

В том же соединении, что и раньше, введите команду:

SELECT * FROM pg_stop_backup(false, true);

Это прекращает режим резервного копирования. На ведущем сервере также выполняется автоматическое переключение на следующий сегмент WAL. В режиме ожидания невозможно автоматическое переключение сегментов WAL, поэтому вы можете запустить pg_switch_wal чтобы выполнить ручное переключение. Цель переключения заключается в том, чтобы последний файл сегмента WAL, записанный в течение интервала резервного копирования, был готов к архивированию.

pg_stop_backup вернет одну строку с тремя значениями. Второе из этих полей должно быть записано в файл с именем backup_label в корневом каталоге резервной копии. Третье поле должно быть записано в файл с именем tablespace_map если поле не пустое. Эти значения крайне важны для резервного копирования и должны быть записаны без изменений.

Как только файлы сегментов WAL, активные во время резервного копирования, будут заархивированы, всё готово. Файл, идентифицируемый первым возвращаемым значением pg_stop_backup является последним сегментом, который требуется для формирования полного набора файлов резервных копий. Если параметр archive_mode включен и параметр wait_for_archive функции pg_stop_backup равен true, pg_stop_backup не выполнится до тех пор, пока не будет заархивирован последний сегмент. На ведомом сервере параметр archive_mode при этом должен иметь значение always. Архивирование этих файлов происходит автоматически, так как вы уже настроили archive_command. В большинстве случаев это происходит быстро, но рекомендуется следить за системой архивирования, чтобы убедиться в отсутствии задержек. Если процесс архивирования отстал из-за сбоев команды архивирования, он будет повторять попытки до тех пор, пока архив не будет успешно завершен и резервное копирование не будет завершено. Если вы хотите установить ограничение по времени выполнения pg_stop_backup, установите соответствующее значение statement_timeout, но учтите, что если pg_stop_backup завершит работу из-за этого, ваша резервная копия может потерять целостность.

Если процесс резервного копирования отслеживает и гарантирует, что все файлы сегментов WAL, необходимые для резервного копирования, успешно заархивированы, то для параметра wait_for_archive (по умолчанию устанавливается значение true) можно установить значение false, чтобы pg_stop_backup завершался, как только запись остановки резервного копирования записывается в WAL. По умолчанию pg_stop_backup будет ждать, пока все WAL будут заархивированы, что может занять некоторое время. Эту опцию следует использовать с осторожностью: если архивирование WAL не контролируется должным образом, резервная копия может не включать все файлы WAL и, следовательно, будет неполной и не сможет быть восстановлена.

Создание монопольной резервной копии на низком уровне

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

Процесс для монопольной резервной копии в основном такой же, как и для немонопольной, но отличается в нескольких ключевых шагах. Этот тип резервного копирования может выполняться только на первичном сервере и не допускает одновременного резервного копирования. Более того, поскольку он создает файл метки резервной копии, как описано ниже, он может заблокировать автоматический перезапуск главного сервера после сбоя. С другой стороны, ошибочное удаление этого файла из резервной копии является распространенной ошибкой, которая может привести к серьезному повреждению данных. Если необходимо использовать этот метод, могут быть выполнены следующие шаги.

Убедитесь, что архивация WAL включена и работает.

Подключитесь к серверу (не имеет значения, какая база данных) как пользователь с правами на запуск pg_start_backup (суперпользователь или пользователь, которому предоставлена ​​привилегия EXECUTE для функции) и выполните команду:

SELECT pg_start_backup('label');

где label - любая строка, которую вы хотите использовать для уникальной идентификации этой операции резервного копирования. pg_start_backup создает файл метки резервной копии с именем backup_label в каталоге кластера с информацией о вашей резервной копии, включая время начала и строку метки. Функция также создает файл карты табличных пространств, называемый tablespace_map , в каталоге кластера с информацией о символических ссылках табличных пространств в pg_tblspc/, если присутствует одна или несколько таких ссылок. Оба файла имеют решающее значение для целостности резервной копии, если вам необходимо восстановить базу из нее.

По умолчанию вызов pg_start_backup может занять довольно продолжительное время. Это связано с тем, что при этом выполняется контрольная точка, и ввод-вывод, необходимый для выполнения контрольной точки, будет распределен в течение значительного периода времени, по умолчанию в течение половины установленного интервала между контрольными точками (см. параметр конфигурации checkpoint_completion_target в разделе Контрольные точки). Обычно это то, что требуется, так как это минимизирует влияние на обработку запросов. Если вы хотите начать резервное копирование как можно скорее, используйте:

SELECT pg_start_backup ('label', true);

Это заставляет контрольную точку выполниться как можно быстрее.

Выполните резервное копирование, используя любой удобный инструмент для резервного копирования файловой системы, такой как tar или cpio (не pg_dump или pg_dumpall ). Нет необходимости останавливать нормальную работу базы данных, пока вы делаете это. См. раздел Резервное копирование каталога данных о том, какие нюансы следует учитывать при выполнении резервного копирования.

Как отмечалось выше, если во время резервного копирования происходит сбой сервера, перезапуск может оказаться невозможным, пока файл backup_label не будет удален вручную из каталога PGDATA . Обратите внимание, что очень важно никогда не удалять файл backup_label при восстановлении резервной копии, поскольку это приведет к повреждению. Путаница в том, когда следует удалить этот файл, является частой причиной повреждения данных при использовании этого метода; убедитесь, что вы удаляете файл только на существующем главном сервере и никогда не удаляйте его при создании резервного сервера или при восстановлении из резервной копии, даже если вы создаете резервный сервер, который впоследствии будет преобразован в новый главный сервер.

Снова подключитесь к базе данных как пользователь с правами на запуск pg_stop_backup (суперпользователь или пользователь, которому предоставлена привилегия EXECUTE для этой функции) и выполните команду:

SELECT pg_stop_backup();

Эта функция завершает режим резервного копирования и выполняет автоматический переход к следующему сегменту WAL. Цель переключения заключается в том, чтобы последний сегмент WAL, записанный в течение интервала резервного копирования, был бы готов к архивированию.

Как только файлы сегментов WAL, активные во время резервного копирования, будут заархивированы, процедура резервирования будет завершена. WAL-файл, идентифицируемый результатом выполнения pg_stop_backup , является последним сегментом, который требуется для формирования полного набора файлов резервных копий. Если опция archive_mode включена, pg_stop_backup не завершается, пока не будет заархивирован последний сегмент. Архивирование этих файлов происходит автоматически, поскольку уже должна быть настроена команда archive_command. В большинстве случаев это происходит быстро, но рекомендуется следить за системой архивирования, чтобы убедиться в отсутствии задержек. Если процесс архивирования отстал из-за сбоев команды архивирования, он будет повторять попытки до тех пор, пока архивация не будет успешно завершена, и только в этом случае резервное копирование закончится.

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

Резервное копирование каталога данных

Некоторые инструменты резервного копирования файловой системы выдают предупреждения или ошибки, если файлы, которые они пытаются скопировать, изменяются в процессе копирования. При создании базовой резервной копии активной базы данных это нормальная ситуация, а не ошибка. Однако вы должны убедиться, что вы можете отличить логи такого рода от реальных ошибок. Например, некоторые версии rsync возвращают отдельный код завершения для «исчезнувших исходных файлов», и вы можете написать скрипт, чтобы принять этот код завершения как случай, не связанный с ошибкой. Кроме того, некоторые версии GNU tar возвращают код ошибки, неотличимый от фатальной ошибки, если файл был усечён во время его копирования через tar. К счастью, GNU tar версии 1.16 и выше завершается с кодом 1, если файл был изменен во время резервного копирования, и 2 для других ошибок.

Заметка
В GNU tar версии 1.23 и более поздних версиях вы можете использовать параметры предупреждений --warning=no-file-changed, --warning=no-file-removed, чтобы скрыть соответствующие предупреждающие сообщения.

Убедитесь, что ваша резервная копия содержит все файлы из каталога экземпляра базы данных (например, /var/lib/qhb/data). Если вы используете табличные пространства, которые не находятся под этим каталогом, будьте осторожны и включайте их (убедитесь, что ваша резервная копия архивирует символические ссылки в виде ссылок, иначе восстановление повредит ваши табличные пространства).

Однако вы должны исключить из резервной копии файлы в подкаталоге экземпляра pg_wal/. Эта небольшая корректировка имеет смысл, поскольку снижает риск ошибок при восстановлении. Это легко организовать, если pg_wal/ является символической ссылкой, указывающей куда-то за пределы каталога экземпляра, что в любом случае является обычной настройкой из соображений производительности. Вы также можете исключить qhbmaster.pid и postmaster.opts, которые записывают информацию о работающем qhbmaster, а не о qhbmaster, который в конечном итоге будет использовать эту резервную копию. (Эти файлы могут запутать qhb_ctl).

Также стоит исключать из резервной копии каталог pg_replslot/ кластера, чтобы слоты репликации, существующие на главном сервере, не попадали в копию. В противном случае последующее использование резервной копии на резервном сервере может привести к неопределенному сроку хранения файлов WAL в резервной системе и, возможно, к раздутию на главном сервере, если включена обратная связь с горячим резервом, поскольку клиенты, использующие эти слоты репликации, все равно будут подключаться и обновлять слоты на мастере, а не на резервном сервере. Даже если резервная копия предназначена только для использования при создании нового мастера, копирование слотов репликации не будет особенно полезным, поскольку содержимое этих слотов, вероятно, будет сильно устаревшим к тому времени, когда новый мастер войдет в сеть.

Содержимое каталогов pg_dynshmem/, pg_notify/, pg_serial/, pg_snapshots/, pg_stat_tmp/ и pg_subtrans/ (но не сами каталоги) может быть исключено из резервной копии, поскольку оно будет инициализировано при запуске qhbmaster. Если stats_temp_directory (см. Статистика во время выполнения) установлен и указывает на подкаталог внутри каталога данных, то содержимое этого каталога также может быть опущено.

Любой файл или каталог, начинающийся с pgsql_tmp может быть опущен из резервной копии. Эти файлы удаляются при запуске qhbmaster, а каталоги восстанавливаются по мере необходимости.

Файлы pg_internal.init могут быть исключены из резервной копии всякий раз, когда найден файл с таким именем. Эти файлы содержат данные кэша отношений, которые всегда восстанавливаются при восстановлении.

Файл метки резервной копии содержит строку метки, которую вы задали во время вызова pg_start_backup, а также имя начального файла WAL. Поэтому в случае путаницы можно заглянуть внутрь архива резервной копии и точно определить, в каком сеансе резервного копирования он был получен. Файл карты табличных пространств содержит имена символических ссылок, поскольку они существуют в каталоге pg_tblspc/ и полный путь каждой символической ссылки. Эти файлы не только для информации, их наличие и содержание имеют решающее значение для правильной работы процесса восстановления системы.

Также возможно сделать резервную копию, когда сервер остановлен, например с помощью qbackup. В этом случае вы, очевидно, не можете использовать pg_start_backup или pg_stop_backup, и поэтому вы будете предоставлены вашим собственным устройствам для отслеживания того, какая резервная копия является какой, и как далеко назад идут связанные файлы WAL (qbackup делает это автоматически). Как правило, лучше следовать процедуре непрерывного архивирования, описанной выше.

Восстановление с помощью непрерывного архива

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

Остановите сервер, если он работает.

Если у вас есть место для этого, скопируйте весь каталог данных экземпляра и все табличные пространства во временную папку на случай, если они понадобятся вам позже. Обратите внимание, что эта мера предосторожности потребует, чтобы у вас было достаточно свободного места в вашей системе для хранения двух копий вашей существующей базы данных. Если у вас недостаточно места, вы должны как минимум сохранить содержимое подкаталога экземпляра pg_wal, так как он может содержать журналы, которые не были заархивированы до завершения работы системы.

Удалите все существующие файлы и подкаталоги в каталоге данных экземпляра и в корневых каталогах всех используемых вами табличных пространств.

Восстановите файлы базы данных из резервной копии вашей файловой системы. Убедитесь, что они восстановлены с правами владельца (пользователя системы баз данных, а не root!) и с правами доступа. Если вы используете табличные пространства, вы должны убедиться, что символические ссылки в pg_tblspc/ были правильно восстановлены.

Удалите все файлы, присутствующие в pg_wal/, которые были получены из резервной копии потому что, вероятно, они устарели и не являются актуальными. Если вы не архивировали pg_wal/, то заново создайте его с надлежащими разрешениями, соблюдая осторожность, чтобы убедиться, что вы восстановили его в качестве символической ссылки, если вы его так настроили ранее.

Если у вас есть разархивированные файлы сегментов WAL, которые вы сохранили на шаге 2, скопируйте их в pg_wal/. (Лучше всего их копировать, а не перемещать, т.к. у вас остаются неизмененные файлы, и, если возникает проблема, и придется начать заново).

Задайте параметры конфигурации восстановления в qhb.conf и создайте файл recovery.signal в каталоге данных экземпляра. Вы также можете временно изменить qhb_hba.conf, чтобы обычные пользователи не могли подключаться, пока вы не убедитесь, что восстановление прошло успешно.

Запустите сервер. Сервер перейдет в режим восстановления и продолжит чтение нужных ему архивированных файлов WAL. Если восстановление будет прервано из-за внешней ошибки, сервер можно просто перезапустить, он продолжит восстановление. По завершении процесса восстановления сервер удалит recovery.signal (для предотвращения случайного повторного входа в режим восстановления позже), а затем начнет обычные операции с базой данных.

Проверьте содержимое базы данных, чтобы убедиться, что вы восстановились до нужного состояния. Если нет, вернитесь к шагу 1. Если все в порядке, разрешите пользователям подключаться, восстановив qhb_hba.conf в нормальном состоянии.

Ключевой частью всего этого является настройка конфигурации восстановления, которая описывает, как вы хотите восстановить и как далеко должно пройти восстановление. Единственное, что вы обязательно должны указать, это restore_command, которая сообщает QHB, как извлекать заархивированные сегменты файла WAL. Как и archive_command, это командная строка оболочки. Она может содержать %f, который заменяется именем нужного файла журнала, и %p, который заменяется путём, по которому нужно скопировать файл журнала. (Путь указывается относительно текущего рабочего каталога, т.е. каталога данных экземпляра). Напишите %%, если вам нужно вставить фактический символ % в команду. Самая простая полезная команда выглядит примерно так:

restore_command = 'cp /mnt/server/archivedir/%f %p'

который скопирует ранее заархивированные сегменты WAL из каталога /mnt/server/archivedir. Конечно, вы можете использовать что-то гораздо более сложное, возможно, даже сценарий оболочки, который просит оператора смонтировать соответствующую ленту. Например, qbackup делает это автоматически, используя встроенную подкоманду.

Важно, чтобы команда возвращала ненулевой статус выхода при ошибке. Эта команда будет вызвана так же для запроса файлов, которых нет в архиве, она должна вернуть ненулевое значение, если их нет. Однако это не должно считаться за ошибку. Исключением является, если команда была прервана сигналом (отличным от SIGTERM, который используется для отключения сервера базы данных) или ошибкой оболочки (например, команда не найдена), то восстановление будет прервано, и сервер не запустится.

Не все запрошенные файлы будут файлами сегментов WAL; также следует ожидать запросов на файлы с суффиксом .history. Также помните, что базовое имя пути %p будет отличаться от %f, не ожидайте, что они будут взаимозаменяемыми.

Сегменты WAL, которые нельзя найти в архиве, будут искать в pg_wal/; это позволяет использовать последние неархивированные сегменты. Однако сегменты, доступные из архива, будут использоваться вместо файлов в pg_wal/, так как имеют приоритет над ними.

Обычно восстановление выполняется через все доступные сегменты WAL, тем самым восстанавливая базу данных до текущего момента времени (или максимально близко, учитывая доступные сегменты WAL). Поэтому нормальное восстановление заканчивается сообщением «файл не найден», точный текст сообщения об ошибке зависит от выбранного вами параметра restore_command. Вы также можете увидеть сообщение об ошибке в начале восстановления для файла с именем что-то вроде 00000001.history. Это также нормально и не указывает на проблему в простых ситуациях восстановления (см. подраздел Линии времени)

Если вы хотите восстановить данные до некоторого предыдущего момента времени (скажем, непосредственно перед тем, как младший администратор баз данных отбросил вашу основную таблицу транзакций), просто укажите необходимую точку остановки. Вы можете указать точку остановки, известную как «целевая точка восстановления», либо по дате/времени, названной точки восстановления, либо по завершению транзакции c определенным идентификатором. (подробную информацию см. в подразделе Целевая точка восстановления)

Заметка
Точка остановки должна быть указана после времени окончания основного резервного копирования, то есть времени окончания pg_stop_backup. Нельзя использовать базовую резервную копию для восстановления до того времени, когда эта резервная копия выполнилась. (Чтобы восстановиться до такого времени, вы должны вернуться к своей предыдущей базовой резервной копии и начать восстановление оттуда).

Заметка
В команде restore в qbackup существуют опции, позволяющие выбрать эти настройки. Ими можно будет воспользоваться только если резервная копия была сделана при помощи backup

Если восстановление обнаружит поврежденные данные WAL, восстановление будет остановлено в этот момент, и сервер не запустится. В таком случае процесс восстановления может быть перезапущен с самого начала с указанием «цели восстановления» до точки повреждения, чтобы восстановление могло завершиться нормально. Если восстановление завершится неудачно по внешней причине, такой как сбой системы или если архив WAL стал недоступен, то восстановление можно просто перезапустить, и оно будет перезапущено почти с того места, где произошел сбой. Перезапуск восстановления работает так же, как контрольная точка в обычной работе: сервер периодически передает все свое состояние на диск, а затем обновляет файл pg_control, чтобы указать, что уже обработанные данные WAL не нужно снова сканировать.

Линии времени

Возможность восстановить базу данных до предыдущего момента времени создаёт некоторые сложности, сродни научно-фантастическим рассказам о путешествиях во времени и параллельных вселенных. Например, в исходной истории базы данных предположим, что вы удалили критическую таблицу в 17:15 во вторник вечером, но не осознали свою ошибку до полудня среды. Однако вы можете получить свою резервную копию, и восстановиться к моменту времени 17:14 во вторник и запустить сервер. В этой истории базы данных вы никогда не удаляли таблицу. Но, предположим, позже вы поймете, что это не такая уж хорошая идея, и необходимо вернуться к утру среды из оригинальной истории. К сожалению. в данной ситуации этого сделать уже не получится, т.к. пока ваша база данных была запущена и работала, она перезаписала некоторые файлы сегментов WAL, которые вели к тому времени, к которому вы теперь хотели бы вернуться. Таким образом, чтобы избежать этого, необходимо отличать серии записей WAL, сгенерированные после восстановления на определенный момент времени, от тех, которые были сгенерированы в исходной истории базы данных.

Чтобы справиться с этой проблемой, в QHB есть понятие линия времени. Всякий раз, когда восстановление архива завершается, создаётся новая временная линия для определения последовательности записей WAL, созданной после этого восстановления. Идентификационный номер линии времени является частью имён файлов сегментов WAL, поэтому новая линия времени не перезаписывает данные WAL, созданные предыдущими линиями времени. Фактически возможно архивировать много различных линий времени. Хотя это может показаться бесполезной функцией, это часто помогает. Рассмотрим ситуацию, когда вы не совсем уверены, к какому моменту времени необходимо восстанавливаться, и поэтому вам приходится делать несколько попыток восстановления на определенный момент времени методом проб и ошибок, пока не найдется лучшее место для ветвления из старой истории. Без линий времени этот процесс быстро приведет к неуправляемому беспорядку. С помощью линий времени вы можете восстановить любое предыдущее состояние, в том числе состояния в ветвях линий времени, от которых вы отказались ранее.

Каждый раз, когда создаётся новая линия времени, QHB создаёт файл «истории линии времени», который показывает, с какой линии времени он вышел и когда. Эти файлы истории необходимы для того, чтобы система могла выбрать правильные файлы сегментов WAL при восстановлении из архива, который содержит несколько линий времени. Поэтому они архивируются в область архивирования WAL, как и файлы сегментов WAL. Файлы истории — это просто небольшие текстовые файлы, поэтому их целесообразно хранить на протяжении долгого времени (в отличие от больших файлов сегментов). Вы можете, если хотите, добавлять комментарии в файлы истории, чтобы записывать свои собственные заметки о том, как и почему была создана эта конкретная линия времени. Такие комментарии будут особенно полезны, когда в результате экспериментов у вас будет череда различных линий времени.

По умолчанию восстановление происходит на ту же линию времени, которая была текущей, когда была сделана базовая резервная копия. Если вы хотите восстановить какую-либо дочернюю линию времени (то есть вы хотите вернуться к некоторому состоянию, которое само было получено после попытки восстановления), вам нужно указать целевой идентификатор временной шкалы в recovery_target_timeline.

Важно!!! Вы не можете восстановиться в состояние, которое находится в линии времени, ответвившейся раньше, чем базовая резервная копия началась.

Советы и примеры

Некоторые советы по настройке непрерывного архивирования приведены здесь.

Автономные горячие резервные копии

Для создания автономных оперативных резервных копий можно использовать средства резервного копирования QHB. Это резервные копии, которые нельзя использовать для восстановления на определенный момент времени, но, как правило, ими гораздо быстрее выполнять резервное копирование и восстановление, чем дампы qhb_dump. (Они также намного больше, чем дампы qhb_dump, поэтому в некоторых случаях преимущество в скорости может быть сведено на нет.)

Как и в случае базовых резервных копий, самый простой способ создать автономное горячее резервное копирование - использовать инструмент qhb_basebackup. Если при вызове задать параметр -X, весь журнал предварительной записи, необходимый для использования резервной копии, будет автоматически включен в резервную копию, и никаких специальных действий для её восстановления не требуется.

Если требуется больше гибкости при копировании файлов резервных копий, процесс более низкого уровня можно использовать и для автономных оперативных резервных копий. Чтобы подготовиться к низкоуровневым автономным горячим резервным копиям, убедитесь, что для wal_level задано значение replica или выше, для параметра archive_mode значение on, и настройте команду archive_command которая выполняет архивирование только при наличии файла переключателя. Например:

archive_command = 'test ! -f /var/lib/qhb/backup_in_progress || (test ! -f /var/lib/qhb/archive/%f && cp %p /var/lib/qhb/archive/%f)'

Заметка
В qbackup есть подкоманда backup-wal, которую можно использовать для этих целей см. подробнее в qbackup backup-wal

Эта команда выполняет архивирование, когда существует /var/lib/qhb/backup_in_progress, и в противном случае молча возвращает нулевое состояние выхода (что позволяет QHB переиспользовать ненужный файл WAL).

С помощью этой подготовки можно сделать резервную копию с помощью сценария, подобного следующему:

touch /var/lib/qhb/backup_in_progress
psql -c "select pg_start_backup('hot_backup');"
tar -cf /var/lib/qhb/backup.tar /var/lib/qhb/data/
psql -c "select pg_stop_backup();"
rm /var/lib/qhb/backup_in_progress
tar -rf /var/lib/qhb/backup.tar /var/lib/qhb/archive/

Файл-переключатель /var/lib/qhb/backup_in_progress создаётся первым, что позволяет архивировать заполненные файлы WAL. После резервного копирования файл переключателя удаляется. Архивные файлы WAL затем добавляются в резервную копию, чтобы как базовая резервная копия, так и все необходимые файлы WAL были частью одного и того же tar-файла. Пожалуйста, не забудьте добавить обработку ошибок в ваши скрипты резервного копирования.

Существует альтернативный подход к горячему резервному копированию в виде утилиты qbackup. Эта утилита комбинирует архивирование WAL-записей и постраничное инкрементальное резервное копирование, что позволяет добиться более компактных резервных копий за счёт сохранения только тех данных, которые изменились с момента предыдущей автономной резервной копии.

Сжатие архивных журналов

Если размер архивного хранилища имеет значение, вы можете использовать gzip для сжатия архивных файлов:

archive_command = 'gzip < %p > /var/lib/qhb/archive/%f'

Затем вам нужно будет использовать gunzip во время восстановления:

restore_command = 'gunzip < /mnt/server/archivedir/%f > %p'

Скрипты archive_command

Многие люди предпочитают использовать сценарии для определения своей команды archive_command, поэтому их запись в qhb.conf выглядит очень просто:

archive_command = 'local_backup_script.sh "%p" "%f"'

Использование отдельного файла сценария целесообразно тогда, когда вы хотите использовать более одной команды в процессе архивирования. Это позволяет управлять всей сложностью скрипта, который может быть написан на популярном языке сценариев, таком как bash или perl.

Примеры задач, которые могут быть решены в сценарии:

Копирование данных для безопасного хранения данных за пределами площадки.

Пакетное копирование WAL, так что они передаются каждые три часа, а не по одному.

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

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

Заметка
При использовании сценария archive_command желательно включить logging_collector. Любые сообщения, записанные в сценарий stderr, будут затем отображаться в журнале сервера базы данных, что позволяет легко диагностировать сложные конфигурации в случае сбоев.

Заметка
Вы можете использовать qbackup backup-wal для этих целей.

Предостережения

На момент написания статьи существует несколько ограничений техники непрерывного архивирования. Это, вероятно, будет исправлено в следующих выпусках:

Если команда CREATE DATABASE выполняется во время создания базовой резервной копии, а затем шаблонная база данных, которую скопировала CREATE DATABASE изменяется, пока базовая резервная копия все ещё выполняется, возможно, что восстановление приведёт к распространению этих изменений на созданную базу данных. Это, конечно, нежелательно. Чтобы избежать этого риска, лучше не изменять шаблонные базы данных при создании базовой резервной копии.

Команды CREATE TABLESPACE регистрируются в WAL с буквальным абсолютным путём и поэтому будут воспроизведены как создания табличного пространства с тем же абсолютным путём. Это может быть нежелательно, если журнал воспроизводится на другом компьютере. Это может быть опасно, даже если журнал воспроизводится на том же компьютере, но в новом каталоге данных: воспроизведение все равно перезапишет содержимое исходного табличного пространства. Чтобы избежать потенциальных ошибок такого рода, рекомендуется создавать новую базовую резервную копию после создания или удаления табличных пространств.

Следует также отметить, что формат WAL по умолчанию довольно громоздкий, поскольку включает в себя множество снимков страницы диска. Эти снимки страниц предназначены для поддержки восстановления после сбоя, поскольку нам может потребоваться исправить частично записанные страницы диска. В зависимости от аппаратного и программного обеспечения системы риск частичной записи может быть достаточно мал, чтобы его можно было игнорировать, и в этом случае вы можете значительно уменьшить общий объём архивированных журналов, выключив снимки страниц с помощью параметра full_page_writes. (Прочтите примечания и предупреждения к WAL перед тем, как сделать это.) Выключение снимков страницы не мешает использованию журналов для операций PITR. Возможность для последующего улучшения - сжатие архивных данных WAL путём удаления ненужных копий страниц, даже если установлен параметр full_page_writes. В то же время администраторы могут захотеть уменьшить количество снимков страниц, включенных в WAL, максимально увеличив параметры интервала контрольных точек.

Мониторинг активности базы данных

Администратор базы данных часто задается вопросом: "Что сейчас делает система"? В этой главе рассказывается, как это выяснить.

Доступны несколько инструментов для мониторинга активности базы данных и анализа производительности. Большая часть этой главы посвящена описанию сборщика статистики QHB , но не следует пренебрегать обычными программами мониторинга Unix, такими как ps, top, iostat и vmstat. Кроме того, как только вы определили неэффективный запрос, может потребоваться дальнейшее исследование с помощью команды EXPLAIN в QHB . В разделе Использование EXPLAIN обсуждается EXPLAIN и другие методы для понимания поведения отдельного запроса.

Стандартные инструменты Unix

В большинстве платформ Unix QHB изменяет заголовок своей команды, сообщаемый ps, так что отдельные процессы сервера могут быть легко идентифицированы. Пример вывода команды:

$ ps auxww | grep ^qhb
qhb  15551  0.0  0.1  57536  7132 pts/0    S    18:02   0:00 qhb -i
qhb  15554  0.0  0.0  57536  1184 ?        Ss   18:02   0:00 qhb: background writer
qhb  15555  0.0  0.0  57536   916 ?        Ss   18:02   0:00 qhb: checkpointer
qhb  15556  0.0  0.0  57536   916 ?        Ss   18:02   0:00 qhb: walwriter
qhb  15557  0.0  0.0  58504  2244 ?        Ss   18:02   0:00 qhb: autovacuum launcher
qhb  15558  0.0  0.0  17512  1068 ?        Ss   18:02   0:00 qhb: stats collector
qhb  15582  0.0  0.0  58772  3080 ?        Ss   18:04   0:00 qhb: joe runbug 127.0.0.1 idle
qhb  15606  0.0  0.0  58772  3052 ?        Ss   18:07   0:00 qhb: tgl regression [local] SELECT waiting
qhb   15610  0.0  0.0  58772  3056 ?        Ss   18:07   0:00 qhb : tgl regression [local] idle in transaction

(Формат вызова ps может отличаться для разных платформ, как и детали отображаемой информации. Этот пример приведен для одной из последних версий системы Linux.) Первым процессом из перечисленных здесь является процесс главного сервера. Аргументы команды, приведенные здесь для него, те же, что использовались при запуске. Следующие пять процессов являются фоновыми рабочими процессами, автоматически запускаемыми главным процессом. (Процесс «stats-collector» не будет показан, если его запуск выключен; аналогично процесс «autovacuum launcher» (фоновый процесс автоочистки) также можно выключить.) Каждый из оставшихся процессов является серверным процессом, обрабатывающим одно клиентское соединение. Для каждого такого процесса устанавливается отображение командной строки в виде

qhb: user database host activity

Элементы имени пользователя, базы данных и хоста остаются неизменными на протяжении всего жизненного цикла клиентского соединения, а индикатор активности изменяется. Он может принимать значение idle ( ожидание клиентской команды), idle in transaction (ожидание клиента внутри блока BEGIN) или имя типа команды, например, SELECT. Кроме того, добавляется waiting если серверный процесс в настоящее время ожидает высвобождения блокировки, которую удерживает другой сеанс. В приведенном выше примере мы можем сделать вывод, что процесс 15606 ожидает, пока процесс 15610 завершит свою транзакцию и тем самым снимет некоторую блокировку. (Процесс 15610 должен быть блокирующим, потому что нет другого активного сеанса. В более сложных случаях необходимо было бы обратиться к системному представлению pg_locks чтобы определить, кто кого блокирует.)

Если параметр cluster_name настроен, имя кластера также будет показано в выводе ps:

$ psql -c 'SHOW cluster_name'
 cluster_name
--------------
 server1
(1 row)

$ ps aux|grep server1
qhb   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 qhb: server1: background writer
...

Если вы выключили параметр update_process_title, то индикатор активности не обновляется; заголовок процесса устанавливается только один раз при запуске нового процесса. На некоторых платформах это снижает существенные накладные расходы, на других влияние обновления этого индикатора незначительно.

Сборщик статистики

Сборщик статистики QHB - это подсистема, которая поддерживает сбор и передачу информации о работе сервера. В настоящее время сборщик может учитывать обращения к таблицам и индексам как по дисковым блокам, так и по отдельным строкам. Он также отслеживает общее количество строк в каждой таблице, информацию о выполнении вакуума и анализирует действия для каждой таблицы. Также он может подсчитывать вызовы пользовательских функций и общее время, потраченное на каждую из них.

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

Конфигурация сбора статистики

Поскольку сбор статистики добавляет некоторые накладные расходы к выполнению запроса, система может быть настроена или не настроена на сбор информации. Это контролируется параметрами конфигурации, которые обычно устанавливаются в qhb.conf. (Подробнее о настройке параметров конфигурации см. в главе Конфигурация сервера).

Параметр track_activities позволяет отслеживать текущие команды, выполняемые любым серверным процессом.

Параметр track_counts контролирует, собирается ли статистика по обращениям к таблицам и индексам.

Параметр track_functions позволяет отслеживать использование пользовательских функций.

Параметр track_io_timing включает мониторинг времени чтения и записи блоков.

Обычно эти параметры устанавливаются в qhb.conf так, что они применяются ко всем процессам сервера, но их можно включить или выключить в отдельных сеансах с помощью команды SET. (Чтобы обычные пользователи не могли скрывать свою активность от администратора, только суперпользователи могут изменять эти параметры с помощью SET.)

Сборщик статистики передает собранные данные другим процессам QHB через временные файлы. Эти файлы хранятся в каталоге, название которого задается параметром stats_temp_directory, по умолчанию содержит значение pg_stat_tmp. Для повышения производительности stats_temp_directory может указывать на файловую систему на основе ОЗУ, что сокращает время физического ввода/вывода. Когда сервер отключается, постоянная копия статистических данных сохраняется в подкаталоге pg_stat, так что статистика может быть сохранена при перезапусках сервера. Когда восстановление выполняется при запуске сервера (например, после немедленного выключения, сбоя сервера и восстановления на определенный момент времени), все счетчики статистики сбрасываются.

Просмотр статистики

Несколько предопределенных представлений, перечисленных в Таблице 1, доступны для отображения текущего состояния системы. Есть также несколько других представлений, перечисленных в Таблице 2, доступных для отображения результатов сбора статистики. В качестве альтернативы можно создавать собственные представления с использованием базовых статистических функций, как описано в разделе Функции статистики.

При использовании статистики для мониторинга собранных данных важно понимать, что информация не обновляется мгновенно. Каждый отдельный процесс сервера передает новые статистические значения сборщику непосредственно перед тем, как перейти в режим ожидания; таким образом, запрос или транзакция, которая еще выполняется, не влияет на отображаемые итоги. Кроме того, сам сборщик генерирует новый отчет не чаще одного раза в PGSTAT_STAT_INTERVAL миллисекунд (500 мс, если значение параметра не было изменено при компиляции сервера). Таким образом, отображаемая информация отстает от фактической активности. Однако информация о текущем запросе, собираемая при установке параметра track_activities, всегда актуальна.

Другим важным моментом является то, что когда серверный процесс запрашивает статистические данные, он сначала получает самый последний снимок от сборщика статистики, а затем продолжает использовать этот снимок для всех статистических представлений и функций до окончания текущей транзакции. Таким образом, статистика не изменится до окончания текущей транзакции. Точно так же собирается информация о текущих запросах всех сеансов, когда она запрашивается в начале транзакции, и эта же информация будет отображаться в течение всей транзакции. Это функциональность, а не ошибка, она позволяет вам выполнять несколько запросов к статистике и сопоставлять результаты, не беспокоясь о том, что данные по статистике изменяются. Но если вы хотите видеть новые результаты при выполнении каждого запроса, обязательно выполняйте запросы вне транзакционных блоков. Или вы можете вызвать pg_stat_clear_snapshot(), что сбросит снимок статистики текущей транзакции (если он был). При следующем обращении к статистической информации будет получен новый снимок.

Транзакция также может видеть свою собственную статистику (пока еще не переданную сборщику) в представлениях pg_stat_xact_all_tables, pg_stat_xact_sys_tables, pg_stat_xact_user_tables и pg_stat_xact_user_functions. Эти данные ведут себя не так, как указано выше. Напротив, они постоянно обновляются на протяжении всей транзакции.

Некоторая информация в представлениях динамической статистики, показанных в Таблице 1, имеет ограничения по безопасности. Обычные пользователи могут видеть только всю информацию о своих собственных сеансах (сеансах, принадлежащих к роли, членом которой они являются). В строках о других сеансах многие столбцы будут нулевыми. Однако обратите внимание, что существование сеанса и его общие свойства, такие как пользователь сеанса и база данных, видны всем пользователям. Суперпользователи и члены встроенной роли pg_read_all_stats (см. также раздел Роли по умолчанию) могут видеть всю информацию обо всех сеансах.

Таблица 1. Динамические статистические представления

Имя представления | Описание
pg_stat_activity | Одна строка для каждого серверного процесса, отображающая информацию, связанную с текущей активностью этого процесса, например состояние и текущий запрос. См. pg_stat_activity.
pg_stat_replication | Одна строка на процесс отправителя WAL, показывающая статистику репликации на подключенный резервный сервер этого отправителя. См. pg_stat_replication.
pg_stat_wal_receiver | Только одна строка, показывающая статистику о приемнике WAL с подключенного сервера этого приемника. См. pg_stat_wal_receiver.
pg_stat_subscription | Как минимум одна строка на подписку, показывающая информацию о процессах подписки. См. pg_stat_subscription.
pg_stat_ssl | Одна строка для каждого подключения (обычного и реплицирующего), показывающая информацию о SSL, используемом для этого соединения См. pg_stat_ssl.
pg_stat_gssapi | Одна строка для каждого подключения (обычного и реплицирующего), показывающая информацию об аутентификации и шифровании GSSAPI, используемых в этом соединении. См. pg_stat_gssapi.
pg_stat_progress_create_index | Одна строка для каждого бэкенда, выполняющего CREATE INDEX или REINDEX, показывающая ход выполнения команды. См. раздел Отчет о ходе выполнения CREATE INDEX.
pg_stat_progress_vacuum | Одна строка для каждого бэкенда (включая рабочие процессы автоочистки), на котором выполняется VACUUM, показывающая ход выполнения команды. См. раздел Отчет о ходе выполнения VACUUM.
pg_stat_progress_cluster | Одна строка для каждого бэкенда, в котором работает CLUSTER или VACUUM FULL, показывающая ход выполнения команды. См. раздел Отчет о ходе выполнения CLUSTER.

Таблица 2. Представления по собранной статистике

Имя представления
Описание

pg_stat_archiver
Только одна строка, показывающая статистику о работе процесса архиватора WAL. См. pg_stat_archiver.

pg_stat_bgwriter
Только одна строка, показывающая статистику о работе фонового процесса записи. См. pg_stat_bgwriter.

pg_stat_database
Одна строка на базу данных, показывающая статистику всей базы данных. См. pg_stat_database.

pg_stat_database_conflicts
Одна строка на базу данных, показывающая статистику всей базы данных по отменам запросов из-за конфликта с восстановлением на резервных серверах. См. pg_stat_database_conflicts.

pg_stat_all_tables
Одна строка для каждой таблицы в текущей базе данных, показывающая статистику доступа к этой конкретной таблице. См. pg_stat_all_tables.

pg_stat_sys_tables
То же, что pg_stat_all_tables, за исключением того, что отображаются только системные таблицы.

pg_stat_user_tables
То же, что pg_stat_all_tables, за исключением того, что отображаются только пользовательские таблицы.

pg_stat_xact_all_tables
Аналогичен pg_stat_all_tables, но подсчитывает статистику по операциям, совершенным до настоящего момента в текущей транзакции (которые еще не включены в pg_stat_all_tables и связанные представления). Столбцы с количеством живых и мертвых строк, а также с данными по выполнению очистки и сбора статистики в этом представлении отсутствуют.

pg_stat_xact_sys_tables
То же, что pg_stat_xact_all_tables, за исключением того, что отображаются только системные таблицы.

pg_stat_xact_user_tables
То же, что pg_stat_xact_all_tables, за исключением того, что отображаются только пользовательские таблицы.

pg_stat_all_indexes
Одна строка для каждого индекса в текущей базе данных, показывающая статистику доступа к этому конкретному индексу. См. pg_stat_all_indexes.

pg_stat_sys_indexes
То же, что pg_stat_all_indexes, за исключением того, что pg_stat_all_indexes представлены только индексы в системных таблицах.

pg_stat_user_indexes
То же, что pg_stat_all_indexes, за исключением того, что pg_stat_all_indexes представлены только индексы пользовательских таблиц.

pg_statio_all_tables
Одна строка для каждой таблицы в текущей базе данных, показывающая статистику ввода-вывода для этой конкретной таблицы. См. pg_statio_all_tables.

pg_statio_sys_tables
То же, что pg_statio_all_tables, за исключением того, что отображаются только системные таблицы.

pg_statio_user_tables
То же, что pg_statio_all_tables, за исключением того, что отображаются только пользовательские таблицы.

pg_statio_all_indexes
Одна строка для каждого индекса в текущей базе данных, показывающая статистику ввода-вывода для этого конкретного индекса. См. pg_statio_all_indexes.

pg_statio_sys_indexes
То же, что pg_statio_all_indexes, за исключением того, что pg_statio_all_indexes представлены только индексы в системных таблицах.

pg_statio_user_indexes
То же, что pg_statio_all_indexes, за исключением того, что pg_statio_all_indexes представлены только индексы пользовательских таблиц.

pg_statio_all_sequences
Одна строка для каждой последовательности в текущей базе данных, показывающая статистику ввода-вывода для этой конкретной последовательности. См. pg_statio_all_sequence для получения подробной информации.

pg_statio_sys_sequences
То же, что pg_statio_all_sequences, за исключением того, что отображаются только системные последовательности. (В настоящее время системные последовательности не определены, поэтому это представление всегда пусто.)

pg_statio_user_sequences
То же, что pg_statio_all_sequences, за исключением того, что отображаются только пользовательские последовательности.

pg_stat_user_functions
Одна строка для каждой отслеживаемой функции, показывающая статистику выполнения этой функции. См. pg_stat_user_functions.

pg_stat_xact_user_functions
Аналогичен pg_stat_user_functions, но учитывает только вызовы во время текущей транзакции (которые еще не включены в pg_stat_user_functions ).

Статистика по индексу особенно полезна для определения того, какие индексы используются и насколько они эффективны.

Представления pg_statio_ в первую очередь полезны для определения эффективности буферного кэша. Когда количество фактических операций чтения с диска намного меньше числа обращений к буферу, кэш удовлетворяет большинству запросов на чтение без вызова ядра операционной системы. Однако эти статистические данные не дают полной картины: из-за того, как QHB обрабатывает дисковый ввод-вывод, данные, которых нет в буферном кэше QHB , могут по-прежнему находиться в кэше ввода/вывода ядра и, следовательно, могут по-прежнему выбираться, не требуя физического чтения. Пользователям, заинтересованным в получении более подробной информации о поведении ввода/вывода QHB , рекомендуется использовать сборщик статистики QHB в сочетании с утилитами операционной системы, которые позволяют лучше понять, как ядро обрабатывает ввод/вывод.

pg_stat_activity

В представлении pg_stat_activity для каждого серверного процесса будет находиться по одной строке, показывающей информацию, касающуюся текущей активности этого процесса.

Таблица 3. Представление pg_stat_activity

Тип столбца
Описание

datid oid
OID базы данных, с которой связан этот бэкенд

datname name
Имя базы данных, с которой связан этот бэкенд

pid integer
Идентификатор процесса этого бэкенда

usesysid oid
OID пользователя, вошедшего в этот бэкенд

usename name
Имя пользователя, вошедшего в этот бэкенд

application_name text
Имя приложения, которое подключено к этому бэкенду

client_addr inet
IP-адрес клиента, подключенного к этому бэкенду. Если это поле пустое, это означает, что клиент подключен через сокет Unix на серверном компьютере или это внутренний процесс, такой как autovacuum.

client_hostname text
Имя хоста подключенного клиента, сообщаемое при обратном поиске DNS client_addr. Это поле будет отличным от NULL для IP-соединений и только при включенном режиме log_hostname.

client_port integer
Номер порта TCP, который клиент использует для связи с этим бэкендом, или -1 если используется сокет Unix

backend_start timestamp with time zone
Время, когда этот процесс был запущен. Для клиентских бэкендов это время, когда клиент подключается к серверу.

xact_start timestamp with time zone
Время, когда была запущена текущая транзакция этого процесса, или ноль, если транзакция не активна. Если текущий запрос является первой из его транзакций, этот столбец равен столбцу query_start .

query_start timestamp with time zone
Время, когда был запущен текущий активный запрос, или если state не active, когда был запущен последний запрос

state_change timestamp with time zone
Время последнего изменения состояния

wait_event_type text
Тип события, которого ожидает серверная часть, если это имеет место; в противном случае NULL. Возможные значения:
- LWLock: бэкенд ждет легкую блокировку. Каждая такая блокировка защищает определенную структуру данных в общей памяти. wait_event будет содержать имя, идентифицирующее цель получения легкой блокировки. (У некоторых блокировок есть особые имена; другие являются частью группы блокировок со схожим назначением.)
- Lock: бэкенд ждет тяжелую блокировку. Тяжелые блокировки, также известные как блокировки или просто блокировки менеджера блокировок, в основном защищают видимые SQL-объекты, такие как таблицы. Однако они также используются для обеспечения взаимоисключающего обновления для некоторых внутренних операций, таких как расширение отношений. В wait_event обозначается конкретное место ожидания.
- BufferPin: серверный процесс ожидает доступа к буферу данных в течение периода, когда никакой другой процесс не может обращаться к этому буферу. Ожидания закрепления буфера могут быть длительными, если другой процесс удерживает открытый курсор, который последним читал данные из рассматриваемого буфера.
- Activity: Серверный процесс простаивает. Используется системными процессами, ожидающими активности в основном цикле обработки. В wait_event обозначается конкретное место ожидания.
- Extension: серверный процесс ожидает активности в модуле расширения. Эта категория полезна при использовании модулей для отслеживания нестандартных мест ожидания.
- Client: серверный процесс ожидает в сокете некоторой активности от пользовательских приложений. Сервер ожидает, что произойдет что-то, что не зависит от его внутренних процессов. В wait_event обозначается конкретное место ожидания.
- IPC: серверный процесс ожидает некоторой активности от другого процесса на сервере. В wait_event обозначается конкретное место ожидания.
- Timeout: процесс сервера ожидает истечения времени ожидания. В wait_event обозначается конкретное место ожидания.
- IO: серверный процесс ожидает завершения ввода-вывода. В wait_event обозначается конкретное место ожидания.

wait_event text
Имя события ожидания, если бэкенд в данный момент в ожидании, иначе NULL. Детали в Таблице 4.

state text
Текущее общее состояние этого бэкенда. Возможные значения:
- active: бэкенд выполняет запрос.
- idle: бэкенд ожидает новой команды от клиента.
- idle in transaction: бэкенд находится внутри транзакции, но в данный момент не выполняет запрос.
- idle in transaction (aborted): это состояние подобно idle in transaction, за исключением того, что один из операторов в транзакции вызвал ошибку.
- fastpath function call: бэкенд выполняет функцию fast-path.
- disabled: об этом состоянии сообщается, если track_activities выключен в этом бэкенде.

backend_xid xid
Идентификатор транзакции верхнего уровня этого бэкенда, если он имеется.

backend_xmin xid
Граница xmin для данного бэкенда.

query text
Текст последнего запроса этого бэкенда. Если поле state содержит значение active, в этом поле отображается текущий выполняемый запрос. Во всех других состояниях он показывает последний запрос, который был выполнен ранее. По умолчанию текст запроса усекается до 1024 символов; это значение можно изменить с помощью параметра track_activity_query_size.

backend_type text
Тип текущего бэкенда. Возможные типы: autovacuum launcher, autovacuum worker, logical replication launcher, logical replication worker, parallel worker, background writer, client backend, checkpointer, startup, walreceiver, walsender и walwriter. Кроме того, фоновые процессы, зарегистрированные расширениям, могут иметь дополнительные типы.

Примечание
Столбцы wait_event и state независимы. Если бэкенд находится в состоянии active, он может быть или не быть в состоянии ожидания какого-либо события. Если состояние active а wait_event не NULL, это означает, что запрос выполняется, но блокируется где-то в системе.

Таблица 4. Типы событий ожидания

Тип события ожидания | Имя события ожидания | Описание
LWLock | ShmemIndexLock | Ожидание поиска и выделения места в разделяемой памяти.
| OidGenLock | Ожидание выделения или назначения OID.
| XidGenLock | Ожидание выделения или назначения идентификатора транзакции.
| ProcArrayLock | Ожидание получения снимка или очистки идентификатора транзакции в конце транзакции.
| SInvalReadLock | Ожидание получения или удаления сообщений из общей очереди инвалидации.
| SInvalWriteLock | Ожидание добавления сообщения в общую очередь инвалидации.
| WALBufMappingLock | Ожидание замены страницы в буферах WAL.
| WALWriteLock | Ожидание записи буферов WAL на диск.
| ControlFileLock | Ожидание чтения или обновления контрольного файла или создания нового файла WAL.
| CheckpointLock | Ожидание выполнения контрольной точки.
| CLogControlLock | Ожидание чтения или обновления статуса транзакции.
| SubtransControlLock | Ожидание чтения или обновления информации подтранзакции.
| MultiXactGenLock | Ожидание чтения или обновления общего состояния мультитранзакций.
| MultiXactOffsetControlLock | Ожидание чтения или обновления смещений мультитранзакций.
| MultiXactMemberControlLock | Ожидание чтения или обновления членов мультитранзакций.
| RelCacheInitLock | Ожидание чтения или записи файла инициализации кэша отношения.
| CheckpointerCommLock | Ожидание при выполнении запросов fsync.
| TwoPhaseStateLock | Ожидание чтения или обновления состояния подготовленных транзакций.
| TablespaceCreateLock | Ожидание создания или удаления табличного пространства.
| BtreeVacuumLock | Ожидание чтения или обновления связанной с вакуумом информации для индекса B-дерева.
| AddinShmemInitLock | Ожидание управления распределением пространства в разделяемой памяти.
| AutovacuumLock | Рабочий процесс автовакуума или процесс запуска автовакуума в ожидании обновления или чтения текущего состояния рабочих процессов автоочистки.
| AutovacuumScheduleLock | Ожидание при проверке, что таблица, которая выбрана для очистки, все еще нуждается в ней.
| SyncScanLock | Ожидание получения начального местоположения сканирования таблицы для синхронизированных сканирований.
| RelationMappingLock | Ожидание обновления файла карты отношения, используемого для сохранения в каталоге данных о файловых узлах.
| AsyncCtlLock | Ожидание чтения или обновления общего состояния уведомлений.
| AsyncQueueLock | Ожидание чтения или обновления уведомлений.
| SerializableXactHashLock | Ожидание получения или сохранения информации о сериализуемых транзакциях.
| SerializableFinishedListLock | Ожидание доступа к списку завершенных сериализуемых транзакций.
| SerializablePredicateLockListLock | Ожидание выполнения операции со списком блокировок, удерживаемых сериализуемыми транзакциями.
| OldSerXidLock | Ожидание чтения или записи конфликтующих сериализуемых транзакций.
| SyncRepLock | Ожидание чтения или обновления информации о синхронных репликах.
| BackgroundWorkerLock | Ожидание чтения или обновления состояния фонового процесса.
| DynamicSharedMemoryControlLock | Ожидание чтения или обновления состояния динамической разделяемой памяти.
| AutoFileLock | Ожидание обновления файла qhb.auto.conf.
| ReplicationSlotAllocationLock | Ожидание выделения или освобождения слота репликации.
| ReplicationSlotControlLock | Ожидание чтения или обновления состояния слота репликации.
| CommitTsControlLock | Ожидание чтения или обновления отметок времени подтверждения транзакции.
| CommitTsLock | Ожидание чтения или обновления последнего значения, установленного для отметки времени транзакции.
| ReplicationOriginLock | Ожидание установки, удаления или использования источника репликации.
| MultiXactTruncationLock | Ожидание чтения или очистки информации мультитранзакций.
| OldSnapshotTimeMapLock | Ожидание чтения или обновления информации о старых снимках.
| LogicalRepWorkerLock | Ожидание завершения действия процесса логической репликации.
| CLogTruncationLock | Ожидание выполнения функции txid_status или обновления самого старого идентификатора транзакции, доступного для неё.
| clog | Ожидание ввода-вывода в clog (буфере состояния транзакций).
| commit_timestamp | Ожидание ввода-вывода в буфере отметок времени фиксации транзакций.
| subtrans | Ожидание ввода-вывода буфера подтранзакций.
| multixact_offset | Ожидание ввода-вывода в буфере смещения мультитранзакций.
| multixact_member | Ожидание ввода-вывода в буфере multixact_member.
| async | Ожидание ввода-вывода в асинхронном (уведомляющем) буфере.
| oldserxid | Ожидание ввода-вывода в буфере oldserxid.
| wal_insert | Ожидание вставки WAL в буфер памяти.
| buffer_content | Ожидание чтения или записи страницы данных в памяти.
| buffer_io | Ожидание ввода-вывода страницы данных.
| replication_origin | Ожидание чтения или обновления состояния процесса репликации.
| replication_slot_io | Ожидание ввода-вывода данных слота репликации.
| proc | Ожидание чтения или обновления информации о блокировке по быстрому пути.
| buffer_mapping | Ожидание связывания блока данных с буфером в буферном пуле.
| lock_manager | Ожидание добавления или проверки блокировок для бэкендов или ожидание присоединения или выхода из группы блокировок (используется параллельными запросами).
| predicate_lock_manager | Ожидание добавления или проверки информации предикатных блокировок.
| serializable_xact | Ожидание выполнения операции сериализуемой транзакцией в параллельном запросе.
| parallel_query_dsa | Ожидание блокировки выделения динамической общей памяти в параллельном запросе.
| tbm | Ожидание блокировки общего итератора TBM.
| parallel_append | Ожидание выбора следующего подплана во время выполнения плана параллельного добавления.
| parallel_hash_join | Ожидание выделения или обмена части памяти или обновления счетчиков во время выполнения плана параллельного хеширования.
Lock | relation | Ожидание получения блокировки в отношении.
| extend | Ожидание расширения отношения.
| page | Ожидание получения блокировки для страницы отношения.
| tuple | Ожидание получения блокировки для кортежа.
| transactionid | Ожидание завершения транзакции.
| virtualxid | Ожидание получения блокировки виртуального XID.
| speculative token | Ожидание получения блокировки спекулятивной вставки.
| object | Ожидание получения блокировки для объекта, не относящегося к базе данных.
| userlock | Ожидание получения пользовательской блокировки.
| advisory | Ожидание получения рекомендательной пользовательской блокировки.
BufferPin | BufferPin | Ожидание закрепления буфера.
Activity | ArchiverMain | Ожидание в основном цикле процесса архивирования.
| AutoVacuumMain | Ожидание в главном цикле процесса запуска автоочистки.
| BgWriterHibernate | Ожидание в фоновом процессе записи в режиме бездействия.
| BgWriterMain | Ожидание в основном цикле рабочего процесса фоновой записи.
| CheckpointerMain | Ожидание в основном цикле процесса контрольной точки.
| LogicalApplyMain | Ожидание в основном цикле процесса применения логической репликации.
| LogicalLauncherMain | Ожидание в основном цикле процесса запуска логической репликации.
| PgStatMain | Ожидание в основном цикле процесса сбора статистики.
| RecoveryWalAll | Ожидание восстановления WAL из любого источника (локального, архивного или потокового) при восстановлении.
| RecoveryWalStream | Ожидание WAL из потока при восстановлении.
| SysLoggerMain | Ожидание в основном цикле процесса syslogger.
| WalReceiverMain | Ожидание в основном цикле процесса приема WAL.
| WalSenderMain | Ожидание в основном цикле процесса отправителя WAL.
| WalWriterMain | Ожидание в основном цикле процесса записи WAL.
Client | ClientRead | Ожидание чтения данных от клиента.
| ClientWrite | Ожидание записи данных, отправляемых клиенту.
| LibPQWalReceiverConnect | Ожидание в приемнике WAL для установления соединения с удаленным сервером.
| LibPQWalReceiverReceive | Ожидание в приемнике WAL для получения данных с удаленного сервера.
| SSLOpenServer | Ожидание SSL при попытке подключения.
| WalReceiverWaitStart | Ожидание запуска процесса отправки начальных данных для потоковой репликации.
| WalSenderWaitForWAL | Ожидание сброса WAL в процессе отправки WAL.
| WalSenderWriteData | Ожидание любого действия при обработке ответов от получателя WAL в процессе отправителя WAL.
Extension | Extension | Ожидание в расширении.
IPC | BgWorkerShutdown | Ожидание выключения фонового рабочего процесса.
| BgWorkerStartup | Ожидание запуска фонового рабочего процесса.
| BtreePage | Ожидание появления номера страницы, необходимой для продолжения параллельного сканирования B-дерева.
| CheckpointDone | Ожидание контрольной точки для завершения.
| CheckpointStart | Ожидание контрольной точки для запуска.
| ClogGroupUpdate | Ожидание, когда ведущий процесс группы обновит статус транзакции в конце транзакции.
| ExecuteGather | Ожидание активности от дочернего процесса при выполнении узла сбора данных(Gather).
| Hash/Batch/Allocating | Ожидание, когда выбранный участник параллельного хеширования разместит хеш-таблицу.
| Hash/Batch/Electing | Выбор участника параллельного хеширования для размещения хеш-таблицы.
| Hash/Batch/Loading | Ожидание, пока другие участники параллельного хеширования завершат загрузку хеш-таблицы.
| Hash/Build/Allocating | Ожидание, когда выбранный участник параллельного хеширования выделит начальную хеш-таблицу.
| Hash/Build/Electing | Выбор участника параллельного хеширования для размещения начальной хеш-таблицы.
| Hash/Build/HashingInner | Ожидание, пока другие участники параллельного хеширования завершат хеширование внутреннего отношения.
| Hash/Build/HashingOuter | Ожидание, пока другие участники параллельного хеширования завершат разбиение внешнего отношения.
| Hash/GrowBatches/Allocating | Ожидание выделения выбранным участником параллельного хеширования большего количества пакетов.
| Hash/GrowBatches/Deciding | Выбор участника параллельного хеширования для принятия решения о будущем добавлении пакетов.
| Hash/GrowBatches/Electing | Выбор участника параллельного хеширования для добавления большего количества пакетов.
| Hash/GrowBatches/Finishing | Ожидание выбранного участника параллельного хеширования для принятия решения о добавлении дополнительных пакетов.
| Hash/GrowBatches/Repartitioning | Ожидание, пока другие участники параллельного хеширования завершат перераспределение.
| Hash/GrowBuckets/Allocating | Ожидание, когда выбранный участник параллельного хеширования закончит выделять дополнительные группы.
| Hash/GrowBuckets/Electing | Выбор участника параллельного хеширования для выделения дополнительных групп.
| Hash/GrowBuckets/Reinserting | Ожидание, пока другие участники параллельного хеширования закончат вставлять кортежи в новые группы.
| LogicalSyncData | Ожидание, когда удаленный сервер логической репликации отправит данные для начальной синхронизации таблицы.
| LogicalSyncStateChange | Ожидание, когда удаленный сервер логической репликации изменит состояние.
| MessageQueueInternal | Ожидание присоединения другого процесса к общей очереди сообщений.
| MessageQueuePutMessage | Ожидание записи сообщения протокола в общую очередь сообщений.
| MessageQueueReceive | Ожидание получения байтов из общей очереди сообщений.
| MessageQueueSend | Ожидание отправки байтов в общую очередь сообщений.
| ParallelBitmapScan | Ожидание инициализации параллельного сканирования по битовой карте.
| ParallelCreateIndexScan | Ожидание завершения сканирования параллельными процессами CREATE INDEX.
| ParallelFinish | Ожидание окончания вычислений параллельных рабочих процессов.
| ProcArrayGroupUpdate | Ожидание, пока ведущий процесс в группе не очистит идентификатор транзакции в конце транзакции.
| Promote | В ожидании продвижения ведомых.
| ReplicationOriginDrop | Ожидание, когда источник репликации станет неактивным и будет удален.
| ReplicationSlotDrop | Ожидание, когда слот репликации станет неактивным и будет удален.
| SafeSnapshot | Ожидание снимка транзакции READ ONLY DEFERRABLE.
| SyncRep | Ожидание подтверждения от удаленного сервера во время синхронной репликации.
Timeout | BaseBackupThrottle | Ожидание во время резервного копирования базы при ограничении активности.
| PgSleep | Ожидание в процессе, который вызвал pg_sleep.
| RecoveryApplyDelay | Ожидание применения WAL при восстановлении из-за задержки.
IO | BufFileRead | Ожидание чтения из буферизованного файла.
| BufFileWrite | Ожидание записи в буферизованный файл.
| ControlFileRead | Ожидание чтения из контрольного файла.
| ControlFileSync | Ожидание помещения контрольного файла в стабильное хранилище.
| ControlFileSyncUpdate | Ожидание обновления контрольного файла в стабильном хранилище.
| ControlFileWrite | Ожидание записи в контрольный файл.
| ControlFileWriteUpdate | Ожидание записи для обновления контрольного файла.
| CopyFileRead | Ожидание чтения во время операции копирования файла.
| CopyFileWrite | Ожидание записи во время операции копирования файла.
| DataFileExtend | Ожидание расширения файла данных отношения.
| DataFileFlush | Ожидание помещения файла данных отношения в стабильное хранилище.
| DataFileImmediateSync | Ожидание немедленной синхронизации файла данных отношения со стабильным хранилищем.
| DataFilePrefetch | Ожидание асинхронной предварительной выборки из файла данных отношений.
| DataFileRead | Ожидание чтения из файла данных отношения.
| DataFileSync | Ожидание переноса изменений в файле реляционных данных в стабильное хранилище.
| DataFileTruncate | Ожидание усечения файла данных отношения.
| DataFileWrite | Ожидание записи в файл данных отношений.
| DSMFillZeroWrite | Ожидание записи нулевых байтов в файл поддержки динамической разделяемой памяти.
| LockFileAddToDataDirRead | Ожидание чтения при добавлении строки в файл блокировки каталога данных.
| LockFileAddToDataDirSync | Ожидание поступления данных в стабильное хранилище при добавлении строки в файл блокировки каталога данных.
| LockFileAddToDataDirWrite | Ожидание записи при добавлении строки в файл блокировки каталога данных.
| LockFileCreateRead | Ожидание чтения при создании файла блокировки каталога данных.
| LockFileCreateSync | Ожидание поступления данных в стабильное хранилище при создании файла блокировки каталога данных.
| LockFileCreateWrite | Ожидание записи при создании файла блокировки каталога данных.
| LockFileReCheckDataDirRead | Ожидание чтения во время повторной проверки файла блокировки каталога данных.
| LogicalRewriteCheckpointSync | Ожидание сопоставления логической перезаписи для достижения стабильного хранилища во время контрольной точки.
| LogicalRewriteMappingSync | Ожидание сопоставления данных для достижения стабильного хранилища во время логической перезаписи.
| LogicalRewriteMappingWrite | Ожидание записи данных сопоставления во время логической перезаписи.
| LogicalRewriteSync | Ожидание сопоставления логической перезаписи для достижения стабильного хранилища.
| LogicalRewriteWrite | Ожидание записи логических сопоставлений перезаписи.
| RelationMapRead | Ожидание чтения файла карты отношений.
| RelationMapSync | Ожидание, пока файл карты отношений не будет записан в стабильное хранилище.
| RelationMapWrite | Ожидание записи в файл карты отношений.
| ReorderBufferRead | Ожидание чтения во время переупорядочения буфера управления.
| ReorderBufferWrite | Ожидание записи во время переупорядочения буфера управления.
| ReorderLogicalMappingRead | Ожидание чтения логического отображения во время управления буфером переупорядочения.
| ReplicationSlotRead | Ожидание чтения из управляющего файла слота репликации.
| ReplicationSlotRestoreSync | Ожидание, пока контрольный файл слота репликации не будет помещен в стабильное хранилище при восстановлении его в памяти
| ReplicationSlotSync | Ожидание, когда контрольный файл слота репликации будет помещен в стабильное хранилище.
| ReplicationSlotWrite | Ожидание записи в управляющий файл слота репликации.
| SLRUFlushSync | Ожидание, пока данные SLRU будут записаны в стабильное хранилище во время проверки или остановки базы данных.
| SLRURead | Ожидание чтения страницы SLRU.
| SLRUSync | Ожидание, пока данные SLRU будут записаны в стабильное хранилище после записи страницы.
| SLRUWrite | В ожидании записи страницы SLRU.
| SnapbuildRead | Ожидание чтения снимка сериализованного исторического каталога.
| SnapbuildSync | Ожидание получения сериализованного моментального снимка каталога для записи в стабильное хранилище.
| SnapbuildWrite | Ожидание записи сериализованного исторического снимка каталога.
| TimelineHistoryFileSync | Ожидание файла хронологии, полученного посредством потоковой репликации, для записи в стабильное хранилище.
| TimelineHistoryFileWrite | Ожидание записи файла истории линии времени, полученной посредством потоковой репликации.
| TimelineHistoryRead | Ожидание чтения файла истории линии времени.
| TimelineHistorySync | Ожидание, когда вновь созданный файл истории линии времени будет записан в стабильное хранилище.
| TimelineHistoryWrite | Ожидание записи нового файла истории.
| TwophaseFileRead | Ожидание чтения файла двухфазного состояния.
| TwophaseFileSync | Ожидание помещения файла двухфазного состояния в стабильное хранилище.
| TwophaseFileWrite | Ожидание записи файла двухфазного состояния.
| WALBootstrapSync | Ожидание, пока WAL будет записан в стабильное хранилище во время начальной загрузки.
| WALBootstrapWrite | Ожидание записи WAL-страницы во время начальной загрузки.
| WALCopyRead | Ожидание чтения при создании нового сегмента WAL путем копирования существующего.
| WALCopySync | Ожидание нового сегмента WAL, созданного путем копирования существующего для записи в стабильное хранилище.
| WALCopyWrite | Ожидание записи при создании нового сегмента WAL путем копирования существующего.
| WALInitSync | Ожидание, пока недавно инициализированный файл WAL будет записан в стабильное хранилище.
| WALInitWrite | Ожидание записи при инициализации нового файла WAL.
| WALRead | Ожидание чтения из файла WAL.
| WALSenderTimelineHistoryRead | Ожидание чтения из файла истории линии времени при выполнении команды timeline процессом walsender.
| WALSync | Ожидание, пока файл WAL будет записан в стабильное хранилище.
| WALSyncMethodAssign | Ожидание, пока данные будут записаны в стабильное хранилище при назначении метода синхронизации WAL.
| WALWrite | Ожидание записи в файл WAL.

Примечание
Для траншей, зарегистрированных расширениями, имя указывается по названию расширения, и именно оно будет отображаться в поле wait_event. Вполне возможно, что пользователь зарегистрировал транш в одном из бэкендов (используя динамическую общую память), и в этом случае другие бэкенды не получат эту информацию, поэтому в таких случаях выводится extension.

Вот пример того, как события ожидания могут быть просмотрены

SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event is NOT NULL;
 pid  | wait_event_type |  wait_event
------+-----------------+---------------
 2540 | Lock            | relation
 6644 | LWLock          | ProcArrayLock
(2 rows)

pg_stat_replication

Таблица 5. Представление pg_stat_replication

Колонка
Тип
Описание

pid
integer
Идентификатор процесса отправителя WAL

usesysid
oid
OID пользователя, вошедшего в этот процесс-отправитель WAL

usename
name
Имя пользователя, подключенного к процессу-отправителю WAL

application_name
text
Имя приложения, подключенного к этому процессу-отправителю WAL

client_addr
inet
IP-адрес клиента, подключенного к этому процессу-отправителю WAL. Если это поле пустое, это означает, что клиент подключен через сокет Unix на сервере.

client_hostname
text
Имя хоста подключенного клиента, получаемое при обратном поиске в DNS по client_addr. Это поле будет отличным от NULL для IP-соединений и только при включенном log_hostname.

client_port
integer
Номер TCP-порта, который клиент использует для связи с процессом-отправителем WAL, или -1, если используется сокет Unix.

backend_start
timestamp with time zone
Время, когда этот процесс был запущен, т.е. когда клиент подключился к этому процессу-отправителю WAL.

backend_xmin
xid
Значение xmin, полученное от сервера-реплики при включенном hot_standby_feedback.

state
text
Текущее состояние отправителя WAL. Возможные значения:

- startup: этот отправитель WAL запускается.

- catchup: подключенный к процессу-отправителю сервер-реплика догоняет основной.

- streaming: процесс-отправитель WAL выполняет потоковую передачу изменений после того, как его подключенный резервный сервер обнаружил основной.

- backup: процесс-отправитель WAL передает резервную копию.

- stopping: процесс-отправитель WAL останавливается.

sent_lsn
pg_lsn
Последняя позиция WAL, отправленная по этому соединению

write_lsn
pg_lsn
Последняя позиция WAL, записанная на диск этим резервным сервером

flush_lsn
pg_lsn
Последняя позиция WAL, сброшенная на диск этим резервным сервером

replay_lsn
pg_lsn
Последняя позиция WAL, примененная в базе данных на этом резервном сервере

write_lag
interval
Время, прошедшее между локальным сбросом последних данных WAL и получением уведомления о том, что этот резервный сервер записал их (но еще не сбросил или применил их). Это может быть использовано для измерения задержки, которая произошла при фиксации транзакции, когда в synchronous_commit выбран уровень remote_write, если этот сервер был настроен как синхронный резервный сервер.

flush_lag
interval
Время, прошедшее между локальным сбросом последних данных WAL и получением уведомления о том, что этот резервный сервер записал и сбросил их (но еще не применил). Это может быть использовано для определения задержки, когда в synchronous_commit выбран уровень on при фиксации, если этот сервер был настроен как синхронный резервный сервер.

replay_lag
interval
Прошло время между локальной очисткой последней WAL и получением уведомления о том, что этот резервный сервер написал, сбросил и применил ее. Это может быть использовано для определения задержки, когда в synchronous_commit выбран уровень remote_apply при фиксации, если этот сервер был настроен как синхронный резервный сервер.

sync_priority
integer
Приоритет этого резервного сервера для выбора в качестве синхронного резервного в синхронной репликации на основе приоритетов. Не имеет значения при синхронной репликации с учетом кворума.

sync_state
text
Состояние синхронизации этого резервного сервера. Возможные значения:

- async: этот резервный сервер является асинхронным.

- potential: этот резервный сервер теперь асинхронный, но потенциально может стать синхронным, если произойдет сбой одного из текущих синхронных серверов.

- sync: этот резервный сервер является синхронным.

- quorum: этот резервный сервер считается кандидатом на участие в кворуме.

reply_time
timestamp with time zone
Время отправки последнего ответного сообщения, полученного от резервного сервера

Представление pg_stat_replication будет содержать по одной строке на процесс-отправитель WAL, показывающий статистику репликации на соответствующий резервный сервер. Перечислены только напрямую подключенные резервные сервера; информация о резервных серверах, подключенных опосредованно, недоступна.

Время задержки, указанное в представлении pg_stat_replication, представляет время, затраченное на запись, сброс и повторное воспроизведение последнего WAL, а также на то, чтобы отправитель узнал об этом. Эти длительность представляет задержку фиксации, которая была (или должна была быть) добавлена каждым уровнем синхронной фиксации, если удаленный сервер был настроен как синхронный резервный сервер. Для асинхронного режима ожидания столбец replay_lag приблизительно определяет задержку перед тем, как последние транзакции стали видимыми для запросов. Если резервный сервер полностью догнал отправляющий сервер и активность WAL больше не наблюдается, последнее измеренное значение времени задержки будет продолжать отображаться в течение короткого времени, а затем показывать NULL.

Время задержки определяется автоматически для физической репликации. Модули логического декодирования могут не отправлять сообщения отслеживания; в этом случае механизм отслеживания просто отобразит в качестве времени задержки значение NULL.

Примечание
Сообщаемое время задержки не является прогнозом того, сколько времени потребуется резервному серверу, чтобы догнать отправляющий сервер, учитывая текущую скорость воспроизведения. Такая система будет показывать аналогичные значения времени, когда будет генерироваться новый WAL, но не тогда, когда отправляющий сервер будет простаивать. В частности, когда резервный сервер полностью догнал ведущий, pg_stat_replication показывает время, затраченное на запись, очистку и воспроизведение самого последнего переданного изменения WAL, а не ноль, как могут ожидать некоторые пользователи. Это соответствует целям измерения синхронных фиксаций и задержек видимости транзакций для недавних записанных транзакций. Чтобы уменьшить путаницу для пользователей, ожидающих другое поведение при запаздывании, значения запаздывания возвращаются в NULL через короткое время в системе, которая уже воспроизвела все изменения и находится в ожидании. Системы мониторинга должны выбрать, следует ли представлять это как отсутствующие данные, обнулять их или продолжать отображать последнее известное значение.

pg_stat_wal_receiver

Таблица 6. Представление pg_stat_wal_receiver

колонка | Тип | Описание
pid | integer | Идентификатор процесса-получателя WAL
status | text | Состояние активности процесса-получателя WAL
receive_start_lsn | pg_lsn | Первая позиция WAL, используемая при запуске приемника WAL
receive_start_tli | integer | Первый номер линии времени, используемый при запуске приемника WAL
received_lsn | pg_lsn | Последняя позиция WAL, уже полученная и записанная на диск, начальное значение этого поля является первой позицией журнала при запуске приемника WAL
received_tli | integer | Номер линии времени последней позиции WAL, полученной и записанной на диск, начальное значение этого поля является номером линии времени первой позиции журнала, использованной при запуске приемника WAL
last_msg_send_time | timestamp with time zone | Время отправки последнего сообщения, полученного от отправителя WAL
last_msg_receipt_time | timestamp with time zone | Время получения последнего сообщения, полученного от отправителя WAL
latest_end_lsn | pg_lsn | Последняя позиция WAL, о которой был уведомлен отправитель WAL
latest_end_time | timestamp with time zone | Время последней записи WAL, о которой был уведомлен отправитель WAL
slot_name | text | Имя слота репликации, используемое этим приемником WAL
sender_host | text | Хост экземпляра QHB , к которому подключен этот приемник WAL. Это может быть имя хоста, IP-адрес или путь к каталогу, если соединение осуществляется через сокет Unix. (Подключение к сокету можно распознать, поскольку он всегда будет абсолютным путем, начинающимся с /.)
sender_port | integer | Номер порта экземпляра QHB , к которому подключен получатель WAL.
conninfo | text | Строка соединения, используемая этим приемником WAL, со скрытыми секретными полями.

Представление pg_stat_wal_receiver будет содержать только одну строку, показывающую статистику о получателе WAL с подключенного сервера этого получателя.

pg_stat_subscription

Таблица 7. Представление pg_stat_subscription

колонка | Тип | Описание
subid | oid | OID подписки
subname | text | Название подписки
pid | integer | Идентификатор процесса рабочего процесса подписки
relid | Oid | OID отношения, которое процесс синхронизирует; для основного процесса применения
received_lsn | pg_lsn | Последняя полученная запись WAL, начальное значение этого поля 0
last_msg_send_time | timestamp with time zone | Время отправки последнего сообщения, полученного от отправителя WAL
last_msg_receipt_time | timestamp with time zone | Время получения последнего сообщения, полученного от отправителя WAL
latest_end_lsn | pg_lsn | Последняя запись WAL, о которой был уведомлен отправитель WAL
latest_end_time | timestamp with time zone | Время последней записи в WAL, о которой был уведомлен отправитель WAL

Представление pg_stat_subscription будет содержать одну строку для каждой подписки для основного процесса (с NULL в PID, если процесс не работает) и дополнительные строки для процессов, обрабатывающих начальные данные для таблиц в подписке.

pg_stat_ssl

Таблица 8. Представление pg_stat_ssl

колонка | Тип | Описание
pid | integer | Идентификатор процесса бэкенда или процесса отправителя WAL
ssl | boolean | True, если SSL используется для этого соединения
version | text | Используемая версия SSL или NULL, если SSL в этом соединении не используется.
cipher | text | Имя используемого шифра SSL или NULL, если SSL не используется в этом соединении
bits | integer | Количество бит в используемом алгоритме шифрования или NULL, если SSL не используется в этом соединении
compression | boolean | True, если используется сжатие SSL, false, если нет, или NULL, если SSL не используется в этом соединении.
client_dn | text | Поле «Уникальное имя» (DN) из используемого клиентского сертификата или NULL, если клиентский сертификат не был предоставлен или SSL не используется в этом соединении. Это поле усекается, если поле DN длиннее, чем NAMEDATALEN (64 символа в стандартной сборке).
client_serial | numeric | Серийный номер сертификата клиента или NULL, если сертификат клиента не предоставлен или SSL не используется в этом соединении. Комбинация серийного номера сертификата и эмитента сертификата однозначно идентифицирует сертификат (если только эмитент ошибочно не использует серийные номера).
issuer_dn | text | DN эмитента клиентского сертификата или NULL, если клиентский сертификат не был предоставлен или SSL не используется в этом соединении. Это поле усекается как client_dn.

Представление pg_stat_ssl будет содержать по одной строке для каждого бэкенда или процесса отправителя WAL, показывая статистику использования SSL для этого соединения. Его можно объединить с pg_stat_activity или pg_stat_replication в столбце pid, чтобы получить более подробную информацию о соединении.

pg_stat_gssapi

Таблица 9. Представление pg_stat_gssapi

колонка | Тип | Описание
pid | integer | Идентификатор процесса бэкенда
gss_authenticated | boolean | Истинно, если для этого соединения использовалась аутентификация GSSAPI
principal | text | Принципал, используемый для аутентификации этого соединения, или NULL, если GSSAPI не использовался для аутентификации этого соединения. Это поле усекается, если участник длиннее, чем NAMEDATALEN (64 символа в стандартной сборке).
encrypted | boolean | True, если в этом соединении используется шифрование GSSAPI

Представление pg_stat_gssapi будет содержать одну строку для каждого бэкенда, показывающую информацию об использовании GSSAPI для этого соединения. Его можно объединить с pg_stat_activity или pg_stat_replication в столбце pid, чтобы получить более подробную информацию о соединении.

pg_stat_archiver

Таблица 10. Представление pg_stat_archiver

колонка | Тип | Описание
archived_count | bigint | Количество файлов WAL, которые были успешно заархивированы
last_archived_wal | text | Имя последнего файла WAL, успешно заархивированного
last_archived_time | timestamp with time zone | Время последней успешной операции архивирования
failed_count | bigint | Количество неудачных попыток архивирования файлов WAL
last_failed_wal | text | Имя файла WAL последней неудачной операции архивирования
last_failed_time | timestamp with time zone | Время последней неудачной архивной операции
stats_reset | timestamp with time zone | Время последнего сброса этой статистики

Представление pg_stat_archiver всегда будет содержать одну строку, содержащую данные о процессе архивации кластера.

pg_stat_bgwriter

Таблица 11. Представление pg_stat_bgwriter

колонка | Тип | Описание
checkpoints_timed | bigint | Количество запланированных контрольных точек, которые были выполнены
checkpoints_req | bigint | Количество запрошенных контрольных точек, которые были выполнены
checkpoint_write_time | double precision | Общее время, потраченное на этап обработки контрольной точки, где файлы записываются на диск, в миллисекундах
checkpoint_sync_time | double precision | Общее количество времени, потраченное на часть обработки контрольной точки, где файлы синхронизируются с диском, в миллисекундах
buffers_checkpoint | bigint | Количество буферов, записанных во время контрольных точек
buffers_clean | bigint | Количество буферов, записанных фоновым процессом записи
maxwritten_clean | bigint | Количество раз, когда процесс записи в фоновом режиме останавливал сброс грязных страниц, поскольку записывал слишком много буферов
buffers_backend | bigint | Количество буферов, записанных непосредственно бэкендом
buffers_backend_fsync | bigint | Сколько раз бэкенд выполнял вызов fsync сам (обычно их обрабатывает фоновый процесс записи, даже когда бэкенд выполняет запись самостоятельно)
buffers_alloc | bigint | Количество выделенных буферов
stats_reset | timestamp with time zone | Время последнего сброса этой статистики

Представление pg_stat_bgwriter всегда будет содержать одну строку, содержащую общие данные для всего кластера.

pg_stat_database

Таблица 12. Представление pg_stat_database

колонка | Тип | Описание
datid | oid | OID базы данных или 0 для объектов, принадлежащих к общим отношениям
datname | name | Имя базы данных или NULL для общих объектов.
numbackends | integer | Количество бэкендов, которые в данный момент подключены к этой базе данных, или NULL для общих объектов. Это единственный столбец в этом представлении, который возвращает значение, отражающее текущее состояние; все остальные столбцы возвращают накопленные значения с момента последнего сброса.
xact_commit | bigint | Количество транзакций в этой базе данных, которые были совершены
xact_rollback | bigint | Количество транзакций в этой базе данных, которые были отменены
blks_read | bigint | Количество дисковых блоков, прочитанных в этой базе данных
blks_hit | bigint | Количество обнаружений дисковых блоков в буферном кэше, так что чтение не было необходимо (это включает только попадания в буферный кэш QHB , а не в кэш файловой системы операционной системы)
tup_returned | bigint | Количество строк, возвращенных запросами в этой базе данных
tup_fetched | bigint | Количество строк, извлеченными запросами в этой базе данных
tup_inserted | bigint | Количество строк, вставленных запросами в этой базе данных
tup_updated | bigint | Количество строк, обновленных запросами в этой базе данных
tup_deleted | bigint | Количество строк, удаленных запросами в этой базе данных
conflicts | bigint | Количество запросов отменено из-за конфликтов при восстановлении в этой базе данных. (Конфликты возникают только на резервных серверах; подробности см. в pg_stat_database_conflicts.)
temp_files | bigint | Количество временных файлов, созданных запросами в этой базе данных. Подсчитываются все временные файлы, независимо от того, почему был создан временный файл (например, при сортировке или хешировании), и независимо от настройки log_temp_files.
temp_bytes | bigint | Общий объем данных, записанных во временные файлы по запросам в этой базе данных. Учитываются все временные файлы, независимо от того, почему был создан временный файл, и независимо от настройки log_temp_files.
deadlocks | bigint | Количество взаимоблокировок, обнаруженных в этой базе данных
checksum_failures | bigint | Число ошибок контрольной суммы страницы данных, обнаруженных в этой базе данных (или в общем объекте), или NULL, если контрольные суммы данных не включены.
checksum_last_failure | timestamp with time zone | Время, когда в этой базе данных (или в общем объекте) был обнаружен последний сбой контрольной суммы страницы данных, или NULL, если контрольные суммы данных не включены.
blk_read_time | double precision | Время, затраченное на чтение блоков файлов данных бэкендами в этой базе данных, в миллисекундах
blk_write_time | double precision | Время, затрачиваемое на запись блоков файлов данных бэкендами в этой базе данных, в миллисекундах
stats_reset | timestamp with time zone | Время последнего сброса этой статистики

Представление pg_stat_database будет содержать одну строку для каждой базы данных в кластере, плюс одну для общих объектов, показывающую общую статистику на уровне всей базы данных.

pg_stat_database_conflicts

Таблица 13. Представление pg_stat_database_conflicts

колонка | Тип | Описание
datid | oid | OID базы данных
datname | name | Имя базы данных
confl_tablespace | bigint | Количество запросов в этой базе данных, которые были отменены из-за удаленных табличных пространств
confl_lock | bigint | Количество запросов в этой базе данных, которые были отменены из-за тайм-аутов блокировки
confl_snapshot | bigint | Количество запросов в этой базе данных, которые были отменены из-за устаревших снимков данных
confl_bufferpin | bigint | Количество запросов в этой базе данных, которые были отменены из-за закрепленных буферов
confl_deadlock | bigint | Количество запросов в этой базе данных, которые были отменены из-за взаимных блокировок

Представление pg_stat_database_conflicts будет содержать одну строку для каждой базы данных, показывающую статистику всей базы данных об отменах запросов, возникающих из-за конфликтов с восстановлением на резервных серверах. Это представление будет содержать информацию только о резервных серверах, поскольку конфликты не возникают на главных серверах.

pg_stat_all_tables

Таблица 14. Представление pg_stat_all_tables

колонка | Тип | Описание
relid | oid | OID таблицы
schemaname | name | Имя схемы, в которой находится эта таблица
relname | name | Имя этой таблицы
seq_scan | bigint | Количество последовательных сканирований, выполненных по этой таблице
seq_tup_read | bigint | Количество живых строк, выбранных при последовательном сканировании
idx_scan | bigint | Количество сканирований индекса, выполненных для этой таблицы
idx_tup_fetch | bigint | Количество живых строк, выбранных при сканировании индекса
n_tup_ins | bigint | Количество вставленных строк
n_tup_upd | bigint | Количество обновленных строк (включая обновленные в режиме HOT)
n_tup_del | bigint | Количество удаленных строк
n_tup_hot_upd | bigint | Количество строк, обновленных в режиме HOT (т.е. не требуется отдельное обновление индекса)
n_live_tup | bigint | Расчетное количество живых строк
n_dead_tup | bigint | Расчетное количество мертвых строк
n_mod_since_analyze | bigint | Предполагаемое количество строк, измененных с момента последнего анализа этой таблицы
last_vacuum | timestamp with time zone | Последний раз, когда эта таблица была очищена вручную (не считая VACUUM FULL)
last_autovacuum | timestamp with time zone | Последний раз, когда эта таблица была очищена фоновым процессом автоочистки
last_analyze | timestamp with time zone | Последний раз, когда эта таблица анализировалась вручную
last_autoanalyze | timestamp with time zone | Последний раз, когда эта таблица анализировалась фоновым процессом автоочистки
vacuum_count | bigint | Сколько раз эта таблица очищалась вручную (не считая VACUUM FULL)
autovacuum_count | bigint | Сколько раз эта таблица очищалась фоновым процессом автоочистки
analyze_count | bigint | Сколько раз эта таблица была проанализирована вручную
autoanalyze_count | bigint | Сколько раз эта таблица анализировалась фоновым процессом автоочистки

Представление pg_stat_all_tables будет содержать одну строку для каждой таблицы в текущей базе данных (включая таблицы TOAST), показывающую статистику доступа к этой конкретной таблице. Представления pg_stat_user_tables и pg_stat_sys_tables содержат ту же информацию, но отображают только пользовательские и системные таблицы соответственно.

pg_stat_all_indexes

Таблица 15. Представление pg_stat_all_indexes

колонка | Тип | Описание
relid | oid | OID таблицы для этого индекса
indexrelid | oid | OID этого индекса
schemaname | name | Имя схемы, в которой находится этот индекс
relname | name | Имя таблицы для этого индекса
indexrelname | name | Наименование этого индекса
idx_scan | bigint | Количество сканирований индекса, выполненных по этому индексу
idx_tup_read | bigint | Количество записей индекса, возвращенных сканированием по этому индексу
idx_tup_fetch | bigint | Количество живых строк таблицы, выбранных при простом сканировании индекса с использованием этого индекса

Представление pg_stat_all_indexes будет содержать одну строку для каждого индекса в текущей базе данных, показывающую статистику доступа к этому конкретному индексу. Представления pg_stat_user_indexes и pg_stat_sys_indexes содержат ту же информацию, но показывают только пользовательские и системные индексы соответственно.

Индексы могут использоваться при простом сканировании индекса, сканировании битовых карт индекса и в работе оптимизатора. При сканировании битовых карт выходные данные нескольких индексов можно комбинировать с помощью правил И или ИЛИ, поэтому сложно связать выборки отдельных строк с конкретными индексами при использовании битовых карт. Следовательно, сканирование битовых карт увеличивает значение pg_stat_all_indexes.idx_tup_read для индексов, которые оно использует, и оно увеличивает значение pg_stat_all_tables.idx_tup_fetch count для таблиц, но не влияет при этом на pg_stat_all_indexes.idx_tup_fetch. Оптимизатор также обращается к индексам при использовании переданных констант, значения которых находятся за пределами имеющегося диапазона статистики оптимизатора, поскольку статистика оптимизатора может быть устаревшей.

Примечание
Значения счетчиков idx_tup_read и idx_tup_fetch могут отличаться даже без использования сканирований битовых карт, поскольку idx_tup_read подсчитывает записи индекса, извлеченные из индекса, в то время как idx_tup_fetch подсчитывает число живых строк, извлеченных из таблицы. Последнее значение будет меньше, если какие-либо мертвые или еще не зафиксированные строки извлекаются с использованием индекса или если какие-либо выборки из таблицы не производятся при выполнении сканирования только по индексу.

pg_statio_all_tables

Таблица 16. Представление pg_statio_all_tables

колонка | Тип | Описание
relid | oid | OID таблицы
schemaname | name | Имя схемы, в которой находится эта таблица
relname | name | Имя этой таблицы
heap_blks_read | bigint | Количество дисковых блоков, прочитанных из этой таблицы
heap_blks_hit | bigint | Количество попаданий в буфер в этой таблице
idx_blks_read | bigint | Количество дисковых блоков, прочитанных по всем индексам в этой таблице
idx_blks_hit | bigint | Количество попаданий в буфер во всех индексах этой таблицы
toast_blks_read | bigint | Количество дисковых блоков, прочитанных из таблицы TOAST этой таблицы (если есть)
toast_blks_hit | bigint | Количество попаданий в буфер в таблице TOAST этой таблицы (если есть)
tidx_blks_read | bigint | Количество дисковых блоков, прочитанных из индексов таблицы TOAST этой таблицы (если есть)
tidx_blks_hit | bigint | Количество попаданий в буфер в индексах таблицы TOAST этой таблицы (если есть)

Представление pg_statio_all_tables будет содержать по одной строке для каждой таблицы в текущей базе данных (включая таблицы TOAST), показывая статистику ввода-вывода для этой конкретной таблицы. Представления pg_statio_user_tables и pg_statio_sys_tables содержат ту же информацию, но отображают только пользовательские и системные таблицы соответственно.

pg_statio_all_indexes

Таблица 17. Представление pg_statio_all_indexes

колонка | Тип | Описание
relid | oid | OID таблицы для этого индекса
indexrelid | oid | OID этого индекса
schemaname | name | Имя схемы, в которой находится этот индекс
relname | name | Имя таблицы для этого индекса
indexrelname | name | Наименование этого индекса
idx_blks_read | bigint | Количество дисковых блоков, прочитанных из этого индекса
idx_blks_hit | bigint | Количество попаданий в буфер в этом индексе

Представление pg_statio_all_indexes будет содержать по одной строке для каждого индекса в текущей базе данных, показывая статистику ввода-вывода для этого конкретного индекса. Представления pg_statio_user_indexes и pg_statio_sys_indexes содержат ту же информацию, но отфильтрованы так, чтобы показывать только пользовательские и системные индексы соответственно.

pg_statio_all_sequence

Таблица 18. Представление pg_statio_all_sequence

колонка | Тип | Описание
relid | oid | OID последовательности
schemaname | name | Имя схемы, в которой находится эта последовательность
relname | name | Имя этой последовательности
blks_read | bigint | Количество дисковых блоков, прочитанных из этой последовательности
blks_hit | bigint | Количество попаданий в буфер в этой последовательности

Представление pg_statio_all_sequence будет содержать по одной строке для каждой последовательности в текущей базе данных, показывая статистику ввода-вывода для этой конкретной последовательности.

pg_stat_user_functions

Таблица 19. Представление pg_stat_user_functions

колонка | Тип | Описание
funcid | oid | OID функции
schemaname | name | Имя схемы, в которой находится эта функция
funcname | name | Имя этой функции
calls | bigint | Сколько раз эта функция была вызвана
total_time | double precision | Общее время, потраченное на эту функцию и все другие функции, вызываемые ею, в миллисекундах
self_time | double precision | Общее время, потраченное на саму функцию, не включая другие вызываемые ею функции, в миллисекундах

Представление pg_stat_user_functions будет содержать одну строку для каждой отслеживаемой функции, показывающую статистику о выполнении этой функции. Параметр track_functions определяет, какие именно функции отслеживаются.

Функции статистики

Другие способы просмотра статистики можно настроить, написав запросы, которые используют те же базовые функции доступа к статистике, которые используются стандартными представлениями, показанными выше. За подробностями, такими как имена функций, обращайтесь к определениям стандартных представлений (например, в psql вы можете выполнить \d+ pg_stat_activity). Функции доступа к статистике для каждой базы данных принимают OID базы данных в качестве аргумента, чтобы определить, в какой базе данных будет выполняться их работа. Функции для каждой таблицы и для индекса принимают OID таблицы или индекса. Функции для статистики по функциям принимают функцию OID. Обратите внимание, что с этими функциями можно видеть только таблицы, индексы и функции лишь в текущей базе данных.

Дополнительные функции, связанные со сбором статистики, перечислены в таблице 20.

Таблица 20. Дополнительные статистические функции

функция
Return Type
Описание

pg_backend_pid()
integer
Идентификатор процесса сервера, обрабатывающего текущий сеанс

pg_stat_get_activity ( integer )
setof record
Возвращает запись информации о бэкенде с указанным PID или одну запись для каждого активного бэкенда в системе, если указано NULL. Возвращенные поля являются подмножеством полей в представлении pg_stat_activity.

pg_stat_get_snapshot_timestamp()
timestamp with time zone
Возвращает время снимка текущей статистики

pg_stat_clear_snapshot()
void
Сбросить текущий снимок статистики

pg_stat_reset()
void
Сброс всех счетчиков статистики для текущей базы данных до нуля (по умолчанию требуются привилегии суперпользователя, но EXECUTE для этой функции может быть предоставлен другим.)

pg_stat_reset_shared (text)
void
Сброс некоторых счетчиков статистики для всего кластера на ноль, в зависимости от аргумента (по умолчанию требуются привилегии суперпользователя, но EXECUTE для этой функции может быть предоставлен другим). Вызов pg_stat_reset_shared (’bgwriter’) обнулит все счетчики, показанные в представлении pg_stat_bgwriter. Вызов pg_stat_reset_shared (’archiver’) обнулит все счетчики, показанные в представлении pg_stat_archiver.

pg_stat_reset_single_table_counters (oid)
void
Сброс статистики для отдельной таблицы или индекса в текущей базе данных до нуля (по умолчанию требуются привилегии суперпользователя, но EXECUTE для этой функции может быть предоставлен другим)

pg_stat_reset_single_function_counters (oid)
void
Сброс статистики для одной функции в текущей базе данных до нуля (по умолчанию требуются привилегии суперпользователя, но EXECUTE для этой функции может быть предоставлен другим)

pg_stat_get_activity, основная функция представления pg_stat_activity, возвращает набор записей, содержащих всю доступную информацию о каждом бэкенд-процессе. Иногда бывает удобнее получить только часть этой информации. В таких случаях может использоваться более старый набор функций доступа к статистике на уровне серверных процессов; они приведены в таблице 21. Эти функции доступа используют идентификационный номер бэкенда, который варьируется от одного до количества текущих активных бэкендов. Функция pg_stat_get_backend_idset предоставляет удобный способ генерации одной строки для каждого активного бэкенда для вызова этих функций. Например, чтобы показать PID и текущие запросы всех бэкендов:

SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
       pg_stat_get_backend_activity(s.backendid) AS query
    FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS s;

Таблица 21. Статистические функции на уровне бэкендов

функция | Return Type | Описание
pg_stat_get_backend_idset() | setof integer | Набор текущих активных идентификационных номеров бэкенда (от 1 до количества активных бэкендов)
pg_stat_get_backend_activity(integer) | text | Текст последнего запроса этого бэкенда
pg_stat_get_backend_activity_start(integer) | timestamp with time zone | Время, когда был запущен самый последний запрос
pg_stat_get_backend_client_addr(integer) | inet | IP-адрес клиента, подключенного к этому бэкенду
pg_stat_get_backend_client_port(integer) | integer | Номер порта TCP, который клиент использует для связи
pg_stat_get_backend_dbid(integer) | oid | OID базы данных, с которой связан этот бэкенд
pg_stat_get_backend_pid(integer) | integer | Идентификатор процесса этого бэкенда
pg_stat_get_backend_start(integer) | timestamp with time zone | Время, когда этот процесс был запущен
pg_stat_get_backend_userid(integer) | oid | OID пользователя, подключенного к этому бэкенду
pg_stat_get_backend_wait_event_type(integer) | text | Имя типа события ожидания, если бэкенд в данный момент ожидает, иначе NULL. См. Таблицу 4 для получения деталей.
pg_stat_get_backend_wait_event(integer) | text | Имя события ожидания, если бэкенд в данный момент ожидает, иначе NULL. См. Таблицу 4 для получения деталей.
pg_stat_get_backend_xact_start(integer) | timestamp with time zone | Время начала текущей транзакции

Просмотр блокировок

Еще одним полезным инструментом для мониторинга активности базы данных является системная таблица pg_locks. Это позволяет администратору базы данных просматривать информацию о блокировках в менеджере блокировок. Например, эту возможность можно использовать для того, чтобы:

Просмотреть все имеющиеся блокировки, все блокировки отношений в конкретной базе данных, все блокировки определенного отношения или все блокировки, удерживаемые определенным сеансом QHB .

Определить отношение в текущей базе данных с наибольшим количеством блокировок (которые могут быть источником проблем среди пользователей базы данных).

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

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

Отчеты о ходе выполнения команд

QHB имеет возможность сообщать о ходе выполнения определенных команд во время их выполнения. В настоящее время командами, которые поддерживают отчеты о ходе выполнения, являются CREATE INDEX, VACUUM и CLUSTER. Этот список может быть расширен в будущем.

Отчет о ходе выполнения CREATE INDEX

Для каждой выполняемой команды CREATE INDEX или REINDEX представление pg_stat_progress_create_index будет содержать одну строку для каждого из бэкендов, создающих в данный момент индексы. Таблицы ниже описывают информацию, которая будет выведена, и предоставляют информацию о том, как ее интерпретировать.

Таблица 22. Просмотр pg_stat_progress_create_index

колонка | Тип | Описание
pid | integer | Идентификатор процесса бэкенда.
datid | oid | OID базы данных, с которой связан этот бэкенд.
datname | name | Имя базы данных, с которой связан этот бэкенд.
relid | oid | OID таблицы, для которой создается индекс.
index_relid | oid | OID создаваемого или переиндексированного индекса. Во время выполнения CREATE INDEX в неблокирующем режиме значение 0.
command | text | Команда, которая выполняется: CREATE INDEX, CREATE INDEX CONCURRENTLY, REINDEX или REINDEX CONCURRENTLY.
phase | text | Текущая фаза обработки создания индекса. Смотри таблицу 23.
lockers_total | bigint | Общее число процессов, требующих ожидания, когда они есть.
lockers_done | bigint | Число процессов, которые завершили ожидание.
current_locker_pid | bigint | Идентификатор процесса, удерживающего блокировку в данный момент.
blocks_total | bigint | Общее количество блоков, подлежащих обработке.
blocks_done | bigint | Количество блоков, уже обработанных на текущем этапе.
tuples_total | bigint | Общее количество кортежей, которые должны быть обработаны в текущей фазе.
tuples_done | bigint | Количество кортежей, уже обработанных в текущей фазе.
partitions_total | bigint | При создании индекса для партиционированной таблицы в этом столбце указывается общее количество партиций, для которых создается индекс.
partitions_done | bigint | При создании индекса для партиционированной таблицы в этом столбце указывается количество партиций, для которых индекс был заполнен.

Таблица 23. Фазы CREATE INDEX

Фаза
Описание

initializing
Инициализация. CREATE INDEX или REINDEX готовится к созданию индекса. Ожидается, что этот этап будет очень коротким.

waiting for writers before build
Ожидание окончания записи перед построением. CREATE INDEX CONCURRENTLY или REINDEX CONCURRENTLY ожидает транзакции с блокировками записи, которые могут читать таблицу. Эта фаза пропускается, когда идет работа в неблокирующем режиме. Столбцы lockers_total, lockers_done и current_locker_pid содержат информацию о ходе выполнения этой фазы.

building index
Построение индекса. Индекс строится с помощью кода, реализующего метод доступа. На этом этапе методы доступа, которые поддерживают отчеты о ходе выполнения, заполняют свои собственные данные о ходе выполнения, и в этом столбце указывается внутренняя фаза. Как правило, данные о ходе выполнения будут содержать столбцы blocks_total и blocks_done, а также могут меняться значения в столбцах tuples_total и tuples_done.

waiting for writers before validation
Ожидание окончания записи перед проверкой. CREATE INDEX CONCURRENTLY или REINDEX CONCURRENTLY ожидают транзакции с блокировками записей, которые потенциально могут завершить обновление данных в таблице. Эта фаза пропускается при выполнении операции в неблокирующем режиме. Столбцы lockers_total, lockers_done и current_locker_pid содержат информацию о ходе выполнения этой фазы.

index validation: scanning index
Проверка индекса: сканирование. CREATE INDEX CONCURRENTLY сканирует индекс в поисках кортежей, которые необходимо проверить. Эта фаза пропускается, когда не в параллельном режиме. Столбцы blocks_total (для общего размера индекса) и blocks_done содержат информацию о ходе выполнения этой фазы.

index validation: sorting tuples
Проверка индекса: сортировка кортежей. CREATE INDEX CONCURRENTLY сортирует выходные данные фазы сканирования индекса.

index validation: scanning table
Проверка индекса: сканирование таблицы. CREATE INDEX CONCURRENTLY сканирует таблицу для проверки кортежей индексов, собранных на предыдущих двух этапах. Эта фаза пропускается, когда не в параллельном режиме. Столбцы blocks_total (для общего размера таблицы) и blocks_done содержат информацию о ходе выполнения этой фазы.

waiting for old snapshots
Ожидание старых снимков. CREATE INDEX CONCURRENTLY или REINDEX CONCURRENTLY ожидает транзакций, которые потенциально могут увидеть таблицу, чтобы выпустить их моментальные снимки. Эта фаза пропускается при выполнении операции в неблокирующем режиме. Столбцы lockers_total, lockers_done и current_locker_pid содержат информацию о ходе выполнения этой фазы.

waiting for readers before marking dead
Ожидание завершения чтения перед выключением старого индекса. REINDEX CONCURRENTLY ожидает завершение транзакций, удерживающих блокировки чтения, прежде чем пометить старый индекс как нерабочий. Эта фаза пропускается при выполнении операции в неблокирующем режиме. Столбцы lockers_total, lockers_done и current_locker_pid содержат информацию о ходе выполнения этой фазы.

waiting for readers before dropping
Ожидание завершения чтения перед удалением старого индекса. REINDEX CONCURRENTLY ожидает завершение транзакций, удерживающих блокировки чтения, прежде чем удалить старый индекс. Эта фаза пропускается при выполнении операции в неблокирующем режиме. Столбцы lockers_total, lockers_done и current_locker_pid содержат информацию о ходе выполнения этой фазы.

Отчет о ходе выполнения VACUUM

Всякий раз, когда VACUUM работает, представление pg_stat_progress_vacuum будет содержать одну строку для каждого бэкенда (включая рабочие процессы автоочистки), который в данный момент производит очистку. Таблицы ниже описывают информацию, которая будет предоставлена, и поясняют, как ее интерпретировать. О ходе выполнения команд VACUUM FULL сообщается через pg_stat_progress_cluster поскольку VACUUM FULL и CLUSTER перезаписывают таблицу, в то время как обычный VACUUM только изменяет ее на месте. См. раздел Отчет о ходе выполнения CLUSTER.

Таблица 24. Просмотр pg_stat_progress_vacuum

колонка
Тип
Описание

pid
integer
Идентификатор процесса бэкенда.

datid
oid
OID базы данных, с которой связан этот бэкенд.

datname
name
Имя базы данных, с которой связан этот бэкенд.

relid
oid
OID стола пылесосят.

phase
text
Текущая фаза обработки вакуума. Смотри Таблицу 25.

heap_blks_total
bigint
Общее количество блоков кучи в таблице. Этот номер сообщается с начала сканирования; добавленные позже блоки не будут (и не обязательно) посещаться этим VACUUM.

heap_blks_scanned
bigint
Количество отсканированных блоков кучи. Поскольку карта видимости используется для оптимизации сканирования, некоторые блоки будут пропущены без проверки; пропущенные блоки включены в это общее количество, так что это число в конечном итоге станет равным heap_blks_total когда вакуум будет завершен. Этот счетчик активируется только тогда, когда фаза scanning heap.

heap_blks_vacuumed
bigint
Количество блоков кучи вакуумировано. Если таблица не имеет индексов, этот счетчик увеличивается только тогда, когда фаза vacuuming heap. Блоки, которые не содержат мертвых кортежей, пропускаются, поэтому счетчик может иногда пропускать вперед с большими приращениями.

index_vacuum_count
bigint
Количество выполненных индексных вакуумных циклов.

max_dead_tuples
bigint
Количество мертвых кортежей, которые мы можем сохранить перед выполнением цикла индекса вакуума, основываясь на maintenance_work_mem.

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

Таблица 25. Фазы VACUUM

фаза
Описание

initializing
VACUUM готовится начать сканирование кучи. Ожидается, что этот этап будет очень коротким.

scanning heap
VACUUM в настоящее время сканирует кучу. Он будет обрезать и дефрагментировать каждую страницу, если потребуется, и, возможно, выполнять замораживание. heap_blks_scanned можно использовать для отслеживания хода сканирования.

vacuuming indexes
VACUUM в настоящее время пылесосит индексы. Если в таблице есть какие-либо индексы, это будет происходить как минимум один раз за вакуум после полного сканирования кучи. Это может происходить несколько раз за вакуум, если maintenance_work_mem недостаточно для хранения количества найденных мертвых кортежей.

vacuuming heap
VACUUM в настоящее время пылесосит кучу. Вакуумирование кучи отличается от сканирования кучи и происходит после каждого случая очистки индексов. Если heap_blks_scanned меньше, чем heap_blks_total, система вернется к сканированию кучи после завершения этой фазы; в противном случае он начнет очищать индексы после завершения этой фазы.

cleaning up indexes
VACUUM в настоящее время очищает индексы. Это происходит после того, как куча была полностью отсканирована и вся очистка индексов и кучи была завершена.

truncating heap
VACUUM в настоящее время урезает кучу, чтобы вернуть пустые страницы в конце отношения к операционной системе. Это происходит после очистки индексов.

performing final cleanup
VACUUM выполняет окончательную очистку. На этом этапе VACUUM будет пылесосить карту свободного пространства, обновлять статистику в pg_class и сообщать статистику pg_class статистики. Когда эта фаза будет завершена, VACUUM закончится.

Отчет о ходе выполнения CLUSTER

Всякий раз, когда CLUSTER или VACUUM FULL работает, представление pg_stat_progress_cluster будет содержать строку для каждого бэкенда, который в данный момент выполняет любую из команд. Таблицы ниже описывают информацию, которая будет сообщена, и предоставляют информацию о том, как ее интерпретировать.

Таблица 26. просмотр pg_stat_progress_cluster

Колонка | Тип | Описание
pid | integer | Идентификатор процесса бэкенда.
datid | oid | OID базы данных, с которой связан этот бэкенд.
datname | name | Имя базы данных, с которой связан этот бэкенд.
relid | oid | OID кластеризованной таблицы.
command | text | Команда, которая выполняется. Либо CLUSTER либо VACUUM FULL.
phase | text | Текущая фаза обработки. Смотри Таблицу 27.
cluster_index_relid | oid | Если таблица сканируется с использованием индекса, это OID используемого индекса; в противном случае это ноль.
heap_tuples_scanned | bigint | Количество отсканированных кучи. Этот счетчик увеличивается только тогда, когда фаза является последовательной seq scanning heap, index scanning heap или writing new heap.
heap_tuples_written | bigint | Количество написанных кучи кортежей. Этот счетчик увеличивается только тогда, когда фаза является последовательной seq scanning heap, index scanning heap или writing new heap.
heap_blks_total | bigint | Общее количество блоков кучи в таблице. Это число сообщается как начало seq scanning heap.
heap_blks_scanned | bigint | Количество отсканированных блоков кучи. Этот счетчик активируется только тогда, когда фаза является последовательной seq scanning heap.
index_rebuild_count | bigint | Количество перестроенных индексов. Этот счетчик увеличивается только тогда, когда фаза rebuilding index.

Таблица 27. Фазы CLUSTER и VACUUM FULL

Фаза | Описание
initializing | Команда готовится начать сканирование кучи. Этот этап должен быть очень коротким.
seq scanning heap | Команда сканирует таблицу с использованием последовательного сканирования.
index scanning heap | CLUSTER в настоящее время сканирует таблицу, используя сканирование индекса.
sorting tuples | CLUSTER в настоящее время сортирует кортежи.
writing new heap | CLUSTER в настоящее время пишет новую кучу.
swapping relation files | В настоящее время команда встраивает вновь созданные файлы.
rebuilding index | В настоящее время команда перестраивает индекс.
performing final cleanup | Команда выполняет окончательную очистку. Когда эта фаза будет завершена, CLUSTER или VACUUM FULL завершатся.

Метрики QHB

Общие замечания

В настоящее время метрики в QHB собираются на уровне всего кластера баз данных и относятся ко всему набору процессов QHB, независимо от того, являются ли они фоновыми или поддерживающими пользовательские подключения. Данные метрик отсылаются на сборщик и агрегатор метрик metricsd, краткое описание которого находится в разделе Сервер метрик. Далее метрики агрегируются и записываются в Graphite, в качестве интерфейса к которому выступает Grafana.

Типы метрик

Gauge. Неотрицательное целое значение. Может быть установлено, увеличено или уменьшено на заданное число.

Counter. Неотрицательное целое значение. Может быть только увеличено на заданное число.

Timer. Неотрицательное целое значение, длительность в наносекундах. Значение может быть только записано, во время агрегации вычисляется ряд статистических характеристик:

sum. Сумма значений

count. Число записанных значений

max. Максимальное из записанных значений

min. Минимальное из записанных значений

mean. Арифметическое среднее

median. Медиана

std. Стандартное квадратичное отклонение

Опционально, список перцентилей

Группы метрик

Системные метрики (группа sys)

Сбор системных метрик регулируется двумя глобальными константами:

qhb_os_monitoring - логическая константа, по умолчанию значение off. При значении on выполняется периодический сбор метрик.

qhb_os_stat_period - период сбора системной статистики в секундах, по умолчанию 30 секунд.

Загрузка и CPU

Наименование | Описание | Тип метрики
sys.load_average.1min | load average за последнюю минуту | gauge
sys.load_average.5min | то же за последние 5 минут | gauge
sys.load_average.15min | то же за последние 15 минут | gauge
sys.cpu.1min | Процент загрузки процессоров за последнюю минуту | gauge
sys.cpu.5min | то же за последние 5 минут | gauge
sys.cpu.15min | то же за последние 15 минут | gauge

Примечание
Значения метрик load average содержат значения с точностью до сотых, однако при передаче данных в силу технических особенностей исходные значения умножаются на 100 и передаются как целые. Поэтому необходимо при выводе данных учитывать эту особенность и делить значения на 100.

load average - усреднённое количество исполняемых и ожидающих потоков за заданный интервал времени (1, 5 и 15 минут). Обычно соответствующие значения выводятся через команды uptime, top либо cat /proc/loadavg Чтобы детальнее ознакомиться с особенностями, связанными с этим показателем, можно ознакомиться с переводом статьи Brendan Gregg: https://habr.com/ru/company/mailru/blog/335326

Если значение этого показателя за последнюю минуту больше, чем за последние 5 и 15 минут, нагрузка растет, если меньше - падает. Однако, этот показатель важен не сам по себе, а по отношению к общему числу процессоров. Дополнительные и производные от load average метрики по загрузке процессоров sys.cpu.min показывают приблизительный процент загрузки процессоров с учетом их количества и рассчитываются по следующей упрощенной формуле:

sys.cpu.<N>min = sys.load_average.<N>min / cpu_count * 100
где cpu_count - количество процессоров в системе, а N принимает значения 1,5 или 15.

Количество процессоров рассчитывается как произведение количества физических сокетов, ядер на сокет и нитей на ядро. Команда lscpu выводит все необходимые данные в следующих строках (пример вывода):

Thread(s) per core:              2
Core(s) per socket:              4
Socket(s):                       1

В данном случае cpu_count = 2 * 4 * 1 = 8.

Альтернативным и более простым методом может быть получение этого значения через команду nproc.

Таким образом, загрузка в 100% будет достигнута в данном случае, если величина load average будет стремиться к 8. Однако, эти расчеты и значения будут иметь довольно приблизительный и даже условный характер, что показывает приведенная выше по ссылке статья.

Использование памяти

Наименование | Описание | Тип метрики
sys.mem.total | общий размер установленной памяти RAM | gauge
sys.mem.used | используемая память | gauge
sys.mem.free | неиспользуемая память | gauge
sys.mem.available | память, доступная для запускаемых приложений (не включая swap, но учитывая потенциально освобождаемую память, занимаемую страничным кэшем) | gauge
sys.swap.total | общий размер файла подкачки | gauge
sys.swap.free | неиспользуемая память файла подкачки | gauge

Значения метрик соответствуют следующим полям из вывода утилиты free (значения в kB, т.е. соответствуют выводу free -k):

Метрика | Поле утилиты free
sys.mem.total | Mem:total
sys.mem.used | Mem:used
sys.mem.free | Mem:free
sys.mem.available | Mem:available
sys.swap.total | Swap:total
sys.swap.free | Swap:free

Величину, соответствующую выводимому в утилите free значению Mem:buff/cache, можно рассчитать по формуле:

Mem:buff/cache = Mem:total - Mem:used - Mem:free

Таким образом, в Графане можно, используя функцию diffSeries, рассчитывать и выводить это значение на основании других имеющихся данных.

Значение Mem:shared (данные виртуальной файловой системы tmpfs) не выводится через метрики.

Значение Swap:used можно рассчитать по формуле:

Swap:used = Swap:total - Swap:free

Это значение можно выводить в Графане также в виде рассчитываемой величины через функцию diffSeries.

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

Использование дискового пространства

Наименование | Описание | Тип метрики
sys.disk_space.total | объем дисковой системы, на которой находится каталог с данными (в байтах) | gauge
sys.disk_space.free | свободное пространство дисковой системы, на которой находится каталог с данными (в байтах) | gauge

Метрики относятся к дисковой системе, на которой расположен каталог с файлами базы данных. Этот каталог определяется параметром командной строки -D при запуске базы данных либо переменной среды $PGDATA. Параметр data_directory в файле параметров qhb.conf может переопределять расположение каталога с данными.

Другие системные метрики

Наименование | Описание | Тип метрики
sys.processes | общее количество запущенных в системе процессов | gauge
sys.uptime | количество секунд, прошедших с начала запуска системы | gauge

Метрики чтения и записи блоков на уровне инстанса QHB (группа db_stat)

Наименование | Описание | Тип метрики
qhb.db_stat.blocks_fetched | количество полученных при чтении блоков | counter
qhb.db_stat.blocks_hit | количество блоков, найденных в кэше при чтении | counter
qhb.db_stat.blocks_read_time | время чтения блоков, в миллисекундах | counter
qhb.db_stat.blocks_write_time | время чтения блоков, в миллисекундах | counter

На основании метрик qhb.db_stat.blocks_fetched и qhb.db_stat.blocks_hit рассчитывается коэффициент попадания в кэш:
k = blocks_hit / blocks_fetched * 100% Хорошим уровнем обычно считается значение более 90%. Если значение коэффициента существенно ниже этой отметки, желательно рассмотреть возможность увеличения объема буферного кэша.

Метрики фонового процесса записи (группа bgwr)

Наименование | Описание | Тип метрики
qhb.bgwr.checkpoints_timed | количество запланированных контрольных точек, которые были выполнены | counter
qhb.bgwr.checkpoints_req | количество запрошенных контрольных точек, выполненных вне очереди запланированных | counter
qhb.bgwr.checkpoint_write_time | время, потраченное на этап обработки контрольной точки, где файлы записываются на диск, в миллисекундах | counter
qhb.bgwr.checkpoint_sync_time | количество времени, потраченное на часть обработки контрольной точки, где файлы синхронизируются с диском, в миллисекундах | counter
qhb.bgwr.buffers_checkpoint | количество буферов, записанных во время контрольных точек | counter
qhb.bgwr.buffers_clean | количество буферов, записанных фоновым процессом записи | counter
qhb.bgwr.maxwritten_clean | количество раз, когда процесс записи в фоновом режиме останавливал сброс грязных страниц, поскольку записывал слишком много буферов | counter
qhb.bgwr.buffers_backend | количество буферов, записанных непосредственно бэкендом | counter
qhb.bgwr.buffers_backend_fsync | сколько раз бэкенд выполнял вызов fsync сам (обычно их обрабатывает фоновый процесс записи, даже когда бэкенд выполняет запись самостоятельно) | counter
qhb.bgwr.buffers_alloc | количество выделенных буферов | counter

Данные по перечисленным метрикам отображаются в разделе "Контрольные точки и операции с буферами" дашборда "QHB". Обычно при выполнении запланированных контрольных точек сначала происходит запись информации о начале контрольной точки, затем в течение некоторого времени идет сброс блоков на диск и по окончании контрольной точки фиксируется информация о продолжительности записи и синхронизации данных. В случае обработки запланированной контрольной точки запись блоков равномерно распределяется во времени согласно параметрам настройки, чтобы снизить влияние этого процесса на общий ввод-вывод. При запрошенных через команду контрольных точках сброс блоков происходит сразу, без искусственной задержки.

Метрики по временным файлам и таблицам QHB

Наименование | Описание | Тип метрики
qhb.temp_stat.temp_files | количество временных файлов, созданных за период агрегации | counter
qhb.temp_stat.temp_bytes | размер временных файлов в байтах за период агрегации | counter
qhb.temp_stat.temp_tables | количество временных таблиц, созданных за период агрегации | counter
qhb.temp_stat.temp_table_bytes | размер временных таблиц в байтах за период агрегации | counter

Данные агрегируются за период агрегации коллектора метрик. Обновление данных происходит по окончании соответствующей операции, например, при создании временной таблицы с указанием DROP ON COMMIT обновление статистики произойдет после выполнения команды COMMIT при удалении временной таблицы. В некоторых случаях размеры временных файлов и таблиц могут быть весьма существенными.

Метрики процесса архивации журналов предзаписи (группа wal)

Наименование | Описание | Тип метрики
qhb.wal.archived | количество успешно выполненных операций архивации журналов предзаписи | counter
qhb.wal.failed | количество попыток архивации, завершившихся сбоем | counter
qhb.wal.archive_time | время, потраченное на копирование файлов журналов, в наносекундах | counter

Эти метрики работают в том случае, если настроена архивация журналов предзаписи. Для этого необходимо установить archive_mode в значение on и определить команду архивации в параметре archive_command.

Метрики по транзакциям (группа transaction)

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

Наименование | Описание | Тип метрики
qhb.transaction.commit | количество фиксаций транзакций | counter
qhb.transaction.rollback | количество отмен транзакций | counter
qhb.transaction.deadlocks | количество дедлоков | counter

Коэффициент подтверждения транзакций рассчитывается как процентное отношение успешных завершений транзакций к сумме подтверждений и откатов транзакций:

k = commit/(commit + rollback)*100%

Обычно значение стремится к 100%, т.к. чаще всего транзакции завершаются успешно. Существенная доля отмен транзакций может говорить о том, что в системе существуют проблемы.

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

Метрики событий ожиданий (группа wait)

Данный набор метрик полностью соответствует набору стандартных событий ожидания. Метрики имеют префикс qhb.wait. Далее в наименовании идет класс события ожидания и через точку название события ожидания. В текущем релизе имена метрик ограничены в размере и имеют в наименовании максимум 31 символ. Все метрики по событиям ожиданий имеют тип counter, однако, значение содержит продолжительность времени в микросекундах, которые эти ожидания заняли в совокупности во всех сессиях за период агрегации.

Примечание
Если в течение периода наблюдения работало множество пользовательских подключений, которые находились в состоянии ожидания, суммарное значение времени ожидания может многократно превзойти этот период. Например, если в течение 10 секунд 1000 сессий провели в ожиданиях по одной секунде, суммарное время ожиданий составит 1000 секунд.

Метрики с типами событий ожидания Lock, LWLock, IO и IPC отображаются в разделе "События ожидания" дашборда QHB. Значения метрик выводятся в микросекундах (автоматически переводясь в другие единицы при увеличении значений). На существующих графиках выводятся не все события ожиданий, а только по пять самых значимых по величине на каждом графике. Разные события ожиданий могут иметь сильно отличающиеся по величине продолжительности. Значительные колебания значений могут отражать возникающие проблемы.

Наиболее значимые события ожиданий

Lock.extend, ожидание расширения отношения. Становится заметным при активном росте таблиц. Необходимость выделения новых блоков приводит к некоторым задержкам, которые отражаются в этой метрике.

Lock.transactionid, ожидание завершения транзакции. Событие ожидания возникает в том случае, если транзакция вынуждена ждать окончания обработки предыдущих транзакций, которые также получают подтверждение своего окончания.

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

LWLock.WALWriteLock, ожидание записи буферов WAL на диск. Часто является лидером среди событий ожиданий этого типа, т.к. операции с диском являются наиболее медленными в этой группе событий ожидания.

LWLock.wal_insert, ожидание вставки WAL в буфер памяти.

LWLock.buffer_content, ожидание чтения или записи страницы данных в памяти. Возникает и становится существенным при интенсивном вводе-выводе.

LWLock.buffer_mapping, ожидание связывания блока данных с буфером в буферном пуле.

LWLock.lock_manager, ожидание добавления или проверки блокировок для бэкендов. Событие становится значимым при частых транзакциях.

Метрики буферного менеджера (группа bufmgr)

Ниже представлены метрики, касающиеся механизмов управления памятью.

Наименование | Операция | Описание | Тип метрики | Агрегат
qhb.bufmgr.BufferAlloc | чтение данных | количество поисков буфера | timer | count
qhb.bufmgr.BufferAlloc | чтение данных | сумма времени на поиск буфера | timer | sum
qhb.bufmgr.happy_path | чтение данных | количество поисков, когда буфер нашёлся сразу | timer | count
qhb.bufmgr.happy_path | чтение данных | сумма времени на поиск, когда буфер нашёлся сразу | timer | sum
qhb.bufmgr.cache_miss | чтение данных | количество промахов кэша буферов | timer | count
qhb.bufmgr.cache_miss | чтение данных | сумма времени обработки промахов кэша буферов | timer | sum
qhb.bufmgr.disk_read | чтение данных | количество чтений страницы с диска (асинхронно) | timer | count
qhb.bufmgr.flush_dirty | чтение данных | количество выгрузки страницы на диск (асинхронно) | timer | count
qhb.bufmgr.retry_counter | чтение данных | количество повторных обработок промаха | counter |
qhb.bufmgr.strategy_pop_cnt | чтение данных | количество срабатываний специальной стратегии получения или вытеснения буфера | counter |
qhb.bufmgr.strategy_reject_cnt | чтение данных | количество забракованных буферов, предложенных специальной стратегией | counter |
tarq_cache.allocate | чтение данных | количество поисков в TARQ | timer | count
tarq_cache.allocate | чтение данных | сумма времени на поиск в TARQ | timer | sum
tarq_cache.allocate_new | чтение данных | количество выборов исключаемого блока в TARQ | timer | count
tarq_cache.rollback | чтение данных | количество откатов вытеснения в TARQ | timer | count
tarq_cache.rollback | чтение данных | сумма времени на откаты вытеснения в TARQ | timer | sum
tarq_cache.touch | чтение данных | сумма времени на учёт популярных страниц в TARQ | timer | sum

Метрики пула соединений QCP

Ниже описаны метрики пула соединений, характеризующие работу пула.

Наименование | Операция | Описание | Тип метрики
backend_held | выполнение запроса | время, в течение которого qcp удерживает соединение к серверу привязанным к какому-то клиенту | timer
queue | выполнение запроса | количество запросов в очереди на текущий момент | gauge
relay.wait_request | выполнение запроса | время ожидания получения запроса от клиента | timer
relay.wait_response | выполнение запроса | время ожидания ответа на запрос от сервера | timer

Включение и выключение записи значений для групп метрик

В QHB имеется механизм, который позволяет включать и выключать отправку групп метрик. В каталоге данных расположен файл настроек metrics_config.toml, в котором каждая строка соответствует группе метрик:

[is_enabled]
default = true
sys = true
db_stat = true
bgwr = true
wal = true
transaction = true
wait = true
bufmgr = true
temp_stat = true

Переменные принимают значения true (отправка метрик группы разрешена) или false (отправка запрещена).

Группа метрик | Описание | Значение по умолчанию
default | Группа по умолчанию (различные метрики, не выделенные в отдельные группы) | true
sys | Метрики операционной системы | true
db_stat | Метрики чтения и записи блоков | true
bgwr | Метрики фонового процесса записи (background writer) | true
wal | Метрики архивации WAL-файлов | true
transaction | Метрики транзакций | true
wait | Метрики событий ожидания | true
bufmgr | Метрики механизмов управления памятью | true
temp_stat | Метрики по данным временных файлов | true

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

Помимо задания значений настроек через файл параметров, имеется возможность менять эти значения через функции. Изменения сразу же отображаются в файле конфигурации.

Функции включения и выключения отправки метрик.

Имя | Тип ответа | Описание
metrics_config_enable_group(group_name: cstring, respect_transient_settings: boolean) | void | Включение отправки значений для метрик заданной группы
metrics_config_disable_group(group_name: cstring, respect_transient_settings: boolean) | void | Выключение отправки значений для метрик заданной группы
metrics_config_transient_set(backend_pid: int4, group_name: cstring, option_value: boolean) | void | Установка временной возможности отправки значений группы метрик для заданного бэкенда

С помощью второго параметра с названием respect_transient_settings в функциях metrics_config_enable_group и metrics_config_disable_group можно задавать, отдавать ли приоритет параметрам отправки групп метрик, установленным на уровне отдельных бэкендов.

С помощью функции metrics_config_transient_set можно включить или выключить отправку значений заданной группы метрик для конкретного бэкенда. Настройка будет действовать в течение всего времени жизни бэкенда либо до вызова функции metrics_config_enable/disable_group со вторым параметром respect_transient_settings = false, что в этом случае распространит действие команды также на те бэкенды, в которых устанавливалось собственное значение параметров.

При запуске бэкенда с ранее использованным номером Pid все временные настройки обнуляются для данного Pid.

Примечание
Все сказанное об выключении записи групп метрик не касается метрик, которые передаются через SQL-функции. При использовании этих функций метрики записываются всегда.

Дашборды метрик QHB для Grafana

Дашборды QHB для Grafana расположены в репозитории по следующей ссылке.

QHB поставляется совместно с сервером метрик, который записывает данные метрик в Graphite и интерфейсом к которым служит Grafana. Текущий набор дашбордов для Grafana поставляется в качестве самодокументируемых образцов, на основе которых пользователи, при необходимости, могут самостоятельно создать дашборда, более соответствующие их потребностям. Вместе с тем, поставляемые дашборды могут использоваться и в исходном виде.

Импорт дашбордов

Экспорт JSON-описания дашбордов выполнен в Grafana 6.7.2.

Перед импортом JSON-описания необходимо решить, будут ли названия метрик содержали в качестве префикса имя хоста. Именно таким образом устроены наименования метрик внутри дашбордов и этот вариант рекомендуется оставить. В начале имен метрик добавлена переменная $server_name, по умолчанию для нее выбрано значение your_host_name. Перед импортом можно заменить в JSON-файлах это значение на наименование одного из хостов. В дальнейшем в этой переменной через интерфейс Grafana можно будет добавить через запятую все имена хостов, с которых будут собираться метрики. Это позволит быстро переключаться при просмотре метрик с одного хоста на другой. Если такая схема использоваться не будет (в случае, если метрики будут использоваться с единственного хоста), можно удалить в файлах JSON во всех именах метрик префикс $server_name до проведения импорта описания JSON. Однако, это более трудоемкий вариант и его выбирать не рекомендуется.

Для импорта описаний дашбордов необходимо выполнить следующие шаги:

В меню "Dashboards" вашего сайта Grafana выбрать пункт "Manage".

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

Находясь в выбранной папке, выбрать в правой верхней части страницы пункт "Import".

На открывшейся странице можно либо нажать справа вверху кнопку "Upload .json file" и загрузить файл либо вставить содержание JSON-файла в поле под заголовком "Or paste JSON" и нажать кнопку "Load". После этого нужно заполнить необходимые параметры и выполнить загрузку JSON-описания.

Дашборд "Операционная система"

Дашборд представляет основные системные показатели:

Время работы инстанса QHB

Load Average

Использование CPU

Использование памяти

Использование дисковой системы, на которой расположен каталог баз данных

Дашборд "QHB"

Дашборд содержит несколько разделов:

Транзакции

Чтение и запись блоков

События ожидания

Контрольные точки и операции с буферами

Архивация WAL

В каждом разделе представлены наборы тематических панелей, отражающие основные показатели.

Настройка сбора метрик

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

Настройка сервера метрик

Настройка сервера метрик описана в разделе Сервер метрик.
Рекомендуется в параметре prefix конфигурационного файла /etc/metricsd/config.yaml сервера метрик прописать имя хоста, на котором он работает. Если сделать это для каждого сервера, все метрики будут организованы иерархически, и первый уровень иерархии будет уровнем серверов. В именах метрик в предлагаемых дашбордах для этих целей присутствует переменная $server_name. Подразумевается, что на хосте работает только один кластер баз данных.

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

Для настройки отправки метрик до релиза QHB 1.3.0 необходимо в qhb.conf установить параметр metrics_collector_id в значение, с которым запускается сборщик метрик, например, 1001. Начиная с релиза QHB 1.3.0 вместо metrics_collector_id используется параметр metrics_collector_path, который по умолчанию имеет значение @metrics-collector (представляет собой путь к Unix domain socket),

сервер метрик по умолчанию запускается именно на этом адресе.

Для настройки отправки аннотаций необходимо в qhb.conf прописать следующие параметры:
grafana.address - адрес Графаны, например http://localhost:3000
grafana.token - необходимо указать токен, полученный в Графане по адресу http://localhost:3000/org/apikeys

Пример настроек в qhb.conf для отправки метрик и аннотаций

# До релиза QHB 1.3.0
# metrics_collector_id = 1001

# С релиза QHB 1.3.0:
metrics_collector_path = @metrics-collector

grafana.address = 'http://localhost:3000'
grafana.token = 'eyJrIjoiNGxTaloxMUNTQkFUMTN0blZqUTN6REN6OWI5YjM1MzMiLCJuIjoidGVzdCIsImlkIjoxfQ=='

Примечание
При необходимости записи данных метрик в CSV-файлы, в качестве значения параметра metrics_collector_path нужно указать путь к файлу Unix-сокета, например, /tmp/metrics-collector.sock. Это же значение нужно указать в параметре bind_addr секции collection в настройках сервера метрик (/etc/metricsd/config.yaml).

Для сбора системных метрик (Дашборд "Операционная система") необходимо установить параметр qhb_os_monitoring в значение on. Можно также задать период сбора системной статистики qhb_os_stat_period, значение по умолчанию которого равно 30 секундам. Не рекомендуется задавать слишком низкое значение для этого параметра, т.к. сбор системной статистики требует некоторых ресурсов.

В файле параметров можно прописать:

qhb_os_monitoring = on
qhb_os_stat_period = 60 # если период по умолчанию в 30 секунд не устраивает

Либо выполнить команды:

alter system set qhb_os_monitoring = on;
alter system set qhb_os_stat_period = 60;
select pg_reload_conf();

Настройка загрузки данных метрик в таблицу базы данных

Для загрузки данных метрик в базу данных используется модуль QDLM (Quantum Data Loader for Metrics), который, в свою очередь, основан на функциональности прямой загрузки данных (предварительная установка QDL для работы QDLM не требуется). Функциональность прямой загрузки данных работает в обход стандартного механизма и позволяет в разы сократить время, необходимое для загрузки данных. Однако, следует помнить о том, что при загрузке данные не попадают в стандартные логи изменений и данные не появятся при восстановлении базы из бэкапа и последующего применения логов.

Установка QDLM

Установка QDLM осуществляется аналогично другим модулям QHB. Для получения более подробной информации можно воспользоваться краткой инструкцией по начальной загрузке и установке.

Настройка параметров в файле параметров QDLM

В /etc/qdlm/config.yml нужно прописать значения для следующих параметров:

# каталог CSV-файлов
csv_directory: "/var/lib/qhb/csv_process"

# параметры соединения с базой данных
qhb_connection: "host=localhost user=qhb"

# файл параметров прямой загрузки данных
qdl_config: "/etc/qdlm/qdl.yml"

# каталог данных
qhb_data: "/var/lib/qhb/data"

Файл параметров прямой загрузки данных метрик

Предварительно настроенный файл параметров прямой загрузки данных метрик копируется при установке QDLM и не требует изменений. Путь к нему по умолчанию: /etc/qdlm/qdl.yml.

Запуск утилиты QDLM

Запуск прямой загрузки данных метрик производится следующей командой:

qdlm load -c /etc/qdlm/config.yml

Примечание
В настоящее время обработка данных CSV-файлов не полностью автоматизирована. Для удобства обработки CSV-файлов следует создать каталог, в котором CSV-файлы создаются (.../csv_create), каталог, где они обрабатываются (.../csv_process) и каталог, в который они перемещаются после обработки (.../csv_archive). При перемещении из каталога создания в каталог обработки нужно брать только те файлы, которые уже полностью заполнены. Для этого достаточно перемещать в каталог обработки только те файлы, дата изменения которых является более ранней, чем текущий момент минус период ротации CSV-файлов, чтобы последний файл всегда оставался доступным для записи текущих данных. После успешной обработки CSV-файлов через QDLM необходимо переместить обработанные файлы из каталога обработки в архивный каталог, т.к. иначе при последующем запуске начнется повторная обработка уже обработанных данных.

Результаты обработки

После запуска обработки CSV-файлов первым шагом проверяется, созданы ли партиционированная таблица metric_archive и последовательность seq_metric_chunk в схеме public базы данных, указанной в параметрах. Если эти объекты еще не созданы, они автоматически создаются.

Описание столбцов таблицы metric_archive:

Столбец | Тип | Описание
instance_id | text | Значение, указанное в параметре qhb_instance сервера метрик. В общем случае данные могут поступать из различных источников и относиться к разным базам данных.
metric_dt | timestamp | Дата и время формирования метрики
metric_type_id | smallint | Тип метрики: 0 - Counter (счетчик), 1 - Gauge (уровень), 2 - Timer (время)
metric_name | text | Название метрики
metric_name_ext | text | Наименования агрегатов (std, max, min, sum, count, median и перцентили) для метрик типа Timer
metric_value | double precision | Значение метрики

Данные каждого CSV-файла обрабатываются аналогично тому, как обрабатываются данные при загрузке таблицы через QDL. После обработки очередного файла в базе данных появляется таблица metric_archive_<N>, где <N> - номер из последовательности seq_metric_chunk. По окончании обработки таблица становится партицией партиционированной таблицы metric_archive. Партиционирование идет по двум столбцам - instance_id и metric_dt. При присоединении таблицы в качестве партиции задаются минимальная и максимальная границы значений столбца metric_dt. В дальнейшем при обращении к таблице metric_archive при указании условий по instance_id и metric_dt будет автоматически происходить выбор соответствующих партиций.

Примеры использования метрик в SQL-функциях

Помимо встроенных метрик, пользователи могут использовать свои метрики через следующие функции SQL.

Тип метрик Timer

Используется при фиксации промежутка времени, единицы измерения - наносекунды.

select qhb_timer_report('qhb.timer.nano',10000000000 /* 10 секунд в наносекундах */);

Тип метрик Counter

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

select qhb_counter_increase_by('qhb.example.counter',10);

Тип метрик Gauge

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

select qhb_gauge_update('qhb.gauge_example.value', 10); /* Установка значения */
select qhb_gauge_add('qhb.gauge_example.value',1); /* Увеличение значения */
select qhb_gauge_sub('qhb.gauge_example.value',1); /* Уменьшение значения */

Аннотации

Используются, если нужно добавить комментарий к данным метрик. Первый параметр функции - текст комментария, последующие параметры - теги.

select qhb_annotation('Начало выполнения теста', 'test','billing'); /* Текст аннотации и два тега */

Мониторинг использования диска

В этой главе рассказывается, как отслеживать использование диска в системе баз данных QHB.

Определение использования диска

У каждой таблицы есть основной файл кучи, где хранится большая часть данных. Если в таблице есть какие-либо столбцы с потенциально широкими значениями, также может быть файл TOAST, связанный с таблицей, который используется для хранения значений, которые слишком широки, чтобы удобно помещаться в основной таблице (см. раздел TOAST). В таблице TOAST будет один действительный индекс, если он есть. Также могут быть индексы, связанные с базовой таблицей. Каждая таблица и индекс хранятся в отдельном файле на диске - возможно, в нескольких файлах, если размер файла превышает один гигабайт. Соглашения об именах этих файлов описаны в разделе Структура файлов базы данных.

Вы можете отслеживать дисковое пространство тремя способами: с помощью функций SQL, перечисленных в разделе Функции управления объектами базы данных, с помощью модуля oid2name или с помощью ручной проверки системных каталогов. Функции SQL являются самыми простыми в использовании и обычно рекомендуются. В оставшейся части этого раздела показано, как это сделать путем проверки системных каталогов.

Используя psql в недавно очищенной или проанализированной базе данных, вы можете создавать запросы, чтобы увидеть использование диском любой таблицы:

SELECT pg_relation_filepath(oid), relpages FROM pg_class WHERE relname = 'customer';

 pg_relation_filepath | relpages
----------------------+----------
 base/16384/16806     |       60
(1 row)

Каждая страница обычно составляет 8 килобайт. (Помните, что relpages обновляется только с помощью VACUUM, ANALYZE и нескольких команд DDL, таких как CREATE INDEX). Путь к файлу представляет интерес, если вы хотите проверить файл таблицы непосредственно.

Чтобы показать пространство, используемое таблицами TOAST, используйте запрос, подобный следующему:

SELECT relname, relpages
FROM pg_class,
     (SELECT reltoastrelid
      FROM pg_class
      WHERE relname = 'customer') AS ss
WHERE oid = ss.reltoastrelid OR
      oid = (SELECT indexrelid
             FROM pg_index
             WHERE indrelid = ss.reltoastrelid)
ORDER BY relname;

       relname        | relpages
----------------------+----------
 pg_toast_16806       |        0
 pg_toast_16806_index |        1

Вы также можете легко отобразить размеры индекса:

SELECT c2.relname, c2.relpages
FROM pg_class c, pg_class c2, pg_index i
WHERE c.relname = 'customer' AND
      c.oid = i.indrelid AND
      c2.oid = i.indexrelid
ORDER BY c2.relname;

      relname      | relpages
-------------------+----------
 customer_id_index |       26

С помощью этой информации легко найти самые большие таблицы и индексы:

SELECT relname, relpages
FROM pg_class
ORDER BY relpages DESC;

       relname        | relpages
----------------------+----------
 bigtable             |     3290
 customer             |     3144

Ошибка диска

Самая важная задача администратора диска для администратора базы данных - убедиться, что диск не переполнен. Заполненный диск с данными не приведет к повреждению данных, но может помешать выполнению полезных действий. Если диск с файлами WAL заполнится, может возникнуть паника сервера базы данных и последующее отключение.

Если вы не можете освободить дополнительное место на диске, удалив другие объекты, вы можете переместить некоторые файлы базы данных в другие файловые системы, используя табличные пространства. См. раздел Табличные пространства для получения дополнительной информации об этом.

Заметка
Некоторые файловые системы работают плохо, когда они почти заполнены, поэтому не ждите, пока диск полностью не заполнится, чтобы принять меры.

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

Прочность и журнал упреждающей записи

В этой главе объясняется, как журнал упреждающей записи (Write Ahead Log, WAL) используется для обеспечения эффективной и надежной работы.

Прочность

Прочность является важным свойством любой серьезной системы баз данных, и QHB делает все возможное, чтобы гарантировать надежную работу. Одним из аспектов надежной работы является то, что все данные, записанные в завершенной транзакции, должны храниться в энергонезависимой памяти, защищенной от потери питания, сбоя операционной системы и аппаратного сбоя (за исключением, разумеется, отказа самой энергонезависимой области). Успешная запись данных в постоянное хранилище компьютера (дисковое или аналогичное) обычно соответствует этому требованию. На самом деле, даже если компьютер серьезно поврежден, если диски сохранились, их можно перенести на другой компьютер с аналогичным оборудованием, и все зафиксированные транзакции останутся без изменений.

Хотя принудительное периодическое сохранение данных на дисках и других устройствах может показаться простой операцией, это не так. Поскольку дисковые такие значительно медленнее, чем основная память и процессор, существует несколько уровней кэширования между основной памятью компьютера и дисками. Во-первых, это буферный кэш операционной системы, который кэширует часто запрашиваемые дисковые блоки и объединяет записи на диск. К счастью, все операционные системы предоставляют приложениям возможность форсировать запись из буферного кэша на диск, и QHB использует эти функции. (См. параметр wal_sync_method, для тонкой настройки).

Далее,кэш может быть в дисковом контроллере - это особенно распространено на RAID-контроллерах. Некоторые из этих кэшей являются сквозными, что означает, что записи отправляются на диск сразу после их поступления. У другие есть обратная запись, то есть данные отправляются на диск через некоторое время. Такие кэши могут быть угрозой надежности, поскольку память в кэше дискового контроллера может оказаться энергозависимой и теряет свое содержимое при сбое питания - качественные контроллеры имеют резервные блоки батарей (Battery Backup Unit, BBU), это означает, что в составе контроллера есть батарея, которая поддерживает питание кэша в случае потери питания системы. После восстановления питания данные будут корректно записаны на дисководы.

И, наконец, большинство дисководов имеют внутренний кэш. Некоторые из них имеют кэш со сквозной, а другие с обратной записью, и для кэшей дисков с обратной записью существуют те же проблемы, что и для дисковых кэшей контроллера. Диски IDE и SATA потребительского уровня особенно часто имеют кэши обратной записи, который не выдержат сбоя питания. Многие твердотельные накопители (SSD) также имеют энергозависимые кэши обратной записи.

Эти кэши обычно можно выключить - однако способ сделать это зависит от операционной системы и типа диска. В Linux диски IDE и SATA можно запрашивать с помощью hdparm -I. Кэширование записи включено, если рядом с Write cache есть *. hdparm -W 0 может использоваться для выключения кэширования записи. Диски SCSI могут быть запрошены с помощью sdparm. Используйте sdparm --get=WCE чтобы проверить, включен ли кэш записи, и sdparm --clear=WCE чтобы выключить его.

Диски SATA последних моделей (те, которые следуют стандарту ATAPI-6 или более новому) предлагают команду очистки кэша диска FLUSH CACHE EXT, в то время как диски SCSI уже давно поддерживают аналогичную команду SYNCHRONIZE CACHE. Эти команды не доступны напрямую для QHB, но некоторые файловые системы (например, ZFS, ext4 ) могут использовать их для сброса данных на диски на дисках с обратной записью. К сожалению, такие файловые системы ведут себя неоптимально в сочетании с дисковыми контроллерами с BBU. В таких системах команда синхронизации направляет все данные из кэша контроллера на диски, что исключает большую часть преимуществ BBU. Если QHB установлена в такую среду, преимущества BBU для производительности можно восстановить, выключив барьеры записи в файловой системе или перенастроив контроллер диска, если это возможно. Если барьеры записи выключены, убедитесь, что батарея остается работоспособной - неисправный аккумулятор может привести к потере данных.

Когда операционная система отправляет запрос на запись в оборудование обеспечивающее долгосрочное хранение, есть мало способов для того , чтобы убедиться, что данные поступили в действительно энергонезависимую область хранения. Скорее, администратор обязан убедиться, что все компоненты хранилища обеспечивают целостность как данных, так и метаданных файловой системы. Избегайте дисковых контроллеров, которые имеют кэши записи без батарейного питания. На уровне диска выключите кэширование с обратной записью, если диск не может гарантировать, что данные будут записаны перед выключением. Если вы используете твердотельные накопители, помните, что многие из них по умолчанию не поддерживают команды очистки кэша.

Еще один риск потери данных связан с самими операциями записи на диск. Пластины механического устройства хранения, как правило, разделены на сектора, обычно по 512 байт каждый. Каждая физическая операция чтения или записи обрабатывает целый сектор. Когда на диск поступает запрос на запись, он может быть кратен 512 байтам ( QHB обычно записывает 8192 байт или 16 секторов за раз), и в процессе записи может произойти сбой из-за потери питания в любое время, что означает, что некоторые из 512-байтовых секторов были записаны, а другие нет. Чтобы защититься от таких сбоев, QHB периодически записывает полностраничные образы в постоянное хранилище WAL перед изменением фактической страницы на диске. Таким образом, во время восстановления после сбоя QHB может восстановить частично записанные страницы из WAL. Если используется файловая система, которая предотвращает частичную запись страниц (например, ZFS), вы можете выключить полностраничное представление (запись 8 КБ или 16 секторов за раз), выключив параметр full_page_writes. Дисковые контроллеры с батарейным блоком (BBU) не предотвращают частичную запись страниц, если они не гарантируют, что данные записываются в BBU как полные (8 КБ) страницы.

QHB также защищает от некоторых видов повреждения данных на устройствах хранения, которые могут возникнуть из-за аппаратных ошибок или сбоя носителя с течением времени, таких как чтение / запись мусорных данных.

Каждая отдельная запись в файле WAL защищена проверкой CRC-32 (32-разрядная версия), которая позволяет нам определить правильность содержимого записи. Значение CRC устанавливается при записи каждой записи WAL и проверяется во время восстановления после сбоя, восстановления архива и репликации.

Страницы данных в конфигурации по умолчанию не имеют контрольной суммы , хотя образы страниц, сохранённые в записях WAL, будут защищены - смотрите qhb_bootstrap для подробностей о включении контрольных сумм для страниц данных.

Внутренние структуры данных хранящиеся на диске, такие как pg_xact, pg_subtrans, pg_multixact, pg_serial, pg_notify, pg_stat, pg_snapshots, не проверяются при помощи контрольных сумм, а страницы не защищены полными записями страниц (по 8Кб). Однако там, где такие структуры данных являются постоянными, записываются записи WAL, которые позволяют точно выстроить заново последние изменения при восстановлении после сбоя, и эти записи WAL защищены, как обсуждалось выше.

Отдельные файлы состояний в pg_twophase защищены CRC-32.

Временные файлы данных, используемые в больших запросах SQL для сортировки, материализации и промежуточных результатов, в настоящее время не проверяются, и в WAL не будут записываться изменения в этих файлах.

QHB не защищает от исправляемых ошибок памяти, и предполагается, что вы будете работать с ОЗУ, которое использует отраслевой стандарт исправления ошибок (ECC) или более эффективную защиту.

Контрольные суммы данных

По умолчанию страницы данных не защищены контрольными суммами, но при необходимости их можно включить для кластера. В таком случае каждая страница данных будет содержать контрольную сумму, рассчитываемую при записи и проверяемую при каждом чтении страницы. Контрольными суммами защищены только страницы данных, но не внутренние структуры данных и временные файлы.

Контрольные суммы обычно включают при инициализации кластера с помощью утилиты initdb. Также контрольные суммы можно включить или выключить позднее, в остановленном кластере. Включить или выключить контрольные суммы данных можно на уровне всего кластера, но не для отдельной базы данных или таблицы.

Текущее состояние контрольных сумм в кластере можно узнать, просмотрев значение неизменяемой конфигурационной переменной data_checksums с помощью команды SHOW data_checksums.

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

Включение контрольных сумм в остановленном кластере

Чтобы проверить, включить или выключить контрольные суммы данных в остановленном кластере, можно использовать утилиту qhb_checksums.

Журнал упреждающей записи (WAL)

Запись в журнал упреждающей записи (Write Ahead Log, WAL ) - это стандартный метод обеспечения целостности данных. Подробное описание можно найти в большинстве (если не во всех) справочных и учебных пособиях по обработке транзакций. Центральная концепция WAL заключается в том, что изменения в файлах данных (в которых находятся таблицы и индексы) должны записываться только после того, как эти изменения были зарегистрированы, то есть после того, как записи журнала, описывающие изменения, были сброшены в постоянное хранилище. Если мы следуем этой процедуре, нам не нужно сбрасывать страницы данных на диск при каждой фиксации транзакции, потому что мы знаем, что в случае сбоя мы сможем восстановить базу данных, используя журнал: любые изменения, которые не были применены на страницы данных могут быть восстановлены из записей журнала. (Это восстановление с повтором транзакций, также известно как REDO).

Заметка
Поскольку WAL восстанавливает содержимое файла базы данных после сбоя, журнальные файловые системы не нужны для надежного хранения файлов данных или файлов WAL. Фактически, издержки журналирования могут снизить производительность, особенно если журналирование часто приводит к сбросу данных файловой системы на диск. Сброс данных журнала часто может быть выключен с помощью опции монтирования файловой системы, например data=writeback в файловой системе Linux ext3. Журналированные файловые системы улучшают скорость загрузки после сбоя.

Использование WAL приводит к значительному сокращению числа операций записи на диск, поскольку для сохранения транзакции требуется только файл журнала, а не каждый файл данных, измененный транзакцией. Файл журнала записывается последовательно, поэтому стоимость синхронизации журнала намного ниже, чем стоимость сброса на диск страниц файлов данных. Это особенно верно для серверов, обрабатывающих множество небольших транзакций, затрагивающих разные части хранилища данных. Кроме того, когда сервер обрабатывает много небольших параллельных транзакций, одной фиксации файла журнала может быть достаточно для фиксации многих транзакций.

WAL также позволяет поддерживать оперативное резервное копирование и восстановление на определенный момент времени, как описано в разделе Выполнение восстановления на момент времени (PITR). Архивируя данные WAL, мы можем поддерживать возврат к любому моменту времени, охваченному доступными данными WAL: мы просто устанавливаем предварительную физическую резервную копию базы данных и "воспроизводим" изменения из журнала столько раз, сколько необходимо. Более того, физическое резервное копирование не должно быть мгновенным снимком состояния базы данных - если оно выполнено в течение некоторого периода времени, то повторное воспроизведение журнала за этот период устранит любые внутренние несоответствия.

Асинхронный коммит

Асинхронная фиксация (asynchronous commit) - это опция, которая позволяет транзакциям завершаться быстрее, за счет того, что самые последние транзакции могут быть потеряны в случае сбоя базы данных. Во многих приложениях это приемлемый компромисс.

Как описано в предыдущем разделе, фиксация транзакции обычно выполняется синхронно: сервер ожидает сброса записей WAL транзакции в постоянное хранилище, а затем возвращает клиенту указание об успешном выполнении. Поэтому клиенту гарантируется, что транзакция, о которой было сообщено, будет сохранена даже в случае сбоя сервера сразу после этого. Однако для коротких транзакций эта задержка является основным компонентом общего времени транзакции. Выбор режима асинхронной фиксации означает, что сервер возвращает сообщение об успешной фиксации, как только транзакция логически завершена, до того, как сгенерированные ею записи WAL действительно попадут на диск. Это может обеспечить значительное увеличение пропускной способности для небольших транзакций.

Асинхронная фиксация транзакций создает риск потери данных. Существует короткое временное окно между отчетом о завершении транзакции для клиента и временем, когда транзакция действительно зафиксирована (то есть она гарантированно не будет потеряна в случае сбоя сервера). Таким образом, асинхронная фиксация транзакций не должна использоваться, если клиент будет выполнять внешние действия, полагаясь на то, что транзакция будет завершена. Например, банк, безусловно, не будет использовать асинхронную фиксацию транзакции, регистрирующей выдачу наличных в банкомате. Но во многих сценариях, таких как регистрация событий, нет необходимости в сильных гарантиях такого рода.

Риск, связанный с использованием асинхронной фиксации транзакций, - это потеря данных, а не их повреждение. Если база данных выйдет из строя, она восстановится путем воспроизведения WAL до последней записи, которая была сохранена. Поэтому база данных будет восстановлена в самосогласованном состоянии, но любые транзакции, которые еще не были записаны на диск, не будут отражены в этом состоянии. Таким образом, минимальные последствия - потеря нескольких последних транзакций. Поскольку транзакции воспроизводятся в порядке фиксации, может быть внесено несоответствие - например, если транзакция B внесла изменения, полагаясь на изменения внесённые предыдущей транзакцией A, невозможно восстановить эффекты A, в то время как эффекты B сохранены.

Пользователь может выбрать режим фиксации каждой транзакции, чтобы можно было одновременно выполнять синхронные и асинхронные фиксации транзакции. Это обеспечивает гибкие компромиссы между производительностью и уверенностью в согласованности транзакций. Режим фиксации управляется настраиваемым пользователем параметром synchronous_commit, который может быть изменен любым из способов настройки параметра конфигурации. Режим, используемый для любой транзакции, зависит от значения synchronous_commit когда начинается фиксация транзакции.

Некоторые служебные команды, например DROP TABLE, принудительно фиксируются синхронно, независимо от значения параметра synchronous_commit. Это необходимо для обеспечения согласованности между файловой системой сервера и логическим состоянием базы данных. Команды, поддерживающие двухфазную фиксацию, такие как PREPARE TRANSACTION, также всегда являются синхронными.

Если во время окна риска между асинхронной фиксацией и записью изменений в WAL транзакции происходит сбой базы данных, изменения, сделанные во время этой транзакции, будут потеряны. Продолжительность окна риска ограничена, потому что фоновый процесс (WAL writer) сбрасывает новые записи WAL на диск каждые миллисекунды wal_writer_delay . Фактическая максимальная продолжительность окна риска в три раза больше wal_writer_delay потому что средство записи WAL предназначено для записи целых страниц за раз в периоды нагрузки.

Предостережение!!!
Выключение в немедленном режиме эквивалентно сбою сервера и, следовательно, приведет к потере любых невыполненных асинхронных фиксаций транзакций.

Асинхронная фиксация транзакции обеспечивает поведение, отличное от установки fsync = off. Параметр fsync применяется ко всему серверу и будет изменять поведение всех транзакций. Он выключает всю логику в QHB, которая пытается синхронизировать записи в различные части базы данных, и, следовательно, сбой системы (то есть сбой оборудования или операционной системы, а не сбой самого QHB) может привести к сколь угодно плохому повреждению состояния базы данных - от потери отдельных таблиц до полной непригодности. Во многих сценариях асинхронная фиксация транзакций обеспечивает значительное улучшение производительности, которое можно получить, выключив fsync, но без риска повреждения данных.

Действие параметра commit_delay также очень похоже на асинхронную фиксацию транзакций, но на самом деле это метод синхронной фиксации (фактически, commit_delay игнорируется во время асинхронной фиксации). commit_delay вызывает задержку непосредственно перед тем, как транзакция сбрасывает WAL на диск, в надежде, что один сброс, выполненный одной такой транзакцией, может также обслуживать другие транзакции, фиксируемые примерно в одно и то же время. Этот параметр можно рассматривать как способ увеличения временного окна, в котором транзакции могут присоединиться к группе, собирающейся участвовать в одном сохранении, чтобы амортизировать стоимость сброса между несколькими транзакциями.

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

Существует несколько параметров конфигурации, связанных с WAL, которые влияют на производительность базы данных. Этот раздел объясняет их использование. Обратитесь к Главе Конфигурация сервера за общей информацией о настройке параметров конфигурации сервера.

Checkpoints это "точки" в последовательности транзакций, при которых гарантируется, что файлы данных и индекса были обновлены и сохранена вся информация, записанная до этой контрольной точки. Во время контрольной точки все грязные страницы данных сбрасываются на диск, и в файл журнала записывается специальная запись контрольной точки. (Записи изменений ранее были сброшены в файлы WAL). В случае сбоя процедура восстановления после сбоя находит последнюю запись контрольной точки, чтобы определить точку в журнале (известную как запись REDO), с которой она должна начать REDO операции. Любые изменения, внесенные в файлы данных до этого момента, гарантированно будут уже на диске. Следовательно, после контрольной точки сегменты журнала, предшествующие тому, в котором содержится повторная запись, больше не нужны и могут быть использованы повторно или удалены. (Когда выполняется архивация WAL , сегменты журнала должны быть заархивированы перед тем, как их повторно используют или удалят).

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

Процесс контрольной точки сервера автоматически выполняет контрольную точку очень часто. Контрольная точка начинается каждые секунды checkpoint_timeout, или, если будет превышено значение max_wal_size , в зависимости от того, что наступит раньше. Настройки по умолчанию - 5 минут и 1 ГБ соответственно. Если с предыдущей контрольной точки не было записи в WAL, новые контрольные точки будут пропущены, даже если checkpoint_timeout прошла. (Если используется архивация WAL, и вы хотите установить более низкий предел частоты архивирования файлов, чтобы ограничить возможную потерю данных, вам следует настроить параметр archive_timeout, а не параметры контрольной точки). Также можно принудительно установить контрольную точку с помощью команды SQL CHECKPOINT.

Уменьшение checkpoint_timeout и / или max_wal_size приводит к тому, что контрольные точки возникают чаще. Это позволяет быстрее восстанавливаться после сбоя, так как потребуется меньше работы. Тем не менее, необходимо сбалансировать это с возросшей стоимостью очистки грязных страниц данных. Если задано full_page_writes (по умолчанию включено), необходимо учитывать еще один фактор. Чтобы обеспечить согласованность страницы данных, первая модификация страницы данных после каждой контрольной точки приводит к регистрации всего содержимого страницы. В этом случае меньший интервал контрольных точек увеличивает объем вывода в журнал WAL, частично сводя на нет цель использовать меньший интервал и в любом случае вызывая больший дисковый ввод-вывод.

Контрольные точки довольно дороги, во-первых, потому что они требуют записи всех текущих грязных буферов, а во-вторых, потому что они приводят к дополнительному последующему трафику WAL, как обсуждалось выше. Поэтому целесообразно устанавливать параметры контрольных точек достаточно высокими, чтобы контрольные точки не возникали слишком часто. В качестве простой проверки работоспособности параметров вашей контрольной точки вы можете установить параметр checkpoint_warning. Если контрольные точки расположены ближе друг к другу, чем секунды checkpoint_warning, в журнал сервера будет выведено сообщение с рекомендацией увеличить max_wal_size. Случайное появление такого сообщения не является причиной для тревоги, но если оно появляется часто, то параметры контроля контрольной точки должны быть увеличены. Массовые операции, такие как чтения больших таблиц с помощью COPY могут вызвать появление ряда таких предупреждений, если вы не установили max_wal_size достаточно высоким.

Чтобы избежать переполнения системы ввода-вывода с помощью записи страниц, запись грязных буферов во время контрольной точки распределяется на некоторый период времени. Этот период контролируется checkpoint_completion_target, который задается как часть интервала контрольных точек. Скорость ввода-вывода регулируется таким образом, чтобы контрольная точка заканчивалась по истечении заданной доли секунды checkpoint_timeout или до превышения max_wal_size, в зависимости от того, что наступит раньше. При значении по умолчанию 0,5 QHB может завершить каждую контрольную точку примерно за половину времени до запуска следующей контрольной точки. В системе, которая очень близка к максимальной пропускной способности ввода-вывода при нормальной работе, вы можете увеличить checkpoint_completion_target чтобы уменьшить нагрузку ввода-вывода с контрольных точек. Недостатком этого является то, что продление контрольных точек влияет на время восстановления, поскольку необходимо будет хранить больше сегментов WAL для возможного использования для восстановления. Хотя checkpoint_completion_target может быть установлено до 1,0, лучше оставить его меньше этого значения (возможно, не более 0,9), поскольку контрольные точки включают в себя некоторые другие действия помимо записи грязных буферов. Значение 1.0 вполне может привести к тому, что контрольные точки не будут выполнены вовремя, что приведет к снижению производительности из-за неожиданного изменения количества необходимых сегментов WAL.

На платформах Linux и POSIX checkpoint_flush_after позволяет принудительно заставить ОС, сбрасывать страницы, записанные контрольной точкой, на диск после определённого количества байтов. В противном случае эти страницы могут храниться в кэше страниц ОС, вызывая остановку при вызове fsync в конце контрольной точки. Этот параметр часто помогает уменьшить задержку транзакции, но также может негативно повлиять на производительность - особенно для рабочих нагрузок, которые больше, чем shared_buffers, но меньше, чем кэш страниц ОС.

Количество файлов сегментов WAL в pg_wal зависит от min_wal_size , max_wal_size и количества WAL, сгенерированного в предыдущих циклах контрольных точек. Когда старые файлы сегментов журнала больше не нужны, они удаляются или повторно используются (то есть переименовываются, чтобы стать будущими сегментами в пронумерованной последовательности). Если из-за кратковременного пика скорости вывода журнала превышен max_wal_size, ненужные файлы сегментов будут удаляться, пока система не вернется к этому пределу. Ниже этого предела система повторно использует достаточно файлов WAL для покрытия предполагаемой потребности до следующей контрольной точки и удаляет остальные. Оценка основана на скользящей средней числа файлов WAL, использованных в предыдущих циклах контрольных точек. Скользящее среднее значение немедленно увеличивается, если фактическое использование превышает оценку, поэтому оно в некоторой степени учитывает пиковое использование, а не среднее использование. min_wal_size устанавливает минимальное количество файлов WAL для будущего использования - столько WAL всегда будет использовано повторно , даже если система простаивает и оценка использования WAL предполагает, что сегментов требуется мало.

Независимо от max_wal_size, wal_keep_size + 1 самые последние файлы WAL хранятся всегда. Кроме того, если используется архивация WAL, старые сегменты не могут быть удалены или повторно использованы, пока они не будут заархивированы. Если архивация WAL не может идти одновременно со скоростью, с которой генерируется WAL, или если archive_command неоднократно завершается с ошибкой, старые файлы WAL будут накапливаться в pg_wal до тех пор, пока ситуация не разрешиться. Медленный или сбойный резервный сервер, который использует слот репликации, будет давать такой же эффект.

В режиме восстановления архива или в режиме ожидания сервер периодически выполняет точки перезапуска, которые аналогичны контрольным точкам в обычной работе: сервер переносит все свое состояние на диск, обновляет файл pg_control чтобы указать, что уже обработанные данные WAL не нужно снова сканировать, а затем переиспользует любые старые файлы сегментов журнала в каталоге pg_wal. Точки перезапуска не могут выполняться чаще, чем контрольные точки в мастере, потому что точки перезапуска могут выполняться только в записях контрольных точек. Точка перезапуска срабатывает при достижении записи контрольной точки, если прошло не менее checkpoint_timeout секунд с момента последней точки перезапуска или если размер WAL собирается превысить max_wal_size. Однако из-за ограничений на время, когда может быть выполнена точка перезапуска, max_wal_size часто превышается во время восстановления на величину WAL до одного контрольного пункта. ( max_wal_size никогда не бывает жестким ограничением, поэтому вы всегда должны оставлять достаточно места, чтобы избежать нехватки дискового пространства для файлов журнала).

Есть две обычно используемые внутренние функции WAL: XLogInsertRecord и XLogFlush. XLogInsertRecord используется для помещения новой записи в буферы WAL в общей памяти. Если для новой записи нет места, XLogInsertRecord должен будет записать (переместить в кэш ядра ОС) несколько заполненных буферов WAL. Это нежелательно, поскольку XLogInsertRecord используется для каждой низкоуровневой модификации базы данных (например, вставка строки) в то время, когда на затронутых страницах данных удерживается монопольная блокировка, поэтому операция должна быть максимально быстрой. Что еще хуже, запись в буферы WAL также может привести к созданию нового сегмента журнала, что занимает еще больше времени. Обычно буферы WAL должны записываться и очищаться с помощью запроса XLogFlush, который, по большей части, выполняется во время фиксации транзакции, чтобы гарантировать, что записи транзакции сбрасываются в постоянное хранилище. В системах с высокой скоростью заполнения журнала запросы XLogFlush могут возникать недостаточно часто, чтобы запретить XLogInsertRecord выполнять записи. В таких системах следует увеличить количество буферов WAL, изменив параметр wal_buffers. Когда full_page_writes установлен и система сильно нагружена, установка wal_buffers выше поможет сгладить время отклика в течение периода, следующего сразу за каждой контрольной точкой.

Параметр commit_delay определяет, на сколько микросекунд процесс лидера групповой фиксации транзакции будет находиться в спящем режиме после получения блокировки в XLogFlush, пока последователи групповой фиксации стоят в очереди после лидера. Эта задержка позволяет другим процессам сервера добавлять свои записи фиксации в буферы WAL, чтобы все они были сброшены возможной синхронизирующей операцией лидера. Спящий режим не будет использоваться, если fsync не включен или если в текущий момент в активных транзакциях меньше, чем commit_siblings - это позволяет избежать ожидания в спящем режиме, когда маловероятно, что какой-либо другой сеанс совершится в ближайшее время. Обратите внимание, что на некоторых платформах разрешение неактивного запроса составляет десять миллисекунд, поэтому любой ненулевой параметр commit_delay от 1 до 10000 микросекунд будет иметь тот же эффект. Также обратите внимание, что на некоторых платформах операции сна могут занимать немного больше времени, чем запрошено параметром.

Поскольку цель commit_delay состоит в том, чтобы позволить амортизировать стоимость каждой операции обращения к диску для одновременной фиксации транзакций (возможно, за счет задержки транзакции), необходимо количественно оценить эту стоимость, прежде чем параметр можно будет выбрать с достаточной точностью. Чем выше эта стоимость, тем эффективнее будет ожидаемая commit_delay для увеличения пропускной способности транзакций до определенного уровня. Значение, составляющее половину среднего времени, операции записи 8 КБ, часто является наиболее эффективным параметром для commit_delay , поэтому это значение рекомендуется в качестве отправной точки для использования при оптимизации для конкретной рабочей нагрузки. Хотя настройка commit_delay особенно полезна, когда журнал WAL хранится на механических вращающихся дисках с высокой задержкой, преимущества могут быть значительными даже для носителей с очень быстрым временем синхронизации, таких как твердотельные накопители или массивы RAID с кэш-памятью с резервным питанием от аккумулятора, но это, безусловно, должно быть проверено на репрезентативной нагрузке. В таких случаях следует использовать более высокие значения commit_siblings, тогда как меньшие значения commit_siblings часто полезны на носителях с более высокой задержкой. Обратите внимание, что вполне возможно, что слишком высокая установка commit_delay может значительно увеличить задержку фиксации транзакции, в связи с чем пострадает общая пропускная способность.

Когда commit_delay установлен в ноль (по умолчанию), все еще возможно, групповая фиксации, но каждая группа будет состоять только из сессий, которые достигают точки, где они должны сбрасывать свои записи фиксации во время окна, в котором выполняется предыдущая операция сохранения (если она была). При большем количестве клиентов, как правило, возникает «давка» между сеансами, так что эффекты групповой фиксации становятся значительными, даже когда commit_delay равен нулю, и, таким образом, явная установка commit_delay оказывает меньше влияния на производительность. Установка commit_delay может помочь, только когда

есть некоторые одновременно завершающиеся транзакции

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

Параметр wal_sync_method определяет, как QHB будет запрашивать ядро ОС для принудительного обновления WAL на диск. Все параметры должны быть одинаковыми с точки зрения надежности, за исключением fsync_writethrough, который иногда может вызвать очистку дискового кэша, даже если другие параметры этого не делают это зависит от платформы, на которой работает QHB. Обратите внимание, что этот параметр не имеет значения, если fsync выключен.

Внутреннее устройство WAL

WAL включается автоматически; никаких действий со стороны администратора не требуется, за исключением обеспечения соблюдения требований к дисковому пространству для журналов WAL и выполнения необходимой настройки (см. раздел Конфигурация WAL).

Записи WAL добавляются в журналы WAL по мере возникновения каждой новой записи. Позиция вставки описывается порядковым номером журнала (LSN), который представляет собой байтовое смещение в журналах, монотонно увеличивающееся с каждой новой записью. Значения LSN имеют тип данных pg_lsn. Значения можно сравнивать для расчета объема данных WAL, которые их разделяют, поэтому они используются для измерения объёмов и процесса репликации и восстановления.

Журналы WAL хранятся на диске в каталоге pg_wal в виде набора файлов сегментов, обычно каждый размером 16 МБ (но размер можно изменить, изменив параметр --wal-segsize qhb_bootstrap). Каждый сегмент делится на страницы, обычно по 8 кБ каждая (этот размер можно изменить с помощью параметра конфигурации --with-wal-blocksize). Содержание записи зависит от типа регистрируемого события. Сегментным файлам присваиваются постоянно растущие числа в качестве имен, начиная с 000000010000000000000000. Числа не переносятся при переполнении, но потребуется очень и очень много времени, чтобы исчерпать доступный запас чисел.

Бывает выгодно для производительности, когда журнал расположен на диске, отличном от основных файлов базы данных. Это может быть достигнуто путем перемещения каталога pg_wal в другое место (конечно, когда сервер выключен) и создания символической ссылки из исходного местоположения в главном каталоге данных на новое местоположение.

Задача QHB - обеспечить, запись журнала до записей изменения в файлах базы данных, но это может быть нарушено драйверами устройств хранения которые ложно сообщают об успешной записи, когда на самом деле они только кэшировали данные в ядре ОС и еще не сохранили их на диске. Сбой питания в такой ситуации может привести к неисправимому повреждению данных. Администраторы должны убедиться, что диски, содержащие файлы журнала WAL QHB, не создают таких ситуаций. (См. раздел Прочность).

После создания контрольной точки журнала ее позиция сохраняется в файле pg_control. Поэтому в начале восстановления сервер сначала читает pg_control а затем запись контрольной точки, затем он выполняет операцию REDO путем сканирования вперед по файлу из местоположения журнала, указанного в записи контрольной точки. Поскольку всё содержимое всех изменений страниц данных, сохраняется в журнале после контрольной точки (при условии, что full_page_writes не выключен) все изменения после контрольной точки будут восстановлены в согласованном состоянии.

Чтобы справиться со случаем, когда pg_control поврежден, мы должны поддерживать возможность сканирования существующих сегментов журнала в обратном порядке - от самого нового к старому - чтобы найти последнюю контрольную точку. Это еще не реализовано. pg_control достаточно мал (менее одной страницы диска), чтобы не создавать проблем с частичной записью, и на момент написания этой статьи не было сообщений о сбоях базы данных только из-за невозможности чтения самого pg_control. Таким образом, хотя теоретически это слабое место, кажется pg_control не является проблемой на практике.

Процедурные языки

QHB позволяет писать пользовательские функции на других языках, кроме SQL и C/RUST. Эти другие языки обычно называются процедурными языкам (PL). Для функции, написанной на процедурном языке, сервер базы данных не имеет встроенных методов, для интерпретации исходного текста функции. Вместо этого задача передается специальному обработчику, который обрабатывает детали языка. Обработчик может либо выполнять всю работу по синтаксическому анализу, синтаксическому анализу, выполнению и т. д., Либо он может служить «связующим звеном» между QHB и существующей реализацией языка программирования. Сам обработчик является функцией языка C/RUST, скомпилированной в общий объект и загружается по требованию, как и любая другая функция C/RUST.

В настоящее время в стандартном выпуске QHB доступен язык: PL/pgSQL (глава PL/pgSQL), другие языки могут быть созданы пользователями самостоятельно.

Установка процедурных языков

Процедурный язык должен быть «установлен» в каждой базе данных, где он будет использоваться. Процедурные языки, установленные в базе данных template1, автоматически доступны во всех впоследствии созданных базах данных, так как их записи в template1 будут скопированы командой CREATE DATABASE. Таким образом, администратор базы данных может решить, какие языки доступны в каких базах данных, и при желании может сделать некоторые языки доступными по умолчанию.

Для языков, поставляемых со стандартным дистрибутивом, необходимо только выполнить CREATE EXTENSION language_name чтобы установить язык в текущей базе данных. Описанная ниже ручная процедура рекомендуется только для установки языков, которые не были упакованы как расширения.

Ручная процедура установки процедурных языков

Процедурный язык устанавливается в базе данных в пять этапов, которые должны выполняться суперпользователем базы данных. В большинстве случаев требуемые команды SQL должны быть упакованы как установочный скрипт «расширения», чтобы их можно было использовать для CREATE EXTENSION.

Общий объект для языкового обработчика (динамически подключаемая библиотека) должен быть скомпилирован и установлен в соответствующий каталог библиотеки, так же, как сборка и установка модулей с обычными пользовательскими функциями C/RUST - см. раздел Компиляция и связывание динамически загружаемых функций. Часто обработчик языка зависит от внешней библиотеки, которая обеспечивает реальный движок языка программирования - в этом случае он также должен быть установлен.

Обработчик должен быть создан командой

CREATE FUNCTION handler_function_name()
    RETURNS language_handler
    AS 'path-to-shared-object'
    LANGUAGE C;

Специальный возвращаемый тип language_handler сообщает системе баз данных, что эта функция не возвращает ни один из определенных типов данных SQL и не может напрямую использоваться в инструкциях SQL .

При желании обработчик языка может предоставлять встроенную функцию обработчика, предназначенную для выполнения блоков анонимного кода (команды DO), написанных на этом языке. Если встроенная функция обработчика предоставляется языком, необходимо объявить ее командой

CREATE FUNCTION inline_function_name(internal)
    RETURNS void
    AS 'path-to-shared-object'
    LANGUAGE C;

При желании обработчик языка может предоставить функцию «валидатора», которая проверяет правильность определения функции, фактически не выполняя ее. Функция валидатора,если она существует, вызывается командой CREATE FUNCTION. Если язык предоставляет функцию валидатора, необходимо объявить ее командой

CREATE FUNCTION validator_function_name(oid)
    RETURNS void
    AS 'path-to-shared-object'
    LANGUAGE C STRICT;

Наконец, сам процедурный язык, должен быть объявлен с командой

CREATE [TRUSTED] LANGUAGE language_name
    HANDLER handler_function_name
    [INLINE inline_function_name]
    [VALIDATOR validator_function_name] ;

Необязательное ключевое слово TRUSTED указывает, что язык не предоставляет доступ к данным,к которым в противном случае, у пользователя нет доступа. Доверенные языки предназначены для обычных пользователей баз данных (без привилегий суперпользователя) и позволяют им безопасно создавать функции и процедуры. Поскольку функции процедурного языка выполняются внутри сервера базы данных, флаг TRUSTED следует указывать только для языков, которые не разрешают доступ к внутренним компонентам сервера базы данных или файловой системе. Язык PL/pgSQL считается доверенным.

При установке QHB по умолчанию обработчик для языка PL/pgSQL создается и устанавливается в каталог «library». Более того, сам язык PL/pgSQL установлен во всех базах данных.

Клиентские приложения QHB

В этом разделе содержится справочная информация о клиентских приложениях и утилитах QHB. Не все эти команды имеют общее назначение; некоторые из них могут требовать особых прав. Общей особенностью этих приложений является то, что их можно запустить на любом хосте, независимо от того, где находится сервер базы данных.

При указании в командной строке имени пользователя и базы данных, они передаются без изменения регистра — все пробелы и специальные символы нужно заключать в кавычки. Имена таблиц и другие идентификаторы не зависят от регистра, за исключением отдельно описанных случаев, когда могут потребоваться кавычки.

Содержание

clusterdb — кластеризация базы данных QHB

createdb — создать новую базу данных QHB

createuser — определить новую учетную запись пользователя QHB

dropdb — удалить базу данных QHB

dropuser — удалить учетную запись пользователя QHB

psql — QHB интерактивный терминал

qhb_amcheck — проверить одну или несколько баз данных QHB на предмет повреждения

qhb_basebackup — сделать базовую резервную копию кластера QHB

qhb_dump — извлечь базу данных QHB в файл сценария или другой архив

qhb_dumpall — извлечь кластер базы данных QHB в файл сценария

qhb_isready — проверить состояние соединения с сервером QHB

qhb_receivewal — потоковые журналы записи с сервера QHB

qhb_recvlogical — управление потоками логического декодирования QHB

qhb_restore — восстановить базу данных QHB из файла архива, созданного qhb_dump

qhb_verifybackup — проверить целостность базовой резервной копии кластера QHB

reindexdb — переиндексировать базу данных QHB

vacuumdb — сбор мусора и анализ базы данных QHB

clusterdb

clusterdb - кластеризовать базу данных QHB.

Синтаксис

clusterdb [параметр-подключения...] [ --verbose | -v ] [ --table | -t таблица ] ... [имя_бд]

clusterdb [параметр-подключения...] [ --verbose | -v ] --all | -a

Описание

clusterdb — это утилита для повторной кластеризации таблиц в базе данных QHB. Она находит ранее кластеризованные таблицы и снова группирует их на основании последнего использованного индекса. Таблицы, которые никогда не группировались, не затрагиваются.

clusterdb — это обертка для команды SQL CLUSTER. Между кластеризацией баз данных с помощью этой утилиты и кластеризацией иными способами при обращении к серверу по сути нет никакой разницы.

Параметры

clusterdb принимает следующие аргументы командной строки:

-a
--all
Кластеризовать все базы данных.

[-d] имя_бд
[--dbname=]имя_бд
Задает имя базы данных для кластеризации, когда не используется параметр -a/--all. Если это указание отсутствует, имя базы данных считывается из переменной среды PGDATABASE. Если эта переменная не установлена, используется имя пользователя, указанное для подключения.

-e
--echo
Отобразить на экране команды, которые clusterdb генерирует и отправляет на сервер.

-q
--quiet
Не показывать сообщения о прогрессе выполнения.

-t таблица
--table=таблица
Кластеризовать конкретную таблицу. Написав параметр -t несколько раз, можно кластеризовать несколько таблиц.

-v
--verbose
Вывести подробную информацию во время процесса.

-V
--version
Вывести версию clusterdb и завершиться.

-?
--help
Показать справку об аргументах командной строки clusterdb и завершиться.

Кроме того, в качестве параметров подключения clusterdb принимает следующие аргументы командной строки:

-h хост
--host=хост
Задает имя хост-компьютера, на котором работает сервер. Если значение начинается со слэша, оно используется в качестве каталога для Unix-сокета.

-p порт
--port=порт
Указывает TCP-порт или расширение файла локального Unix-сокета, через который сервер принимает подключения.

-U имя_пользователя
--username=имя_пользователя
Имя пользователя, под которым производится подключение.

-w
--no-password
Не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, и пароль недоступен с помощью иных средств, таких как файл .pgpass, попытка подключения завершится неудачно. Этот параметр может быть полезен в пакетных заданиях и скриптах, где нет пользователя, чтобы ввести пароль.

-W
--password
Принудительно запрашивать пароль перед подключением к базе данных.

Это несущественный параметр, так как clusterdb автоматически запросит пароль, если сервер требует аутентификацию по паролю. Однако чтобы выяснить это, clusterdb потребуется дополнительная попытка подключения к серверу. В некоторых случаях имеет смысл ввести -W, чтобы исключить эту лишнюю попытку.

--maintenance-db=имя_бд
Задает имя базы данных, к которой будет выполняться подключение для определения, какие базы данных должны быть кластеризованы, когда используется параметр -a/--all. Если это имя не указано, будет выбрана база данных qhb, а если она не существует — template1.

Переменные среды

PGDATABASE
PGHOST
PGPORT
PGUSER
Параметры подключения по умолчанию.

PG_COLOR
Указывает, использовать ли цвет в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Диагностика

В случае возникновения затруднений обратитесь к CLUSTER и psql, где рассматриваются потенциальные проблемы и сообщения об ошибках. Сервер базы данных должен работать на целевом хосте.

Примеры

Выполнить кластеризацию базы данных test:

$ clusterdb test

Выполнить кластеризацию отдельной таблицы foo в базе данных xyzzy:

$ clusterdb --table=foo xyzzy

См. также

CLUSTER

createdb

createdb — создать новую базу данных QHB

Синтаксис

createdb [флаг...] [параметр-подключения...] --host=хост [аргумент...]

Описание

Утилита createdb создает новую базу данных QHB.

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

createdb — это обертка для команды SQL CREATE DATABASE. Между созданием баз данных с помощью этой утилиты и их созданием иными способами при обращении к серверу по сути нет никакой разницы.

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

Если базу данных невозможно создать, то программа выведет сообщение об ошибке, при этом иногда предлагая решение проблемы.

Параметры

createdb принимает следующие аргументы командной строки:

Флаги

-?
--help
Показать справку об аргументах командной строки createdb и завершиться.

-V
--version
Вывести версию createdb и завершиться.

-v
--verbose
Задает уровень ведения журнала в Debug. Значение по умолчанию — Info.

-w
--no-password
Никогда не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, и пароль недоступен с помощью иных средств, таких как файл .pgpass, попытка подключения завершится неудачно. Этот параметр может быть полезен в пакетных заданиях и скриптах, где нет пользователя, чтобы ввести пароль.

-W
--password
Принудительно запрашивать пароль перед подключением к базе данных.

Это несущественный параметр, так как createdb автоматически запросит пароль, если сервер требует аутентификацию по паролю. Однако чтобы выяснить это, createdb потребуется дополнительная попытка подключения к серверу. В некоторых случаях имеет смысл ввести -W, чтобы исключить эту лишнюю попытку.

Параметры подключения

-D табличное_пространство
--tablespace=табличное_пространство
Задает табличное пространство, которое по умолчанию будет использоваться в базе данных. (Это имя обрабатывается аналогично идентификатору, заключенному в кавычки.)

-e
--echo
Отобразить на экране команды, которые createdb генерирует и отправляет на сервер.

-E кодировка
--encoding=кодировка
Задает схему кодировки символов, которая будет использоваться в этой базе данных. Поддерживаемые сервером QHB кодировки описаны в подразделе Поддерживаемые кодировки.

-h хост
--host=хост
Задает имя хост-компьютера, на котором работает сервер. Если значение начинается со слэша, оно используется в качестве каталога для Unix-сокета. Вместо этого параметра можно воспользоваться переменной среды PGHOST.

-l локаль
--locale=локаль
Задает локаль, которая будет использоваться в этой базе данных. Этот параметр равнозначен одновременной установке флагов --lc-collate и --lc-ctype.

--lc-collate=локаль
Задает параметр LC_COLLATE, который будет использоваться в этой базе данных.

--lc-ctype=локаль
Задает параметр LC_CTYPE, который будет использоваться в этой базе данных.

--maintenance-db=имя_бд
Задает имя базы данных, к которой будет произведено подключение при создании новой базы данных. Если это имя не указано, будет выбрана база данных qhb, а если она не существует (или это имя новой базы данных) — template1.

-O владелец
--owner=владелец
Указывает пользователя базы данных, которому будет принадлежать новая база данных. (Это имя обрабатывается аналогично идентификатору, заключенному в кавычки.)

-p порт
--port=порт
Указывает TCP-порт или расширение файла локального Unix-сокета, через который сервер принимает подключения. Значение по умолчанию — 5432.

-T шаблон
--template=шаблон
Задает шаблон, на основе которого будет создана эта база данных. (Это имя обрабатывается аналогично идентификатору, заключенному в кавычки.)

-U имя_пользователя
--username=имя_пользователя
Имя пользователя, под которым производится подключение.

Аргументы

имя_бд
Задает имя создаваемой базы данных. Имя должно быть уникальным среди всех баз данных QHB в этом кластере. По умолчанию в качестве имени новой базы данных выбирается имя текущего системного пользователя. Вместо этого параметра можно воспользоваться переменной среды PGDATABASE.

комментарий
Добавляет комментарий к создаваемой базе данных.

Параметры -D, -l, -E, -O и -T соответствуют параметрам базовой команды SQL CREATE DATABASE; здесь можно найти дополнительную информацию по ним.

Переменные среды

PGDATABASE
Если установлено и не переопределено в командной строке, задает имя создаваемой базы данных.

PGHOST
PGPORT
PGUSER
Параметры подключения по умолчанию. PGUSER также определяет имя создаваемой базы данных, если оно не указано в командной строке или в переменной среды PGDATABASE.

PG_COLOR
Указывает, использовать ли цвет в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Диагностика

В случае возникновения затруднений обратитесь к CREATE DATABASE и psql, где рассматриваются потенциальные проблемы и сообщения об ошибках. Сервер базы данных должен работать на целевом хосте.

Примеры

Сценарии использования

Создать базу данных new на сервере, используемом по умолчанию:

$ createdb new

Создать базу данных new с именем пользователя, от которого запущена программа:

$ createdb

Создать базу данных new с локалью en_US.UTF-8, взяв за основу базу template0 и подключившись к обслуживающей базе qhb:

$ createdb new -l en_US.UTF-8 -T template0 --maintenance-db qhb

Создать базу данных new с локалью en_us и кодировкой LATIN1, взяв за основу базу template0.

$ createdb new "my comment" -l en_US -E LATIN1 -T template0 -v

После создания на базу накладывается комментарий "my comment".
Флаг -v обеспечивает вывод отладочной информации:

```
[DEBUG] Running getpwuid_r for user #1000
[DEBUG] Loading user with uid 1000
[DEBUG] executing statement batch: SELECT pg_catalog.set_config('search_path', '', false);
[DEBUG] executing statement batch: CREATE DATABASE new2 ENCODING 'LATIN1' TEMPLATE template0 LC_COLLATE 'en_US' LC_CTYPE 'en_US';
[DEBUG] executing statement batch: COMMENT ON DATABASE new2 IS 'my comment';
[DEBUG] Database has been successfully created
```

Особенности использования

Записи

createdb new -l en_US.UTF-8 -T template0

и

createdb new -l en_US -E UTF-8 -T template0

не равнозначны. В первом случае база данных будет успешно создана, во втором случае будет выдана ошибка.

[ERROR] db error: ERROR: encoding "UTF8" does not match locale "en_US": SQL: "CREATE DATABASE new3 ENCODING 'UTF-8' TEMPLATE template0 LC_COLLATE 'en_US' LC_CTYPE 'en_US';"
DETAIL: The chosen LC_CTYPE setting requires encoding "LATIN1".

([ОШИБКА] ошибка базы данных: ОШИБКА: кодировка "UTF8" не соответствует локали "en_US": SQL: "CREATE DATABASE new3 ENCODING 'UTF-8' TEMPLATE template0 LC_COLLATE 'en_US' LC_CTYPE 'en_US';"
ДЕТАЛИЗАЦИЯ: Выбранному значению LC_CTYPE требуется кодировка "LATIN1".)

Невозможно добавить комментарий к базе данных, не указав ее имя.

Флаг -l, --locale объединяет в себе --lc-collate и --lc-ctype, поэтому вместе их указывать нельзя. То есть:

createdb new -l en_US = CREATE DATABASE new LC_COLLATE 'en_US' LC_CTYPE 'en_US';

А при одновременном указании этих двух параметров

createdb new --lc-collate en_US --lc-ctype en_US

будет выдана ошибка.

По умолчанию clusterdb пытается использовать базу template1 как обслуживающую новую базу данных ( флаг --maintenance-db), а если подключиться к ней не выходит, то используется база данных qhb.

Флаг -v, --verbose помимо отладочной информации исполняет роль ECHO, выводя SQL-команды, отправленные на сервер для исполнения.

См. также

dropdb, CREATE DATABASE

createuser

createuser — определить новую учетную запись пользователя QHB

Синтаксис

createuser [флаг...] [параметр-подключения...] [параметр...] [имя_роли]

Описание

Утилита createuser создает нового пользователя QHB (или, точнее, роль). Создавать новых пользователей могут только суперпользователи и пользователи с правом CREATEROLE, поэтому createuser должна вызываться от их лица.

При необходимости создать нового суперпользователя следует подключиться как суперпользователь, одного права CREATEROLE недостаточно. Суперпользователи имеют возможность обходить все проверки прав доступа в базе данных, поэтому беспечно такие права предоставлять не стоит.

createuser — это обертка для команды SQL CREATE ROLE. Между созданием пользователей с помощью этой утилиты и их созданием иными способами при обращении к серверу по сути нет никакой разницы. Подробную информацию про атрибуты ролей см. в подразделе Атрибуты ролей.

Параметры

createuser принимает следующие аргументы командной строки:

Флаги

-e
--echo
Отобразить на экране команды, которые createuser генерирует и отправляет на сервер.

-V
--version
Вывести версию createuser и завершиться.

-?
--help
Показать справку об аргументах командной строки createuser и завершиться.

Параметры подключения

-h хост
--host=хост
Задает имя хост-компьютера, на котором работает сервер. Если значение начинается со слэша, оно используется в качестве каталога для Unix-сокета. Вместо этого параметра можно воспользоваться переменной среды PGHOST.

-p порт
--port=порт
Указывает TCP-порт или расширение файла локального Unix-сокета, через который сервер принимает подключения.

-U имя_пользователя
--username=имя_пользователя
Имя пользователя, под которым производится подключение (не имя создаваемого пользователя).

-w
--no-password
Никогда не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, и пароль недоступен с помощью иных средств, таких как файл .pgpass, попытка подключения завершится неудачно. Этот параметр может быть полезен в пакетных заданиях и скриптах, где нет пользователя, чтобы ввести пароль.

-W
--password
Принудительно запрашивать пароль перед подключением к базе данных.

Это несущественный параметр, так как createuser автоматически запросит пароль, если сервер требует аутентификацию по паролю. Однако чтобы выяснить это, createuser потребуется дополнительная попытка подключения к серверу. В некоторых случаях имеет смысл ввести -W, чтобы исключить эту лишнюю попытку.

Параметры

-c число
--connection-limit=число
Задает максимальное число подключений для нового пользователя. По умолчанию без ограничений.

-d
--createdb
Новому пользователю будет разрешено создавать базы данных.

-D
--no-createdb
Новому пользователю будет запрещено создавать базы данных. Это поведение по умолчанию.

-g роль
--role=роль
Указывает роль, к которой создаваемая роль будет немедленно добавлена в качестве нового члена. Написав параметр -g несколько раз, можно задать несколько таких ролей.

-i
--inherit
Новая роль автоматически унаследует права ролей, членом которых она является. Это поведение по умолчанию.

-I
--no-inherit
Новая роль не будет автоматически наследовать права ролей, членом которых она является.

-l
--login
Новому пользователю будет разрешено подключаться к серверу (то есть его имя может быть идентификатором исходного пользователя сеанса). Это поведение по умолчанию.

-L
--no-login
Новому пользователю будет запрещено подключаться к серверу. (Роль без права входа на сервер все равно полезна в качестве средства управления разрешениями в базе данных.)

-P
--pwprompt
Если этот параметр указан, то createuser выдаст запрос на пароль для нового пользователя. Если не планируется аутентификация по паролю, в этом нет необходимости.

-r
--createrole
Новому пользователю будет разрешено создавать другие роли (то есть этот пользователь наделяется правом CREATEROLE).

-R
--no-createrole
Новому пользователю будет запрещено создавать новые роли. Это поведение по умолчанию.

-s
--superuser
Новый пользователь будет суперпользователем.

-S
--no-superuser
Новый пользователь не будет суперпользователем. Это поведение по умолчанию.

--interactive
Запросить имя пользователя, а также значения параметров -d/-D, -r/-R и -s/-S, если они не указаны в командной строке.

--replication
Новый пользователь будет наделен правом REPLICATION, которое более подробно описано в документации по CREATE ROLE.

--no-replication
Новый пользователь не будет наделен правом REPLICATION.

Аргументы

имя_роли
Задает имя создаваемого пользователя QHB. Это имя должно отличаться от имен всех существующих ролей в этой инсталляции QHB.

Переменные среды

PGHOST
PGPORT
PGUSER
Параметры подключения по умолчанию.

PG_COLOR
Указывает, использовать ли цвет в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Диагностика

В случае возникновения затруднений обратитесь к CREATE ROLE и psql, где рассматриваются потенциальные проблемы и сообщения об ошибках. Сервер базы данных должен работать на целевом хосте.

Примеры

Создать пользователя joe на сервере базы данных по умолчанию:

$ createuser joe

Создать пользователя joe на сервере базы данных по умолчанию с запросом дополнительных атрибутов:

$ createuser --interactive joe
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) n
Shall the new role be allowed to create more new roles? (y/n) n

Создать того же пользователя joe с явно заданными атрибутами, подключившись к серверу на хосте eden, порт 5000:

$ createuser -h eden -p 5000 -S -D -R -e joe
CREATE ROLE joe NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN;

Создать пользователя joe как суперпользователя с немедленным назначением пароля:

$ createuser -P -s -e joe
Enter password for new role: xyzzy
Enter it again: xyzzy
CREATE ROLE joe PASSWORD 'md5b5f5ba1a423792b526f799ae4eb3d59e' SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN;

В приведенном выше примере новый пароль на самом деле не отображается на экране при вводе, но для ясности мы показываем, что было введено. Как можно заметить, перед отправкой клиенту пароль шифруется.

См. также

dropuser, CREATE ROLE

dropdb

dropdb — удалить базу данных QHB

Синтаксис

dropdb [Флаг...] [Параметр...] имя_бд --host=хост

Описание

Утилита dropdb удаляет уже существующую базу данных QHB. Пользователь, который выполняет эту команду, должен быть суперпользователем или владельцем этой базы.

dropdb — это обертка для команды SQL DROP DATABASE. Между удалением баз данных с помощью этой утилиты и их удалением иными способами при обращении к серверу по сути нет никакой разницы.

Для сохранения соединения необходимо подключиться к обслуживающей базе данных (флаг maintenance-db), так как нельзя удалить базу данных, к которой подключен. Также нельзя удалить базу template1 и/или template0.

Параметры

dropdb принимает следующие аргументы командной строки:

имя_бд
Задает имя удаляемой базы данных.

-e
--echo
Отобразить на экране команды, которые dropdb генерирует и отправляет на сервер.

-f
--force
Попытаться принудительно завершить все существующие подключения к целевой базе, прежде чем удалять ее. Подробную информацию об этом см. в описании команды DROP DATABASE.

-i
--interactive
Выдает запрос о подтверждении перед удалением.

--if-exists
Не считать ошибкой, если базы данных не существует. В этом случае выдается уведомление.

-V
--version
Вывести версию dropdb и завершиться.

-?
--help
Показать справку об аргументах командной строки dropdb и завершиться.

Кроме того, в качестве параметров подключения dropdb принимает следующие аргументы командной строки:

-h хост
--host=хост
Задает имя хост-компьютера, на котором работает сервер. Если значение начинается со слэша, оно используется в качестве каталога для Unix-сокета.

-p порт
--port=порт
Указывает TCP-порт или расширение файла локального Unix-сокета, через который сервер принимает подключения.

-U имя_пользователя
--username=имя_пользователя
Имя пользователя, под которым производится подключение.

-w
--no-password
Никогда не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, и пароль недоступен с помощью иных средств, таких как файл .pgpass, попытка подключения завершится неудачно. Этот параметр может быть полезен в пакетных заданиях и скриптах, где нет пользователя, чтобы ввести пароль.

-W
--password
Принудительно запрашивать пароль перед подключением к базе данных.

Это несущественный параметр, так как dropdb автоматически запросит пароль, если сервер требует аутентификацию по паролю. Однако чтобы выяснить это, dropdb потребуется дополнительная попытка подключения к серверу. В некоторых случаях имеет смысл ввести -W, чтобы исключить эту лишнюю попытку.

--maintenance-db=имя_бд Задает имя базы данных, к которой будет произведено подключение при удалении целевой базы. Если это имя не указано, будет выбрана база данных qhb, а если она не существует (или это имя удаляемой базы) — template1.

Окружение

PGHOST
PGPORT
PGUSER
Параметры подключения по умолчанию

PG_COLOR
Указывает, использовать ли цвета в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Диагностика

В случае возникновения затруднений обратитесь к DROP DATABASE и psql, где рассматриваются потенциальные проблемы и сообщения об ошибках. Сервер базы данных должен работать на целевом хосте.

Примеры

Удалить базу данных new_db. Информация о подключении берется из среды:

$ dropdb new_db

Удалить базу данных new_db на сервере localhost, порт 5432, в интерактивном режиме:

$ dropdb -h localhost -p 5432 -i new_db
Database "new_db" will be permanently removed.
Are you sure? (Y/n):

См. также

createdb, DROP DATABASE

dropuser

dropuser — удалить учетную запись пользователя QHB

Синтаксис

dropuser [параметр-подключения...] [параметр...] [имя_пользователя]

Описание

Утилита dropuser удаляет уже существующего пользователя QHB. Только суперпользователи и пользователи с правом CREATEROLE могут удалять пользователей QHB. (Чтобы удалить суперпользователя, нужно самому быть суперпользователем.)

dropuser — это обертка для команды SQL DROP ROLE. Между удалением пользователей с помощью этой утилиты и их удалением иными способами при обращении к серверу по сути нет никакой разницы.

Параметры

dropuser принимает следующие аргументы командной строки:

имя_пользователя
Задает имя удаляемого пользователя QHB. Если используется параметр -i/--interactive и имя не указано в командной строке, вам будет предложено ввести его интерактивно.

-e
--echo
Отобразить на экране команды, которые dropuser генерирует и отправляет на сервер.

-i
--interactive
Запросить подтверждение перед фактическим удалением пользователя и запросить его имя, если оно не было указано в командной строке.

-V
--version
Вывести версию dropuser и завершиться.

--if-exists
Не считать ошибкой, если пользователя не существует. В этом случае выдается уведомление.

-?
--help
Показать справку об аргументах командной строки dropuser и завершиться.

Кроме того, в качестве параметров подключения dropuser принимает следующие аргументы командной строки:

-h хост
--host=хост
Задает имя хост-компьютера, на котором работает сервер. Если значение начинается со слэша, оно используется в качестве каталога для Unix-сокета.

-p порт
--port=порт
Указывает TCP-порт или расширение файла локального Unix-сокета, через который сервер принимает подключения.

-U имя_пользователя
--username=имя_пользователя
Имя пользователя, под которым производится подключение (а не удаляемого пользователя).

-w
--no-password
Никогда не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, и пароль недоступен с помощью иных средств, таких как файл .pgpass, попытка подключения завершится неудачно. Этот параметр может быть полезен в пакетных заданиях и скриптах, где нет пользователя, чтобы ввести пароль.

-W
--password
Принудительно запрашивать пароль перед подключением к базе данных.

Это несущественный параметр, так как dropuser автоматически запросит пароль, если сервер требует аутентификацию по паролю. Однако чтобы выяснить это, dropuser потребуется дополнительная попытка подключения к серверу. В некоторых случаях имеет смысл ввести -W, чтобы исключить эту лишнюю попытку.

Переменные среды

PGHOST
PGPORT
PGUSER
Параметры подключения по умолчанию

PG_COLOR
Указывает, использовать ли цвета в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Диагностика

В случае возникновения затруднений обратитесь к DROP ROLE и psql, где рассматриваются потенциальные проблемы и сообщения об ошибках. Сервер базы данных должен работать на целевом хосте.

Примеры

Удалить пользователя alex с сервера базы данных по умолчанию:

$ dropuser alex

Удалить пользователя alex на сервере eden, порт 5000, с проверкой и просмотром базовой команды:

$ dropuser -p 5000 -h eden -i -e alex
Role "alex" will be permanently removed.
Are you sure? (y/n) y
DROP ROLE alex;

См. также

createuser, DROP ROLE

psql

psql — интерактивный терминал QHB.

Синтаксис

psql [параметр...] [имя_бд [имя_пользователя]]

Описание

psql — это терминальный клиент для работы с QHB. Эта программа позволяет интерактивно вводить запросы, выполнять их в QHB и просматривать их результаты. Также запросы можно получать из файла или из аргументов командной строки. Кроме того, psql предоставляет ряд метакоманд и различные функциональности, схожие с имеющимися у командных оболочек, для облегчения написания скриптов и автоматизации широкого спектра задач.

Параметры

-a
--echo-all
Отправить все непустые входные строки в стандартный вывод по мере их чтения. (Это не относится к строкам, считанным интерактивно.) Равнозначно установке переменной ECHO в значение all.

-A
--no-align
Переключает на режим невыровненного вывода. (По умолчанию используется режим aligned (выровненный).) Равнозначно команде \pset format unaligned.

-b
--echo-errors
Отправить команды SQL с ошибками в стандартный вывод для ошибок. Равнозначно установке переменной ECHO в значение errors.

-c команда
--command=команда
Указывает, что psql должна выполнить заданную командную строку (команду). Этот параметр можно вводить несколько раз и комбинировать в любом порядке с параметром -f. Когда указывается -c или -f, psql не читает команды со стандартного ввода; вместо этого она завершается после обработки всех параметров -c и -f по порядку.

Заданная команда должна быть либо командной строкой, которая полностью анализируется сервером (т. е. не содержит специфические функциональности psql), либо одиночной командой с обратным слэшем. Таким образом, в параметре -c нельзя смешивать метакоманды SQL psql. Чтобы добиться этого, можно передать несколько ключей -c или передать строку в psql через канал, например:

psql -c '\x' -c 'SELECT * FROM foo;'

или

echo '\x \\ SELECT * FROM foo;' | psql

(\\ — разделитель метакоманд.)

Каждая строка команд SQL, переданная в -c, отправляется на сервер как один запрос. Поэтому сервер выполняет ее в одной транзакции, даже когда эта строка содержит несколько команд SQL, если только в ней не содержатся явные команды BEGIN/COMMIT, разделяющие ее на несколько транзакций. Кроме того, psql выводит результат только последней команды SQL в строке. Это отличается от поведения, когда та же строка считывается из файла или подается на стандартный ввод, потому что в последнем случае psql передает каждую команду SQL по отдельности.

Из-за такого поведения указание нескольких команд SQL в одной строке -c зачастую приводит к неожиданным результатам. Лучше использовать параметр -c несколько раз или подавать команды на стандартный ввод psql, применяя либо параметр echo, как показано выше, либо создаваемый непосредственно в оболочке документ, например:

psql <<EOF
\x
SELECT * FROM foo;
EOF

--csv
Переключает в режим вывода CSV (Comma-Separated Values, значения, разделенные запятыми). Равнозначно команде \pset format csv.

-d имя_бд
--database=имя_бд
Задает имя базы данных для подключения. Это равнозначно указанию имя_бд в качестве первого аргумента, не являющегося ключом, в командной строке.

-e
--echo-queries
Скопировать все команды SQL, отправленные на сервер, еще и на стандартный вывод. Равнозначно установке переменой ECHO в значение queries.

-E
--echo-hidden
Отобразить фактические запросы, генерируемые \d и другими командами, начинающимися с обратного слэша. Это можно использовать для изучения внутренних операций psql. Равнозначно установке переменой ECHO_HIDDEN в значение on (включено).

-f имя_файла
--file=имя_файла
Читать команды из файла имя_файла, а не из стандартного ввода. Этот параметр можно вводить несколько раз и комбинировать в любом порядке с параметром -c. Когда указывается -c или -f, psql не читает команды со стандартного ввода; вместо этого она завершается после обработки всех параметров -c и -f по порядку. Не считая этого, данный параметр практически равнозначен метакоманде \i.

Если имя_файла задано символом «-» (минус), то стандартный ввод считывается до признака конца файла или до метакоманды \q. Это позволяет перемежать интерактивный ввод с вводом из файлов. Однако обратите внимание, что Readline в таком случае не применяется (почти так же, как и с ключом -n).

Использование этого параметра немного отличается от написания psql < имя_файла. В целом, оба варианта будут делать то, что вы ожидаете, но с -f доступны некоторые полезные свойства, например сообщения об ошибках с номерами строк. Также есть небольшая вероятность, что использование этого параметра снизит издержки при запуске. С другой стороны, вариант с перенаправлением ввода из оболочки (в теории) гарантирует получение точно такого же вывода, какой вы получили бы, введя все вручную.

-F разделитель
--field-separator=разделитель
Использовать разделитель в качестве разделителя полей при режиме невыровненного вывода. Равнозначно команде \pset fieldsep или \f.

-h имя_хоста
--host=имя_хоста
Указывает имя хоста сервера баз данных. Если значение начинается со слэша, оно определяет каталог Unix-сокета.

-H
--html
Переключает в режим вывода HTML. Равнозначно команде \pset format html или \H.

-l
--list
Вывести список всех доступных баз данных и завершиться. Другие параметры, не связанные с подключением, игнорируются. Это похоже на метакоманду \list.

При использовании этого параметра psql будет подключаться к базе данных qhb, если только в командной строке не задана другая база (в параметре -d или не через параметры, а, допустим, через запись службы, но не через переменную среды).

-L имя_файла
--log-file=имя_файла
В дополнение к обычному расположению вывода, записывать вывод результатов всех запросов в файл имя_файла.

-n
--no-readline
Не использовать Readline для редактирования командной строки и не использовать историю команд. Это может быть полезно для выключения расширенных действий клавиш табуляции при вырезании и вставке.

-o имя_файла
--output=имя_файла
Записать вывод результатов всех запросов в файл имя_файла. Равнозначно команде \o.

-p порт
--port=порт
Указывает порт TCP или расширение файла локального Unix-сокета, на котором сервер принимает подключения. Значение по умолчанию определяется переменной среды PGHOST, или, если она не установлена, числом, заданным при компиляции, обычно 5432.

-P присвоение
--pset=присвоение
Задает параметры печати, в стиле команды \pset. Обратите внимание, что здесь вам придется разделить имя присваиваемого параметра и значение знаком равенства, а не пробела. Например, чтобы установить формат вывода в LaTeX, нужно написать -P format=latex.

-q
--quiet
Указывает, что psql должна работать «тихо». По умолчанию она выводит приветствия и различные информационные сообщения. При использовании данного параметра этого не произойдет. Имеет смысл применять его вместе с параметром c. Равнозначно установке переменной QUIET в значение on (включено).

-R разделитель
--record-separator=разделитель
Использовать разделитель в качестве разделителя записей при режиме невыровненного вывода. Равнозначно команде \pset recordsep.

-s
--single-step
Запуск в пошаговом режиме. Это означает, что пользователь будет подтверждать выполнение каждой команды перед ее отправкой на сервер, с возможностью отменить выполнение. Используется для отладки скриптов.

-S
--single-line
Запуск в построчном режиме, при котором символ новой строки завершает команду SQL, так же как это делает точка с запятой.

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

-t
--tuples-only
Выключить вывод имен столбцов и результирующей строки с количеством выбранных записей. Равнозначно команде \t или \pset tuples_only.

-T параметры_таблицы
--table-attr=параметры_таблицы
Задает параметры, которые будут вставлены в тег HTML table. За подробностями обратитесь к описанию команды \pset tableattr.

-U имя_пользователя
--username=имя_пользователя
Использовать для подключения имя_пользователя вместо установленного по умолчанию. (Разумеется, это потребует соответствующего разрешения.)

-v присвоение
--set=присвоение
--variable=присвоение
Произвести присвоение значения переменной, как метакоманда \set. Обратите внимание, что в командной строке нужно разделить имя переменной и значение (при наличии) знаком равенства. Чтобы сбросить переменную, опустите знак равенства. Чтобы установить пустое значение, добавьте знак равенства, но опустите значение. Эти присвоения выполняются во время обработки командной строки, так что переменные, отражающие состояние подключения, позже будут переписаны.

--version
Вывести версию psql и завершиться.

-w
--no-password
Никогда не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, и пароль недоступен с помощью иных средств, таких как файл .pgpass, попытка подключения завершится неудачно. Этот параметр может быть полезен в пакетных заданиях и скриптах, где нет пользователя, чтобы ввести пароль.

Обратите внимание, что этот параметр действует в течение всего сеанса и, следовательно, влияет на использование метакоманды \connect, а также на первую попытку подключения.

-W
--password
Принудительно запрашивать пароль перед подключением к базе данных, даже если он не будет использоваться.

Если сервер требует аутентификацию по паролю и пароль нельзя получить из других источников, например из файла .pgpass, psql запросит пароль в любом случае. Однако чтобы понять, что требуется пароль, psql потребуется дополнительная попытка подключения к серверу. В некоторых случаях имеет смысл ввести -W, чтобы исключить эту лишнюю попытку.

Обратите внимание, что этот параметр действует в течение всего сеанса и, следовательно, влияет на использование метакоманды \connect, а также на первую попытку подключения.

-x
--expanded
Включить режим развернутого форматирования таблицы. Равнозначно команде \x или \pset.

-X
--no-psqlrc
Не читать стартовые файлы (ни общесистемный файл psqlrc, ни пользовательский файл ~/.psqlrc).

-z
--field-separator-zero
Установить нулевой байт в качестве разделителя полей для невыровненного вывода. Равнозначно команде \pset fieldsep_zero.

-0
--record-separator-zero
Установить нулевой байт в качестве разделителя записей для невыровненного вывода. Это полезно при взаимодействии, например, с xargs -0. Равнозначно команде \pset recordsep_zero.

-1
--single-transaction
Этот параметр можно использовать только в сочетании с одним или несколькими параметрами -c и/или -f. С ним psql выполняет команду BEGIN перед обработкой первого такого параметра и команду COMMIT после последнего, заворачивая таким образом все команды в одну транзакцию. Это гарантирует, что либо все команды завершатся успешно, либо никакие изменения не сохранятся.

Если в самих этих командах содержатся операторы BEGIN, COMMIT или ROLLBACK, этот параметр не даст желаемого эффекта. Кроме того, если какая-либо отдельная команда не может выполняться внутри блока транзакции, указание этого параметра приведет к тому, что вся транзакция прервется с ошибкой.

-?
--help[=тема]
Показать справку по psql и завершиться. Необязательный параметр тема (по умолчанию options) выбирает описание интересующей части psql: commands описывает команды psql с обратным слэшем; options описывает параметры командной строки, которые можно передать psql; а variables выдает справку по переменным конфигурации psql.

Код завершения

psql возвращает в командную оболочку 0 при нормальном завершении, 1 — если произошла фатальная ошибка в самом psql (например, недостаточно памяти, файл не найден), 2 — при неудачном подключении к серверу неинтерактивного сеанса, и 3 — при ошибке в скрипте и установленной переменной ON_ERROR_STOP.

Использование

Подключение к базе данных

psql — это обычное клиентское приложение QHB. Для подключения к базе данных необходимо знать имя целевой базы, имя хоста и номер порта сервера, а также имя пользователя, под которым вы хотите подключиться. Для psql эти параметры можно задать через аргументы командной строки, а именно -d, -h, -p и -U соответственно. При обнаружении в командной строке аргумента не относящегося ни к одному параметру, оно будет воспринято как имя базы данных (или имя пользователя, если имя базы данных уже задано). Задавать все эти аргументы необязательно; у них есть подходящие значения по умолчанию. Если опустить имя хоста, psql подключится через Unix-сокет к серверу на локальном хосте или через TCP/IP к localhost, если в системе нет Unix-сокетов. Номер порта по умолчанию определяется во время компиляции. Поскольку сервер баз данных использует то же значение по умолчанию, в большинстве случаев указывать порт нет необходимости. Имя пользователя по умолчанию, как и имя базы данных по умолчанию — это ваше имя пользователя в операционной системе. Обратите внимание, что просто так подключиться к любой базе данных под любым именем пользователя не получится. О правах доступа можно узнать у администратора баз данных.

Если значения по умолчанию не вполне подходят, вместо ввода нужных параметров вручную можно установить переменные среды PGDATABASE, PGHOST, PGPORT и/или PGUSER в подходящие значения. Также удобно иметь файл ~/.pgpass, чтобы каждый раз не вводить пароли.

Альтернативный способ указания параметров подключения — использование вместо имени базы данных строки conninfo или URI. Этот механизм дает широкие возможности для управления подключением. Например:

$ psql "service=myservice sslmode=require"
$ psql postgresql://dbmaster:5433/mydb?sslmode=require

Этот способ также позволяет использовать для получения параметров подключения LDAP.

Если по какой-либо причине соединение не может быть установлено (например, недостаточно прав, сервер на целевом хосте не работает и т. д.), psql вернет ошибку и прекратит работу.

Если и стандартный ввод, и стандартный вывод являются терминалом, то psql установит кодировку клиента в «auto», и подходящая клиентская кодировка будет определяться из настроек локалей (переменная среды LC_CTYPE в системах Unix). Если это работает не так, как ожидалось, клиентскую кодировку можно изменить, установив переменную среды PGCLIENTENCODING.

Ввод команд SQL

При работе в штатном режиме в командной строке psql отображается имя базы данных, к которой psql подключена в данный момент, а затем идет строка =>. Например:

$ psql testdb
psql (1.0)
Type "help" for help.

testdb=>

В командной строке пользователь может вводить команды SQL. Обычно введенные строки отправляются на сервер по достижении точки с запятой, завершающей команду. Конец строки не завершает команду. Таким образом, команды можно для ясности разбить на несколько строк. Если команда была отправлена и выполнена без ошибок, ее результаты отображаются на экране.

Если к базе данных, в которой не был установлен шаблон безопасного использования схем имеют доступ недоверенные пользователи, начинайте сеанс с удаления общедоступных схем из search_path (пути поиска). Для этого перед выполнением других команд SQL можно выполнить команду SELECT pg_catalog.set_config('search_path', '', false). Это касается не только psql, но и всех остальных интерфейсов для выполнения произвольных команд SQL.

При каждом выполнении команды psql также проверяет асинхронные уведомления о событиях, генерируемые командами LISTEN и NOTIFY.

Тогда как комментарии в стиле C передаются на сервер для обработки и удаления, комментарии в стандарте SQL удаляются psql.

Метакоманды

Все, что вводится в psql не заключенное в кавычки и начинающееся с обратного слэша, является метакомандой psql, которая обрабатывается самой psql. Эти команды делают psql более полезной для администрирования или написания скриптов. Метакоманды часто называют командами слэша или обратного слэша.

Команды psql имеют следующий формат: обратный слэш, сразу за ним команда, затем аргументы. Аргументы отделяются от команды и друг от друга любым числом пробелов.

Чтобы включить в аргумент пробел, нужно заключить его в апострофы. Чтобы включить в аргумент апостроф, нужно написать два апострофа внутри текста в апострофах. Все, что содержится в апострофах, в дальнейшем подлежит заменам, принятым в языке C: \n (новая строка), \t (табуляция), \b (backspace), \r (возврат каретки), \f (подача страницы), \цифры (восьмеричное число) и \xцифры (шестнадцатеричное число). Внутри апострофов обратный слэш, стоящий перед любым другим символом, экранирует этот символ.

Если внутри аргумента встречается двоеточие (:) не в кавычках с последующим именем переменной psql, оно заменяется значением этой переменной, как описано ниже в разделе Интерполяция SQL. Также будут работать описанные там формы :'имя_переменной' и :"имя_переменной". Синтаксис :{?имя_переменной} позволяет проверить, определена ли переменная. Оно заменяется значением TRUE или FALSE. Экранирование двоеточия обратным слэшем заменяет его от замены.

Внутри аргумента текст, взятый в обратные кавычки (`), считается командной строкой, которая передается в командную оболочку. Вывод этой команды (с удаленными в конце символами новой строки) заменяет текст в обратных кавычках. Внутри этого текста не обрабатываются никакие кавычки или другие управляющие символы или последовательности, за исключением того, что все вхождения :имя_переменной, где имя_переменной — это имя переменной psql, заменяются значением этой переменной. Также вхождения :'имя_переменной' заменяются значением переменной, заключенным в подходящие кавычки, чтобы стать одним аргументом команды оболочки. (Последняя форма почти всегда предпочтительна, если только вы не абсолютно точно знаете, что содержится в переменной.) Поскольку символы возврата каретки и перевода строки невозможно надежно защитить на всех платформах, форма :'имя_переменной' выводит сообщение об ошибке и не производит подстановку значения переменной, когда это значение содержит такие символы.

Некоторые команды принимают в качестве аргумента идентификатор SQL (например, имя таблицы). Такие аргументы следуют правилам синтаксиса SQL: буквы, не заключенные в кавычки, преобразуются в нижний регистр, тогда как кавычки (") защищают буквы от преобразования регистра и позволяют включать пробелы в идентификатор. Внутри кавычек в итоговом имени две кавычки сокращаются до одной. Например, FOO"BAR"BAZ интерпретируется как fooBARbaz, а "A weird"" name" становится A weird" name.

Разбор аргументов останавливается в конце строки или когда встречается другой обратный слэш, не взятый в кавычки. Обратный слэш не внутри кавычек принимается за начало новой метакоманды. Специальная последовательность \\ (два обратных слэша) обозначает окончание аргументов и продолжение разбора команд SQL, если таковые имеются. Таким образом, команды SQL и psql можно свободно смешивать в одной строке. Но в любом случае аргументы метакоманд не могут выходить за пределы текущей строки.

Многие из метакоманд оперируют с буфером текущего запроса. Это обычный буфер, содержащий произвольный текст команды SQL, который был введен, но еще не передан серверу для выполнения. В него будут входить и предыдущие строки ввода, а также текст, находящийся в той же строке перед метакомандой.

Определены следующие метакоманды:

\a

Если текущий формат вывода таблицы невыровненный, то он переключается на выровненный. Если текущий формат выровненный, то устанавливается невыровненный. Эта команда поддерживается для обратной совместимости. См. \pset для более общего решения.

\c или \connect [ -reuse-previous=on|off ] [ имя_бд [ имя_пользователя ] [ хост ] [ порт ] | строка_подключения ]

Устанавливает новое подключение к серверу QHB. Параметры подключения можно указывать как позиционно (один или несколько по списку: база данных, пользователь, хост и порт), так и передавая аргумент строка_подключения. Если аргументы отсутствуют, новое подключение устанавливается с теми же параметрами, что и предыдущее.

Указание в параметре имя_бд, имя_пользователя, хост или порт значения - равнозначно опущению этого параметра.

Новое подключение может повторно использовать параметры предыдущего; не только имя базы данных, пользователя, хост и порт, но и, к примеру, режим_ssl. По умолчанию параметры используются повторно при позиционной записи, но не когда указывается строка_подключения. Если передать в первом аргументе -reuse- revious=on или -reuse-previous=off, это поведение переопределяется. При повторном использовании любой параметр, не заданный явно в виде позиционного или в строке_подключения, берется из параметров текущего подключения. Исключение составляет параметр адрес_хоста — если в новом подключении значение хост меняется в позиционной записи, значение адрес_хоста из текущих параметров подключения сбрасывается. Кроме того, пароль существующего подключения будет использован повторно, только если имя пользователя, хост и порт не меняются.

Если новое подключение успешно установлено, предыдущее подключение закрывается. Если попытка подключения не удалась (неверное имя пользователя, доступ запрещен и т. д.), то предыдущее соединение останется активным, если psql находится в интерактивном режиме. Но если скрипт выполняется неинтерактивно, предыдущее соединение закрывается с сообщением об ошибке. При этом скрипт может завершиться или не завершиться; в последнем случае все команды, обращающиеся к базе данных, будут неуспешными, пока не будет успешно выполнена еще одна команда \connect. Различное поведение было выбрано для удобства пользователя: с одной стороны в качестве защиты от опечаток, а с другой — как защитный механизм, не позволяющий случайно запустить скрипты в неправильной базе данных. Обратите внимание, что когда команда \connect пытается повторно использовать параметры, она использует параметры последнего удачного подключения, а не какой-либо из последующих неудачных попыток. Однако в случае неудачи при неинтерактивном выполнении команды \connect никакие параметры не позволено использовать впоследствии, так как скрипт, скорее всего, рассчитывает на то, что должны повторно использоваться значения именно от неуспешной команды \connect.

Примеры:

=> \c mydb myuser host.dom 6432
=> \c service=foo
=> \c "host=localhost port=5432 dbname=mydb connect_timeout=10 sslmode=disable"
=> \c -reuse-previous=on sslmode=require    -- меняется только sslmode
=> \c qhb://tom@localhost/mydb?application_name=myapp

\C [ заголовок ]

Задает заголовок любых таблиц, который будет выводиться в результате запроса, или отменяет установленный ранее заголовок. Эта команда равнозначна \pset title заголовок. (Название этой команды происходит от слова «caption» (заголовок), поскольку раньше она применялась только для задания заголовков HTML-таблиц.)

\cd [ каталог ]

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

Совет
Для отображения текущего рабочего каталога используйте команду \! pwd.

\conninfo

Выводит информацию о текущем подключении к базе данных.

\copy { таблица [ ( список_столбцов ) ] } from { 'имя_файла' | program 'команда' | stdin | pstdin } [ [ with ] ( параметр [, ...] ) ] [ where условие ]
\copy { таблица [ ( список_столбцов ) ] | ( запрос ) } to { 'имя_файла' | program 'команда' | stdout | pstdout } [ [ with ] ( параметр [, ...] ) ]

Производит копирование данных с участием терминала (клиента). Данная операция запускает выполнение команды SQL COPY, но вместо чтения или записи указанного файла на сервере, его читает или записывает psql и пересылает данные между сервером и терминалом. Это означает, что здесь используются доступ к файлу и права локального пользователя, а не сервера, и не требуются права суперпользователя SQL.

Если указан параметр program, psql выполняет команду и данные, поступающие из/в нее, передаются между сервером и клиентом. Опять же, для выполнения программ используются права локального пользователя, а не сервера, и не требуются права суперпользователя SQL.

При выполнении \copy ... from stdin строки данных считываются из источника, выполнившего команду, и считываются до тех пор, пока не встретится \. или не будет достигнут конец файла. Этот параметр полезен для заполнения таблиц непосредственно в SQL-скриптах. При выполнении \copy ... to stdout вывод направляется в то же место, что и вывод команд psql, а статус команды COPY не отображается (так как его можно перепутать со строкой данных). Для чтения/ записи стандартного ввода или вывода psql, вне зависимости от источника текущей команды или параметра \o, напишите from pstdin или to pstdout.

Синтаксис этой команды похож на синтаксис команды SQL COPY. Все параметры, кроме источника/получателя данных, задаются так же, как в COPY. Из-за этого при разборе метакоманды \copy применяются другие правила. В отличие от большинства других метакоманд, для нее остаток строки всегда воспринимается как аргументы \copy, и в этих аргументах не выполняется ни интерполяция переменных, ни раскрытие обратных кавычек.

Совет
Другой способ получить тот же результат, что и с \copy ... to, — использовать команду SQL COPY ... TO STDOUT и завершить ее командой \g имя_файла или \g |программа. В отличие от \copy, этот метод позволяет разбивать команду на несколько строк, а также использовать интерполяцию переменных и раскрытие обратных кавычек.

Совет
Эти операции не так эффективны, как команда SQL COPY, в которой источником или получателем данных является файл, поскольку все данных должны перемещаться по клиент-серверному соединению. Для больших объемов данных команда SQL может оказаться предпочтительнее.

\copyright

Показывает информацию об авторских правах и условиях распространения QHB.

\crosstabview [ столбВ [ столбГ [ столбД [ столбсортГ ] ] ] ]

Выполняет содержимое буфера текущего запроса (как \g) и показывает результаты в виде перекрестной таблицы. Этот запрос должен возвращать как минимум три столбца. Столбец вывода, заданный параметром столбВ, становится вертикальным заголовком, а столбец вывода, заданный параметром столбГ — горизонтальным заголовком. Параметр столбД задает столбец с данными для отображения внутри таблицы. столбсортГ задает необязательный столбец сортировки для горизонтального заголовка.

Каждое указание столбца может быть номером (начиная с 1) или именем столбца. К именам применяются обычные принятые в SQL правила учета регистра и кавычек. По умолчанию (т. е. если значения не заданы) в качестве столбВ подразумевается столбец 1, а в качестве столбГ — столбец 2. столбГ должен отличаться от столбВ. Если столбД не задан, в результате запроса должно быть ровно три столбца, и в качестве столбД выбирается столбец, отличный от столбВ и столбГ.

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

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

Внутри перекрестной таблицы для каждого уникального значения x в столбГ и каждого уникального значения y в столбВ ячейка, находящаяся на пересечении (x,y), содержит значение столбД в строке результата запроса, в которой значение столбГ равно х, а значение столбВ — у. Если такой строки нет, ячейка остается пустой. Если таких строк несколько, выдается ошибка.

\d[S+] [ шаблон ]

Для каждого отношения (таблицы, представления, материализованного представления, индекса, последовательности или сторонней таблицы) или составного типа, соответствующих шаблону, показать все столбцы, их типы, табличное пространство (если это не значение по умолчанию) и все специальные атрибуты, такие как NOT NULL или значения по умолчанию. Также показываются связанные индексы, ограничения, правила и триггеры. Для сторонних таблиц также показывается связанный сторонний сервер. («Соответствие шаблону» определяется ниже в подразделе Шаблоны.)

Для некоторых типов отношений \d показывает дополнительную информацию по каждому столбцу: значения столбца для последовательностей, индексируемые выражения для индексов и параметры обертки сторонних данных для сторонних таблиц.

Форма команды \d+ идентична \d, но отображает больше информации: все комментарии к столбцам таблицы, наличие в таблице OID, определение представления, если отношение является представлением, свойство replica identity, если оно было изменено, и имя метода доступа, если для отношения определен метод доступа.

По умолчанию отображаются только объекты, созданные пользователями; для включения системных объектов нудно задать шаблон или добавить модификатор S.

Примечание
Если \d используется без аргумента шаблон, эта команда равнозначна \dtvmsE, то есть показывает список всех видимых таблиц, представлений, материализованных представлений, последовательностей и сторонних таблиц. Это сделано исключительно ради удобства.

\da[S] [ шаблон ]

Выводит список агрегатных функций вместе с типом возвращаемого значения и типами данных, которыми они оперируют. Если задан шаблон, показываются только те агрегатные функции, имена которых соответствуют этому шаблону. По умолчанию показываются только объекты, созданные пользователями; для включения системных объектов нужно задать шаблон или добавить модификатор S.

\dA[+] [ шаблон ]

Выводит список методов доступа. Если задан шаблон, показываются только те методы доступа, имена которых соответствуют этому шаблону. При добавлении + к имени команды для каждого метода доступа показываются его описание и связанная функция-обработчик.

\dAc[+] [шаблон-методов-доступа [шаблон-входных-типов]]

Выводит список классов операторов. Если задан шаблон-методов-доступа, перечисляются только классы операторов, связанные с методами доступа, имена которых соответствуют этому шаблону. Если задан шаблон-входных-типов, перечисляются только классы операторов, связанные с входными типами, имена которых соответствуют этому шаблону. При добавлении + к имени команды для каждого класса операторов показываются связанное с ним семейство операторов и владелец этого класса.

\dAf[+] [шаблон-методов-доступа [шаблон-входных-типов]]

Выводит список семейств операторов. Если задан шаблон-методов-доступа, перечисляются только семейства операторов, связанные с методами доступа, имена которых соответствуют этому шаблону. Если задан шаблон-входных-типов, перечисляются только семейства операторов, связанные с входными типами, имена которых соответствуют этому шаблону. При добавлении + к имени команды для каждого семейства операторов показывается его владелец.

\dAo[+] [шаблон-методов-доступа [шаблон-семейств-операторов]]

Выводит список операторов, связанных с семействами операторов. Если задан шаблон-методов-доступа, перечисляются только члены семейств операторов, связанных с методами доступа, имена которых соответствуют этому шаблону. Если задан шаблон-семейств- операторов, перечисляются только члены семейств операторов, имена которых соответствуют этому шаблону. При добавлении + к имени команды для каждого оператора показывается его семейство сортировки (если это оператор упорядочивания).

\dAp[+] [шаблон-методов-доступа [шаблон-семейств-операторов]]

Выводит список вспомогательных функций, связанных с семействами операторов. Если задан шаблон-методов-доступа, перечисляются только функции семейств операторов, связанных с методами доступа, имена которых соответствуют этому шаблону. Если задан шаблон-семейств-операторов, перечисляются только функции семейств операторов, имена которых соответствуют этому шаблону. При добавлении + к имени команды функции отображаются в развернутом виде, со списками фактических параметров.

\db[+] [ шаблон ]

Выводит список табличных пространств. Если задан шаблон, показываются только те табличные пространства, имена которых соответствуют этому шаблону. При добавлении + к имени команды для каждого табличного пространства показываются параметры, объем на диске, права доступа и описание.

\dc[S+] [ шаблон ]

Выводит список преобразований между кодировками наборов символов. Если задан шаблон, показываются только те преобразования кодировок, имена которых соответствуют этому шаблону. По умолчанию показываются только объекты, созданные пользователями; для включения системных объектов нужно задать шаблон или добавить модификатор S. При добавлении + к имени команды для каждого объекта дополнительно показывается описание.

\dC[+] [ шаблон ]

Выводит список приведений типов. Если задан шаблон, показываются только те приведения типов, у которых исходные или целевые типы соответствуют этому шаблону. При добавлении + к имени команды для каждого объекта показывается описание.

\dd[S] [ шаблон ]

Показывает описания объектов следующих видов: ограничение, класс операторов, семейство операторов, правило и триггер. Все остальные комментарии можно посмотреть посредством соответствующих метакоманд для этих типов объектов.

\dd показывает описания для объектов, соответствующих шаблону, или для доступных объектов указанных типов, если аргументы не заданы. Но в любом случае выводятся только те объекты, у которых есть описание. По умолчанию показываются только объекты, созданные пользователями; для включения системных объектов нужно задать шаблон или добавить модификатор S.

Описания объектов можно создать при помощи команды SQL COMMENT.

\dD[S+] [ шаблон ]

Выводит список доменов. Если задан шаблон, показываются только те домены, имена которых соответствуют этому шаблону. По умолчанию показываются только объекты, созданные пользователями; для включения системных объектов нужно задать шаблон или добавить модификатор S. При добавлении + к имени команды для каждого объекта показываются права доступа и описание.

\ddp [ шаблон ]

Выводит список прав доступа по умолчанию. Выводится строка для каждой роли (и схемы, если применимо), для которой права доступа по умолчанию отличаются от встроенных. Если задан шаблон, выводятся строки только для тех ролей или схем, имена которых соответствуют этому шаблону.

Права доступа по умолчанию устанавливаются командой ALTER DEFAULT PRIVILEGES. Смысл отображаемых прав объясняется в разделе Права.

\dE[S+] [ шаблон ]
\di[S+] [ шаблон ]
\dm[S+] [ шаблон ]
\ds[S+] [ шаблон ]
\dt[S+] [ шаблон ]
\dv[S+] [ шаблон ]

В этой группе команд буквы E, i, m, s, t и v обозначают соответственно: внешнюю таблицу, индекс, материализованное представление, последовательность, таблицу и представление. Можно указывать все или часть этих букв, в произвольном порядке, чтобы получить список объектов этих типов. Например, \dit выводит список индексов и таблиц. При добавлении + к имени команды для каждого объекта выводится состояние хранения (постоянный, временный или нежурналируемый), физический размер на диске и описание, при наличии. Если задан шаблон, выводятся только те объекты, имена которых соответствуют этому шаблону. По умолчанию показываются только объекты, созданные пользователями; для включения системных объектов нужно задать шаблон или добавить модификатор S.

\des[+] [ шаблон ]

Выводит список сторонних серверов (мнемоника: «external servers»). Если задан шаблон, выводятся только те серверы, имена которых соответствуют этому шаблону. Если используется форма \des+, показывается полное описание каждого сервера, включая права доступа, тип, версию, параметры и описание.

\det[+] [ шаблон ]

Выводит список сторонних таблиц (мнемоника: «external tables»). Если задан шаблон, выводятся только те записи, у которых имя таблицы или схемы соответствует этому шаблону. Если используется форма \det+, то дополнительно отображаются общие параметры и описание сторонней таблицы.

\deu[+] [ шаблон ]

Выводит список сопоставлений пользователей (мнемоника: «external users»). Если задан шаблон, выводятся только те сопоставления, в которых имена пользователей соответствуют этому шаблону. Если используется форма \deu+, то показывается дополнительная информация о каждом сопоставлении пользователей.

Внимание!
\deu+ также может отображать имя и пароль удаленного пользователя, поэтому следует позаботиться о том, чтобы не раскрывать их.

\dew[+] [ шаблон ]

Выводит список оберток сторонних данных (мнемоника: «external wrappers»). Если задан шаблон, выводятся только те обертки сторонних данных, имена которых соответствуют этому шаблону. Если используется форма \dew+, то дополнительно показываются права доступа, параметры и описание обертки.

\df[antwS+] [ шаблон [ арг_шаблон ... ] ]

Выводит список функций с типами данных их результатов, аргументов и классификацией: «agg» (агрегатная), «normal» (обычная), «procedure» (процедурная), «trigger» (триггерная) или «window» (оконная). Чтобы получить функции только определенного вида (видов), добавьте в команду соответствующие буквы a, n, p, t или w. Если задан шаблон, показываются только те функции, имена которых соответствуют этому шаблону. Любые дополнительные аргументы являются шаблонами имен типов, которые сопоставляются с именами типов первого, второго и последующих аргументов функций. (У сопоставленных функций может быть больше аргументов, чем вы задали. Чтобы предотвратить это, напишите минус (-) в качестве последнего аргумента арг_шаблон.) По умолчанию показываются только объекты, созданные пользователями; для включения системных объектов нужно задать шаблон или добавить модификатор S. Если используется форма \df+, то дополнительно показываются характеристики каждой функции, включая изменчивость, допустимость распараллеливания, владельца, классификацию по безопасности, права доступа, язык, исходный код и описание.

\dF[+] [ шаблон ]

Выводит список конфигураций текстового поиска. Если задан шаблон, показываются только те конфигурации, имена которых соответствуют этому шаблону. Если используется форма \dF+, то выводится полное описание для каждой конфигурации, включая базовый синтаксический анализатор и список словарей для каждого типа фрагментов.

\dFd[+] [ шаблон ]

Выводит список словарей текстового поиска. Если задан шаблон, показываются только те словари, имена которых соответствуют этому шаблону. Если используется форма \dFd+, то показывается дополнительная информация о каждом выбранном словаре, включая базовый шаблон текстового поиска и параметры инициализации.

\dFp[+] [ шаблон ]

Выводит список анализаторов текстового поиска. Если задан шаблон, показываются только те анализаторы, имена которых соответствуют этому шаблону. Если используется форма \dFp+, то показывается полное описание для каждого анализатора, включая базовые функции и список распознаваемых типов фрагментов.

\dFt[+] [ шаблон ]

Выводит список шаблонов текстового поиска. Если задан шаблон, показываются только те шаблоны, имена которых соответствуют этому шаблону. Если используется форма \dFt+, то показывается дополнительная информация о каждом шаблоне, включая имена базовых функций.

\dg[S+] [ шаблон ]

Выводит список ролей базы данных. (Поскольку понятия «пользователи» и «группы» были объединены в «роли», теперь эта команда равнозначна \du.) По умолчанию показываются только роли, созданные пользователями; для включения системных ролей нужно добавить модификатор S. Если задан шаблон, показываются только те роли, имена которых соответствуют этому шаблону. Если используется форма \dg+, показывается дополнительная информация о каждой роли; в настоящее время это комментарий к роли.

\dl

Это псевдоним для \lo_list, который показывает список больших объектов.

\dL[S+] [ шаблон ]

Выводит список процедурных языков. Если задан шаблон, выводятся только те языки, имена которых соответствуют этому шаблону. По умолчанию показываются только языки, созданные пользователями; для включения системных объектов нужно добавить модификатор S. При добавлении + к имени команды для каждого языка выводятся обработчик вызова, функция проверки, права доступа и является ли этот язык системным объектом.

\dn[S+] [ шаблон ]

Выводит список схем (пространств имен). Если задан шаблон, выводятся только те схемы, имена которых соответствуют этому шаблону. По умолчанию показываются только объекты, созданные пользователями; для включения системных объектов нужно задать шаблон или добавить модификатор S. При добавлении + к имени команды для каждого объекта выводятся права доступа и описание, при наличии.

\do[S+] [ шаблон [ арг_шаблон [ арг_шаблон ] ] ]

Выводит список операторов с их операндами и типами результатов. Если задан шаблон, выводятся только те операторы, имена которых соответствуют этому шаблону. Если задан один арг_шаблон, выводятся только те префиксные операторы, у которых тип правого аргумента соответствует этому шаблону. Если задано два арг_шаблона, выводятся только те бинарные операторы, у которых имена типов аргументов соответствуют этому шаблону. (Также можно написать - в качестве неиспользуемого аргумента унарного оператора.) По умолчанию показываются только объекты, созданные пользователями; для включения системных объектов нужно задать шаблон или добавить модификатор S. При добавлении + к имени команды для каждого оператора показывается дополнительная информация; в настоящее время это только имя базовой функции.

\dO[S+] [ шаблон ]

Выводит список правил сортировки. Если задан шаблон, выводятся только те правила, имена которых соответствуют этому шаблону. По умолчанию показываются только объекты, созданные пользователями; для включения системных объектов нужно задать шаблон или добавить модификатор S. При добавлении + к имени команды для каждого правила выводится описание, при наличии. Обратите внимание, что показываются только правила сортировки, применимые к кодировке текущей базы данных, поэтому в разных базах данных одной инсталляции результаты этой команды могут отличаться.

\dp [ шаблон ]

Выводит список таблиц, представлений и последовательностей с их правами доступа. Если задан шаблон, выводятся только те таблицы, представления и последовательности, имена которых соответствуют этому шаблону.

Для установки прав доступа используются команды GRANT and REVOKE. Смысл отображаемых прав объясняется в разделе Права.

\dP[itn+] [ шаблон ]

Выводит список партиционированных отношений. Если задан шаблон, выводятся только те записи, имена которых соответствуют этому шаблону. К команде можно добавить модификаторы t (tables, таблицы) и i (indexes, индексы), отфильтровав список по типу отношений. По умолчанию выводятся и партиционированные таблицы, и индексы.

При использовании модификаторы n («nested», вложенные) или указании шаблона в список включаются также вложенные партиционированные таблицы и столбец, показывающий родителя каждого партиционированного отношения.

При добавлении + к имени команды также отображается суммарный размер всех партиций каждого отношения и его описание. Если + дополняет модификатор n, показывается два размера: общий размер партиций листового уровня, непосредственно связанных с отношением, и общий размер всех партиций, включая субпартиции, связанные с отношением опосредованно.

\drds [ шаблон-ролей [ шаблон-баз ] ]

Выводит список определенных параметров конфигурации. Эти параметры могут быть специфичными для роли, специфичными для базы данных или для обеих. Параметры шаблон-ролей и шаблон-баз используются для отбора определенных ролей и баз данных соответственно. Если они опущены или указано *, выводятся все параметры, в том числе не специфичные для ролей или баз данных.

Для определения параметров конфигурации, специфичных для роли и базы данных, используются команды ALTER ROLE и ALTER DATABASE.

\dRp[+] [ шаблон ]

Выводит список публикаций репликации. Если задан шаблон, выводятся только те публикации, имена которых соответствуют этому шаблону. При добавлении + к имени команды для каждой публикации показываются также связанные с ней таблицы.

\dRs[+] [ шаблон ]

Выводит список подписок на репликацию. Если задан шаблон, выводятся только те подписки, имена которых соответствуют этому шаблону. При добавлении + к имени команды показываются дополнительные свойства подписок.

\dT[S+] [ шаблон ]

Выводит список типов данных. Если задан шаблон, выводятся только те типы, имена которых соответствуют этому шаблону. При добавлении + к имени команды для каждого типа выводятся внутреннее имя и размер, допустимые значения, если это тип enum, и права доступа. По умолчанию показываются только объекты, созданные пользователями; для включения системных объектов нужно задать шаблон или добавить модификатор S.

\du[S+] [ шаблон ]

Выводит список ролей базы данных. (Поскольку понятия «пользователи» и «группы» были объединены в «роли», теперь эта команда равнозначна \dg.) По умолчанию показываются только роли, созданные пользователями; для включения системных ролей нужно добавить модификатор S. Если задан шаблон, показываются только те роли, имена которых соответствуют этому шаблону. Если используется форма \du+, показывается дополнительная информация о каждой роли; в настоящее время это комментарий к роли.

\dx[+] [ шаблон ]

Выводит список установленных расширений. Если задан шаблон, выводятся только те расширения, имена которых соответствуют этому шаблону. Если используется форма \dx+, то для каждого расширения выводятся все принадлежащие ему объекты.

\dX [ шаблон ]

Выводит объекты расширенной статистики. Если задан шаблон, выводятся только те объекты, имена которых соответствуют этому шаблону.

Состояние каждого вида расширенной статистки показывается в столбце с именем, отражающим ее вид (например, Ndistinct). Состояние defined (определен) означает, что этот вид был запрошен при создании статистики, а NULL — что он не был запрошен. Чтобы узнать, запускалась ли команда ANALYZE и доступна ли статистика для планировщика, можно обратиться к представлению pg_stats_ext.

\dy[+] [ шаблон ]

Выводит список событийных триггеров. Если задан шаблон, выводятся только те событийные триггеры, имена которых соответствуют этому шаблону. При добавлении + к имени команды для каждого объекта выводится описание.

\e или \edit [ имя_файла ] [ номер_строки ]

Если задано имя_файла, этот файл открывается для редактирования; после выхода из редактора содержимое файла копируется в буфер текущего запроса. Если имя_файла не задано, буфер текущего запроса копируется во временный файл, который затем редактируется таким же образом. Либо, если буфер текущего запроса пуст, во временный файл копируется последний выполненный запрос, который затем редактируется таким же образом.

Если после редактирования файла или предыдущего запроса выйти из редактора без сохранения файла, буфер запроса очищается. В ином случае новое содержимое буфера запроса повторно разбирается в соответствии с обычными правилами psql, при этом весь буфер обрабатывается как одна строка. Все завершенные запросы немедленно выполняются; то есть если буфер запроса содержит или заканчивается точкой с запятой, все его содержимое до этой точки выполняется и удаляется из буфера. Все, что остается в буфере, повторно выводится на экран. Для его передачи серверу нужно ввести точку с запятой или \g, а для отмены путем очищения буфера запроса — \r.

Обработка буфера как одной стоки в основном влияет на метакоманды: все, что находится в буфере после метакоманды, будет восприниматься как аргумент(ы) этой метакоманды, даже если этот текст занимает несколько строк. (Поэтому таким способом нельзя выполнять скрипты с метакомандами. Для таких скриптов используйте \i.)

Если задан номер_строки, psql будет позиционировать курсор на указанную строку файла или буфера запроса. Обратите внимание, что если указан один аргумент и он числовой, psql предполагает, что это номер строки, а не имя файла.

Совет
Как конфигурировать редактор и настроить его поведение, рассказывается ниже, в разделе Переменные среды.

\echo текст [ ... ]

Печатает вычисленные аргументы в стандартный вывод, разделяя их пробелами с переводом строки в конце. Это может быть полезно для добавления дополнительной информации в вывод скриптов. Например:

=> \echo `date`
Tue Oct 26 21:40:57 CEST 1999

Если в первом аргументе передается -n без кавычек, перевод строки в конце не добавляется (и не выводится сам первый аргумент).

Совет
Если вы перенаправляете вывод запросов с помощью команды \o, возможно, вместо этой команды будет лучше применить \qecho. См. также описание \warn.

\ef [ описание_функции [ номер_строки ] ]

Эта команда извлекает определение именованной функции или процедуры в форме команды CREATE OR REPLACE FUNCTION или CREATE OR REPLACE PROCEDURE и открывает его для редактирования. Редактирование производится тем же образом, что и при выполнении \edit. Если выйти из редактора без сохранения, этот оператор сбрасывается. Если сохранить файл и выйти из редактора, измененная команда немедленно выполняется, если вы добавили к ней точку с запятой. В противном случае она повторно выводится на экран; для ее передачи серверу нужно ввести \g, а для отмены — \r.

Целевую функцию можно задать только по имени или именем и аргументами, например foo(integer, text). Типы аргументов необходимо давать, если существует более одной функции с таким именем.

Если функция не задана, для редактирования открывается пустая строка CREATE FUNCTION.

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

В отличие от большинства других метакоманд, весь остаток строки всегда воспринимается как аргумент(ы) \ef, и в этих аргументах не выполняется ни интерполяция переменных, ни раскрытие обратных кавычек.

Совет
Как конфигурировать редактор и настроить его поведение, рассказывается ниже, в разделе Переменные среды.

\encoding [ кодировка ]

Устанавливает кодировку набора символов на клиенте. Без аргумента эта команда показывает текущую кодировку.

\errverbose

Повторяет последнее серверное сообщение об ошибке с максимальным уровнем детализации, как если бы для переменной VERBOSITY было установлено значение verbose, а для SHOW_CONTEXT — always.

\ev [ имя_представления [ номер_строки ] ]

Эта команда извлекает определение именованного представления в форме команды CREATE OR REPLACE VIEW и открывает его для редактирования. Редактирование производится тем же образом, что и при выполнении \edit. Если выйти из редактора без сохранения, этот оператор сбрасывается. Если сохранить файл и выйти из редактора, измененная команда немедленно выполняется, если вы добавили к ней точку с запятой. В противном случае она повторно выводится на экран; для ее передачи серверу нужно ввести \g, а для отмены — \r.

Если представление не задано, для редактирования открывается пустая строка CREATE VIEW.

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

В отличие от большинства других метакоманд, весь остаток строки всегда воспринимается как аргумент(ы) \ev, и в этих аргументах не выполняется ни интерполяция переменных, ни раскрытие обратных кавычек.

\f [ строка ]

Устанавливает разделитель полей для невыровненного вывода запросов. По умолчанию используется вертикальная черта (|). Равнозначно команде \pset fieldsep.

\g [ (параметр=значение [...]) ] [ имя_файла ]
\g [ (параметр=значение [...]) ] [ |команда ]

Передает содержимое буфера текущего запроса серверу для выполнения.

Если после \g идут скобки, внутри них содержится разделенный пробелами список предложений параметров форматирования в виде параметр=значение, которые воспринимаются так же, как и в команде \pset параметр значение, но действуют только во время выполнения этого запроса. В этом списке пробелы не должны находиться вокруг знаков =, но должны разделять предложения параметров. Если =значение опускается, именованный параметр меняется так же, как и при выполнении \pset параметр без явно заданного значения.

Если заданы имя_файла или |команда, вывод запроса записывается в именованный файл или передается через поток заданной команде оболочки, а не отображается как обычно. Вывод записывается в файл или команду, только если запрос успешно возвращает ноль или более кортежей, но не при сбое запроса или выполнении команды SQL, не возвращающей данные.

Если буфер текущего запроса пуст, вместо этого повторно выполняется последний из запросов. За исключением этого поведения \g без аргументов по сути равнозначна точке с запятой. С аргументами же \g предоставляет «одноразовую» альтернативу команде \o и вдобавок позволяет корректировать на время выполнения параметры форматирования вывода, которые обычно устанавливаются командой \pset.

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

\gdesc

Показывает описание (то есть имена и типы данных столбцов) результата текущего запроса в буфере. При этом в действительности сам запрос не выполняется; однако если он содержит какие-либо синтаксические ошибки, о них сообщается обычным способом.

Если буфер текущего запроса пуст, вместо этого будет описан последний переданный запрос.

\gexec

Передает буфер текущего запроса серверу, а затем обрабатывает содержимое каждого столбца каждой строки вывода запроса (если он имеется) как оператор SQL, подлежащий выполнению. Например, создание индекса по каждому столбцу my_table:

=> SELECT format('create index on my_table(%I)', attname)
-> FROM pg_attribute
-> WHERE attrelid = 'my_table'::regclass AND attnum > 0
-> ORDER BY attnum
-> \gexec
CREATE INDEX
CREATE INDEX
CREATE INDEX
CREATE INDEX

Генерируемые запросы выполняются в том порядке, в каком возвращаются строки, и слева направо, если возвращается более одного столбца. Поля NULL игнорируются. Генерируемые запросы передаются для обработки на сервер в буквальном виде, так что они не могут быть метакомандами psql или содержать ссылки на переменные psql. В случае сбоя в одном из запросов, выполнение оставшихся запросов продолжается, если только не установлена переменная ON_ERROR_STOP. Выполнение каждого запроса подвергается влиянию переменной ECHO. (Используя команду \gexec, часто рекомендуется устанавливать для ECHO значение all или queries.) Протоколирование запросов, пошаговый режим, замер времени и другие расширенные возможности, также действуют при выполнении каждого генерируемого запроса.

Если буфер текущего запроса пуст, вместо этого повторно выполняется последний переданный запрос.

\gset [ префикс ]

Передает буфер текущего запроса на сервер и сохраняет вывод запроса в переменных psql (см. раздел Переменные ниже). Выполняемый запрос должен возвращать ровно одну строку. Каждый столбец этой строки сохраняется в отдельной переменной, которая называется так же, как и столбец. Например:

=> SELECT 'hello' AS var1, 10 AS var2
-> \gset
=> \echo :var1 :var2
hello 10

Если указан префикс, то он добавляется в начало имен столбцов запроса для создания имен переменных:

=> SELECT 'hello' AS var1, 10 AS var2
-> \gset result_
=> \echo :result_var1 :result_var2
hello 10

Если результат столбца NULL, то вместо присвоения значения соответствующая переменная сбрасывается.

Если запрос завершается неудачно или не возвращает одну строку, то переменные не изменяются.

Если буфер текущего запроса пуст, вместо этого повторно выполняется последний переданный запрос.

\gx [ (параметр=значение [...]) ] [ имя_файла ]
\gx [ (параметр=значение [...]) ] [ |команда ]

Метакоманда \gx равнозначна метакоманде \g, за исключением того, что она принудительно включает расширенный режим вывода для этого запроса, так же, как если бы в список параметров \pset было включено указание expanded=on. См. также описание \x.

\h или \help [ команда ]

Выдает справку по синтаксису указанной команде SQL. Если команда не указана, то psql выведет список всех команд, для которых доступна справка по синтаксису. Если в качестве команды указана звездочка (*), показывается справка по синтаксису для всех команд SQL.

В отличие от большинства других метакоманд, весь остаток строки всегда воспринимается как аргумент(ы) \help, и в этих аргументах не выполняется ни интерполяция переменных, ни раскрытие обратных кавычек.

Примечание
Для упрощения ввода команды, состоящие из нескольких слов, необязательно заключать в кавычки. Таким образом, можно просто писать \help alter table.

\H или \html

Включает вывод запросов в формате HTML. Если формат HTML уже включен, происходит переключение обратно на выровненный формат текста. Эта команда предназначена для совместимости и удобства, но в описании \pset можно узнать о настройке других вариантов вывода.

\i или \include имя_файла

Читает ввод из файла имя_файла и выполняет его, как если бы он был набран на клавиатуре.

Если имя_файла задано как - (минус), читается стандартный ввод до обозначения конца файла или до метакоманды \q. Это можно использовать для совмещения интерактивного ввода с вводом из файлов. Обратите внимание, что поведение Readline будет применяться, только если оно активно на внешнем уровне.

Примечание
Если вы хотите видеть строки файла на экране по мере их прочтения, следует установить для переменной ECHO значение all.

\if выражение
\elif выражение
\else
\endif

Эта группа команд реализует вложенные условные блоки. Условный блок должен начинаться командой \if и заканчиваться \endif. Между ними может быть любое количество предложений \elif, за которыми может дополнительно следовать одно предложение \else. Между командами, формирующими условный блок, могут располагаться (и обычно располагаются) обычные запросы и другие типы команд с обратным слэшем.

Команды \if и \elif считывают свои аргументы и вычисляют их как логические выражения. Если такое выражение выдает true, обработка продолжается как обычно; в противном случае строки пропускаются до достижения соответствующих команд \elif, \else или \endif. Как только проверка \if или \elif успешно завершается, аргументы последующих команд \elif в том же блоке не вычисляются, а считаются ложными. Строки после \else обрабатываются, только если ни одна из предыдущих проверок \if или \elif не была успешной.

В аргументе выражение команды \if или \elif производится интерполяция переменных и раскрытие обратных кавычек, как и в аргументе любой другой команды с обратным слэшем. После этого он оценивается как значение переменной параметра вкл/выкл. Поэтому допустимым значением является любое однозначное значение (без учета регистра) из следующих: true, false, 1, 0, on, off, yes, no. Например, значения t, T и tR все будут восприниматься как true.

Если выражения не удается оценить как true или false, будет выдано предупреждение, а их результат будет считаться ложным.

Пропускаемые строки разбираются как обычно для идентификации запросов и команд с обратным слэшем, но запросы не передаются на сервер, а команды с обратным слэшем, отличные от условных (\if, \elif, \else, \endif), игнорируются. Условные команды проверяются только на правильность вложенности. Ссылки на переменные в пропускаемых строках не разворачиваются, как и не производится раскрытие обратных кавычек.

Все команды с обратным слэшем в заданном условном блоке должны находиться в одном исходном файле. Если до того, как были закрыты все локальные блоки \if, в основном файле входа будет достигнут конец файла или встретится включение другого файла (команда \include), psql выдаст ошибку.

Например:

-- проверка существования двух отдельных записей в базе данных и сохранение
-- результатов в двух разных переменных psql
SELECT
    EXISTS(SELECT 1 FROM customer WHERE customer_id = 123) as is_customer,
    EXISTS(SELECT 1 FROM employee WHERE employee_id = 456) as is_employee
\gset
\if :is_customer
    SELECT * FROM customer WHERE customer_id = 123;
\elif :is_employee
    \echo 'is not a customer but is an employee'
    SELECT * FROM employee WHERE employee_id = 456;
\else
    \if yes
        \echo 'not a customer or employee'
    \else
        \echo 'this will never print'
    \endif
\endif

\ir или \include_relative имя_файла

Команда \ir схожа с \i, по-другому интерпретирует относительные имена файлов. При выполнении в интерактивном режиме обе команды ведут себя одинаково. Однако при вызове из скрипта \ir интерпретирует имена файлов относительно каталога, в котором расположен скрипт, а не текущего рабочего каталога.

\l[+] или \list[+] [ шаблон ]

Выводит список баз данных на сервере и показывает их имена, владельцев, кодировку набора символов и права доступа. Если задан шаблон, выводятся только те базы данных, имена которых соответствуют этому шаблону. При добавлении + к имени команды также отображаются размеры баз данных, табличные пространства по умолчанию и описания. (Информация о размере доступна только для тех баз данных, к которым может подключиться текущий пользователь.)

\lo_export oid_БО имя_файла

Читает большой объект с OID oid_БО из базы данных и записывает его в файл имя_файла. Обратите внимание, что это несколько отличается от серверной функции lo_export, действующей с правами пользователя, от имени которого работает сервер базы данных, и в файловой системе сервера.

Совет
Используйте команду \lo_list для получения OID больших объектов.

\lo_import имя_файла [ комментарий ]

Сохраняет файл в большом объекте QHB. Кроме того, с объектом можно связать заданных комментарий. Пример:

foo=> \lo_import '/home/peter/pictures/photo.xcf' 'a picture of me'
lo_import 152801

Ответ указывает на то, что большой объект получил OID 152801, который можно использовать для доступа к вновь созданному большому объекту в будущем. Для удобочитаемости рекомендуется всегда связывать все объекты с понятными для человека комментариями. OID и комментарии можно посмотреть с помощью команды \lo_list.

Обратите внимание, что эта команда слегка отличается от серверной функции lo_import, поскольку действует от имени локального пользователя в локальной файловой системе, а не пользователя сервера в файловой системе сервера.

\lo_list

Показывает список всех больших объектов QHB, в настоящее время хранящихся в базе данных, вместе с предоставленными комментариями.

\lo_unlink oid_БО

Удаляет большой объект с OID oid_БО из базы данных.

Совет
Используйте команду \lo_list для получения OID больших объектов.

\o или \out [ имя_файла ]
\o или \out [ |команда ]

Организует сохранение результатов будущих запросов в файле имя_файла или передачу их через поток команде оболочки команда. Если аргументы не заданы, результат запроса перенаправляется на стандартный вывод.

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

«Результаты запросов» включают в себя все таблицы, ответы команд и уведомления, полученные от сервера баз данных, а также вывод от команд с обратным слэшем, обращающихся к базе данных (например, \d), но не сообщения об ошибках.

Совет
Чтобы вставить текстовый вывод между результатами запросов, используйте команду \qecho.

\p или \print

Вывести содержимое буфера текущего запроса в стандартный вывод. Если буфер текущего запроса пуст, вместо этого выводится последний выполненный запрос.

\password [ имя_пользователя ]

Изменяет пароль указанного пользователя (по умолчанию текущего пользователя). Эта команда запрашивает новый пароль, шифрует его и передает на сервер в виде команды ALTER ROLE. Это гарантирует, что новый пароль не отображается в открытом виде в истории команд, журнале сервера или где-то еще.

\prompt [ текст ] имя

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

По умолчанию \prompt использует терминал для ввода и вывода. Однако при указании ключа командной строки -f, \prompt использует стандартные ввод и вывод.

\pset [ параметр [ значение ] ]

Эта команда устанавливает параметры, влияющие на вывод таблиц результатов запросов. Аргумент параметр определяет, какой параметр нужно установить. Семантика значения меняется в зависимости от выбранного параметра. Для некоторых параметров отсутствие значения вызывает переключение или сброс параметра, как рассказывается ниже в описании конкретного параметра. Если такое поведение не упоминается, то отсутствие значения приводит всего лишь к отображению текущего значения параметра.

\pset без аргументов отображает текущий статус всех выведенных параметров.

Имеются следующие настраиваемые параметры:

border
значение должно быть номером. В целом, чем больше это число, тем больше границ и линий будет в таблицах, но детали зависят от конкретного формата. В формате HTML заданное значение будет напрямую переводиться в атрибут border=.... Для большинства других форматов имеют смысл только значения 0 (нет границы), 1 (внутренние разделительные линии) и 2 (рамка таблицы), а значения больше 2 будут восприниматься как border = 2. Форматы latex и latex-longtable дополнительно поддерживают значение 3, добавляющее разделительные линии между строками данных.

columns
Устанавливает целевую ширину для формата wrapped, а также ограничение по ширине вывода, свыше которого потребуется программа листания или переключение на вертикальное отображение в режиме expanded auto. При значении ноль (по умолчанию) целевая ширина управляется переменной среды COLUMNS или, если COLUMNS не установлена, определенной шириной экрана. Кроме того, если columns равно нулю, то формат wrapped влияет только на вывод на экран. Если columns не равно нулю, то это также влияет на вывод данных в файл или передачу через поток.

csv_fieldsep
Задает разделитель полей для формата вывода CSV. Если символ-разделитель оказывается в значении поля, это поле выводится в кавычках согласно стандартным правилам CSV. Разделитель по умолчанию — запятая.

expanded (или x)
При указании значения допускаются варианты on или off, которые включают или выключают развернутый режим, или auto. Если значение опущено, команда включает/выключает режим. Когда развернутый режим включен, результаты запроса выводятся в два столбца: в левом имя столбца, в правом данные. Этот режим полезен, если данные не помещаются на экране в обычном «горизонтальном» режиме. Со значением auto развернутый режим используется, когда вывод запроса содержит более одного столбца и по ширине не умещается на экране; в противном случае используется обычный режим. Вариант auto действует только в форматах aligned и wrapped. С другими форматами он всегда равнозначен выключению развернутого режима.

fieldsep
Задает разделитель полей для невыровненного режима вывода. Таким способом можно создать вывод со значениями, разделенными, к примеру, табуляцией, что может быть предпочтительным для других программ. Для установки символа табуляции в качестве разделителя полей нужно написать \pset fieldsep '\t'. Разделитель по умолчанию — '|' (вертикальная черта).

fieldsep_zero
Устанавливает разделитель полей для невыровненного режима вывода в нулевой байт.

footer
При указании значения допускаются варианты on или off, которые включают или выключают отображение итоговой части таблицы с количеством выбранных записей (n строк). Если значение опущено, команда включает/выключает отображение итоговой строки.

format
Устанавливает один из следующих форматов вывода: aligned, asciidoc, csv, html, latex, latex-longtable, troff-ms, unaligned или wrapped. Допускаются уникальные сокращения этих слов.

Формат aligned — это стандартный, удобочитаемый, хорошо отформатированный текстовый вывод; это значение по умолчанию.

В формате unaligned все столбцы выводятся в одной строке, отделенные друг от друга активным на данный момент разделителем полей. Это полезно для создания вывода, который может быть предназначен для чтения другими программами, например, для форматов с символом табуляции или запятой в качестве разделителя. Однако если разделитель полей оказывается в значении столбца, он обрабатывается как часть значения, поэтому для таких целей больше подходит формат CSV.

В формате csv значения столбцов выводятся через запятую и могут заключаться в кавычки по правилам, описанным в RFC 4180. Этот вывод совместим с форматом CSV серверной команды COPY. Создается строка заголовка с именами столбцов, если только не включен параметр tuples_only. Верхний и нижний колонтитулы не выводятся. Каждая строка заканчивается символом конца строки, зависящим от операционной системы; В Unix-подобных системах это обычно символ новой строки (\n). Разделители полей, отличные от запятой, можно выбрать командой \pset csv_fieldsep.

Формат wrapped похож на aligned, но переносит длинные значения на новые строки, чтобы вывод поместился в целевую ширину столбца. Задание целевой ширины описано в параметре columns. Обратите внимание, что psql не будет пытаться переносить на новые строки заголовки столбцов; таким образом, формат wrapped работает так же, как aligned, если общая ширина, требуемая для заголовков столбцов, превышает целевую.

Форматы asciidoc, html, latex, latex-longtable и troff-ms выводят таблицы, которые предназначены для включения в документы со помощью соответствующего языка разметки. Они не являются полноценными документами! Возможно, в этом нет необходимости в HTML, но для LaTeX обязателен документ- контейнер. Формат latex использует среду LaTeX tabular. Формату latex-longtable требуются пакеты LaTeX longtable и booktabs.

linestyle
Задает стиль отрисовки линий границы: ascii, old-ascii или unicode. Допускаются уникальные сокращения этих слов. (Это значит, что достаточно и одной буквы.) Стиль по умолчанию — ascii. Этот параметр действует только в форматах вывода aligned и wrapped.

Стиль ascii использует простые символы ASCII. Символы новой строки в данных показываются с использованием символа + с правого края. Когда при формате wrapped происходит перенос данных на новую строку без символа новой строки, с правого края первой строки и с левого края следующей строки ставится точка (.).

Стиль old-ascii использует простые символы ASCII в стиле форматирования более старых версий QHB. Символы новой строки в данных показываются с использованием символа : вместо левого разделителя столбцов. Когда происходит перенос данных на новую строку без символа новой строки, вместо левого разделителя столбцов используется символ ;.

Стиль unicode использует символы Unicode для рисования рамок. Символы новой строки в данных показываются с использованием символа возврата каретки с правого края. Когда происходит перенос данных на новую строку без символа новой строки, с правого края первой строки и с левого края следующей строки ставится многоточие.

Когда значение border больше нуля, параметр linestyle также определяет символы, которыми будут рисоваться линии границы. Простые символы ASCII работают везде, но символы Unicode лучше выглядят на распознающих их терминалах.

null
Задает строку, которая будет выводиться вместо значения NULL. По умолчанию не выводится ничего, что можно по ошибке принять за пустую строку. Например, можно было бы предпочесть \pset null '(null)'.

numericlocale
При указании значения допускаются варианты on или off, которые включают или выключают отображение специфичного для локали символа, отделяющие группы цифр слева от десятичного знака. Если значение опущено, команда переключает вывод чисел с обычного на локализованный и обратно.

pager
Управляет использованием программы листания для вывода результатов запросов и справки по psql. Если установлена переменная среды PSQL_PAGER или PAGER, вывод передается через поток указанной программе. В противном случае используется платформо-зависимая программа по умолчанию (например, more).
Когда параметр pager установлен в значение off, программа листания не используется. Когда pager имеет значение on, программа листания применяется при необходимости, т. е. когда вывод на терминал не помещается на экране. Кроме того, параметр pager может принимать значение always, при котором программа листания будет использоваться для всех выводов на терминал, независимо от того, помещается ли тот на экране. Команда \pset pager без указания значения включает/выключает программу листания.

pager_min_lines
Если в pager_min_lines задается число, превышающее высоту страницы, программа листания не будет вызываться, пока не наберется это число строк вывода для показа. Значение по умолчанию — 0.

recordsep
Задает разделитель записей (строк) для невыровненного режима вывода. Разделитель по умолчанию — символ новой строки.

recordsep_zero
Устанавливает разделитель записей для невыровненного режима вывода в нулевой байт.

tableattr (или T)
В формате HTML этот параметр задает атрибуты, которые будут помещены в тег table. Например, это может быть cellpadding или bgcolor. Обратите внимание, что, возможно, здесь необязательно задавать border, поскольку для этого уже есть \pset border. Если значение не задано, атрибуты таблицы удаляются.
В формате latex-longtable этот параметр управляет пропорциональной шириной каждого столбца, содержащего тип данных, выровненный по левому краю. Он задается как список значений, разделенных пробелами, например '0.2 0.2 0.6'. Для столбцов вывода, которым не хватает значений, используется последнее из заданных.

title (или C)
Устанавливает заголовок таблицы для любых выводимых впоследствии таблиц. Это можно использовать для задания описательных тегов при создании вывода. Если значение не задано, заголовок удаляется.

tuples_only (или t)
При указании значения допустимы варианты on или off, которые будут включать или выключать режим вывода только кортежей. Если значение опущено, команда переключает режим на противоположный. Обычный вывод включает в себя дополнительную информацию, например заголовки столбцов и различные колонтитулы. В режиме вывода только кортежей отображаются только фактические данные таблицы.

unicode_border_linestyle
Устанавливает стиль отрисовки границы для стиля линий unicode: single (одинарный) или double (двойной).

unicode_column_linestyle
Устанавливает стиль отрисовки столбцов для стиля линий unicode: single (одинарный) или double (двойной).

unicode_header_linestyle
Устанавливает стиль отрисовки заголовка для стиля линий unicode: single (одинарный) или double (двойной).

Иллюстрации того, как могут выглядеть различные форматы, можно увидеть ниже в разделе Примеры.

Совет
Для некоторых параметров \pset существуют короткие команды. См. \a, \C, \f, \H, \t, \T и \x.

\q или \quit

Завершает программу psql. При использовании в скрипте прекращается только выполнение этого скрипта.

\qecho текст [ ... ]

Эта команда идентична \echo за исключением того, что вывод будет записываться в канал вывода запросов, заданный \o.

\r или \reset

Сбрасывает (очищает) буфер запроса.

\s [ имя_файла ]

Записать историю командной строки psql в файл имя_файла. Если имя_файла опущено, история записывается в стандартный вывод (с использованием программы листания в случае необходимости). Эта команда недоступна, если psql была собрана без поддержки Readline.

\set [ имя [ значение [ ... ] ] ]

Задает для переменной psql имя указанное значение или, если указано более одного значения, конкатенацию всех этих значений. Если задано только один аргумент, значением переменной становится пустая строка. Для сброса переменной используйте команду \unset.

\set без аргументов отображает имена и значения всех установленных на данный момент переменных psql.

В именах переменных допустимы буквы, цифры и знаки подчеркивания. Подробнее см. в разделе Переменные ниже. Имена переменных чувствительны к регистру.

Некоторые переменные отличаются от остальные тем, что управляют поведением psql или устанавливаются автоматически, отражая состояние соединения. Эти переменные описаны ниже, в разделе Переменные.

Примечание
Эта команда не имеет отношения к команде SQL SET.

\setenv имя [ значение ]

Задает для переменной среды имя значение или, если значение не задано, сбрасывает переменную среды. Пример:

testdb=> \\setenv PAGER less
testdb=> \\setenv LESS -imx4F

\sf[+] описание_функции

Эта команда извлекает и показывает определение именованной функции или процедуры в форме команды CREATE OR REPLACE FUNCTION или CREATE OR REPLACE PROCEDURE. Определение записывается в текущий канал вывода запросов, заданный \o.

Целевую функцию можно задать только именем или именем и аргументами, например foo(integer, text). Типы аргументов необходимо задавать, если существует более одной функции с тем же именем.

При добавлении + к имени команды строки вывода нумеруются, при этом первая строка тела функции получит номер 1.

В отличие от большинства других метакоманд, весь остаток строки всегда воспринимается как аргумент(ы) \sf, и в этих аргументах не выполняется ни интерполяция переменных, ни раскрытие обратных кавычек.

\sv[+] имя_представления

Эта команда извлекает и показывает определение именованного представления в форме команды CREATE OR REPLACE VIEW command. Определение записывается в текущий канал вывода запросов, заданный \o.

При добавлении + к имени команды строки вывода нумеруются, начиная с 1.

В отличие от большинства других метакоманд, весь остаток строки всегда воспринимается как аргумент(ы) \sv, и в этих аргументах не выполняется ни интерполяция переменных, ни раскрытие обратных кавычек.

\t

Включает/выключает в выводе отображение заголовков столбцов с именами и итоговой строки с количеством записей. Эта команда равнозначна \pset tuples_only и предоставлена для удобства.

\T параметры_таблицы

Задает атрибуты, которые будут помещены в тег table при формате вывода HTML. Эта команда равнозначна \pset tableattr параметры_таблицы.

\timing [ on | off ]

С параметром (в зависимости от него) эта команда включает или выключает отображение времени выполнения каждого оператора SQL. Без параметра она переключает состояние отображения на противоположное. Время отображается в миллисекундах; интервалы больше 1 секунды показываются в формате минуты:секунды, а при необходимости добавляются поля часов и дней.

\unset имя

Сбрасывает (удаляет) переменную psql имя.

Большинство переменных, управляющих поведением psql, нельзя сбросить, поэтому команда \unset воспринимается для них как установка значений по умолчанию. См. раздел Переменные ниже.

\w или \write имя_файла
\w или \write |команда

Записывает буфер текущего запроса в файл имя_файла или передает через поток в команду оболочки команда. Если буфер текущего запроса пуст, вместо этого будет записан последний выполненный запрос.

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

\warn текст [ ... ]

Эта команда идентична \echo за исключением того, что вывод будет записываться в канал вывода ошибок psql, а не в канал стандартного вывода.

\watch [ секунды ]

Многократно выполнять текущий запрос в буфере (как делает \g does), пока команда не будет прервана или не возникнет ошибка в запросе. Между выполнениями запроса ждать заданное количество секунд (по умолчанию 2). Результат каждого запроса отображается с заголовком, включающим строку \pset title (если она задана), время запуска запроса и интервал задержки.

Если буфер текущего запроса пуст, вместо этого повторно выполняется последний переданный запрос.

\x [ on | off | auto ]

Задает или переключает режим развернутого форматирования таблицы. По сути равнозначна \pset expanded.

\z [ шаблон ]

Выводит список таблиц, представлений и последовательностей с их правами доступа. Если задан шаблон, выводятся только те таблицы, представления и последовательности, имена которых соответствуют этому шаблону.

Это псевдоним для \dp («отобразить права доступа»).

\! [ команда ]

Без аргументов запускает подчиненную оболочку; когда эта оболочка завершается, psql продолжает работу. С аргументом выполняет команду оболочки команда.

В отличие от большинства других метакоманд, весь остаток строки всегда воспринимается как аргумент(ы) \!, и в этих аргументах не выполняется ни интерполяция переменных, ни раскрытие обратных кавычек. Этот остаток строки просто передается оболочке в буквальном виде.

? [ тема ]

Показывает справочную информацию. Необязательный параметр тема (по умолчанию commands) выбирает, описание какой части psql будет показано: commands описывает команды psql с обратным слэшем; options описывает параметры командной строки, которые можно передать psql; а variables показывает справку по переменным конфигурации psql.

\;

Точка с запятой после обратного слэша не является метакомандой в том смысле, что предыдущие; при ее вводе в буфер запросов просто добавляется точка с запятой без дальнейшей обработки.

Обычно psql передает команду SQL серверу, как только доходит до завершающей команду точки с запятой, даже если в текущей строке еще остались данные на ввод. Таким образом, например, ввод

select 1; select 2; select 3;

приведет к тому, что на сервер будут по отдельности переданы три команды SQL, и результат каждой команды будет выведен перед переходом к следующей. Однако если точку с запятой ввести как \;, она не запустит обработку команды, так что команды перед и после этих символов в сущности объединяются и передаются серверу в одном запросе. Так, например, при вводе

select 1\; select 2\; select 3;

три команды SQL передаются серверу в одном запросе по достижении неэкранированной обратным слэшем точки с запятой. Сервер выполняет такую строку как одну транзакцию, если только туда не включены явные команды BEGIN/COMMIT, разделяющие ее на несколько транзакций. psql выводит результат только последнего запроса из всей строки; в этом примере, несмотря на то, что определенно выполнялись все три команды SELECT, psql выводит только 3.

Шаблоны

Различные команды \d принимают параметр шаблон для указания имени (имён) команд, которые будут отображены. В простейшем случае шаблон — это просто указание имени объекта. Обычно символы внутри шаблона переводятся в нижний регистр, как в именах SQL; например \dt FOO отобразит таблицу с именем foo. Как и в именах SQL, заключение шаблона в кавычки предотвращает перевод в нижний регистр. Если нужно включить в шаблон настоящий символ кавычки, он записывается в удвоенном виде внутри последовательности в кавычках; опять же, это соответствует правилам для идентификаторов SQL в кавычках. Например \dt "FOO""BAR" отобразит таблицу с именем FOO"BAR (не foo"bar). В отличие от обычных правил для имен SQL, можно взять в кавычки только часть шаблона, например \dt FOO"FOO"BAR отобразит таблицу с именем fooFOObar.

Если шаблон вообще опущен, команды \d отображают все объекты, видимые в текущем пути поиска схем — это равнозначно использованию * в качестве шаблона. (Объект считается видимым, если схема, в которой он содержится, находится в пути поиска, и объект того же типа и с тем же именем в пути поиска еще не встречался. Это равнозначно утверждению, что на объект можно ссылаться по имени без явного указания схемы.) Чтобы увидеть все объекты в базе данных, независимо от видимости, используйте в качестве шаблона *.*.

Внутри шаблона * приравнивается к любой последовательности символов (включая отсутствие символов), а ? приравнивается к любому одному символу. (Это обозначение соответствует шаблонам имен файлов в Unix.) Например, \dt int* отображает таблицы, имена которых начинаются с int. Но внутри кавычек * и ? теряют свое специальное значение и воспринимаются буквально.

Шаблон, содержащий точку (.) интерпретируется как шаблон имени схемы, за которым следует шаблон имени объекта. Например, \dt foo*.*bar* отображает все таблицы, имена которых включают bar, расположенные в схемах, имена которых начинаются с foo. Шаблону, не содержащему точку, соответствуют только объекты, видимые в пути поиска текущей схемы. Опять же, точка внутри кавычек теряет свое специальное значение и воспринимается буквально.

Продвинутые пользователи могут использовать записи регулярных выражений, таких как классы символов. Например [0-9] соответствует любой цифре. Все специальные символы регулярных выражений работают, как описано в подразделе Регулярные выражения POSIX, за исключением: ., который используется в качестве разделителя, как упоминалось выше, *, который переводится в запись регулярного выражения .*, ?, который переводится в ., и $, который воспринимается буквально. При необходимости эти шаблонные символы можно эмулировать, записав ? для эмуляции ., (R+|) для R* или (R|) для R?. Символ $ не требуется в качестве символа регулярного выражения, поскольку шаблон должен соответствовать имени целиком, в отличие от обычной интерпретации регулярных выражений (другими словами, $ автоматически добавляется в шаблон). Напишите * в начале и/или в конце, если не хотите, чтобы шаблон закрепился. Обратите внимание, что внутри кавычек все специальные символы регулярных выражений теряют свое специальное значение и воспринимаются буквально. Кроме того, специальные символы регулярных выражений воспринимаются буквально в шаблонах имен операторов (т. е. в аргументе команды \do).

Расширенные возможности

Переменные

psql предоставляет возможности подстановки переменных, схожие с имеющимися в обычных командных оболочках Unix. Переменные — это просто пары имя/значение, где значением может быть любая строка любой длины. Имя должно состоять из букв (включая нелатинские буквы), цифр и символов подчеркивания.

Чтобы установить переменную, используется метакоманда psql \set. Например,

testdb=> \set foo bar

задает для переменной foo значение bar. Чтобы извлечь содержимое переменной, нужно поставить перед ее именем двоеточие, например:

testdb=> \echo :foo
bar

Это работает как в обычных командах SQL, так и в метакомандах; более подробно это описывается в разделе Интерполяция SQL ниже.

При вызове \set без второго аргумента значением переменной становится пустая строка. Для сброса (т. е. удаления) переменной используйте команду \unset. Чтобы показать значения всех переменных, вызовите \set без аргументов.

Примечание
Аргументы \set подчиняются тем же правилам подстановки, что и другие команды. Таким образом, можно конструировать интересные ссылки, такие как \set :foo 'something', и получать «гибкие ссылки» или «переменные переменных» в Perl или PHP соответственно. К сожалению (или к счастью?), с этими конструкциями нельзя сделать ничего полезного. С другой стороны, \set bar :foo является прекрасным способом копирования переменной.

Ряд этих переменных обрабатывается в psql особым образом. Они представляют собой определенные параметры, которые можно менять во время выполнения путем изменения значения переменной, а в некоторых случаях отражают изменяемое состояние psql. По соглашению, имена всех специальных переменных состоят только из заглавных букв ASCII (и, возможно, цифр и символов подчеркивания). В целях максимальной совместимости в будущем не рекомендуется использовать такие имена для собственных переменных.

Переменные, управляющие поведением psql, как правило, нельзя сбросить или задать для них недопустимые значения. Команда \unset допустима, но интерпретируется как установка значения по умолчанию. Команда \set без второго аргумента интерпретируется как присвоение переменной значения on, для управляющих переменных, принимающих это значение, и не принимается для других. Кроме того, управляющие переменные, принимающие значения on и off, также примут и другие общепринятые написания логических значений, например true и false.

Специальные переменные:

AUTOCOMMIT
При значении on (по умолчанию), все команды SQL после успешного выполнения автоматически фиксируются. Чтобы отложить фиксацию в этом режиме, необходимо выполнить команду SQL BEGIN или START TRANSACTION. При значении off или сбросе переменной команды SQL не фиксируются, пока явно не выполнена команда COMMIT или END. В режиме выключенной автофиксации неявно выполняется BEGIN непосредственно перед любой командой, кроме: команд, уже находящихся в транзакционном блоке; самой команды BEGIN или другой команды управления транзакциями; команды, которая не может выполняться внутри транзакционного блока (например VACUUM).

Примечание
В режиме выключенной автофиксации требуется явно откатывать неуспешные транзакции, выполняя команду ABORT или ROLLBACK. Также имейте в виду, что при выходе из сеанса без фиксации изменений проделанная работа пропадет.

Примечание
Режим включенной автофиксации является традиционным поведением QHB, а режим выключенной автофиксации ближе к спецификации SQL. Если вы предпочитаете выключить режим автофиксации, это можно сделать в общесистемном файле psqlrc или в персональном файле ~/.psqlrc.

COMP_KEYWORD_CASE
Определяет, какой буквенный регистр будет использован при автоматическом завершении ключевых слов SQL. Если установлено значение lower или upper, то завершаемое слово будет в нижнем или верхнем регистре соответственно. Если установлено значение preserve-lower или preserve-upper (по умолчанию), то завершаемое слово будет в том же регистре, что и уже введенное начало слова, но последующие слова, завершаемые полностью, будут в нижнем или верхнем регистре соответственно.

DBNAME
Имя базы данных, к которой вы подключены в настоящий момент. Эта переменная устанавливается каждый раз при подключении к базе данных (в том числе при запуске программы), но ее можно изменить или сбросить.

ECHO
Если установлено значение all, все непустые входные строки выдаются в стандартный вывод по мере их прочтения. (Это не относится к строкам, считываемым интерактивно.) Чтобы выбрать это поведение при запуске программы, нужно добавить ключ -a. Если установлено значение queries, psql выдает в стандартный вывод каждый запрос, отправляемый серверу. За выбор этого поведения отвечает ключ -e. Если установлено значение errors, то в стандартном выводе ошибок отображаются только неудавшиеся запросы. За выбор этого поведения отвечает ключ -b. Со значением none (по умолчанию) никакие запросы не отображаются.

ECHO_HIDDEN
Когда для этой переменной установлено значение on и команда обратного слэша обращается к базе данных, сначала показывается текст этого запроса. Эта функциональная возможность помогает изучить внутреннее устройство QHB и реализовать похожую функциональность в своих программах. (Чтобы выбрать это поведение при запуске программы, используйте ключ -E.) Если установить для этой переменной значение noexec, запросы будут просто показываться, но не будут отправляться серверу и выполняться. Значение по умолчанию — off (выключено).

ENCODING
Текущая кодировка набора символов на стороне клиента. Эта переменная устанавливается всякий раз при подключении к базе данных (в том числе при запуске программы) и при смене кодировки с помощью команды \encoding, но ее можно изменить или сбросить.

ERROR
true в случае ошибки последнего запроса SQL, false в случае его успешного выполнения. См. также SQLSTATE.

FETCH_COUNT
Если для этой переменной установлено значение, представляющее собой целое число больше нуля, результаты запросов SELECT извлекаются из базы данных и отображаются группами с заданным количеством строк, в отличие от поведения по умолчанию, когда результирующий набор перед отображением собирается целиком. Таким образом, используется лишь ограниченный объем памяти, независимо от размера результирующего набора. При включении этой функциональности обычно устанавливаются значения от 100 до 1000. Имейте в виду, что при использовании этой возможности запрос может завершиться ошибкой уже после отображения нескольких строк.

Совет
Хотя с этой функциональностью можно использовать любой формат вывода, формат по умолчанию aligned, как правило, выглядит плохо, потому что каждая группа из FETCH_COUNT строк будет форматироваться отдельно, приводя к разной ширине столбцов в разных группах строк. Другие форматы вывода работают лучше.

HIDE_TABLEAM
Если для этой переменной установлено значение true, информация о методах доступа таблицы не отображается. Это полезно в основном для регрессионных тестов.

HIDE_TOAST_COMPRESSION
Если для этой переменной установлено значение true, информация о методах сжатия столбцов не отображается. Это полезно в основном для регрессионных тестов.

HISTCONTROL
Если для этой переменной установлено значение ignorespace, строки, начинающиеся с пробела, не добавляются в историю. Если установлено значение ignoredups, в историю не добавляются строки, которые в ней уже есть. Значение ignoreboth объединяет эти два варианта. Если установлено значение none (по умолчанию), все строки, считываемые в интерактивном режиме, сохраняются в списке истории.

Примечание
Эта функциональная возможность была бессовестно списана с Bash.

HISTFILE
Имя файла, который будет использоваться для сохранения истории. Если эта переменная не установлена, имя файла берется из переменной среды PSQL_HISTORY. Если и эта переменная не установлена, имя по умолчанию — ~/.psql_history. Например, если записать:

\set HISTFILE ~/.psql_history-:DBNAME

в ~/.psqlrc, psql будет вести отдельный файл истории для каждой базы данных.

Примечание
Эта функциональная возможность была бессовестно списана с Bash.

HISTSIZE
Максимальное число команд, которые будут сохраняться в истории команд (по умолчанию 500). Если установлено отрицательное значение, ограничение не накладывается.

Примечание
Эта функциональная возможность была бессовестно списана с Bash.

HOST
Имя хоста, где работает сервер базы данных, к которому вы подключены в настоящий момент. Эта переменная устанавливается всякий раз при подключении к базе данных (включая запуск программы), но ее можно изменить или сбросить.

IGNOREEOF
Если установлено значение 1 или меньше, передача символа конца файла (обычно вводится сочетанием клавиш Ctrl+D) интерактивному сеансу psql завершит работу приложения. Если установлено большее числовое значение, оно определяет, сколько последовательных символов конца файла нужно ввести, чтобы завершить интерактивный сеанс. Если для переменной установлено значение, отличное от числового, оно воспринимается как 10. Значение по умолчанию — 0.

Примечание
Эта функциональная возможность была бессовестно списана с Bash.

LASTOID
Значение последнего обработанного OID, возвращенного командой INSERT или \lo_import. Эта переменная будет гарантированно иметь корректное значение лишь до тех пор, пока не будет отображен результат следующей команды SQL.

LAST_ERROR_MESSAGE
LAST_ERROR_SQLSTATE
Основное сообщение об ошибке и связанный код SQLSTATE для последнего неудавшегося запроса в текущем сеансе psql или пустая строка и 00000, если в текущем сеансе не происходили ошибки.

ON_ERROR_ROLLBACK
Когда установлено значение on, если оператор в блоке транзакции выдает ошибку, эта ошибка игнорируется и транзакция продолжается. Когда установлено значение interactive, такие ошибки игнорируются только в интерактивных сеансах, но не при чтении файлов скриптов. Когда установлено значение off (по умолчанию), оператор в блоке транзакции, выдающий ошибку, прерывает всю транзакцию. В режиме отката транзакции при ошибке неявно выполняется команда SAVEPOINT непосредственно перед каждой командой в блоке транзакции, и в случае неудачного завершения команды происходит откат к этой точке сохранения.

ON_ERROR_STOP
По умолчанию после возникновения ошибки обработка команд продолжается. Когда для этой переменной установлено значение on, обработка команд будет немедленно прекращена. В интерактивном режиме psql вернется в командную строку; иначе psql завершит работу, вернув код ошибки 3, чтобы отличить этот случай от состояний фатальной ошибки, о которых сообщают с помощью кода ошибки 1. В любом случае выполнение всех запущенных в настоящий момент скриптов (высокоуровневого скрипта и любых других, которые он мог запустить) будет немедленно прекращено. Если высокоуровневая командная строка содержит несколько команд SQL, выполнение остановится на текущей команде.

PORT
Порт сервера базы данных, к которому вы подключены в настоящий момент. Эта переменная устанавливается всякий раз при подключении к базе данных (включая запуск программы), но ее можно изменить или сбросить.

PROMPT1
PROMPT2
PROMPT3
Эти переменные указывают, как должны выглядеть приглашения psql. См. раздел Настройка приглашений ниже.

QUIET
Установка для этой переменной значения on равнозначна параметру командной строки -q. Вероятно, она не слишком полезна в интерактивном режиме.

ROW_COUNT
Число строк, возвращенных или обработанных последним запросом SQL, или 0, если запрос завершился неудачно или не выдал количество строк.

SERVER_VERSION_NAME
SERVER_VERSION_NUM
Номер версии сервера в виде строки, например 9.6.2, 10.1 или 11beta1, и в числовом виде, например 90602 или 100001. Эта переменная устанавливается всякий раз при подключении к базе данных (включая запуск программы), но ее можно изменить или сбросить.

SHOW_CONTEXT
Для этой переменной можно установить значения never (никогда), errors (ошибки) или always (всегда), управляющие тем, будут ли в сообщениях с сервера отображаться поля CONTEXT. По умолчанию установлено значение errors (которое означает, что контекст будет выводиться в сообщениях об ошибках, но не в уведомлениях или предупреждениях). Этот параметр не действует, когда установлен уровень VERBOSITY terse или sqlstate. (Также, если вам нужна подробная версия только что полученной ошибки, обратитесь к команде \errverbose.)

SINGLELINE
Установка для этой переменной значения on равнозначна параметру командной строки -S.

SINGLESTEP
Установка для этой переменной значения on равнозначна параметру командной строки -s.

SQLSTATE
Код ошибки (см. раздел Приложения Коды ошибок QHB), связанной с неудачным выполнением последнего запроса SQL, или 00000, если тот завершился успешно.

USER
Пользователь базы данных, который подключен в настоящий момент. Эта переменная устанавливается всякий раз при подключении к базе данных (включая запуск программы), но ее можно изменить или сбросить.

VERBOSITY
Для этой переменной можно установить значения default, verbose, terse или sqlstate для управления уровнем детализации в сообщениях об ошибках. (Также, если вам нужна подробная версия только что полученной ошибки, обратитесь к команде \errverbose.)

VERSION
VERSION_NAME
VERSION_NUM
Эти переменные устанавливаются при запуске программы и отражают версию psql соответственно в виде развернутой строки, краткой строки (например, 9.6.2, 10.1 или 11beta1) и числа (например, 90602 или 100001). Их можно изменить или сбросить.

Интерполяция SQL

Ключевой особенностью переменных psql является возможность подставлять («интерполировать») их в обычные операторы SQL, а также в аргументы метакоманд. Помимо этого, psql предоставляет средства для обеспечения корректного применения кавычек для значений переменных, используемых как литералы и идентификаторы SQL. Чтобы интерполировать значение без кавычек, необходимо добавить перед именем переменной двоеточие (:). Например,

testdb=> \set foo 'my_table'
testdb=> SELECT * FROM :foo;

будет запрашивать таблицу my_table. Обратите внимание, что это может быть небезопасно: значение переменной копируется буквально, поэтому может содержать непарные кавычки или даже команды обратного слэша. При его применении следует убедиться, что это имеет смысл.

Когда значение предназначено к использованию в качестве литерала или идентификатора SQL, безопаснее всего заключить его в кавычки. Для заключения в кавычки значения переменной, используемого как литерал SQL, необходимо после двоеточия написать имя переменной в апострофах. Для заключения в кавычки значения переменной, используемого как идентификатор SQL, необходимо после двоеточия написать имя переменной в кавычках. Эти конструкции корректно работают с кавычками и другими специальными символами, содержащимися в значении переменной. Предыдущий пример безопаснее было бы записать так:

testdb=> \set foo 'my_table'
testdb=> SELECT * FROM :"foo";

Интерполяция переменных не будет выполняться в литералах и идентификаторах SQL, заключенных в кавычки. Поэтому такая конструкция, как ':foo', не приведет к превращению значения переменной во взятый в кавычки литерал (и если бы это работало, это было бы небезопасно, поскольку не получилось бы корректно обработать кавычки внутри значения переменной).

Один из примеров использования этого механизма — копирование содержимого файла в столбец таблицы. Сперва загрузим файл в переменную, а затем интерполируем значение переменной как строку в кавычках:

testdb=> \set content `cat my_file.txt`
testdb=> INSERT INTO my_table VALUES (:'content');

(Обратите внимание, что это пока не будет работать, если my_file.txt содержит байты NUL. psql не поддерживает байты NUL в значениях переменных.)

Поскольку двоеточие вполне законно может присутствовать в командах SQL, явная попытка интерполяции (например для :name, :'name' или :"name") не выполняется, если именованная переменная в настоящий момент не установлена. В любом случае можно экранировать двоеточие с помощью обратного слэша, чтобы защитить его от подстановки.

Специальный синтаксис :{?имя} возвращает TRUE или FALSE в зависимости от того, существует ли такая переменная, и поэтому всегда подменяется значением, если только двоеточие не экранировано обратным слэшем.

Использование двоеточия для переменных является стандартом SQL для встраиваемых языков запросов, таких как ECPG. Использование двоеточия для срезов массивов и приведения типов является расширениями QHB, которые иногда могут конфликтовать со стандартным использованием. Использование двоеточия и кавычек для экранирования значения переменной при подстановке в качестве литерала или идентификатора SQL является расширением psql.

Настройка приглашений

Приглашения, выдаваемые psql, можно настроить по своему вкусу. Три переменные PROMPT1, PROMPT2 и PROMPT3 содержат строки и специальные управляющие последовательности, описывающие внешний вид приглашения. Приглашение 1 (PROMPT1) — это обычное приглашение, которое выдается, когда psql запрашивает ввод новой команды. Приглашение 2 (PROMPT2) выдается, когда при вводе команды ожидаются дополнительные данные, например потому что команда не была завершена точкой с запятой или не закрыты кавычки. Приглашение 3 (PROMPT3) выдается при выполнении команды SQL COPY FROM STDIN, когда в терминале нужно ввести значение новой строки.

Значение выбранных переменных приглашений выводятся буквально, за исключением случаев, когда в них попадается знак процента (%). В зависимости от следующего символа будет подставляться определенный текст. Описаны следующие подстановки:

%M
Полное имя хоста (с именем домена) сервера базы данных, или [local], если подключение выполнено через Unix-сокет, или [local:/каталог/имя], если при компиляции было изменено расположение Unix-сокета по умолчанию.

%m
Имя хоста сервера базы данных, усеченное до первой точки, или [local], если подключение выполнено через Unix-сокет.

%>
Номер порта, который прослушивает сервер базы данных.

%n
Имя пользователя базы данных в текущем сеансе. (Это значение может меняться в течение сеанса в результате выполнения команды SET SESSION AUTHORIZATION.)

%/
Имя текущей базы данных.

%~
Похоже на %/, но выводит ~ (тильду), если текущая база данных является базой данных по умолчанию.

%#
Если пользователь сеанса является суперпользователем базы данных, то выводит #, иначе >. (Это значение может меняться в течение сеанса в результате выполнения команды SET SESSION AUTHORIZATION.)

%p
PID обслуживающего процесса для текущего подключения.

%R
В приглашении 1 это обычно =, но будет @, если сеанс находится в неактивной ветви блока условия, либо ^ в однострочном режиме, либо !, если сеанс не подключен к базе данных (что может произойти, если \connect завершилась неудачно). В приглашении 2 %R заменяется символом, который зависит от того, почему psql ожидает ввод дополнительных строк: -, если команда просто еще не была завершена, но *, если есть незавершенный комментарий /* ... */; апостроф, если не завершена строка в апострофах; кавычки, если не завершен идентификатор в кавычках; знак доллара, если не завершена стока в долларах, или (, если не закрыты скобки. В приглашении 3 %R не выдает ничего.

%x
Состояние транзакции: пустая строка, когда не в блоке транзакции; *, когда в блоке транзакции; !, когда в блоке транзакции, в котором произошла ошибка; ?, когда состояние транзакции не определено (например, потому что отсутствует подключение).

%l
Номер строки в текущем операторе, начиная с 1.

%цифры
Подставляется символ с указанным восьмеричным кодом.

%:имя:
Значение переменной psql имя. Подробную информацию см. в разделе Переменные.

%`команда`
Вывод команды; схоже с обычной подстановкой с обратными апострофами.

%[ ... %]
Приглашения могут содержать символы управления терминалом, которые, например, меняют цвет, фон и стиль текста приглашения или изменяют заголовок окна терминала. Для того чтобы возможности редактирования строк Readline работали как следует, эти невыводимые управляющие символы нужно сделать невидимыми, расположив их между %[ и %]. В приглашении может находиться несколько таких включений. Например:

testdb=> \set PROMPT1 '%[%033[1;33;40m%]%n@%/%R%[%033[0m%]%# '

сделает жирное (1;), желтое на черном фоне (33;40) приглашение для VT100-совместимых цветных терминалов.

%w
Пробелы той же ширины, что и в последнем выведенном приглашении PROMPT1. Это можно использовать при установке PROMPT2, чтобы многострочные операторы были выровнены по первой строке, но при этом вторичного приглашения не было видно.

Чтобы вставить знак процента, напишите %%. По умолчанию для приглашений 1 и 2 используется значение '%/%R%x%# ', а для приглашения 3 — '>> '.

Примечание
Эта функциональная возможность была бессовестно списана с tcsh.

Редактирование командной строки

psql поддерживает библиотеку Readline для удобного редактирования и извлечения командной строки. История команд автоматически сохраняется при выходе из psql и загружается при ее запуске. Кроме того, поддерживается завершение клавишей Tab, хотя логика завершения не претендует на роль анализатора SQL. Запросы, генерируемые завершением по Tab, также могут создавать помехи для других команд SQL, например SET TRANSACTION ISOLATION LEVEL. Если по какой-то причине вам не нравится завершение по Tab, его можно выключить, записав в файле .inputrc в вашем домашнем каталоге следующее:

$if psql
set disable-completion on
$endif

(Это функциональная возможность не psql, а Readline. Дополнительную информацию см. в документации к Readline.)

Переменные среды

COLUMNS
Если \pset columns равно нулю, управляет шириной формата вывода wrapped, а также шириной, определяющей, требуется ли использовать в широком выводе программу листания или нужно переключиться в вертикальный формат, в режиме expanded auto.

PGDATABASE
PGHOST
PGPORT
PGUSER
Параметры подключения по умолчанию..

PG_COLOR
Указывает, использовать ли цвет в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

PSQL_EDITOR
EDITOR
VISUAL
Редактор, используемый командами \e, \ef и \ev. Эти переменные проверяются в вышеуказанном порядке; применяется первая установленная переменная. Если ни одна из них не установлена, по умолчанию в системах Unix используется vi.

PSQL_EDITOR_LINENUMBER_ARG
Когда команда \e, \ef или \ev используется с аргументом номера строки, эта переменная задает аргумент командной строки, применяемый для передачи начального номера строки в редактор пользователя. Для таких редакторов, как Emacs или vi, это знак плюс. Добавьте в конец значения переменной пробел, если он требуется для разделения имени параметра и номера строки. Примеры:

PSQL_EDITOR_LINENUMBER_ARG='+'
PSQL_EDITOR_LINENUMBER_ARG='--line '

Значением по умолчанию является + в системах Unix (соответствует редактору по умолчанию vi и полезно для многих других распространенных редакторов).

PSQL_HISTORY
Альтернативное расположение файла истории команд. Выполняется добавление тильды (~).

PSQL_PAGER
PAGER
Если результаты запросов не помещаются на экране, они пропускаются через эту команду. Обычные значения это more или less. Использование программы листания можно выключить, присвоив переменной PSQL_PAGER или PAGER пустую строку или изменив связанные с этой программой параметры в команде \pset. Эти переменные проверяются в вышеуказанном порядке применяется первая установленная переменная. Если ни одна из них не установлена, на большинстве платформ по умолчанию используется more.

PSQLRC
Альтернативное расположение пользовательского файла .psqlrc. Выполняется добавление тильды (~).

SHELL
Команда оболочки, выполняемая командой \!.

TMPDIR
Каталог для хранения временных файлов. По умолчанию /tmp.

Файлы

psqlrc и ~/.psqlrc

При запуске без параметра -X psql пытается считать и выполнить команды из общесистемного файла запуска (psqlrc), а затем из пользовательского файла запуска (~/.psqlrc) после подключения к базе данных, но до получения обычных команд. Эти файлы можно использовать для настройки клиента и/или сервера по своему вкусу, обычно с помощью команд \set и SET.

Общесистемный файл запуска называется psqlrc и ищется в каталоге установки «конфигурация системы», который надежнее всего определяется с помощью команды pg_config --sysconfdir. По умолчанию этот каталог будет расположен в ../etc/ относительно каталога, содержащего исполняемые файлы QHB. Имя этого каталога можно задать явно через переменную среды PGSYSCONFDIR.

Персональный файл запуска называется .psqlrc и ищется в домашнем каталоге вызывающего пользователя. Расположение пользовательского файла запуска можно задать явно через переменную среды PSQLRC.

Как общесистемный, так и персональный файлы запуска можно привязать к конкретной версии psql, добавив к имени файла старший или младший номер релиза QHB, например ~/.psqlrc-1.2 или ~/.psqlrc-1.5.1. При наличии нескольких файлов будет прочитан файл с наиболее детальным номером версии.

.psql_history

История командной строки хранится в файле ~/.psql_history.

Расположение файла истории можно задать явно через переменную psql HISTFILE или через переменную среды PSQL/_HISTORY.

Примечания

psql лучше всего работает с серверами той же или более старой основной версии. Сбой команд обратного слэша наиболее вероятен, если версия сервера новее версии самой psql. Общая функциональность выполнения команд SQL и отображение результатов запросов должны работать на серверах с более новой основной версией, но это не гарантируется во всех случаях.

Если вы хотите использовать psql для подключения к нескольким серверам с различными основными версиями, рекомендуется использовать последнюю версию psql. Как вариант, можно держать под рукой копии psql от каждой основной версии и использовать ту, которая соответствует версии сервера. Но на практике в этих дополнительных сложностях нет необходимости.

Примеры

Первый пример показывает, как распределить команду на несколько строк ввода. Обратите внимание на изменение приглашения:

testdb=> CREATE TABLE my_table (
testdb(>  first integer not null default 0,
testdb(>  second text)
testdb-> ;
CREATE TABLE

Теперь снова посмотрим на определение таблицы:

testdb=> \d my_table
              Table "public.my_table"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 first  | integer |           | not null | 0
 second | text    |           |          |

Теперь изменим приглашение на что-нибудь более интересное:

testdb=> \set PROMPT1 '%n@%m %~%R%# '
peter@localhost testdb=>

Предположим, что вы внесли данные в таблицу и хотите на них посмотреть:

peter@localhost testdb=> SELECT * FROM my_table;
 first | second
-------+--------
     1 | один
     2 | два
     3 | три
     4 | четыре
(4 rows)

Таблицу можно вывести разными способами с помощью команды \pset:

peter@localhost testdb=> \pset border 2
Установлен стиль границ 2.
peter@localhost testdb=> SELECT * FROM my_table;
+-------+--------+
| first | second |
+-------+--------+
|     1 | один   |
|     2 | два    |
|     3 | три    |
|     4 | четыре |
+-------+--------+
(4 rows)

peter@localhost testdb=> \pset border 0
Установлен стиль границ 0.
peter@localhost testdb=> SELECT * FROM my_table;
first second
----- ------
    1 один
    2 два
    3 три
    4 четыре
(4 rows)

peter@localhost testdb=> \pset border 1
Установлен стиль границ 1.
peter@localhost testdb=> \pset format csv
Формат вывода: csv.
peter@localhost testdb=> \pset tuples_only
Режим вывода только кортежей включен.
peter@localhost testdb=> SELECT second, first FROM my_table;
один,1
два,2
три,3
четыре,4
peter@localhost testdb=> \pset format unaligned
Формат вывода: unaligned.
peter@localhost testdb=> \pset fieldsep '\t'
Разделитель полей: "    ".
peter@localhost testdb=> SELECT second, first FROM my_table;
один      1
два       2
три       3
четыре    4

Либо можно использовать короткие команды:

peter@localhost testdb=> \a \t \x
Формат вывода: aligned.
Режим вывода только кортежей выключен.
Расширенный вывод включен.
peter@localhost testdb=> SELECT * FROM my_table;
-[ RECORD 1 ]-
first  | 1
second | один
-[ RECORD 2 ]-
first  | 2
second | два
-[ RECORD 3 ]-
first  | 3
second | три
-[ RECORD 4 ]-
first  | 4
second | четыре

Кроме того, эти параметры формата вывода можно задать только для одного запроса, используя команду \g:

peter@localhost testdb=> SELECT * FROM my_table
peter@localhost testdb-> \g (format=aligned tuples_only=off expanded=on)
-[ RECORD 1 ]-
first  | 1
second | один
-[ RECORD 2 ]-
first  | 2
second | два
-[ RECORD 3 ]-
first  | 3
second | три
-[ RECORD 4 ]-
first  | 4
second | четыре

Вот пример использования команды \df для нахождения только тех функций, имя которых соответствует шаблону int*pl, а второй аргумент имеет тип bigint:

testdb=> \df int*pl * bigint
                          List of functions
   Schema   |  Name   | Result data type | Argument data types | Type
------------+---------+------------------+---------------------+------
 pg_catalog | int28pl | bigint           | smallint, bigint    | func
 pg_catalog | int48pl | bigint           | integer, bigint     | func
 pg_catalog | int8pl  | bigint           | bigint, bigint      | func
(3 rows)

Когда это уместно, результаты запросов можно показать в виде перекрестной таблицы с помощью команды \crosstabview:

testdb=> SELECT first, second, first > 2 AS gt2 FROM my_table;
 first | second | gt2
-------+--------+-----
     1 | один   | f
     2 | два    | f
     3 | три    | t
     4 | четыре | t
(4 rows)

testdb=> \crosstabview first second
 first | один | два | три | четыре
-------+------+-----+-----+--------
     1 | f    |     |     |
     2 |      | f   |     |
     3 |      |     | t   |
     4 |      |     |     | t
(4 rows)

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

testdb=> SELECT t1.first as "A", t2.first+100 AS "B", t1.first*(t2.first+100) as "AxB",
testdb(> row_number() over(order by t2.first) AS ord
testdb(> FROM my_table t1 CROSS JOIN my_table t2 ORDER BY 1 DESC
testdb(> \crosstabview "A" "B" "AxB" ord
 A | 101 | 102 | 103 | 104
---+-----+-----+-----+-----
 4 | 404 | 408 | 412 | 416
 3 | 303 | 306 | 309 | 312
 2 | 202 | 204 | 206 | 208
 1 | 101 | 102 | 103 | 104
(4 rows)

qhb_amcheck

qhb_amcheck — проверить одну или несколько баз данных QHB на предмет повреждения

Синтаксис

qhb_amcheck [параметр...] [имя_бд]

Описание

Утилита qhb_amcheck поддерживает запуск реализованных в расширении amcheck функций проверки на повреждения в одной или нескольких базах данных, с возможностью выбора проверяемых схем, таблиц и индексов, выбора видов проверки, а также возможностью запустить эти проверки в параллельном режиме и задать для него число параллельных подключений.

В настоящее время поддерживаются только табличные отношения и индексы B-деревья. Другие типы отношений молча пропускаются.

Если задается имя_бд, это должно быть имя одной проверяемой базы данных, и никакие другие параметры выбора баз данных задаваться не должны. В ином случае, если присутствуют какие-либо параметры выбора баз данных, будут проверяться все соответствующие базы. В отсутствие таких параметров будет проверяться база данных по умолчанию. Параметрами выбора баз данных являются --all, --database и --exclude-database. Также сюда можно включить --relation, --exclude- relation, --table, --exclude-table, --index и --exclude-index, но только когда эти параметры используются с трехкомпонентным шаблоном (например, mydb*.myschema*.myrel*). И наконец, к ним можно отнести --schema и --exclude-schema, когда эти параметры используются с двухкомпонентным шаблоном (например, mydb*.myschema*).

Параметры

Следующие параметры командной строки определяют, что будет проверяться:

-a
--all
Проверить все базы данных, кроме исключенных параметром --exclude-database.

-d шаблон
--database=шаблон
Проверить базы данных, соответствующие заданному шаблону, кроме исключенных параметром --exclude-database. Этот параметр можно указать неоднократно.

-D шаблон
--exclude-database=шаблон
Исключить базы данных, соответствующие заданному шаблону. Этот параметр можно указать неоднократно.

-i шаблон
--index=шаблон
Проверить индексы, соответствующие заданному шаблону, если они не исключены каким-либо образом. Этот параметр можно указать неоднократно.

Этот параметр схож с параметром --relation, за исключением того, что он применяется только к индексам, а не таблицам.

-I шаблон
--exclude-index=шаблон
Исключить индексы, соответствующие заданному шаблону. Этот параметр можно указать неоднократно.

Этот параметр схож с параметром --exclude-relation, за исключением того, что он применяется только к индексам, а не таблицам.

-r шаблон
--relation=шаблон
Проверить отношения, соответствующие заданному шаблону, если они не исключены каким-либо образом. Этот параметр можно указать неоднократно.

Шаблоны могут быть неполными, например myrel*, либо могут быть дополнены схемой, например myschema*.myrel*, либо могут быть дополнены базой данных и схемой, например mydb*.myschema*.myrel*. Если шаблон указан с базой данных, все соответствующие базы будут добавлены в список баз данных, подлежащих проверке.

-R шаблон
--exclude-relation=шаблон
Исключить отношения, соответствующие заданному шаблону. Этот параметр можно указать неоднократно.

Как и с --relation, шаблон может быть неполным, дополненным схемой или базой данных и схемой.

-s шаблон
--schema=шаблон
Проверить таблицы и индексы в схемах, соответствующие заданному шаблону, если они не исключены каким-либо образом. Этот параметр можно указать неоднократно.

Чтобы выбрать таблицы только в схемах, соответствующих определенному шаблону, можно написать что-то вроде --table=ШАБЛОН_СХЕМЫ.* --no-dependent-indexes. Чтобы выбрать только индексы, можно использовать синтаксис --index=ШАБЛОН_СХЕМЫ.*.

Шаблон схемы можно дополнить указанием базы данных. Например, можно написать --schema=mydb*.myschema*, чтобы выбрать схемы, соответствующие myschema*, в базах данных, соответствующих mydb*.

-S шаблон
--exclude-schema=шаблон
Исключить таблицы и индексы, соответствующие заданному шаблону. Этот параметр можно указать неоднократно.

Как и с --schema, шаблон можно дополнить указанием базы данных.

-t шаблон
--table=шаблон
Проверить таблицы, соответствующие заданному шаблону, если они не исключены каким-либо образом. Этот параметр можно указать неоднократно.

Этот параметр схож с параметром --relation, за исключением того, что он применяется только к таблицам, а не к индексам.

-T шаблон
--exclude-table=шаблон
Исключить таблицы, соответствующие заданному шаблону. Этот параметр можно указать неоднократно.

Этот параметр схож с параметром --exclude-relation, за исключением того, что он применяется только к таблицам, а не к индексам.

--no-dependent-indexes
По умолчанию, если проверяется таблица, также будут проверяться все индексы B-деревья, даже если они не были явно выбраны параметрами вроде --index или --relation. Данный параметр отключает это поведение.

--no-dependent-toast
По умолчанию, если проверяется таблица, также будет проверяться ее таблица TOAST (если таковая имеется), даже она не была явно выбрана параметрами вроде --table или --relation. Данный параметр отключает это поведение.

--no-strict-names
По умолчанию, если аргументу --database, --table, --index или --relation не соответствует ни один объект, это является критической ошибкой. Данный параметр снижает уровень ошибки до предупреждения. .

Следующие параметры командной строки управляют проверками таблиц:

--exclude-toast-pointers
По умолчанию, когда в таблице встречается указатель на TOAST, производится поиск для проверки того, что указатель ссылается на однозначно корректные записи в таблице TOAST. Эти проверки могут быть довольно медленными, и данный параметр позволяет пропустить их.

--on-error-stop
Прекратить обработку табличного отношения после сообщения обо всех повреждениях на первой странице, где они были обнаружены, и перейти к следующей таблице или индексу.

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

--skip=параметр
Если задано all-frozen, проверки повреждений таблиц будут пропускать страницы, помеченные как полностью замороженные, во всех таблицах.

Если задано all-visible, проверки повреждений таблиц будут пропускать страницы, помеченные как полностью видимые, во всех таблицах.

По умолчанию никакие страницы не пропускаются. Это поведение задается значением none, но поскольку это значение по умолчанию, указывать его необязательно.

--startblock=блок
Начать проверку с блока с указанным номером. Если в проверяемом табличном отношении находится меньше этого числа блоков, будет выдана ошибка. Этот параметр не применим к индексам и, вероятно, полезен только при проверке одного табличного отношения. Дополнительные пояснения см. в описании --endblock.

--endblock=блок
Закончить проверку на блоке с указанным блоком. Если в проверяемом табличном отношении находится меньше этого числа блоков, будет выдана ошибка. Этот параметр не применим к индексам и, вероятно, полезен только при проверке одного табличного отношения. Если проверяется и обычная таблица, и таблица TOAST, этот параметр подействует на обе, но при проверке указателей TOAST все равно возможны обращения к блокам за указанным пределом, если только эта проверка не выключена параметром --exclude-toast-pointers. .

Следующие параметры командной строки управляют проверками индексов B-деревьев:

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

--parent-check
Для каждого проверяемого индекса B-дерева выполнять функцию amcheck bt_index_parent_check, которая при проверке индекса проводит дополнительные проверки отношений родитель/потомок.

По умолчанию выполняется функция amcheck bt_index_check, но обратите внимание, что при использовании параметра --rootdescend неявно выбирается функция bt_index_parent_check.

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

При использовании этого параметра также неявно выбирается параметр --parent-check.

Эта форма проверки изначально была написана в помощь разработке функциональности индексов B-деревьев. Она может иметь ограниченное применение или вовсе быть бесполезной для обнаружения тех видов повреждений, которые встречаются на практике. Также она может значительно замедлить проверку на повреждения и потребовать гораздо больше серверных ресурсов.

ПРЕДУПРЕЖДЕНИЕ!
Дополнительные проверки, выполняемые для индексов B-деревьев, когда указан параметр --parent-check или --rootdescend, требуют относительно сильных блокировок на уровне отношений. Только эти проверки блокируют параллельное изменение данных командами INSERT, UPDATE и DELETE.

Следующие параметры командной строки управляют подключением к серверу:

-h имя_хоста
--host=имя_хоста
Задает имя компьютера, на котором работает сервер. Если значение начинается со слэша, оно определяет каталог Unix-сокета.

-p порт
--port=порт
Указывает порт TCP или расширение файла локального Unix-сокета, на котором сервер принимает подключения.

-U имя_пользователя
--username=имя_пользователя
Имя пользователя для подключения.

-w
--no-password
Никогда не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, и пароль недоступен с помощью иных средств, таких как файл .pgpass, попытка подключения завершится неудачно. Этот параметр может быть полезен в пакетных заданиях и скриптах, где нет пользователя, чтобы ввести пароль.

-W
--password
Принудительно запрашивать пароль перед подключением к базе данных, даже если он не будет использоваться.

Это несущественный параметр, поскольку qhb_amcheck автоматически запросит пароль, если сервер требует аутентификацию по паролю. Однако чтобы понять, что требуется пароль, qhb_amcheck потребуется дополнительная попытка подключения к серверу. В некоторых случаях имеет смысл ввести -W, чтобы исключить эту лишнюю попытку.

--maintenance-db=имя_бд
Задает базу данных или строку подключения для установки подключения, через которое будет получен список баз данных для проверки. Если не установлен ни --all, ни какой-либо из параметров, задающих шаблон баз данных, такое подключение не требуется и этот параметр ничего не делает. Иначе все параметры из строки подключения, кроме имени базы данных, включенные в значение этого параметра, также будут использованы при подключении к проверяемым базам данных. Если этот параметр опущен, по умолчанию выполняется подключение к базе qhb, а если к ней подключиться не удается — к базе template1. .

Также доступны другие параметры:

-e
--echo
Выводить в stdout все запросы SQL, передаваемые серверу.

-j число
--jobs=число
Использовать заданное число одновременных подключений к серверу или, если проверяемых объектов меньше, чем заданных подключений, по одному на объект.

По умолчанию используется одно подключение.

-P
--progress
Показывать информацию о прогрессе операции. Информация о прогрессе включает число отношений, для которых уже была проведена проверка, и общий размер этих отношений. В нее также включается общее число отношений, которые в итоге будут проверены, и примерный размер этих отношений.

-v
--verbose
Выводить больше сообщений. В частности, будут выводиться сообщения о каждом проверенном отношении, а также повысится уровень детализации серверных ошибок.

--version
Вывести версию qhb_amcheck и завершиться.

--install-missing
--install-missing=схема
Установить все отсутствующие расширения, требуемые для проверки баз(ы) данных. Если это расширение еще не было установлено, все его объекты будут установлены в указанную схему или, если она не задана, в схему pg_catalog.

В настоящее время единственным необходимым расширением является amcheck.

-?
--help
Показать справку об аргументах командной строки qhb_amcheck и завершиться.

См. также

amcheck

qhb_basebackup

qhb_basebackup — создать резервную копию кластера QHB

Синтаксис

qhb_basebackup [параметр...]

Описание

Утилита qhb_basebackup применяется для создания резервных копий работающего кластера баз данных QHB. Создание копии не влияет на работу других клиентов базы данных. Полученные копии можно использовать как для восстановления на момент времени (см. раздел Непрерывное архивирование и восстановление на момент времени (PITR) ), так и в качестве отправной точки для резервного сервера при реализации трансляции файлов или потоковой репликации

.

qhb_basebackup создает бинарную копию файлов кластера базы данных, при этом контролируя автоматическое переключение системы в режим резервного копирования и обратно. Резервные копии всегда создаются для кластера целиком; невозможно создание копии отдельных баз данных или их объектов. Для этой цели следует использовать утилиту qhb_dump.

Резервное копирование выполняется через обычное соединение QHB, используя протокол репликации. Подключаться нужно от имени суперпользователя или пользователя с правом REPLICATION (см. раздел Атрибуты ролей), а qhb_hba.conf должен явно разрешать подключение для репликации. Кроме того, значение параметра max_wal_senders на сервере должно быть достаточно большим, чтобы как минимум один сеанс был доступен для резервного копирования и еще один — для потоковой передачи WAL (если она используется).

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

qhb_basebackup может сделать базовую копию не только с главного, но и с резервного сервера. Для этого нужно настроить резервный сервер, чтобы тот мог принимать подключения репликации (т. е. настроить параметры max_wal_senders и hot_standby и аутентификацию клиента). Также нужно будет включить full_page_writes на главном сервере.

Обратите внимание, что при резервном копировании с резервного сервера есть некоторые ограничения:

Файл истории резервного копирования в целевом кластере баз данных не создается.

qhb_basebackup не может принудительно переключить резервный сервер на новый файл WAL в конце копирования. Если используется режим -X none и активность записи на основном сервере низкая, есть вероятность, что qhb_basebackup придется долго ждать переключения и архивирования последнего файла WAL, необходимого для полноты копии. В таком случае целесообразно запустить на основном сервере функцию pg_switch_wal для немедленного переключения файла WAL.

Если во время копирования резервный сервер становится главным, оно прерывается.

Все записи WAL, необходимые для резервного копирования, должны содержать достаточное количество полных страниц, для чего необходимо включить full_page_writes на главном сервере и не использовать в archive_command утилиты вроде pg_compresslog для удаления полностраничных записей из файлов WAL.

Параметры

-D каталог
--pgdata=каталог
Каталог для записи резервной копии. qhb_basebackup создаст его и родительские каталоги, если это необходимо. Каталог может быть создан заранее, но должен быть пустым, иначе возникнет ошибка.

Если резервная копия создается в режиме tar, а имя каталога задано как - (тире), tar-файл будет записан в stdout.

Этот параметр является обязательным.

-F формат
--format=формат
Выбирает формат вывода. формат может принимать одно из следующих значений:

p
plain
Записать результат в виде простых файлов, сохраняя структуру текущего каталога хранения данных и табличных пространств. Если в кластере нет дополнительных табличных пространств, вся база данных будет помещена в целевой каталог. В ином случае основной каталог хранения данных будет помещен в целевой каталог, а все остальные табличные пространства — в те же абсолютные пути, что и на исходном сервере. (Чтобы изменить это, воспользуйтесь параметром --tablespace-mapping).
Это формат по умолчанию.

t
tar
Записать результат в целевой каталог в виде tar-файлов. Основной каталог хранения данных будет записан в файл с именем base.tar, а все остальные табличные пространства будут названы в соответствии с их OID.
Если в качестве имени целевого каталога задано - (тире), данные будут записаны в стандартный вывод, подходящий для передачи, например, в gzip. Это возможно только в том случае, если в кластере нет дополнительных табличных пространств и не используется потоковая передача WAL.

-r скорость_передачи
--max-rate=скорость_передачи
Максимальная скорость передачи данных с сервера. Значения задаются в килобайтах в секунду. Чтобы указать мегабайты, можно использовать суффикс M. Также принимается суффикс k, но он не действует. Допустимые значения находятся в пределах от 32 Кб/с до 1024 Мб/с.

Служит для ограничения влияния qhb_basebackup на производительность работающего сервера.

Этот параметр всегда влияет на передачу каталога хранения данных. На передачу файлов WAL он влияет, только если используется метод передачи fetch.

-R
--write-recovery-conf
Создать файл standby.signal и добавить параметры подключения в qhb.auto.conf в каталоге вывода (или в базовый архивный файл, если используется формат tar) для упрощения настройки резервного сервера. В файл qhb.auto.conf будут записаны параметры подключения и, если указан, слот репликации, который использует qhb_basebackup, так что впоследствии при потоковой репликации будут использованы те же параметры.

-T старый_каталог=новый_каталог
--tablespace-mapping=старый_каталог=новый_каталог
Переместить табличное пространство из старого_каталога в новый_каталог в процессе копирования. Чтобы перемещение произошло, старый_каталог должен точно соответствовать пути табличного пространства, как тот определен. (Однако если в старом_каталоге архива не окажется табличного пространства, это не будет ошибкой.) И старый_каталог, и новый_каталог должны задаваться абсолютными путями. Если путь содержит символ =, нужно экранировать его обратным слэшем. Этот параметр можно указать несколько раз для нескольких табличных пространств (см. примеры ниже).

Если табличное пространство перемещается таким образом, символические ссылки внутри основного каталога данных обновляются, чтобы указывать на новое местоположение. Таким образом, для нового экземпляра сервера подготавливается новый каталог хранения данных, где все табличные пространства находятся в новых расположениях.

--waldir=каталог_wal
Указывает местоположение для каталога журнала упреждающей записи. каталог_wal должен задаваться абсолютным путем. Каталог с журналом упреждающей записи можно задать только при создании резервной копии в простом режиме.

-X метод
--wal-method= метод
Включает в резервную копию все необходимые файлы журнала упреждающей записи (файлы WAL). Включает в себя журналы, созданные в процессе резервного копирования. Если только не выбран метод none, главный процесс QHB можно запускать непосредственно в извлеченном каталоге без необходимости обращаться к архиву журналов; таким образом будет получена полностью автономная резервная копия.

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

n
none
Не включать журнал упреждающей записи в резервную копию.

f
fetch
Файлы журнала упреждающей записи собираются в конце процесса резервного копирования. Поэтому необходимо задать достаточно большое значение параметра wal_keep_size, чтобы журнал не удалялся раньше времени. Если журнал был удален до момента передачи, резервное копирование прервется, а копия будет непригодной к использованию.
При использовании формата tar файлы журнала упреждающей записи будут записываться в файл base.tar.

s
stream
Журнал упреждающей записи передается в процессе создания резервной копии. При этом открывается второе соединение с сервером, по которому происходит потоковая передача журнала упреждающей записи, одновременно с резервным копированием. Таким образом будут использоваться два подключения из заданных параметром max_wal_senders. Пока клиент может отслеживать передаваемый журнал упреждающей записи, в этом режиме главному серверу не нужно хранить дополнительные файлы журнала. При использовании формата tar файлы журнала упреждающей записи будут записываться в отдельный файл с именем pg_wal.tar.
Это значение по умолчанию.

-z
--gzip
Включает gzip-сжатие выводимого tar-файла с уровнем сжатия по умолчанию. Сжатие поддерживается только для формата tar, при этом ко всем именам файлов tar будет автоматически добавлен суффикс .gz.

-Z уровень
--compress=уровень
Включает gzip-сжатие выводимого tar-файла и задает уровень сжатия от 0 (без сжатия) до 9 (максимальное сжатие). Сжатие поддерживается только для формата tar, при этом ко всем именам файлов tar будет автоматически добавлен суффикс .gz.

Переменные среды

PG_COLOR
Указывает, использовать ли цвет в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Примечания

В начале резервного копирования на сервере с копируемой базой следует выполнить контрольную точку. И если не установлен флаг --checkpoint=fast, это может занять некоторое время, в течение которого qhb_basebackup будет бездействовать.

Резервная копия будет включать в себя все файлы каталога хранения данных и табличных пространств, в том числе файлы конфигурации и все дополнительные файлы, помещенные в этот каталог сторонними разработчиками, за исключением некоторых временных файлов, управляемых QHB. Однако копируются только обычные файлы и каталоги, а помимо этого сохраняются лишь символические ссылки на табличные пространства. Символические ссылки, указывающие на определенные каталоги, известные QHB, копируются как пустые каталоги. Другие символические ссылки и файлы специальных устройств пропускаются.

Если не указан флаг --tablespace-mapping, по умолчанию табличные пространства в простом формате будут копироваться в тот же путь, что и на сервере. Без этого параметра при использовании табличных пространств создать копию базы в простом формате на том же сервере не получится, поскольку резервная копия должна быть записана в те же каталоги, что и исходные табличные пространства.

Когда используется режим tar, пользователь должен позаботиться о распаковке каждого tar-файла перед запуском сервера QHB. При наличии дополнительных табличных пространств их tar-файлы необходимо распаковать в правильные расположения. В таком случае сервером будут созданы символические ссылки на эти табличные пространства, в соответствии с содержимым файла tablespace_map, включенного в архив base.tar.

qhb_basebackup* работает с серверами QHB версии 1.1 и выше.

qhb_basebackup сохранит разрешения группы в форматах plain и tar, если разрешения для группы установлены в исходном кластере.

Примеры

Создать базовую резервную копию сервера на mydbserver и сохранить ее в локальном каталоге /var/lib/qhb/data:

$ qhb_basebackup -h mydbserver -D /var/lib/qhb/data

Создать резервную копию локального сервера в отдельных файлах tar для каждого табличного пространства и сохранить ее в каталоге backup, отчитываясь о прогрессе во время выполнения:

$ qhb_basebackup -D backup -Ft -z -P

Создать резервную копию локальной базы данных с одним табличным пространством и сжать ее с помощью bzip2:

$ qhb_basebackup -D - -Ft -X fetch | bzip2 > backup.tar.bz2

(Эта команда не будет выполнена, если в базе данных есть несколько табличных пространств.)

Создать резервную копию локальной базы данных с перемещением табличного пространства из /opt/ts в ./backup/ts:

$ qhb_basebackup -D backup/data -T /opt/ts=$(pwd)/backup/ts

См. также

qhb_dump

qhb_dump

qhb_dump — выгрузить базу данных QHB в виде скрипта или в архивном формате

Синтаксис

qhb_dump [параметр-подключения...] [параметр...] [имя_бд]

Описание

qhb_dump — это утилита для резервного копирования базы данных QHB. Она создает последовательные резервные копии, даже если база данных параллельно используется. qhb_dump не блокирует доступ к базе данных другим пользователям (ни для чтения, ни для записи).

Утилита qhb_dump выгружает только одну базу данных. Для резервного копирования всего кластера или глобальных объектов, общих для всех баз данных в кластере (например, ролей и табличных пространств), следует применять программу qhb_dumpall.

Дамп можно создать в виде скрипта или архивного файла. Скрипты представляют собой текстовые файлы, содержащие команды SQL, необходимые для восстановления базы данных до состояния, в котором она находилась на момент сохранения. Для восстановления из такого скрипта, нужно передать его psql. Скрипты можно использовать для восстановления базы данных на других машинах и в других архитектурах, а с некоторыми модификациями даже в других SQL-совместимых СУБД.

Для восстановления базы данных из архивных форматов следует использовать утилиту qhb_restore. Эти форматы позволяют qhb_restore избирательно восстанавливать данные, а также изменять порядок восстанавливаемых элементов. Архивные форматы предназначены для переноса на платформы с иной архитектурой.

Применение архивных форматов и сочетание утилит qhb_restore и qhb_dump обеспечивает гибкий механизм архивирования и переноса данных. qhb_dump можно использовать для резервного копирования всей базы данных, затем с помощью qhb_restore проверить архив и/или выбрать, какие части базы данных необходимо восстановить. Наиболее гибкими форматами выходного файла являются «custom» (-Fc) и «directory» (-Fd). Они позволяют выбирать и переупорядочивать все заархивированные элементы, поддерживают восстановление в несколько потоков, а также сжимаются по умолчанию. При этом только формат «directory» поддерживает выгрузку дампа в несколько потоков.

Во время работы qhb_dump необходимо проверять выводимые сообщения на наличие предупреждений (они печатаются в стандартный поток ошибок), особенно в свете перечисленных ниже ограничений.

Параметры

Следующие параметры командной строки управляют содержимым и форматом вывода.

имя_бд
Задает имя базы данных, из которой будет выгружаться дамп. Если имя не задано, используется значение переменной среды PGDATABASE. Если и переменная не задана, используется имя пользователя, под которым производится подключение.

-a
--data-only
Выгружать только данные, но не схемы (DDL, определения данных). Будут дампированы данные таблиц, большие объекты и значения последовательностей.

Этот параметр схож с --section=data, но по историческим причинам не идентичен.

-b
--blobs
Включить в дамп большие объекты. Это поведение по умолчанию, если только не указаны ключи --schema, --table или --schema-only. Поэтому ключ -b полезен только для добавления больших объектов в дампы при выгрузке конкретной схемы или таблицы. Обратите внимание, что большие бинарные объекты относятся к данным и поэтому будут включены при использовании ключа --data-only, но не ключа --schema-only.

-B
--no-blobs
Исключить большие объекты из дампа.

Когда заданы и -b, и -B, большие объекты при выгрузке данных будут выводиться (см. описание ключа -b).

-c
--clean
Включить в выходной файл команды удаления (DROP) объектов базы данных перед командами создания (CREATE) этих объектов. (Если дополнительно не указать флаг --if-exists, то при восстановлении в базу данных, где отсутствуют некоторые объекты, будут выводиться сообщения об ошибке, которые можно игнорировать.)

Этот параметр игнорируется, когда данные дампируются в архивном (не текстовом) формате. Для таких форматов этот параметр можно указать при вызове qhb_restore.

-C
--create
Вставить в начало выходного файла команду для создания базы данных и последующего подключения к ней. (В этом случае не важно, к какой базе из целевой инсталляции было произведено подключение перед выполнением этого скрипта.) Если дополнительно указан ключ --clean, то скрипт удаляет и пересоздает целевую базу данных перед повторным подключением к ней.

С параметром --create в выходной файл также включается комментарий к базе данных (если таковой задан) и все параметры переменных конфигурации, специфичные для этой базы данных, то есть все команды ALTER DATABASE ... SET ... и ALTER ROLE ... IN DATABASE ... SET ..., которые ссылаются на эту базу. Также выгружаются права доступа к самой базе данных, если не указан ключ --no-acl.

Этот параметр игнорируется, когда данные дампируются в архивном (не текстовом) формате. Для таких форматов этот параметр можно указать при вызове qhb_restore.

-e шаблон
--extension=шаблон
Выгрузить только расширения, соответствующие шаблону. Когда этот параметр не задан, дампируются все несистемные расширения в целевой базе данных. Задав ключ -e несколько раз, можно выбрать несколько расширений. Значение шаблон интерпретируется по тем же правилам, что и шаблон в командах \d утилиты psql , поэтому несколько расширений также можно выбрать, вписав в шаблон подстановочные знаки. Используя подстановочные знаки, не забывайте при необходимости заключать шаблон в кавычки, чтобы эти знаки не разворачивала оболочка системы.

Если расширение указано в --extension, в дамп включаются все его конфигурационные отношения, зарегистрированные функцией pg_extension_config_dump.

Примечание
При указании -e утилита qhb_dump не пытается выгружать прочие объекты базы данных, от которых могут зависеть выбранные расширения. Таким образом, нет гарантии, что эти расширения будут успешно восстановлены в чистой базе данных.

-E кодировка
--encoding=кодировка
Создать дамп в заданной кодировке набора символов. По умолчанию дамп создается в кодировке базы данных. (Тот же результат можно получить, задав желаемую кодировку в переменной среды PGCLIENTENCODING.) Поддерживаемые кодировки описаны в подразделе Поддерживаемые кодировки.

-f файл
--file=файл
Отправить вывод в заданный файл. Этот параметр можно не указывать для форматов с выводом в файл, поскольку в этом случае используется стандартный вывод. Однако его обязательно следует задавать для формата с выводом в каталог, где этот параметр вместо файла задает путь к целевому каталогу. В этом случае каталог создается qhb_dump и не должен существовать заранее.

-F формат
--format=формат
Задает формат вывода дампа. формат может принимать следующие значения:

p
plain
Вывести простой текстовый SQL-скрипт (поведение по умолчанию).

c
custom
Вывести данные в специальном архивном формате, пригодном для использования утилитой qhb_restore. Вместе с форматом каталога это наиболее гибкий формат вывода, поскольку он позволяет вручную выбирать и переупорядочивать заархивированные элементы во время восстановления. Кроме того, вывод в этом формате по умолчанию сжимается.

d
directory
Вывести данные в архиве в формате каталога, пригодном для использования утилитой qhb_restore. При этом будет создан каталог в котором у каждой выгруженной таблицы и большого объекта будет отдельный файл, а также будет создан файл-оглавление, описывающий выгруженные объекты в машиночитаемом формате, понятном qhb_restore. С архивом в формате каталога можно работать стандартными средствами Unix; например, файлы в несжатом архиве можно сжать с помощью gzip. Этот формат по умолчанию сжимается, а также поддерживает многопоточную выгрузку.

t
tar
Вывести данные в архиве в формате tar, пригодном для использования утилитой qhb_restore. Формат tar совместим с форматом каталога: при распаковке архива в формате tar создается корректная копия в формате каталога. Однако формат tar не поддерживает сжатие. Кроме того, при использовании формата tar нельзя изменить относительный порядок элементов данных в таблице во время восстановления.

-j число_заданий
--jobs= число_заданий
Запустить многопоточную выгрузку, обрабатывая одновременно несколько таблиц (в количестве число_заданий). Это может сократить время создания дампа, но при этом увеличивает нагрузку на сервер базы данных. Этот параметр можно использовать только с форматом вывода в каталог, так как это единственный формат, в котором несколько процессов могут записывать данные одновременно.

qhb_dump откроет число_заданий + 1 соединений с базой данных, поэтому необходимо установить такое значение параметра max_connections, которое бы покрыло все эти соединения.

Если при многопоточном дампировании будут запрашиваться эксклюзивные блокировки на объекты базы данных, может произойти сбой. Причина этого в том, что главный процесс qhb_dump запрашивает общие блокировки для объектов, которые позже будут выгружать рабочие процессы, чтобы никто не удалил их во время создания дампа. Если после этого другой клиент запросит эксклюзивную блокировку таблицы, этот запрос будет поставлен в очередь в ожидании снятия общей блокировки главного процесса. В последующем любая попытка доступа к таблице также будет вставлена в очередь, после запроса на эксклюзивную блокировку. В том числе в очередь попадет и рабочий процесс, пытающийся выгрузить таблицу. Если не принять никаких мер предосторожности, получится классическая взаимоблокировка. Для выявления таких конфликтов рабочий процесс qhb_dump запрашивает еще одну общую блокировку, используя параметр NOWAIT. И если он не получит эту блокировку, значит, кто-то еще запросил эксклюзивную блокировку и нет возможности продолжить выгрузку, поэтому qhb_dump придется ее прервать.

Для получения согласованной резервной копии сервер базы данных должен поддерживать возможность синхронизированных снимков. С помощью этой функциональности клиенты базы данных могут убедиться, что они видят одну и ту же версию данных, несмотря на использование разных подключений. qhb_dump -j использует множественные подключения к базе данных; первое подключение происходит с главным процессом, а последующие — с рабочими процессами. Без функциональности синхронизированных снимков нет гарантии, что разные рабочие процессы в каждом подключении увидят одни и те же данные, что может привести к несогласованности данных резервной копии.

-n шаблон
--schema= шаблон
Выгрузить только схемы, соответствующие шаблону; вместе со схемами будут выгружены и все содержащиеся в них объекты. Если этот параметр не задан, выгружаются все несистемные схемы в целевой базе данных. Задав ключ -n несколько раз, можно выбрать несколько схем. Значение шаблон интерпретируется по тем же правилам, что и шаблон в командах \d утилиты psql , поэтому несколько схем также можно выбрать, вписав в шаблон подстановочные знаки. Используя подстановочные знаки, не забывайте при необходимости заключать шаблон в кавычки, чтобы эти знаки не разворачивала оболочка системы; см. Примеры.

Примечание
При указании -n утилита qhb_dump не пытается выгружать прочие объекты базы данных, от которых могут зависеть выбранные схемы. Таким образом, нет гарантии, что эти схемы будут успешно восстановлены в чистой базе данных.

Примечание
Объекты, не принадлежащие схемам, например большие бинарные объекты, при указании -n не выводятся. Однако их можно добавить в дамп с помощью ключа --blobs.

-N шаблон
--exclude-schema=шаблон
Не выгружать схемы, соответствующие шаблону. Шаблон интерпретируется по тем же правилам, что и для параметра -n. Задав ключ -N несколько раз, можно исключить схемы, соответствующие любому из нескольких шаблонов.

Когда заданы и -n, и -N, в дамп включаются схемы, соответствующие как минимум одному ключу -n и не совпадающие ни с одним из шаблонов в -N. Если параметр -N задается один, то соответствующие ему схемы исключаются из обычного дампа.

-O
--no-owner
Не формировать команды для установки владельца объектов как в исходной базе данных. По умолчанию qhb_dump создает команды ALTER OWNER или SET SESSION AUTHORIZATION для назначения владельца объектов базы. Эти команды выполнятся успешно, только если скрипт будет запущен суперпользователем (или владельцем всех объектов в скрипте). Чтобы создать скрипт, который может выполниться при восстановлении любым пользователем и назначить его владельцем всех объектов, следует задать параметр -O.

Этот параметр игнорируется, когда данные дампируются в архивном (не текстовом) формате. Для таких форматов этот параметр можно указать при вызове qhb_restore.

-R
--no-reconnect
Этот параметр устарел, но все еще принимается в целях обратной совместимости.

-s
--schema-only
Выгрузить только определения объектов (схемы), но не сами данные.

Этот параметр действует противоположно параметру --data-only. Это похоже на указание --section=pre-data --section=post-data, но по историческим причинам не идентично ему.

(Не путайте этот параметр с параметром --schema, который использует термин «схема» в другом значении.)

Чтобы исключить данные таблицы только для подмножества таблиц в базе данных, используйте параметр --exclude-table-data.

-S имя_пользователя
--superuser=имя_пользователя
Задать имя суперпользователя, которое будет использоваться при выключении триггеров. Имеет значение только вместе с параметром --disable-triggers. (Обычно лучше вместо этого запускать полученный скрипт от лица суперпользователя.)

-t шаблон
--table=шаблон
Выгрузить только таблицы, соответствующие шаблону. Задав ключ -t несколько раз, можно выбрать несколько таблиц. Значение шаблон интерпретируется по тем же правилам, что и шаблон в командах \d утилиты psql, поэтому несколько таблиц также можно выбрать, вписав в шаблон подстановочные знаки. Используя подстановочные знаки, не забывайте при необходимости заключать шаблон в кавычки, чтобы эти знаки не разворачивала оболочка системы; см. Примеры.

Помимо таблиц с этим параметром можно выгрузить определения соответствующих шаблону представлений, материализованных представлений, сторонних таблиц и последовательностей. При этом содержимое представлений и материализованных представлений выгружаться не будет, а содержимое сторонних таблиц будет выгружено, только если в параметре --include-foreign-data задан соответствующий сторонний сервер.

При использовании параметра -t не действуют ключи -n и -N, так как выбранные с его помощью таблицы все равно будут выгружаться, а нетабличные объекты выгружаться не будут.

Примечание
При указании -t утилита qhb_dump не пытается выгружать прочие объекты базы данных, от которых могут зависеть выбранные таблицы. Таким образом, нет гарантии, что эти таблицы будут успешно восстановлены в чистой базе данных.

-T шаблон
--exclude-table=шаблон
Не выгружать таблицы, соответствующие шаблону. Шаблон интерпретируется по тем же правилам, что и для параметра -t. Задав ключ -T несколько раз, можно исключить таблицы, соответствующие любому из нескольких шаблонов.

Когда заданы и -t, и -T, в дамп включаются таблицы, соответствующие как минимум одному ключу -t и не совпадающие ни с одним из шаблонов в -N. Если параметр -T задается один, то соответствующие ему таблицы исключаются из обычного дампа.

-v
--verbose
Включает подробный режим. qhb_dump будет выводить в стандартный поток ошибок подробные комментарии к объектам и время начала/окончания выгрузки в файл дампа, а также сообщения о прогрессе выполнения. Если повторить этот ключ, в стандартный поток ошибок будут выводиться дополнительные отладочные сообщения.

-V
--version
Вывести версию qhb_dump и завершиться.

-x
--no-privileges
--no-acl
Запретить выгрузку прав доступа (команды GRANT/REVOKE).

-Z 0-9
--compress=0-9
Задать уровень сжатия данных. При нулевом значении сжатие отсутствует. Для архивов специального формата и формата каталога будут сжиматься отдельные сегменты табличных данных, и по умолчанию используется умеренный уровень сжатия. Если указать ненулевой уровень сжатия для простого текстового формата, сжиматься будет весь выходной файл, как если бы он был передан через gzip; по умолчанию простой формат не сжимается. Формат tar в настоящий момент сжатие не поддерживает.

--binary-upgrade
Этот параметр предназначен для использования утилитами обновления на месте. Применение в других целях не рекомендуется и не поддерживается. Поведение параметра может быть изменено в будущих версиях без предварительного уведомления.

--column-inserts
Выгружать данные в виде команды INSERT с явными указанием имен столбцов (INSERT INTO таблица (столбец, ...) VALUES ...). Восстановление при этом будет происходить очень медленно; в основном этот вариант оправдан при создании дампов, которые можно загружать в СУБД, отличные от QHB. Любая ошибка во время перезагрузки приведет к потере только тех строк INSERT, в которых возникли ошибки, а не всего содержимого таблицы.

--disable-dollar-quoting
Этот параметр выключает возможность заключать тела функций в долларовые кавычки, заставляя использовать кавычки из стандартного синтаксиса SQL.

--disable-triggers
Этот параметр представляет интерес только при создании дампа, содержащего одни данные. Указывает qhb_dump включать в дамп команды для временного выключения триггеров на целевых таблицах во время восстановления данных. Применяется в ситуациях, когда существуют проверки ссылочной целостности или другие триггеры для таблиц, которые не стоит вызывать во время восстановления данных.

В настоящее время команды, создаваемые с параметром --disable-triggers, должны выполняться суперпользователем. Следовательно, необходимо задать имя суперпользователя с помощью флага -S или запустить полученный скрипт от лица суперпользователя.

Этот параметр игнорируется, когда данные дампируются в архивном (не текстовом) формате. Для таких форматов этот параметр можно указать при вызове qhb_restore.

--enable-row-security
Этот параметр представляет интерес только при выгрузке содержимого таблиц, в которых есть защита строк. По умолчанию qhb_dump выключает row_security, чтобы все данные гарантированно были выгружены из таблицы. Если у пользователя недостаточно прав для обхода защиты строк, выдается ошибка. Этот параметр указывает qhb_dump включить row_security, позволяя пользователю выгрузить части содержимого таблицы, к которым у него есть доступ.

Обратите внимание, что в настоящее время при использовании этого параметра желательно, чтобы дамп был в формате INSERT, так как команда COPY FROM в процессе восстановления не поддерживает защиту строк.

--exclude-table-data=шаблон
Не выгружать данные из таблиц, соответствующих шаблону. Шаблон интерпретируется по тем же правилам, что и для параметра -t. Задав ключ --exclude-table-data несколько раз, можно исключить таблицы, соответствующие любому из нескольких шаблонов. Этот параметр полезен, когда необходимо получить определение конкретной таблицы, но содержащиеся в ней данные не нужны.

Чтобы не выгружать данные из всех таблиц базы, воспользуйтесь параметром --schema-only.

--extra-float-digits=число_цифр
Выгружать числа с плавающей запятой не с максимальной доступной точностью, а с заданным значением extra_float_digits. При обычном дампировании в целях резервного копирования этот параметр использовать не следует.

--if-exists
Использовать условные команды (т. е. добавлять предложение IF EXISTS) при удалении объектов базы данных. Этот параметр не работает без флага --clean.

--include-foreign-data=сторонний_сервер
Выгрузить данные всех сторонних таблиц со стороннего сервера, имя которого соответствует шаблону сторонний_сервер. Задав ключ --include-foreign-data несколько раз, можно выбрать несколько сторонних серверов. Кроме того, значение сторонний сервер интерпретируется по тем же правилам, что и шаблон в командах \d утилиты psql, поэтому несколько сторонних серверов также можно выбрать, вписав в шаблон подстановочные знаки. Используя подстановочные знаки, не забывайте при необходимости заключать шаблон в кавычки, чтобы эти знаки не разворачивала оболочка системы; см. Примеры. Единственное отличие от этих правил в том, что шаблон не может быть пустым.

Примечание
При указании --include-foreign-data утилита qhb_dump не проверяет возможность записи в стороннюю таблицу. Таким образом, нет гарантии, что выгруженная сторонняя таблица может быть успешно восстановлена.

--inserts
Выгрузить данные в виде команд INSERT (а не COPY). Восстановление при этом будет происходить очень медленно; в основном этот вариант оправдан при создании дампов, которые можно загружать в СУБД, отличные от QHB. Любая ошибка во время перезагрузки приведет к потере только тех строк INSERT, в которых возникли ошибки, а не всего содержимого таблицы. Обратите внимание, что восстановление может полностью провалиться, если у таблицы был изменен порядок столбцов. В таком случае можно воспользоваться параметром --column-inserts, для которого порядок столбцов не важен, но он работает еще медленнее.

--load-via-partition-root
При выгрузке данных партиции таблицы выводить команды COPY или INSERT, ссылающиеся на корневую таблицу в содержащей ее иерархии партиционирования, а не на саму эту партицию. В результате при загрузке данных для каждой строки будет заново выбираться подходящая партиция. Это может быть полезно при перезагрузке данных, когда на целевом сервере строки не всегда попадают в те же партиции, в которых они были на исходном сервере. Это возможно, например, когда у столбца разбиения текстовый тип и в двух системах по-разному определено правило сортировки, по которому упорядочивается этот столбец.

При восстановлении из архива, созданного с помощью этого параметра, лучше не использовать параллельную обработку, поскольку qhb_restore не будет точно знать, в какие партиции тот или иной элемент архива будет загружать данные. Это может привести к существенному снижению производительности из-за конфликтов блокировок между параллельными заданиями или даже сбой восстановления из-за того, что ограничения внешнего ключа вступят в силу раньше, чем загрузятся все нужные данные.

--lock-wait-timeout=время_ожидания
Не ждать бесконечно получения разделяемых блокировок таблиц в начале дампирования. Вместо этого выдать ошибку, если не удастся заблокировать таблицу за указанное время_ожидания. Время ожидания можно указать в любом из форматов, принимаемых командой SET statement_timeout. (Допустимые форматы различаются в зависимости от версии сервера, с которого выгружаются данные, однако целое число миллисекунд принимается всеми версиями.)

--no-comments
Не выгружать комментарии.

--no-publications
Не выгружать публикации.

--no-security-labels
Не выгружать метки безопасности.

--no-subscriptions
Не выгружать подписки.

--no-sync
По умолчанию qhb_dump будет ждать, пока все файлы не будут надежно записаны на диск. Этот параметр заставляет qhb_dump завершаться без ожидания, т. е. выполняться быстрее, но впоследствии при неожиданном сбое операционной системы дамп может оказаться поврежден. В целом, этот параметр предназначен для тестирования, и его не следует использовать при дампировании данных из производственной инсталляции.

--no-tablespaces
Не формировать команды для указания табличных пространств. При указании этого параметра все объекты будут создаваться в том табличном пространстве, которое устанавливается по умолчанию во время восстановления.

Этот параметр игнорируется, когда данные выводятся в архивном (не текстовом) формате. Для таких форматов этот параметр можно указать при вызове qhb_restore.

--no-toast-compression
Не формировать команды, задающие методы сжатия TOAST. При указании этого параметра все столбцы будут восстановлены с методом сжатия, выбранным по умолчанию.

--no-unlogged-table-data
Не выгружать содержимое нежурналируемых таблиц. Этот параметр не влияет на выгрузку определений (схем) таблиц; он лишь подавляет выгрузку данных из таблиц. При запуске на резервном сервере содержимое нежурналируемых таблиц никогда не выгружается.

--on-conflict-do-nothing
Добавить предложения ON CONFLICT DO NOTHING в команды INSERT. Этот параметр недопустим, если также не указаны параметры --inserts, --column-insert или --rows-per-insert.

--quote-all-identifiers
Принудительно экранировать все идентификаторы. Этот параметр рекомендуется при выгрузке базы данных с сервера QHB, основная версия которого отличается от версии qhb_dump, или когда дамп предназначен для загрузки на сервер с другой основной версией. По умолчанию qhb_dump заключает в кавычки только те идентификаторы, которые являются зарезервированными словами в ее собственной основной версии. Иногда это приводит к проблемам совместимости при работе с серверами других версий, в которых наборы зарезервированных слов могут немного отличаться. Использование параметра --quote-all-identifiers предотвращает подобные проблемы ценой ухудшения читаемости скрипта с дампом.

--rows-per-insert=число_строк
Выгружать данные в виде команд INSERT (вместо COPY). Этот параметр задает максимальное число строк в одной команде INSERT. Указанное значение должно быть больше нуля. Любая ошибка во время перезагрузки приведет к потере только тех строк INSERT, в которых возникли ошибки, а не всего содержимого таблицы.

--section=имя_партиции
Выгрузить только заданную партицию. Имя партиции может принимать значения pre-data, data или post-data. Для выбора нескольких партиций этот параметр можно указать более одного раза. По умолчанию выгружаются все партиции.

Партиция data содержит собственно данные таблиц, содержимое больших объектов и значения последовательности. Партиция post-data содержит определения индексов, триггеров, правил и ограничений (кроме ограничений проверки, созданных без NOT VALID). Партиция pre-data содержит определения всех остальных элементов.

--serializable-deferrable
Использовать для выгрузки транзакцию с уровнем изоляции serializable для гарантированного получения снимка, согласованного с последующими состояниями базы данных. Однако для этого нужно дождаться момента, когда в потоке транзакций не может быть никаких аномалий, и поэтому нет риска сбоя выгрузки или отката других транзакций с ошибкой serialization_failure. Более подробную информацию об изоляции транзакций и управлении параллельным доступом см. в главе Управление параллельным доступом.

Этот параметр не несет практической пользы для дампа, предназначенного только для восстановления после сбоя. Он может быть полезен для дампа, используемого для загрузки копии базы данных, в которой формируются отчеты и выполняются другие операции чтения, в то время как в исходной базе продолжается обычная работа. Без этого параметра дамп может отражать состояние, не согласующееся ни с каким последовательным выполнением фиксируемых в итоге транзакций. Например, если используются методы пакетной обработки, статус пакета может отображаться как завершенный, хотя на самом деле в дампе будут находиться не все элементы пакета.

Этот параметр не повлияет на результат, если при запуске qhb_dump нет активных транзакций чтения/записи. Если же активные транзакции чтения-записи есть, то начало выгрузки может быть отложено на неопределенный срок. После запуска выгрузки производительность с этим ключом или без него будет одинаковой.

--snapshot=имя_снимка
Использовать заданный синхронизированный снимок при создании дампа базы данных (подробную информацию см. в таблице 87).

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

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

--strict-names
Требовать, чтобы каждому квалификатору схемы (-n/--schema), расширения (-e/--extension) и таблицы (-t/--table) соответствовал как минимум один объект схема/расширение/таблица в выгружаемой базе данных. Обратите внимание, что если ни для одного из этих квалификаторов не найдется соответствующего объекта, qhb_dump выдаст ошибку и без ключа --strict-names.

Этот параметр не действует на ключи -N/--exclude-schema, -T/ --exclude-table или --exclude-table-data. Если не находятся объекты, соответствующие шаблонам исключения, это не считается ошибкой.

--use-set-session-authorization
Выводить команды SET SESSION AUTHORIZATION, соответствующие стандарту SQL, вместо команд ALTER OWNER, чтобы назначить владельцев объекта. В итоге дамп будет более стандартизированным, но, в зависимости от истории объектов, может не восстановиться должным образом. Кроме того, при использовании SET SESSION AUTHORIZATION для корректного восстановления из дампа безусловно понадобятся права суперпользователя, тогда как ALTER OWNER требует меньших привилегий.

-?
--help
Показать справку об аргументах командной строки qhb_dump и завершиться.

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

-d имя_бд
--dbname=имя_бд
Задает имя базы данных для подключения. Равнозначно указанию имя_бд в качестве первого аргумента, не являющегося ключом, в командной строке.

-h хост
--host=хост
Задает имя хост-компьютера, на котором работает сервер. Если значение начинается со слэша, оно используется в качестве каталога для Unix-сокета. Значение по умолчанию определяется переменной среды PGHOST, если она установлена. В противном случае производится попытка подключения к Unix-сокету.

-p порт
--port=порт
Указывает TCP-порт или расширение файла локального Unix-сокета, через который сервер принимает подключения. Значение по умолчанию определяется переменной среды PGPORT, если она установлена, или числом, заданным при компиляции.

-U имя_пользователя
--username=имя_пользователя
Имя пользователя, под которым производится подключение.

-w
--no-password
Никогда не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, и пароль недоступен с помощью иных средств, таких как файл .pgpass, попытка подключения завершится неудачно. Этот параметр может быть полезен в пакетных заданиях и скриптах, где нет пользователя, чтобы ввести пароль.

-W
--password
Принудительно запрашивать пароль перед подключением к базе данных.

Это несущественный параметр, так как qhb_dump автоматически запросит пароль, если сервер требует аутентификацию по паролю. Однако чтобы выяснить это, qhb_dump потребуется дополнительная попытка подключения к серверу. В некоторых случаях имеет смысл ввести -W, чтобы исключить эту лишнюю попытку.

--role=имя_роли
Задает имя роли, которая будет использоваться для создания дампа. При указании этого параметра qhb_dump выполнит команду SET ROLE имя_роли после подключения к базе данных. Это полезно, когда проходящий проверку пользователь (указанному ключом -U) не имеет прав, требуемых qhb_dump, но может переключиться на роль, наделенную этими правами. В некоторых инсталляциях правила запрещают подключаться к серверу непосредственно от имени суперпользователя, и этот параметр позволяет создавать дампы, не нарушая их.

Переменные среды

PGDATABASE
PGHOST
PGOPTIONS
PGPORT
PGUSER
Параметры подключения по умолчанию.

PG_COLOR
Указывает, использовать ли цвет в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Диагностика

qhb_dump внутри себя выполняет команды SELECT. При возникновении проблем с запуском qhb_dump убедитесь, что в базе данных можно выполнить SELECT, например, с помощью psql.

Обычно активность qhb_dump в базе данных собирается сборщиком статистики. Если это нежелательно, то можно установить параметр track_counts в false в переменной среды PGOPTIONS или в команде ALTER USER.

Примечания

Если в базу данных кластера template1 были локально установлены какие-либо дополнительные объекты, необходимо проследить за тем, чтобы вывод qhb_dump выгружался в по-настоящему пустую базу данных; в противном случае есть вероятность возникновения ошибок из-за дублирования определений добавленных объектов. Чтобы создать пустую базу данных без каких-либо локальных дополнений, копируйте ее из шаблона template0, а не template1, например:

CREATE DATABASE foo WITH TEMPLATE template0;

Если выбрано создание дампа только с данными и используется параметр --disable-triggers, qhb_dump формирует команды для выключения триггеров в пользовательских таблицах перед вставкой данных, а затем — команды, включающие триггеры обратно вставки данных. Если восстановление прерывается в середине процесса, системные каталоги могут остаться в неправильном состоянии.

Файл дампа, созданный qhb_dump, не содержит статистику, используемую оптимизатором для принятия решений по планированию запросов. Поэтому после восстановления из файла дампа имеет смысл выполнить команду ANALYZE для обеспечения оптимальной производительности; дополнительную информацию см. в разделах Обновление статистики планировщика и Процесс «Автовакуум».

Поскольку qhb_dump используется для переноса данных в более новые версии QHB, предполагается, что вывод qhb_dump загрузится на сервер QHB, более новой версии, чем версия qhb_dump. Также qhb_dump может создавать дампы серверов QHB более старых версий, чем ее собственная. Однако qhb_dump не может создавать дампы серверов QHB более новых основных версий, чем ее собственная; она не станет даже пытаться это делать, во избежание риска некорректной выгрузки. Кроме того, не гарантируется, что вывод qhb_dump может быть загружен на сервер более старой основной версии, даже если дамп был взят с сервера той же версии. Загрузка такого файла может потребовать его редактирования вручную, с удалением синтаксиса, не воспринимаемого старой версией. В случаях разных версий рекомендуется использовать параметр --quote-all-identifiers, поскольку он может предотвратить проблемы, возникающие из-за изменения списков зарезервированных слов в разных версиях QHB .

Дампируя подписки логической репликации, qhb_dump будет генерировать команды CREATE SUBSCRIPTION которые используют параметр connect = false, так что при восстановлении подписки не устанавливаются удаленные подключения для создания слота репликации или для начального копирования таблиц. Таким образом, дамп может быть восстановлен без сетевого доступа к удаленным серверам. В задачу пользователя входит последующая повторная активация подписок должным образом. Если задействованные хосты поменялись, возможно, потребуется изменить информацию о подключении. Также может быть целесообразно урезать целевые таблицы перед началом нового полного копирования таблиц.

Примеры

Выгрузка базы данных mydb в файл SQL-скрипта:

$ qhb_dump mydb > db.sql

Восстановление из такого скрипта в базу данных (только что созданную) с именем newdb:

$ psql -d newdb -f db.sql

Выгрузка базы данных в специальном архивном формате:

$ qhb_dump -Fc mydb > db.dump

Выгрузка базы данных в формате каталога:

$ qhb_dump -Fd mydb -f dumpdir

Выгрузка базы данных в формате каталога в 5 параллельных потоков:

$ qhb_dump -Fd mydb -j 5 -f dumpdir

Восстановление из архивного файла в базу данных (только что созданную) с именем newdb:

$ qhb_restore -d newdb db.dump

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

$ qhb_restore -d qhb --clean --create db.dump

Выгрузка отдельной таблицы с именем mytab:

$ qhb_dump -t mytab mydb > db.sql

Выгрузка всех таблиц, имена которых начинаются с emp и которые принадлежат схеме detroit, кроме таблицы employee_log:

$ qhb_dump -t 'detroit.emp*' -T detroit.employee_log mydb > db.sql

Выгрузка всех схем, имена которых начинаются с east или west, а заканчиваются на gsm, при этом исключая любые схемы, имена которых содержат слово test:

$ qhb_dump -n 'east*gsm' -n 'west*gsm' -N '*test*' mydb > db.sql

То же самое, но с использованием записи регулярного выражения для объединения ключей:

$ qhb_dump -n '(east|west)*gsm' -N '*test*' mydb > db.sql

Выгрузка всех объектов базы данных, кроме таблиц, имена которых начинаются с ts_:

$ qhb_dump -T 'ts_*' mydb > db.sql

Чтобы указать имя в верхнем или смешанном регистре в ключе -t и связанных с ним ключах, нужно заключить это имя в кавычки; иначе оно будет приведено к нижнему регистру. Но кавычки являются спецсимволом для оболочки поэтому и они, в свою очередь, должны быть заключены в кавычки. Таким образом, чтобы дампировать одну таблицу с именем в смешанном регистре, нужно написать что-то вроде

$ qhb_dump -t "\"MixedCaseName\"" mydb > mytab.sql

См. также

qhb_dumpall, qhb_restore, psql

qhb_dumpall

qhb_dumpall — выгрузить кластер базы данных QHB в виде скрипта

Синтаксис

qhb_dumpall [параметр-подключения...] [параметр...]

Описание

qhb_dumpall — это утилита для записи («дампирования») всех баз данных кластера QHB в один файл скрипта. Этот файл содержит команды SQL, которые можно передать на вход psql для восстановления баз данных. Файл создается путем вызова qhb_dump для каждой базы данных в кластере. qhb_dumpall также дампирует глобальные объекты, общие для всех баз данных, то есть роли и табличные пространства. (Утилита qhb_dump не сохраняет эти объекты.)

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

SQL-скрипт будет записан в стандартный вывод. Чтобы перенаправить его в файл, используйте параметр -f/--file или операторы оболочки,

qhb_dumpall требуется подключиться к серверу QHB несколько раз (по разу для каждой базы данных). Если вы используете аутентификацию по паролю, эта утилита каждый раз будет запрашивать пароль. В таких случаях удобно иметь файл ~/.pgpass.

Параметры

Следующие параметры командной строки управляют содержимым и форматом вывода.

-a
--data-only
Выгружать только данные, но не схемы (DDL, определения данных).

-c
--clean
Включить SQL-команды удаления (DROP) баз данных перед командами их создания (CREATE). Также в дамп будут добавлены команды DROP для ролей и табличных пространств.

-E кодировка
--encoding=кодировка
Создать дамп в заданной кодировке набора символов. По умолчанию дамп создается в кодировке базы данных. (Тот же результат можно получить, задав желаемую кодировку в переменной среды PGCLIENTENCODING.)

-f файл
--file=файл
Отправить вывод в заданный файл. Если этот параметр опущен, используется стандартный вывод.

-g
--globals-only
Выгружать только глобальные объекты (роли и табличные пространства), без баз данных.

-O
--no-owner
Не формировать команды для установки владельца объектов как в исходной базе данных. По умолчанию qhb_dumpall создает команды ALTER OWNER или SET SESSION AUTHORIZATION для назначения владельца объектов базы. Эти команды выполнятся успешно, только если скрипт будет запущен суперпользователем (или владельцем всех объектов в скрипте). Чтобы создать скрипт, который может выполниться при восстановлении любым пользователем и назначить его владельцем всех объектов, следует задать параметр -O.

-r
--roles-only
Выгружать только роли, без баз данных или табличных пространств.

-s
--schema-only
Выгружать только определения объектов (схемы), но не сами данные.

-S имя_пользователя
--superuser=имя_пользователя
Задать имя суперпользователя, которое будет использоваться при выключении триггеров. Имеет значение только вместе с параметром --disable-triggers. (Обычно лучше вместо этого запускать полученный скрипт от лица суперпользователя.)

-t
--tablespaces-only
Выгружать только табличные пространства, без баз данных или ролей.

-v
--verbose
Включает подробный режим. qhb_dumpall будет выводить в стандартный поток ошибок подробные комментарии к объектам и время начала/окончания выгрузки в файл дампа, а также сообщения о прогрессе выполнения. Если повторить этот ключ, в стандартный поток ошибок будут выводиться дополнительные отладочные сообщения. Этот параметр также действует и на qhb_dump.

-V
--version
Вывести версию qhb_dumpall и завершиться.

-x
--no-privileges
--no-acl
Запретить выгрузку прав доступа (команды GRANT/REVOKE).

--binary-upgrade
Этот параметр предназначен для использования утилитами обновления на месте. Применение в других целях не рекомендуется и не поддерживается. Поведение параметра может быть изменено в будущих версиях без предварительного уведомления.

--column-inserts
Выгружать данные в виде команды INSERT с явными указанием имен столбцов (INSERT INTO таблица (столбец, ...) VALUES ...). Восстановление при этом будет происходить очень медленно; в основном этот вариант оправдан при создании дампов, которые можно загружать в СУБД, отличные от QHB.

--disable-dollar-quoting
Этот параметр выключает возможность заключать тела функций в долларовые кавычки, заставляя использовать кавычки из стандартного синтаксиса SQL.

--disable-triggers
Этот параметр представляет интерес только при создании дампа, содержащего одни данные. Указывает qhb_dumpall включать в дамп команды для временного выключения триггеров на целевых таблицах во время восстановления данных. Применяется в ситуациях, когда существуют проверки ссылочной целостности или другие триггеры для таблиц, которые не стоит вызывать во время восстановления данных.

В настоящее время команды, создаваемые с параметром --disable-triggers, должны выполняться суперпользователем. Следовательно, необходимо задать имя суперпользователя с помощью флага -S или запустить полученный скрипт от лица суперпользователя.

--exclude-database=шаблон
Не выгружать базы данных, имена которых соответствуют шаблону. Задав ключ --exclude-database несколько раз, можно исключить имена по нескольким шаблонам. Значение шаблон интерпретируется по тем же правилам, что и шаблон в командах \d утилиты psql, поэтому несколько баз данных также можно исключить, вписав в шаблон подстановочные знаки. Используя подстановочные знаки, не забывайте при необходимости заключать шаблон в кавычки, чтобы эти знаки не разворачивала оболочка системы.

--extra-float-digits=число_цифр
Выгружать числа с плавающей запятой не с максимальной доступной точностью, а с заданным значением extra_float_digits. При обычном дампировании в целях резервного копирования этот параметр использовать не следует.

--if-exists
Использовать условные команды (т. е. добавлять предложение IF EXISTS) при удалении баз данных и других объектов. Этот параметр не работает без флага --clean.

--inserts
Выгрузить данные в виде команд INSERT (а не COPY). Восстановление при этом будет происходить очень медленно; в основном этот вариант оправдан при создании дампов, которые можно загружать в СУБД, отличные от QHB. Обратите внимание, что восстановление может полностью провалиться, если в таблицах был изменен порядок столбцов. Параметр --column-inserts безопаснее, но он работает еще медленнее.

--load-via-partition-root
При выгрузке данных партиции таблицы выводить команды COPY или INSERT, ссылающиеся на корневую таблицу в содержащей ее иерархии партиционирования, а не на саму эту партицию. В результате при загрузке данных для каждой строки будет заново выбираться подходящая партиция. Это может быть полезно при перезагрузке данных, когда на целевом сервере строки не всегда попадают в те же партиции, в которых они были на исходном сервере. Это возможно, например, когда у столбца разбиения текстовый тип и в двух системах по-разному определено правило сортировки, по которому упорядочивается этот столбец.

--lock-wait-timeout=время_ожидания
Не ждать бесконечно получения разделяемых блокировок таблиц в начале дампирования. Вместо этого выдать ошибку, если не удастся заблокировать таблицу за указанное время_ожидания. Время ожидания можно указать в любом из форматов, принимаемых командой SET statement_timeout.

--no-comments
Не выгружать комментарии.

--no-publications
Не выгружать публикации.

--no-role-passwords
Не сбрасывайте пароли для ролей. После восстановления роли получат пароль NULL, и не смогут пройти аутентификацию по паролю, пока им не будут назначены пароли. Поскольку при указании этого параметра значения пароля не нужны, информация о роли читается из представления каталога pg_roles, а не pg_authid. Как следствие, этот параметр также полезен, если доступ к pg_authid ограничен какой-либо политикой безопасности.

--no-security-labels
Не выгружать метки безопасности.

--no-subscriptions
Не выгружать подписки.

--no-sync
По умолчанию qhb_dumpall будет ждать, пока все файлы не будут надежно записаны на диск. Этот параметр заставляет qhb_dumpall завершаться без ожидания, т. е. выполняться быстрее, но впоследствии при неожиданном сбое операционной системы дамп может оказаться поврежден. В целом, этот параметр предназначен для тестирования, и его не следует использовать при дампировании данных из производственной инсталляции.

--no-tablespaces
Не формировать команды, создающие или выбирающие табличные пространства. При указании этого параметра все объекты будут создаваться в том табличном пространстве, которое устанавливается по умолчанию во время восстановления.

--no-toast-compression
Не формировать команды, задающие методы сжатия TOAST. При указании этого параметра все столбцы будут восстановлены с методом сжатия, выбранным по умолчанию.

--no-unlogged-table-data
Не выгружать содержимое нежурналируемых таблиц. Этот параметр не влияет на выгрузку определений (схем) таблиц; он лишь подавляет выгрузку данных из таблиц.

--on-conflict-do-nothing
Добавить предложения ON CONFLICT DO NOTHING в команды INSERT. Этот параметр недопустим, если также не указаны параметры --inserts или --column-insert.

--quote-all-identifiers
Принудительно экранировать все идентификаторы. Этот параметр рекомендуется при выгрузке базы данных с сервера QHB, основная версия которого отличается от версии qhb_dumpall, или когда дамп предназначен для загрузки на сервер с другой основной версией. По умолчанию qhb_dumpall заключает в кавычки только те идентификаторы, которые являются зарезервированными словами в ее собственной основной версии. Иногда это приводит к проблемам совместимости при работе с серверами других версий, в которых наборы зарезервированных слов могут немного отличаться. Использование параметра --quote-all-identifiers предотвращает подобные проблемы ценой ухудшения читаемости скрипта с дампом.

--rows-per-insert=число_строк
Выгружать данные в виде команд INSERT (вместо COPY). Этот параметр задает максимальное число строк в одной команде INSERT. Указанное значение должно быть больше нуля. Любая ошибка во время перезагрузки приведет к потере только тех строк INSERT, в которых возникли ошибки, а не всего содержимого таблицы.

--use-set-session-authorization
Выводить команды SET SESSION AUTHORIZATION, соответствующие стандарту SQL, вместо команд ALTER OWNER, чтобы назначить владельцев объекта. В итоге дамп будет более стандартизированным, но, в зависимости от истории объектов, может не восстановиться должным образом.

-?
--help
Показать справку об аргументах командной строки qhb_dumpall и завершиться.

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

-d строка_подключения
--dbname=строка_подключения
Задает параметры, используемые для подключения к серверу, в виде строки подключения

; они будут

переопределять любые конфликтующие параметры командной строки.

Этот параметр называется --dbname для согласованности с другими клиентскими приложениями, но поскольку qhb_dumpall необходимо подключаться ко многим базам данных, имя базы в строке подключения будет игнорироваться. Чтобы указать имя базы данных для начального подключения, которое будет использоваться для дампирования глобальных объектов и выявления других баз, которые необходимо дампировать, воспользуйтесь параметром -l.

-h хост
--host=хост
Задает имя хост-компьютера, на котором работает сервер. Если значение начинается со слэша, оно используется в качестве каталога для Unix-сокета. Значение по умолчанию берется из переменной среды PGHOST, если она установлена. В противном случае производится попытка подключения к Unix-сокету.

-l имя_бд
--database=имя_бд
Задает имя базы данных, через подключение к которой будет производиться дампирование глобальных объектов и выявление других баз, которые необходимо дампировать. Если имя не указано, будет использоваться база данных qhb, а если ее не существует — template1.

-p порт
--port=порт
Указывает TCP-порт или расширение файла локального Unix-сокета, через который сервер принимает подключения. Значение по умолчанию определяется переменной среды PGPORT, если она установлена, или числом, заданным при компиляции.

-U имя_пользователя
--username=имя_пользователя
Имя пользователя, под которым производится подключение.

-w
--no-password
Никогда не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, и пароль недоступен с помощью иных средств, таких как файл .pgpass, попытка подключения завершится неудачно. Этот параметр может быть полезен в пакетных заданиях и скриптах, где нет пользователя, чтобы ввести пароль.

-W
--password
Принудительно запрашивать пароль перед подключением к базе данных.

Это несущественный параметр, так как qhb_dumpall автоматически запросит пароль, если сервер требует аутентификацию по паролю. Однако чтобы выяснить это, qhb_dumpall потребуется дополнительная попытка подключения к серверу. В некоторых случаях имеет смысл ввести -W, чтобы исключить эту лишнюю попытку.

--role=имя_роли
Задает имя роли, которая будет использоваться для создания дампа. При указании этого параметра qhb_dumpall выполнит команду SET ROLE имя_роли после подключения к базе данных. Это полезно, когда проходящий проверку пользователь (указанному ключом -U) не имеет прав, требуемых qhb_dumpall, но может переключиться на роль, наделенную этими правами. В некоторых инсталляциях правила запрещают подключаться к серверу непосредственно от имени суперпользователя, и этот параметр позволяет создавать дампы, не нарушая их.

Переменные среды

PGHOST
PGOPTIONS
PGPORT
PGUSER
Параметры подключения по умолчанию.

PG_COLOR
Указывает, использовать ли цвет в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Примечания

Поскольку qhb_dumpall внутри себя вызывает qhb_dump, некоторые диагностические сообщения будут относиться к qhb_dump.

Параметр --clean может быть полезен, даже если вы собираетесь восстановить дамп из скрипта в новом кластере. Использование --clean позволяет скрипту удалять и пересоздавать встроенные базы данных qhb и template1, добиваясь того, чтобы эти базы сохранили свойства (например, локаль и кодировку), которые имели в исходном кластере. Без этого параметра эти базы сохранят свои свойства уровня базы данных, а также все уже существующее содержимое.

После восстановления рекомендуется запускать ANALYZE для каждой базы данных, чтобы оптимизатор получил актуальную статистику. Также можно запустить vacuumdb -a -z для анализа всех баз данных.

Не следует ожидать, что скрипт дампа будет выполняться совсем без ошибок. В частности, поскольку скрипт будет содержать команду CREATE ROLE для каждой роли, существующей в исходном кластере, при попытке создать суперпользователя для начальной загрузки он обязательно получит ошибку «role already exists» (роль уже существует), если только целевой кластер не был инициализирован с другим начальным именем суперпользователя. Эта ошибка безвредна и ее следует игнорировать. Использование параметра --clean, скорее всего, вызовет появления ряда дополнительных безобидных сообщений об ошибках, связанных с несуществующими объектами; их число можно уменьшить, добавив параметр --if-exists.

qhb_dumpall требует, чтобы все необходимые каталоги табличных пространств существовали до восстановления; в противном случае создание базы данных в нестандартном размещении завершится ошибкой.

Примеры

Выгрузка всех баз данных:

$ qhb_dumpall > db.out

Перезагрузить базы данных из этого файла можно вот так:

$ psql -f db.out qhb

Неважно, к какой базе данных вы здесь подключаетесь, поскольку скрипт, созданный qhb_dumpall, будет содержать соответствующие команды для создания и подключения к сохраненным базам данных. Исключением является случай, если вы указали ключ --clean, — тогда следует сначала подключиться к базе данных qhb; скрипт попытается немедленно удалить другие базы данных, но не сможет удалить базу, к которой вы подключены.

См. также

Обратитесь к описанию qhb_dump, чтобы подробнее узнать об условиях, при которых могут возникнуть проблемы.

qhb_isready - проверка состояние соединения с сервером QHB

Синтаксис

qhb_isready [FLAGS] [OPTIONS]

Описание

qhb_isready - это утилита для проверки состояния соединения с сервером базы данных QHB. Статус выхода определяет результат проверки соединения.

Параметры

FLAGS:

Аргумент | Описание
-?, --help | Показать справку об аргументах командной строки и выйти.
--no-timeout | Запустить приложение без тайм-аута, используется вместо -t 0s
-q, --quiet | Устанавливает уровень ведения журнала в Error, конфликтует с флагом verbose.
-V, --version | Вывести версию qhb_isready и выйти.
-v, --verbose | Устанавливает уровень ведения журнала в Debug (по умолчанию: Info).

OPTIONS:

Аргумент | Описание
-d, --dbname DBNAME | Определяет имя базы данных для подключения (env: PGDATABASE).
-h, --host HOST | Указывает имя хоста компьютера, на котором работает сервер. Если значение начинается с косой черты, оно используется как каталог для сокета Unix-домена. (env:PGHOST)
-p, --port PORT | Указывает порт TCP или расширение файла локального сокета Unix-домена, на котором сервер прослушивает соединения. По умолчанию используется значение переменной среды (env: PGPORT) или, если не установлено, значение порта, указанного во время компиляции, обычно 5432.
-t, --timeout TIMEOUT | Максимальное количество секунд ожидания при попытке соединения, поддерживает «человеческое время», например -t 3s.
-U, --username USERNAME | Подключение к базе данных с использованием имени пользователя вместо имени по умолчанию. (env: PGUSER)

Статус выхода

qhb_isready возвращает оболочке:

Код выхода | Условия
0 | сервер принимает соединения нормально
1 | сервер отклоняет соединения (например, во время запуска или неверно заданных аргументов: --no-existent-arg)
2 | не было ответа на попытку соединения
3 | попытка не была предпринята (например, из-за неверных параметров аргумента(или аргументов))

Примечания

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

Примеры

Стандартное использование:

$ qhb_isready echo $?
[INFO] /home/user/qhb/core_db/build/dbsockets:5432 - accepting connections
0

Запуск с параметрами подключения к неотвечающему кластеру QHB:

$  qhb_isready --host localhost --port 22; echo $?
[WARN] Unable to load login packet: Provided buffer (66560) is shorter than a packet length (body_length = 1397239086, packet_id = 83 (S), raw_length = 1397239090).
[ERROR] localhost:22 - connection rejected
1

Ошибка при попытке установить подключение

$ qhb_isready --host localhost --port 42; echo $?
[WARN] ConnectionError: failed to connect to 127.0.0.1:42: Failed to establish connection to 127.0.0.1:42: Connection refused (os error 111)
[ERROR] localhost:42 - no response
2

Запуск с некорректными параметрами

$ qhb_isready --host asd; echo $?
[WARN] Failed to lookup address asd: failed to lookup address information: Name or service not known
[ERROR] asd:5432 - incorrect parameters
3

qhb_receivewal - потоковые журналы записи с сервера QHB

qhb_receivewal - потоковые журналы записи с сервера QHB

Описание

qhb_receivewal используется для потоковой передачи журнала предварительной записи (Write Ahead Log, WAL) из работающего кластера QHB. Журнал упреждающей записи передается по протоколу потоковой репликации и записывается в локальный каталог. Этот каталог можно использовать в качестве расположения архива для восстановления с использованием восстановления на определенный момент времени (см. раздел Непрерывное архивирование и восстановление на момент времени (PITR)).

qhb_receivewal направляет журнал упреждающей записи в режиме реального времени, поскольку он генерируется на сервере, и не ожидает завершения сегментов, как это делает archive_command. По этой причине нет необходимости устанавливать archive_timeout при использовании qhb_receivewal.

В отличие от приемника WAL резервного сервера QHB, qhb_receivewal по умолчанию сбрасывает данные WAL только тогда, когда файл WAL закрыт. Опция --synchronous должна быть указана для сброса данных WAL в режиме реального времени. Так как qhb_receivewal не применяет WAL, вы не должны допускать, чтобы он стал синхронным резервом, когда synchronous_commit равен remote_apply. Если это произойдет, он окажется резервным, который никогда не догонит, и приведет к блокировке фиксации транзакции. Чтобы избежать этого, вы должны либо настроить подходящее значение для synchronous_standby_names, либо указать application_name для qhb_receivewal, который ему не соответствует, либо изменить значение synchronous_commit на что-то отличное от remote_apply .

Журнал предварительной записи передается по обычному соединению QHB и использует протокол репликации. Соединение должно быть установлено суперпользователем или пользователем, имеющим разрешения REPLICATION (см. раздел Атрибуты ролей), а qhb_hba.conf должен разрешить соединение репликации. Сервер также должен быть настроен с max_wal_senders, установленным достаточно высоко, чтобы оставить хотя бы один сеанс доступным для потока.

Если соединение потеряно или если оно не может быть изначально установлено, с не фатальной ошибкой, qhb_receivewal будет повторять попытку подключения бесконечно и восстанавливать потоковую передачу как можно скорее. Чтобы избежать этого, используйте параметр -n.

При отсутствии фатальных ошибок qhb_receivewal будет работать до тех пор, пока не прекратится сигналом SIGINT (Control + C).

Синтаксис

qhb_receivewal [OPTION] ...

Параметры

CONNECTION OPTIONS

Аргумент | Описание
-d, --dbname=CONNSTR | Имя базы данных для подключения.
-h, --host=HOSTNAME | Имя хоста, на котором работает сервер. Если значение начинается с косой черты, оно используется в качестве каталога для сокета домена Unix. Значение по умолчанию берется из переменной среды PGHOST, далее предпринимается попытка подключения через сокет домена Unix.
-p, --port=PORT | TCP порт или расширение файла локального сокета домена Unix. По умолчанию берётся переменная среды PGPORT, или порт заданный при компиляции.
-U, --username=NAME | Имя пользователя для подключения.
-w, --no-password | Не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, а пароль недоступен другими способами, такими как файл .pgpass, попытка подключения завершится неудачно. Эта опция может быть полезна в пакетных заданиях и сценариях, где нет ни одного пользователя для ввода пароля.
-W, --password | Автоматически запросить пароль, если сервер требует аутентификацию по паролю. Утилита будет пытаться подключиться, для аутентификации. В некоторых случаях стоит ввести -W чтобы избежать дополнительной попытки подключения.

OPTIONS

Аргумент | Описание
-D, --directory=DIR | Каталог для записи вывода. Обязательный параметр.
-E, --endpos=LSN | Автоматически останавливать репликацию и выходить с кодом выхода 0, когда приём достигает указанного номера LSN. Если есть запись с LSN, равным lsn, запись будет обработана.
--if-not-exists | Не генерировать ошибку, если при опции create-slot будет найден уже существующий слот с указанным именем.
-n, --no-loop | Завершить работу с ошибкой, и не выполнять новую попытку подключения при ошибках соединения.
--no-sync | Выключить принудительный сброс на диск WAL-журналов. Последующий сбой операционной системы может привести к повреждению сегментов WAL. Полезно для тестирования, но не следует использовать при архивации WAL в производственной среде. Эта опция несовместима с --synchronous.
-s, --status-interval=SECS | Интервал отправки пакетов состояния. Подробнее
-S, --slot=SLOTNAME | Требовать от qhb_receivewal использования существующего слота репликации. Подробнее
--synchronous | Записать данные WAL на диск сразу же после их получения. Подробнее
-v, --verbose | Включает подробный режим.
-V, --version | Вывести версию qhb_receivewal и выйти
-Z, --compress=0-9 | Включает gzip-сжатие журналов записи с опережением и указывает уровень сжатия (от 0 до 9, 0 - без сжатия и 9 - с наилучшим сжатием). Суффикс .gz будет автоматически добавлен ко всем именам файлов.
-?, --help | Показать справку об аргументах командной строки qhb_receivewal и выйти.

status interval

-s SECS, --status-interval=SECS

Задает количество секунд между пакетами состояния, отправляемыми обратно на сервер. Это позволяет упростить мониторинг прогресса с сервера. Нулевое значение полностью выключает периодические обновления статуса, хотя обновление по-прежнему будет отправляться по запросу сервера, чтобы избежать выключения по тайм-ауту. Значение по умолчанию составляет 10 секунд.

SLOTNAME

-S SLOTNAME, --slot=SLOTNAME Требовать от qhb_receivewal использования существующего слота репликации. Когда используется эта опция, qhb_receivewal будет сообщать серверу позицию сброса, указывая, когда каждый сегмент был синхронизирован с диском, чтобы сервер мог удалить этот сегмент, если в этом нет необходимости.

Когда клиент репликации qhb_receivewal настроен на сервере как синхронный резерв, то использование слота репликации сообщит серверу позицию сброса, но только когда файл WAL закрыт. Следовательно, такая конфигурация приведет к тому, что транзакции на первичном сервере ожидают долгое время и эффективно не будут работать удовлетворительно.

Опция --synchronous должна быть указана дополнительно для правильной работы.

synchronous

--synchronous Записать данные WAL на диск сразу после их получения. Также отправьте пакет состояния обратно на сервер сразу после очистки независимо от --status-interval.

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

Дополнительные опции

qhb_receivewal может выполнить одно из двух следующих действий для управления слотами физической репликации:

Параметр | Описание
--create-slot | Создать новый слот физической репликации с именем, указанным в параметре --slot, и выйти.
--drop-slot | Удалить слот репликации с именем, указанным в --slot, и выйти.

Статус Выхода

qhb_receivewal выйдет со статусом 0, когда завершится сигналом SIGINT . (Это нормальный способ завершить его. Следовательно, это не ошибка.) Для фатальных ошибок или других сигналов состояние выхода будет отличным от нуля.

Окружение

Эта утилита, как и большинство других утилит QHB, использует переменные окружения, поддерживаемые libpq .

Переменная окружения PG_COLOR указывает, использовать ли цвет в диагностических сообщениях. Возможные значения always, auto, never.

Примечания

При использовании qhb_receivewal вместо archive_command в качестве основного метода резервного копирования WAL настоятельно рекомендуется использовать слоты репликации. В противном случае сервер может свободно перезаписывать или удалять файлы журналов WAL перед их резервным копированием, поскольку он не имеет никакой информации ни от архива, ни от слотов репликации о том, как далеко заархивирован поток WAL. Однако обратите внимание, что слот репликации заполнит дисковое пространство сервера, если получатель не успевает за извлечением данных WAL.

qhb_receivewal сохранит разрешения группы для полученных файлов WAL, если разрешения группы включены в исходном кластере.

Примеры

Для потоковой передачи журнала предварительной записи с сервера mydbserver и его сохранения в локальном каталоге /var/lib/qhb/data/archive :

$ qhb_receivewal -h mydbserver -D /var/lib/qhb/data/archive

Смотрите Также

qhb_basebackup

qhb_recvlogical - управление потоками логического декодирования QHB

qhb_recvlogical - управление потоками логического декодирования QHB

Описание

qhb_recvlogical управляет слотами репликации с логическим декодированием и передает данные из таких слотов репликации.

Он создает соединение в режиме репликации, поэтому к нему применяются те же ограничения, что и для qhb_receivewal, плюс ограничения для логической репликации.

qhb_recvlogical не имеет эквивалента режимам просмотра и получения интерфейса SQL логического декодирования. Утилита отправляет подтверждения воспроизведения для данных в "ленивом режиме", по мере их получения и при чистом выходе. Чтобы проверить ожидающие данные в слоте, не используя их, используйте pg_logical_slot_peek_changes.

Синтаксис

qhb_recvlogical [option...]

Параметры

Для выбора действия необходимо указать хотя бы один из следующих параметров:

Возможные действия

--create-slot Создайте новый слот логической репликации с именем, заданным параметром --slot, используя плагин вывода, заданный параметром --plugin, для базы данных, заданной параметром --dbname.

--drop-slot Удалить слот репликации с именем, указанным параметром --slot, затем выйти.

--start Начать запись потока в слот логической репликации, указанный в параметре --slot, продолжая до сигнала отмены. Если поток изменений на стороне сервера заканчивается отключением или отключением сервера, повторите цикл, если не указан параметр --no-loop.

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

--create-slot и --start могут быть указаны вместе. --drop-slot не может быть объединено с другим действием.

Options

Следующие параметры командной строки управляют расположением и форматом вывода и другим поведением репликации:

Аргумент | Описание
-S, --slot=SLOTNAME | Имя слота логической репликации для режимов --create-slot, --create-slot, --drop-slot.
-E, --endpos=LSN | Автоматически останавливать репликацию и выходить с кодом выхода 0, когда приём достигает указанного номера LSN. Подробнее
-f, --file=FILE | Записать полученные и декодированные данные транзакций в файл. Используйте -(минус) для стандартного вывода (stdout).
-F --fsync-interval=SECS | Частота вызова fsync() Подробнее
--if-not-exists | Не создавать ошибку, если --create-slot указан, а слот с таким именем уже существует.
-I, --startpos=LSN | В режиме --start начать репликацию с указанного номера LSN. Игнорируется в других режимах.
-n, --no-loop | Завершить работу с ошибкой, и не выполнять новую попытку подключения при ошибках соединения.
-o, --option=NAME[=VALUE] | Передать пару Name:Value в плагин вывода, указание Value опционально. Существующие параметры и их поведение зависит от используемого выходного плагина.
-P, --plugin=PLUGIN | Использовать указанный плагин вывода логического декодирования при создании слота репликации. Не действует, если слот уже существует.
-s, --status-interval=SECS | Интервал отправки пакетов состояния. Аналогично параметру qhb_receivewal - status interval

End position

-E lsn, --endpos=lsn Автоматически останавливать репликацию и выходить с кодом выхода 0, когда приём достигает указанного номера LSN. Если есть запись с LSN, равным lsn, запись будет обработана.

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

fsync-interval

-F SECS, --fsync-interval=SECS Определяет, как часто qhb_recvlogical должен вызывать вызовы fsync() чтобы убедиться, что выходной файл безопасно записан на диск. Сервер иногда запрашивает у клиента очистку и сообщает серверу о положении очистки.

Этот параметр необходим для более частого выполнения сбросов. Задание интервала 0 выключает вызовы fsync() полностью, в то же время сообщая о ходе выполнения на сервер. В этом случае данные могут быть потеряны в случае сбоя.

CONNECTION OPTIONS

Следующие параметры командной строки управляют параметрами подключения к базе данных.

Аргумент | Описание
-d, --dbname=DBNAME | Имя базы данных для подключения.
-h, --host=HOSTNAME | Имя хоста, на котором работает сервер. Если значение начинается с косой черты, оно используется в качестве каталога для сокета домена Unix. Значение по умолчанию берется из переменной среды PGHOST, далее предпринимается попытка подключения через сокет домена Unix.
-p, --port=PORT | TCP порт или расширение файла локального сокета домена Unix. По умолчанию берётся переменная среды PGPORT, или порт заданный при компиляции.
-U, --username=NAME | Имя пользователя для подключения.
-w, --no-password | Не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, а пароль недоступен другими способами, такими как файл .pgpass, попытка подключения завершится неудачно. Эта опция может быть полезна в пакетных заданиях и сценариях, где нет ни одного пользователя для ввода пароля.
-W, --password | Автоматически запросить пароль, если сервер требует аутентификацию по паролю. Утилита будет пытаться подключиться, для аутентификации. В некоторых случаях стоит ввести -W чтобы избежать дополнительной попытки подключения.

COMMON OPTIONS

Аргумент | Описание
-?, --help | Показать справку об аргументах командной строки и выйти.
-V, --version | Вывести версию утилиты и выйти.
-v, --verbose | Включает подробный режим.

Окружение

Эта утилита, как и большинство других утилит QHB, использует переменные окружения, поддерживаемые libpq .

Переменная окружения PG_COLOR указывает, использовать ли цвет в диагностических сообщениях. Возможные значения always, auto, never .

Примечания

qhb_recvlogical сохранит разрешения группы для полученных файлов WAL, если разрешения группы включены в исходном кластере.

Смотрите также

qhb_receivewal

qhb_restore

qhb_restore - восстанавливает базу данных QHB из файла архива, созданного qhb_dump

Описание

qhb_restore - это утилита для восстановления базы данных QHB из архива, созданного qhb_dump в одном из текстовых форматов. Он выдаст команды, необходимые для восстановления базы данных до состояния, в котором она находилась на момент сохранения. Архивные файлы также позволяют qhb_restore избирательно выбирать то, что восстанавливается, или даже переупорядочивать элементы перед восстановлением. Архивные файлы предназначены для переноса между архитектурами.

qhb_restore может работать в двух режимах. Если указано имя базы данных, qhb_restore подключается к этой базе данных и восстанавливает содержимое архива непосредственно в базу данных. В противном случае сценарий, содержащий команды SQL, необходимые для перестройки базы данных, создается и записывается в файл или стандартный вывод. Этот вывод сценария эквивалентен формату вывода простого текста qhb_dump. Поэтому некоторые параметры, управляющие выводом, аналогичны параметрам qhb_dump .

Очевидно, что qhb_restore не может восстановить информацию, которой нет в файле архива. Например, если архив был создан с использованием опции « сбросить данные как команды INSERT »( [qhb_dump --insert] ), qhb_restore не сможет загружать данные с помощью операторов COPY.

Синтаксис

qhb_restore [connection-option...] [option...] [filename]

Параметры

qhb_restore принимает следующие аргументы командной строки.

Обратите внимание на то что для многих параметров существуют особенности использования и ознакомьтесь с расширенной справкой в разделе Дополнительная информация по параметрам.

COMMON OPTIONS

Аргумент | Описание
-?, --help | Показать справку об аргументах командной строки и выйти.
-V, --version | Вывести версию qhb_restore и выйти.
-v, --verbose | Включить подробный режим.

GENERAL OPTIONS

Аргумент
Описание

-f, --file=FILENAME
Укажите выходной файл для сгенерированного скрипта или для листинга (ключ -l). Используйте - для стандартного вывода.

-F, --format=c, d, t
Укажите формат архива. Обычно нет необходимости указывать формат, так как qhb_restore определит формат автоматически. Но если формат задаётся, допускается один из этих вариантов:

c

custom

Архив сохранён в специальном формате pg_dump.

d

directory

Архив сохранён в каталоге.

t

tar

Архив сохранён в формате tar.

-j, --jobs=NUM
Запустить восстановление дампа в NUM потоков. Подробнее.

CONNECTION OPTIONS

Аргумент | Описание
-d, --dbname=DBNAME | Имя базы данных для подключения. qhb_restore не использует переменную PGDATABASE.
-h, --host=HOSTNAME | Имя хоста, на котором работает сервер. Если значение начинается с косой черты, оно используется в качестве каталога для сокета домена Unix. Значение по умолчанию берется из переменной среды PGHOST, далее предпринимается попытка подключения через сокет домена Unix.
-p, --port=PORT | TCP порт или расширение файла локального сокета домена Unix. По умолчанию берётся переменная среды PGPORT, или порт заданный при компиляции.
-U, --username=USERNAME | Имя пользователя для подключения.
-w, --no-password | Не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, а пароль недоступен другими способами, такими как файл .pgpass, попытка подключения завершится неудачно. Эта опция может быть полезна в пакетных заданиях и сценариях, где нет ни одного пользователя для ввода пароля.
-W, --password | qhb_restore автоматически запросит пароль, если сервер требует аутентификацию по паролю. qhb_restore будет пытаться подключиться, для аутентификации. В некоторых случаях стоит ввести -W чтобы избежать дополнительной попытки подключения.
--role=ROLENAME | Задает имя роли, которая будет использоваться для восстановления. Подробнее

OPTIONS

Аргумент | Описание
-a, --data-only | Восстановить только данные, а не схему (определения данных). Данные таблицы, крупные объекты и значения последовательности восстанавливаются, если они присутствуют в архиве. Эта опция похожа, но по историческим причинам не идентична --section=data .
-c, --clean | Очистить (DROP) объекты базы данных перед их воссозданием. (Если не используется --if-exists это может привести к сообщениям об ошибках, если какие-либо объекты не присутствуют в целевой базе данных.)
-C, --create | Создать базу данных перед ее восстановлением. Подробнее
-e, --exit-on-error | Выйти, если при отправке команд SQL в базу данных возникла ошибка. По умолчанию работа продолжается, а количество ошибок отображается в конце восстановления.
-I index, --index=index | Восстановить определение только именованного индекса. Несколько индексов могут быть указаны с несколькими ключами -I.
-l, --list | Создать оглавление архива Подробнее
-L, --use-list=FILENAME | Восстановление по оглавлению Подробнее
-n, --schema=SCHEMA | Восстановить объекты только из указанной схемы. Несколько схем могут быть указаны с несколькими ключами -n . Комбинируется с опцией -t чтобы восстановить только определенную таблицу.
-N, --exclude-schema=SCHEMA | Не восстанавливать объекты из указанной схеме. Исключает несколько схем, с несколькими ключами -N. Ключ -N приоритетнее чем-n, при одновременном указании - схема исключается.
-O, --no-owner | Не выполнять команды для установки владельца объектов в соответствии с исходной базой данных. Подробнее
-P, --function=NAME(args) | Восстановить только указанную функцию. Указывайте имя функции и аргументы в точности, как они указаны в оглавлении файла дампа. Несколько функций могут быть указаны с несколькими ключами -P .
-s, --schema-only | Восстановить только схему (определения данных), а не данные. Подробнее
-S, --superuser=NAME | Имя суперпользователя, которое будет использоваться при выключении триггеров. Актуально, только для --disable-triggers.
-t, --table=NAME | Восстановить определение и/или данные только указанной таблицы. Подробнее
-T, --trigger=NAME | Восстановить только названый триггер. Несколько триггеров могут быть указаны с несколькими ключами -T.
-x, --no-privileges | Пропустить восстановление прав доступа (команды GRANT / REVOKE).
-1, --single-transaction | Выполнить восстановление как одну транзакцию (обернуть отправленные команды в BEGIN / COMMIT ). Гарантирует что все команды завершаться успешно, либо изменения не применяются. Эта опция подразумевает --exit-on-error.
--disable-triggers | Выключение триггеров при восстановлении. Подробнее
--enable-row-security | Восстанавливать данные из таблиц с защитой строк. Подробнее
--if-exists | Использовать условные команды (добавить IF EXISTS ) при очистке объектов базы данных. Не работает без --clean.
--no-comments | Не восстанавливать комментарии даже если архив содержит их.
--no-publications | Не восстанавливать публикации даже если архив содержит их.
--no-security-labels | Не восстанавливать защитные метки даже если архив содержит их.
--no-subscriptions | Не восстанавливать подписки даже если архив содержит их.
--no-tablespaces | Не восстанавливать табличные пространства. Все объекты будут восстановлены в табличном пространстве по умолчанию.
--no-data-for-failed-tables | Не восстанавливать данные в таблицы которые не удалось создать Подробнее
--section=SECTION | Восстановить только указанный раздел. Имя раздела может быть pre-data, data, post-data. Подробнее
--strict-names | Требовать строго соответствия схем и таблиц таковым в файле резервной копии. Подробнее
--use-set-session-authorization | Использовать стандартные SQL команды для назначения владельцев объектов. Подробнее

Дополнительная информация по параметрам

ROLENAME

--role=rolename

Указывает имя роли, которое будет использоваться для восстановления. Эта опция заставляет qhb_restore выдавать команду SET ROLE rolename после подключения к базе данных. Это полезно, когда аутентифицированному пользователю (указанному -U ) не хватает привилегий, необходимых для qhb_restore, но он может переключиться на роль с необходимыми правами. В некоторых установках есть политика, запрещающая вход в систему непосредственно от имени суперпользователя, и использование этого параметра позволяет выполнять восстановление без нарушения политики.

create

-C, --create

Создать базу данных перед ее восстановлением. Если также указан параметр --clean удалить и заново создать целевую базу данных перед подключением к ней.

С помощью qhb_restore --create также восстанавливаются комментарий базы данных, если он есть, и любые параметры переменной конфигурации, специфичные для этой базы данных, то есть любые ALTER DATABASE ... SET ... и ALTER ROLE ... IN DATABASE ... SET ... команды, которые упоминают эту базу данных. Права доступа к самой базе данных также восстанавливаются, если не указан параметр --no-acl .

Когда используется эта опция, база данных с именем -d используется только для выдачи начальных команд DROP DATABASE и CREATE DATABASE . Все данные восстанавливаются в базу данных по имени которое указано в архиве.

Параллельное восстановление

-j NUM, --jobs=NUM

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

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

Оптимальное значение для этого параметра зависит от аппаратной настройки сервера, клиента и сети. Факторы включают количество ядер ЦП и настройку диска. Как правило, можно установить параметр равным количеству ядер ЦП на сервере, но значения, превышающие это, во многих случаях также могут привести к более быстрому восстановлению. Конечно, слишком высокие значения приведут к снижению производительности из-за перегрузки.

Эта опция поддерживает только пользовательские форматы и форматы каталогов. Входными данными должен быть обычный файл или каталог (не, например, канал). Этот параметр игнорируется при создании сценария, а не при подключении напрямую к серверу базы данных. Кроме того, несколько заданий нельзя использовать вместе с опцией --single-transaction .

Оглавление архива

-l, --list

Вывести оглавление архива. Выходные данные этой операции могут использоваться как входные данные для опции -L. Обратите внимание, что если с -l используются переключатели фильтрации, такие как -n или -t, они ограничат перечисленные элементы.

-L FILENAME, --use-list=FILENAME

Восстановите только те элементы архива, которые перечислены в FILENAME, и восстановите их в порядке их появления в файле. Обратите внимание, что если с -L используются переключатели фильтрации, такие как -n или -t, они еще больше ограничат восстановленные элементы.

Входной файл обычно создается путем редактирования вывода предыдущей операции -l. Строки можно перемещать или удалять, а также закомментировать, поместив точку с запятой ( ; ) в начале строки. Смотрите ниже примеры.

no owner

-O, --no-owner

Не выполнять команды для установки владельца объектов в соответствии с исходной базой данных. По умолчанию qhb_restore выдает операторы ALTER OWNER или SET SESSION AUTHORIZATION чтобы установить владельца созданных элементов схемы. Эти операторы потерпят неудачу, если исходное соединение с базой данных установлено не суперпользователем (или пользователем, которому принадлежат все объекты в сценарии). С ключом -O любой пользователь может использоваться для начального соединения, и этот пользователь будет владеть всеми созданными объектами.

schema only

-s, --schema-only

Восстановить только схему (определения данных), а не данные, в той степени, в которой записи схемы присутствуют в архиве.

Эта опция является обратной к --data-only. Это похоже на, но по историческим причинам не идентично указанию --section=pre-data --section=post-data .

(Не путайте это с параметром --schema, который использует слово « схема » в другом значении.)

table

-t table, --table= table

Восстановить определение и / или данные только указанной таблицы. В этом случае «таблица» включает в себя представления, материализованные представления, последовательности и сторонние таблицы. Несколько таблиц можно выбрать, написав несколько ключей -t. Эта опция может быть объединена с опцией -n для указания таблиц в конкретной схеме.

Заметка
Если указан параметр -t, qhb_restore не пытается восстановить какие-либо другие объекты базы данных, от которых могут зависеть выбранные таблицы. Следовательно, нет гарантии, что восстановление определенной таблицы в чистую базу данных будет успешным.

Заметка
Этот флаг не ведет себя идентично флагу -t qhb_dump. В настоящее время в qhb_restore нет условий для подстановочных знаков, и вы не можете включить имя схемы в его -t. И хотя флаг -t qhb_dump также будет выгружать вспомогательные объекты (например, индексы) выбранных таблиц, флаг -t qhb_restore не включает такие вспомогательные объекты.

Выключение триггеров

--disable-triggers

Эта опция актуальна только при выполнении восстановления только данных. Опция инструктирует qhb_restore выполнять команды для временного выключения триггеров на целевых таблицах во время загрузки данных. Используйте, если есть проверки ссылочной целостности или другие триггеры для таблиц, которые не нужно вызывать во время восстановления данных.

В настоящее время команды, отправляемые для --disable-triggers должны выполняться как суперпользователь. Поэтому вы должны также указать имя суперпользователя с помощью -S или, предпочтительно, запустить qhb_restore как суперпользователь.

Защита строк

--enable-row-security

Эта опция актуальна только при восстановлении содержимого таблицы с защитой строк. По умолчанию qhb_restore выключит row_security, чтобы все данные были восстановлены в таблице. Если у пользователя недостаточно прав для обхода защиты строк, выдается ошибка. Этот параметр указывает qhb_restore включить вместо row_security значение, позволяющее пользователю попытаться восстановить содержимое таблицы с включенной защитой строк. Это может по-прежнему не работать, если пользователь не имеет права вставлять строки из дампа в таблицу.

Обратите внимание, что этот параметр также требует, чтобы дамп был в формате INSERT, так как COPY FROM не поддерживает безопасность строк.

no-data-for-failed-tables

--no-data-for-failed-tables

По умолчанию данные таблицы восстанавливаются, даже если команда создания таблицы не выполнена (например, потому что она уже существует). С этой опцией данные для такой таблицы пропускаются. Это поведение полезно, если целевая база данных уже содержит требуемое содержимое таблицы. Например, вспомогательные таблицы для расширений QHB, таких, как PostGIS, могут быть уже загружены в целевую базу данных; указание этого параметра предотвращает загрузку в них дубликатов или устаревших данных.

Этот параметр действует только при восстановлении непосредственно в базу данных, но не при выводе сценария SQL.

Section

--section=SECTION

Восстановить только указанный раздел. Имя раздела может быть pre-data, data, post-data. Эта опция может быть указана более одного раза, чтобы выбрать несколько разделов. По умолчанию восстанавливается все разделы.

Раздел данных содержит фактические данные таблиц, а также определения крупных объектов. Элементы после данных состоят из определений индексов, триггеров, правил и ограничений, отличных от проверенных проверочных ограничений. Элементы предварительных данных состоят из всех других элементов определения данных.

strict-names

--strict-names

Проверять в обязательном порядке, чтобы каждая схема ( -n / --schema ) и --schema таблицы (-t / --table ) соответствовали хотя бы одной схеме / таблице в файле резервной копии.

use-set-session-authorization

--use-set-session-authorization

Использовать стандартные команды SQL SET SESSION AUTHORIZATION вместо команд ALTER OWNER для назначения принадлежности объектов. Это делает скрипт более совместимым со стандартами, но в зависимости от истории объектов в дампе, восстановление может произойти не должным образом.

Окружение

qhb_restore не использует переменную PGDATABASE, если имя базы данных не указано.

PGHOST

PGOPTIONS

PGPORT

PGUSER

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

PG_COLOR

Указывает, использовать ли цвет в диагностических сообщениях. Возможные значения always, auto, never .

Эта утилита, как и большинство других утилит QHB, также использует переменные среды, поддерживаемые libpq.

Диагностика

Когда прямое соединение с базой данных указывается с помощью опции -d, qhb_restore внутренне выполняет операторы SQL. Если у вас возникли проблемы с запуском qhb_restore, убедитесь, что вы можете выбирать информацию из базы данных, используя, например, psql. Кроме того, будут применяться любые параметры подключения по умолчанию и переменные среды, используемые интерфейсной библиотекой libpq .

Примечания

Если в вашей установке есть какие-либо локальные дополнения к базе данных template1, будьте осторожны, чтобы загрузить вывод qhb_restore в действительно пустую базу данных; в противном случае вы можете получить ошибки из-за дублирования определений добавленных объектов. Чтобы создать пустую базу данных без каких-либо локальных дополнений, скопируйте из template0 не из template1, например:

CREATE DATABASE foo WITH TEMPLATE template0;

Ограничения

Ограничения qhb_restore подробно описаны ниже.

При восстановлении данных в существующей таблице и использовании опции --disable-triggers, утилита qhb_restore генерирует команды для выключения триггеров в пользовательских таблицах перед вставкой данных, а затем выполняет команды для их повторного включения после вставки данных. Если восстановление остановлено в середине, системные каталоги могут остаться в неправильном состоянии.

qhb_restore не может выборочно восстанавливать большие объекты; например, только для определенной таблицы. Если в архиве содержатся большие объекты, то все большие объекты будут восстановлены, или ни один из них, если они исключены с помощью -L, -t или других параметров.

Смотрите также документацию по qhb_dump для подробностей об ограничениях qhb_dump .

Файл дампа, созданный qhb_dump, не содержит статистику, используемую оптимизатором для принятия решений по планированию запросов. Поэтому целесообразно запускать ANALYZE после восстановления из дампа, чтобы обеспечить оптимальную производительность; см. раздел Обновление статистики планировщика и раздел Процесс «Автовакуум» для получения дополнительной информации.

Примеры

Предположим, мы сделали дамп базы данных mydb в пользовательском формате:

$ qhb_dump -Fc mydb > db.dump

Чтобы удалить базу данных и воссоздать ее из дампа:

$ dropdb mydb
$ qhb_restore -C -d qhb db.dump

База данных, указанная в -d может быть любой базой данных, существующей в кластере; qhb_restore использует его только для выдачи команды CREATE DATABASE для mydb. С ключом -C данные всегда восстанавливаются в имя базы данных, которое появляется в файле дампа.

Чтобы перезагрузить дамп в новую базу данных с именем newdb :

$ createdb -T template0 newdb
$ qhb_restore -d newdb db.dump

Обратите внимание, что мы не используем -C, а вместо этого подключаемся непосредственно к базе данных, в которую нужно восстановить. Также обратите внимание, что мы клонируем новую базу данных из template0 не template1, чтобы убедиться, что она изначально пуста.

Чтобы изменить порядок элементов базы данных, сначала необходимо вывести содержимое архива:

$ qhb_restore -l db.dump > db.list

Файл списка состоит из заголовка и одной строки для каждого элемента, например:

;
; Archive created at Mon Sep 14 13:55:39 2019
;     dbname: DBDEMOS
;     TOC Entries: 81
;     Compression: 9
;     Dump Version: 1.10-0
;     Format: CUSTOM
;     Integer: 4 bytes
;     Offset: 8 bytes
;     Dumped from database version: 8.3.5
;     Dumped by qhb_dump version: 8.3.8
;
;
; Selected TOC Entries:
;
3; 2615 2200 SCHEMA - public pasha
1861; 0 0 COMMENT - SCHEMA public pasha
1862; 0 0 ACL - public pasha
317; 1247 17715 TYPE public composite pasha
319; 1247 25899 DOMAIN public domain0 pasha

Точки с запятой начинаются с комментария, а цифры в начале строк относятся к внутреннему идентификатору архива, назначенному каждому элементу.

Строки в файле могут быть закомментированы, удалены и переупорядочены. Например:

10; 145433 TABLE map_resolutions qhb
;2; 145344 TABLE species qhb
;4; 145359 TABLE nt_header qhb
6; 145402 TABLE species_records qhb
;8; 145416 TABLE ss_old qhb

может использоваться в качестве входных данных для qhb_restore и восстанавливает только элементы 10 и 6 в следующем порядке:

 qhb_restore -L db.list db.dump

Смотрите Также

qhb_dump, qhb_dumpall, psql

qhb_verifybackup

qhb_verifybackup — проверить целостность базовой резервной копии кластера QHB

Синтаксис

qhb_verifybackup [параметр...]

Описание

Утилита qhb_verifybackup используется для проверки целостности резервной копии кластера баз данных, сделанной утилитой qhb_basebackup по манифесту backup_manifest, сгенерированному сервером во время резервного копирования. Эта копия должна храниться в формате «plain»; копию в формате «tar» можно проверить, разархивировав ее.

Важно отметить, что валидация, выполняемая qhb_verifybackup не включает и не может включать в себя все проверки, которые будет выполнять запущенный сервер при попытке воспользоваться резервной копией. Даже если вы пользуетесь этим инструментом, вам все равно следует выполнить тестовое восстановление данных и убедиться, что полученные в результате базы данных работают как положено и содержат корректные данные. Тем не менее, qhb_verifybackup может выявить многие распространенные проблемы, возникающие вследствие неисправности хранилища или ошибок пользователя.

Проверка резервной копии производится в четыре этапа. На первом этапе qhb_verifybackup читает файл backup_manifest. Если этот файл не существует, не может быть прочитан, неправильно оформлен или не соответствует своей внутренней контрольной сумме, qhb_verifybackup завершит работу с критической ошибкой.

На втором этапе qhb_verifybackup попытается проверить, что файлы данных, в настоящий момент хранящиеся на диске, в точности совпадают с файлами данных, которые собирается передать сервер, за некоторыми исключениями, описанными ниже. При этом будут выявлены все лишние и отсутствующие файлы, за некоторыми исключениями. На этом этапе будет игнорироваться наличие, отсутствие или какое-либо изменение файлов qhb.auto.conf, standby.signal и recovery.signal, поскольку ожидается, что эти файлы могут создаваться или меняться в процессе создания резервной копии. Также не будет учитываться файл backup_manifest в целевом каталоге и все содержимое каталога qhb_wal, несмотря на то, что эти файлы не будут перечислены в манифесте резервной копии. Проверяются только файлы; наличие или отсутствие каталогов контролируется только косвенно: если каталог отсутствует, все файлы, которые он должен содержать, тоже безусловно будут отсутствовать.

Далее qhb_verifybackup проверит контрольные суммы всех файлов, сравнивая их со значениями в манифесте, и выдаст ошибки для тех файлов, у которых вычисленная контрольная сумма не соответствует хранящейся в манифесте. Этот этап не выполняется для файлов, вызвавших ошибки на предыдущем этапе, поскольку уже известно, что они проблемные. Файлы, игнорировавшиеся на предыдущем этапе, игнорируются и на этом.

В конце qhb_verifybackup воспользуется манифестом для проверки наличия в журнале упреждающей записи всех записей, необходимых для восстановления из резервной копии, а также того, можно ли их прочитать и проанализировать. В файле backup_manifest содержится информация о том, какие записи WAL понадобятся, и qhb_verifybackup воспользуется этой информацией для утилиты qhb_waldump, чтобы синтаксически проанализировать эти записи. При этом будет передан флаг --quiet, так что qhb_waldump будет сообщать только об ошибках, не выводя каких-либо других сообщений. Хотя этот уровень проверки достаточен для выявления очевидных проблем, например, отсутствия файла или несоответствия внутренних контрольных сумм, он недостаточно масштабен для выявления всех возможных проблем, которые могут возникнуть при попытке восстановления базы данных. К примеру, этим методом нельзя обнаружить ошибку сервера, приводящую к появлению записей WAL с правильными контрольными суммами, но при этом задающих бессмысленные действия.

Обратите внимание, что при наличии дополнительных файлов WAL, не требующихся для восстановления из резервной копии, они не будут проверяться этим инструментом, хотя для этой цели можно отдельно вызвать qhb_waldump. Также обратите внимание, что проверка WAL зависит от версии: следует использовать версию qhb_verifybackup и, как следствие, qhb_waldump, соответствующую проверяемой копии. И наоборот, проверки целостности файлов данных должны работать с любой версией сервера, сгенерировавшего файл backup_manifest.

Параметры

Утилита qhb_verifybackup принимает следующие аргументы командной строки:

-e
--exit-on-error
Завершиться сразу после обнаружения проблемы с резервной копией. Если этот параметр не задан, qhb_verifybackup продолжит проверять копию даже после обнаружения первой проблемы и сообщит о всех проблемах как об ошибках.

-i путь
--ignore=путь
Игнорировать указанный файл или каталог, который должен быть задан относительным путем, при сравнении списка файлов данных, фактически присутствующих в резервной копии, со списком в файле backup_manifest. Если указывается каталог, этот параметр действует на все дерево подкаталогов с корнем в заданном расположении. Если относительный путь файла совпадает с указанным, предупреждения о лишних или отсутствующих файлах, разницах в размерах файлов или несоответствиях контрольных сумм выдаваться не будут. Этот параметр можно задать несколько раз.

-m путь
--manifest-path=путь
Использовать файл манифеста по указанному пути, а не файл, расположенный в корневом каталоге резервной копии.

-n
--no-parse-wal
Не пытаться проанализировать данные журнала упреждающей записи, которые понадобятся для восстановления из этой резервной копии.

-q
--quiet
Не выводить ничего, когда резервная копия успешно проходит проверку.

-s
--skip-checksums
Не проверять контрольные суммы файлов данных. При этом все равно будет проверяться наличие или отсутствие этих файлов и их размеры. С этим параметром проверка выполняется гораздо быстрее, так как нет необходимости читать сами файлы.

-w путь
--wal-directory=путь
Попытаться проанализировать файлы WAL, хранящиеся в указанном каталоге, а не в qhb_wal. Это может быть полезно, если резервная копия сохраняется отдельно от архива WAL.

Также доступны и другие параметры:

-V
--version
Вывести версию qhb_verifybackup и завершиться.

-?
--help
Показать справку об аргументах командной строки qhb_verifybackup и завершиться.

Примеры

Создать базовую резервную копию сервера mydbserver и проверить целостность этой копии:

$ qhb_basebackup -h mydbserver -D /usr/local/pgsql/data
$ qhb_verifybackup /usr/local/pgsql/data

Создать базовую резервную копию сервера mydbserver, переместить файл манифеста куда-нибудь во внешний каталог и проверить эту копию:

$ qhb_basebackup -h mydbserver -D /usr/local/pgsql/backup1234
$ mv /usr/local/pgsql/backup1234/backup_manifest /my/secure/location/backup_manifest.1234
$ qhb_verifybackup -m /my/secure/location/backup_manifest.1234 /usr/local/pgsql/backup1234

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

$ qhb_basebackup -h mydbserver -D /usr/local/pgsql/data
$ edit /usr/local/pgsql/data/note.to.self
$ qhb_verifybackup --ignore=note.to.self --skip-checksums /usr/local/pgsql/data

См. также

qhb_basebackup

reindexdb - переиндексировать базу данных QHB

reindexdb - переиндексировать базу данных QHB

Синтаксис

reindexdb [connection-option...] [option...] [ --schema | -S schema ] ...
    [ --table | -t table ] ... [ --index | -i index ] ... [dbname]

reindexdb [connection-option...] [option...] --all | -a

reindexdb [connection-option...] [option...] --system | -s [dbname]

Описание

reindexdb - это утилита для перестроения индексов в базе данных QHB.

reindexdb - это "обертка" над SQL командой REINDEX. Нет разницы между переиндексацией базы данных с помощью этой утилиты или же иным способом при обращении к серверу.

Параметры

reindexdb принимает следующие аргументы командной строки:

Аргумент | Описание
-a, --all | Переиндексировать все базы данных
--concurrently | Использовать параметр CONCURRENTLY. См. REINDEX для получения дополнительной информации
[-d] dbname, [--dbname=]dbname | Указывает имя базы данных для переиндексации. Если не указывать и не использовать параметр -a (или --all), имя базы данных считывается из переменной окружения PGDATABASE. Если не установлена переменная окружения, используется имя пользователя, указанное в параметрах подключения
-e, --echo | Выводить команды, которые reindexdb генерирует и отправляет на сервер
-i index, --index=index | Пересоздать конкретный индекс. Несколько индексов можно воссоздать, написав несколько ключей -i
-q, --quiet | Не отображать сообщения о прогрессе выполнения
-s, --system | Переиндексировать системные каталоги
-S schema, --schema=schema | Переиндексировать конкретную схему. Несколько схем можно переиндексировать, написав несколько ключей -S
-t table, --table=table | Переиндексировать конкретную таблицу. Несколько таблиц можно переиндексировать, написав несколько ключей -t
-v, --verbose | Показать подробную информацию во время процесса
-V, --version | Показать версию reindexdb и выйти
-?, --help | Показать справку об аргументах командной строки reindexdb и выйти

reindexdb также принимает следующие параметры командной строки для параметров подключения:

Аргумент | Описание
-h host, --host=host | Указывает имя или адрес компьютера, на котором работает сервер. Если значение начинается с косой черты, оно используется в качестве каталога для Unix-сокета
-p port, --port=port | Указывает порт TCP или расширение файла локального Unix-сокета, на котором сервер прослушивает соединения
-U username, --username=username | Имя пользователя
-w, --no-password | Не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, а пароль недоступен другими способами, такими как файл .pgpass, попытка подключения завершится неудачно. Эта опция может быть полезна в пакетных заданиях и сценариях, где нет ни одного пользователя для ввода пароля
-W, --password | Эта опция не является существенной, так как reindexdb автоматически запросит пароль, если сервер требует аутентификацию по паролю. Тем не менее, reindexdb потратит одну дополнительную попытку подключения для аутентификации. В некоторых случаях стоит ввести -W, чтобы не делать эту попытку
--maintenance-db=dbname | Задает имя базы данных для подключения, чтобы определить какие другие базы данных должны быть переиндексированы. Если не указано иное, будет использоваться база данных qhb, а если и её не существует, то будет использоваться template1

Окружение

PGDATABASE

PGHOST

PGPORT

PGUSER

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

PG_COLOR

Указывает, использовать ли цвет в диагностических сообщениях. Возможные значения always, auto, never .

Эта утилита, как и большинство других утилит QHB, также использует переменные окружения, поддерживаемые libpq.

Диагностика

В случае затруднений см. REINDEX и psql для обсуждения потенциальных проблем и сообщений об ошибках. Сервер базы данных должен работать на целевом хосте. Кроме того, при запуске утилиты, будут применяться параметры подключения и переменные окружения, которые используются библиотекой libpq.

Примечания

С помощью reindexdb может потребоваться несколько раз подключиться к серверу QHB, каждый раз запрашивая пароль. В таких случаях удобно иметь файл ~/.pgpass.

Примеры

Чтобы переиндексировать базу данных test:

$ reindexdb test`

Чтобы переиндексировать таблицу foo и индекса bar в базе данных abcd:

$ reindexdb --table=foo --index=bar abcd`

Смотрите Также

REINDEX

vacuumdb сборка мусора и анализ базы данных QHB

vacuumdb - сборка мусора и анализ базы данных QHB

Синтаксис

vacuumdb [connection-option...] [option...] [ --table | -t table [( column [,...] )] ] ... [dbname]

vacuumdb [connection-option...] [option...] --all | -a

Описание

vacuumdb утилита для очистки базы данных QHB. vacuumdb также генерирует внутреннюю статистику, используемую оптимизатором запросов QHB.

ⓘ
vacuumdb - это "обертка" над SQL командой VACUUM. Нет разницы между очисткой и анализом баз данных с помощью этой утилиты или же иным способом при обращении к серверу

Параметры

vacuumdb принимает следующие аргументы командной строки:

-a
--all

Обрабатывать все базы данных.

[-d] dbname
[--dbname=]dbname

Указывает имя базы данных, которая будет очищена или проанализирована. Если не указывать и не использовать параметр -a (или --all), имя базы данных считывается из переменной окружения PGDATABASE. Если не установлена переменная окружения, используется имя пользователя, указанное в параметрах подключения.

--disable-page-skipping

Выключить пропуск страниц на основе содержимого карты видимости.

-e
--echo

Выводить команды, которые vacuumdb генерирует и отправляет на сервер.

-f
--full

Выполнить «полную» очистку.

-F
--freeze

Агрессивно «замораживать» кортежи.

-j njobs
--jobs=njobs

Выполняет вакуум или анализ параллельно, запуская несколько (в количестве njobs) команд одновременно. Этот параметр сокращает время обработки, но также увеличивает нагрузку на сервер базы данных.

vacuumdb открывает njobs соединений с базой данных, поэтому убедитесь, что параметр max_connections достаточно высок, чтобы принять все соединения.

Обратите внимание, что использование этого режима вместе с параметром -f (FULL) может привести к сбоям из-за взаимоблокировки, если определенные системные каталоги обрабатываются параллельно.

--min-mxid-age mxid_age

Выполняет вакуум или анализ только для таблиц с возрастом идентификатора мультитранзакции не менее mxid_age. Этот параметр полезен для определения приоритетности таблиц для обработки, чтобы предотвратить зацикливание при обходе идентификатора мультитранзакции. (см. раздел Мультитранзакции и зацикливание).

Для этого параметра возраст идентификатора мультитранзакции для отношения является наибольшим из возрастов основного отношения и связанной с ним таблицы TOAST, если такая существует. Так как команды, выполняемые vacuumdb также будут при необходимости обрабатывать таблицу TOAST для отношений, их возрасты не нужно рассматривать по отдельности.

--min-xid-age xid_age

Выполняет вакуум или анализ только для таблиц с возрастом идентификатора транзакции не менее xid_age. Этот параметр полезен для определения приоритетности таблиц для обработки, чтобы предотвратить зацикливание идентификатора транзакции (см. раздел Предотвращение ошибок зацикливания идентификатора транзакции).

Для этого параметра возраст идентификатора транзакции для отношения является наибольшим из возрастов основного отношения и связанной с ним таблицы TOAST, если такая существует. Поскольку команды, выполняемые vacuumdb, также при необходимости будут обрабатывать таблицу TOAST для отношений, их возрасты не нужно рассматривать по отдельности.

-q
--quiet

Не отображать сообщения о прогрессе выполнения.

--skip-locked

Пропускать отношения, которые не удаётся немедленно заблокировать для обработки.

-t table[ (column [,...]) ]
--table=table[ (column [,...]) ]

Очистить или проанализировать конкретные таблицы. Имена столбцов могут быть указаны только вместе с --analyze или --analyze-only. Несколько таблиц можно очищать, написав несколько параметров -t.

Заметка
Если вы укажете столбцы, вам, вероятно, придется экранировать скобки в консоли. (См. Примеры ниже.)

-v
--verbose

Показать подробную информацию во время процесса.

-V
--version

Показать версию vacuumdb и выйти.

-z
--analyze

Рассчитать статистику для оптимизатора.

-Z
--analyze-only

Рассчитать статистику для оптимизатора (без вакуума).

--analyze-in-stages

Рассчитать статистику для оптимизатора (без вакуума), наподобие --analyze-only. Чтобы быстрее получить полезную статистику, выполняется в несколько этапов (в настоящее время в три этапа) с различными настройками конфигурации.

Этот параметр полезен для анализа базы данных, которая была заполнена путем восстановлением из дампа или с помощью утилиты qhb_upgrade. С ним vacuumdb попытается собрать некоторую статистику как можно быстрее, чтобы как можно скорее сделать базу данных пригодной для использования. А затем, рассчитать полную статистику на последующих этапах.

-?
--help

Показать справку об аргументах командной строки vacuumdb и выйти.

vacuumdb также принимает следующие аргументы командной строки для параметров подключения:

-h host
--host=host

Указывает имя или адрес компьютера, на котором работает сервер. Если значение начинается с косой черты, оно используется в качестве каталога для Unix-сокета.

-p port
--port=port

Указывает порт TCP или расширение файла локального Unix-сокета, на котором сервер прослушивает соединения.

-U username
--username=username

Имя пользователя для подключения как.

-w
--no-password

Не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, а пароль недоступен другими способами, такими как файл .pgpass, попытка подключения завершится неудачно. Эта опция может быть полезна в пакетных заданиях и сценариях, где нет ни одного пользователя для ввода пароля.

-W
--password

Эта опция не является существенной, так как vacuumdb автоматически запросит пароль, если сервер требует аутентификацию по паролю. Тем не менее, vacuumdb потратит одну дополнительную попытку подключения для аутентификации. В некоторых случаях стоит ввести -W, чтобы не делать эту попытку.

--maintenance-db=dbname

Задает имя базы данных для подключения, чтобы определить какие другие базы данных должны быть очищены или проанализированы. Если не указано иное, будет использоваться база данных QHB, а если и её не существует, то будет использоваться template1.

Окружение

PGDATABASE

PGHOST

PGPORT

PGUSER

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

PG_COLOR

Указывает, использовать ли цвета в диагностических сообщениях. Возможные значения always, auto, never.

Эта утилита, как и большинство других утилит QHB, также использует переменные среды, поддерживаемые libpq.

Диагностика

В случае затруднений см. VACUUM и psql для обсуждения потенциальных проблем и сообщений об ошибках. Сервер базы данных должен работать на целевом хосте. Кроме того, будут применяться любые параметры подключения по умолчанию и переменные среды, используемые интерфейсной библиотекой libpq.

Примечания

vacuumdb может потребоваться несколько раз подключиться к серверу QHB, каждый раз запрашивая пароль. В таких случаях удобно иметь файл ~/.pgpass.

Примеры

Очистка базы данных test:

$ vacuumdb test

Очистка и анализ для оптимизатора базы данных с именем bigdb:

$ vacuumdb --analyze bigdb

Чтобы очистить одну таблицу foo в базе данных с именем xyzzy и проанализировать один столбец таблицы для оптимизатора:

$ vacuumdb --analyze --verbose --table='foo(bar)' xyzzy

Смотрите Также

VACUUM

Серверные приложения QHB

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

initdb - создать новый кластер базы данных QHB

qhb_archivecleanup - очистить архивные файлы QHB WAL

qhb_checksums - включить, выключить или проверить контрольные суммы данных в кластере базы данных QHB

qhb_controldata - отображать управляющую информацию кластера базы данных QHB

qhb_ctl - инициализировать, запустить, остановить или управлять сервером QHB

qhb_resetwal - сбросить журнал предварительной записи и другую управляющую информацию кластера базы данных QHB

qhb_rewind - синхронизирует каталог данных QHB с другим каталогом данных, который был ответвлен от него

qhb_upgrade - обновить экземпляр сервера QHB

qhb_waldump - отображает WAL кластера базы данных QHB в удобочитаемом виде

initdb

initdb — создать новый кластер баз данных QHB

Синтаксис

initdb [параметр...][--pgdata|-D]каталог

Описание

Утилита initdb создает новый кластер баз данных QHB. Кластер — это коллекция баз данных, управляемых одним экземпляром сервера.

Создание кластера базы данных состоит из создания каталогов, в которых будут храниться данные базы данных, создания общих таблиц каталогов (таблиц, которые принадлежат всему кластеру, а не какой-то конкретной базе данных) и создания баз данных template1 и qhb. При последующем создании новой базы данных все данные будут скопированы из базы-шаблона template1. (Следовательно, все, что будет добавлено в template1, автоматически скопируется в каждую новую базу данных.) База данных qhb — это база данных по умолчанию, предназначенная для использования пользователями, утилитами и сторонними приложениями.

Хотя initdb попытается создать указанный каталог данных, ей может не хватить прав, если родительский каталог нужного каталога данных принадлежит суперпользователю root. В такой ситуации для запуска этой операции необходимо создать пустой каталог данных от имени root, затем назначить владельцем этого каталога пользователя базы данных с помощью chown, после чего выполнить su, чтобы стать пользователем базы данных и запустить initdb.

initdb должна выполняться от имени пользователя, которому будет принадлежать серверный процесс, так как серверу необходим доступ к файлам и каталогам, создаваемым initdb. Поскольку сервер не может запуститься от имени суперпользователя root, initdb тоже нельзя выполнять от его имени. (На самом деле в таком случае выполнение initdb будет отклонено.)

По соображениям безопасности новый кластер, созданный initdb, будет доступен только владельцу кластера по умолчанию. Параметр --allow-group-access позволяет читать файлы в кластере любому пользователю из группы владельца кластера. Это полезно для выполнения резервного копирования от имени непривилегированного пользователя.

initdb инициализирует локаль и кодировку набора символов баз данных кластера, которые будут использоваться по умолчанию. Кодировка, порядок сортировки (LC_COLLATE) и классы наборов символов (LC_CTYPE, например, заглавные, строчные буквы, цифры) можно задавать отдельно при создании новой базы данных. initdb определяет эти параметры для базы-шаблона template1, которая будет использоваться по умолчанию для всех остальных баз данных.

Чтобы изменить порядок сортировки по умолчанию или классы наборов символов, используются параметры --lc-collate и --lc-ctype. Порядок сортировки, отличный от C или POSIX, снижает производительность. По этим причинам при запуске initdb важно выбрать надлежащую локаль.

Остальные категории локалей можно изменить после запуска сервера. Также можно использовать параметр --locale, чтобы задать значения по умолчанию для всех категорий локалей, включая порядок сортировки и классы наборов символов. Все значения локалей сервера (lc_*) можно вывести командой SHOW ALL. Более подробную информацию см. в разделе Поддержка языковых стандартов.

Для изменения кодировки по умолчанию используется параметр --encoding. Более подробную информацию см. в разделе Поддержка кодировок.

Параметры

-A метод_аутентификации
--auth=метод_аутентификации
Этот параметр задает метод аутентификации по умолчанию для локальных пользователей, используемый в файле qhb_hba.conf (строки host и local). Утилита initdb предварительно внесет в qhb_hba.conf указанный метод аутентификации как для обычных, так и для репликационных соединений.

Не используйте trust, если не доверяете всем локальным пользователям в вашей системе. Режим trust используется по умолчанию для упрощения процесса установки.

--auth-host=метод_аутентификации
Этот параметр задает метод аутентификации для локальных пользователей, подключающихся по TCP/IP, используемый в qhb_hba.conf (строки host).

--auth-local=метод_аутентификации
Этот параметр задает метод аутентификации для локальных пользователей, подключающихся через Unix-сокет, используемый в qhb_hba.conf (строки local).

-D каталог
--pgdata=каталог
Этот параметр задает каталог, в котором должен храниться кластер базы данных. Это единственная информация, необходимая initdb, но ее можно не вводить, если установить переменную среды PGDATA. Этот вариант может быть удобен, поскольку впоследствии сервер базы данных (qhb) сможет найти каталог базы данных по этой переменной.

-E кодировка
--encoding=кодировка
Выбирает кодировку шаблона базы данных. Эта кодировка также будет использоваться по умолчанию для любой вновь создаваемой базы данных, если ее не переопределить. Значение по умолчанию устанавливается на основе указанной локали, а если ее не удалось определить, выбирается SQL_ASCII. Кодировки, поддерживаемые сервером QHB, описаны в подразделе Поддержка кодировок.

-g
--allow-group-access
Позволяет пользователям из группы владельца кластера читать все файлы кластера, создаваемые утилитой initdb.

-k
--data-checksums
Использовать контрольные суммы в страницах данных, чтобы помочь выявить сбои системы ввода/вывода, которые в противном случае остались бы незамеченными. Включение расчета контрольных сумм может повлечь за собой заметное снижение производительности. Если этот параметр установлен, контрольные суммы будут рассчитываться для всех объектов и во всех базах данных. Все ошибки контрольных сумм будут видны в представлении pg_stat_database. Подробную информацию см. в разделе Контрольные суммы данных.

--locale=локаль
Устанавливает локаль по умолчанию для кластера базы данных. Если этот параметр не указан, локаль наследуется от среды, в которой запускается initdb. Поддержка локали описана в разделе Поддержка языковых стандартов.

--lc-collate=локаль
--lc-ctype=локаль
--lc-messages=локаль
--lc-monetary=локаль
--lc-numeric=локаль
--lc-time=локаль
Схожи с --locale, но устанавливают локаль только в указанной категории.

--no-locale
Равнозначен флагу --locale=C.

-N
--no-sync
По умолчанию initdb ожидает, пока все файлы не будут надежно записаны на диск. Этот параметр позволяет initdb завершаться быстрее, без ожидания, но в последующем сбой операционной системы может привести к повреждению каталога данных. В целом, этот параметр полезен для тестирования, но при создании производственной инсталляции его применять не следует.

--no-instructions
По умолчанию initdb после всех сообщений выводит инструкции по запуску кластера. С этим параметром такие инструкции выводиться не будут. Этот параметр предназначен в первую очередь для применения на платформах, где initdb используется специальными программными средствами и где данные инструкции, скорее всего, окажутся некорректными.

--pwfile=имя_файла
Заставляет initdb читать пароль суперпользователя базы данных из файла. В качестве пароля берется первая строка этого файла.

-S
--sync-only
Безопасно записать все файлы базы данных на диск и завершиться. Никакие другие обычные операции initdb не выполняются.

-T конфигурация
--text-search-config=конфигурация
Устанавливает конфигурацию текстового поиска по умолчанию. За дополнительной информацией обратитесь к default_text_search_config.

-U имя_пользователя
--username=имя_пользователя
Устанавливает имя суперпользователя базы данных. По умолчанию это имя пользователя операционной системы, запустившего initdb. В действительности само по себе имя суперпользователя неважно, но этот параметр позволяет оставить привычное имя qhb, даже если имя пользователя операционной системы другое.

-W
--pwprompt
Заставляет initdb запросить пароль, который будет назначен суперпользователю базы данных. Если не планируется использовать аутентификацию по паролю, это не важно. В противном случае этот вариант аутентификации будет неприменим, пока не задан пароль.

-X каталог
--waldir=каталог
Этот параметр задает каталог, в котором должен храниться журнал упреждающей записи.

--wal-segsize=размер
Задает размер сегмента WAL в мегабайтах, то есть размер каждого отдельного файла в журнале WAL. По умолчанию размер составляет 16 мегабайт. Значение должно задаваться степенью 2 от 1 до 1024 (в мегабайтах). Этот параметр можно установить только во время инициализации и нельзя изменить потом.

Этот размер бывает полезно поменять при настройке глубины детализации трансляции или архивации WAL. Кроме того, в базах данных с большим объемом WAL значительное количество файлов WAL в каталоге может вызвать проблемы со стороны производительности и управления. Увеличение размера файлов WAL приведет к уменьшению их количества.

Также доступны и другие, реже используемые параметры:

-d
--debug
Вывести отладочные и другие сообщения загрузчика, менее интересные широкой публике. Загрузчик — это программа, которую initdb использует для создания таблиц каталога. С этим параметром генерируется большое количество отладочных и информационных сообщений.

--discard-caches
Запустить загрузчик с параметром debug_discard_caches=1. Это очень сильно замедлит выполнение и полезно только для углубленной отладки.

-L каталог
Указывает initdb, где следует искать входные файлы для инициализации кластера базы данных. Обычно это не требуется. В случае необходимости приложение само попросит явно указать их местоположение.

-n
--no-clean
По умолчанию, когда initdb выявляет, что ошибка помешала завершить создание кластера базы данных, она удаляет все созданные к тому моменту файлы. Этот параметр предотвращает удаление файлов, вследствие чего полезен для отладки.

Прочие параметры:

-V
--version
Вывести версию initdb и завершиться.

-?
--help
Показать справку об аргументах командной строки initdb и завершиться.

Переменные среды

PGDATA
Задает каталог, в котором будет храниться кластер базы данных; можно переопределить с помощью параметра -D.

PG_COLOR
Указывает, использовать ли цвета в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

TZ
Задает для созданного кластера базы данных часовой пояс по умолчанию. Ее значением должно быть полное название часового пояса (см. раздел Часовые пояса).

Примечания

initdb также можно вызвать командой qhb_ctl initdb.

См. также

qhb_ctl, qhb

qhb_bootstrap

qhb_bootstrap — создать новый кластер баз данных QHB.

Синтаксис

qhb_bootstrap [флаг...] [параметр...] каталог_данных

Описание

Утилита qhb_bootstrap создает новый кластер базы данных QHB. Кластер — это коллекция баз данных под управлением единого экземпляра сервера.

Инициализация кластера базы данных заключается в создании каталогов для хранения данных, формировании общих системных таблиц (относящихся ко всему кластеру, а не к какой-то отдельной базе) и создании баз данных template1 и qhb. Впоследствии все новые базы будут создаваться на основе шаблона template1 (все, что будет добавлено в template1, автоматически скопируется в каждую новую базу данных). База данных qhb — это база данных по умолчанию, предназначенная для использования пользователями, утилитами и сторонними приложениями.

Безопасность

qhb_bootstrap должна выполняться от имени пользователя, под которым будет запускаться сервер, так как серверу необходим полный доступ к файлам и каталогам, создаваемым qhb_bootstrap. Сервер не может запускаться от имени суперпользователя, поэтому выполнение утилиты qhb_bootstrap от его лица будет отклонено.

Из соображений безопасности новый кластер, созданный qhb_bootstrap, будет доступен только владельцу кластера. Флаг --allow-group-access позволяет читать файлы в кластере всем пользователям, входящим в группу владельца кластера. Это полезно для выполнения резервного копирования от имени непривилегированного пользователя.

Не используйте метод аутентификации trust, если не доверяете всем локальным пользователям в вашей системе. Режим trust используется по умолчанию для облегчения процесса установки.

Локализация

qhb_bootstrap инициализирует локали и кодировки баз данных кластера, которые будут использоваться по умолчанию. Кодировка, порядок сортировки (LC_COLLATE), классы наборов символов (LC_CTYPE, например, заглавные, строчные буквы, цифры) можно задавать отдельно при создании новой базы данных. qhb_bootstrap определяет параметры локали для шаблона template1, которые будут применяться по умолчанию для новых баз. Указанная локаль должна быть установлена в системе (locale -a).

Чтобы изменить порядок сортировки по умолчанию или классы наборов символов, используются параметры --lc-collate и --lc-ctype. Порядок сортировки, отличный от C или POSIX, оказывает влияние на производительность. Поэтому при выполнении qhb_bootstrap следует тщательно выбирать необходимую и достаточную локаль. См. статью «Ещё раз про collation в PostgreSQL».

Остальные категории локалей можно изменить и после запуска сервера. Также можно использовать параметр --locale, чтобы задать локаль для всех категорий одновременно, включая порядок сортировки и классы наборов символов. Все значения локалей сервера (lc_*) можно вывести командой SHOW ALL. Более подробную информацию можно найти в разделе Поддержка языковых стандартов.

Параметры

-A метод_аутентификации
--auth=метод_аутентификации
Этот параметр задает метод аутентификации по умолчанию для локальных пользователей, используемый в файле qhb_hba.conf (строки host и local). Значение по умолчанию — trust.

--auth-host=метод_аутентификации
Этот параметр задает метод аутентификации для локальных пользователей, подключающихся по TCP/IP, используемый в qhb_hba.conf (строки host). Возможные значения: trust, reject, scram_sha_256, md5, password, radius, pam, bsd, ldap, ident, gss, sspi, cert. По умолчанию значение берется из параметра --auth.

--auth-local=метод_аутентификации
Этот параметр задает метод аутентификации для локальных пользователей, подключающихся через Unix-сокет, используемый в qhb_hba.conf (строки local). Возможные значения: trust, reject, scram_sha_256, md5, password, radius, pam, bsd, ldap, peer. По умолчанию значение берется из параметра --auth.

-D каталог
--pgdata=каталог
Каталог хранения кластера. Это единственный обязательный параметр для утилиты qhb_bootstrap. Может быть задан как флагами -D/--data-dir, так и последним параметром, тогда флаги можно опустить. При этом его можно указать в переменной среды PGDATA, что будет удобным при дальнейшем использовании (QHB обращается к этой же переменной).

-E кодировка
--encoding=кодировка
Выбирает кодировку шаблона базы данных. Эта кодировка также будет использоваться по умолчанию для любой вновь создаваемой базы данных, если ее не переопределить. В настоящий момент поддерживается только UTF8. Значение по умолчанию — UTF8.

--locale=локаль
Устанавливает локаль по умолчанию для кластера базы данных. По умолчанию установлена нейтральная локаль C (C Locale).

--lc-collate=локаль
Устанавливает локаль для порядка сортировки строк. По умолчанию значение берется из параметра --locale.

--lc-ctype=локаль
Устанавливает локаль для классификации символов. По умолчанию значение берется из параметра --locale.

--lc-messages=локаль
Устанавливает язык сообщений. По умолчанию значение берется из параметра --locale.

--lc-monetary=локаль
Устанавливает локаль для форматирования валют. По умолчанию значение берется из параметра --locale.

--lc-numeric=локаль
Устанавливает локаль для форматирования чисел. По умолчанию значение берется из параметра --locale.

--lc-time=локаль
Устанавливает локаль для форматирования даты и времени. По умолчанию значение берется из параметра --locale.

--no-locale
Этот параметр равнозначен --locale=C, то установке нейтральной локали по умолчанию. Нельзя задать одновременно с параметром --locale.

--pwfile=имя_файла
Заставляет qhb_bootstrap читать пароль суперпользователя базы данных из первой строки заданного файла. Параметры pwprompt и pwfile являются взаимоисключающими. Если в параметрах auth-host или auth-local указан метод аутентификации, отличный от trust, то наличие одного из параметров pwprompt или pwfile является обязательным.

-L каталог
--share-dir=каталог
Указывает qhb_bootstrap, где следует искать входные файлы для развертывания кластера базы данных. По умолчанию это share в каталоге из параметра data-dir.

-T конфигурация
--text-search-config=конфигурация
Устанавливает конфигурацию текстового поиска по умолчанию. За дополнительной информацией обратитесь к default_text_search_config. Значение по умолчанию соответствует локали, выбранной в параметре lc_ctype (если задана), иначе simple.

-U имя_пользователя
--username=имя_пользователя
Устанавливает имя суперпользователя базы данных. По умолчанию это имя пользователя ОС, запустившего qhb_bootstrap. В действительности само по себе имя суперпользователя неважно, но этот параметр позволяет оставить привычное имя qhb, даже если имя пользователя ОС другое.

-X каталог
--waldir=каталог
Этот параметр задает каталог, в котором должен храниться журнал упреждающей записи. По умолчанию это pg_wal в каталоге из параметра data-dir.

--wal-segsize=размер
Задает размер сегмента WAL в мегабайтах, то есть размер каждого отдельного файла в журнале WAL. По умолчанию размер составляет 16 мегабайт. Значение должно задаваться степенью 2 от 1 до 1024 (в мегабайтах). Этот параметр можно установить только во время инициализации и нельзя изменить потом.

Этот размер бывает полезно поменять при тонкой настройке трансляции или архивации WAL. Кроме того, в базах данных с большим объемом WAL значительное количество файлов WAL в каталоге может вызвать проблемы со стороны производительности и управления. Увеличение размера файлов WAL приведет к уменьшению их количества.

Флаги

-d
--debug
Выводит отладочные и другие сообщения загрузчика. Загрузчик — это программа, которую qhb_bootstrap использует для создания таблиц каталога. С этим параметром генерируется большое количество отладочных и информационных сообщений. Значение по умолчанию — false (выключено).

-h
--help
Вывод справочной информации

-g
--allow-group-access
Позволяет пользователям из группы владельца кластера читать все файлы кластера, создаваемые утилитой qhb_bootstrap. Значение по умолчанию — false (выключено).

-k
--data-checksums
Использовать контрольные суммы в страницах данных для выявления сбоев при вводе/выводе, которые иначе останутся незамеченными. Включение расчета контрольных сумм может повлечь за собой заметное снижение производительности. Если этот параметр установлен, контрольные суммы будут рассчитываться для всех объектов и во всех базах данных. Все ошибки контрольных сумм будут видны в представлении pg_stat_database. Подробную информацию см. в разделе Контрольные суммы данных. Значение по умолчанию — false (выключено).

-n
--no-clean
По умолчанию, когда qhb_bootstrap выявляет ошибку на этапе развертывания кластера, она удаляет все созданные к тому моменту файлы. Этот параметр предотвращает удаление файлов, вследствие чего полезен для отладки. Значение по умолчанию — false (удалять файлы).

-N
--no-sync
По умолчанию qhb_bootstrap ожидает, пока все файлы не будут надежно записаны на диск. Этот параметр позволяет qhb_bootstrap завершаться быстрее, без ожидания, но в последующем сбой операционной системы может привести к повреждению каталога данных. В целом, этот параметр полезен для тестирования, но при создании производственной инсталляции его применять не следует. Значение по умолчанию — false (выключено).

-s
--show
Вывести конфигурацию без создания кластера. Никакие другие обычные операции qhb_bootstrap не выполняются. Значение по умолчанию — false (выключено).

-S
--sync-only
Безопасно записать все файлы базы данных на диск и завершиться. Никакие другие обычные операции qhb_bootstrap не выполняются. Значение по умолчанию — false (выключено).

-V
--version
Вывести текущую версию qhb_bootstrap.

-W
--pwprompt
Заставляет qhb_bootstrap запросить пароль, который будет назначен суперпользователю базы данных. Если не планируется использовать аутентификацию по паролю, это не важно. В противном случае этот вариант аутентификации будет неприменим, пока не задан пароль.

Переменные среды

PGDATA
Задает каталог, в котором хранится кластер базы данных; можно переопределить с помощью параметра -D.

PG_COLOR
Указывает, использовать ли цвета в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Примеры

Создать кластер в каталоге /qhb/qhb-data с параметрами по умолчанию:

qhb_bootstrap --data-dir /qhb/qhb-data

Создать кластер в каталоге /qhb/qhb-data с кодировкой ru_RU и шифрованием SCRAM-SHA-256 с выводом подробной отладочной информации:

qhb_bootstrap --data-dir /qhb/qhb-data --locale ru_RU --auth scram_sha_256 -d

См. также

qhb_ctl, qhb

qhb_archivecleanup

qhb_archivecleanup — вычистить архивные файлы WAL QHB.

Синтаксис

qhb_archivecleanup [параметр...] расположение_архива старейший_сохраняемый_файл_wal

Описание

Утилита qhb_archivecleanup предназначена для использования в качестве archive_cleanup_command для удаления архивных файлов WAL при работе на резервном сервере. Также qhb_archivecleanup можно использовать и как отдельную программу для очистки архивных файлов WAL.

Чтобы использовать qhb_archivecleanup на резервном сервере, добавьте в файл конфигурации qhb.conf следующую строку:

archive_cleanup_command = 'qhb_archivecleanup расположение_архива %r'

где расположение_архива — это каталог, из которого нужно удалить файлы сегментов WAL.

Чтобы использовать qhb_archivecleanup как отдельную программу, добавьте в файл конфигурации qhb.conf следующие строки:

archive_mode = on;
archive_command = 'test ! -f расположение_архива %f && cp %p расположение_архива %f';

Это относится только к основному серверу, так как именно он передает файлы журнала.

При использовании этой утилиты в качестве archive_cleanup_command из расположения_архива будут удалены все файлы WAL, логически предшествующие значению аргумента %r. Это сокращает количество файлов, которые необходимо сохранить, без потери возможности аварийного перезапуска. Использование этого варианта целесообразно, если расположение_архива указывает на область рабочих файлов конкретного резервного сервера, но не когда расположение_архива — каталог с архивом WAL для долговременного хранения, или когда несколько резервных серверов восстанавливаются из одного расположения архива.

При использовании этой утилиты как самостоятельной программы из каталога расположение_архива будут удалены все файлы WAL, логически предшествующие файлу старейший_сохраняемый_файл_wal. В этом режиме, если вы укажете имя файла с расширением .partial или .backup, старейший_сохраняемый_файл_wal будет определяться только по префиксу, без расширения. Благодаря такой интерпретации имени файла .backup будут корректно удалены все файлы WAL, заархивированные до определенной базовой копии. Например, следующая команда удалит все файлы старее файла WAL с именем 000000010000003700000010:

qhb_archivecleanup -d archive 000000010000003700000010.00000020.backup

qhb_archivecleanup:  keep WAL file "archive/000000010000003700000010" and later
qhb_archivecleanup:  removing file "archive/00000001000000370000000F"
qhb_archivecleanup:  removing file "archive/00000001000000370000000E"

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

Параметры

qhb_archivecleanup принимает следующие аргументы командной строки:

-d
Выводить подробные отладочные сообщения в stderr.

-n
Вывести имена файлов, которые будут удалены, в stdout (выполняет пробный запуск).

-V
--version
Вывести версию qhb_archivecleanup и завершиться.

-x расширение
Установить расширение, которое будет убрано из всех имен файлов до принятия решения об их удалении. Это обычно полезно для очистки архивов, которые были сжаты для хранения и поэтому имеют расширение, добавленное программой сжатия. Например: -x .gz.

-?
--help
Показать справку об аргументах командной строки qhb_archivecleanup и завершиться.

Переменные среды

Переменная среды PG_COLOR указывает, использовать ли цвета в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Примеры

В системах Linux или Unix можно использовать команду:

archive_cleanup_command = 'qhb_archivecleanup -d /mnt/standby/archive %r 2>>cleanup.log'

где каталог архива физически расположен на резервном сервере, так что команда archive_command обращается к нему по NFS, но для резервного сервера эти файлы локальные. Это команда будет:

выводить отладочную информацию в cleanup.log

удалять ставшие ненужными файлы из каталога архива

qhb_checksums

qhb_checksums — включить, выключить или проверить контрольные суммы данных в кластере базы данных QHB

Синтаксис

qhb_checksums [флаг...] [параметр...] --pgdata каталог_данных

В этом синтаксисе термины «флаг» и «параметр» объединены:

qhb_checksums [параметр...] [[ -D | --pgdata ]каталог_данных]

Описание

Утилита qhb_checksums позволяет проверить, включить или выключить контрольные суммы данных в кластере QHB. Перед запуском qhb_checksums сервер должен быть остановлен в штатном режиме. При проверке контрольных сумм код завершения равен нулю, если ошибок нет, и отличен от нуля, если обнаружена хотя бы одна ошибка контрольной суммы. При включении или выключении контрольных сумм ненулевой код завершения означает, что выполнить операцию не удалось.

У всех файлов таблиц есть заголовок, по умолчанию в нем в поле с контрольной суммой стоит значение 0, также в общем файле ControlFile «версия контрольных сумм» равна 0 (выключено). При первом запуске нужно включить контрольные суммы с помощью флага --enable, который установит версию контрольных сумм на 1 и изменит поле контрольной суммы во всех заголовках файлов таблиц базы данных (директории global, base, pg_tblspc). Если нужно проверить правильность контрольных сумм в файлах, программа запоминает значение контрольной суммы из файла, обнуляет его, считает новое, сравнивает и возвращает на место старое значение (если оно не совпало с подсчитанным, выводится ошибка с указанием ее расположения). Если нужно выключить контрольные суммы, программа меняет значение версии контрольных сумм на 0 и ничего не делает с файлами таблиц.

При проверке контрольных сумм сканируется каждый файл в кластере. При включении контрольных сумм каждый файл в кластере перезаписывается на месте. Выключение контрольных сумм обновляет только файл pg_control.

Параметры

-D каталог
--pgdata=каталог
Задает каталог, в котором хранится кластер базы данных (равнозначно указанию переменной среды, например: PGDATA=/tmp/qhb-data/).

-f файловый_узел
--filenode=файловый_узел
Проверить контрольные суммы только в отношении с указанным файловым_узлом.

Флаги

Принимаются также следующие параметры командной строки:

-c
--check
Запускает проверку контрольных сумм. Это режим по умолчанию, если не указан никакой другой.

-d
--disable
Выключает контрольные суммы.

-e
--enable
Включает контрольные суммы.

-h
--help
Показать справку об аргументах командной строки qhb_checksums и завершиться.

-N
--no-sync
По умолчанию qhb_checksums ожидает, пока все файлы не будут надежно записаны на диск. Этот параметр позволяет qhb_checksums завершаться быстрее, без ожидания, но в последующем сбой операционной системы может привести к повреждению каталога данных. В целом, этот параметр полезен для тестирования, но при создании производственной инсталляции его применять не следует.

-P
--progress
Включает вывод сообщений о прогрессе выполнения. Эти сообщения будут выводиться при проверке или включении контрольных сумм.

-v
--verbose
Выводить подробные сообщения, в том числе список всех проверенных файлов. Уровень отладки по умолчанию — Warn.

-V
--version
Вывести версию qhb_checksums и завершиться.

Переменные среды

PGDATA
Задает каталог, в котором хранится кластер базы данных; можно переопределить с помощью параметра -D.

PG_COLOR
Указывает, использовать ли цвета в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Примечания

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

При использовании репликации, которая выполняется путем непосредственного копирования блоков отношений на уровне файлов (например, с помощью qhb_rewind), включение или выключение контрольных сумм может привести к повреждению страниц в виде расхождения контрольных сумм, если эта операция не будет выполнена согласованно на всех узлах. Поэтому в таких случаях рекомендуется остановить все кластеры, для того чтобы одновременно переключить их в другой режим. Еще один безопасный метод — удаление всех резервных серверов, выполнение нужной операции на основном и последующее создание ведомых серверов заново.

Если qhb_checksums прерывается или процесс уничтожается при включении или выключении контрольных сумм, конфигурация контрольной суммы данных в кластере остается неизменной, и qhb_checksums можно перезапустить для выполнения той же операции.

Обработка ошибок

Ошибка Possibly encrypted block проявляется либо при шифровке файла, либо при очень серьезном его повреждении, подразумевая MismatchBlockChecksum.

Примеры

Включить контрольные суммы, в конце выводится статистика с количеством обработанных блоков и файлов, любая ошибка приводит к остановке и выходу с кодом 1:

qhb_checksums -e

Проверить уже включенные контрольные суммы; отношение к статистике и ошибкам такое же, как у --enable:

qhb_checksums -с

Выключить контрольные суммы, несмотря на любые ошибки, кроме Permission denied на /global/pg_control:

qhb_checksums -d

Примечание
--enable, --check и --disable — три главных аргумента, к которым могут быть применены следующие флаги:

--verbose для вывода отладочной информации;

--no-sync для выключения сброса изменений на диск;

--progress для отображения хода выполнения.

qhb_controldata

qhb_controldata — показать управляющую информацию кластера базы данных QHB

Синтаксис

qhb_controldata [параметр] [[ --pgdata | -D ] каталог_данных]

Описание

Утилита qhb_controldata выводит информацию, заданную в процессе выполнения initdb, например версию каталога. Она также показывает сведения о работе журнала упреждающей записи и контрольных точек. Эта информация относится ко всему кластеру, а не к какой-то отдельной базе данных.

Эту утилиту может запустить только пользователь, инициализировавший кластер, поскольку она требует права на чтения в каталоге хранения данных. Путь к этому каталогу можно указать в командной строке, или использовать переменную среды PGDATA. Эта утилита поддерживает параметры -V и --version, которые выводят версию qhb_controldata и прерывают выполнение, а также -? и --help, которые выдают справку по поддерживаемым аргументам.

Переменные среды

PGDATA
Расположение каталога данных по умолчанию

PG_COLOR
Указывает, использовать ли цвета в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

qhb_ctl

qhb_ctl — инициализировать, запустить, остановить или управлять сервером QHB

Синтаксис

qhb_ctl init[db] [-D каталог_данных] [-s] [-o параметры-initdb]

qhb_ctl start [-D каталог_данных] [-l имя_файла] [-W] [-t секунды] [-s] [-o параметры] [-p путь] [-c]

qhb_ctl stop [-D каталог_данных] [-m s[mart] | f[ast] | i[mmediate] ] [-W] [-t секунды] [-s]

qhb_ctl restart [-D каталог_данных] [-m s[mart] | f[ast] | i[mmediate] ] [-W] [-t секунды] [-s] [-o параметры] [-c]

qhb_ctl reload [-D каталог_данных] [-s]

qhb_ctl status [-D каталог_данных]

qhb_ctl promote [-D каталог_данных] [-W] [-t секунды] [-s]

qhb_ctl logrotate [-D каталог_данных] [-s]

qhb_ctl kill имя_сигнала id_процесса

Описание

qhb_ctl — это утилита для инициализации, запуска, остановки или перезапуска кластера базы данных QHB (qhb) или отображения состояния работающего сервера. Хотя сервер можно запустить вручную, qhb_ctl берет на себя такие задачи, как направление вывода в журнал и корректное выключение от терминала и группы процессов. Также она предоставляет удобные параметры для управления остановкой кластера.

Команда init, или initdb, создает новый кластер баз данных QHB, то есть коллекцию баз данных, которая будет управляться одним экземпляром сервера. Эта команда вызывает утилиту initdb. Подробную информацию см. в описании initdb.

Команда start запускает сервер. Сервер запускается в фоновом режиме, а его стандартный ввод связывается с /dev/null. В Unix-подобных системах стандартные вывод и ошибки по умолчанию посылаются в устройство стандартного вывода (не ошибок) qhb_ctl. Затем стандартный вывод qhb_ctl должен быть перенаправлен в файл или в другой процесс, например, программе ротации журналов rotatelogs; в противном случае qhb будет писать свой вывод в управляющий терминал (в фоновом режиме) и останется в группе процессов оболочки.

Команда stop останавливает сервер, который работает в указанном каталоге данных. Параметр -m позволяет выбрать один из трех методов остановки. Режим «Smart» запрещает новые подключения, а затем ожидает отключения всех активных клиентов и завершения всех текущих процессов резервного копирования. Если сервер находится в режиме горячего резерва, восстановление и потоковая репликация будут прерваны сразу после отключения всех клиентов. Режим «Fast» (выбирается по умолчанию) не ожидает отключения клиентов и завершает все текущие процессы резервного копирования. Все активные транзакции откатываются, клиенты принудительно отключаются, после чего сервер останавливается. Режим «Immediate» немедленно прерывает все серверные процессы, не выполняя процедуру штатной остановки. Этот вариант повлечет необходимость выполнить восстановление после сбоя при следующем запуске сервера.

Команда restart в сущности выполняет остановку с последующим запуском сервера. Это позволяет изменить параметры командной строки qhb или файла конфигурации, которые не вступят в силу без перезапуска сервера. Если при запуске сервера в командной строке были указаны относительные пути, команда restart может завершиться ошибкой, если только не вызывать qhb_ctl в том же каталоге, где производился предыдущий запуск сервера.

Команда reload просто отправляет серверному процессу qhb сигнал SIGHUP, заставляя его перечитать свои файлы конфигурации (qhb.conf, qhb_hba.conf и т. д.). Это позволяет изменять параметры файла конфигурации, которым для вступления в силу не требуется полный перезапуск сервера.

Команда status проверяет, работает ли сервер в указанном каталоге данных. Если да, отображаются PID сервера и параметры командной строки, которые использовались для его запуска. Если сервер не запущен, qhb_ctl возвращает код завершения 3. Если не указан доступный каталог данных, qhb_ctl возвращает код завершения 4.

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

Команда logrotate прокручивает файл журнала сервера. Подробнее о том, как использовать этот режим с внешними инструментами ротации журналов, см. в разделе Обслуживание файла журнала.

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

Параметры

-c
--core-files
Способствует генерированию файлов дампа памяти при сбоях сервера на платформах, где это возможно, снимая все мягкие ограничения ресурсов, установленные для файлов дампа. Это полезно при отладке и диагностике проблем, поскольку позволяет получить трассировку стека отказавшего серверного процесса.

-D каталог_данных
--pgdata=каталог_данных
Указывает расположение файлов конфигурации базы данных. Если этот параметр не указан, используется переменная среды PGDATA.

-l имя_файла
--log=имя_файла
Направляет вывод сообщений сервера в файл имя_файла. Если файла еще не существует, он создается. При этом у umask устанавливается значение 077, поэтому доступ к файлу журнала другим пользователям по умолчанию запрещен.

-m режим
--mode=режим
Задает режим остановки кластера. Значением режим может быть smart, fast или immediate (или первая буква этих вариантов). Если этот параметр не указан, по умолчанию выбирается режим fast.

-o параметры
--options=параметры
Задает параметры, которые будут переданы непосредственно команде qhb. Флаг -o можно указывать несколько раз, передавая все имеющиеся параметры.

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

-o параметры-initdb
--options=параметры-initdb
Задает параметры, которые будут переданы непосредственно команде initdb. Флаг -o можно указывать несколько раз, передавая все имеющиеся параметры.

Обычно параметры-initdb следует заключать в апострофы или кавычки, чтобы они гарантированно передавались группой.

-p путь
Указывает расположение исполняемого файла qhb. По умолчанию исполняемый файл qhb берется из того же каталога, что и qhb_ctl, или, если это невозможно, из встроенного каталога установки. Использовать этот параметр может понадобиться, только если вы делаете что-то необычное или получаете сообщения, что исполняемый файл qhb не найден.

В режиме init этот параметр аналогичным образом указывает расположение исполняемого файла initdb.

-s
--silent
Выводить только сообщения об ошибках, без информационных сообщений.

-t секунды
--timeout=секунды
Задает максимальное время (в секундах) ожидания завершения операции (см. параметр -w). По умолчанию используется значение переменной среды PGCTLTIMEOUT или, если оно не задано, 60 секунд.

-V
--version
Вывести версию qhb_ctl и завершиться.

-w
--wait
Ждать завершения операции. Поддерживается (и действует по умолчанию) для команд start, stop, restart, promote и register.

В процессе ожидания qhb_ctl несколько раз проверяет PID-файл сервера, переключаясь в неактивный режим на короткое время между проверками. Запуск считается завершенным, когда PID-файл указывает на то, что сервер готов принимать подключения. Остановка считается завершенной, когда сервер удаляет свой PID-файл. Утилита qhb_ctl возвращает код завершения в зависимости от успеха запуска или остановки.

Если операция не завершается за отведенное время ожидания (см. параметр -t), то qhb_ctl завершается с ненулевым кодом выхода. Но обратите внимание, что при этом операция может продолжаться в фоновом режиме и в конечном итоге завершиться успешно.

-W
--no-wait
Не ждать завершения операции. Этот параметр противоположен параметру -w.

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

-?
--help
Показать справку об аргументах командной строки qhb_ctl и завершиться.

Если заданный параметр допустим, но не применим к выбранному режиму работы, qhb_ctl его игнорирует.

Переменные среды

PGCTLTIMEOUT
Максимальное время (в секундах) ожидания запуска или остановки сервера по умолчанию. Если не задано, то по умолчанию это время составляет 60 секунд.

PGDATA
Расположение каталога данных по умолчанию.

Для большинства режимов qhb_ctl требуется знать расположение каталога данных; поэтому если не задана переменная PGDATA, параметр -D является обязательным.

Список дополнительных переменных, влияющих на сервер, см. в описании qhb.

Файлы

qhbmaster.pid
qhb_ctl проверяет этот файл в каталоге данных, чтобы определить, работает ли сервер в настоящий момент.

postmaster.opts
Если этот файл существует в каталоге данных, qhb_ctl (в режиме restart) передаст его содержимое в качестве аргументов qhb, если это не переопределено параметром -o. Содержимое этого файла также отображается в режиме status.

Примеры

Запуск сервера

Запуск сервера и ожидание момента, когда он начнет принимать подключения:

$ qhb_ctl start

Запуск сервера на порту 5433 и без fsync:

$ qhb_ctl -o "-F -p 5433" start

Остановка сервера

Чтобы остановить сервер, выполните:

$ qhb_ctl stop

Параметр -m позволяет управлять тем, как сервер будет остановлен:

$ qhb_ctl stop -m smart

Перезапуск сервера

Перезапуск сервера практически равносилен его остановке и повторному запуску, за исключением того, что по умолчанию qhb_ctl сохраняет и повторно использует параметры командной строки, которые были переданы ранее запущенному экземпляру. Чтобы перезапустить сервер, используя те же параметры, с какими он был запущен ранее, выполните:

$ qhb_ctl restart

Но если указан параметр -o, он заменяет все предыдущие параметры. Чтобы перезапустить сервер на порту 5433 и выключить fsync при перезапуске, используйте:

$ qhb_ctl -o "-F -p 5433" restart

Отображение состояния сервера

Вот пример вывода состояния qhb_ctl:

$ qhb_ctl status

qhb_ctl: server is running (PID: 13718)
/usr/local/qhb/bin/qhb "-D" "/var/lib/qhb/data" "-p" "5433" "-B" "128"

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

См. также

initdb, qhb

qhb_resetwal

qhb_resetwal - очищает журнал предзаписи (WAL) и другую управляющую информацию кластера базы данных QHB

Синтаксис

qhb_resetwal [ --force | -f ] [ --dry-run | -n ] [option...] [ --pgdata | -D ] datadir

Описание

qhb_resetwal очищает журнал предзаписи (WAL) и, при необходимости, сбрасывает некоторую другую управляющую информацию, хранящуюся в файле pg_control. Эта функция бывает необходима, когда эти файлы повреждены. Её следует использовать как крайнюю меру, когда сервер не может запуститься из-за таких повреждений.

После выполнения этой команды станет возможным запустить сервер, но имейте в виду, что база данных может содержать несогласованные данные из-за частично зафиксированных транзакций. Вы должны немедленно выгрузить ваши данные, запустить initdb и восстановить данные. После этого проверьте согласованность базы данных и при необходимости внесите коррективы.

Эта утилита может быть запущена только тем пользователем, который установил сервер, поскольку ей требуется доступ на чтение/запись к каталогу данных. По соображениям безопасности вы должны указать каталог данных в командной строке. qhb_resetwal не использует переменную окружения PGDATA.

Если qhb_resetwal выводит сообщение о том, что он не может определить действительные данные из pg_control, вы можете принудительно запустить его, указав параметр -f (--force). В этом случае, отсутствующие данные будут заменены наиболее вероятными значениями. Для большинства полей это нормально, но может потребоваться ручное вмешательство для: следующего значения OID, следующего ID транзакции и времени, следующего ID мультитранзакции и смещения и начальной позиции WAL. Эти поля могут быть указаны с помощью параметров, описанных ниже. Если вы не можете определить правильные значения для всех этих полей, можно использовать -f, однако, к восстановленной базе данных следует относиться с еще большим подозрением, чем обычно: нужно будет немедленно выгрузить и затем восстановить данные. Не выполняйте никакие операции по изменению данных до того, как вы создадите дамп, так как любое такое действие может усугубить повреждение.

Параметры

-f
--force

Принудительное выполнение qhb_resetwal даже если он не может получить приемлемые данные из pg_control, как объяснено выше.

-n
--dry-run

Вывести значения, восстановленные из pg_control и значения, которые должны быть изменены, и выйти ничего не изменяя. В основном используется как инструмент отладки, но он также может быть полезен для проверки корректности параметров, прежде чем позволить qhb_resetwal начать работу по-настоящему.

-V
--version

Показать версию и выйти.

-?
--help

Показать справку и выйти.

Следующие параметры нужны только тогда, когда qhb_resetwal не может определить подходящие значения, читая pg_control. Безопасные значения могут быть определены, как описано ниже. Для значений, которые принимают числовые аргументы, с помощью префикса 0x могут быть указаны шестнадцатеричные значения.

-c xid, xid
--commit-timestamp-ids=xid, xid

Вручную установить самые старые и новейшие ID транзакций, для которых можно получить время фиксации.

Безопасное значение для самого старого ID транзакции, для которого можно получить время фиксации, можно определить найдя наименьшее в числовом виде имя файла в подкаталоге pg_commit_ts каталога данных. И наоборот, безопасное значение для новейшего ID транзакции, для которого можно получить время фиксации (вторая часть), можно определить найдя наибольшее в числовом виде имя файла. Числа в именах файлов представлены в шестнадцатеричном формате.

-e xid_epoch
--epoch=xid_epoch
Вручную установить эпоху идентификатора следующей транзакции.

Эпоха идентификатора транзакции фактически не сохраняется нигде в базе данных, кроме поля, установленного параметром qhb_resetwal, поэтому любое значение будет допустимым в отношении самой базы данных. Возможно, вам придется настроить это значение, чтобы обеспечить правильную работу систем репликации, таких как Slony-I и Skytools, - для такого случая соответствующее значение должно быть получено из состояния нижележащей реплицированной базы данных.

-l walfile
--next-wal-file=walfile

Вручную установить начальную позицию WAL, указав имя файла следующего сегмента WAL.

Имя файла следующего сегмента WAL должно быть больше, любых других имен файлов сегментов WAL, которые в настоящее время находятся в подкаталоге pg_wal каталога данных. Эти имена также в представлены шестнадцатеричном виде и состоят из трех частей. Первая часть является «ID линии времени» и обычно должна оставаться неизменной. Например, если 00000001000000320000004A является самой большой записью в pg_wal, используйте -l 00000001000000320000004B или большее число.

Обратите внимание, что при использовании нестандартных размеров сегментов WAL числа в именах файлов WAL отличаются от номеров LSN, выдаваемых системными функциями и представлениями. Этот параметр принимает имя файла WAL, а не LSN.

Заметка
Сам qhb_resetwal просматривает файлы в pg_wal и по умолчанию выбирает для параметра -l значение идущее следующим после найденного. Поэтому задавать параметр -l вручную необходимо только в том случае, если вам известно о существовании сегментов WAL, которые в данный момент отсутствуют в pg_wal, например если лежат в отдельном архиве, либо если содержимое pg_wal полностью утеряно.

-m mxid, mxid
--multixact-ids=mxid, mxid

Вручную установить ID следующей и самой старой мультитранзакции.

Безопасное значение для следующего идентификатора мультитранзакции может быть определено путем поиска самого большого в числовом виде имени файла в подкаталоге pg_multixact/offsets в каталоге данных. К найденному числу надо прибавить один и умножить на 65536 (0x10000). И наоборот, безопасное значение идентификатора самой старой мультитранзакции (вторая часть -m) можно определить, найдя наименьшее в числовом виде имя файла в том же каталоге и его умножив на 65536. Имена файлов представлены в шестнадцатеричном формате, поэтому самый простой способ умножить это добавить четыре нуля к имени файлу.

-o oid
--next-oid=oid

Вручную установить следующий OID.

Не существует сравнительно простого способа определить следующий за наибольшим из существующих значений OID, но, к счастью, это не критично.

-O mxoff
--multixact-offset=mxoff

Вручную установить смещение следующей мультитранзакции.

Безопасное значение может быть определено, найдя наибольшее в числовом виде имя файла в подкаталоге pg_multixact/members каталога данных, прибавив один и умножив на 52352 (0xCC80). Имена файлов представлены в шестнадцатеричном формате. Простого рецепта, как для других параметров с добавлением нулей, здесь нет.

--wal-segsize=wal_segment_size

Установить новый размер сегмента WAL в мегабайтах. Значение должно быть степенью 2 от 1 до 1024 (мегабайт). Смотрите тот же параметр в initdb для получения дополнительной информации.

Заметка
В то время как qhb_resetwal устанавливает начальный адрес WAL, превышающий номер последнего существующего файла сегмента WAL, при некоторых изменениях размера предыдущие имена файлов WAL могут использоваться повторно. Вместе с этим параметром, рекомендуется использовать -l, чтобы вручную установить начальный адрес WAL, если наложение имен файлов WAL вызывает проблемы для вашей стратегией архивирования.

-x xid
--next-transaction-id=xid

Вручную установить идентификатор следующей транзакции.

Безопасное значение можно определить, найдя наибольшее в числовом виде имя файла в подкаталоге pg_xact каталога данных, прибавив один и умножив на 1048576 (0x100000). Обратите внимание, что имена файлов представлены в шестнадцатеричном формате. Обычно проще всего указать значение параметра в шестнадцатеричном формате. Например, если 0011 наибольшее значение в pg_xact, то подходящим значение будет -x 0x1200000 (пять последних нулей обеспечивают правильный множитель).

Окружение

PG_COLOR

Указывает, использовать ли цвета в диагностических сообщениях. Возможные значения always, auto, never.

Примечания

Эту команду нельзя использовать во время работы сервера. qhb_resetwal не станет запускаться, при обнаруженном блокирующем файле в каталоге хранения данных. Если произошел сбой сервера, блокирующий файл может остаться в системе; в этом случае вы можете вручную удалить его, чтобы запустить qhb_resetwal. Но прежде чем сделать это, дважды удостоверьтесь, что серверный процесс не запущен.

qhb_resetwal работает только с серверами той же основной версии.

Смотрите также

qhb_controldata

qhb_rewind

qhb_rewind - синхронизирует каталог данных QHB с другим каталогом данных, который был ответвлён от него

Синтаксис

qhb_rewind [option...] { -D | --target-pgdata } directory { --source-pgdata=directory | --source-server=connstr }

Описание

qhb_rewind - это инструмент для синхронизации кластера QHB с другой копией того же кластера после того, как временные линии кластеров разошлись. Типичным сценарием является возвращение к работе после отказа бывшего главного сервера в качестве ведомого к другому серверу, ставшего новым главным.

Результат эквивалентен замене целевого каталога данных на исходный. Копируются только измененные блоки из файлов отношений; все остальные файлы копируются полностью, включая файлы конфигурации. Преимущество qhb_rewind перед созданием новой базовой резервной копии или таких инструментов, как rsync, заключается в том, что qhb_rewind не требует чтения неизмененных блоков в кластере. Это делает синхронизацию намного быстрее, когда база данных большая, и только небольшая часть блоков отличается между кластерами.

qhb_rewind анализирует историю линии времени исходного и целевого кластеров, чтобы определить точку их расхождения, и ожидает найти WAL в каталоге pg_wal целевого кластера, pg_wal до точки расхождения. Точку расхождения можно найти либо на целевой или на исходной временной линии, либо у их общего предка. В типичном сценарии отработки отказа, когда целевой кластер был отключен вскоре после расхождения, это не проблема, но если целевой кластер работал в течение длительного времени после расхождения, старые файлы WAL могут быть удалены. В этом случае их можно вручную скопировать из архива WAL в каталог pg_wal или считать при запуске, настроив primary_conninfo или restore_command. Использование qhb_rewind не ограничивается переключением при отказе. Например, резервный сервер может быть повышен и выполняет несколько записывающих транзакций, затем, после синхронизации, снова становится резервным.

Когда целевой сервер запускается впервые после запуска qhb_rewind, он переходит в режим восстановления и воспроизводит все изменения из WAL, сгенерированные на исходном сервере, после точки расхождения. Если какие-то сегменты WAL не доступны на исходном сервере при запуске qhb_rewind, и, следовательно, не могли быть скопированы, то их необходимо предоставить при запуске целевого сервера. Это можно сделать, создав файл recovery.signal в целевом каталоге данных и настроив подходящую команду restore_command в qhb.conf.

qhb_rewind требует, чтобы на целевом сервере был включен режим wal_log_hints в qhb.conf, либо включены контрольные суммы данных, когда кластер был инициализирован с помощью initdb. Ни один из этих режимов в настоящее время не включен по умолчанию. full_page_writes также должен быть включен (по умолчанию он включен).

Предупреждение
Если во время обработки происходит сбой qhb_rewind, вероятно, целевой каталог данных будет в таком состоянии, которое не подойдёт для восстановления. В таком случае рекомендуется сделать новую резервную копию.

qhb_rewind сразу прекратит работу, если найдет файлы, непосредственная запись в которые невозможна. Это может произойти, например, когда исходный и целевой сервер используют одинаковые пути файлов для ключей и сертификатов SSL, доступных только для чтения. Если такие файлы существуют на целевом сервере, рекомендуется их удалить перед запуском qhb_rewind . После синхронизации некоторые из этих файлов могут быть скопированы из источника, и в этом случае может потребоваться удалить скопированные данные и восстановить ссылки, использовавшиеся до синхронизации.

Параметры

qhb_rewind принимает следующие аргументы командной строки:

Аргумент
Описание

-D directory, --target-pgdata=directory
Указывает целевой каталог данных, который синхронизируется с источником. Целевой сервер должен быть штатно остановлен перед запуском qhb_rewind

--source-pgdata=directory
Задает путь файловой системы к каталогу данных исходного сервера, с которым нужно синхронизировать целевой. Этот параметр требует, чтобы исходный сервер был штатно остановлен

--source-server=connstr
Указывает строку подключения libpq для подключения к исходному серверу QHB, с которым нужно синхронизировать целевой. Соединение должно быть обычным (без репликации) от имени роли, имеющей достаточные разрешения для выполнения функций, используемых qhb_rewind на исходном сервере (см. Раздел «Примечания»), или от имени суперпользователя. Этот параметр требует, чтобы исходный сервер был запущен, и работал не в режиме восстановления

-n, --dry-run
Делать все, кроме внесения изменений в целевой каталог

-N, --no-sync
По умолчанию qhb_rewind будет ожидать записи всех файлов на диск. С этим параметром qhb_rewind будет завершаться без ожидания, что несколько быстрее, но в случае сбоя операционной системы может привести к повреждению каталога синхронизированных данных. Как правило, этот параметр полезен для тестирования, но его не следует использовать в продакшене

-P, --progress
Выводить сообщения о прогрессе. Включение этого параметра даст приблизительный отчет о ходе выполнения при копировании данных из исходного кластера.

--debug
Выводить отладочную информацию. (Полезно для разработчиков при отладке qhb_rewind)

-V, --version
Показать версию qhb_rewind и выйти

-?, --help
Показать справку об аргументах командной строки qhb_rewind и выйти

Окружение

Когда используется параметр --source-server, qhb_rewind также использует переменные окружения, поддерживаемые libpq.

Переменная окружения PG_COLOR указывает, использовать ли цвета в диагностических сообщениях. Возможные значения always, auto, never .

Примечания

Если при выполнении qhb_rewind исходным кластером является работающий сервер, вместо суперпользователя может использоваться роль, имеющая достаточные права в исходном кластере для выполнения функций, используемых qhb_rewind. Вот как можно создать такую роль с именем rewind_user:

CREATE USER rewind_user LOGIN;
GRANT EXECUTE ON function pg_catalog.pg_ls_dir(text, boolean, boolean) TO rewind_user;
GRANT EXECUTE ON function pg_catalog.pg_stat_file(text, boolean) TO rewind_user;
GRANT EXECUTE ON function pg_catalog.pg_read_binary_file(text) TO rewind_user;
GRANT EXECUTE ON function pg_catalog.pg_read_binary_file(text, bigint, bigint, boolean) TO rewind_user;

Если при выполнении qhb_rewind исходным кластером является работающий сервер, который был недавно повышен, то на нём необходимо выполнить CHECKPOINT, чтобы его управляющий файл содержал актуальную информацию о временной линии, которая используется qhb_rewind, чтобы проверить, может ли целевой кластер синхронизироваться с выбранным исходным кластером.

Как это устроено

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

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

Копируются все эти измененные блоки из исходного кластера в целевой кластер, напрямую через файловую систему ( --source-pgdata) или через SQL ( --source-server).

Копируются все остальные файлы, такие как pg_xact и файлы конфигурации, из исходного кластера в целевой (исключая файлы отношений). Как и при базовом копировании, содержимое каталогов pg_dynshmem/, pg_notify/, pg_replslot/, pg_serial/, pg_snapshots/, pg_stat_tmp/ и pg_subtrans/ исключается из данных, копируемых из исходного кластера. Также исключаются файлы или каталоги, начинающиеся с pgsql_tmp, а также файлы backup_label, tablespace_map, pg_internal.init, postmaster.opts и qhbmaster.pid.

Применяется WAL из исходного кластера, начиная с контрольной точки, созданной при отработке отказа. (Строго говоря, qhb_rewind не применяет WAL, он просто создает файл метки резервной копии, который запускает QHB и воспроизводит все WAL с этой контрольной точки).

qhb_upgrade

qhb_upgrade — обновить экземпляр сервера QHB

Синтаксис

qhb_upgrade -b старый_каталог_bin -B новый_каталог_bin -d старый_каталог_конфигурации -D новый_каталог_конфигурации [параметр...]

Описание

Программа qhb_upgrade позволяет обновлять данные, хранящиеся в файлах данных QHB (в т. ч. PostgreSQL версий, начиная с 12), до более поздней версии QHB без выгрузки/перезагрузки данных, обычно требующейся для обновлений основных версий (например, с 1.1.0 до 1.3.1 или с 1.4 до 1.5). Это не требуется для обновлений минорных (например, с 1.3.0 до 1.3.1) или корректирующих версий.

В основных выпусках QHB регулярно добавляются новые функции, которые зачастую меняют структуру системных таблиц, но формат внутреннего хранилища данных меняется редко. qhb_upgrade использует этот факт для быстрого обновления путем создания новых системных таблиц и простого повторного использования старых файлов пользовательских данных. Если будущий основной выпуск когда-либо изменит формат хранения данных таким образом, что старый формат данных станет нечитаемым, qhb_upgrade не будет использоваться для таких обновлений.

qhb_upgrade делает все возможное для гарантии того, что старый и новый кластеры двоично-совместимы, например, путем проверки совместимых настроек времени компиляции, включая 32/64-битные двоичные файлы. Важно, чтобы любые внешние модули также были двоично-совместимыми, хотя с помощью qhb_upgrade это проверить нельзя.

В таблице ниже приведены соответствия версий баз данных, для которых работает программа qhb_upgrade.

Исходная база данных | QHB 1.1.x | QHB 1.2.x | QHB 1.3.x | QHB 1.4.x | QHB 1.5.x
QHB 1.1.x |  | + | + | + | +
QHB 1.2.x |  |  | + | + | +
QHB 1.3.x |  |  |  | + | +
QHB 1.4.x |  |  |  |  | +
PostgreSQL 12.x | + | + | + | + | +
PostgreSQL 13.x |  |  |  |  | +
PostgreSQL 14.x |  |  |  |  | +

Важно!!!
Вследствие использования генерируемых ключей для шифрования в QHB 1.4.0 qhb_upgrade не может обновлять на эту и более новые версии QHB с версии qhb_upgrade 1.3.0 и более ранних, если они используют шифрование (qss_mode = 1).

Параметры

qhb_upgrade принимает следующие аргументы командной строки:

-b каталог_bin
--old-bindir=каталог_bin

каталог исполняемых файлов старой версии QHB; переменная окружения PGBINOLD

-B каталог_bin
--new-bindir=каталог_bin

каталог исполняемых файлов новой версии QHB; по умолчанию это каталог, в котором располагается qhb_upgrade; переменная окружения PGBINNEW

-c
--check

только проверить кластеры, не изменять никакие данные

-d каталог_конфигурации
--old-datadir=каталог_конфигурации

каталог конфигурации старого кластера базы данных; переменная окружения PGDATAOLD

-D каталог_конфигурации
--new-datadir=каталог_конфигурации

каталог конфигурации нового кластера базы данных; переменная окружения PGDATANEW

-s каталог
--socketdir=каталог

каталог, используемый для сокетов процесса postmaster во время обновления; по умолчанию это текущий рабочий каталог; переменная окружения PGSOCKETDIR

-U имя_пользователя
--username=имя_пользователя

имя пользователя, устанавливающего кластер; переменная окружения PGUSER

-v
--verbose

включить подробные внутренние сообщения

-V
--version

показать версию и завершить работу

--help

показать справку и завершить работу

Использование

Далее пошагово описано выполнение обновления с помощью qhb_upgrade:

Переместить старый кластер (необязательно)

Если вы используете установочный каталог, привязанный к конкретной версии, например /opt/QHB/1, то перемещать старый кластер не требуется. Все графические установщики используют установочные каталоги, привязанные к конкретной версии.

Если ваш установочный каталог не привязан к версии, например /usr/local/qhb, то текущий установочный каталог QHB необходимо переместить, чтобы он не конфликтовал с новой установкой QHB. Когда текущий сервер QHB выключен, каталог этой установки QHB можно безопасно переместить; если старый каталог /usr/local/qhb, его можно переименовать:

mv /usr/local/qhb /usr/local/qhb.old

Установить новые исполняемые файлы QHB

Установите новые исполняемые файлы сервера и вспомогательные файлы. Программа qhb_upgrade включена в установку по умолчанию.

Инициализировать новый кластер QHB

Инициализируйте новый кластер, используя qhb_bootstrap. Используйте совместимые флаги qhb_bootstrap, которые соответствуют флагам старого кластера. Многие готовые установщики делают этот шаг автоматически. Нет необходимости запускать новый кластер.

Установить дополнительные разделяемые объектные файлы

Установите в новый кластер все дополнительные разделяемые объектные файлы (или DLL), которые использовались старым кластером, например pgcrypto.so, где бы они ни находились — в contrib или в другом месте. Нет необходимости устанавливать определения схемы, (например, CREATE EXTENSION pgcrypto), так как они будут перенесены из старого кластера. Кроме того, в новый кластер нужно скопировать все нестандартные файлы поддержки полнотекстового поиска (словарь, синоним, тезаурус, стоп-слова).

Настроить аутентификацию

qhb_upgrade будет подключаться к старому и новому серверам несколько раз, поэтому, возможно, имеет смысл установить режим аутентификации peer в qhb_hba.conf или использовать файл ~/.pgpass.

Остановить оба сервера

Убедитесь, что оба сервера базы данных остановлены. Для этого в Unix можно выполнить:

pg_ctl -D /opt/pgsql stop
qhb_ctl -D /opt/PostgreSQL stop

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

Подготовиться к обновлению резервного сервера

Если вы обновляете резервные серверы, используя методы, описанные в шаге 9, убедитесь, что старые резервные серверы находятся в актуальном состоянии, запустив qhb_controldata в старых основном и резервном кластерах. Убедитесь, что значения «Последнее положение контрольной точки» совпадают во всех кластерах. (Несоответствие возникнет, если старые резервные серверы все еще работают или были отключены раньше старого основного.) Также измените значение wal_level на replica в файле qhb.conf нового основного кластера.

Запустить qhb_upgrade

Всегда запускайте программу qhb_upgrade от нового сервера, а не от старого. Для qhb_upgrade требуется указать каталоги данных и исполняемых файлов (bin) старого и нового кластеров. Также можно указать имя пользователя.

После запуска qhb_upgrade проверит совместимость двух кластеров, а затем выполнит обновление. Если нужно выполнить только проверки, можно использовать qhb_upgrade --check (старый сервер при этом останавливать необязательно). Также qhb_upgrade --check сообщит какие корректировки потребуется сделать вручную после обновления.

Очевидно, что во время обновления никто не должен иметь доступ к кластерам. По умолчанию qhb_upgrade запускает серверы на порту 5432, чтобы избежать непреднамеренных клиентских подключений.

Если при восстановлении схемы базы данных происходит ошибка,qhb_upgrade завершает свою работу, и вам придется вернуться к старому кластеру, как описано в шаге 15 ниже. Чтобы снова попытаться запустить qhb_upgrade, вам придется внести коррективы в старый кластер, чтобы qhb_upgrade могла успешно восстановить схему. Если проблема возникла в модуле contrib, может потребоваться удалить этот модуль в старом кластере, а затем установить его в новом после обновления (предполагается, что этот модуль не используется для хранения пользовательских данных).

Обновить резервные серверы с потоковой репликацией и трансляцией журнала

Если у вас настроена потоковая репликация или трансляция журнала для резервных серверов, то для быстрого их обновления можно выполнить следующие шаги. Вам не придется запускать на них qhb_upgrade, вместо этого запустите rsync на основном сервере. На этом этапе пока не нужно запускать никаких серверов.

Примечание
Если вы не хотите использовать rsync (или у вас его нет), либо вам нужно более простое решение, пропустите инструкции этого шага и просто заново создайте резервные серверы после завершения qhb_upgrade и запуска нового основного сервера.

Установите новые исполняемые файлы QHB на резервных серверах

Убедитесь, что новые исполняемые и вспомогательные файлы установлены на всех резервные серверах.

Убедитесь, что на резервных серверах новых каталогов данных не существует (или они пустые)

Если запускалась qhb_bootstrap, удалите на резервных серверах новые каталоги данных.

Установите дополнительные разделяемые объектные файлы

Установите на новых резервных серверах те же дополнительные разделяемые объектные файлы, что вы установили в новом основном кластере.

Остановите резервные серверы

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

Сохраните файлы конфигурации

Сохраните все файлы конфигурации из старых каталогов конфигурации резервных серверов, такие как qhb.conf и qhb_hba.conf, так как на следующем этапе они будут перезаписаны или удалены.

Запустите rsync

При использовании режима ссылок резервные серверы можно быстро обновить с помощью rsync. Для этого в каталоге, внутри которого находятся каталоги старого и нового кластера, для каждого резервного сервера выполните на основном:

rsync --archive --delete --hard-links --size-only --no-inc-recursive old_cluster new_cluster remote_dir

Здесь каталоги old_cluster и new_cluster задаются относительно текущего каталога на основном, а remote_dir находится над каталогами старого и нового кластера на резервном. Структура подкаталогов в указанных каталогах на основном и резервном серверах должна совпадать. Обратитесь к странице руководства rsync за подробной информацией о том, как указать удаленный каталог, например так:

rsync --archive --delete --hard-links --size-only --no-inc-recursive /opt/PostgreSQL \
      /opt/PostgreSQL standby.example.com:/opt/PostgreSQL

Используя параметр rsync --dry-run, можно проверить, что будет делать команда. Хотя rsync должен быть запущен на основном сервере как минимум с одним резервным, но затем, пока обновленный резервный сервер остается остановленным, на нем можно запустить rsync для обновления других резервных серверов.

Во время этой операции записываются ссылки, созданные в режиме ссылок qhb_upgrade, которые связывают файлы в старом и новом кластерах на основном сервере. Затем находятся соответствующие файлы в старом кластере резервного и в новом кластере резервного создаются ссылки на них. Файлы, которые не были связаны ссылками на основном, просто копируются с него на резервный. (Обычно они небольшого размера.) Это обеспечивает быстрое обновление резервного сервера. К сожалению, при этом rsync напрасно копирует файлы, связанные с временными и нежурналируемыми таблицами, поскольку эти файлы обычно не существуют на резервных серверах.

Если у вас есть табличные пространства, вам нужно будет выполнить аналогичную команду rsync для каждого каталога табличного пространства, например:

rsync --archive --delete --hard-links --size-only --no-inc-recursive /vol1/pg_tblsp/PG_9.5_201510051 \
      /vol1/pg_tblsp/PG_9.6_201608131 standby.example.com:/vol1/pg_tblsp

Если вы переместили pg_wal за пределы каталогов данных, rsync следует запустить и в этих каталогах.

Настройте резервные серверы с потоковой репликации и трансляцией журнала

Настройте серверы для трансляции журнала. (Запускать pg_start_backup() и pg_stop_backup() или делать резервную копию файловой системы не требуется, так как резервные серверы по-прежнему синхронизированы с основным.)

Восстановить qhb_hba.conf

Если вы изменили qhb_hba.conf, восстановите его исходные настройки. Также может потребоваться настроить другие файлы конфигурации в новом кластере в соответствии со старым кластером, например qhb.conf (и все включенные в него файлы).

Запустить новый сервер

Теперь можно безопасно запустить новый сервер, а затем и любые резервные серверы, синхронизированные с помощью rsync.

Действия после обновления

Если требуются какие-либо действия после обновления, qhb_upgrade выдаст предупреждения об этом по завершении работы. Она также сгенерирует файлы скриптов, которые должен будет запустить администратор. Эти скрипты будут подключаться к каждой базе данных, которой требуются дополнительные действия после обновления. Каждый скрипт должен быть запущен командой:

psql --username=qhb --file=script.sql qhb

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

Внимание!
Как правило, к таблицам, которые задействованы в перестраивающих базу данных скриптах опасно обращаться, пока эти скрипты не завершат свою работу; это может привести к некорректным результатам или плохой производительности. К таблицам, которые не задействованы в скриптах, можно обращаться немедленно.

Статистика

Поскольку статистика оптимизатора посредством qhb_upgrade не передается, вы получите указание выполнить команду для восстановления этой информации после обновления. Возможно, для этого придется установить параметры подключения к новому кластеру.

Удалить старый кластер

Если вы удовлетворены обновлением, можно удалить каталоги данных старого кластера, а также каталоги старой установки (например, bin, share).

Возврат к старому кластеру

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

Если использовался параметр --check, то старый кластер не изменился; просто перезапустите его.

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

Примечания

Программу qhb_upgrade следует запускать пользователю, от имени которого будет запускаться новый сервер. Также можно указать его параметром -U.

qhb_upgrade нельзя запускать под пользователем root.

qhb_upgrade создает различные файлы (например, дампы схем) в текущем рабочем каталоге, поэтому текущий пользователь должен иметь права на запись в него. Также в целях безопасности убедитесь, что этот каталог не доступен для чтения или записи другим пользователям.

qhb_upgrade запускает на короткое время процессы postmaster со старым и новым каталогом данных. Временные файлы Unix-сокетов для работы с этими процессами по умолчанию создаются в текущем рабочем каталоге. В некоторых ситуациях путь к текущему каталогу может быть слишком длинным, чтобы быть допустимым именем сокета. В этом случае можно воспользоваться параметром -s и поместить файлы сокетов в другой каталог с коротким путем. В целях безопасности убедитесь, что этот каталог не доступен для чтения или записи другим пользователям.

qhb_upgrade сообщит обо всех случаях сбоя, перестроения и переиндексации, если они влияют на вашу установку; по завершении обновления скрипты для восстановления таблиц и индексов будут созданы автоматически. Если вы попытаетесь автоматизировать обновление многих кластеров, то обнаружите, что кластеры с одинаковыми схемами баз данных требуют одинаковых шагов после обновления. Это связано с тем, что действия после обновления основаны на схемах базы данных, а не на пользовательских данных.

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

qhb_upgrade не поддерживает обновление баз данных, содержащих таблицы со столбцами, имеющими следующие системные типы данных reg, ссылающиеся на OID: regproc, regprocedure, regoper, regoperator, regconfig и regdictionary. (Тип regtype обновить можно.)

См. также

qhb_bootstrap, qhb_ctl, qhb_dump, qhb

qhb_waldump

qhb_waldump - отображает WAL кластера базы данных QHB в удобочитаемом виде

Синтаксис

qhb_waldump [option...] [startseg [endseg]]

Описание

qhb_waldump отображает журнал предзаписи (WAL) и в основном используется для отладки или в целях обучения.

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

Параметры

Следующие параметры командной строки управляют расположением и форматом вывода:

Аргумент | Описание
startseg | Начать чтение с указанного файла сегмента журнала. Это неявно определяет путь, по которому будут выполняться поиск файлов, и временную шкалу для использования
endseg | Остановиться после прочтения указанного файла сегмента журнала
-b, --bkp-details | Выводить подробную информацию о резервных блоках
-e end, --end=end | Прекратить чтение в указанном месте WAL, вместо чтения потока журнала до конца
-f, --follow | Продолжать опрашивать (раз в секунду) появление новых записей WAL, после достижения конца WAL
-n limit, --limit=limit | Отобразите указанное количество записей и остановиться
-p path, --path=path | Указывает каталог для поиска файлов сегментов журнала, либо каталог с подкаталогом pg_wal который содержит такие файлы. По умолчанию выполняется поиск в текущем каталоге, подкаталоге pg_wal текущего каталога и подкаталоге pg_wal PGDATA
-r rmgr, --rmgr=rmgr | Выводить только записи, созданные указанным менеджером ресурсов. Если list передан в качестве имени, то вывести список допустимых имен менеджера ресурсов и завершить работу
-s start, --start=start | место в WAL, с которого нужно начать чтение. По умолчанию начинается чтение первой корректной записи журнала в самом первом найденном файле
-t timeline, --timeline=timeline | Временная шкала, с которой нужно читать записи журнала. По умолчанию, если указано используется значение startseg,. Если же и оно не указано, то значение равно 1
-V, --version | Показать версию qhb_waldump и выйти
-x xid, --xid=xid | Вывести только те записи, которые отмечены указанным идентификатором транзакции
-z, --stats[=record] | Вывести сводную статистику (количество и размер записей и образов полных страниц) вместо отдельных записей. Опционально генерировать статистику для каждой записи, а не по per-rmgr
-?, --help | Показать справку об аргументах командной строки qhb_waldump и выйти

Окружение

PGDATA

Каталог данных; смотрите также опцию -p.

PG_COLOR

Указывает, использовать ли цвета в диагностических сообщениях. Возможные значения always, auto, never.

Примечания

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

Отображается только указанная временная шкала (или по умолчанию, если она не указана). Записи в другие сроки игнорируются.

qhb_waldump не может читать файлы WAL с суффиксом .partial. Если эти файлы должны быть прочитаны, суффикс .partial должен быть удален из имени файла.

Смотрите также

Внутреннее устройство WAL

Написание обёртки сторонних данных

Все операции над сторонней таблицей производятся через созданную для неё обёртку сторонних данных, которая состоит из набора функций, вызываемых основным сервером. Обёртка сторонних данных отвечает за извлечение данных из удаленного источника данных и передачу их исполнителю запросов QHB. Если обновление сторонних таблиц должно поддерживаться, обёртка также должна поддерживать это. В этой главе описывается, как написать обёртку сторонних данных.

Обёртки сторонних данных, включенные в дистрибутив, являются хорошими примерами для написания собственных обёрток (см. каталог contrib дерева исходного кода). Справочная страница CREATE FOREIGN DATA WRAPPER также содержит некоторые полезные сведения.

Примечание!!!
Стандарт SQL определяет интерфейс для написания обёрток сторонних данных. Однако QHB не реализует этот API, потому что усилия по его размещению в QHB были бы большими, а стандартизованный API все равно не получил широкого распространения.

Функции обёрток сторонних данных

Автор FDW¹ должен реализовать функцию обработчика и (опционально) функцию валидатора. Обе функции должны быть написаны на компилируемом языке, таком как C/RUST, с использованием интерфейса версии 1. Дополнительные сведения о соглашениях о вызовах языка C и динамической загрузке см. в разделе Функции на нативном языке.

Функция обработчика просто возвращает структуру указателей функций для функций обратного вызова, которые будут вызваны планировщиком, исполнителем и различными командами обслуживания. Большая часть усилий по написанию FDW¹ заключается в реализации этих функций обратного вызова. Функция обработчика должна быть зарегистрирована в QHB как не имеющая аргументов и возвращающая специальный псевдотип fdw_handler. Функции обратного вызова являются простыми функциями C/RUST и не видны или не вызываются на уровне SQL. Функции обратного вызова описаны в разделе Процедуры обратного вызова сторонних данных.

Функция validator отвечает за проверку параметров, указанных в командах CREATE и ALTER для обёртки сторонних данных, а также сторонних серверов, сопоставлений пользователей и сторонних таблиц, использующих обёртку. Функция validator должна быть зарегистрирована как принимающая два аргумента, текстовый массив, содержащий параметры для проверки, и OID, представляющий тип объекта, с которым связаны параметры (в виде OID системного каталога объект будет сохранен либо ForeignDataWrapperRelationId, ForeignServerRelationId, UserMappingRelationId, или ForeignTableRelationId). Если функция валидатора не задана, параметры не проверяются во время создания объекта или во время изменения объекта.

Процедуры обратного вызова сторонних данных

Функция обработчика FDW¹ возвращает структуру palloc’D FdwRoutine, содержащая указатели на функции обратного вызова, описанные ниже. Функции, связанные со сканированием, являются обязательными, остальные-необязательными.

Тип структуры FdwRoutine объявлен в src/include/foreign/fdwapi.h, который см. для получения дополнительных сведений.

Процедуры FDW для сканирования сторонних таблиц

void
GetForeignRelSize(PlannerInfo *root,
                  RelOptInfo *baserel,
                  Oid foreigntableid);

Получение оценки размера отношения для сторонней таблицы. Функция вызывается в начале планирования запроса, который сканирует стороннюю таблицу. root - глобальная информация планировщика о запросе; baserel - информация планировщика об этой таблице; и foreigntableid это pg_class OID из сторонней таблицы. (foreigntableid может быть получен из структур данных планировщика, но он передается явно, чтобы сэкономить усилия).

Эта функция должна выставлять baserel->rows в ожидаемое количество строк, возвращаемых сканированием таблицы, с учётом фильтра, заданного ограничением выборки. Начальное значение baserel->rows — некая константа по умолчанию, которую следует заменить, если это вообще возможно. Эта функция также может изменять baserel->width, если есть возможность улучшить оценку ширины строки результата.

Дополнительную информацию смотрите в разделе Планирование запросов на обработку сторонних данных

void
GetForeignPaths(PlannerInfo *root,
                RelOptInfo *baserel,
                Oid foreigntableid);

Создаёт возможные пути доступа для сканирования сторонней таблицы. Вызывается во время планирования запросов. Параметры такие же, как и для GetForeignRelSize (см. выше).

Эта функция должна генерировать по крайней мере один путь доступа (ForeignPath узел) для сканирования сторонней таблицы и должна вызвать add_path, чтобы добавить каждый такой путь к baserel->pathlist. Рекомендуется использовать create_foreignscan_path для того, чтобы построить ForeignPath узлы. Функция может генерировать несколько путей доступа, например, путь, который имеет допустимый pathkeys для представления предварительно отсортированного результата. Каждый путь доступа должен содержать смету расходов и может содержать любую конфиденциальную информацию FDW¹, необходимую для идентификации конкретного метода сканирования.

Дополнительную информацию смотрите в разделе Планирование запросов на обработку сторонних данных.

ForeignScan *
GetForeignPlan(PlannerInfo *root,
               RelOptInfo *baserel,
               Oid foreigntableid,
               ForeignPath *best_path,
               List *tlist,
               List *scan_clauses,
               Plan *outer_plan);

Создать ForeignScan планирование узла из выбранного внешнего пути доступа. Это называется в конце планирования запросов. Параметры как для GetForeignRelSize, плюс выбранные ForeignPath (ранее произведенный мимо GetForeignPaths, GetForeignJoinPaths, или GetForeignUpperPaths), целевой список, создаваемый узлом плана, условия ограничения, применяемые узлом плана, и внешний подплан узла плана. ForeignScan, который используется для перепроверок, выполняемых RecheckForeignScan. (Если путь предназначен для соединения, а не для базового отношения, foreigntableid является InvalidOid).

Эта функция должна создавать и возвращать a ForeignScan узел планирования; рекомендуется использовать его make_foreignscan для того чтобы построить the ForeignScan узел.

Дополнительную информацию смотрите в разделе Планирование запросов на обработку сторонних данных.

void
BeginForeignScan(ForeignScanState *node,
                 int eflags);

Начните выполнять внешнее сканирование. Это называется во время запуска исполнителя. Он должен выполнить любую инициализацию, необходимую перед началом сканирования, но не начать выполнение фактического сканирования (это должно быть сделано при первом вызове к IterateForeignScan). То ForeignScanState узел уже был создан, но его fdw_state поле по-прежнему имеет значение NULL. Информация о таблице, подлежащей сканированию, доступна через ForeignScanState узел (в частности, от нижележащего ForeignScan узел плана, который содержит любую FDW¹-частную информацию, предоставляемую компанией GetForeignPlan). eflags содержит флаговые биты, описывающие рабочий режим исполнителя для данного узла плана.

Обратите внимание, что когда (eflags & EXEC_FLAG_EXPLAIN_ONLY) верно, что эта функция не должна выполнять никаких внешне видимых действий; она должна выполнять только минимум, необходимый для того, чтобы сделать состояние узла допустимым для ExplainForeignScan и EndForeignScan.

TupleTableSlot *
IterateForeignScan(ForeignScanState *node);

Извлеките одну строку из внешнего источника, возвращая ее в слот таблицы кортежей (узел ScanTupleSlot следует использовать для этой цели). Возвращайте значение NULL, если больше нет доступных строк. Инфраструктура слотов таблиц кортежей позволяет возвращать либо физический, либо виртуальный Кортеж; в большинстве случаев последний вариант предпочтительнее с точки зрения производительности. Обратите внимание, что это вызывается в контексте кратковременной памяти, который будет сброшен между вызовами. Создайте контекст памяти в BeginForeignScan если вам нужно более долговечное хранение, или используйте es_query_cxt узлы EState.

Возвращаемые строки должны соответствовать целевому списку fdw_scan_tlist, если он был предоставлен, в противном случае они должны соответствовать типу строки сканируемой внешней таблицы. Если вы решите оптимизировать удаленную выборку столбцов, которые не нужны, вы должны вставить значения NULL в эти позиции столбцов, или же сгенерировать a fdw_scan_tlist список с опущенными столбцами.

Обратите внимание, что исполнителю QHB все равно, нарушают ли возвращенные строки какие — либо ограничения, определенные для внешней таблицы, но планировщик заботится и может неправильно оптимизировать запросы, если в внешней таблице есть строки, которые не удовлетворяют объявленному ограничению. Если ограничение нарушается, когда пользователь объявил, что ограничение должно иметь значение true, может быть целесообразно вызвать ошибку (так же, как это необходимо сделать в случае несоответствия типа данных).

void
ReScanForeignScan(ForeignScanState *node);

Перезапустите сканирование с самого начала. Обратите внимание, что любые параметры, от которых зависит проверка, могут иметь измененное значение, поэтому новое сканирование не обязательно возвращает точно такие же строки.

void
EndForeignScan(ForeignScanState *node);

Завершите сканирование и освободите ресурсы. Как правило, не важно, чтобы освободить palloc’D память, но, например, открытые файлы и соединения с удаленными серверами должны быть очищены.

Процедуры FDW для сканирования сторонних соединений

Если FDW¹ поддерживает выполнение внешних соединений удаленно (а не путем извлечения данных обеих таблиц и выполнения соединения локально), он должен предоставить эту функцию обратного вызова:

void
GetForeignJoinPaths(PlannerInfo *root,
                    RelOptInfo *joinrel,
                    RelOptInfo *outerrel,
                    RelOptInfo *innerrel,
                    JoinType jointype,
                    JoinPathExtraData *extra);

Создайте возможные пути доступа для объединения двух (или более) внешних таблиц, принадлежащих одному и тому же внешнему серверу. Эта необязательная функция вызывается во время планирования запроса. Как и в случае с GetForeignPaths, эта функция должна генерировать ForeignPath путь (или пути) для поставляемого устройства joinrel (использовать create_foreign_join_path чтобы построить их), и вызовите add_path чтобы добавить эти пути к набору путей, рассматриваемых для объединения. Но в отличие от него GetForeignPaths, нет необходимости, чтобы эта функция успешно создавала хотя бы один путь, поскольку пути, включающие локальное присоединение, всегда возможны.

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

Если a ForeignPath путь выбирается для соединения, он будет представлять весь процесс соединения; пути, созданные для таблиц компонентов и дочерних соединений, использоваться не будут. Последующая обработка пути соединения выполняется во многом так же, как и для пути сканирования одной внешней таблицы. Одно отличие заключается в том, что scanrelid из полученных результатов: ForeignScan узел плана должен быть установлен в ноль, так как нет ни одного отношения, которое он представляет; вместо этого, узел плана должен быть установлен в ноль. fs_relids область применения: ForeignScan узел представляет собой набор отношений, которые были соединены. (Последнее поле автоматически настраивается кодом основного планировщика и не должно заполняться FDW¹). Другое отличие состоит в том, что поскольку список столбцов для удаленного соединения не может быть найден из системных каталогов, FDW¹ должен заполнить fdw_scan_tlist с соответствующим перечнем TargetEntry узлы, представляющие набор столбцов, которые он будет предоставлять во время выполнения в кортежах, которые он возвращает.

Дополнительную информацию смотрите в разделе Планирование запросов на обработку сторонних данных.

Процедуры FDW для планирования обработки после сканирования / соединения

Если FDW¹ поддерживает выполнение удаленной обработки после сканирования/соединения, такой как удаленная агрегация, он должен предоставить эту функцию обратного вызова:

void
GetForeignUpperPaths(PlannerInfo *root,
                     UpperRelationKind stage,
                     RelOptInfo *input_rel,
                     RelOptInfo *output_rel,
                     void *extra);

Создайте возможные пути доступа для обработки верхних отношений, которая является термином планировщика для всех процессов обработки запросов после сканирования/объединения, таких как агрегация, функции окон, сортировка и обновления таблиц. Эта необязательная функция вызывается во время планирования запроса. В настоящее время он вызывается только в том случае, если все базовые отношения, участвующие в запросе, принадлежат одному и тому же FDW¹. Эта функция должна генерировать ForeignPath путь(или пути) для любой обработки post-scan/join, которую FDW¹ умеет выполнять удаленно (используйте create_foreign_upper_path чтобы построить их), и вызовите add_path чтобы добавить эти пути к указанному верхнему отношению. Как и в случае с GetForeignJoinPaths, нет необходимости, чтобы эта функция успешно создавала какие-либо пути, поскольку пути, включающие локальную обработку, всегда возможны.

Параметр stage определяет, какой шаг post-scan/join в настоящее время рассматривается. output_rel является ли верхнее отношение, которое должно принимать пути, представляющие вычисление этого шага, и input_rel является отношением, представляющим входные данные для этого шага. То дополнительный параметр предоставляет дополнительную информацию, в настоящее время он установлен только для UPPERREL_PARTIAL_GROUP_AGG или UPPERREL_GROUP_AGG, и в этом случае он указывает на GroupPathExtraData структура; или для UPPERREL_FINAL, и в этом случае он указывает на a FinalPathExtraData структура. (Заметить что ForeignPath пути, добавленные в output_rel как правило, не будет иметь никакой прямой зависимости от путей input_rel, поскольку их обработка, как ожидается, будет осуществляться извне. Однако изучение путей, ранее созданных для предыдущего этапа обработки, может быть полезно во избежание избыточной работы по планированию).

Дополнительную информацию смотрите в разделе Планирование запросов на обработку сторонних данных.

Процедуры FDW для обновления сторонних таблиц

Если FDW¹ поддерживает записываемые внешние таблицы, он должен предоставлять некоторые или все из следующих функций обратного вызова в зависимости от потребностей и возможностей FDW¹:

void
AddForeignUpdateTargets(Query *parsetree,
                        RangeTblEntry *target_rte,
                        Relation target_relation);

Операции UPDATE и DELETE выполняются для строк, ранее извлеченных функциями сканирования таблиц. FDW¹ может потребоваться дополнительная информация, такая как идентификатор строки или значения столбцов первичного ключа, чтобы гарантировать, что он может идентифицировать точную строку для обновления или удаления. Чтобы поддержать это, эта функция может добавить дополнительные скрытые или “мусорные” целевые столбцы в список столбцов, которые должны быть получены из внешней таблицы во время UPDATE или DELETE .

Чтобы сделать это, добавьте TargetEntry пункты к parsetree - >targetList, содержащий выражения для получения дополнительных значений. Каждая такая запись должна быть отмечена resjunk = true, и должны иметь различное resname это позволит идентифицировать его во время выполнения. Избегайте использования сопоставления имен ctidN, wholerow, или wholerowN, поскольку основная система может генерировать ненужные столбцы этих имен. Если дополнительные выражения являются более сложными, чем простые Vars, они должны быть выполнены через eval_const_expressions перед добавлением их в targetlist.

Хотя эта функция вызывается во время планирования, предоставленная информация немного отличается от той, которая доступна для других процедур планирования. parsetree является ли дерево синтаксического анализа для команды UPDATE или DELETE, в то время как target_rte и target_relation опишите целевую внешнюю таблицу.

Если AddForeignUpdateTargets указатель установлен в значение NULL, никакие дополнительные целевые выражения не добавляются. (Это сделает невозможным выполнение операций удаления, хотя UPDATE все еще может быть осуществимо, если FDW¹ использует неизменяемый первичный ключ для идентификации строк).

List *
PlanForeignModify(PlannerInfo *root,
                  ModifyTable *plan,
                  Index resultRelation,
                  int subplan_index);

Выполните все дополнительные действия планирования, необходимые для вставки, обновления или удаления во внешней таблице. Эта функция генерирует FDW¹-закрытую информацию, которая будет присоединена к ModifyTable запланируйте узел, который выполняет действие обновления. Эта частная информация должна иметь вид: Список, и будет поставлен к BeginForeignModify во время стадии исполнения.

root это глобальная информация планировщика о запросе. plan это ModifyTable узел плана, который является полным за исключением fdwPrivLists поле. resultRelation определяет целевую внешнюю таблицу по индексу таблицы диапазонов. subplan_index определяет, какая это цель ModifyTable узла плана, считая от нуля; используйте это, если вы хотите индексировать в намеченные планы или другой субструктуры план узел.

Дополнительную информацию смотрите в разделе Планирование запросов на обработку сторонних данных.

Если PlanForeignModify указатель установлен в значение NULL, никакие дополнительные действия план-времени не приняты, и fdw_private список доставленных в BeginForeignModify будет ноль.

void
BeginForeignModify(ModifyTableState *mtstate,
                   ResultRelInfo *rinfo,
                   List *fdw_private,
                   int subplan_index,
                   int eflags);

Начните выполнение операции изменения внешней таблицы. Эта процедура вызывается во время запуска исполнителя. Он должен выполнить любую инициализацию, необходимую до фактического изменения таблицы. Впоследствии, ExecForeignInsert, ExecForeignUpdate или ExecForeignDelete будет вызван для каждого кортежа, который будет вставлен, обновлен или удален.

mtstate является ли общее состояние ModifyTable выполняется узел плана; глобальные данные о состоянии плана и выполнения доступны через эту структуру. rinfo это ResultRelInfo структура, описывающая целевую внешнюю таблицу. (Этот ri_FdwState область применения: ResultRelInfo доступен для FDW¹, чтобы хранить любое частное государство, которое ему нужно для этой операции). fdw_private содержит личные данные, созданные с помощью PlanForeignModify если вообще есть. subplan_index определяет, какой целевой объект ModifyTable план узла такой есть. eflags содержит флаговые биты, описывающие рабочий режим исполнителя для данного узла плана.

Обратите внимание, что когда (eflags & EXEC_FLAG_EXPLAIN_ONLY) верно, что эта функция не должна выполнять никаких внешне видимых действий; она должна выполнять только минимум, необходимый для того, чтобы сделать состояние узла допустимым для ExplainForeignModify и EndForeignModify.

Если BeginForeignModify указатель установлен в значение NULL, никакие действия не выполняются во время запуска исполнителя.

TupleTableSlot *
ExecForeignInsert(EState *estate,
                  ResultRelInfo *rinfo,
                  TupleTableSlot *slot,
                  TupleTableSlot *planSlot);

Вставьте один Кортеж во внешнюю таблицу. estate является глобальным состоянием выполнения для запроса. rinfo это ResultRelInfo структура, описывающая целевую внешнюю таблицу. slot содержит Кортеж, который будет вставлен; он будет соответствовать определению типа строки внешней таблицы. planSlot содержит Кортеж, который был создан с помощью ModifyTable подплан узла плана; он отличается от slot в возможно содержащих дополнительные "мусорные" колонки. (Этот planSlot обычно представляет небольшой интерес для случаев вставки, но предоставляется для полноты).

Возвращаемое значение является либо слотом, содержащим данные, которые были фактически вставлены (это может отличаться от данных, предоставленных, например, в результате действий триггера), либо NULL, если строка фактически не была вставлена (опять же, как правило, в результате триггеров). passed-in slot может быть повторно использован для этой цели.

Данные в возвращаемом слоте используются только в том случае, если инструкция INSERT имеет вид RETURNING предложение или включает в себя представление WITH CHECK OPTION; или если внешняя таблица имеет вид AFTER ROW спусковой крючок. Триггеры требуют все столбцы, но FDW¹ может выбрать оптимизацию, возвращающую некоторые или все столбцы в зависимости от содержимого RETURNING статья или WITH CHECK OPTION ограничения. Независимо от этого, некоторый слот должен быть возвращен, чтобы указать на успех, или отчетное число строк запроса будет неверным.

Если ExecForeignInsert указатель установлен в значение NULL, попытки вставки во внешнюю таблицу завершатся ошибкой с сообщением об ошибке.

Обратите внимание, что эта функция также вызывается при вставке маршрутизируемых кортежей в раздел внешней таблицы или выполнении COPY FROM внешней таблицы, и в этом случае она вызывается иначе, чем в случае вставки. Смотрите функции обратного вызова, описанные ниже, которые позволяют FDW¹ поддерживать это.

TupleTableSlot *
ExecForeignUpdate(EState *estate,
                  ResultRelInfo *rinfo,
                  TupleTableSlot *slot,
                  TupleTableSlot *planSlot);

Обновите один Кортеж во внешней таблице. estate является глобальным состоянием выполнения для запроса. rinfo это ResultRelInfo структура, описывающая целевую внешнюю таблицу. slot содержит новые данные для кортежа; он будет соответствовать определению типа строки внешней таблицы. planSlot содержит Кортеж, который был создан с помощью ModifyTable подплан узла плана; он отличается от slot в возможно содержащих дополнительные” мусорные " колонки. В частности, любые ненужные столбцы, которые были запрошены AddForeignUpdateTargets будет доступен из этого слота.

Возвращаемое значение является либо слотом, содержащим строку, как она была фактически обновлена (это может отличаться от данных, предоставленных, например, в результате действий триггера), либо NULL, если ни одна строка не была фактически обновлена (снова, как правило, в результате триггеров). passed-in slot может быть повторно использован для этой цели.

Данные в возвращаемом слоте используются только в том случае, если инструкция UPDATE имеет RETURNING предложение или включает в себя представление WITH CHECK OPTION; или если внешняя таблица имеет вид AFTER ROW спусковой крючок. Триггеры требуют все столбцы, но FDW¹ может выбрать оптимизацию, возвращающую некоторые или все столбцы в зависимости от содержимого RETURNING статья или WITH CHECK OPTION ограничения. Независимо от этого, некоторый слот должен быть возвращен, чтобы указать на успех, или отчетное число строк запроса будет неверным.

Если ExecForeignUpdate указатель установлен в значение NULL, попытки обновить внешнюю таблицу завершатся ошибкой с сообщением об ошибке.

TupleTableSlot *
ExecForeignDelete(EState *estate,
                  ResultRelInfo *rinfo,
                  TupleTableSlot *slot,
                  TupleTableSlot *planSlot);

Удалите один кортеж из внешней таблицы. estate является глобальным состоянием выполнения для запроса. rinfo это ResultRelInfo структура, описывающая целевую внешнюю таблицу. slot не содержит ничего полезного при вызове, но может использоваться для удержания возвращенного кортежа. planSlot содержит Кортеж, который был создан с помощью ModifyTable подплан узла plan; в частности, он будет содержать любые ненужные столбцы, которые были запрошены с помощью AddForeignUpdateTargets. Для идентификации удаляемого кортежа необходимо использовать столбец(ы) нежелательной почты.

Возвращаемое значение является либо слотом, содержащим строку, которая была удалена, либо NULL, если ни одна строка не была удалена (как правило, в результате триггеров). passed-in slot может использоваться для удержания кортежа, подлежащего возврату.

Данные в возвращаемом слоте используются только в том случае, если запрос на удаление имеет вид RETURNING предложение или внешняя таблица имеет вид AFTER ROW спусковой крючок. Триггеры требуют все столбцы, но FDW¹ может выбрать оптимизацию, возвращающую некоторые или все столбцы в зависимости от содержимого RETURNING пункт. Независимо от этого, некоторый слот должен быть возвращен, чтобы указать на успех, или отчетное число строк запроса будет неверным.

Если ExecForeignDelete указатель установлен в значение NULL, попытки удаления из внешней таблицы завершатся ошибкой с сообщением об ошибке.

void
EndForeignModify(EState *estate,
                 ResultRelInfo *rinfo);

Завершите обновление таблицы и освободите ресурсы. Как правило, не важно, чтобы освободить palloc’D память, но, например, открытые файлы и соединения с удаленными серверами должны быть очищены.

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

Кортежи, вставленные в партиционированную таблицу с помощью INSERT или COPY FROM, направляются в партиции. Если FDW¹ поддерживает маршрутизируемые разделы внешней таблицы, он также должен предоставлять следующие функции обратного вызова. Эти функции также вызываются, когда COPY FROM выполняется на внешней таблице.

void
BeginForeignInsert(ModifyTableState *mtstate,
                   ResultRelInfo *rinfo);

Начните выполнение операции вставки во внешнюю таблицу. Эта процедура вызывается непосредственно перед первым кортежем, вставленным во внешнюю таблицу, в обоих случаях, когда это партиция, выбранная для маршрутизации кортежа, и цель, указанная в команде COPY FROM. Он должен выполнить любую инициализацию, необходимую до фактической вставки. Впоследствии, ExecForeignInsert будет вызван для каждого кортежа, который будет вставлен во внешнюю таблицу.

mtstate является ли общее состояние ModifyTable выполняется узел плана; глобальные данные о состоянии плана и выполнения доступны через эту структуру. rinfo это ResultRelInfo структура, описывающая целевую внешнюю таблицу. (Этот ri_FdwState область применения: ResultRelInfo доступен для FDW¹, чтобы хранить любое частное государство, которое ему нужно для этой операции).

Когда это вызывается командой COPY FROM, глобальные данные, связанные с планом, в mtstate не предусмотрено и то planSlot параметр из ExecForeignInsert впоследствии вызывается для каждого вставленного Кортежа является NULL, является ли внешняя таблица партицией, выбранной для маршрутизации кортежа, или целевым объектом, указанным в команде.

Если Beginforeignsert указатель установлен в значение NULL, для инициализации не предпринимается никаких действий.

Обратите внимание, что если FDW¹ не поддерживает маршрутизируемые разделы внешней таблицы и / или выполнение COPY FROM внешних таблиц, эта функция или ExecForeignInsert впоследствии вызванный должен бросить ошибку по мере необходимости.

void
EndForeignInsert(EState *estate,
                 ResultRelInfo *rinfo);

Завершите операцию вставки и освободите ресурсы. Как правило, не важно, чтобы освободить palloc’D память, но, например, открытые файлы и соединения с удаленными серверами должны быть очищены.

Если Endforeignsert указатель установлен в значение NULL, никакие действия не принимаются для прекращения.

int
IsForeignRelUpdatable(Relation rel);

Отчет о том, какие операции обновления поддерживает указанная внешняя таблица. Возвращаемое значение должно представлять собой битовую маску номеров событий правила, указывающую, какие операции поддерживаются внешней таблицей, с помощью CmdType перечисление; то есть, (1 << CMD_UPDATE) = 4 для UPDATE, (1 << CMD_INSERT) = 8 для INSERT, и (1 << CMD_DELETE) = 16 для DELETE.

Если IsForeignRelUpdatable указатель установлен в значение NULL, внешние таблицы считаются вставляемыми, обновляемыми или удаляемыми, если FDW¹ предоставляет ExecForeignInsert, ExecForeignUpdate, или ExecForeignDelete соответственно. Эта функция необходима только в том случае, если FDW¹ поддерживает некоторые таблицы, которые можно обновить, а некоторые-нет. (Даже в этом случае допустимо выбросить ошибку в подпрограмму выполнения вместо проверки этой функции. Однако эта функция используется для определения возможности обновления для отображения в information_schema число просмотров).

Некоторые вставки, обновления и удаления во внешние таблицы можно оптимизировать, реализовав альтернативный набор интерфейсов. Обычные интерфейсы для вставки, обновления и удаления строк выборки с удаленного сервера и последующего изменения этих строк по одному за раз. В некоторых случаях такой пошаговый подход необходим, но он может быть неэффективным. Если внешний сервер может определить, какие строки следует изменить, фактически не извлекая их, и если нет локальных структур, которые могли бы повлиять на операцию (локальные триггеры на уровне строк, сохраненные сгенерированные столбцы или WITH CHECK OPTION ограничения из родительских представлений), то можно организовать вещи так, что вся операция выполняется на удаленном сервере. Интерфейсы, описанные ниже, делают это возможным.

bool
PlanDirectModify(PlannerInfo *root,
                 ModifyTable *plan,
                 Index resultRelation,
                 int subplan_index);

Решите, безопасно ли выполнять прямую модификацию на удаленном сервере. Если да, то вернитесь истинный после выполнения необходимых для этого плановых действий. В противном случае, возврат ложный. Эта необязательная функция вызывается во время планирования запроса. Если эта функция выполняется успешно, BeginDirectModify, IterateDirectModify и EndDirectModify будет вызван на этапе выполнения, вместо этого. В противном случае модификация таблицы будет выполнена с использованием функций обновления таблиц, описанных выше. Параметры такие же, как и для PlanForeignModify.

Чтобы выполнить прямое изменение на удаленном сервере, эта функция должна переписать целевой подплан с ForeignScan узлом планирования, который выполняет прямое изменение на удаленном сервере. То операция область применения: ForeignScan должно быть установлено на CmdType перечисление соответственно; то есть, CMD_UPDATE для UPDATE, CMD_INSERT для INSERT, и CMD_DELETE для DELETE .

Дополнительную информацию смотрите в разделе Планирование запросов на обработку сторонних данных.

Если PlanDirectModify указатель установлен в значение NULL, никакие попытки выполнить прямое изменение на удаленном сервере не предпринимаются.

void
BeginDirectModify(ForeignScanState *node,
                  int eflags);

Подготовьтесь к выполнению прямого изменения на удаленном сервере. Это называется во время запуска исполнителя. Он должен выполнить любую инициализацию, необходимую до прямого изменения (это должно быть сделано при первом вызове к IterateDirectModify). ForeignScanState узел уже был создан, но его fdw_state поле по-прежнему имеет значение NULL. Информация о таблице, которую необходимо изменить, доступна через ForeignScanState узел (в частности, от нижележащего ForeignScan узел плана, который содержит любую FDW¹-частную информацию, предоставляемую компанией PlanDirectModify). eflags содержит флаговые биты, описывающие рабочий режим исполнителя для данного узла плана.

Обратите внимание, что когда (eflags & EXEC_FLAG_EXPLAIN_ONLY) верно, что эта функция не должна выполнять никаких внешне видимых действий; она должна выполнять только минимум, необходимый для того, чтобы сделать состояние узла допустимым для ExplainDirectModify и EndDirectModify.

Если BeginDirectModify указатель установлен в значение NULL, никакие попытки выполнить прямое изменение на удаленном сервере не предпринимаются.

TupleTableSlot *
IterateDirectModify(ForeignScanState *node);

Когда запрос INSERT, UPDATE или DELETE не имеет a RETURNING предложение, просто возвращает NULL после прямого изменения на удаленном сервере. Когда запрос содержит предложение, извлеките один результат, содержащий данные, необходимые для выполнения запроса. RETURNING вычисление, возвращая его в слот таблицы кортежа (узел ScanTupleSlot следует использовать для этой цели). Данные, которые были фактически вставлены, обновлены или удалены, должны храниться в системе es_result_relation_info - >ri_projectReturning - >>pi_exprContext->>>ecxt_scantuple из узла s EState. Возвращайте значение NULL, если больше нет доступных строк. Обратите внимание, что это вызывается в контексте кратковременной памяти, который будет сброшен между вызовами. Создайте контекст памяти в BeginDirectModify если вам нужно более долговечное хранение, или используйте es_query_cxt из узла s EState.

Возвращаемые строки должны соответствовать fdw_scan_tlist целевой список, если он был предоставлен, в противном случае они должны соответствовать типу строки обновляемой внешней таблицы. Если вы решите оптимизировать удаленную выборку столбцов, которые не нужны для RETURNING вычисление, вы должны вставить значения NULL в этих позициях столбца, или же сгенерировать a fdw_scan_tlist список с опущенными столбцами.

Независимо от того, содержит ли запрос предложение или нет, количество строк в отчете запроса должно быть увеличено самим FDW¹. Если запрос не содержит предложения, то FDW¹ также должен увеличить количество строк для запроса. ForeignScanState узел в случае объяснения анализа.

Если IterateDirectModify указатель установлен в значение NULL, никакие попытки выполнить прямое изменение на удаленном сервере не предпринимаются.

void
EndDirectModify(ForeignScanState *node);

Очистка после прямого изменения на удаленном сервере. Как правило, не важно, чтобы освободить palloc’D память, но, например, открытые файлы и соединения с удаленным сервером должны быть очищены.

Если EndDirectModify указатель установлен в значение NULL, никакие попытки выполнить прямое изменение на удаленном сервере не предпринимаются.

FDW-процедуры для блокировки строк

Если FDW¹ желает поддерживать late row locking (как описано в разделе Блокировка строк в обёртках сторонних данных), он должен предоставить следующие функции обратного вызова:

RowMarkType
GetForeignRowMarkType(RangeTblEntry *rte,
                      LockClauseStrength strength);

Сообщите, какой параметр маркировки строк следует использовать для внешней таблицы. rte это RangeTblEntry узел для таблицы и strength описывает силу блокировки, запрошенную соответствующим предложением FOR UPDATE/SHARE, если таковой имеется. Результат должен быть членом группы RowMarkType тип enum.

Эта функция вызывается во время планирования запроса для каждой внешней таблицы, которая появляется в запросе UPDATE, DELETE или SELECT FOR UPDATE/SHARE и не является целью UPDATE или DELETE .

Если GetForeignRowMarkType указатель установлен в значение NULL, ROW_MARK_COPY опция всегда используется. (Это подразумевает, что RefetchForeignRow никогда не будет называться, поэтому его не нужно предоставлять также).

Дополнительную информацию смотрите в разделе Блокировка строк в обёртках сторонних данных.

void
RefetchForeignRow(EState *estate,
                  ExecRowMark *erm,
                  Datum rowid,
                  TupleTableSlot *slot,
                  bool *updated);

Повторно извлеките один слот кортежа из внешней таблицы, после блокировки его, если это требуется. EState является глобальным состоянием выполнения для запроса. erm - ExecRowMark структура, описывающая целевую внешнюю таблицу и тип блокировки строк (если таковой имеется) для получения. rowid определяет Кортеж, который будет извлечен. slot не содержит ничего полезного при вызове, но может использоваться для удержания возвращенного кортежа. updated является выходным параметром.

Эта функция должна хранить Кортеж в предоставленном слоте или очистить его, если блокировка строк не может быть получена. Тип блокировки строки для получения определяется с помощью erm - >markType, который является значением, ранее возвращенным с помощью GetForeignRowMarkType. (ROW_MARK_REFERENCE означает просто повторно забрать кортеж, не приобретая никакой блокировки, и ROW_MARK_COPY никогда не будет видна эта процедура).

Кроме того, *updated должно быть установлено true если то, что было извлечено, было обновленной версией кортежа, а не той же самой версией, полученной ранее. (Если FDW¹ не может быть уверен в этом, всегда рекомендовано возвращать true).

Обратите внимание, что по умолчанию неспособность получить блокировку строки должна привести к возникновению ошибки; возврат с пустым слотом подходит только в том случае, если SKIP LOCKED опция указывается путем erm - >waitPolicy.

rowid это ctid значение, ранее считанное для строки, которая будет повторно извлечена. Хотя rowid значение передается как Datum величина, в настоящее время это может быть только tid. API функции выбран в надежде, что в будущем можно будет разрешить другие типы данных для идентификаторов строк.

Если RefetchForeignRow указатель установлен в значение NULL, попытки повторно извлечь строки завершатся ошибкой с сообщением об ошибке.

Дополнительную информацию смотрите в разделе Блокировка строк в обёртках сторонних данных.

bool
RecheckForeignScan(ForeignScanState *node,
                   TupleTableSlot *slot);

Еще раз проверьте, что ранее возвращенный Кортеж все еще соответствует соответствующим квалификаторам сканирования и соединения, и, возможно, предоставит измененную версию кортежа. Для обёрток сторонних данных, которые не выполняют функцию объединения, обычно будет удобнее установить это значение NULL а вместо этого поставили fdw_recheck_quals соответственно. Однако когда внешние объединения выталкиваются вниз, недостаточно повторно применить проверки, относящиеся ко всем базовым таблицам, к результирующему кортежу, даже если все необходимые атрибуты присутствуют, потому что неспособность соответствовать некоторому квалификатору может привести к тому, что некоторые атрибуты будут иметь значение NULL, а не к отсутствию возвращаемого кортежа. RecheckForeignScan может перепроверять квалификаторы и возвращать true, если они все еще удовлетворены, и false в противном случае, но он также может хранить кортеж замены в поставляемом слоте.

Для реализации функции push-соединения обёртка сторонних данных обычно создает альтернативный локальный план соединения, который используется только для повторных проверок; он становится внешним подпланом объекта ForeignScan. Когда требуется перепроверка, этот подплан может быть выполнен, и полученный Кортеж может быть сохранен в слоте. Этот план не должен быть эффективным, так как ни одна базовая таблица не будет возвращать более одной строки; например, он может реализовать все соединения как вложенные циклы. Функция GetExistingLocalJoinPath может использоваться для поиска существующих путей для подходящего локального пути соединения, который может использоваться в качестве альтернативного локального плана соединения. GetExistingLocalJoinPath выполняет поиск непараметризованного пути в списке путей указанного отношения соединения. (Если он не находит такой путь, он возвращает NULL, и в этом случае обёртка сторонних данных может построить локальный путь самостоятельно или может решить не создавать пути доступа для этого соединения).

Процедуры FDW для EXPLAIN

void
ExplainForeignScan(ForeignScanState *node,
                   ExplainState *es);

Печать дополнительных EXPLAIN вывода для сканирования внешней таблицы. Эта функция может вызывать ExplainPropertyText и связанные функции для добавления полей в выходные данные EXPLAIN. Поля флага внутри es может использоваться для определения того, что нужно распечатать, и состояния объекта ForeignScanState узел может быть проверен для предоставления статистики времени выполнения в случае объяснения анализа.

Если ExplainForeignScan указатель установлен в значение NULL, никакая дополнительная информация не печатается во время EXPLAIN .

void
ExplainForeignModify(ModifyTableState *mtstate,
                     ResultRelInfo *rinfo,
                     List *fdw_private,
                     int subplan_index,
                     struct ExplainState *es);

Печать дополнительных выходных данных объяснения для обновления внешней таблицы. Эта функция может вызывать ExplainPropertyText и связанные функции для добавления полей в выходные данные EXPLAIN. Поля флага внутри es может использоваться для определения того, что нужно распечатать, и состояния объекта ModifyTableState узел может быть проверен для предоставления статистики времени выполнения в случае объяснения анализа. Первые четыре аргумента такие же, как и для BeginForeignModify.

Если ExplainForeignModify указатель установлен в значение NULL, никакая дополнительная информация не печатается во время EXPLAIN.

void
ExplainDirectModify(ForeignScanState *node,
                    ExplainState *es);

Распечатайте дополнительные EXPLAIN выходные данные для прямого изменения на удаленном сервере. Эта функция может вызывать ExplainPropertyText и связанные функции для добавления полей в выходные данные EXPLAIN. Поля флага внутри es может использоваться для определения того, что нужно распечатать, и состояния объекта ForeignScanState узел может быть проверен для предоставления статистики времени выполнения в случае объяснения анализа.

Если ExplainDirectModify указатель установлен в значение NULL, никакая дополнительная информация не печатается во время EXPLAIN .

Процедуры FDW для ANALYZE

bool
AnalyzeForeignTable(Relation relation,
                    AcquireSampleRowsFunc *func,
                    BlockNumber *totalpages);

Эта функция вызывается, когда ANALYZE выполняется на внешней таблице. Если FDW¹ может собирать статистику для этой внешней таблицы, он должен вернуться истинный, и предоставляет указатель на функцию, которая будет собирать образцы строк из таблицы в функция, плюс предполагаемый размер таблицы в страницах в всего страниц. В противном случае, возврат ложный.

Если FDW¹ не поддерживает сбор статистических данных для любых таблиц, то AnalyzeForeignTable указатель может быть установлен на NULL.

Если это предусмотрено, функция выборки образцов должна иметь подпись

int
AcquireSampleRowsFunc(Relation relation,
                      int elevel,
                      HeapTuple *rows,
                      int targrows,
                      double *totalrows,
                      double *totaldeadrows);

Случайная выборка до targrows строки должны быть собраны из таблицы и сохранены в предоставленной вызывающей стороне rows массив. Должно быть возвращено фактическое количество собранных строк. Кроме того, сохраните оценки общего числа живых и мертвых строк в таблице в выходные параметры totalrows и totaldeadrows. (Набор totaldeadrows до нуля, если FDW¹ не имеет никакого понятия мертвых строк).

Процедуры FDW для IMPORT FOREIGN SCHEMA

List *
ImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid);

Получите список команд для создания внешней таблицы. Эта функция вызывается при выполнении IMPORT FOREIGN SCHEMA, и передается дерево синтаксического анализа для этого оператора, а также OID внешнего сервера для использования. Он должен возвращать список строк C, каждая из которых должна содержать команду CREATE FOREIGN TABLE. Эти строки будут проанализированы и выполнены главным сервером.

Внутри же ImportForeignSchemaStmt структуры, remote_schema это имя удаленной схемы, из которой должны быть импортированы таблицы. тип списка определяет способ фильтрации имен таблиц: FDW_IMPORT_SCHEMA_ALL означает, что все таблицы в удаленной схеме должны быть импортированы (в этом случае table_list быть пустой), FDW_IMPORT_SCHEMA_LIMIT_TO средства для включения только таблиц, перечисленных в table_list, и FDW_IMPORT_SCHEMA_EXCEPT средства для исключения таблиц, перечисленных в table_list. Возможности это список параметров, используемых для процесса импорта. Значения этих вариантов зависят от FDW¹. Например, FDW¹ может использовать параметр для определения того, является ли NOT NULL атрибуты столбцов должны быть импортированы. Эти параметры не должны иметь ничего общего с теми, которые поддерживаются FDW¹ в качестве параметров объекта базы данных.

FDW¹ может проигнорировать локальная схема область применения: ImportForeignSchemaStmt, потому что главный сервер автоматически вставит это имя в анализируемые команды CREATE FOREIGN TABLE.

FDW¹ не должен заботиться о реализации фильтрации, указанной выше тип списка и table_list, либо, поскольку главный сервер автоматически пропустит все возвращенные команды для таблиц, исключенных в соответствии с этими опциями. Однако часто бывает полезно избегать работы по созданию команд для исключенных таблиц в первую очередь. Функция IsImportableForeignTable() может быть полезна для проверки того, будет ли данное имя внешней таблицы проходить фильтр.

Если FDW¹ не поддерживает импорт определений таблиц, то указатель ImportForeignSchema может быть установлен на NULL.

Программы FDW для параллельного выполнения

Один ForeignScan узел может, дополнительно, поддерживать параллельное выполнение. Параллель ForeignScan будет выполняться в нескольких процессах и должен возвращать каждую строку ровно один раз через все взаимодействующие процессы. Для этого процессы могут координироваться через блоки динамической общей памяти фиксированного размера. Эта общая память не обязательно должна быть сопоставлена с одним и тем же адресом в каждом процессе, поэтому она не должна содержать указателей. Все перечисленные ниже функции являются необязательными, но большинство из них необходимы для поддержки параллельного выполнения.

bool
IsForeignScanParallelSafe(PlannerInfo *root, RelOptInfo *rel,
                          RangeTblEntry *rte);

Проверьте, можно ли выполнить сканирование в параллельном процессе. Эта функция будет вызываться только тогда, когда планировщик считает, что параллельный план может быть возможен, и должен возвращать true, если это безопасно для выполнения сканирования в параллельном процессе. Как правило, это не будет иметь место, если удаленный источник данных имеет семантику транзакций, если только соединение процесса с данными не может быть каким-то образом сделано для совместного использования того же контекста транзакции, что и лидер.

Если эта функция не определена, предполагается, что сканирование должно выполняться в пределах параллельной выноски. Обратите внимание, что возврат true не означает, что само сканирование может быть выполнено параллельно, только то, что сканирование может быть выполнено в параллельном процессе. Поэтому может быть полезно определить этот метод, даже если параллельное выполнение не поддерживается.

Size
EstimateDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt);

Оцените объем динамической общей памяти, который потребуется для параллельной работы. Это может быть больше, чем сумма, которая будет фактически использована, но она не должна быть ниже. Возвращаемое значение находится в байтах. Эта функция является необязательной и может быть опущена, если она не требуется; но если она опущена, то следующие три функции также должны быть опущены, поскольку общая память не будет выделена для использования FDW¹.

void
InitializeDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt,
                         void *coordinate);

Инициализируйте динамическую общую память, которая будет необходима для параллельной работы. координировать указывает на общую область памяти размером, равным возвращаемому значению EstimateDSMForeignScan. Эта функция является необязательной и может быть опущена, если это не требуется.

void
ReInitializeDSMForeignScan(ForeignScanState *node, ParallelContext *pcxt,
                           void *coordinate);

Повторно инициализируйте динамическую общую память, необходимую для параллельной работы, когда узел плана внешнего сканирования готовится к повторному сканированию. Эта функция является необязательной и может быть опущена, если это не требуется. Рекомендуемая практика заключается в том, что эта функция сбрасывает только общее состояние, в то время как ReScanForeignScan функция сбрасывает только локальное состояние. В настоящее время эта функция будет вызвана раньше ReScanForeignScan но лучше всего не полагаться на этот заказ.

void
InitializeWorkerForeignScan(ForeignScanState *node, shm_toc *toc,
                            void *coordinate);

Инициализируйте локальное состояние параллельного процесса на основе общего состояния, заданного руководителем во время выполнения InitializeDSMForeignScan. Эта функция является необязательной и может быть опущена, если это не требуется.

void
ShutdownForeignScan(ForeignScanState *node);

Высвобождайте ресурсы, когда ожидается, что узел не будет выполнен до завершения. Это называется не во всех случаях; иногда, EndForeignScan может вызываться без того, чтобы эта функция была вызвана первой. Поскольку сегмент DSM, используемый параллельным запросом, уничтожается сразу же после вызова этого обратного вызова, внешние обертки данных, которые хотят предпринять некоторые действия, прежде чем сегмент DSM исчезнет, должны реализовать этот метод.

Процедуры FDW для репараметризации путей

List *
ReparameterizeForeignPathByChild(PlannerInfo *root, List *fdw_private,
                                 RelOptInfo *child_rel);

Эта функция вызывается при преобразовании пути, параметризованного самым верхним родителем данного дочернего отношения child_rel быть параметризованным по отношению к ребенку. Эта функция используется для перезамера любых путей или преобразования любых узлов выражения, сохраненных в данном файле fdw_private член а ForeignPath. Обратный вызов может использовать reparameterize_path_by_child, adjust_appendrel_attrs или adjust_appendrel_attrs_multilevel как и положено.

Вспомогательные функции для обёртки сторонних данных

Несколько вспомогательных функций экспортируются с главного сервера, чтобы авторы обёрток сторонних данных могли легко получить доступ к атрибутам объектов, связанных с FDW¹, таким как параметры FDW¹. Чтобы использовать любую из этих функций, необходимо включить файл заголовка foreign/foreign.h в вашем исходном файле. Этот заголовок также определяет типы структур, возвращаемые этими функциями.

ForeignDataWrapper *
GetForeignDataWrapperExtended(Oid fdwid, bits16 flags);

Эта функция возвращает объект ForeignDataWrapper для обёртки сторонних данных с заданным OID. ForeignDataWrapper объект содержит свойства FDW¹ (см. foreign/foreign.h относительно деталей). флаги это побитовая или битовая маска, указывающая на дополнительный набор опций. Он может принять значение FDW_MISSING_OK, и в этом случае a NULL результат возвращается вызывающему объекту вместо ошибки для неопределенного объекта.

ForeignDataWrapper *
GetForeignDataWrapper(Oid fdwid);

Эта функция возвращает a объект ForeignDataWrapper для обёртки сторонних данных с заданным OID. ForeignDataWrapper объект содержит свойства FDW¹ (см. foreign/foreign.h относительно деталей).

ForeignServer *
GetForeignServerExtended(Oid serverid, bits16 flags);

Эта функция возвращает ForeignServer объект для внешнего сервера с заданным OID. ForeignServer объект содержит свойства сервера (см. foreign/foreign.h относительно деталей). флаги это побитовая или битовая маска, указывающая на дополнительный набор опций. Он может принять значение FSV_MISSING_OK, и в этом случае a NULL результат возвращается вызывающему объекту вместо ошибки для неопределенного объекта.

ForeignServer *
GetForeignServer(Oid serverid);

Эта функция возвращает ForeignServer объект для внешнего сервера с заданным OID. Один ForeignServer объект содержит свойства сервера (см. foreign/foreign.h относительно деталей).

UserMapping *
GetUserMapping(Oid userid, Oid serverid);

Эта функция возвращает объект UserMapping для отображения пользователя данной роли на данном сервере. (Если нет никакого сопоставления для конкретного пользователя, он вернет сопоставление для PUBLIC, или бросьте ошибку, если ее нет). UserMapping объект содержит свойства пользовательского сопоставления (см. foreign/foreign.h относительно деталей).

ForeignTable *
GetForeignTable(Oid relid);

Эта функция возвращает объект ForeignTable для внешней таблицы с заданным OID. ForeignTable объект содержит свойства внешней таблицы (см. foreign/foreign.h относительно деталей).

List *
GetForeignColumnOptions(Oid relid, AttrNumber attnum);

Эта функция возвращает параметры FDW¹ для каждого столбца для столбца с заданным внешним OID таблицы и номером атрибута, в виде списка DefElem. Значение NIL возвращается, если столбец не имеет параметров.

Некоторые типы объектов имеют функции поиска на основе имен в дополнение к основанным на OID функциям:

ForeignDataWrapper *
GetForeignDataWrapperByName(const char *name, bool missing_ok);

Эта функция возвращает объект ForeignDataWrapper для обёртки сторонних данных с заданным именем. Если обёртка не найдена, верните значение NULL, если missing_ok имеет значение true, иначе возникнет ошибка.

ForeignServer *
GetForeignServerByName(const char *name, bool missing_ok);

Эта функция возвращает ForeignServer объект для внешнего сервера с заданным именем. Если сервер не найден, верните значение NULL, если missing_ok имеет значение true, иначе возникнет ошибка.

Планирование запросов на обработку сторонних данных

Функции обратного вызова FDW¹ GetForeignRelSize, GetForeignPaths, GetForeignPlan, PlanForeignModify, GetForeignJoinPaths, GetForeignUpperPaths, и PlanDirectModify должны вписываться в работу планировщика QHB. Вот некоторые заметки о том, что они должны делать.

Информация внутри корень и подложка может использоваться для уменьшения объема информации, которая должна быть извлечена из внешней таблицы (и, следовательно, снизить стоимость). baserel - >baserestrictinfo это особенно интересно, так как содержит ограничительные кавычки (WHERE предложения), которые должны использоваться для фильтрации строк, подлежащих извлечению. (Сам FDW¹ не требуется для принудительного применения этих тестов, поскольку основной исполнитель может проверить их вместо этого). baserel- > reltarget - >>exprs можно использовать, чтобы определить, какие столбцы должны быть извлечены; но обратите внимание, что он только перечисляет столбцы, которые должны быть испущены ForeignScan узел плана, а не столбцы, которые используются при квалификационной оценке, но не выводятся запросом.

Различные частные поля доступны для функций планирования FDW¹, чтобы сохранить информацию. Как правило, все, что вы храните в частных полях FDW¹, должно быть убрано, чтобы оно было исправлено в конце планирования.

baserel - >fdw_private это void указатель, доступный для функций планирования FDW¹ для хранения информации, относящейся к конкретной внешней таблице. Основной планировщик не трогает его, кроме как для инициализации его в ноль, когда RelOptInfo узел будет создан. Это полезно для передачи информации вперед от GetForeignRelSize Для GetForeignPaths и-или GetForeignPaths Для GetForeignPlan, тем самым избегая пересчета.

GetForeignPaths можно определить значение различных путей доступа, храня личную информацию в fdw_private область применения: ForeignPath узлы. fdw_private объявляется как a Список указатель, но на самом деле может содержать что-либо, так как основной планировщик не касается его. Тем не менее, рекомендуется использовать представление, которое может быть выгружено nodeToString, для использования с поддержкой отладки, доступной в бэкенде.

GetForeignPlan может исследовать fdw_private поле выбранного ForeignPath узла, и может произвести fdw_exprs и fdw_private списки, подлежащие размещению в ForeignScan запланируйте узел, где они будут доступны во время выполнения. Оба эти списка должны быть представлены в такой форме, чтобы copyObject умеет копировать. fdw_private список не имеет никаких других ограничений и никоим образом не интерпретируется ядром backend. То fdw_exprs список, если он не равен NIL, должен содержать деревья выражений, предназначенные для выполнения во время выполнения. Эти деревья будут проходить пост-обработку планировщиком, чтобы сделать их полностью исполняемыми.

В GetForeignPlan, как правило, переданный целевой список можно скопировать в узел плана как есть. Переданный scan_clauses список содержит те же пункты, что и baserel - >baserestrictinfo, но может быть переупорядочен для для лучшей эффективности выполнения. В простых случаях FDW¹ может просто удалить RestrictInfo узлы из списка scan_clauses (с помощью extract_actual_clauses) и поместить все предложения в список квалификации узла плана, что означает, что все предложения будут проверены исполнителем во время выполнения. Более сложные FDW¹ могут быть в состоянии проверить некоторые пункты внутренне, и в этом случае эти пункты могут быть удалены из списка квалификации узла плана, чтобы исполнитель не тратил время на повторную проверку их.

Например, FDW¹ может идентифицировать некоторые положения об ограничении формы foreign_variable = sub_expression, который он определяет, может быть выполнен на удаленном сервере с учетом локально оцененного значения sub_expression. Фактическая идентификация такой оговорки должна произойти во время GetForeignPaths, так как это повлияло бы на смету расходов для пути. Путь fdw_private поле, вероятно, будет содержать указатель на идентифицированное предложение RestrictInfo узел. Затем GetForeignPlan удалил бы это положение из scan_clauses, но добавить то sub_expression Для fdw_exprs чтобы убедиться, что он получает массаж в исполняемую форму. Это, вероятно, также поместит управляющую информацию в узел плана fdw_private поле, указывающее функциям выполнения, что следует делать во время выполнения. Запрос, передаваемый на удаленный сервер, будет включать что-то вроде Где foreign_variable = $1, при этом значение параметра получено во время выполнения из оценки fdw_exprs дерево выражения.

Все предложения, удаленные из списка квалификации узла плана, должны быть добавлены вместо этого fdw_recheck_quals или перепроверить с помощью RecheckForeignScan для того чтобы обеспечить правильное поведение на READ COMMITTED уровень изоляции. Когда параллельное обновление происходит для некоторой другой таблицы, вовлеченной в запрос, исполнителю может потребоваться проверить, что все исходные тесты все еще удовлетворены для кортежа, возможно, против другого набора значений параметров. С помощью fdw_recheck_quals обычно это проще, чем реализовать проверки внутри RecheckForeignScan, но этот метод будет недостаточным, когда внешние соединения были вытеснены, так как кортежи соединения в этом случае могут иметь некоторые поля, переходящие в NULL, не отвергая кортеж полностью.

Другой ForeignScan поле, которое может быть заполнено FDWs¹ является fdw_scan_tlist, который описывает кортежи, возвращенные FDW¹ для этого узла плана. Для простых сканирований внешней таблицы это может быть установлено в NULL, подразумевая, что возвращенные кортежи имеют тип строки, объявленный для внешней таблицы. non-NIL значение должно быть целевым списком (списком TargetEntry) содержащие Vars и / или выражения, представляющие возвращаемые столбцы. Это может быть использовано, например, чтобы показать, что FDW¹ пропустил некоторые столбцы, которые он заметил, не будут нужны для запроса. Кроме того, если FDW¹ может вычислять выражения, используемые запросом, дешевле, чем это можно сделать локально, он может добавить эти выражения в fdw_scan_tlist. Обратите внимание, что планы объединения (созданные из путей, сделанных GetForeignJoinPaths) должны всегда поставлять fdw_scan_tlist чтобы описать набор столбцов, которые они вернут.

FDW¹ всегда должен создавать по крайней мере один путь, который зависит только от предложений ограничения таблицы. В запросах join он также может создавать пути, которые зависят от предложений join, например foreign_variable = локальная переменная. Такие пункты не будут найдены в baserel - >baserestrictinfo но нужно искать в списках присоединения к отношениям. Путь, использующий такое предложение, называется "параметризованным путем". Он должен идентифицировать другие отношения, используемые в выбранном предложении соединения с подходящим значением param_info; использовать get_baserel_parampathinfo чтобы вычислить это значение. В GetForeignPlan, этот локальная переменная часть предложения соединения будет добавлена к fdw_exprs, и тогда во время выполнения дело работает так же, как и для обычного предложения об ограничении.

Если FDW¹ поддерживает удаленные соединения, GetForeignJoinPaths должен производить ForeignPath для потенциальных удаленных соединений во многом таким же образом, как GetForeignPaths работает для базовых таблиц. Информацию о предполагаемом присоединении можно передать вперед GetForeignPlan точно так же, как описано выше. Однако, baserestrictinfo не относится к отношениям соединения; вместо этого соответствующие условия соединения для конкретного соединения передаются в GetForeignJoinPaths как отдельный параметр (extra - >restrictlist).

FDW¹ может дополнительно поддерживать прямое выполнение некоторых действий плана, которые находятся выше уровня сканирования и объединения, таких как группирование или агрегирование. Чтобы предложить такие варианты, FDW¹ должен генерировать пути и вставлять их в соответствующее верхнее отношение. Например, путь, представляющий удаленную агрегацию, должен быть вставлен в поле UPPERREL_GROUP_AGG связь, используя add_path. Этот путь будет сравниваться по стоимости с локальным агрегированием, выполненным путем считывания простого пути сканирования для внешней связи (обратите внимание, что такой путь также должен быть указан, иначе будет ошибка во время планирования). Если путь удаленной агрегации выигрывает, что обычно и происходит, то он будет преобразован в план обычным способом, путем вызова GetForeignPlan. Рекомендуемое место для создания таких путей находится в GetForeignUpperPaths функция обратного вызова, которая вызывается для каждого верхнего отношения (т. е. для каждого шага обработки после сканирования/соединения), если все базовые отношения запроса исходят из одного и того же FDW¹.

PlanForeignModify и другие обратные вызовы, описанные в разделе Процедуры FDW для обновления сторонних таблиц, разработаны вокруг предположения, что внешнее отношение будет сканироваться обычным способом, а затем отдельные обновления строк будут управляться локальным ModifyTable план узла. Этот подход необходим для общего случая, когда обновление требует чтения локальных таблиц, а также внешних таблиц. Тем не менее, если операция может быть выполнена полностью внешним сервером, FDW¹ может создать путь, представляющий это, и вставить его в поле UPPERREL_FINAL верхнее отношение, где он будет конкурировать с ModifyTable подход. Этот подход также может быть использован для реализации удаленного доступа SELECT FOR UPDATE, а не использовать обратные вызовы блокировки строк, описанные в разделе FDW-процедуры для блокировки строк. Имейте в виду, что путь вставляется в UPPERREL_FINAL отвечает за реализацию всего поведения запроса.

При планировании UPDATE или DELETE, PlanForeignModify и PlanDirectModify может искать RelOptInfo структуру для внешней таблицы и использовать baserel - >fdw_private данные, ранее созданные функциями планирования сканирования. Однако в INSERT целевая таблица не сканируется, поэтому ее нет RelOptInfo для него. То List возвращаемый PlanForeignModify имеет те же ограничения, что и fdw_private список a ForeignScan узел плана, то есть он должен содержать только структуры, которые copyObject умеет копировать.

INSERT с ON CONFLICT предложением не поддерживает указание целевого объекта конфликта, поскольку уникальные ограничения или ограничения исключения для удаленных таблиц не известны локально. Это в свою очередь подразумевает, что ON CONFLICT DO UPDATE не поддерживается, так как спецификация там обязательна.

Блокировка строк в обёртках сторонних данных

Если базовый механизм хранения FDW¹ имеет концепцию блокировки отдельных строк для предотвращения одновременного обновления этих строк, обычно целесообразно, чтобы FDW¹ выполнял блокировку на уровне строк с максимально близким приближением к семантике, используемой в обычных таблицах QHB. Есть несколько соображений, связанных с этим.

Одним из ключевых решений, которые необходимо принять, является ли выполнять раннюю блокировку (early locking) или позднюю блокировку (late locking). В ранней блокировке строка блокируется, когда она впервые извлекается из базового хранилища, в то время как в поздней блокировке строка блокируется только тогда, когда известно, что она должна быть заблокирована. (Разница возникает потому, что некоторые строки могут быть отброшены локально проверенным ограничением или условиями соединения). Ранняя блокировка намного проще и позволяет избежать дополнительных обходов в удаленный магазин, но она может привести к блокировке строк, которые не должны были быть заблокированы, что приводит к снижению параллелизма или даже неожиданным тупикам. Кроме того, поздняя блокировка возможна только в том случае, если строка, подлежащая блокировке, может быть однозначно повторно идентифицирована позже. Предпочтительно идентификатор строки должен идентифицировать конкретную версию строки, как это делают TIDs QHB.

По умолчанию QHB игнорирует соображения блокировки при взаимодействии с FDW¹, но FDW¹ может выполнять раннюю блокировку без какой-либо явной поддержки со стороны основного кода. Функции API, описанные в разделе FDW-процедуры для блокировки строк, позволяют FDW¹ использовать позднюю блокировку, если она пожелает.

Дополнительным соображением является то, что в READ COMMITTED режим изоляции, QHB, возможно, потребуется повторно проверить ограничение и условия соединения с обновленной версией некоторого целевого кортежа. Повторная проверка условий соединения требует повторного получения копий нецелевых строк, которые ранее были присоединены к целевому кортежу. При работе со стандартными таблицами QHB это делается путем включения tid нецелевых таблиц в список столбцов, проецируемых через соединение, а затем повторной выборки нецелевых строк, когда это требуется. Этот подход сохраняет компактность набора данных соединения, но требует недорогой возможности повторного извлечения, а также TID, который может однозначно определить версию строки, подлежащую повторному извлечению. Поэтому по умолчанию метод, используемый с внешними таблицами, заключается в том, чтобы включить копию всей строки, извлеченной из внешней таблицы, в список столбцов, проецируемых через соединение. Это не ставит никаких особых требований к FDW¹, но может привести к снижению производительности слияния и хеш-соединений. FDW¹, способный удовлетворять требованиям повторной выборки, может выбрать для этого первый способ.

Для UPDATE или DELETE на внешней таблице рекомендуется, чтобы ForeignScan операция над целевой таблицей выполняет раннюю блокировку строк, которые она извлекает, возможно, через эквивалент SELECT FOR UPDATE. FDW¹ может определить, является ли таблица целью UPDATE/DELETE во время плана, сравнивая ее relid с root- > parse - >>resultRelation, или во время выполнения с помощью ExecRelationIsTargetRelation(). Альтернативной возможностью является выполнение поздней блокировки в пределах ExecForeignUpdate или ExecForeignDelete обратный вызов, но никакой специальной поддержки для этого не предусмотрено.

Для внешних таблиц, которые должны быть заблокированы с помощью команды SELECT FOR UPDATE/SHARE, ForeignScan операция может снова выполнить раннюю блокировку путем выборки кортежей с эквивалентом SELECT FOR UPDATE/SHARE. Для выполнения поздней блокировки вместо этого, предоставьте функции обратного вызова, определенные в разделе FDW-процедуры для блокировки строк. В GetForeignRowMarkType, выберите параметр rowmark ROW_MARK_EXCLUSIVE, ROW_MARK_NOKEYEXCLUSIVE, ROW_MARK_SHARE, или ROW_MARK_KEYSHARE в зависимости от требуемого режима блокировки. (Основной код будет действовать одинаково независимо от того, какой из этих четырех вариантов вы выберете). В других местах вы можете определить, была ли внешняя таблица заблокирована этим типом команды с помощью get_plan_rowmark в запланированное время, или ExecFindRowMark во время выполнения; вы должны проверить не только то, возвращается ли ненулевая структура rowmark, но и то, что нет ее strength поля LCS_NONE.

Наконец, для внешних таблиц, которые используются в команде UPDATE, DELETE или SELECT FOR UPDATE/SHARE, но не заданы для блокировки строк, можно переопределить выбор по умолчанию для копирования целых строк, имея GetForeignRowMarkType выбранный вариант ROW_MARK_REFERENCE когда он видит режима блокировки LCS_NONE. Это приведет к тому, что RefetchForeignRow быть вызванным с этим значением для markType; затем он должен повторно получить строку, не приобретая никакого нового замка. (Если у вас есть a GetForeignRowMarkType функция, но не хотите, чтобы повторно получить разблокированные строки, выберите опцию ROW_MARK_COPY для LCS_NONE).

Смотрите src/include/nodes/lockoptions.h, комментарии для RowMarkType и PlanRowMark в src/include/nodes/plannodes.h, и комментарии для ExecRowMark в src/include/nodes/execnodes.h для дополнительной информации.

¹

Foreign Data Wrapper, обёртка сторонних данных

Инструмент резервного копирования qbackup

Синтаксис

qbackup version

qbackup help [команда]

qbackup list -B каталог_копий [параметр...]

qbackup backup -B каталог_копий -D каталог_данных [--incremental]
        [--compress] [--remote] [параметр...]

qbackup replica -B каталог_копий -D каталог_данных [параметр...]

qbackup restore -B каталог_копий -D каталог_данных
        -i идентификатор_копии [параметр...]

qbackup remove -B каталог_копий -i идентификатор_копии ...
        [--single] [параметр...]

qbackup validate -B каталог_копий -i идентификатор_копии
        [-D каталог_данных] [-t тип_проверки] [параметр...]

qbackup backup-wal -B каталог_копий -D каталог_данных -p путь_файла_wal
        -f имя_файла_wal [параметр...]

qbackup restore-wal -B каталог_копий -p путь_файла_wal
        -f путь_для_файла_wal [параметр...]

Описание

qbackup — инструмент для управления резервным копированием и восстановлением кластеров баз данных QHB.

Обзор

По сравнению с другими средствами резервного копирования qbackup имеет следующие преимущества, полезные для реализации различных стратегий резервного копирования и работы с базами данных большого объема:

Постраничное инкрементальное копирование (2 подхода): позволяет сэкономить место на диске и создавать копии быстрее, чем при полном копировании. Восстановление инкрементальных копий также осуществляется быстрее, чем воспроизведение файлов WAL.

Параллельное выполнение: выполнение внутренних процессов команд backup и restore в несколько параллельных потоков.

Сжатие: хранение копируемых данных в сжатом состоянии для экономии дискового пространства.

Каталогизация резервных копий: получение списка резервных копий и соответствующей метаинформации в виде простого текста или JSON.

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

Расширенные возможности валидации резервных копий и восстановленных кластеров.

Для управления резервными копиями qbackup создает каталог резервных копий. В этом каталоге сохраняются все файлы резервных копий с дополнительной метаинформацией, а также архивы WAL, необходимые для восстановления на момент времени.

Используя qbackup, вы можете выполнять полное или инкрементальное резервное копирование:

Полные резервные копии содержат все файлы, необходимые для восстановления кластера баз данных с нуля.

Инкрементальные копии создаются на уровне страниц и включают только те данные, которые изменились со времени последнего копирования. Это позволяет сэкономить место на диске и создавать копии быстрее, чем при полном копировании. Восстановление инкрементальных копий также осуществляется быстрее, чем воспроизведение файлов WAL.

qbackup может производить копирование как работающего, так и остановленного экземпляра. Для копирования WAL используются внутренние механизмы QHB (скрипты archive_command).

Ограничения

В настоящее время qbackup версии 1.5.0 имеет следующие ограничения:

qbackup работает с серверами QHB версии 1.5.0 и новее.

На сервере QHB, где была сделана копия, и на сервере, где она будет восстанавливаться, должны быть одинаковые значения параметров block_size и wal_block_size и одинаковая основная версия. В зависимости от конфигурации кластера, QHB может накладывать дополнительные ограничения, например, по архитектуре процессора и версии libc/libicu.

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

Инкрементальные резервные копии невозможно базировать на резервных копиях, созданных на остановленном сервере.

Термины

Каталог — директория с резервными копиями. Внутри нее могут содержаться директории с резервными копиями, информация о них в файле каталога catalog.json и директория wal. Директорию каталога нужно создать до начала резервного копирования.

Следующие поля в файле каталога являются служебными, но могут быть полезны. Они меняются с каждой минорной версией QHB:

Версия кластера (cluster_version) — бинарная версия QHB. Это номер сборки базы данных.

Версия каталога (qbackup_version) — версия структуры данных в файле каталога.

Установка и подготовка

Установив qbackup, выполните следующие действия:

Создайте каталог копий.

Настройте кластер баз данных для использования qbackup.

Создание каталога копий

qbackup хранит резервные копии в каталоге копий, путь к которому указывается в переменной окружения BACKUPS_DIR или в аргументе командной строки -B (--backups-dir). Каталог копий должен быть создан пользователем перед началом работы с утилитой и не должен содержать никаких файлов. Ручное редактирование файлов в каталоге копий не допускается. Пользователь, запускающий qbackup, должен иметь полный доступ к каталогу_копий и как минимум доступ на чтение всего содержимого каталога_данных.

Настройка кластера баз данных

Хотя qbackup можно использовать от имени суперпользователя, рекомендуется создать отдельную роль с минимальными правами, необходимыми для выбранной стратегии копирования. В этих инструкциях по настройке такой ролью служит роль backup.

Для выполнения резервного копирования роль backup должна иметь следующие разрешения на сервере QHB (только в базе данных, к которой производится подключение):

BEGIN;
CREATE ROLE backup WITH LOGIN;
GRANT USAGE ON SCHEMA pg_catalog TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.pg_start_backup(text, boolean, boolean) TO backup;
GRANT EXECUTE ON FUNCTION pg_catalog.pg_stop_backup(boolean, boolean) TO backup;
COMMIT;

В файле qhb_hba.conf разрешите подключение к кластеру баз данных пользователю с именем backup.

Программа qbackup должна читать непосредственно файлы кластера, поэтому запускать qbackup (или подключаться к нему удаленно) нужно от имени пользователя ОС, который имеет доступ на чтение всех файлов и каталогов внутри каталога данных кластера (PGDATA), подлежащего копированию.

Модифицируйте файл конфигурации qhb.conf:

Установите параметр wal_level в значение выше minimal.

Установите параметр archive_mode в значение on или always.

Установите параметр archive_command:

archive_command='путь_инсталляции/qbackup backup-wal
  -B каталог_копий -f %f -p %p'

Здесь путь_инсталляции — путь к каталогу установленной версии qbackup, которую вы хотите использовать.

Установка archive_command необходима для резервного копирования работающего кластера. Для копирования остановленного кластера устанавливать этот параметр не требуется.

Пример минимально возможной конфигурации для копирования работающего кластера:

# qhb.conf
wal_level='replica'
archive_mode='on'
archive_command='/usr/bin/qbackup backup-wal -B /opt/backup_dir -f %f -p %p'

Если резервное копирование планируется осуществлять с использованием параметра --remote или подкоманды replica, то параметры archive_mode и archive_command можно не настраивать. Все необходимые файлы WAL будут автоматически архивированы (возможны варианты архивации). Подробнее о параметрах для репликации, указываемых в qhb.conf, можно прочитать в подразделе Репликация.

Для инкрементальных резервных копий существует два механизма:

Бинарное сканирование файлов

CDC — захват изменения данных (Change Data Capture)

Для использования второго подхода необходимо настроить параметр qcdc_registry_size. Он принимает размер в мегабайтах. После перезапуска базы данных в ее корне появится файл qhb-cdc.map с указанным размером. Размер может быть любым в указанных далее пределах; для оптимальной работы следует выбирать его исходя из правила: предполагаемый размер кластера / 1000. Максимальное значение этого параметра — 10 Гб (10240 Мб), минимальное — 1 Мб. При значении 0 CDC считается выключенным.

Пример:

# qhb_hba.conf
# Предполагаемый размер кластера — 30GB
qcdc_registry_size=30MB

Если вы используете qbackup через отдельного пользователя, не забудьте дать ему права в qhb_hba.conf.

Пример:

CREATE USER repluser REPLICATION PASSWORD 'repluser';

# qhb_hba.conf
host replication repluser master_ip md5
host replication repluser standby_ip md5
host all repluser master_ip md5
host all repluser standby_ip md5

Если вы используете qbackup вместе с QDL, то обратите внимание на пункт ВАЖНО в шаге 3 раздела Сценарий использования QDL.

Использование

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

Для создания резервной копии выполните следующую команду:

qbackup backup -B каталог_копий -D каталог_данных [--compress] [--incremental]
    [параметры подключения...] [--remote] [параметры репликации...] [параметр...]

qbackup поддерживает два основных режима создания резервных копий:

Полные копии. Содержимое каталога_данных копируется в каталог_копий, за исключением необязательных файлов. В каталоге_копий может содержаться любое количество независимых резервных копий.

Инкрементальные копии. При передаче флага --incremental qbackup автоматически находит последнюю подходящую копию в каталоге_копий и выполняет сравнение ее содержимого с содержимым каталога_данных (подход бинарного сравнения файлов). Второй, более предпочтительный подход, — CDC. При нем qbackup запрашивает у сервера данные об изменившихся файлах и на основе этих данных создает резервную копию. В инкрементальной копии хранятся только те файлы (или части файлов), которые изменились с момента создания полной копии. Это значительно сокращает размер резервной копии, но может потребовать больше времени для выполнения операций сравнения файлов. Эти два подхода совместимы между собой, поэтому CDC можно начать использовать посреди цепочки резервных копий. (Это экспериментальная функция, но, несмотря на это, резервные копии всегда будут консистентны.) Обратите внимание, что инкрементальные резервные копии могут создаваться продолжительное время и иметь большой размер.

При восстановлении кластера из инкрементальной копии qbackup использует родительскую полную копию и все инкрементальные копии между ними, в совокупности образующие «цепочку копий». Таким образом, прежде чем делать инкрементальные копии, необходимо сделать как минимум одну полную.

Флаг --compress значительно уменьшает размер резервной копии, но может привести к замедлению процесса копирования.

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

Создание реплики кластера

Для создания реплики выполните следующую команду:

qbackup replica -B каталог_копий -D каталог_данных [--compress] [параметры подключения...]
    [параметры репликации...] [параметр...]

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

Эта подкоманда полезна для создания резервного сервера.

Флаг --compress значительно уменьшает размер резервной копии, но может привести к замедлению процесса копирования.

Восстановление кластера

Чтобы восстановить кластер баз данных из резервной копии, выполните команду restore:

qbackup restore -B каталог_копий -D каталог_данных
  -i идентификатор_копии [--tablespace-mapping=старый_путь=новый_путь] [параметры...]

Здесь:

каталог_копий — каталог, в котором хранятся все файлы резервных копий и метаданные.

каталог_данных — каталог, в котором будут храниться данные восстановленного кластера. Этот каталог должен быть создан перед выполнением команды, быть пустым и доступным для записи.

идентификатор_копии определяет, из какой резервной копии будет восстановлен кластер. Если вы выбираете для восстановления инкрементальную копию, qbackup автоматически восстанавливает нижележащую полную копию и затем последовательно применяет все необходимые добавления.

См. описание параметра --tablespace-mapping.

По окончании работы команды restore запустите службу базы данных, используя qhb_ctl.

Выполнение восстановления на момент времени (PITR)

Вы можете восстановить состояние кластера на любой момент времени (до заданной точки восстановления), используя с командой restore параметры точки восстановления.

Чтобы восстановить состояние кластера на определенный момент времени, укажите это время в параметре --target-time, в формате timestamp. Например:

qbackup restore -B каталог_копий -D каталог_данных
  -i идентификатор_копии --target-time='2017-05-18 14:18:11+03'

Чтобы восстановить состояние кластера до определенной транзакции, воспользуйтесь параметром --target-xid. Например:

qbackup restore -B каталог_копий -D каталог_данных
  -i идентификатор_копии --target-xid=687

Чтобы восстановить состояние кластера до определенной позиции в журнале (LSN), воспользуйтесь параметром --target-lsn. Например:

qbackup restore -B каталог_копий -D каталог_данных
  -i идентификатор_копии --target-lsn=16/B374D848

Чтобы восстановить состояние кластера до заданной именованной точки восстановления, воспользуйтесь параметром --target-name. Например:

qbackup restore -B каталог_копий -D каталог_данных
  -i идентификатор_копии --target-name='before_app_upgrade'

Чтобы восстановить самое раннее из возможных согласованное состояние кластера, укажите флаг --immediate:

qbackup restore -B каталог_копий -D каталог_данных
  -i идентификатор_копии --immediate

В дополнение к параметрам, устанавливающим точку восстановления можно также указать параметры --action, --timeline и --inclusive.

Валидирование резервных копий

Команда validate позволяет проверить (и в некоторых случая восстановить) целостность резервной копии, а так же восстановленного кластера. Существует три вида проверки:

Checksum (режим по умолчанию)

По умолчанию по окончании резервного копирования qbackup подсчитывает контрольную сумму всех файлов внутри директории резервной копии, что позволяет впоследствии проверять их консистентность. Эта опция позволяет сравнить эту контрольную сумму с той, которая будет вычислена в процессе проверки.

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

QhbChecksum

Позволяет проверить контрольные суммы файлов таблиц баз данных. Эта функция аналогична qhb_checksums, за тем исключением, что данный режим работает с каталогизированными qbackup резервными копиями. Если резервная копия была сделана с включенными контрольными суммами, то данная опция позволяет сверить их. Если контрольные суммы были выключены в кластере на момент резервного копирования, то флаг -e --enable позволяет включить их. Может использоваться исключительно на полных несжатых резервных копиях.

Database

Этот режим позволяет бинарно сравнить файлы внутри резервной копии с файлами кластера. Проверка должна производится на выключенном сервере. Если будут найдены повреждённые файлы, то будет предложено их восстановить. Эта опция полезна для проверки только что восстановленной базы данных, а также тех файлов таблиц, которые не должны были измениться с момента восстановления. Этим режимом можно также проверить только что созданную/восстановленную не архивированную полную резервную копию.

Пример:

$ qbackup backup -B /tmp/backup -h localhost -u user1 -fc # полная сжатая резервная копия ID: QZWGEI
$ qbackup backup -B /tmp/backup -h localhost -u user1 -fi # инкрементальная резервная копия ID: QZWGFJ

$ qbackup validate -B /tmp/cat -t checksum -i QZWGFJ # Проверка типа Checksum
$ qbackup validate -B /tmp/cat -t qhbchecksum -i QZWGEI # Проверка типа QhbChecksum

$ qbackup restore -B /tmp/backup -i QZWGFJ -D /tmp/qhb-data/ # Восстановление

# Предположительное повреждение файла `/tmp/qhb-data/base/12677/175`

$ qbackup validate -B /tmp/backup -i QZWGFJ -D /tmp/qhb-data/ -t database # Проверка типа database
You are going to restore
/tmp/cat/QZWH7I/base/12677/175 into /tmp/qhb-data/base/12677/175
You are going to restore
/tmp/cat/QZWH7I/base/12677/175 into /tmp/qhb-data/base/12677/175
 yes

$ qbackup validate -B /tmp/backup -i QZWGFJ -D /tmp/qhb-data/ -t database # Проверка типа database (успешная)

При использовании режима проверки database можно использовать флаг -y --yes, позволяющий автоматически согласится на восстановление

Заметка
В каких ситуациях восстановить файл не получится?

Повреждены одинаковые файлы внутри резервной копии и внутри кластера

Файл были изменён с помощью СУБД

Файл был полностью удалён

В файле повреждён заголовок (информация о последнем изменении)

Заметка
Если вы видите предупредительное сообщение о том, что файл не найден, то, скорее всего, за время работы СУБД, были созданы новые файлы таблиц. Если это на так, то это означает потерю файла в резервной копии.

Запуск qbackup в параллельных потоках

Команды backup и restore могут выполняться в несколько параллельных потоков. Это может существенно ускорять работу qbackup при наличии достаточных ресурсов (ядер процессора, производительности дисковой подсистемы и сети).

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

qbackup backup -B каталог_копий -D каталог_данных [--compress] [--incremental] -j 4

По умолчанию, или если заданное число потоков равно 0, qbackup автоматически выбирает число потоков, которое обеспечит максимальную производительность копирования (в ущерб другим операциям).

Управление каталогом резервных копий

С помощью qbackup вы можете управлять резервными копиями в командной строке:

Просматривать имеющиеся резервные копии

Удалять резервные копии

Просмотр информации о резервных копиях

Чтобы просмотреть список существующих копий для каждого экземпляра, выполните команду list:

qbackup list -B каталог_копий

qbackup выводит список всех имеющихся резервных копий. Например:

+--------+--------+----------+-----------------+------------------------+-----------+--------+---------------------+---------------------+---------+
|   ID   | status |   size   | compressed size |          kind          | start lsn | parent |     start time      |      end time       | comment |
+--------+--------+----------+-----------------+------------------------+-----------+--------+---------------------+---------------------+---------+
| QYAEEO |  Done  | 30.87MiB |        —        |    Full (Physical)     | 0/2000028 |   —    | 2021-08-23 09:59:12 | 2021-08-23 09:59:12 | Planned |
+--------+--------+----------+-----------------+------------------------+-----------+--------+---------------------+---------------------+---------+
| QYAEFN |  Done  | 73.64KiB |        —        | Incremental (Physical) | 0/4000028 | QYAEEO | 2021-08-23 09:59:47 | 2021-08-23 09:59:47 |    —    |
+--------+--------+----------+-----------------+------------------------+-----------+--------+---------------------+---------------------+---------+
| QYAEGO |  Done  | 30.87MiB |     4.68MiB     |    Full (Physical)     | 0/6000028 |   —    | 2021-08-23 10:00:24 | 2021-08-23 10:00:24 |    —    |
+--------+--------+----------+-----------------+------------------------+-----------+--------+---------------------+---------------------+---------+
| QYAEM1 | Error  |    0B    |        —        |     Full (Remote)      |     —     |   —    | 2021-08-23 10:03:37 | 2021-08-23 10:03:37 |    —    |
+--------+--------+----------+-----------------+------------------------+-----------+--------+---------------------+---------------------+---------+

Для каждой копии выдаются следующие сведения:

ID — уникальный идентификатор копии.

status — состояние резервной копии. Возможные варианты:

Done — резервная копия выполнена и готова к использованию;

In Progress — резервное копирование еще выполняется;

Error — резервное копирование завершилось с ошибкой.

size — размер резервной копии (в несжатом виде).

compressed size — размер резервной копии на диске (только для сжатых копий).

kind — тип резервной копии. Возможные варианты:

Full (Physical) — полная физическая резервная копия;

Incremental (Physical) — инкрементальная физическая резервная копия;

Full (Remote) — полная удаленная резервная копия;

Incremental (Remote) — инкрементальная удаленная резервная копия.

is compressed — сжата ли копия.

start lsn — LSN (последовательный номер в журнале WAL, Log Sequence Number) в момент начала копирования. Отсутствует для копий остановленного кластера.

parent — идентификатор предыдущей копии для инкрементальных копий;

start time — системное время начала резервного копирования;

end time — системное время окончания резервного копирования.

Также можно получить подробную информацию о резервных копиях в формате JSON:

qbackup list -B каталог_копий --json

Пример вывода:

[
    {
        "identifier": "QSZR4B",
        "status": "Ok",
        "size": 32138205,
        "compressed_size": 4793242,
        "compressed": true,
        "kind": {
            "Full": "Remote"
        },
        "start_lsn": "0/6000028",
        "start_time": "2021-05-12T11:12:11.215543311Z",
        "end_time": "2021-05-12T11:12:11.634187960Z",
        "parent": null
    },
    {
        "identifier": "QSZRC1",
        "status": {
            "Error": "The database is in active state but connection parameters were not supplied"
        },
        "size": 0,
        "compressed_size": null,
        "compressed": false,
        "kind": {
            "Full": "Physical"
        },
        "start_lsn": null,
        "start_time": "2021-05-12T11:16:49.101115677Z",
        "end_time": "2021-05-12T11:16:49.108127415Z",
        "parent": null
    }
]

Удаление резервных копий

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

qbackup remove -B каталог_копий -i идентификатор_копии [идентификатор_копии_2]

Эта команда удалит резервную копию с заданным идентификатором_копии вместе со всеми инкрементальными копиями, которые от нее зависят (если таковые найдутся). Также производится удаление связных WAL-файлов (если таковые найдутся), это можно выключить используя опцию --keep-wal-files. Таким образом вы можете удалить некоторые последние инкрементальные копии из «цепочки копий». Опции -i можно передавать несколько идентификаторов через пробел. Перед удалением программа выводит информацию об удаляемых резервных копиях и требует дополнительного подтверждения, это можно выключить с помощью флага -y.

Подробнее о расширенных параметрах можно прочитать в дополнительные параметры

Справка по командной строке

Команды

В этом подразделе описываются команды qbackup. Необязательные параметры этих команд заключаются в квадратные скобки. Подробно все параметры описываются ниже в подразделе Параметры.

version

qbackup version

Выводит версию qbackup.

help

qbackup help [команда]

Выдает справку по командам qbackup. Если в параметрах задается одна из команд qbackup, выводит подробную информацию по параметрам, которые принимает эта команда.

Аналогичную роль выполняет флаг --help, доступный для любой из команд.

list

qbackup list -B каталог_копий [--json] [--help]

Показывает содержимое каталога копий.

По умолчанию содержимое каталога представляется в виде обычного текста. Можно передать флаг --json, чтобы получить результат в формате JSON.

Более подробно использование этой команды описывается в подразделе Управление каталогом резервных копий.

backup

qbackup backup -B каталог_копий -D каталог_данных
[--compress] [--incremental]
[--help] [-j число_потоков] [--progress] [--remote] [парметры репликации...]

Создает резервную копию экземпляра QHB.

Более подробно использование этой команды описывается в подразделе Создание резервной копии.

Подробнее о расширенных параметрах можно прочитать в дополнительные параметры

restore

qbackup restore -B каталог_копий -D каталог_данных -i идентификатор_копии
[--help]
[-j число_потоков] [--progress]
[параметры точки восстановления...]

Восстанавливает экземпляр QHB из резервной копии, расположенной в каталоге каталог_копий.

Более подробно использование этой команды описывается в подразделе Восстановление кластера. За подробностями по параметрам точки восстановления обратитесь к разделу Выполнение восстановления на момент времени (PITR).

replica

qbackup replica -B каталог_копий -D каталог_данных
[--compress] [--help] [--progress] [парметры репликации...]

Создает реплику экземпляра QHB.

Более подробно использование этой команды описывается в подразделе Создание реплики кластера.

remove

qbackup remove -B каталог_копий -i идентификатор_копии [идентификатор_копии_2]...  [--single]
[--help] [--progress] [--keep-wal-files]

Удаляет копию с заданным идентификатором (идентификатор_копии) и все инкрементальные копии, которые от нее зависят. Если передан флаг --single, удаляет только указанную копию.

Подробнее о расширенных параметрах можно прочитать в дополнительные параметры

Более подробно использование этой команды описывается в подразделе Удаление резервных копий.

validate

qbackup validate -B каталог_копий -i идентификатор_копии [-D каталог_данных] [-t тип_проверки]
[--help] [--progress] [параметр...]

Проверяет корректность резервной копии с идентификатором (идентификатор_копии). Существует три типа проверки: Checksum, QhbChecksum, Database, читайте подробнее в Валидирование резервных копий

backup-wal

qbackup backup-wal -B каталог_копий --wal-file-name=имя_файла_wal --wal-file-path=путь_к_файлу_wal
[--help]

Копирует файлы WAL в соответствующий подкаталог каталога копий.

Команду backup-wal можно использовать в значении параметра archive_command QHB при настройке непрерывного архивирования WAL. За подробностями обратитесь к подразделу Настройка кластера баз данных.

restore-wal

qbackup restore-wal -B каталог_копий --destination-wal-file-path=путь_и_имя_файла_wal --source-wal-file-path=путь_к_исходному_файлу_wal
[--help]

Копирует файлы WAL в соответствующий подкаталог с нужным именем.

Команда restore-wal автоматически используется для restore_command в qbackup.conf после восстановления.

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

restore_command='qbackup restore-wal -f /tmp/catalog/wal/%f -p %p -B /tmp/catalog'

Параметры

Общие параметры

Общие параметры поддерживаются большей частью команд qbackup.

-j
--threads

Позволяет указать количество потоков для операций, которые поддерживают параллельное выполнение (backup и restore).

По умолчанию, если значение параметра не установлено или равно 0, используется оптимальное число потоков для данной системы.

-P
--progress

Выводит интерактивную информацию о процессе выполнения операции. Предназначено для отображения пользователям. При использовании с параметром --remote также получает информацию о размере табличных пространств.

-v
--verbose

Выводит более подробную информацию о процессе выполнения операции.

Параметры подключения

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

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

-u
--user

Имя пользователя базы, от имени которого выполняется копирование.

Если установлена переменная среды QBACKUP_USER, используется ее значение. Указанный параметр в командной строке имеет приоритет над переменной среды.

-h
--host

Указывает имя хоста компьютера, на котором работает сервер. Если значение начинается с косой черты, оно используется в качестве каталога для сокета домена Unix. Множественные имена хоста можно перечислить через запятую. Каждое из имен будет опробовано последовательно.

Если установлена переменная среды QBACKUP_HOST, используется ее значение. Указанный параметр в командной строке имеет приоритет над переменной среды.

--db-name

Имя базы данных для подключения. По умолчанию соответствует имени пользователя.

Если установлена переменная среды QBACKUP_DBNAME, используется ее значение. Указанный параметр в командной строке имеет приоритет над переменной среды.

-p
--port

Порт для подключения к базе. Несколько портов можно перечислить через запятую.

Число указанных портов может быть равно 1, (в этом случае для всех хостов используется один и тот же порт), либо должно соответствовать указанному число хостов. Значение по умолчанию равно 5432.

Если установлена переменная среды QBACKUP_PORT, используется ее значение. Указанный параметр в командной строке имеет приоритет над переменной среды.

--password

Пароль пользователя для подключения к базе.

Если установлена переменная среды QBACKUP_PASSWORD, используется ее значение. Указанный параметр в командной строке имеет приоритет над переменной среды. Если пароль не указан, но он потребовался в процессе работы программы, он будет запрошен в диалоговом режиме. После первой неверной попытки будет выведено сообщение об ошибке аутентификации.

-w
--no-password

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

Параметры точки восстановления

Для команды restore можно указать дополнительные параметры, регулирующие восстановление до определенной точки (PITR).

Используются только в команде restore.

--target-time=момент_времени

Восстановление до определенного момента времени в формате timestamp.

Значение этого параметра задается в том же формате, что принимается типом данных timestamp with time zone, за исключением того, что в нем нельзя использовать сокращенное название часового пояса. Поэтому рекомендуется задавать числовое смещение от UTC или записывать название часового пояса полностью, например Europe/ Helsinki (но не EEST).

--target-xid=xid_транзакции

Восстановление на определенный идентификатор транзакции.

Имейте в виду, что числовое значение идентификатора отражает последовательность именно старта транзакций, а фиксироваться они могут в ином порядке. Восстановлению будут подлежать все транзакции, которые были зафиксированы до указанной (и, возможно, включая ее, в зависимости от значения параметра --inclusive).

--target-lsn=LSN

Восстановление на определенный LSN. Этот параметр принимает значение системного типа данных pg_lsn.

--target-name=имя_точки_восстановления

Восстановление на определенную именованную точку восстановления, созданную с помощью pg_create_restore_point().

--immediate

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

--action=[ pause | promote | shutdown ]

Указывает, какое действие должен предпринять сервер после достижения цели восстановления. Вариант по умолчанию — pause, что означает приостановку восстановления. Второй вариант, promote, означает, что процесс восстановления завершится, и сервер начнет принимать подключения. Наконец, с вариантом shutdown сервер остановится, как только целевая точка восстановления будет достигнута.

Вариант pause позволяет выполнить запросы к базе данных и убедиться в том, что достигнутая цель оказалась желаемой точкой восстановления. Для снятия с паузы нужно вызвать pg_wal_replay_resume(), что в итоге приведет к завершению восстановления. Если окажется, что желаемая точка восстановления еще не достигнута, нужно остановить сервер, установить более позднюю цель и перезапустить сервер для продолжения восстановления.

Вариант shutdown полезен для получения готового экземпляра сервера в желаемой точке. При этом данный экземпляр сможет воспроизводить дополнительные записи WAL (а при перезапуске ему придется воспроизводить записи WAL после последней контрольной точки).

Заметьте, что так как recovery.signal не переименовывается, когда в --action выбран вариант shutdown, при последующем запуске будет происходить немедленная остановка, пока вы не измените конфигурацию или не удалите файл recovery.signal вручную.

Этот параметр не действует, если целевая точка восстановления не установлена.

--timeline=[ current | latest | timeline_id ]

Указывает линию времени для восстановления. Значение может задаваться числовым идентификатором линии времени или ключевым словом (timeline_id). С ключевым словом current восстанавливается та линия времени, которая была активной при создании базовой резервной копии. С ключевым словом latest восстанавливаться будет последняя линия времени, найденная в архиве, что полезно для ведомого сервера. По умолчанию подразумевается latest.

--inclusive=[ on | off ]

Прекратить восстановление в момент достижения заданной точки (on) или непосредственно перед ее достижением (off). По умолчанию установлено on.

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

-R
--write-recovery-conf

Записать строку подключения к основному серверу в qhb.auto.conf. Записывает опцию(и) primary_slot_name и primary_conninfo. Это вспомогательный параметр, при ошибке подключения к основному серверу нужно отредактировать эти параметры вручную.

-r
--max-rate=максимальная_скорость

Ограничить скорость передачи файлов. Принимает суффиксы k — Кб и M - Мб. Принимает значения в пределах от 32 кБ/с до 1024 МБ/с.

-X
--wal-method=[ stream | fetch | none ]

Способ архивирования WAL-файлов во время репликации.

Здесь:

stream — архивировать WAL в отдельном потоке параллельно. Это опция по умолчанию. Файлы архивируются в путь_каталога/wal/.

fetch — архивировать WAL вместе с файлами кластера. Они будут расположены внутри директории $PGDATA/pg_wal/.

none — не архивировать файлы WAL.

Важно!!!
При выборе метода none файлы WAL НЕ будут архивированы! Вам требуется архивировать файлы WAL вручную, иначе резервная копия будет недействительна!

-S
--slotname=имя_слота

Создать слот репликации для архивирования WAL-файлов. По умолчанию создается временный слот репликации, этот параметр создает постоянный слот репликации.

Заметка
Имя слота репликации может включать в себя только английские буквы нижнего регистра (a-z), цифры (0-9) и символ нижнего подчеркивания _.

--no-slot

Не создавать слот репликации при удаленном копировании WAL-файлов.

Важно!!!
При отсутствии слота репликации необходимые файлы WAL могут быть удалены сервером прежде, чем будут архивированы! Это может привести к повреждению резервной копии! Вы должны убедиться, что все файлы WAL будут архивированы.

-T
--tablespace-mapping=старый_путь=новый_путь

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

Здесь:

старый_путь — оригинальный путь табличного пространства.

новый_путь — новый путь (путь адресации) табличного пространства.

Прочие параметры

Дополнительные опции для команды backup

-f
--fast-checkpoint

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

--skip-broken

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

-C --comment

Эта опция позволяет добавить комментарий для резервной копии.

--no-checksum

Этот флаг позволяет выключить подсчет контрольной суммы резервной копии. Применяйте только если уверены, что осознаете все последствия.

Дополнительные опции для команды remove

-y --yes

Позволяет удалить резервные копии без дополнительного предостережения.

--prune

Флаг, позволяющий удалить все резервные копии.

--single

Флаг, позволяющий удалить одну копию без удаления цепочки.

Важно!!!
После удаления резервной копии из цепочки все копии, сделанные позже нее, становятся недействительными!

-K
--keep-wal-files

Этот флаг позволяет удалить резервную копию из каталога без удаления связанных с ней WAL-файлов (если таковые найдутся). Это может быть полезно, если вы хотите использовать файлы журналов для Выполнение восстановления на момент времени (PITR).

Обратите внимание, что автоматическая очистка WAL-файлов возможно только для резервных копий, сделанных qbackup версии 1.5.0 и выше.

Версионирование

При разработке qbackup используется семантическое версионирование semver.

Часть III. Язык SQL

В этой части описывается использование языка SQL в QHB. Мы начнем с общего синтаксиса SQL, затем объясним, как создавать структуры для хранения данных, как наполнять базу данных и как выполнять к ней запросы. Далее перечислим существующие типы данных и функции, применяемые с командами SQL. И закончим рассмотрением нескольких важных для оптимальной производительности аспектов.

Главы, представленные в этой части упорядочены определенным образом, чтобы
даже новичок мог проработать информацию последовательно, от начала до конца. При этом главы самодостаточны, так что опытные пользователи могут читать их так, как им будет удобно. Полное описание конкретных команд представлено в главе Команды SQL.

Читатели этой части должны знать, как подключиться к базе данных QHB и выполнять команды SQL. Если вы этого еще не знаете, то рекомендуется сначала прочитать первую и вторую часть. Команды SQL обычно вводятся с использованием интерактивного терминала QHB psql, но также можно использовать и другие программы с аналогичной функциональностью.

Язык SQL

Язык SQL. Введение

Основные принципы

Создание таблицы

Ввод данных

Запросы к таблице

Соединения таблиц

Агрегатные функции

Изменение строк

Удаление строк

Расширенные функции

Расширенные функции. Вступление

Представления (VIEW)

Внешние ключи

Транзакции

Оконные функции

Синтаксис SQL

Лексическая структура

Выражения значения

Вызов функции

Определение данных

Базовая информация по таблицам

Значения по умолчанию

Сгенерированные столбцы

Ограничения

Системные столбцы

Изменение таблиц

Права

Политики защиты строк

Схемы

Наследование

Партиционирование таблиц

Внешние данные

Другие объекты базы данных

Отслеживание зависимостей

Манипулирование данными

Вставка данных

Обновление данных

Удаление данных

Возврат данных из измененных строк

Запросы

Обзор

Табличные выражения

Списки выборки

Сочетание запросов (UNION, INTERSECT, EXCEPT)

Сортировка строк (ORDER BY)

LIMIT и OFFSET

Списки VALUES

Запросы WITH (общие табличные выражения)

Типы данных

Числовые типы

Денежные типы

Символьные типы

Двоичные типы данных

Типы даты / времени

Логический тип

Перечислимые типы

Геометрические типы

Типы сетевых адресов

Типы битовых строк

Типы для текстового поиска

Тип UUID

Тип XML

Типы JSON

Массивы

Составные типы

Диапазонные типы

Типы доменов

Типы идентификаторов объектов

Тип pg_lsn

Псевдо-типы

Функции и операторы

Логические операторы

Функции и операторы сравнения

Математические функции и операторы

Строковые функции и операторы

Функции и операторы для двоичных строк

Функции и операторы для битовых строк

Сопоставление с шаблоном

Функции форматирования типов данных

Функции и операторы даты/времени

Функции поддержки перечислений

Геометрические функции и операторы

Функции и операторы для сетевых адресов

Функции и операторы текстового поиска

Функции XML

Функции и операторы JSON

Функции управления последовательностями

Условные выражения

Функции и операторы для массивов

Диапазонные/мультидиапазонные функции и операторы

Агрегатные функции

Оконные функции

Выражения подзапроса

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

Функции возврата наборов (SET)

Системные информационные функции и операторы

Функции системного администрирования

Триггерные функции

Функции запуска событий

Функции статистической информации

PL / pgSQL - процедурный язык SQL

Структура PL / pgSQL

Объявления

Выражения

Основные положения

Управляющие структуры

Курсоры

Управление транзакциями

Ошибки и сообщения

Триггерные функции PL / pgSQL

PL / pgSQL под капотом

Советы по разработке на PL / pgSQL

Портирование из Oracle PL / SQL

Команды SQL

ABORT

ALTER AGGREGATE

ALTER COLLATION

ALTER CONVERSION

ALTER DATABASE

ALTER DEFAULT PRIVILEGES

ALTER DOMAIN

ALTER EVENT TRIGGER

ALTER EXTENSION

ALTER FOREIGN DATA WRAPPER

ALTER FOREIGN TABLE

ALTER FUNCTION

ALTER GROUP

ALTER INDEX

ALTER LANGUAGE

ALTER LARGE OBJECT

ALTER MATERIALIZED VIEW

ALTER OPERATOR

ALTER OPERATOR CLASS

ALTER OPERATOR FAMILY

ALTER POLICY

ALTER PROCEDURE

ALTER PUBLICATION

ALTER ROLE

ALTER ROUTINE

ALTER RULE

ALTER SCHEMA

ALTER SEQUENCE

ALTER SERVER

ALTER STATISTICS

ALTER SUBSCRIPTION

ALTER SYSTEM

ALTER TABLE

ALTER TABLESPACE

ALTER TEXT SEARCH CONFIGURATION

ALTER TEXT SEARCH DICTIONARY

ALTER TEXT SEARCH PARSER

ALTER TEXT SEARCH TEMPLATE

ALTER TRIGGER

ALTER TYPE

ALTER USER

ALTER USER MAPPING

ALTER VIEW

ANALYZE

BEGIN

CALL

CHECKPOINT

CLOSE

CLUSTER

COMMENT

COMMIT

COMMIT PREPARED

COPY

CREATE ACCESS METHOD

CREATE AGGREGATE

CREATE CAST

CREATE COLLATION

CREATE CONVERSION

CREATE DATABASE

CREATE DOMAIN

CREATE EVENT TRIGGER

CREATE EXTENSION

CREATE FOREIGN DATA WRAPPER

CREATE FOREIGN TABLE

CREATE FUNCTION

CREATE GROUP

CREATE INDEX

CREATE LANGUAGE

CREATE MATERIALIZED VIEW

CREATE OPERATOR

CREATE OPERATOR CLASS

CREATE OPERATOR FAMILY

CREATE POLICY

CREATE PROCEDURE

CREATE PUBLICATION

CREATE ROLE

CREATE RULE

CREATE SCHEMA

CREATE SEQUENCE

CREATE SERVER

CREATE STATISTICS

CREATE SUBSCRIPTION

CREATE TABLE

CREATE TABLE AS

CREATE TABLESPACE

CREATE TEXT SEARCH CONFIGURATION

CREATE TEXT SEARCH DICTIONARY

CREATE TEXT SEARCH PARSER

CREATE TEXT SEARCH TEMPLATE

CREATE TRANSFORM

CREATE TRIGGER

CREATE TYPE

CREATE USER

CREATE USER MAPPING

CREATE VIEW

DEALLOCATE

DECLARE

DELETE

DISCARD

DO

DROP ACCESS METHOD

DROP AGGREGATE

DROP CAST

DROP COLLATION

DROP CONVERSION

DROP DATABASE

DROP DOMAIN

DROP EVENT TRIGGER

DROP EXTENSION

DROP FOREIGN DATA WRAPPER

DROP FOREIGN TABLE

DROP FUNCTION

DROP GROUP

DROP INDEX

DROP LANGUAGE

DROP MATERIALIZED VIEW

DROP OPERATOR

DROP OPERATOR CLASS

DROP OPERATOR FAMILY

DROP OWNED

DROP POLICY

DROP PROCEDURE

DROP PUBLICATION

DROP ROLE

DROP ROUTINE

DROP RULE

DROP SCHEMA

DROP SEQUENCE

DROP SERVER

DROP STATISTICS

DROP SUBSCRIPTION

DROP TABLE

DROP TABLESPACE

DROP TEXT SEARCH CONFIGURATION

DROP TEXT SEARCH DICTIONARY

DROP TEXT SEARCH PARSER

DROP TEXT SEARCH TEMPLATE

DROP TRANSFORM

DROP TRIGGER

DROP TYPE

DROP USER

DROP USER MAPPING

DROP VIEW

END

EXECUTE

EXPLAIN

FETCH

GRANT

IMPORT FOREIGN SCHEMA

INSERT

LISTEN

LOAD

LOCK

MOVE

NOTIFY

PREPARE

PREPARE TRANSACTION

REASSIGN OWNED

REFRESH MATERIALIZED VIEW

REINDEX

RELEASE SAVEPOINT

RESET

REVOKE

ROLLBACK

ROLLBACK PREPARED

ROLLBACK TO SAVEPOINT

SAVEPOINT

SECURITY LABEL

SELECT

SELECT INTO

SET

SET CONSTRAINTS

SET ROLE

SET SESSION AUTHORIZATION

SET TRANSACTION

SHOW

START TRANSACTION

TRUNCATE

UNLISTEN

UPDATE

VACUUM

VALUES

Поддержка SQL-стандарта

Ключевые слова

Коды ошибок

Поддержка даты и времени

Соответствие стандарту SQL

Расширенный SQL

Язык SQL. Общие сведения

Язык SQL. Введение

Основные принципы

Создание таблицы

Ввод данных

Запросы к таблице

Соединения таблиц

Агрегатные функции

Изменение строк

Удаление строк

Язык SQL. Введение

В этой главе рассматриваются простейшие команды SQL. Эта глава знакомит вас с SQL, но никоим образом не является полным справочным руководством. Вам следует учитывать, что некоторые возможности языка QHB являются расширениями стандарта.

В следующих примерах мы предполагаем, что вы создали базу данных mydb, как описано в разделе Начало работы, и смогли запустить psql, как описано в разделе psql.

Примеры в этом руководстве также можно найти в дистрибутиве исходного кода QHB в каталоге src/tutorial/. (Бинарные дистрибутивы QHB могут не компилировать эти файлы). Чтобы использовать эти файлы, перейдите в этот каталог и запустите make:

$ cd ..../src/tutorial
$ make

Эта команда создает сценарии и компилирует файлы C/RUST, содержащие пользовательские функции и типы. Затем, чтобы начать обучение, выполните следующее:

$ cd ..../tutorial
$ psql -s mydb

...

mydb=> \i basics.sql

Команда \i считывает команды из указанного файла. Параметр psql -s переводит \psql в пошаговый режим, с задержкой перед отправкой каждого оператора на сервер. Команды, используемые в этом разделе, находятся в файле basics.sql.

Основные принципы

QHB — это реляционная система управления базами данных (РСУБД). Это означает, что это система управления данными, представленными в виде отношений. Отношение — это, по сути, двумерная таблица. Хранение данных в таблицах на сегодняшний день очень распространено, но вместе с тем существует ряд других способов организации баз данных. К примеру, файлы и каталоги в Unix-подобных операционных системах образуют иерархическую базу данных. Также существуют объектно-ориентированные базы данных.

Любая таблица представляет собой именованный набор строк. Все строки таблицы имеет одинаковый набор именованных столбцов, и каждый столбец имеет определенный тип данных. Несмотря на то что порядок столбцов фиксированный, важно помнить, что SQL никоим образом не гарантирует порядок строк в таблице (хотя им можно задать правила сортировки при выводе).

Таблицы объединены в базы данных, а набор баз данных, управляемых одним сервером QHB, составляет кластер баз данных.

Создание таблицы

Вы можете создать таблицу, указав имя таблицы и все именами столбцов, с указанием их типов:

CREATE TABLE weather (
    city            varchar(80),
    temp_lo         int,           -- low temperature
    temp_hi         int,           -- high temperature
    prcp            real,          -- precipitation
    date            date
);

Этот текст можно также ввести в в psql вместе с переносами строк. psql поймет, что команда продожается до точки с запятой.

В командах можно использовать пробельные символы (пробелы, табуляции и переводы строк). То есть вы можете ввести команду, выровняв её иначе, или даже уместив в одной строке. Два минуса («--») обозначают начало комментария. Все, что идёт за ними до конца строки игнорируется. SQL нечувствителен к регистру ключевых слов и идентификаторов, за исключением случаев, когда идентификаторы заключены в двойные кавычки.

Используемые типы данных:

varchar(80) - тип данных, который может хранить произвольные строки символов длиной до 80 символов.

int - обычный целочисленный тип;

real - числа с плавающей запятой одинарной точности;

date тип для хранения даты.

QHB поддерживает стандартные типы SQL int, smallint, real, double precision, char(N), varchar(N), date, time, timestamp и interval, а также другие общие типы и богатый набор геометрических типов. QHB может быть расширен произвольными наборами пользовательских типов данных. Поэтому имена типов не являются ключевыми словами, кроме тех случаев, когда это требуется для поддержки особых случаев в стандарте SQL.

Во второй таблице будут храниться города и их географическое местоположение:

CREATE TABLE cities (
    name            varchar(80),
    location        point
);

Тип point - пример специфического типа данных QHB.

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

DROP TABLE tablename;

Ввод данных

Для добавления строк в таблицу используется команда INSERT:

INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27');

Обратите внимание, что для всех типов данных используются довольно очевидные форматы. Константы, за исключением простых числовых значений, должны быть заключены в одинарные кавычки (’), аналогично примеру выше. Тип date самом деле очень гибкий и принимает разные форматы, но лучше придерживаться формата, показанного здесь.

Тип point требует ввода пары координат:

INSERT INTO cities VALUES ('San Francisco', '(-194.0, 53.0)');

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

INSERT INTO weather (city, temp_lo, temp_hi, prcp, date)
    VALUES ('San Francisco', 43, 57, 0.0, '1994-11-29');

Также можно перечислить столбцы в другом порядке или даже пропустить некоторые столбцы, например, если осадки нам неизвестны:

INSERT INTO weather (date, city, temp_hi, temp_lo)
    VALUES ('1994-11-29', 'Hayward', 54, 37);

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

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

Чтобы загрузить большой объем данных из текстовых файлов можно использовать команду COPY. Часто такая загрузка проходит быстрее, потому что команда COPY оптимизирована для этого применения, хотя и менее гибка, чем INSERT. Например, COPY можно применить так:

COPY weather FROM '/home/user/weather.txt';

Файл weather.txt должен быть доступен на компьютере, где выполняется серверный процесс, а не на клиенте, т.к. файл будет прочитан непосредственно на сервере.

Запросы к таблице

Для выборки информации из таблиц баз данных используется оператор SQL SELECT. Общий вид оператора представлен ниже

[WITH запросы_with]
SELECT [DISTINCT] перечень_столбцов
FROM перечень_таблиц
[WHERE условия]
[GROUP BY  список_группировки]
[HAVING условия_группировки]
[ORDER BY условия_сортировки]

Следующий запрос позволяет получить все строки таблицы weather:

SELECT * FROM weather;

Здесь символ * означает «все столбцы¹». Этот же результат можно получить с помощью запроса:

SELECT city, temp_lo, temp_hi, prcp, date FROM weather;

Результат будет следующий:

city           | temp_lo | temp_hi | prcp |    date
---------------+---------+---------+------+------------
San Francisco  |      46 |      50 | 0.25 | 1994-11-27
San Francisco  |      43 |      57 |    0 | 1994-11-29
Hayward        |      37 |      54 |      | 1994-11-29
(3 rows)

Можно вывести результат выражения, а не просто значение поля в столбце. Например, можно вывести среднюю температуру:

SELECT city, (temp_hi+temp_lo)/2 AS temp_avg, date FROM weather;

Результат:

city           | temp_avg |    date
---------------+----------+------------
San Francisco  |       48 | 1994-11-27
San Francisco  |       50 | 1994-11-29
Hayward        |       45 | 1994-11-29
(3 rows)

Для переименования заголовков столбцов можно использовать не обязательное ключевое слово AS, как в примере выше.

Множество выбираемых строк можно ограничить с помощью ключевого слова WHERE. За предложением WHERE следует логическое выражение (условия поиска), и возвращаются только те строки, для которых логическое выражение имеет значение true. В предложении WHERE можно использовать обычные логические операторы (AND, OR и NOT).

Например, следующий запрос вернет температуру города Сан-Франциско в дождливые дни:

SELECT * FROM weather
    WHERE city = 'San Francisco' AND prcp > 0.0;

Результат:

city           | temp_lo | temp_hi | prcp |    date
---------------+---------+---------+------+------------
San Francisco  |      46 |      50 | 0.25 | 1994-11-27
(1 row)

С помощью :

SELECT * FROM weather
    ORDER BY city;

Результат:

city           | temp_lo | temp_hi | prcp |    date
---------------+---------+---------+------+------------
Hayward        |      37 |      54 |      | 1994-11-29
San Francisco  |      43 |      57 |    0 | 1994-11-29
San Francisco  |      46 |      50 | 0.25 | 1994-11-27

В общем случае выводимые в результирующей таблице строки не упорядочены, однако их можно отсортировать используя предложение ORDER BY (не обязательный):

SELECT * FROM weather
    ORDER BY city, temp_lo;

Получить уникальные строки из результирующего набора можно с помощью предложения DISTINCT:

SELECT DISTINCT city
    FROM weather;

Результат:

city
---------------
Hayward
San Francisco
(2 rows)

Порядок вывода строк результирующей таблицы может отличаться. Вы можете задать правильную сортировку, используя DISTINCT и ORDER BY вместе²:

SELECT DISTINCT city
    FROM weather
    ORDER BY city;

¹

Хотя SELECT * полезен общих запросов, в рабочем коде это плохая практика, поскольку добавление столбца в таблицу приведет к изменению результатов.

²

В некоторых базах данных, предложение DISTINCT автоматически упорядочивает строки, поэтому ORDER BY не требуется. Но это не требуется стандартом SQL, и QHB не гарантирует, что DISTINCT отсортирует выводимые строки.

Соединения таблиц

До сих пор мы строили запросы только к одной таблице. Но запросы могут обращаться сразу к нескольким таблицам или к одной и той же таблице так, что одновременно будут обрабатываться разные наборы её строк. Запрос, который обращается к нескольким строкам одной или разных таблиц одновременно, называется соединением (JOIN).

Например, вы хотите увидеть параметры погоды вместе с местонахождением соответствующего города. Для этого нужно сравнить столбец city каждой строки таблицы weather со столбцом name всех строк таблицы cities и выбрать строки, в которых эти значения совпадают.

SELECT *
    FROM weather, cities
    WHERE city = name;

Результат:

city          | temp_lo | temp_hi | prcp |    date    |     name      | location
--------------+---------+---------+------+------------+---------------+-----------
San Francisco |      46 |      50 | 0.25 | 1994-11-27 | San Francisco | (-194,53)
San Francisco |      43 |      57 |    0 | 1994-11-29 | San Francisco | (-194,53)
(2 rows)

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

Город Хейвард (Hayward) отсутствует в результатах. Это связано с тем, что в таблице cities для Хейвард нет соответствующей записи, поэтому соединение игнорирует эту строку в таблице weather. Такое соединение называется внутренним. Далее мы увидим, как это можно исправить.

Присутствуют 2 столбца с названием города. Это правильно, т.к. столбцы из таблиц weather и cities объединяются. На практике такое поведение нежелательно, поэтому лучше перечислять результирующие столбцы явно, а не с помощью *:

SELECT city, temp_lo, temp_hi, prcp, date, location
    FROM weather, cities
    WHERE city = name;

Результат:

city          | temp_lo | temp_hi | prcp | date       | location
--------------+---------+---------+------+------------+-----------
San Francisco | 46      | 50      | 0.25 | 1994-11-27 |  (-194,53)
San Francisco | 43      | 57      | 0    | 1994-11-29 |  (-194,53)

(2 rows)

Т.к. у всех столбцов имена различаются, парсер автоматически обнаружил, к какой таблице они принадлежат. Если в двух таблицах имена столбцов повторяются, то необходимо явно указать какой столбец имеется в виду, например:

SELECT weather.city, weather.temp_lo, weather.temp_hi,
       weather.prcp, weather.date, cities.location
    FROM weather, cities
    WHERE cities.name = weather.city;

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

Предыдущий запрос также может быть записан в следующем виде:

SELECT *
    FROM weather INNER JOIN cities ON (weather.city = cities.name);

Теперь мы выясним, как получить в результирующем наборе город Хейвард. Мы хотим, чтобы запрос для каждой строки таблицы weather находил соответствующие строки cities. Если подходящей строки не найдено, мы хотим, чтобы вместо столбцов таблицы cities были выведены некоторые «пустые значения». Этот вид запроса называется внешним соединением.

Запрос выглядит так:

SELECT *
    FROM weather LEFT OUTER JOIN cities ON (weather.city = cities.name);

     city      | temp_lo | temp_hi | prcp |    date    |     name      | location
---------------+---------+---------+------+------------+---------------+-----------
 Hayward       |      37 |      54 |      | 1994-11-29 |               |
 San Francisco |      46 |      50 | 0.25 | 1994-11-27 | San Francisco | (-194,53)
 San Francisco |      43 |      57 |    0 | 1994-11-29 | San Francisco | (-194,53)
(3 rows)

Этот запрос называется левым внешним соединением, поскольку в результирующий набор попадут все строки таблицы, указанной слева от оператора JOIN, и только те строки таблицы указанной справа, которые соответствуют условию указанному в предложении ON. Для записей левой таблицы, которые не соответствуют условию, значение столбца из правой таблицы будет пустыми (NULL).

Также можно соединить таблицу саму с собой. Это называется самосоединением. К примеру, если мы хотим найти все записи о погоде, которые находятся в диапазоне температур других записей о погоде. Поэтому нам нужно сравнить temp_lo temp_hi каждой строки weather с temp_lo и temp_hi всех других строк weather.

Мы можем сделать это с помощью следующего запроса:

SELECT W1.city, W1.temp_lo AS low, W1.temp_hi AS high,
    W2.city, W2.temp_lo AS low, W2.temp_hi AS high
    FROM weather W1, weather W2
    WHERE W1.temp_lo < W2.temp_lo
    AND W1.temp_hi > W2.temp_hi;

     city      | low | high |     city      | low | high
---------------+-----+------+---------------+-----+------
 San Francisco |  43 |   57 | San Francisco |  46 |   50
 Hayward       |  37 |   54 | San Francisco |  46 |   50
(2 rows)

Здесь мы переименовали таблицу погоды на W1 и W2 чтобы можно было различить левую и правую стороны соединения. Подобные псевдонимы можно использовать в любых запросах, чтобы сохранить некоторую типизацию, например:

SELECT *
    FROM weather w, cities c
    WHERE w.city = c.name;

Этот стиль сокращения встречается довольно часто.

Агрегатные функции

Как и большинство других реляционных баз данных, QHB поддерживает агрегатные функции. Агрегатная функция вычисляет единственное результирующее значение из множества входных строк. Например, есть агрегаты для вычисления количества (COUNT), суммы (SUM), среднего (AVG), максимального (MAX) и минимального (MIN) набора строк.

Например, мы можем найти максимум температуры из всех низких температур (столбец temp_lo):

SELECT max(temp_lo) FROM weather;

max
-----
 46
(1 row)

Если мы хотим узнать, в каком городе (или городах) было это значение температуры, мы можем попробовать ввести следующий код:

SELECT city FROM weather WHERE temp_lo = max(temp_lo);   --  WRONG

Этот запрос не будет работать, так как функция MAX не может использоваться в WHERE. (Это ограничение объясняется тем, что WHERE определяет, какие строки будут рассчитаны с помощью агрегатной функции, так что оно должно вычисляться до агрегатных функций). Однако, можно переписать запрос с помощью подзапроса:

SELECT city FROM weather
    WHERE temp_lo = (SELECT max(temp_lo) FROM weather);

city
---------------
San Francisco
(1 row)

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

Агрегатные функции также очень полезны в сочетании с предложением GROUP BY. Например, мы можем получить максимум низкой температуры, для каждого города:

SELECT city, max(temp_lo)
    FROM weather
    GROUP BY city;

city          | max
--------------+-----
Hayward       |  37
San Francisco |  46
(2 rows)

В итоге мы получаем одну результирующую строку для каждого города. Каждый агрегированный результат вычисляется по строкам таблицы, соответствующим этому городу. Теперь можно отфильтровать эти строки, используя HAVING:

SELECT city, max(temp_lo)
    FROM weather
    GROUP BY city
    HAVING max(temp_lo) < 40;

city   | max
---------+-----
Hayward |  37
(1 row)

получив те же результаты только для тех городов, у которых значения temp_lo ниже 40.

Наконец, если нам нужны только города, названия которых начинаются с «S», можно написать:

SELECT city, max(temp_lo)
    FROM weather
    WHERE city LIKE 'S%'            (1)
    GROUP BY city
    HAVING max(temp_lo) < 40;

city  | max
------+-----
(0 rows)

(1): Синтаксис оператора LIKE (выполняющий сравнение по шаблону) рассматривается в разделе Функции и операторы.

Важно понимать взаимодействие между агрегатными функциями и предложениями WHERE и HAVING. Принципиальное различие между WHERE и HAVING заключается в том, что WHERE сначала выбирает строки, а потом их группирует и вычисляет агрегатные функции (таким образом, оно фильтрует строки для вычисления агрегатов), тогда как HAVING выбирает группы строк, после вычисления группировки и вычисления агрегатных функций. Поэтому, WHERE не должно содержать агрегатных функций - нет смысла использовать агрегатную функцию, для определения входных строки для агрегатов. Предложение же HAVING всегда содержит агрегатные функции. (Строго говоря, вы можете написать HAVING, в котором нет агрегатных функций, но вряд ли это будет полезно. То же самое условие скорее всего будет работать более эффективно на стадии WHERE).

В предыдущем примере мы применили фильтр по имени города в WHERE, так как это не агрегатная функция. Это правильней, чем добавлять условие к HAVING, т.к. в этом случае не требуется выполнять группировки и вычислять агрегаты для строк, не удовлетворяющих условию WHERE.

Изменение строк

Вы можете модифицировать строки таблицы с помощью команды UPDATE. Предположим, вы обнаружили, что показания температуры завышены на 2 градуса после 28 ноября. Вы можете исправить данные следующим образом:

UPDATE weather
    SET temp_hi = temp_hi - 2,  temp_lo = temp_lo - 2
    WHERE date > '1994-11-28';

Теперь запрос вернёт обновленные данные:

SELECT * FROM weather;

     city      | temp_lo | temp_hi | prcp |    date
---------------+---------+---------+------+------------
 San Francisco |      46 |      50 | 0.25 | 1994-11-27
 San Francisco |      41 |      55 |    0 | 1994-11-29
 Hayward       |      35 |      52 |      | 1994-11-29
(3 rows)

Удаление строк

Строки можно удалить из таблицы с помощью оператора DELETE. Предположим, вас больше не интересует погода в Хейварде. Вы можете удалить эти строки из таблицы:

DELETE FROM weather WHERE city = 'Hayward';

Все записи наблюдений для города Хейвард, удалены.

SELECT * FROM weather;

city          | temp_lo | temp_hi | prcp |    date
--------------+---------+---------+------+------------
San Francisco |      46 |      50 | 0.25 | 1994-11-27
San Francisco |      41 |      55 |    0 | 1994-11-29
(2 rows)

Остерегайтесь использования оператора DELETE без предложения WHERE

DELETE FROM tablename;

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

Расширенные функции

Расширенные функции. Вступление

Представления (VIEW)

Внешние ключи

Транзакции

Оконные функции

Расширенные функции. Вступление

В главе Язык SQL. Общие сведения рассмотрены основы использования SQL для хранения и доступа к данным в QHB. Теперь мы обсудим некоторые более продвинутые функции SQL, которые упрощают управление и предотвращают потерю или повреждение ваших данных. Также, мы рассмотрим некоторые расширения QHB.

В этой главе иногда приводятся ссылки на примеры, из главы Язык SQL. Общие сведения, для их изменения или улучшения, поэтому будет полезно прочитать эту главу. Некоторые примеры из этой главы также можно найти в главе Расширенный SQL. Это приложение также содержит некоторые примеры данных и таблицы, которые повторно в этой главе не описываются.

Представления (VIEW)

В разделе Соединения таблиц мы рассматривали запросы из нескольких таблиц. Предположим, что комбинированный список записей о погоде и местоположения города представляет особый интерес для вашего приложения, но вы не хотите использовать весь запрос каждый раз, когда он вам нужен. Вы можете создать представление запроса (VIEW), которое создаёт уникальное имя для запроса, и теперь вы можете обращаться к этому имени, как к обычной таблице:

CREATE VIEW myview AS
    SELECT city, temp_lo, temp_hi, prcp, date, location
        FROM weather, cities
        WHERE city = name;

SELECT * FROM myview;

Использование представлений (VIEW) является ключевым аспектом хорошего дизайна базы данных. Представления позволяют инкапсулировать детали структуры таблиц, которые могут меняться по мере развития приложения, за согласованными интерфейсами.

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

Внешние ключи

Вспомните таблицы weather и cities из главы Язык SQL. Рассмотрим следующую проблему: Вы хотите убедиться, что никто не сможет вставить в таблицу погоды (weather) строки, у которых нет соответствующей записи в таблице городов (cities). Это ограничение называется поддержанием ссылочной целостности данных. В упрощенных системах баз данных это будет реализовано (если вообще будет реализовано) путем просмотра таблицы городов (cities) перед вставкой, чтобы проверить, существует ли соответствующая запись, а затем будут вставлены или отклонены новые записи о погоде. Этот подход имеет ряд проблем и очень неудобен, поэтому QHB может сделать это за вас.

Новое объявление таблиц будет выглядеть так:

CREATE TABLE cities (
        city     varchar(80) primary key,
        location point
);

CREATE TABLE weather (
        city      varchar(80) references cities(city),
        temp_lo   int,
        temp_hi   int,
        prcp      real,
        date      date
);

Теперь попробуйте вставить неверную запись:

INSERT INTO weather VALUES ('Berkeley', 45, 53, 0.0, '1994-11-28');

ERROR:  insert or update on table "weather" violates foreign key constraint "weather_city_fkey"
DETAIL:  Key (city)=(Berkeley) is not present in table "cities".

Поведение внешних ключей может быть настроено для вашего приложения, см. дополнительную информацию в главе Определение данных. Правильное использование внешних ключей улучшит качество ваших приложений баз данных, поэтому вам настоятельно рекомендуется узнать о них.

Транзакции

Транзакции являются фундаментальной концепцией любых систем баз данных. Важным моментом транзакции является то, что она объединяет несколько последовательных операций в одну операцию «все или ничего». Промежуточные состояния между операциями не видны другим параллельным транзакциям, и если происходит какой-либо сбой, препятствующий завершению транзакции, то ни одна из операций никак не влияет на базу данных.

Например, рассмотрим банковскую базу данных, которая содержит остатки по различным счетам клиентов, а также общие остатки по депозитам для филиалов. Предположим, что мы хотим записать платеж в размере 100 долларов со счета Алисы на счет Боба. Сильно упрощая, SQL-команды для этого могут выглядеть так:

UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
UPDATE branches SET balance = balance - 100.00
    WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice');
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
UPDATE branches SET balance = balance + 100.00
    WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob');

Детали этих команд здесь не важны; важно отметить, что для выполнения этой довольно простой операции требуется несколько отдельных обновлений. Сотрудники банка хотят быть уверены, что либо все эти обновления произойдут, либо ни одно из них не произойдет. Конечно, не годится, чтобы системный сбой привел к тому, что Боб получит 100 долларов, которые не были списаны со счёта Алисы. Также Алиса не останется счастливым клиентом, если у нее со счёта будет списана сумма без зачисления Бобу. Нам нужна гарантия того, что если во время операции что-то пойдет не так, то ни одна из выполненных до сих пор операций не вступит в силу. Группировка обновлений в транзакцию дает нам эту гарантию. Транзакция называется атомарной: с точки зрения других транзакций она либо происходит полностью, либо вообще не происходит.

Мы также хотим гарантировать, что после того, как транзакция будет завершена и подтверждена системой базы данных, она действительно будет записана в долговременное хранилище и не будет потеряна, даже если вскоре после этого произойдет сбой системы. Например, если мы записываем снятие наличных Бобом, мы не хотим иметь никаких шансов на то, что дебет его счета исчезнет в результате аварии сразу после того, как он выйдет из двери банка. Транзакционная база данных гарантирует, что все обновления, сделанные транзакцией, будут зарегистрированы в постоянном хранилище (то есть, на диске) до того, как будет сообщено о завершении транзакции.

Еще одно важное свойство транзакционных баз данных тесно связано с понятием атомарных обновлений: когда одновременно выполняется несколько транзакций, каждая из них не должна видеть неполные изменения, внесенные другими. Например, если одна транзакция занята суммированием всех остатков в филиалах, не годится, чтобы она включала дебет из филиала Алисы, но не включила кредит из филиала Боба, и наоборот. Таким образом, транзакции должны выполнять «все или ничего» не только с точки зрения их постоянного воздействия на базу данных, но также с точки зрения видимости во время их исполнения. Обновления, сделанные открытой транзакцией, невидимы для других транзакций, до тех пор, пока транзакция не завершится, после чего все обновления станут видимыми одновременно.

В QHB транзакция устанавливается путем окружения команд SQL транзакции командами BEGIN и COMMIT. Таким образом, наша банковская транзакция будет выглядеть так:

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
-- etc etc
COMMIT;

Если в ходе транзакции мы решили, что не хотим фиксировать (возможно, мы только что заметили, что баланс Алисы стал отрицательным), мы можем выполнить команду ROLLBACK вместо COMMIT, и все наши обновления будут отменены.

QHB фактически обрабатывает каждый оператор SQL как выполняемый в транзакции. Если вы не выполните команду BEGIN, то каждый отдельный оператор имеет неявный BEGIN и (в случае успеха) COMMIT обернутый вокруг него. Группу операторов, окруженную BEGIN и COMMIT, иногда называют блоком транзакции.

Заметка
Некоторые клиентские библиотеки выдают команды BEGIN и COMMIT автоматически, так что вы можете получить эффект блоков транзакций без запроса. Проверьте документацию для прикладного интерфейса, который вы используете.

Можно управлять транзакциями более детально, используя точки сохранения. Точки сохранения позволяют вам выборочно отбрасывать части транзакции, в то же время фиксируя остальные. После определения точки сохранения с помощью SAVEPOINT вы можете при необходимости откатиться до точки сохранения с помощью ROLLBACK TO. Все изменения базы данных транзакции между определением точки сохранения и откатом к ней отбрасываются, но изменения, сохраненные раньше, чем точка сохранения, сохраняются.

После отката к точке сохранения она продолжает оставаться определённой, поэтому вы можете откатиться к ней несколько раз. И наоборот, если вы уверены, что вам не нужно будет снова возвращаться к определенной точке сохранения, она может быть освобождена, для того что бы освободить некоторые ресурсы системы. Имейте в виду, что освобождение или откат к точке сохранения автоматически сбросит все точки сохранения, которые были определены после нее.

Все это происходит внутри блока транзакции, поэтому ни одно из изменений не видно другим сеансам базы данных. Когда и если вы фиксируете блок транзакции, зафиксированные действия становятся видимыми как единое целое для других сеансов, тогда как отмененные действия никогда не становятся видимыми вообще.

Вспоминая банковскую базу данных, предположим, что мы списываем 100,00 долларов со счета Алисы и зачисляем на счет Боба, чтобы потом выяснить, что мы должны были зачислить счет Уолли. Мы могли бы сделать это, используя точки сохранения, например:

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
-- oops ... forget that and use Wally's account
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Wally';
COMMIT;

Этот пример, конечно, упрощен, но в блоке транзакций возможен больший контроль с помощью точек сохранения. Более того, ROLLBACK TO — это единственный способ восстановить контроль над блоком транзакции, который был переведен системой в прерванное состояние из-за ошибки, если не считать полного отката и повторного запуска.

Оконные функции

Оконная функция выполняет различные вычисления для набора строк таблицы, которые так или иначе связаны с текущей строкой. Это сопоставимо с таким расчетом, который можно выполнить с помощью агрегатной функции. Однако оконные функции не приводят к тому, что строки группируются в одну выходную строку, как это сделали бы обычные, не оконные агрегатные вызовы. Вместо этого строки сохраняют свои отдельные идентичности. За кулисами оконная функция может получить доступ не только к текущей строке результата запроса.

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

SELECT depname, empno, salary, avg(salary) OVER (PARTITION BY depname) FROM empsalary;

depname  | empno | salary |          avg
-----------+-------+--------+-----------------------
develop   |    11 |   5200 | 5020.0000000000000000
develop   |     7 |   4200 | 5020.0000000000000000
develop   |     9 |   4500 | 5020.0000000000000000
develop   |     8 |   6000 | 5020.0000000000000000
develop   |    10 |   5200 | 5020.0000000000000000
personnel |     5 |   3500 | 3700.0000000000000000
personnel |     2 |   3900 | 3700.0000000000000000
sales     |     3 |   4800 | 4866.6666666666666667
sales     |     1 |   5000 | 4866.6666666666666667
sales     |     4 |   4800 | 4866.6666666666666667
(10 rows)

Первые три выходных столбца взяты непосредственно из таблицы empsalary, и для каждой строки в таблице есть одна выходная строка. Четвертый столбец представляет среднее значение по всем строкам таблицы, которые имеют то же значение depname что и текущая строка. (На самом деле это та же функция, что и обычный агрегат avg, но предложение OVER приводит к тому, что она обрабатывается как оконная функция и вычисляет результат в рамках тех значений, что выделены для расчета в окне).

Вызов оконной функции всегда содержит предложение OVER непосредственно после имени и аргумента (или аргументов) оконной функции. Это то, что синтаксически отличает её от обычной функции или обычного агрегата. Предложение OVER точно определяет, как строки запроса разделяются для обработки оконной функцией. Предложение PARTITION BY в OVER делит строки на группы или разделы, которые имеют одинаковые выражения значений PARTITION BY. Для каждой строки оконная функция вычисляется по строкам, которые попадают в тот же раздел, что и текущая строка.

Вы также можете контролировать порядок, в котором строки обрабатываются оконными функциями, используя ORDER BY в OVER. (Окно ORDER BY даже не должно соответствовать порядку, в котором выводятся строки). Вот пример:

SELECT depname, empno, salary,
       rank() OVER (PARTITION BY depname ORDER BY salary DESC)
FROM empsalary;

depname  | empno | salary | rank
-----------+-------+--------+------
develop   |     8 |   6000 |    1
develop   |    10 |   5200 |    2
develop   |    11 |   5200 |    2
develop   |     9 |   4500 |    4
develop   |     7 |   4200 |    5
personnel |     2 |   3900 |    1
personnel |     5 |   3500 |    2
sales     |     1 |   5000 |    1
sales     |     4 |   4800 |    2
sales     |     3 |   4800 |    2
(10 rows)

Как видно из результата, функция rank формирует числовой ранг текущей строки, в рамках заданных партиций, для каждого уникального значения параметра, используя порядок сортировки, определенный в предложении ORDER BY. Для rank не требуется явный параметр, поскольку его поведение полностью определяется предложением OVER.

Строки, рассматриваемые оконной функцией, являются строками «виртуальной таблицы», созданной предложением FROM запроса, отфильтрованным по его WHERE, GROUP BY и HAVING если таковые имеются. Например, строка, удаленная из результата из-за несоответствия условию WHERE не видна ни одной оконной функции. Запрос может содержать несколько оконных функций, которые по-разному разбивают данные на части с использованием разных предложений OVER, но все они действуют на одну и ту же коллекцию строк, определенных этой виртуальной таблицей.

Мы уже видели, что ORDER BY можно опустить, если порядок строк не важен. Также возможно опустить PARTITION BY, и в этом случае будет один раздел, содержащий все строки.

Есть еще одна важная концепция, связанная с оконными функциями: для каждой строки в ее разделе есть набор строк, называемый кадром окна. Некоторые оконные функции действуют только на строки кадра окна, а не на весь раздел. По умолчанию, если задано ORDER BY, то кадр состоит из всех строк от начала раздела до текущей строки, а также любых последующих строк, которые равны текущей строке в соответствии с предложением ORDER BY. Если ORDER BY опущен, кадр по умолчанию состоит из всех строк в разделе¹. Вот пример использования sum:

SELECT salary, sum(salary) OVER () FROM empsalary;

salary |  sum
--------+-------
  5200 | 47100
  5000 | 47100
  3500 | 47100
  4800 | 47100
  3900 | 47100
  4200 | 47100
  4500 | 47100
  4800 | 47100
  6000 | 47100
  5200 | 47100
(10 rows)

Мы видим что, поскольку в предложении OVER нет ORDER BY, кадр окна совпадает с разделом, который в отсутствие PARTITION BY и представляет собой всю таблицу; другими словами, каждая сумма берется по всей таблице, и поэтому мы получаем одинаковый результат для каждой выходной строки. Но если мы добавим предложение ORDER BY, мы получим совсем другие результаты:

SELECT salary, sum(salary) OVER (ORDER BY salary) FROM empsalary;

salary |  sum
--------+-------
   3500 |  3500
   3900 |  7400
   4200 | 11600
   4500 | 16100
   4800 | 25700
   4800 | 25700
   5000 | 30700
   5200 | 41100
   5200 | 41100
   6000 | 47100
(10 rows)

Здесь сумма берется с первого (самого низкого) оклада до текущего, включая любые дубликаты текущего (обратите внимание на результаты для дублированных окладов).

Оконные функции разрешены только в списке SELECT и предложении ORDER BY запроса. Они запрещены в других местах, например в предложениях GROUP BY, HAVING и WHERE. Это потому, что они логически выполняются после обработки этих предложений. Кроме того, оконные функции выполняются после обычных агрегатных функций. Это означает, что допустимо включать вызов агрегатной функции в аргументы оконной функции, но не наоборот.

Если есть необходимость отфильтровать или сгруппировать строки после выполнения расчетов в окне, вы можете использовать дополнительный выбор. Например:

SELECT depname, empno, salary, enroll_date
FROM
  (SELECT depname, empno, salary, enroll_date,
          rank() OVER (PARTITION BY depname ORDER BY salary DESC, empno) AS pos
     FROM empsalary
  ) AS ss
WHERE pos < 3;

Приведенный выше запрос показывает только строки из внутреннего запроса, имеющие ранг менее 3.

Когда запрос включает несколько оконных функций, можно записать каждую из них с отдельным предложением OVER, но это является дублированием и приводит к ошибкам, если требуется одинаковое поведение окон для нескольких функций. Вместо этого каждый оконный блок может быть указан в предложении WINDOW, а затем использован в OVER. Например:

SELECT sum(salary) OVER w, avg(salary) OVER w
  FROM empsalary
  WINDOW w AS (PARTITION BY depname ORDER BY salary DESC);

¹

Более подробную информацию об оконных функциях можно найти в раздел Вызовы оконных функций, раздел Оконные функции, раздел Обработка оконных функций и на странице справки SELECT.

Синтаксис SQL

В этой главе описывается синтаксис SQL. Он формирует основу для понимания следующих глав, в которых подробно рассматриваются способы применения команд SQL для определения и изменения данных.

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

Лексическая структура

Предложение на SQL состоит из последовательности команд. Команда состоит из последовательности токенов, оканчивающейся точкой с запятой («;»). Конец потока ввода также завершает команду. Какие токены являются действительными, зависит от синтаксиса конкретной команды.

Токен может быть ключевым словом, идентификатором, идентификатором в кавычках, литералом (или константой) или специальным символом. Токены разделяются символами-разделителями (пробел, табуляция, новая строка), но это необязательно, если нет двусмысленности (что обычно имеет место только в том случае, если специальный символ соседствует с каким-либо другим типом токена).

Например, следующее (синтаксически) допустимый ввод SQL:

SELECT * FROM MY_TABLE;
UPDATE MY_TABLE SET A = 5;
INSERT INTO MY_TABLE VALUES (3, 'hi there');

Это последовательность из трех команд, по одной на строку (хотя это и не требуется; в одной строке может быть несколько команд, и команды могут быть с пользой разбиты на строки).

Кроме того, комментарии могут появляться во входных данных SQL. Они не являются токенами, фактически они эквивалентны пробелам.

Синтаксис SQL не очень согласован относительно того, какие токены идентифицируют команды, а какие являются операндами или параметрами. Первые несколько токенов обычно являются именем команды, поэтому в приведенном выше примере мы обычно говорим о командах «SELECT», «UPDATE» и «INSERT». Но, например, команда UPDATE всегда требует, чтобы токен SET появлялся в определенной позиции, и этот конкретный вариант INSERT также требует VALUES для завершения. Точные правила синтаксиса для каждой команды описаны в главе Команды SQL.

Идентификаторы и ключевые слова

Токены, такие как SELECT, UPDATE или VALUES в приведенном выше примере, являются примерами ключевых слов, то есть слов, которые имеют фиксированное значение в языке SQL. MY_TABLE и A являются примерами идентификаторов. Они идентифицируют имена таблиц, столбцов или других объектов базы данных в зависимости от команды, в которой они используются. Поэтому их иногда просто называют «именами». Ключевые слова и идентификаторы имеют одинаковую лексическую структуру, что означает, что невозможно знать, является ли токен идентификатором или ключевым словом, не зная языка. Полный список ключевых слов, используемых стандарте SQL и в QHB можно найти в Ключевые слова SQL

Идентификаторы SQL и ключевые слова должны начинаться с буквы (a - z, но также с букв с диакритическими знаками и нелатинскими буквами) или подчеркивания (_). Последующие символы в идентификаторе или ключевом слове могут быть буквами, подчеркиванием, цифрами (0 - 9) или знаками доллара ($). Обратите внимание, что знаки доллара не допускаются в идентификаторах в соответствии с буквой стандарта SQL, поэтому их использование может сделать приложения менее переносимыми. Стандарт SQL не будет определять ключевое слово, которое содержит цифры, начинается или заканчивается подчеркиванием, поэтому идентификаторы этой формы защищены от возможного конфликта с будущими расширениями стандарта.

Система использует не более NAMEDATALEN-1 байтов идентификатора; более длинные имена могут быть записаны в командах, но они будут усечены. По умолчанию NAMEDATALEN равен 64, поэтому максимальная длина идентификатора составляет 63 байта.

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

UPDATE MY_TABLE SET A = 5;

можно эквивалентно записать как:

uPDaTE my_TabLE SeT a = 5;

Часто используемое соглашение заключается в написании ключевых слов в верхнем регистре и имен в нижнем регистре, например:

UPDATE my_table SET a = 5;

Существует второй тип идентификатора: идентификатор с разделителями (delimited identifier) или идентификатор в кавычках (quoted identifier). Он формируется путем включения произвольной последовательности символов в двойные кавычки ("). Идентификатор с разделителями всегда является идентификатором, а не ключевым словом. Таким образом, "select" может использоваться для ссылки на столбец или таблицу с именем "select", тогда как select без кавычек был бы взят как ключевое слово и поэтому вызвал бы ошибку синтаксического анализа при использовании там, где ожидается имя таблицы или столбца. Пример может быть написан с помощью заключенных в кавычки идентификаторов как это:

UPDATE "my_table" SET "a" = 5;

Идентификаторы в кавычках могут содержать любой символ, кроме символа с нулевым кодом. (Чтобы включить двойные кавычки, напишите две двойные кавычки). Это позволяет создавать имена таблиц или столбцов, которые в противном случае были бы невозможны, например, содержащие пробелы или амперсанды. Ограничение длины все еще применяется.

Вариант заключенных в кавычки идентификаторов позволяет включать экранированные символы Юникода, идентифицируемые их кодовыми точками. Этот вариант рассмотрен в разделе Строковые константы с экранированием Unicode. Заключение в кавычки идентификатора также делает его чувствительным к регистру, тогда как имена без кавычек всегда свертываются в нижний регистр. Например, идентификаторы FOO, foo и "foo" в QHB считаются одинаковыми, но "Foo" и "FOO" отличаются друг от друга. (Свертывание имен без кавычек в нижний регистр в QHB несовместимо со стандартом SQL, который говорит, что имена без кавычек должны быть приведены в верхний регистр. Таким образом, foo должен быть эквивалентен "FOO", а не "foo" в соответствии со стандартом. Если Вы хотите писать переносимые приложения. Рекомендуется всегда указывать конкретное имя или никогда его не указывать).

Константы

В QHB есть три типа неявно типизированных констант: строки, битовые строки и числа. Константы также могут быть указаны с явными типами, которые могут обеспечить более точное представление и более эффективную обработку системой. Эти альтернативы обсуждаются в следующих подразделах.

Строковые константы

Строковая константа в SQL — это произвольная последовательность символов, ограниченная одинарными кавычками (’), например, ’This is a string’. Чтобы включить символ одинарной кавычки в строковую константу, напишите две соседние одинарные кавычки, например, ’Dianne”s horse’. Обратите внимание, что это не то же самое, что символ двойной кавычки (").

Две строковые константы, которые разделены только пробелами хотя бы с одной новой строкой, объединяются и эффективно обрабатываются так, как если бы строка была записана как одна константа. Например:

SELECT 'foo'
'bar';

эквивалентно:

SELECT ’foobar’;

но:

SELECT 'foo' 'bar';

неверный синтаксис (Это немного странное поведение определяется SQL и QHB следует стандарту).

Строковые константы с экранированием в стиле C

QHB также принимает строковые константы с экранированием, которые являются расширением стандарта SQL. Константная строка с экранированием указывается путем написания буквы E (в верхнем или нижнем регистре) непосредственно перед открывающей одинарной кавычкой, например, E’foo’. (При продолжении строковые константы с экранированием через строки пишите E только перед первой открывающей кавычкой). Внутри строки с экранированием символ обратной косой черты (\) начинает C-подобную escape- последовательность с обратной косой чертой, в которой комбинация обратной косой черты и следующего символа (или символов) представляет специальное байтовое значение, как показано в таблице:

Escape-последовательности с обратной косой чертой

Escape-последовательность с обратной косой чертой | Описание
\b | backspace
\f | form feed
\n | newline
\r | carriage return
\t | tab
\o, \oo, \ooo (o = 0 - 7) | octal byte value
\xh, \xhh (h = 0 - 9, A - F) | hexadecimal byte value
\uxxxx, \Uxxxxxxxx (x = 0 - 9, A - F) | 16 or 32-bit hexadecimal Unicode character value

Любой другой символ после обратной косой черты воспринимается буквально. Таким образом, чтобы включить символ обратной косой черты, напишите две обратные косые черты (\\). Кроме того, одиночная кавычка может быть включена в escape-строку, с помощью \’, в дополнение к обычному способу ''.

Вы несете ответственность за то, что создаваемые вами последовательности байтов, особенно при использовании восьмеричного или шестнадцатеричного экранирования, составляют допустимые символы в кодировке набора символов сервера. Если кодировкой сервера является UTF-8, вместо этого следует использовать кодировку Unicode или альтернативный синтаксис escape Unicode, описанный в разделе Строковые константы с экранированием Unicode. Альтернативой может быть ручное кодирование UTF-8 и запись байтов, что будет очень громоздким.

Экранирующий синтаксис Unicode работает полностью, только если кодировка сервера UTF8. При использовании других серверных кодировок можно указывать только кодовые точки в диапазоне ASCII (до \u007F). Как 4-значная, так и 8-значная форма могут использоваться для указания суррогатных пар UTF-16 для составления символов с кодовыми точками, большими, чем U + FFFF, хотя наличие 8-значной формы технически делает это ненужным. (Если суррогатные пары используются, когда кодировкой сервера является UTF8, они сначала объединяются в одну кодовую точку, которая затем кодируется в UTF-8).

Символ с нулевым кодом не может быть строковой константой.

Строковые константы с экранированием Unicode

QHB также поддерживает другой тип синтаксиса с экранированием для строк, который позволяет указывать произвольные символы Юникода по кодовой точке. Константа escape-строки Unicode начинается с U& (заглавная или строчная буква U, за которой следует амперсанд) непосредственно перед открывающей кавычкой, без пробелов между ними, например, U&’foo’. (Обратите внимание, что это создает неоднозначность с оператором &. Используйте пробелы вокруг оператора, чтобы избежать этой проблемы). Внутри кавычек символы Unicode могут быть указаны в экранированной форме путем написания обратной косой черты с последующим четырехзначным шестнадцатеричным номером кодовой точки или в качестве альтернативы обратный слеш, за которым следует знак плюс, за которым следует шестизначный шестнадцатеричный номер кода. Например, строка ’data’ может быть записана как

U&'d\0061t\+000061'

Следующий менее тривиальный пример пишет русское слово «слон» кириллическими буквами:

U&'\0441\043B\043E\043D'

Если вместо обратного слэша требуется другой управляющий символ, его можно указать с помощью оператора UESCAPE после строки, например:

U&'d!0061t!+000061' UESCAPE '!'

Управляющим символом может быть любой отдельный символ, кроме шестнадцатеричной цифры, знака плюс, одинарной кавычки, двойной кавычки или пробела. Обратите внимание, что escape-символ пишется в одинарных кавычках, а не в двойных.

Экранирующий синтаксис Unicode работает только в том случае, если кодировка сервера UTF8. При использовании других серверных кодировок можно указывать только кодовые точки в диапазоне ASCII (до \007F). Как 4-значная, так и 6-значная форма могут использоваться для указания суррогатных пар UTF-16 для составления символов с кодовыми точками, большими, чем U + FFFF, хотя технически наличие 6-значной формы делает это ненужным. (Если суррогатные пары используются, когда кодировкой сервера является UTF8, они сначала объединяются в одну кодовую точку, которая затем кодируется в UTF-8).

Кроме того, escape-синтаксис Unicode для строковых констант работает только при включенном параметре конфигурации standard_conforming_strings. Это связано с тем, что в противном случае этот синтаксис может запутать клиентов, которые анализируют операторы SQL, до такой степени, что это может привести к инъекциям SQL и аналогичным проблемам безопасности. Если параметр выключен, этот синтаксис будет отклонен с сообщением об ошибке.

Чтобы буквально включить escape-символ в строку, напишите его дважды.

Строковые константы с экранированием знаками доллара

Хотя стандартный синтаксис для указания строковых констант обычно удобен, бывает трудно понять, когда нужная строка содержит много одинарных кавычек или обратных косых черт, поскольку каждая из них должна быть удвоена. Чтобы разрешить более удобочитаемые запросы в таких ситуациях, QHB предоставляет другой способ записи строковых констант, называемый «экранирование знаками доллара». Строковая константа, заключенная в такие «кавычки», состоит из знака доллара ($), необязательного «тега», состоящего из нуля или более символов, еще одного знака доллара, произвольной последовательности символов, составляющих содержимое строки, знака доллара, того же тега, которым началась эта цитата, и знака доллара. Например, вот два разных способа указать строку «Dianne's horse» с использованием экранирования знаками доллара:

$$Dianne's horse$$
$SomeTag$Dianne's horse$SomeTag$

Обратите внимание, что внутри строки, заключенной в знаки доллара, можно использовать одинарные кавычки без экранирования. Действительно, ни один символ внутри строки в кавычках не экранируется: содержимое строки всегда пишется буквально. Обратные косые черты, как и знаки доллара, не являются особыми, если они не являются частью последовательности, соответствующей открывающему тегу.

Можно вкладывать строковые константы в знаках доллара, выбирая разные теги на каждом уровне вложенности. Это чаще всего используется при написании определений функций. Например:

$function$
BEGIN
    RETURN ($1 ~ $q$[\t\r\n\v\\]$q$);
END;
$function$

Здесь последовательность $q$[\t\r\n\v\\]$q$ представляет заключенную в знаки доллара текстовую строку [\t\r\n\v\\], которая будет распознаваться, когда тело функции будет выполняться QHB. Но так как последовательность не соответствует внешнему разделителю долларовых кавычек $function$, это просто еще несколько символов внутри константы, поскольку речь идет о внешней строке.

Тэг строки в кавычках, если таковой имеется, следует тем же правилам, что и идентификатор без кавычек, за исключением того, что он не может содержать знак доллара. Теги чувствительны к регистру, поэтому $tag$String content$tag$ является правильным, но $TAG$String content$tag$ - нет.

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

Экранирование знаками доллара не являются частью стандарта SQL, но зачастую это более удобный способ написания сложных строковых литералов, чем синтаксис с одинарными кавычками, соответствующий стандарту. Это особенно полезно при представлении строковых констант внутри других констант, что часто требуется в определениях процедурных функций. В синтаксисе с одинарными кавычками каждая обратная черта в приведенном выше примере должна быть записана как четыре обратных черты, которые будут уменьшены до двух обратных черт при разборе исходной строковой константы, а затем до одного, когда внутренняя строковая константа будет повторно проанализирована во время выполнения функции.

Константы битовых строк

Константы битовых строк выглядят как обычные строковые константы с буквой B (в верхнем или нижнем регистре) непосредственно перед открывающей кавычкой (без пробелов), например, B'1001'. В битовых строковых константах допускаются только символы 0 и 1.

В качестве альтернативы константы битовой строки могут быть указаны в шестнадцатеричном формате с использованием начального X (верхний или нижний регистр), например, X'1FF'. Это обозначение эквивалентно константе битовой строки с четырьмя двоичными цифрами для каждой шестнадцатеричной цифры.

Обе формы константы битовой строки могут быть продолжены между строками так же, как обычные строковые константы. «Экранирование знаками доллара» нельзя использовать в константе битовой строки.

Числовые константы

Числовые константы принимаются в следующих общих формах:

digits
digits.[digits][e[+-]digits]
[digits].digits[e[+-]digits]
digitse[+-]digits

где digits — это одна или несколько десятичных цифр (от 0 до 9). Как минимум одна цифра должна быть до или после десятичной точки, если она используется. По крайней мере одна цифра должна следовать за маркером экспоненты (e), если таковая имеется. В константе не должно быть пробелов или других символов. Обратите внимание, что любой ведущий знак плюс или минус на самом деле не считается частью константы; это оператор, применяемый к константе.

Вот несколько примеров допустимых числовых констант:

42
3.5
4.
.001
5e2
1.925e-3

Числовая константа, которая не содержит ни десятичной точки, ни экспоненты, изначально считается типом integer если ее значение соответствует типу integer (32 бита); в противном случае предполагается, что это тип bigint если его значение соответствует типу bigint (64 бита); в противном случае он считается типом numeric. Константы, которые содержат десятичные точки и/или показатели, всегда изначально считаются numeric.

Первоначально назначенный тип данных числовой константы является лишь отправной точкой для алгоритмов разрешения типов. В большинстве случаев константа будет автоматически приведена к наиболее подходящему типу в зависимости от контекста. При необходимости вы можете принудительно интерпретировать числовое значение как определенный тип данных путем его приведения. Например, вы можете заставить числовое значение обрабатываться как тип real (float4), написав:

REAL '1.23'  -- string style
1.23::REAL   -- PostgreSQL (historical) style

На самом деле это просто особые случаи общих обозначений приведения, обсуждаемых далее.

Константы других типов

Константа произвольного типа может быть введена с использованием любого из следующих обозначений:

type 'string'
'string'::type
CAST ( 'string' AS type )

Текст строковой константы передается на вход процедуры преобразования для маркировки в соответствии с указанным типом. Результатом является константа указанного типа. Явное приведение типов может быть опущено, если нет неопределенности относительно типа, которым должна быть константа (например, когда она назначается непосредственно столбцу таблицы), и в этом случае она будет автоматически приведена к нужному типу.

Строковая константа может быть записана с использованием обычной записи SQL или знаков доллара.

Также можно указать приведение типа с использованием функционально-подобного синтаксиса:

typename ( 'string' )

но не все имена типов могут быть использованы таким образом; детальную информацию см. в разделе Приведение типов.

CAST(), «::» и функционально-подобный синтаксис также можно использовать для указания преобразований типов во время выполнения произвольных выражений, как обсуждается в разделе Приведение типов. Чтобы избежать синтаксической неоднозначности, синтаксис type 'string' может использоваться только для указания типа простой литеральной константы. Другое ограничение синтаксиса type type 'string' заключается в том, что он не работает для типов массивов; используйте «::» или CAST() чтобы указать тип константы массива.

Синтаксис CAST() соответствует SQL. Синтаксис type 'string' является обобщением стандарта: SQL определяет этот синтаксис только для нескольких типов данных, но QHB допускает его для всех типов.

Операторы

Имя оператора — это последовательность символов длиной до 63 символов из следующего списка:

+ - * / < > = ~ ! @ # % ^ & | ` ?

Однако есть несколько ограничений на имена операторов:

-- и /* нигде не могут появляться в имени оператора, так как они будут приняты в качестве начала комментария.

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

~ ! @ # % ^ & | ` ?

Например, @- это разрешенное имя оператора, а *- нет. Это ограничение позволяет QHB анализировать SQL-совместимые запросы без пробелов между токенами.

При работе с нестандартными именами операторов вам обычно нужно разделять соседние операторы пробелами, чтобы избежать двусмысленности. Например, если вы определили левый унарный оператор с именем @, вы не можете написать X*@Y; вы должны написать X* @Y чтобы QHB прочитал его как два оператора, а не как один.

Специальные символы

Некоторые символы, которые не являются буквенно-цифровыми, имеют особое значение, отличное от оператора. Подробную информацию об использовании можно найти в том месте, где описан соответствующий элемент синтаксиса. Этот раздел существует только для того, чтобы сообщить о существовании и обобщить назначение этих символов.

Знак доллара ($), за которым следуют цифры, используется для представления позиционного параметра в теле определения функции или подготовленного оператора. В других контекстах знак доллара может быть частью идентификатора или строковой константы в кавычках.

Круглые скобки (()) имеют обычное значение для группирования выражений и обеспечения приоритета. В некоторых случаях скобки требуются как часть фиксированного синтаксиса конкретной команды SQL.

Скобки ([]) используются для выбора элементов массива. См. раздел Массивы для получения дополнительной информации о массивах.

Запятые (,) используются в некоторых синтаксических конструкциях для разделения элементов списка.

Точка с запятой (;) завершает команду SQL. Он не может появляться где-либо внутри команды, кроме как внутри строковой константы или идентификатора в кавычках.

Двоеточие (:) используется для выбора «кусочков» из массивов. (См. раздел Массивы. В некоторых диалектах SQL (таких как Embedded SQL) двоеточие используется для добавления префиксов к именам переменных.

Звездочка (*) используется в некоторых контекстах для обозначения всех полей строки таблицы или составного значения. Он также имеет особое значение при использовании в качестве аргумента агрегатной функции, а именно, что агрегат не требует какого-либо явного параметра.

Точка (.) Используется в числовых константах и для разделения имен схем, таблиц и столбцов.

Комментарии

Комментарий — это последовательность символов, начинающаяся с двойных черт и продолжающаяся до конца строки, например:

-- This is a standard SQL comment

В качестве альтернативы можно использовать комментарии блока в стиле C:

/* multiline comment
 * with nesting: /* nested block comment */
 */

где комментарий начинается с /* и распространяется на совпадение вхождения */. Эти блочные комментарии вложены, как указано в стандарте SQL, но в отличие от C, так что можно закомментировать большие блоки кода, которые могут содержать существующие блочные комментарии.

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

Приоритеты операторов

Таблица "Приоритеты операторов" показывает приоритет и ассоциативность операторов в QHB. Большинство операторов имеют одинаковый приоритет и являются левоассоциативными. Приоритет и ассоциативность операторов встроены в синтаксический анализатор.

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

SELECT 5 ! - 6;

будет проанализирован как:

SELECT 5 ! (- 6);

потому что синтаксический анализатор понятия не имеет - пока не стало слишком поздно – «!» определяется как постфиксный оператор, а не как инфиксный. Чтобы получить желаемое поведение в этом случае, вы должны написать:

SELECT (5 !) - 6;

Приоритеты операторов (от высшего к низшему)

Оператор / Элемент | Ассоциативность | Описание
. | left | разделитель имени таблицы/столбца
:: | left | определение типа в стиле QHB
[ ] | left | выбор элемента массива
+ - | right | унарный плюс, унарный минус
^ | left | экспоненцирование
* / % | left | умножение, деление, по модулю
+ - | left | сложение, вычитание
(любой другой оператор) | left | все остальные собственные и пользовательские операторы
BETWEEN IN LIKE ILIKE SIMILAR |  | ограничение диапазона, набор членов, сопоставление строк
< > = <= >= <> |  | операторы сравнения
IS ISNULL NOT NULL |  | IS TRUE, IS FALSE, IS NULL, IS DISTINCT FROM т.д.
NOT | right | логическое отрицание
AND | left | логическое соединение
OR | left | логическая дизъюнкция

Обратите внимание, что правила приоритета операторов также применяются к пользовательским операторам, имена которых совпадают с именами встроенных операторов, упомянутых выше. Например, если вы определите оператор «+» для некоторого пользовательского типа данных, он будет иметь тот же приоритет, что и встроенный оператор «+», независимо от того, что делает ваш.

Когда полное имя схемы используется в синтаксисе OPERATOR, как, например, в:

SELECT 3 OPERATOR(pg_catalog.+) 4;

конструкция OPERATOR имеет приоритет по умолчанию, показанный в таблице 9 для «любого другого оператора». Это верно независимо от того, какой конкретный оператор появляется внутри OPERATOR().

Выражения значения

Выражение значения используются в различных контекстах, таких как список целей команды SELECT, в качестве новых значений столбцов в INSERT или UPDATE или в условиях поиска в ряде команд. Результат вычисления выражения значений иногда называют скаляром, чтобы отличить его от результата табличного выражения (которое является таблицей). Поэтому выражения значений также называют скалярными выражениями (или даже просто выражениями). Синтаксис выражения позволяет вычислять значения из примитивных частей, используя арифметические, логические, множественные и другие операции.

Выражением значения может быть:

Постоянное или буквальное значение;

Ссылка на столбец;

Ссылка на позиционный параметр в теле определения функции или подготовленного оператора;

Выражение подзапроса;

Выражение выбора поля;

Вызов оператора;

Вызов функции;

Агрегатное выражение;

Вызов оконной функции;

Приведение типа;

Сортировка выражения;

Скалярный подзапрос;

Конструктор массива;

Конструктор строк.

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

В дополнение к этому списку, существует ряд конструкций, которые могут быть классифицированы как выражения, но не следуют никаким общим правилам синтаксиса. Как правило, они имеют семантику функции или оператора и объясняются в соответствующем месте в главе Функции и операторы. Примером является предложение IS NULL.

Константы уже обсуждались в разделе Константы. В следующих подразделах обсуждаются остальные варианты.

Ссылки на столбец

На столбец можно ссылаться в виде:

correlation.columnname

correlation — это имя таблицы (возможно, дополненной именем схемы) или псевдонима для таблицы, определенного с помощью предложения FROM. Имя таблицы и разделяющая точка могут быть опущены, если имя столбца уникально во всех таблицах, используемых в текущем запросе. (См. также главу Запросы).

Позиционные параметры

Ссылка на позиционный параметр используется для указания значения, которое подается извне в оператор SQL. Параметры используются в определениях функций SQL и в подготовленных запросах. Некоторые клиентские библиотеки также поддерживают указание значений данных отдельно от командной строки SQL, и в этом случае параметры используются для ссылки на внешние значения данных. Форма ссылки на параметр:

$number

Например, рассмотрим определение функции dept как:

CREATE FUNCTION dept(text) RETURNS dept
    AS $$ SELECT * FROM dept WHERE name = $1 $$
    LANGUAGE SQL;

Здесь $1 ссылка на значение первого аргумента функции при каждом её вызове.

Подзапросы

Если выражение дает значение типа массива, то конкретный элемент значения массива можно извлечь, написав

expression[subscript]

или несколько смежных элементов («срез массива») можно извлечь, написав

expression[lower_subscript:upper_subscript]

(Здесь скобки [ ] должны появляться буквально). Каждый подзапрос сам по себе является выражением, которое должно давать целочисленное значение.

В общем случае массив expression должен быть заключен в скобки, но круглые скобки могут быть опущены, когда выражение, которое должно быть подписано, является просто ссылкой на столбец или позиционным параметром. Кроме того, несколько подзапросов могут быть объединены, если исходный массив является многомерным. Например:

mytable.arraycolumn[4]
mytable.two_d_column[17][34]
$1[10:42]
(arrayfunction(a,b))[42]

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

Выбор поля

Если выражение возвращает значение составного типа (тип строки), то конкретное поле строки можно извлечь, написав

выражение.имя_поля

В общем случае выражение строки должно быть заключено в скобки, но их можно опустить, если это просто ссылка на таблицу или позиционный параметр. Например:

mytable.mycolumn
$1.somecolumn
(rowfunction(a,b)).col3

(Таким образом, полная ссылка на столбец на самом деле является просто частным случаем синтаксиса выбора поля). Важный частный случай здесь — это извлечение поля из столбца составного типа:

(compositecol).somefield
(mytable.compositecol).somefield

Здесь скобки нужны, чтобы показать, что compositecol — это имя столбца, а не таблицы, или, во втором случае, что mytable — это имя таблицы, а не схемы.

Можно запросить все поля составного значения, написав .*:

(compositecol).*

Эта запись ведет себя по-разному в зависимости от контекста; подробную информацию см. в разделе Использование составных типов в запросах.

Вызов оператора

Существует три возможных синтаксиса для вызова оператора:

expression operator expression (binary infix operator)
operator expression (unary prefix operator)
expression operator (unary postfix operator)

где маркер operator следует синтаксическим правилам раздел Операторы, или является одним из ключевых слов AND, OR и NOT, или является квалифицированным именем оператора в форме:

OPERATOR(schema.operatorname)

Какие конкретные операторы существуют и являются ли они унарными или двоичными, зависит от того, какие операторы были определены системой или пользователем. В главе Функции и операторы описываются встроенные операторы.

Вызов функции

Синтаксис для вызова функции — это имя функции (возможно, дополненное именем схемы), за которым следует список аргументов, заключенный в скобки:

function_name ([expression [, expression ... ]] )

Например, вычисление квадратного корня из 2:

sqrt(2)

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

При отправке запросов в базу данных, когда некоторые пользователи не доверяют другим пользователям, соблюдайте меры безопасности при написании вызовов таких функций.

К аргументам необязательно могут быть прикреплены имена. Детальную информацию см. в разделе Вызов функции.

Функция, которая принимает один аргумент составного типа, может вызываться с использованием синтаксиса выбора поля, и наоборот, выбор поля может быть написан в функциональном стиле. То есть обозначения col(table) и table.col являются взаимозаменяемыми. Такое поведение не является стандартом SQL, но предоставляется в QHB, поскольку позволяет использовать функции для эмуляции «вычисляемых полей».

Агрегатные выражения

Агрегатное выражение представляет собой применение агрегатной функции для строк, выбранных запросом. Агрегатная функция преобразует несколько входных значений к одному результирующему, например, сумме или среднему значению исходных данных. Синтаксис статистического выражения является одним из следующих:

aggregate_name (expression [, ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ]

aggregate_name (ALL expression [, ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ]

aggregate_name (DISTINCT expression [, ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ]

aggregate_name ( * ) [ FILTER ( WHERE filter_clause ) ]

aggregate_name ( [ expression [, ... ] ] ) WITHIN GROUP ( order_by_clause ) [ FILTER ( WHERE filter_clause ) ]

где aggregate_name — это ранее определенный агрегат (возможно, с указанием имени схемы), а expression — это любое выражение значения, которое само не содержит выражения агрегации или вызова оконной функции. Необязательные order_by_clause и filter_clause описаны ниже.

Первая форма агрегатного выражения вызывает агрегат один раз для каждой входной строки. Вторая форма такая же, как и первая, поскольку по умолчанию используется ALL. Третья форма вызывает агрегат один раз для каждого отдельного выражения значения (или отдельного набора значений для нескольких выражений), найденных во входных строках. Четвертая форма вызывает агрегат один раз для каждой входной строки; поскольку конкретное входное значение не указано, оно обычно используется только для агрегатной функции count(*). Последняя форма используется с агрегатными функциями с упорядоченным набором, которые описаны ниже.

Большинство агрегатных функций игнорируют нулевые входные данные, поэтому строки, в которых одно или несколько выражений дают нулевое значение, отбрасываются. Это можно считать верным для всех встроенных агрегатов, если не указано иное.

Например, count(*) дает общее количество входных строк; count(f1) возвращает количество входных строк, в которых f1 не равно нулю, поскольку count игнорирует нули; и count(distinct f1) дает количество различных ненулевых значений f1.

Обычно входные строки подаются в агрегатную функцию в неопределенном порядке. Во многих случаях это не имеет значения; например, min выдает один и тот же результат независимо от того, в каком порядке он принимает входные данные. Однако некоторые агрегатные функции (например, array_agg и string_agg) выдают результаты, которые зависят от упорядочения входных строк. При использовании такого агрегата необязательный order_by_clause может использоваться для указания желаемого порядка. У order_by_clause тот же синтаксис, что и для предложения ORDER BY уровня запроса, как описано в разделе Сортировка строк (ORDER BY), за исключением того, что его выражения всегда являются просто выражениями и не могут быть именами или числами выходных столбцов. Например:

SELECT array_agg(a ORDER BY b DESC) FROM table;

При работе с агрегатными функциями с несколькими аргументами обратите внимание, что предложение ORDER BY идет после всех аргументов агрегации. Например, напишите это:

SELECT string_agg(a, ',' ORDER BY a) FROM table;

но не это:

SELECT string_agg(a ORDER BY a, ',') FROM table; -- неверно

Последний синтаксически допустим, но он представляет собой вызов однопараметрической агрегатной функции с двумя ключами ORDER BY (второй является довольно бесполезным, поскольку он является константой).

Если DISTINCT указан в дополнение к order_by_clause, то все выражения ORDER BY должны соответствовать обычным аргументам агрегата; то есть вы не можете сортировать выражения, которые не включены в список DISTINCT.

Помещение ORDER BY в обычный список аргументов агрегата, как описано выше, используется при упорядочении входных строк для универсальных и статистических агрегатов, для которых упорядочение необязательно. Существует подкласс агрегатных функций, называемых агрегатами упорядоченного набора, для которых требуется order_by_clause, обычно потому, что вычисление агрегата имеет смысл только с точки зрения конкретного порядка его входных строк. Типичными примерами агрегатов с упорядоченным набором являются вычисления ранга и процентиля. Для агрегата упорядоченного набора order_by_clause записывается внутри WITHIN GROUP (...), как показано в финальной альтернативе синтаксиса выше. Выражения в order_by_clause вычисляются один раз для каждой входной строки, как обычные агрегатные аргументы, сортируются в соответствии с требованиями order_by_clause и передаются в агрегатную функцию в качестве входных аргументов. (Это непохоже на случай non-WITHIN GROUP order_by_clause, который не рассматривается как аргумент(ы) для агрегатной функции). Выражения аргументов, предшествующие WITHIN GROUP, если таковые имеются, называются прямыми аргументами, чтобы отличать их от агрегатных аргументов, перечисленных в order_by_clause. В отличие от обычных агрегатных аргументов, прямые аргументы вычисляются только один раз за вызов агрегата, а не один раз на каждую входную строку. Это означает, что они могут содержать переменные, только если эти переменные сгруппированы по GROUP BY; это ограничение такое же, как если бы прямые аргументы вообще не были внутри агрегатного выражения. Прямые аргументы обычно используются для таких вещей, как процентильные дроби, которые имеют смысл только как одно значение для расчета агрегации. Список прямых аргументов может быть пустым; в этом случае просто напишите () не (*). (QHB фактически принимает любое написание, но только первый способ соответствует стандарту SQL).

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

SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY income) FROM households;

 percentile_cont
-----------------
           50489

которая получает 50-й процентиль, или медиану, значения столбца income из таблицы households. Здесь 0.5 - прямой аргумент; не имеет смысла, чтобы процентная доля была значением, варьирующимся по строкам.

Если указан FILTER, то в агрегатную функцию передаются только входные строки, для которых filter_clause оценивается как «истина»; другие строки отбрасываются. Например:

SELECT
    count(*) AS unfiltered,
    count(*) FILTER (WHERE i < 5) AS filtered
FROM generate_series(1,10) AS s(i);

 unfiltered | filtered
------------+----------
         10 |        4
(1 row)

Предопределенные агрегатные функции описаны в разделе Агрегатные функции. Другие агрегатные функции могут быть добавлены пользователем.

Агрегатное выражение может появляться только в списке результатов или в предложении HAVING команды SELECT. Это запрещено в других разделах, таких как WHERE, поскольку эти разделы логически оцениваются до формирования результатов агрегатов.

Когда агрегатное выражение появляется в подзапросе (см.раздел Скалярные подзапросы и раздел Выражения подзапроса, агрегат обычно вычисляется по строкам подзапроса. Но возникает исключение, если аргументы агрегата (и filter_clause при наличии) содержат только переменные внешнего уровня: агрегат в этом случае принадлежит такому ближайшему внешнему уровню и вычисляется поверх строк этого запроса. Агрегатное выражение в целом является тогда внешней ссылкой для подзапроса, в котором оно появляется, и действует как константа поверх любого вычисления этого подзапроса. Ограничение относительно отображения только в списке результатов или предложении HAVING применяется к уровню запроса, к которому принадлежит агрегат.

Вызовы оконных функций

Вызов оконной функции представляет собой применение агрегатоподобной функции над некоторой частью строк, выбранных запросом. В отличие от агрегатных вызовов, это не связано с группировкой выбранных строк в одну выходную строку - каждая строка остается отдельной в выходных данных запроса. Однако оконная функция имеет доступ ко всем строкам, которые будут частью группы текущей строки в соответствии со спецификацией группировки (список PARTITION BY) вызова оконной функции. Синтаксис вызова оконной функции один из следующих:

function_name ([expression [, expression ... ]]) [ FILTER ( WHERE filter_clause ) ] OVER window_name
function_name ([expression [, expression ... ]]) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition )
function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER window_name
function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition )

где window_definition имеет синтаксис:

[ existing_window_name ]
[ PARTITION BY expression [, ...] ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ frame_clause ]

Необязательный frame_clause может быть одним из:

{ RANGE | ROWS | GROUPS } frame_start [ frame_exclusion ]
{ RANGE | ROWS | GROUPS } BETWEEN frame_start AND frame_end [ frame_exclusion ]

где frame_start и frame_end могут быть одним из:

UNBOUNDED PRECEDING
offset PRECEDING
CURRENT ROW
offset FOLLOWING
UNBOUNDED FOLLOWING

и frame_exclusion может быть одним из:

EXCLUDE CURRENT ROW
EXCLUDE GROUP
EXCLUDE TIES
EXCLUDE NO OTHERS

Здесь expression представляет любое выражение значения, которое само не содержит вызовов оконных функций.

window_name является ссылкой на именованную спецификацию окна, определенную в предложении WINDOW запроса. В качестве альтернативы полное определение window_definition может быть указано в скобках, используя тот же синтаксис, что и для определения именованного окна в предложении WINDOW; см. страницу выбора SELECT для деталей. Стоит отметить, что OVER wname не совсем эквивалентно OVER (wname ...); последнее подразумевает копирование и изменение определения окна и будет отклонено, если спецификация окна, на которое ссылается ссылка, включает предложение frame.

Предложение PARTITION BY группирует строки запроса в разделы, которые обрабатываются отдельно оконной функцией. PARTITION BY работает аналогично предложению GROUP BY уровня запроса, за исключением того, что его выражения всегда являются просто выражениями и не могут быть именами или числами выходных столбцов. Без PARTITION BY все строки, созданные запросом, обрабатываются как один раздел. Предложение ORDER BY определяет порядок, в котором строки раздела обрабатываются оконной функцией. Он работает аналогично предложению ORDER BY уровня запроса, но также не может использовать имена или номера выходных столбцов. Без ORDER BY строки обрабатываются в неопределенном порядке.

frame_clause определяет набор строк, составляющих кадр окна, который является подмножеством текущего раздела, для тех оконных функций, которые действуют на кадр, а не на весь раздел. Набор строк в кадре может варьироваться в зависимости от того, какая строка является текущей. Кадр может быть указан в режиме RANGE, ROWS или GROUPS; в каждом случае он выполняется от frame_start до frame_end. Если frame_end опущен, конец по умолчанию равен CURRENT ROW.

frame_start с UNBOUNDED PRECEDING означает, что кадр начинается с первой строки раздела, и аналогично frame_end с UNBOUNDED FOLLOWING означает, что кадр заканчивается последней строкой раздела.

В режиме RANGE или GROUPS, frame_start с CURRENT ROW означает, что кадр начинается с первой строки однорангового соединения текущей строки (строка, которую предложение ORDER BY окна сортирует как эквивалентную текущей строке), в то время как frame_end с CURRENT ROW означает, что кадр заканчивается с последней равноправной строкой для текущей строки. В режиме ROWS с CURRENT ROW просто означает текущую строку.

В опциях offset PRECEDING и offset FOLLOWING offset должно быть выражением, не содержащим никаких переменных, агрегатных функций или оконных функций. Значение offset зависит от режима кадра:

В режиме ROWS offset должно давать ненулевое, неотрицательное целое число, а параметр означает, что кадр начинается или заканчивается указанным числом строк до или после текущей строки.

В режиме GROUPS offset снова должно давать ненулевое, неотрицательное целое число, а параметр означает, что кадр начинается или заканчивается за указанное число одноранговых групп до или после группы текущей строки, где одноранговые группы представляют набор строк, которые эквивалентных в порядке ORDER BY. (Для использования режима GROUPS в определении окна должно быть предложение ORDER BY).

В режиме RANGE эти параметры требуют, чтобы предложение ORDER BY указывало ровно один столбец. offset определяет максимальную разницу между значением этого столбца в текущей строке и его значением в предыдущих или последующих строках кадра. Тип данных выражения offset варьируется в зависимости от типа данных столбца упорядочения. Для числовых упорядочивающих столбцов обычно это тот же тип, что и упорядочивающий столбец, но для столбцов с упорядочиванием даты и времени это интервал. Например, если столбец заказа имеет тип date или timestamp, можно написать RANGE BETWEEN '1 day' PRECEDING AND '10 days' FOLLOWING. offset по-прежнему должно быть ненулевым и неотрицательным, хотя значение «неотрицательный» зависит от его типа данных.

В любом случае расстояние до конца кадра ограничено расстоянием до конца раздела, поэтому для строк рядом с концом раздела кадр может содержать меньше строк, чем в других местах.

Обратите внимание, что в режимах ROWS и GROUPS, 0 PRECEDING и 0 FOLLOWING эквивалентны CURRENT ROW. Это обычно выполняется и в режиме RANGE для соответствующего значения специфического типа данных “zero”.

Опция frame_exclusion позволяет исключить строки вокруг текущей строки из кадра, даже если они будут включены в соответствии с параметрами начала и конца кадра. EXCLUDE CURRENT ROW исключает текущую строку из кадра. EXCLUDE GROUP исключает текущую строку и ее упорядоченные одноранговые элементы из кадра. EXCLUDE TIES исключает любые одноранговые элементы текущей строки из фрейма, но не саму текущую строку. EXCLUDE NO OTHERS просто явно указывает поведение по умолчанию - не исключать текущую строку или ее одноранговые узлы.

Опция кадрирования по умолчанию - RANGE UNBOUNDED PRECEDING, которая совпадает с RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. С ORDER BY это устанавливает кадр, для всех строк от начала раздела до последнего однорангового узла текущей строки с учетом ORDER BY. Без ORDER BY это означает, что все строки раздела включены в кадр окна, так как все строки становятся равноправными в текущей строке.

Ограничения состоят в том, что frame_start не может быть UNBOUNDED FOLLOWING, frame_end не может быть UNBOUNDED PRECEDING, а выбор frame_end не может появляться раньше в приведенном выше списке frame_start и frame_end, чем выбор frame_start - например, RANGE BETWEEN CURRENT ROW AND offset PRECEDING не допускается. Но, например, ROWS BETWEEN 7 PRECEDING AND 8 PRECEDING разрешены, даже если они никогда не будут выбирать какие-либо строки.

Если указан параметр FILTER, то только входные строки, для которых filter_clause оценивается как «истина», передаются оконной функции; другие строки отбрасываются. Только оконные функции, которые являются агрегатами, принимают предложение FILTER.

Встроенные оконные функции описаны в таблице Оконные функции. Другие оконные функции могут быть добавлены пользователем. Кроме того, любой встроенный или определяемый пользователем универсальный или статистический агрегат может использоваться в качестве оконной функции. (Агрегаты упорядоченного набора и гипотетического набора в настоящее время не могут использоваться в качестве оконных функций).

Синтаксисы, использующие *, используются для вызова агрегатных функций без параметров в качестве оконных функций, например, count(*) OVER (PARTITION BY x ORDER BY y). Звездочка (*) обычно не используется для оконных функций. Специфичные оконные функции не позволяют использовать DISTINCT или ORDER BY в списке аргументов функции.

Вызовы оконных функций разрешены только в списке SELECT и предложении ORDER BY запроса.

Более подробную информацию о оконных функциях можно найти в разделе Руководство по оконным функциям, разделе Оконные функции и разделе Обработка оконных функций.

Приведение типов

Приведение типов определяет преобразование из одного типа данных в другой. QHB принимает два эквивалентных синтаксиса для приведения типов:

CAST ( expression AS type )
expression::type

Синтаксис CAST соответствует SQL; синтаксис с «::» является историческим наследием QHB.

Когда приведение применяется к выражению значения известного типа, оно представляет преобразование типа во время выполнения. Приведение будет успешным, только если определена подходящая операция преобразования типа. Обратите внимание, что это немного отличается от использования приведений с константами, как показано в разделе Константы других типов. Приведение, примененное к произвольному строковому литералу, представляет собой начальное присвоение типа постоянному значению литерала, и поэтому оно выполнится успешно для любого типа (если содержимое строкового литерала является приемлемым входным синтаксисом для используемого типа данных).

Явное приведение типов обычно может быть опущено, если нет двусмысленности относительно типа, который должно генерировать выражение значения (например, когда оно назначено столбцу таблицы); система автоматически применяет приведение типа в таких случаях. Однако автоматическое приведение выполняется только для приведений, помеченных как «OK to apply implicitly» в системных каталогах. Другие приведения должны вызываться с явным синтаксисом приведения. Это ограничение предназначено для того, чтобы предотвратить неожиданные преобразования, применяемые автоматически.

Также возможно указать приведение типа с использованием функционально-подобного синтаксиса:

typename (expression)

Однако это работает только для типов, имена которых также допустимы в качестве имен функций. Например, double precision не может быть использована таким образом, но эквивалентный float8 может. Кроме того, имена interval, time и timestamp могут использоваться таким образом, только если они заключены в двойные кавычки, из-за синтаксических конфликтов. Следовательно, использование функционально-подобного синтаксиса приведений приводит к несоответствиям, и его, вероятно, следует избегать.

Функционально-подобный синтаксис на самом деле является просто вызовом функции. Когда один из двух стандартных синтаксисов преобразования используется для выполнения преобразования во время выполнения, он будет внутренне вызывать зарегистрированную функцию для выполнения преобразования. По соглашению, эти функции преобразования имеют то же имя, что и их тип вывода, и, таким образом, «функционально-подобный синтаксис» является не чем иным, как прямым вызовом базовой функции преобразования. Очевидно, что это не то, на что портативное приложение должно положиться. Для получения дополнительной информации см. CREATE CAST.

Сортировка выражений

Предложение COLLATE переопределяет параметры сортировки выражения. Он добавляется к выражению, к которому он относится:

expr COLLATE collation

где collation — это идентификатор, который может быть определен схемой. Предложение COLLATE связывает крепче, чем операторы; при необходимости могут быть использованы круглые скобки.

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

Два типовых варианта использования предложения COLLATE переопределяют порядок сортировки в предложении ORDER BY, например:

SELECT a, b, c FROM tbl WHERE ... ORDER BY a COLLATE "C";

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

SELECT * FROM tbl WHERE a > 'foo' COLLATE "C";

Обратите внимание, что в последнем случае предложение COLLATE присоединяется к входному аргументу оператора, на который необходимо повлиять. Неважно, к какому аргументу оператора или функции, вызывающей предложение COLLATE, присоединено, потому что сортировка, применяемая оператором или функцией, получается с учетом всех аргументов, и явное предложение COLLATE будет переопределять сортировку всех других аргументы. (Однако присоединение несоответствующих предложений COLLATE к более чем одному аргументу является ошибкой. Более подробную информацию см. в разделе Поддержка правил сортировки. Таким образом, это дает тот же результат, что и в предыдущем примере:

SELECT * FROM tbl WHERE a COLLATE "C" > 'foo';

Но этот вариант будет ошибкой:

SELECT * FROM tbl WHERE (a > 'foo') COLLATE "C";

потому что он пытается применить параметры сортировки к результату оператора «>», который является логическим типом данных без сортировки.

Скалярные подзапросы

Скалярный подзапрос — это обычный запрос SELECT в скобках, который возвращает ровно одну строку с одним столбцом. (См. главу Запросы для получения информации о написании запросов). Запрос SELECT выполняется, и единственное возвращаемое значение используется в контексте его вызова. Ошибочно использовать запрос, который возвращает более одной строки или более одного столбца в качестве скалярного подзапроса. (Но, если во время конкретного выполнения подзапрос не возвращает строк, ошибки нет; скалярный результат принимается равным нулю). Подзапрос может ссылаться на переменные из окружающего запроса, которые будут действовать как константы во время любого разового вызова подзапроса.

Например, следующий запрос находит наибольшее население города в каждом штате:

SELECT name, (SELECT max(pop) FROM cities WHERE cities.state = states.name)
    FROM states;

Конструкторы массивов

Конструктор массива — это выражение, которое создает значение массива, используя значения для его элементов-членов. Простой конструктор массива состоит из ключевого слова «ARRAY», левой квадратной скобки [, списка выражений (разделенных запятыми) для значений элементов массива и, наконец, правой квадратной скобки ]. Например:

SELECT ARRAY[1,2,3+4];
  array
---------
 {1,2,7}
(1 row)

По умолчанию тип элемента массива является общим типом выражений-членов, определяемых с использованием тех же правил, что и для конструкций UNION или CASE. Вы можете переопределить это, явно приведя конструктор массива к желаемому типу, например:

SELECT ARRAY[1,2,22.7]::integer[];
  array
----------
 {1,2,23}
(1 row)

Это имеет тот же эффект, что и приведение каждого выражения к типу элемента массива по отдельности. Подробнее о приведении см. раздел Приведение типов.

Значения многомерного массива можно построить с помощью вложенных конструкторов массива. Во внутренних конструкторах ключевое слово ARRAY может быть опущено. Например, следующие варианты дают один и тот же результат:

SELECT ARRAY[ARRAY[1,2], ARRAY[3,4]];
     array
---------------
 {{1,2},{3,4}}
(1 row)

SELECT ARRAY[[1,2],[3,4]];
     array
---------------
 {{1,2},{3,4}}
(1 row)

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

Элементы конструктора многомерного массива могут быть чем угодно, дающим массив правильного вида, а не только конструкцией sub-ARRAY. Например:

CREATE TABLE arr(f1 int[], f2 int[]);

INSERT INTO arr VALUES (ARRAY[[1,2],[3,4]], ARRAY[[5,6],[7,8]]);

SELECT ARRAY[f1, f2, '{{9,10},{11,12}}'::int[]] FROM arr;
                     array
------------------------------------------------
 {{{1,2},{3,4}},{{5,6},{7,8}},{{9,10},{11,12}}}
(1 row)

Вы можете создать пустой массив, но так как невозможно иметь массив без типа, вы должны явно привести пустой массив к нужному типу. Например:

SELECT ARRAY[]::integer[];
 array
-------
 {}
(1 row)

Также возможно построить массив из результатов подзапроса. В этой форме конструктор массива записывается с ключевым словом ARRAY за которым следует подзапрос (не заключенный в скобки). Например:

SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
                                 array
-----------------------------------------------------------------------
 {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413}
(1 row)

SELECT ARRAY(SELECT ARRAY[i, i*2] FROM generate_series(1,5) AS a(i));
              array
----------------------------------
 {{1,2},{2,4},{3,6},{4,8},{5,10}}
(1 row)

Подзапрос должен возвращать один столбец. Если выходной столбец подзапроса не является типом массива, результирующий одномерный массив будет иметь элемент для каждой строки в результате подзапроса, причем тип элемента соответствует типу выходного столбца подзапроса. Если выходной столбец подзапроса имеет тип массива, результатом будет массив того же типа, но с одним более высоким измерением; в этом случае все строки подзапроса должны давать массивы одинаковой размерности, иначе результат не будет прямоугольным.

Индексы значения массива, построенного с помощью ARRAY, всегда начинаются с единицы. Для получения дополнительной информации о массивах см. раздел Массивы.

Конструкторы строк

Конструктор строки — это выражение, которое создает значение строки (также называемое составным значением), используя значения для его полей-членов. Конструктор строки состоит из ключевого слова ROW, левой круглой скобки, нуля или более выражений (разделенных запятыми) для значений поля строки и, наконец, правой круглой скобки. Например:

SELECT ROW(1,2.5,'this is a test');

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

Конструктор строки может включать синтаксис rowvalue.*, который будет расширен до списка элементов значения строки, как это происходит, когда синтаксис .* используется на верхнем уровне списка SELECT (см. раздел Использование составных типов в запросах). Например, если таблица t имеет столбцы f1 и f2, следующие записи выдают идентичные результаты:

SELECT ROW(t.*, 42) FROM t;
SELECT ROW(t.f1, t.f2, 42) FROM t;

По умолчанию значение, созданное ROW-выражением, относится к анонимному типу записи. При необходимости его можно привести к названному составному типу - либо к типу строки таблицы, либо к составному типу, созданному с помощью CREATE TYPE AS. Чтобы избежать двусмысленности, может потребоваться явное приведение. Например:

CREATE TABLE mytable(f1 int, f2 float, f3 text);

CREATE FUNCTION getf1(mytable) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;

-- No cast needed since only one getf1() exists
SELECT getf1(ROW(1,2.5,'this is a test'));
 getf1
-------
     1
(1 row)

CREATE TYPE myrowtype AS (f1 int, f2 text, f3 numeric);

CREATE FUNCTION getf1(myrowtype) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;

-- Now we need a cast to indicate which function to call:
SELECT getf1(ROW(1,2.5,'this is a test'));
ERROR:  function getf1(record) is not unique

SELECT getf1(ROW(1,2.5,'this is a test')::mytable);
 getf1
-------
     1
(1 row)

SELECT getf1(CAST(ROW(11,'this is a test',2.5) AS myrowtype));
 getf1
-------
    11
(1 row)

Конструкторы строк можно использовать для построения составных значений, которые будут храниться в столбце таблицы составного типа или передаваться в функцию, которая принимает составной параметр. Кроме того, можно сравнить два значения строки или проверить строку с помощью IS NULL или IS NOT NULL, например:

SELECT ROW(1,2.5,'this is a test') = ROW(1, 3, 'not the same');

SELECT ROW(table.*) IS NULL FROM table;  -- detect all-null rows

Подробнее см. раздел Сравнение строк и массивов. Конструкторы строк также могут использоваться в связи с подзапросами, как обсуждается в разделе Выражения подзапроса.

Правила вычисления выражений

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

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

SELECT true OR somefunc();

параметр somefunc() (вероятно) не будет вызван вообще. То же самое будет, если написать:

SELECT somefunc() OR true;

Обратите внимание, что это не то же самое, что «оптимизация» логических операторов слева направо, встречающееся в некоторых языках программирования.

Как следствие, неразумно использовать функции с побочными эффектами как часть сложных выражений. Особенно опасно полагаться на побочные эффекты или порядок вычисления в предложениях WHERE и HAVING предложениях, так как эти пункты тщательно обрабатываются как часть построения плана выполнения. Булевы выражения (комбинации AND/OR/NOT) в этих предложениях могут быть реорганизованы любым способом, разрешенным законами булевой алгебры.

Когда необходимо форсировать порядок вычислений, можно использовать CASE конструкцию (см. раздел Условные выражения). Например, это ненадежный способ избежать деления на ноль в WHERE предложении:

SELECT ... WHERE x > 0 AND y/x > 1.5;

Но это безопасно:

SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;

CASE-конструкция используемая таким образом, будет препятствовать попыткам оптимизации, так что это должно быть сделано только в случае необходимости. (В этом конкретном примере было бы лучше обойти проблему, написав y > 1.5*x).

Однако CASE, это не панацея от подобных проблем. Одним из ограничений метода, показанного выше, является то, что он не препятствует раннему вычислению постоянных подвыражений. Как описано в разделе Категории изменчивости функций, функции и операторы, помеченные как IMMUTABLE могут быть вычислены при планировании запроса, а не при его выполнении. Так например:

SELECT CASE WHEN x > 0 THEN x ELSE 1/0 END FROM tab;

скорее всего, это приведет к ошибке деления на ноль из-за того, что планировщик пытается упростить постоянное подвыражение, даже если в каждой строке таблицы есть x > 0 так, что вариант с ELSE никогда не будет обработан во время выполнения.

Хотя этот конкретный пример может показаться глупым, связанные запросы, которые явно не включают константы, могут возникать в запросах, выполняемых внутри функций, поскольку значения аргументов функции и локальных переменных могут быть вставлены в запросы в качестве констант для целей планирования. Например, в функциях PL/pgSQL использование оператора IF-THEN-ELSE для защиты рискованных вычислений намного безопаснее, чем просто вложить его в выражение CASE.

Другое ограничение того же типа состоит в том, что CASE не может предотвратить вычисление содержащегося в нем агрегатного выражения, потому что агрегатные выражения вычисляются до того, как будут рассмотрены другие выражения в списке SELECT или предложении HAVING. Например, следующий запрос может вызвать ошибку деления на ноль, несмотря на кажущуюся защиту от нее:

SELECT CASE WHEN min(employees) > 0
            THEN avg(expenses / employees)
       END
    FROM departments;

Агрегаты min() и avg() вычисляются одновременно по всем входным строкам, так что, если любая строка имеет employees равное нулю, то ошибка деления на ноль будет происходить до того, как появится возможность проверить результат min(). Вместо этого используйте предложение WHERE или FILTER, чтобы предотвратить попадание проблемных входных строк в агрегатную функцию.

Вызов функции

QHB позволяет вызывать функции с именованными параметрами с использованием позиционной или именованной нотации. Именованная нотация особенно полезна для функций, которые имеют большое количество параметров, поскольку она делает ассоциации между параметрами и фактическими аргументами более явными и надежными. В позиционной нотации вызов функции записывается со значениями аргументов в том же порядке, в котором они определены в объявлении функции. В именованной нотации аргументы сопоставляются с параметрами функции по имени и могут быть записаны в любом порядке.

В любой нотации параметры, которые имеют значения по умолчанию, указанные в объявлении функции, вообще не должны записываться в вызове. Но это особенно полезно в именованной нотации, поскольку любая комбинация параметров может быть опущена; в то время как в позиционной нотации параметры могут быть пропущены только справа налево.

QHB также поддерживает смешанную нотацию, которая объединяет позиционную и именованную нотацию. В этом случае позиционные параметры записываются первыми, а именованные параметры появляются после них.

Следующие примеры иллюстрируют использование всех трех обозначений, используя следующее определение функции:

CREATE FUNCTION concat_lower_or_upper(a text, b text, uppercase boolean DEFAULT false)
RETURNS text
AS
$$
 SELECT CASE
        WHEN $3 THEN UPPER($1 || ' ' || $2)
        ELSE LOWER($1 || ' ' || $2)
        END;
$$
LANGUAGE SQL IMMUTABLE STRICT;

Функция concat_lower_or_upper имеет два обязательных параметра, a и b. Кроме того, есть один необязательный параметр uppercase который по умолчанию равен false. Входы a и b будут объединены и переведены в верхний или нижний регистр в зависимости от параметра uppercase. Остальные детали этого определения функции здесь не важны (см. главу Расширение SQL для получения дополнительной информации).

Использование позиционной нотации

Позиционная нотация — это традиционный механизм передачи аргументов функциям в QHB. Примером является:

SELECT concat_lower_or_upper('Hello', 'World', true);
 concat_lower_or_upper
-----------------------
 HELLO WORLD
(1 row)

Все аргументы указаны в порядке. Результат - верхний регистр, поскольку uppercase указан как true. Другой пример:

SELECT concat_lower_or_upper('Hello', 'World');
 concat_lower_or_upper
-----------------------
 hello world
(1 row)

Здесь параметр uppercase пропущен, поэтому он получает значение по умолчанию false, что приводит к выводу в нижнем регистре. В позиционной нотации аргументы могут быть опущены справа налево, если они имеют значения по умолчанию.

Использование именованных обозначений

В именованной нотации имя каждого аргумента указывается с помощью => чтобы отделить его от выражения аргумента. Например:

SELECT concat_lower_or_upper(a => 'Hello', b => 'World');
 concat_lower_or_upper
-----------------------
 hello world
(1 row)

Опять же, аргумент в uppercase был опущен, поэтому он неявно установлен в значение false. Одно из преимуществ использования именованных обозначений заключается в том, что аргументы могут быть указаны в любом порядке, например:

SELECT concat_lower_or_upper(a => 'Hello', b => 'World', uppercase => true);
 concat_lower_or_upper
-----------------------
 HELLO WORLD
(1 row)

SELECT concat_lower_or_upper(a => 'Hello', uppercase => true, b => 'World');
 concat_lower_or_upper
-----------------------
 HELLO WORLD
(1 row)

Более старый синтаксис, основанный на ":=", поддерживается для обратной совместимости:

SELECT concat_lower_or_upper(a := 'Hello', uppercase := true, b := 'World');
 concat_lower_or_upper
-----------------------
 HELLO WORLD
(1 row)

Использование смешанной нотации

Смешанная нотация объединяет позиционную и именованную нотацию. Однако, как уже упоминалось, именованные аргументы не могут предшествовать позиционным аргументам. Например:

SELECT concat_lower_or_upper('Hello', 'World', uppercase => true);
 concat_lower_or_upper
-----------------------
 HELLO WORLD
(1 row)

В приведенном выше запросе аргументы a и b указываются позиционно, а uppercase - именем. В этом примере это мало что добавляет, кроме документации. С более сложной функцией, имеющей многочисленные параметры со значениями по умолчанию, именованные или смешанные обозначения могут сэкономить много времени и уменьшить вероятность ошибок.

Именованные и смешанные нотации вызовов в настоящее время не могут использоваться при вызове агрегатной функции (но они работают, когда агрегатная функция используется в качестве оконной функции).

Определение данных

В этой главе рассказывается, как создать структуры в которых будут храниться данные. В реляционной базе данных необработанные данные хранятся в таблицах, поэтому большая часть этой главы посвящена объяснению того, как создаются и изменяются таблицы, и какие функции доступны для управления тем, какие данные хранятся в таблицах. Затем мы обсудим, как таблицы могут быть организованы в схемы, и как могут быть назначены права для таблиц. Наконец, мы кратко рассмотрим другие функции, которые влияют на хранение данных, такие как наследование, партиционирование (разбиение таблиц на фрагменты), представления, функции и триггеры.

Базовая информация по таблицам

Таблица в реляционной базе данных внешне очень похожа на таблицу на бумаге: она состоит из строк и столбцов. Количество и порядок столбцов фиксированы, и у каждого столбца есть имя. Количество строк является переменным - оно отражает, сколько данных хранится в данный момент. SQL не дает никаких гарантий относительно порядка строк в таблице. Когда таблица прочитана, строки появятся в неуказанном порядке, если сортировка явно не задана, см. главу Запросы. Кроме того, SQL не присваивает уникальные идентификаторы строкам, поэтому в таблице может быть несколько полностью идентичных строк. Это является следствием математической модели, которая лежит в основе реляционных СУБД, но обычно это нежелательно. Далее в этой главе мы увидим, как бороться с этой проблемой.

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

QHB включает в себя значительный набор встроенных типов данных, которые подходят для многих приложений. Пользователи также могут определять свои собственные типы данных. Большинство встроенных типов данных имеют очевидные имена и семантику, подробное объяснение см. главу Типы данных. Некоторыми из часто используемых типов данных являются integer для целых чисел, numeric для возможно дробных чисел, text для символьных строк, date для дат, time для значений timestamp дня и timestamp для значений, содержащих как дату, так и время.

Чтобы создать таблицу, используйте команду CREATE TABLE с подходящим названием. В этой команде вы указываете как минимум имя новой таблицы, имена столбцов и тип данных каждого столбца. Например:

CREATE TABLE my_first_table (
    first_column text,
    second_column integer
);

Это создает таблицу с именем my_first_table с двумя столбцами. Первый столбец называется first_column и имеет тип данных text, второй столбец имеет имя second_column и тип integer. Имена таблиц и столбцов соответствуют синтаксису идентификатора см. раздел Идентификаторы и ключевые слова. Имена типов обычно также являются идентификаторами, но есть некоторые исключения. Обратите внимание, что список столбцов разделен запятыми и заключен в скобки.

Конечно, предыдущий пример был сильно надуманным. Обычно таблицам и столбцам дают имена, которые передают, какие данные они хранят. Итак, давайте посмотрим на более реалистичный пример:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric
);

(тип numeric может хранить дробные компоненты, как это было бы типично для денежных сумм).

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

Существует ограничение на количество столбцов в таблице. В зависимости от типов столбцов оно составляет от 250 до 1600. Однако определение таблицы с таким количеством столбцов весьма необычно и часто вызывает сомнение в правильности модели данных.

Если вам больше не нужна таблица, вы можете удалить ее с помощью команды DROP TABLE. Например:

DROP TABLE my_first_table;
DROP TABLE products;

Попытка удалить несуществующую таблицу является ошибкой. Тем не менее в файлах сценариев SQL принято удалять каждую таблицу перед ее созданием, игнорируя любые сообщения об ошибках, чтобы скрипт работал независимо от того, существует таблица или нет. (При необходимости, вы можете использовать вариант DROP TABLE IF EXISTS чтобы избежать сообщений об ошибках, но это не стандартный SQL).

Если вам нужно изменить таблицу, которая уже существует, см. раздел Изменение таблиц.

Используя описанные выше команды, вы можете создавать полностью функциональные таблицы. Остальная часть этой главы посвящена командам для добавления в определение таблицы возможностей для обеспечения целостности, безопасности или удобства данных. Если вы хотите заполнить ваши таблицы данными сейчас, вы можете перейти к главе Манипулирование данными и прочитать остальную часть этой главы позже.

Значения по умолчанию

Столбцу может быть назначено значение по умолчанию. Когда создается новая строка и для некоторых столбцов не указаны значения, эти столбцы будут заполнены соответствующими значениями по умолчанию. Команда манипулирования данными может также явно запросить, чтобы для столбца было установлено значение по умолчанию, без необходимости знать, что это за значение. (Подробности о командах манипулирования данными находятся в главе Манипулирование данными.

Если никакое значение по умолчанию не объявлено явно, значением по умолчанию является null значение. Обычно это имеет смысл, поскольку null значение может рассматриваться как представляющее неизвестные данные.

В определении таблицы значения по умолчанию перечислены после типа данных столбца. Например:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric DEFAULT 9.99
);

Значением по умолчанию может быть выражение, которое будет вычисляться всякий раз, когда вставляется значение по умолчанию (а не при создании таблицы). Типичный пример - для столбца timestamp по умолчанию задано значение CURRENT_TIMESTAMP, поэтому для него устанавливается время вставки строки. Другим распространенным примером является генерация «серийного номера» для каждой строки. В QHB это обычно делается примерно так:

CREATE TABLE products (
    product_no integer DEFAULT nextval('products_product_no_seq'),
    ...
);

где функция nextval() предоставляет последовательные значения из объекта последовательности (см. раздел Функции управления последовательностями). Такое объявление достаточно распространено, поэтому для него есть специальное сокращение:

CREATE TABLE products (
    product_no SERIAL,
    ...
);

Сокращение SERIAL обсуждается далее в разделе Последовательные типы.

Сгенерированные столбцы

Сгенерированный столбец — это специальный столбец, который всегда вычисляется из других столбцов. Таким образом, для столбцов это то же самое, что представление для таблиц. Существует два вида генерируемых столбцов: хранимые и виртуальные. Сохраненный сгенерированный столбец вычисляется, когда он записывается в хранилище (вставляется или обновляется) и занимает пространство в хранилище, как если бы это был обычный столбец. Виртуальный сгенерированный столбец не занимает места в хранилище и вычисляется при чтении. Таким образом, виртуальный сгенерированный столбец похож на представление, а сохраненный сгенерированный столбец похож на материализованное представление (за исключением того, что оно всегда обновляется автоматически). В настоящее время QHB реализует только хранимые сгенерированные столбцы.

Чтобы создать сгенерированный столбец, используйте предложение GENERATED ALWAYS AS в CREATE TABLE, например:

CREATE TABLE people (
    ...,
    height_cm numeric,
    height_in numeric GENERATED ALWAYS AS (height_cm / 2.54) STORED
);

Ключевое слово STORED должно быть указано, чтобы выбрать сохраненный вид сгенерированного столбца. Смотрите CREATE TABLE для более подробной информации.

Сгенерированный столбец не может быть записан напрямую. В командах INSERT или UPDATE значение не может быть указано для сгенерированного столбца, но может быть указано ключевое слово DEFAULT.

Рассмотрим различия между столбцом с заданным по умолчанию и сгенерированным столбцом. Столбец по умолчанию вычисляется один раз, когда строка впервые вставляется, если не было предоставлено никакого другого значения - сгенерированный столбец обновляется при каждом изменении строки и не может быть переопределен. Столбец по умолчанию может не ссылаться на другие столбцы таблицы, сгенерированный столбец обычно имеет такие ссылки. В столбце по умолчанию могут использоваться нестабильные функции, например random() или функции, относящиеся к текущему времени - которые не допускаются для сгенерированных столбцов.

Несколько ограничений применяются к определению сгенерированных столбцов и таблиц, включающих сгенерированные столбцы:

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

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

Выражение используемое для генерации не может ссылаться на системный столбец, кроме tableoid.

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

Сгенерированный столбец не может быть частью ключа разбиения.

Внешние таблицы могут иметь сгенерированные столбцы. См. CREATE FOREIGN TABLE для деталей.

Дополнительные ограничения относятся к использованию сгенерированных столбцов.

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

Сгенерированные столбцы обновляются после запуска триггеров. Поэтому изменения, внесенные в базовые столбцы в триггере BEFORE будут отражены в сгенерированных столбцах. Но, наоборот, не допускается доступ к сгенерированным столбцам в триггерах BEFORE.

Ограничения

Типы данных — это способ ограничения возможных значений данных, которые могут храниться в таблице. Однако для многих приложений ограничение, которое они предоставляют, слишком грубое. Например, столбец, содержащий цену товара, вероятно, должен принимать только положительные значения. Но нет стандартного типа данных, который принимает только положительные числа. Другая проблема заключается в том, что может потребоваться ограничить данные столбца относительно других столбцов или строк. Например, в таблице, содержащей информацию о продукте, должна быть только одна строка для каждого номера продукта.

Для этого SQL позволяет определять ограничения для столбцов и таблиц. Ограничения дают вам больше контроля над данными в ваших таблицах. Если пользователь пытается сохранить в столбце данные, которые нарушают ограничение, возникает ошибка. Это выполняется, даже если значение пришло из определения значения по умолчанию.

Проверочные ограничения

Проверочное ограничение (CHECK) является наиболее общим типом ограничения. Оно позволяет указать, что значение в определенном столбце должно удовлетворять логическому выражению (истинностному значению). Например, чтобы требовать положительных цен на товары, можно использовать:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0)
);

Определение ограничения идет после типа данных, также как и определения значений по умолчанию. Значения и ограничения по умолчанию могут быть перечислены в любом порядке. Проверочное ограничение состоит из ключевого слова CHECK за которым следует выражение в скобках. Выражение проверочного ограничения должно включать столбец, для которого задается выражение, иначе ограничение не будет иметь большого смысла.

Также можно задать ограничению отдельное имя. Это уточняет сообщения об ошибках и позволяет ссылаться на ограничение, когда вам нужно его изменить. Синтаксис:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CONSTRAINT positive_price CHECK (price > 0)
);

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

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

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0),
    discounted_price numeric CHECK (discounted_price > 0),
    CHECK (price > discounted_price)
);

Первые два ограничения описаны выше. Третье использует новый синтаксис. Он не привязан к определенному столбцу, а отображается как отдельный элемент в списке столбцов, разделенных запятыми. Определения столбцов и эти определения ограничений могут быть перечислены в смешанном порядке.

Мы говорим, что первые два ограничения являются ограничениями столбца, тогда как третье ограничение является ограничением таблицы, потому что оно написано отдельно от любого определения одного столбца. Ограничения столбца также могут быть записаны как ограничения таблицы, тогда как обратное не обязательно возможно, поскольку предполагается, что ограничение столбца относится только к столбцу, к которому оно прикреплено. (QHB не применяет это правило, но вы должны следовать ему, если хотите, чтобы определения таблиц работали с другими системами баз данных). Приведенный выше пример также можно записать так:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric,
    CHECK (price > 0),
    discounted_price numeric,
    CHECK (discounted_price > 0),
    CHECK (price > discounted_price)
);

или даже:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric CHECK (price > 0),
    discounted_price numeric,
    CHECK (discounted_price > 0 AND price > discounted_price)
);

Имена могут быть назначены ограничениям таблицы так же, как ограничения столбцов:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric,
    CHECK (price > 0),
    discounted_price numeric,
    CHECK (discounted_price > 0),
    CONSTRAINT valid_discount CHECK (price > discounted_price)
);

Важно отметить, что проверочное ограничение выполняется, если проверочное выражение имеет значение true или значение NULL. Поскольку большинство выражений будут иметь нулевое значение, если какой-либо операнд будет нулевым, они не будут предотвращать нулевые значения в ограниченных столбцах. Чтобы столбец не содержал нулевых значений, можно использовать ограничение NOT NULL, описанное в следующем разделе.

Примечание!!!
QHB не поддерживает проверочные ограничения, которые ссылаются на данные таблицы, кроме проверяемой новой или обновленной строки. Хотя проверочное ограничение которое нарушает это правило, может показаться работающим в простых тестах, оно не может гарантировать, что база данных не достигнет состояния, в котором условие ограничения ложно (из-за последующих изменений другой строки (строк)). Это может привести к сбою - база данных может завершиться с ошибкой, даже если полное состояние базы данных соответствует ограничению из-за того, что строки не загружаются в порядке, который будет удовлетворять ограничению. Если возможно, используйте ограничения UNIQUE, EXCLUDE или FOREIGN KEY для выражения ограничений между строками и таблицами.
Если вам требуется одноразовая проверка по отношению к другим строкам при вставке строки, а не постоянная гарантия согласованности, для ее реализации можно использовать пользовательский триггер. (Этот подход позволяет избежать проблемы сброса/перезагрузки, поскольку qhb_dump не переустанавливает триггеры до перезагрузки данных, поэтому проверка не будет выполняться во время выполнения дампа / перезагрузки).

Примечание!!!
QHB предполагает, что условия ограничений CHECK неизменны, то есть они всегда будут давать один и тот же результат для одной и той же входной строки. Это предположение оправдывает выполнение проверки на это ограничение только при вставке или обновлении строки, а не в других случаях. (Вышеприведенное предупреждение о том, что не следует ссылаться на другие данные таблицы является частным случаем этого ограничения). Примером распространенного способа нарушить это предположение является ссылка на пользовательскую функцию в выражении CHECK, а затем изменение поведения этой функции. QHB не запрещает этого, но не заметит, есть ли в таблице строки, которые теперь нарушают ограничение CHECK. Это может вызвать сбой последующего дампа базы данных. Рекомендуемый способ обработки такого изменения состоит в том, чтобы удалить ограничение (используя ALTER TABLE), скорректировать определение функции и повторно добавить ограничение, таким образом перепроверив его для всех строк таблицы.

Ограничения not-null

Ограничение not-null (Not-Null constraints) указывает, что столбец не должен принимать null значение. Пример синтаксиса:

CREATE TABLE products (
    product_no integer NOT NULL,
    name text NOT NULL,
    price numeric
);

Ограничение not-null всегда записывается как ограничение столбца. Ограничение not-null функционально эквивалентно созданию контрольного ограничения CHECK (column_name IS NOT NULL), но в QHB создание явного ограничения not-null более эффективно. Недостатком является то, что вы не можете дать явные имена ограничениям not-null, созданным таким образом.

Столбец может иметь более одного ограничения. Просто напишите ограничения один за другим:

CREATE TABLE products (
    product_no integer NOT NULL,
    name text NOT NULL,
    price numeric NOT NULL CHECK (price > 0)
);

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

Ограничение NOT NULL имеет обратное ограничение NULL. Это не означает, что столбец должен всегда быть нулевым - в таком случае он бесполезен. Вместо этого просто выбирается поведение по умолчанию, согласно которому столбец может быть пустым. Ограничение NULL отсутствует в стандарте SQL и не должно использоваться в переносимых приложениях. (Оно было добавлено только в QHB для совместимости с некоторыми системами баз данных). Однако такой синтаксис позволяет легко переключать ограничение в файле сценария. Например, вы можете начать с:

CREATE TABLE products (
    product_no integer NULL,
    name text NULL,
    price numeric NULL
);

и затем вставьте ключевое слово NOT, где это необходимо.

Ограничения уникальности

Ограничения уникальности (UNIQUE constraints) гарантируют, что данные, содержащиеся в столбце или группе столбцов, являются уникальными среди всех строк в таблице. Синтаксис:

CREATE TABLE products (
    product_no integer UNIQUE,
    name text,
    price numeric
);

когда задается как ограничение столбца, и:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric,
    UNIQUE (product_no)
);

когда задается, как ограничение таблицы.

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

CREATE TABLE example (
    a integer,
    b integer,
    c integer,
    UNIQUE (a, c)
);

Это указывает на то, что комбинация значений в указанных столбцах уникальна для всей таблицы, хотя любой из столбцов не обязательно должен быть (и обычно не является) уникальным.

Вы можете назначить свое собственное имя для ограничения уникальности обычным способом:

CREATE TABLE products (
    product_no integer CONSTRAINT must_be_different UNIQUE,
    name text,
    price numeric
);

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

Как правило, ограничения уникальности нарушается, если в таблице имеется более одной строки, в которой значения всех столбцов, входящих в ограничение, равны. Однако два null значения никогда не считаются равными в этом сравнении. Это означает, что даже при наличии уникального ограничения можно хранить повторяющиеся строки, которые содержат null значение, по крайней мере, в одном из ограниченных столбцов. Такое поведение соответствует стандарту SQL, но другие базы данных SQL могут не следовать этому правилу. Поэтому будьте осторожны при разработке приложений, предназначенных для переносимости.

Ограничение первичного ключа

Ограничение первичного ключа (Primary Keys constraint) указывает, что столбец или группа столбцов могут использоваться в качестве уникального идентификатора для строк в таблице. Необходимо, чтобы значения были как уникальными, так и не нулевыми. Итак, следующие два определения таблицы принимают одинаковые данные:

CREATE TABLE products (
    product_no integer UNIQUE NOT NULL,
    name text,
    price numeric
);

CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

Первичные ключи могут занимать более одного столбца в этом случае синтаксис похож на ограничения уникальности:

CREATE TABLE example (
    a integer,
    b integer,
    c integer,
    PRIMARY KEY (a, c)
);

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

Таблица не может иметь более одного первичного ключа. (Может быть любое количество уникальных и ненулевых ограничений, которые функционально почти одинаковы, но только одно может быть идентифицировано как первичный ключ). Теория реляционных баз данных требует, чтобы у каждой таблицы был первичный ключ. QHB не применяет это правило, но обычно его соблюдают.

Первичные ключи полезны как для целей документирования, так и для клиентских приложений. Например, приложение с графическим интерфейсом, которое позволяет изменять значения строк, скорее всего, должно знать первичный ключ таблицы, чтобы иметь возможность уникально идентифицировать строки. Существуют различные способы, которыми система баз данных использует первичный ключ, если он был объявлен, например, первичный ключ определяет целевой столбец (столбцы) по умолчанию для внешних ключей, ссылающихся на его таблицу.

Ограничение внешнего ключа

Ограничение внешнего ключа (foreign key constraint) указывает, что значения в столбце (или группе столбцов) должны соответствовать значениям, появляющимся в некоторой строке другой таблицы. Это ограничение поддерживает ссылочную целостность между двумя связанными таблицами.

Допустим, у вас есть таблица продуктов, которую мы уже использовали несколько раз:

CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

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

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    product_no integer REFERENCES products (product_no),
    quantity integer
);

Теперь невозможно создавать заказы с номерами продуктов отличными от NULL, которые отсутствуют в таблице продуктов.

В этой ситуации таблица заказов является дочерней таблицей, а таблица продуктов - родительской таблицей. Аналогично, есть ссылающиеся и упоминаемые столбцы.

Указанную выше команду можно сократить до:

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    product_no integer REFERENCES products,
    quantity integer
);

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

Внешний ключ также может ограничивать и ссылаться на группу столбцов. Как обычно, его необходимо записать в виде табличного ограничения. Вот пример надуманного синтаксиса:

CREATE TABLE t1 (
  a integer PRIMARY KEY,
  b integer,
  c integer,
  FOREIGN KEY (b, c) REFERENCES other_table (c1, c2)
);

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

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

Таблица может иметь более одного ограничения внешнего ключа. Это используется для реализации отношений «многие ко многим» между таблицами. Скажем, у вас есть таблицы о продуктах и заказах, но теперь вы хотите, чтобы один заказ мог содержать много продуктов (что не разрешено в приведенной выше структуре). Вы можете использовать следующую структуру таблиц:

CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    shipping_address text,
    ...
);

CREATE TABLE order_items (
    product_no integer REFERENCES products,
    order_id integer REFERENCES orders,
    quantity integer,
    PRIMARY KEY (product_no, order_id)
);

Обратите внимание, что первичный ключ накладывается на внешние ключи в последней таблице.

Внешние ключи запрещают создание заказов, которые не относятся ни к каким продуктам. Но если продукт будет удален после создания заказа, который ссылается на него? SQL позволяет вам справиться и с этим. У нас есть несколько вариантов:

запретить удаление указанного продукта;

удалить также и соответствующие заказы;

что-то другое?

Чтобы проиллюстрировать это, давайте реализуем следующую политику в приведенном выше примере отношения «многие ко многим»: когда кто-то хочет удалить продукт, на который все еще ссылается заказ (через order_items), мы запрещаем его. Если кто-то удаляет заказ, элементы заказа также удаляются:

CREATE TABLE products (
    product_no integer PRIMARY KEY,
    name text,
    price numeric
);

CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    shipping_address text,
    ...
);

CREATE TABLE order_items (
    product_no integer REFERENCES products ON DELETE RESTRICT,
    order_id integer REFERENCES orders ON DELETE CASCADE,
    quantity integer,
    PRIMARY KEY (product_no, order_id)
);

Ограничение внешнего ключа и каскадное удаление являются двумя наиболее распространенными вариантами. RESTRICT предотвращает удаление упоминаемой строки. NO ACTION означает, что если при проверке ограничения все еще существуют какие-либо ссылающиеся строки, то возникает ошибка - это поведение по умолчанию, если вы ничего не указали. (Существенное различие между этими двумя вариантами заключается в том, что NO ACTION позволяет отложить проверку до более поздней стадии в транзакции, тогда как RESTRICT этого не позволяет). CASCADE указывает, что при удалении строки, на которую есть ссылка, строки, ссылающиеся на нее, также должны быть автоматически удалены. Есть два других варианта: SET NULL и SET DEFAULT. Это приводит к тому, что ссылающиеся столбцы в ссылающейся строке становятся равными null или их значениям по умолчанию, соответственно, при удалении упоминаемой строки. Обратите внимание, что это не освобождает вас от соблюдения каких-либо других ограничений. Например, если действие указывает SET DEFAULT, но значение по умолчанию не удовлетворяет ограничению внешнего ключа, операция завершится ошибкой.

Аналогично ON DELETE также существует опция ON UPDATE которая вызывается при изменении (обновлении) упоминаемого столбца. Возможные действия одинаковы. В этом случае CASCADE означает, что обновленные значения упоминаемых столбцов должны быть скопированы также и в ссылающуюся строку.

Как правило, ссылающаяся строка не должна удовлетворять ограничению внешнего ключа, если какой-либо из ее ссылающихся столбцов равен null. Если к объявлению внешнего ключа добавляется MATCH FULL, то ссылающаяся строка экранируется, удовлетворяя ограничению, только если все ее ссылочные столбцы равны null (таким образом, сочетание нулевых и ненулевых значений гарантированно приведет к сбою ограничения MATCH FULL). Если вы не хотите, чтобы ссылающиеся строки не удовлетворяли ограничению внешнего ключа, объявите ссылающиеся столбцы как NOT NULL.

Внешний ключ должен ссылаться на столбцы, которые являются первичным ключом или образуют уникальное ограничение. Это означает, что упоминаемые столбцы всегда имеют связанный индекс (тот, который лежит в основе первичного ключа или ограничения уникальности) - поэтому проверка на соответствие ссылающейся строки будет эффективной. Поскольку удаление строки из упоминаемой таблицы или обновление упоминаемого столбца потребуется сканирование ссылающейся таблицы на наличие строк, соответствующих старому значению, часто рекомендуется также индексировать ссылающиеся столбцы. Поскольку это не всегда необходимо, и существует множество вариантов индексации, объявление ограничения внешнего ключа не создает автоматически индекс для ссылающихся столбцов.

Более подробная информация об обновлении и удалении данных приведена в главе Манипулирование данными. Также см. Описание синтаксиса ограничения внешнего ключа в справочной документации по CREATE TABLE.

Ограничения исключения

Ограничения исключения (Exclusion constraints) гарантируют, что, если любые две строки сравниваются по указанным столбцам или выражениям с использованием указанных операторов, то по крайней мере один из этих операторов сравнения вернет false или null. Синтаксис:

CREATE TABLE circles (
    c circle,
    EXCLUDE USING gist (c WITH &&)
);

Смотрите также CREATE TABLE ... CONSTRAINT ... EXCLUDE для подробностей.

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

Системные столбцы

Каждая таблица имеет несколько системных столбцов, которые неявно определяются системой. Следовательно, эти имена нельзя использовать в качестве имен пользовательских столбцов. (Обратите внимание, что эти ограничения не зависят от того, является ли имя ключевым словом или нет - цитирование имени не позволит вам обойти эти ограничения).

Системные столбцы

Имя столбца
Назначение

tableoid
OID таблицы, содержащей эту строку. Этот столбец особенно удобен для запросов, которые выбираются из иерархий наследования (см. раздел Наследование, поскольку без него трудно определить, из какой отдельной таблицы получена строка. tableoid можно объединить со столбцом oid из таблицы pg_class чтобы получить имя таблицы

xmin
Идентификатор (идентификатор транзакции) вставляющей транзакции для этой версии строки. (Версия строки - это отдельное состояние строки, каждое обновление строки создает новую версию строки для той же логической строки)

cmin
Идентификатор команды (начиная с нуля) внутри транзакции

xmax
Идентификатор (идентификатор транзакции) удаляющей транзакции или ноль для неустановленной версии строки. Этот столбец может быть ненулевым в видимой версии строки. Обычно это означает, что транзакция удаления еще не зафиксирована или что попытка удаления была отменена

cmax
Идентификатор команды в транзакции удаления или ноль если запись не удалялась

ctid
Физическое местоположение версии строки в ее таблице. Обратите внимание, что хотя ctid можно использовать для быстрого поиска версии строки, ctid изменится, если он будет обновлен или перемещен с помощью операции VACUUM FULL. Поэтому ctid бесполезен в качестве долгосрочного идентификатора строки. Для логической идентификации строк должен использоваться первичный ключ

Идентификаторы транзакций также являются 32-битными величинами. В долгоживущей базе данных можно начинать заново нумерацию для идентификаторов транзакций. Это не фатальная проблема с учетом соответствующих процедур обслуживания - см. главу Регулярные задачи обслуживания базы данных для деталей. Однако плохим решением будет зависеть от уникальности идентификаторов транзакций при длительной эксплуатации БД (более одного миллиарда транзакций).

Идентификаторы команд также являются 32-битными величинами. Это создает жесткий предел в 2^32 (примерно 4 миллиарда) команд SQL в одной транзакции. На практике это ограничение не является проблемой - обратите внимание, что ограничение на количество команд SQL, а не количество обработанных строк. Кроме того, только команды, которые фактически изменяют содержимое базы данных, будут использовать идентификатор команды.

Изменение таблиц

Когда вы создаете таблицу и понимаете, что допустили ошибку или изменились требования приложения, вы можете удалить таблицу и создать ее заново. Но это не самый удобный вариант, если таблица уже заполнена данными, или если на таблицу ссылаются другие объекты базы данных (например, ограничение внешнего ключа). Поэтому QHB предоставляет семейство команд для внесения изменений в существующие таблицы. Обратите внимание, что это принципиально отличается от изменения данных, содержащихся в таблице: здесь мы заинтересованы в изменении структуры таблицы.

Можно вносить следующие изменения в структуру таблицы:

Добавить столбцы

Удалить столбцы

Добавить ограничения

Удалить ограничения

Изменить значения по умолчанию

Изменить типы данных столбца

Переименовать столбцы

Переименовать таблицы

Все эти действия выполняются с помощью команды ALTER TABLE, справочная страница которой содержит дополнительные подробности, кроме приведенных ниже.

Добавление столбца

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

ALTER TABLE products ADD COLUMN description text;

Новый столбец изначально заполняется любым заданным значением по умолчанию (null, если вы не указано предложение DEFAULT).

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

ALTER TABLE products ADD COLUMN description text CHECK (description <> '');

Фактически все параметры, которые можно применить к описанию столбца в CREATE TABLE можно использовать в этой команде. Имейте в виду, однако, что значение по умолчанию должно удовлетворять заданным ограничениям, иначе ADD завершится с ошибкой. Кроме того, можно добавить ограничения позже (см. ниже) после того, как вы правильно заполнили новый столбец.

Удаление столбца

Чтобы удалить столбец, используйте команду, например:

ALTER TABLE products DROP COLUMN description;

Какие бы данные ни были в столбце, они исчезнут. Ограничения таблицы, включающие столбец, также удалятся. Однако, если на столбец ссылается ограничение внешнего ключа другой таблицы, QHB не будет автоматически отброшено это ограничение. Вы можете разрешить удаление всего, что зависит от столбца, добавив CASCADE:

ALTER TABLE products DROP COLUMN description CASCADE;

См. раздел Отслеживание зависимостей для знакомства с общим механизмом отслеживания зависимостей.

Добавление ограничения

Чтобы добавить ограничение, используется синтаксис ограничения таблицы. Например:

ALTER TABLE products ADD CHECK (name <> '');
ALTER TABLE products ADD CONSTRAINT some_name UNIQUE (product_no);
ALTER TABLE products ADD FOREIGN KEY (product_group_id) REFERENCES product_groups;

Чтобы добавить ограничение not-null, которое нельзя записать как ограничение таблицы, используйте этот синтаксис:

ALTER TABLE products ALTER COLUMN product_no SET NOT NULL;

Ограничение будет проверено немедленно, поэтому данные таблицы должны удовлетворять ограничению, прежде чем его можно будет добавить.

Снятие ограничения

Чтобы удалить ограничение, нужно знать его имя. Если имя задано, это легко. В противном случае система присваивает сгенерированное имя, которое необходимо выяснить. Команда для снятия ограничения может выглядеть так:

ALTER TABLE products DROP CONSTRAINT some_name;

(Если вы имеете дело с сгенерированным именем ограничения, таким как $2, не забывайте, что вам нужно будет заключить его в двойные кавычки, чтобы сделать его действительным идентификатором).

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

Это работает одинаково для всех типов ограничений, кроме ограничений not-null, в связи с тем, что ограничения not-null не имеют имен. Чтобы удалить ограничение not-null, используйте:

ALTER TABLE products ALTER COLUMN product_no DROP NOT NULL;

Изменение значения столбца по умолчанию

Чтобы установить новое значение по умолчанию для столбца, используйте команду, например:

ALTER TABLE products ALTER COLUMN price SET DEFAULT 7.77;

Обратите внимание, что выполнение этой команды не влияет на существующие строки в таблице, она просто меняет значение по умолчанию для будущих команд INSERT.

Чтобы удалить любое значение по умолчанию, используйте:

ALTER TABLE products ALTER COLUMN price DROP DEFAULT;

Это практически то же самое, что установка по умолчанию на null. Как следствие, не является ошибкой сбрасывать значение по умолчанию, если оно не было определено, потому что значение по умолчанию является неявным null значением.

Изменение типа данных столбца

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

ALTER TABLE products ALTER COLUMN price TYPE numeric(10,2);

Команда завершится успешно, только если каждая существующая запись в столбце может быть преобразована в новый тип путем неявного приведения. Если требуется более сложное преобразование, вы можете добавить предложение USING, в котором указано, как получить новые значения из старых.

QHB будет пытаться преобразовать значение столбца по умолчанию (если оно есть) в новый тип, а также проверить любые ограничения, связанные со столбцом. Но эти преобразования и проверки могут потерпеть неудачу или могут привести к неожиданным результатам. Часто лучше удалить любые ограничения на столбец перед изменением его типа, а затем добавить измененные ограничения.

Переименование столбца

Чтобы переименовать столбец:

ALTER TABLE products RENAME COLUMN product_no TO product_number;

Переименование таблицы

Чтобы переименовать таблицу:

ALTER TABLE products RENAME TO items;

Права

При создании объекта ему назначается владелец. Обычно владельцем является роль, выполнившая команды создания. Для большинства типов объектов начальное состояние таково, что только владелец (или суперпользователь) может что-либо делать с объектом. Чтобы другие роли могли его использовать, необходимо предоставить права.

Существуют различные виды прав: SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER, CREATE, CONNECT, TEMPORARY, EXECUTE и USAGE. Права, применимые к конкретному объекту, различаются в зависимости от типа объекта (таблица, функция и т. д.). Более подробная информация о значениях этих прав приведена ниже. В следующих разделах и главах также будет показано, как используются эти права.

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

Объект может быть назначен новому владельцу с помощью команды ALTER соответствующего вида для объекта, например:

ALTER TABLE table_name OWNER TO new_owner;

Суперпользователи всегда могут это сделать, обычные роли могут делать это только в том случае, если они являются одновременно текущим владельцем объекта (или членом роли-владельца) и членом новой роли-владельца.

Для назначения прав используется команда GRANT. Например, если joe является существующей ролью, а accounts - существующей таблицей, право для обновления таблицы может быть предоставлено с помощью:

GRANT UPDATE ON accounts TO joe;

Запись ALL вместо определенного права предоставляет все права, которые относятся к типу объекта.

Специальное имя роли «PUBLIC» можно использовать для предоставления права каждой роли в системе. Кроме того, «групповые» роли могут быть установлены, чтобы помочь управлять правами, когда существует много пользователей базы данных - подробности см. в главе Роли в базе данных.

Чтобы отозвать право, используйте подходящую команду REVOKE:

REVOKE ALL ON accounts FROM PUBLIC;

Специальные права владельца объекта (т.е. право выполнять DROP, GRANT, REVOKE и т. д.) всегда подразумеваются для владельца и не могут быть предоставлены или отозваны. Но владелец объекта может отозвать свои обычные права, например, сделать таблицу доступной только для чтения как для себя, так и для других.

Обычно, только владелец объекта (или суперпользователь) может предоставить или отозвать права для объекта. Тем не менее, можно предоставить право «с опцией предоставления», которое дает получателю возможность предоставлять его в свою очередь другим лицам. Если опция предоставления впоследствии аннулируется, тогда все, кто получил право от этого получателя (напрямую или через цепочку грантов), потеряют это право. Подробнее см. Справочные страницы GRANT и REVOKE.

Доступные права:

Право
Описание

SELECT
Позволяет выполнять выбор из любого столбца или определенного столбца (столбцов) таблицы, представления, материализованного представления или другого табличного объекта. Также позволяет использовать *COPY TO. Это право также необходимо для ссылки на существующие значения столбца в *UPDATE или DELETE. Для последовательностей это право также позволяет использовать функцию currval. Для больших объектов это право позволяет объекту быть прочитанным.

INSERT
Разрешает вставку новой строки в таблицу, представление и т. д. Может быть предоставлено определенным столбцам, в этом случае только командам INSERT могут быть назначены только эти столбцы (поэтому другие столбцы будут получать значения по умолчанию). Также позволяет использовать COPY FROM

UPDATE
Разрешает обновление любого столбца или определенного столбца (столбцов) таблицы, представления и т. Д. (На практике любая нетривиальная команда UPDATE также требует права SELECT, поскольку она должна ссылаться на столбцы таблицы, чтобы определить, какие строки обновлять, и / или для вычисления новых значений для столбцов). SELECT ... FOR UPDATE и SELECT ... FOR SHARE также требуют это право как минимум для одного столбца в дополнение к праву SELECT. Для последовательностей это право позволяет использовать функции nextval и setval. Для больших объектов это право позволяет писать или очищать (TRUNCATE) объект.

DELETE
Позволяет удалить строки из таблицы, представления и т. д. (На практике любой нетривиальной команде DELETE также потребуется право SELECT, поскольку она должна ссылаться на столбцы таблицы, чтобы определить, какие строки следует удалить).

TRUNCATE
Позволяет очистить таблицы, представления и т. д.

REFERENCES
Позволяет создать ограничение внешнего ключа, ссылающегося на таблицу или определенные столбцы таблицы.

TRIGGER
Позволяет создать триггер для таблицы, представления и т. д.

CREATE
Для баз данных позволяет создавать новые схемы и публикации в базе данных. Для схем позволяет создавать новые объекты внутри схемы. Чтобы переименовать существующий объект, вы должны владеть объектом и иметь это право для содержащей схемы.Для табличных пространств позволяет создавать таблицы, индексы и временные файлы в табличном пространстве и позволяет создавать базы данных, в которых табличное пространство является табличным пространством по умолчанию. (Обратите внимание, что отзыв этого права не изменит размещение существующих объектов).

CONNECT
Позволяет грантополучателю подключаться к базе данных. Это право проверяется при запуске соединения (в дополнение к проверке любых ограничений, налагаемых qhb_hba.conf).

TEMPORARY
Позволяет создавать временные таблицы при использовании базы данных.

EXECUTE
Позволяет вызывать функцию или процедуру, включая использование любых операторов, которые реализованы поверх функции. Это единственный тип привилегий, применимый к функциям и процедурам.

USAGE
Для процедурных языков позволяет использовать язык для создания функций на этом языке. Это единственный тип привилегий, применимый к процедурным языкам.Для схем разрешает доступ к объектам, содержащимся в схеме (при условии, что собственные требования к правам объектов также выполнены). По сути, это позволяет получателю « искать » объекты в схеме. Без этого разрешения все еще можно увидеть имена объектов, например, путем запроса системных каталогов. Кроме того, после отзыва этого разрешения существующие сеансы могут иметь операторы, которые ранее выполняли этот поиск, так что это не является полностью безопасным способом предотвращения доступа к объекту.Для последовательностей позволяет использовать функции currval и nextval.Для типов и доменов позволяет использовать тип или домен при создании таблиц, функций и других объектов схемы. (Обратите внимание, что это право не управляет всем « использованием » типа, например значениями типа, появляющимися в запросах. Она только предотвращает создание объектов, зависящих от типа. Основная цель этого права - контролировать, какие пользователи могут создать зависимости от типа, которые могут помешать владельцу изменить тип позже).Для оболочек сторонних данных позволяет создавать новые серверы с использованием *foreign-data wrapper. Для сторонних серверов позволяет создавать сторонние таблицы с использованием сервера. Получатели могут также создавать, изменять или удалять свои собственные сопоставления пользователей, связанные с этим сервером.

Права, требуемые для других команд, перечислены на странице ссылок соответствующей команды.

QHB предоставляет права для некоторых типов объектов PUBLIC по умолчанию при создании объектов. PUBLIC по умолчанию не предоставляет никаких привилегий для таблиц, столбцов таблиц, последовательностей, внешних источников данных, внешних серверов, больших объектов, схем или табличных пространств. Для других типов объектов правами по умолчанию, предоставляемыми PUBLIC являются:

права CONNECT и TEMPORARY (создание временных таблиц) для баз данных; EXECUTE право для функций и процедур;

право USAGE для языков и типов данных(включая домены).

Владелец объекта может, конечно, отозвать права по умолчанию и явно предоставленные права. (Для обеспечения максимальной безопасности выполните команду REVOKE в той же транзакции, в которой создается объект тогда не будет промежутка, в котором другой пользователь может использовать объект). Кроме того, эти настройки привилегий по умолчанию можно изменить с помощью команды ALTER DEFAULT PRIVILEGES.

Таблица "Сокращения привилегий ACL" показывает однобуквенные сокращения, которые используются для этих типов привилегий в значениях ACL (Access Control List). Вы увидите эти буквы в выходных данных команд psql, перечисленных ниже, или при просмотре столбцов ACL системных каталогов.

Таблица Сокращения привилегий ACL

Льгота | Сокращенное название | Применимые типы объектов
SELECT | r («читать») | LARGE OBJECT, SEQUENCE, TABLE (и табличные объекты), столбец таблицы
INSERT | a («добавить») | TABLE, столбец таблицы
UPDATE | w («написать») | LARGE OBJECT, SEQUENCE, TABLE, столбец таблицы
DELETE | d | TABLE
TRUNCATE | D | TABLE
REFERENCES | x | TABLE, столбец таблицы
TRIGGER | t | TABLE
CREATE | C | DATABASE, SCHEMA, TABLESPACE
CONNECT | c | DATABASE
TEMPORARY | T | DATABASE
EXECUTE | X | FUNCTION, PROCEDURE
USAGE | U | DOMAIN, FOREIGN DATA WRAPPER, FOREIGN SERVER, LANGUAGE, SCHEMA, SEQUENCE, TYPE

Таблица "Сводка прав доступа" объединяет права, доступные для каждого типа объекта SQL, с использованием приведенных выше сокращений.

Таблица 4.2. Сводка прав доступа

Тип объекта | Все права | PUBLIC права по умолчанию
DATABASE | CTc | Tc
DOMAIN | U | U
FUNCTION или PROCEDURE | X | X
FOREIGN DATA WRAPPER | U | none
FOREIGN SERVER | U | none
LANGUAGE | U | U
LARGE OBJECT | rw | none
SCHEMA | UC | none
SEQUENCE | rwU | none
TABLE (и табличные объекты) | arwdDxt | none
Столбец таблицы | arwx | none
TABLESPACE | C | none
TYPE | U | U

Права, которые были предоставлены для определенного объекта, отображаются в виде списка записей aclitem, где каждый aclitem описывает разрешения одного грантополучателя, которые были предоставлены конкретным лицом, предоставляющим право. Например, calvin=r*w/hobbes указывает, что роль calvin имеет право SELECT (r) с опцией его предоставления (*), а также не подлежащее предоставлению право UPDATE (w), оба из которых предоставлены ролью hobbes. Если calvin также имеет некоторые права на тот же объект, предоставленные другим лицом, предоставляющим право, они будут отображаться как отдельная запись aclitem. Пустое поле грантополучателя в aclitem означает PUBLIC.

В качестве примера предположим, что пользователь miriam создает таблицу mytable и выполняет:

GRANT SELECT ON mytable TO PUBLIC;
GRANT SELECT, UPDATE, INSERT ON mytable TO admin;
GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw;

Тогда команда psql \dp покажет:

=> \dp mytable
                                  Access privileges
 Schema |  Name   | Type  |   Access privileges   |   Column privileges   | Policies
--------+---------+-------+-----------------------+-----------------------+----------
 public | mytable | table | miriam=arwdDxt/miriam+| col1:                +|
        |         |       | =r/miriam            +|   miriam_rw=rw/miriam |
        |         |       | admin=arw/miriam      |                       |
(1 row)

Если столбец «Access privileges» для данного объекта пуст, это означает, что объект имеет права по умолчанию (то есть запись его привилегий в соответствующем системном каталоге равна null). Права по умолчанию всегда включают все права для владельца и могут включать некоторые права для PUBLIC в зависимости от типа объекта, как описано выше. При первом предоставлении или отзыве объекта создаются экземпляры привилегий по умолчанию (например, miriam=arwdDxt/miriam), а затем они изменяются в соответствии с указанным запросом. Аналогично, записи отображаются в «Column privileges» только для столбцов с правами не по умолчанию. (Примечание: для этой цели «права по умолчанию» всегда означают встроенные права по умолчанию для типа объекта. Объект, чьи права были затронуты командой ALTER DEFAULT PRIVILEGES, всегда будет отображаться с явной записью права, включающей эффекты ALTER).

Обратите внимание, что неявные параметры предоставления прав владельца не отмечены в выводе привилегий доступа. А * появится только тогда, когда параметры прав были кому-либо явно предоставлены.

Политики защиты строк

В дополнение к стандартной системе привилегий SQL, доступной через GRANT, таблицы могут иметь политики защиты строк, которые ограничивают для каждого пользователя возможность того какие строки могут быть возвращены обычными запросами или вставлены, обновлены или удалены с помощью команд изменения данных. Эта функция также известна как «безопасность на уровне строк». По умолчанию таблицы не имеют никаких политик, поэтому, если у пользователя есть права доступа к таблице в соответствии с системой привилегий SQL, все строки в ней одинаково доступны для запроса или обновления.

Когда в таблице включена защита строк (с помощью ALTER TABLE ... ENABLE ROW LEVEL SECURITY), любой нормальный доступ к таблице для выбора строк или изменения строк должен быть разрешен политикой безопасности строк. (Однако владелец таблицы обычно не подчиняется политикам безопасности строк). Если для таблицы не существует политики, используется политика запрета по умолчанию, что означает, что строки не видны или не могут быть изменены. Операции, которые применяются ко всей таблице, такие как TRUNCATE и REFERENCES, не подлежат защите строк.

Политики защиты строк могут быть специфичными для команд, ролей или для того и другого. Можно указать политику, применяемую ко всем командам или к SELECT, INSERT, UPDATE или DELETE. Несколько политик могут быть назначены для данной политики, и применяются нормальные правила членства и наследования ролей.

Чтобы указать, какие строки являются видимыми или изменяемыми в соответствии с политикой, требуется выражение, которое возвращает логический результат. Это выражение будет оцениваться для каждой строки перед проверкой любых условий или выполнения функций, поступающих из пользовательского запроса. (Единственным исключением из этого правила являются герметичные функции, которые гарантированно не допускают утечки информации - оптимизатор может выбрать применение таких функций перед проверкой безопасности строк). Строки, для которых выражение не возвращает true, не будут обрабатываться. Отдельные выражения могут быть указаны для обеспечения независимого контроля над видимыми строками и строками, которые могут быть изменены. Выражения политики выполняются как часть запроса с правами пользователя, выполняющего запрос, хотя функции определения безопасности могут использоваться для доступа к данным, недоступным для вызывающего пользователя.

Суперпользователи и роли с атрибутом BYPASSRLS всегда обходят систему безопасности строк при доступе к таблице. Владельцы таблиц обычно также обходят защиту строк, хотя владелец таблицы может выбрать защиту строк с помощью ALTER TABLE ... FORCE ROW LOWEL SECURITY.

Включение и выключение защиты строк, а также добавление политик в таблицу всегда является привилегией только владельца таблицы.

Политики создаются с помощью команды CREATE POLICY, изменяются с помощью команды ALTER POLICY и удаляются с помощью команды DROP POLICY. Чтобы включить и выключить защиту строк для данной таблицы, используйте команду ALTER TABLE.

У каждой политики есть имя, и для таблицы можно определить несколько политик. Поскольку политики зависят от таблицы, каждая политика для таблицы должна иметь уникальное имя. Разные таблицы могут иметь политики с одинаковым именем.

Когда к данному запросу применяются несколько политик, они объединяются либо с помощью OR (для разрешающих политик, которые используются по умолчанию), либо с помощью AND (для ограничительных политик). Это похоже на правило, согласно которому данная роль обладает правами всех ролей, членом которых они являются. Разрешительные и ограничительные политики обсуждаются ниже.

В качестве простого примера ниже показано, как создать политику в отношении account чтобы разрешить доступ к строкам только членам роли managers и только строкам их учетных записей:

CREATE TABLE accounts (manager text, company text, contact_email text);

ALTER TABLE accounts ENABLE ROW LEVEL SECURITY;

CREATE POLICY account_managers ON accounts TO managers
    USING (manager = current_user);

Приведенная выше политика неявно предоставляет предложение WITH CHECK, идентичное его предложению USING, так что ограничение применяется как к строкам, выбранным командой (таким образом, менеджер не может SELECT, UPDATE или DELETE существующих строк, принадлежащих другому менеджеру), так и к строкам, измененным командой (таким образом, строки, принадлежащие другому менеджеру, не могут быть созданы с помощью INSERT или UPDATE).

Если роль не указана или используется специальное имя пользователя PUBLIC, то политика применяется ко всем пользователям в системе. Чтобы разрешить всем пользователям доступ только к своей строке в таблице users, можно использовать простую политику:

CREATE POLICY user_policy ON users
    USING (user_name = current_user);

Это работает аналогично предыдущему примеру.

Чтобы использовать другую политику для строк, добавляемых в таблицу, по сравнению с видимыми строками, можно объединить несколько политик. Эта пара политик позволит всем пользователям просматривать все строки в таблице users, но изменять только свои собственные:

CREATE POLICY user_sel_policy ON users
    FOR SELECT
    USING (true);
CREATE POLICY user_mod_policy ON users
    USING (user_name = current_user);

В команде SELECT эти две политики объединяются с использованием OR, в результате чего можно выбрать все строки. В других типах команд применяется только вторая политика, так что эффекты такие же, как и раньше.

Защита строк также может быть выключена с помощью команды ALTER TABLE. Выключение защиты строк не приводит к удалению политик, определенных в таблице - они просто игнорируются. Затем все строки в таблице становятся видимыми и изменяемыми в соответствии со стандартной системой привилегий SQL.

Ниже приведен более крупный пример того, как эту функцию можно использовать в производственных средах. Таблица passwd эмулирует файл паролей Unix:

-- Simple passwd-file based example
CREATE TABLE passwd (
  user_name             text UNIQUE NOT NULL,
  pwhash                text,
  uid                   int  PRIMARY KEY,
  gid                   int  NOT NULL,
  real_name             text NOT NULL,
  home_phone            text,
  extra_info            text,
  home_dir              text NOT NULL,
  shell                 text NOT NULL
);

CREATE ROLE admin;  -- Administrator
CREATE ROLE bob;    -- Normal user
CREATE ROLE alice;  -- Normal user

-- Populate the table
INSERT INTO passwd VALUES
  ('admin','xxx',0,0,'Admin','111-222-3333',null,'/root','/bin/dash');
INSERT INTO passwd VALUES
  ('bob','xxx',1,1,'Bob','123-456-7890',null,'/home/bob','/bin/zsh');
INSERT INTO passwd VALUES
  ('alice','xxx',2,1,'Alice','098-765-4321',null,'/home/alice','/bin/zsh');

-- Be sure to enable row level security on the table
ALTER TABLE passwd ENABLE ROW LEVEL SECURITY;

-- Create policies
-- Administrator can see all rows and add any rows
CREATE POLICY admin_all ON passwd TO admin USING (true) WITH CHECK (true);
-- Normal users can view all rows
CREATE POLICY all_view ON passwd FOR SELECT USING (true);
-- Normal users can update their own records, but
-- limit which shells a normal user is allowed to set
CREATE POLICY user_mod ON passwd FOR UPDATE
  USING (current_user = user_name)
  WITH CHECK (
    current_user = user_name AND
    shell IN ('/bin/bash','/bin/sh','/bin/dash','/bin/zsh','/bin/tcsh')
  );

-- Allow admin all normal rights
GRANT SELECT, INSERT, UPDATE, DELETE ON passwd TO admin;
-- Users only get select access on public columns
GRANT SELECT
  (user_name, uid, gid, real_name, home_phone, extra_info, home_dir, shell)
  ON passwd TO public;
-- Allow users to update certain columns
GRANT UPDATE
  (pwhash, real_name, home_phone, extra_info, shell)
  ON passwd TO public;

Как и при любых настройках безопасности, важно протестировать и убедиться, что система работает должным образом. В приведенном выше примере показано, что система разрешений работает правильно.

-- admin can view all rows and fields
qhb=> set role admin;
SET
qhb=> table passwd;
 user_name | pwhash | uid | gid | real_name |  home_phone  | extra_info | home_dir    |   shell
-----------+--------+-----+-----+-----------+--------------+------------+-------------+-----------
 admin     | xxx    |   0 |   0 | Admin     | 111-222-3333 |            | /root       | /bin/dash
 bob       | xxx    |   1 |   1 | Bob       | 123-456-7890 |            | /home/bob   | /bin/zsh
 alice     | xxx    |   2 |   1 | Alice     | 098-765-4321 |            | /home/alice | /bin/zsh
(3 rows)

-- Test what Alice is able to do
qhb=> set role alice;
SET
qhb=> table passwd;
ERROR:  permission denied for relation passwd
qhb=> select user_name,real_name,home_phone,extra_info,home_dir,shell from passwd;
 user_name | real_name |  home_phone  | extra_info | home_dir    |   shell
-----------+-----------+--------------+------------+-------------+-----------
 admin     | Admin     | 111-222-3333 |            | /root       | /bin/dash
 bob       | Bob       | 123-456-7890 |            | /home/bob   | /bin/zsh
 alice     | Alice     | 098-765-4321 |            | /home/alice | /bin/zsh
(3 rows)

qhb=> update passwd set user_name = 'joe';
ERROR:  permission denied for relation passwd
-- Alice is allowed to change her own real_name, but no others
qhb=> update passwd set real_name = 'Alice Doe';
UPDATE 1
qhb=> update passwd set real_name = 'John Doe' where user_name = 'admin';
UPDATE 0
qhb=> update passwd set shell = '/bin/xx';
ERROR:  new row violates WITH CHECK OPTION for "passwd"
qhb=> delete from passwd;
ERROR:  permission denied for relation passwd
qhb=> insert into passwd (user_name) values ('xxx');
ERROR:  permission denied for relation passwd
-- Alice can change her own password; RLS silently prevents updating other rows
qhb=> update passwd set pwhash = 'abc';
UPDATE 1

Все созданные до сих пор политики были разрешающими, то есть при применении нескольких политик они объединяются с использованием логического оператора «ИЛИ». Хотя разрешающие политики могут создаваться так, чтобы разрешать доступ к строкам только в намеченных случаях, может быть проще объединить разрешающие политики с ограничительными политиками (которые должны пропускать записи и которые объединяются с использованием логического оператора «И»). Основываясь на приведенном выше примере, мы добавляем ограничительную политику, требующую подключения администратора через локальный сокет Unix для доступа к записям таблицы passwd:

CREATE POLICY admin_local_only ON passwd AS RESTRICTIVE TO admin
    USING (pg_catalog.inet_client_addr() IS NULL);

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

=> SELECT current_user;
 current_user
--------------
 admin
(1 row)

=> select inet_client_addr();
 inet_client_addr
------------------
 127.0.0.1
(1 row)

=> SELECT current_user;
 current_user
--------------
 admin
(1 row)

=> TABLE passwd;
 user_name | pwhash | uid | gid | real_name | home_phone | extra_info | home_dir | shell
-----------+--------+-----+-----+-----------+------------+------------+----------+-------
(0 rows)

=> UPDATE passwd set pwhash = NULL;
UPDATE 0

Проверки ссылочной целостности, такие как ограничения уникальных или первичных ключей и ссылки на внешние ключи, всегда обходят защиту строк, чтобы обеспечить целостность данных. При разработке схем и политик на уровне строк необходимо соблюдать осторожность, чтобы избежать утечек информации по «скрытому каналу» посредством таких проверок ссылочной целостности.

В некоторых случаях важно быть уверенным, что защита строк не применяется. Например, при создании резервной копии это может привести к катастрофическим последствиям, если политика безопасности строк приведет к исключению некоторых строк из резервной копии. В такой ситуации вы можете выключить параметр конфигурации row_security. Это само по себе не обходит безопасность строк - он выдает ошибку, если результаты какого-либо запроса будут отфильтрованы политикой. Причина ошибки может быть затем исследована и исправлена.

В приведенных выше примерах политики учитывают только текущие значения в строке, к которым осуществляется доступ или которые обновляются. Это самый простой и наиболее эффективный случай - когда это возможно, лучше всего разрабатывать приложения для защиты строк таким образом. Если необходимо обратиться к другим строкам или другим таблицам для принятия решения о политике, это может быть выполнено с использованием вложенных элементов SELECT или функций, содержащих SELECT**, в выражениях политики. Имейте в виду, однако, что такой доступ может создать условия, которые могут привести к утечке информации, если не принять меры предосторожности. В качестве примера рассмотрим следующую конструкцию таблицы:

-- definition of privilege groups
CREATE TABLE groups (group_id int PRIMARY KEY,
                     group_name text NOT NULL);

INSERT INTO groups VALUES
  (1, 'low'),
  (2, 'medium'),
  (5, 'high');

GRANT ALL ON groups TO alice;  -- alice is the administrator
GRANT SELECT ON groups TO public;

-- definition of users' privilege levels
CREATE TABLE users (user_name text PRIMARY KEY,
                    group_id int NOT NULL REFERENCES groups);

INSERT INTO users VALUES
  ('alice', 5),
  ('bob', 2),
  ('mallory', 2);

GRANT ALL ON users TO alice;
GRANT SELECT ON users TO public;

-- table holding the information to be protected
CREATE TABLE information (info text,
                          group_id int NOT NULL REFERENCES groups);

INSERT INTO information VALUES
  ('barely secret', 1),
  ('slightly secret', 2),
  ('very secret', 5);

ALTER TABLE information ENABLE ROW LEVEL SECURITY;

-- a row should be visible to/updatable by users whose security group_id is
-- greater than or equal to the row's group_id
CREATE POLICY fp_s ON information FOR SELECT
  USING (group_id <= (SELECT group_id FROM users WHERE user_name = current_user));
CREATE POLICY fp_u ON information FOR UPDATE
  USING (group_id <= (SELECT group_id FROM users WHERE user_name = current_user));

-- we rely only on RLS to protect the information table
GRANT ALL ON information TO public;

Теперь предположим, что alice хочет изменить «не всем доступную» информацию, но решает, что mallory не следует доверять новое содержимое этой строки, поэтому она делает:

BEGIN;
UPDATE users SET group_id = 1 WHERE user_name = 'mallory';
UPDATE information SET info = 'secret from mallory' WHERE group_id = 2;
COMMIT;

Это выглядит безопасно - нет промежутка между выполнением команд, в котором mallory должен видеть строку «secret от mallory». Тем не менее, здесь есть условия гонки. Если mallory одновременно делает, скажем,

SELECT * FROM information WHERE group_id = 2 FOR UPDATE;

и его транзакция находится в режиме READ COMMITTED, он может видеть «секрет от mallory». Это происходит, если его транзакция достигает информационной строки сразу после транзакции alice. Он блокирует ожидание фиксации транзакции alice, а затем извлекает обновленное содержимое строки благодаря предложению FOR UPDATE. Однако он не извлекает обновленную строку для неявного SELECT from users, потому что этот sub-SELECT не имел FOR UPDATE - вместо этого строка users читается из снимка, сделанного в начале запроса. Поэтому выражение политики проверяет старое значение уровня привилегий mallory и позволяет ему видеть обновленную строку.

Есть несколько способов обойти эту проблему. Одним простым ответом является использование SELECT ... FOR SHARE в параметрах SELECT в политиках безопасности строк. Однако для этого необходимо предоставить право UPDATE для ссылочной таблицы (в данном случае users) заинтересованным пользователям, что может быть нежелательным. (Но можно применить другую политику безопасности строк, чтобы предотвратить их фактическое использование этого права или встроить подзапрос в функцию определения безопасности). Кроме того, интенсивное одновременное использование блокировок общего ресурса строки в ссылочной таблице может привести к снижению производительности, особенно если строку часто обновляют. Другое практическое решение, если обновления ссылочной таблицы происходят нечасто — установить исключительную блокировку ссылочной таблицы при ее обновлении, чтобы никакие параллельные транзакции не могли проверять старые значения строк. Или можно просто дождаться завершения всех одновременных транзакций после фиксации обновления таблицы, на которую есть ссылка, начавшихся до внесения изменений, основанных на новой ситуации безопасности.

Для получения дополнительной информации см. CREATE POLICY и ALTER TABLE.

Схемы

Кластер баз данных QHB содержит одну или несколько именованных баз данных. Пользователи и группы пользователей являются общими для всего кластера, но никакие другие данные не передаются между базами данных. Любое данное клиентское соединение с сервером может получить доступ только к объектам в одной базе данных, указанной в запросе на соединение.

Пользователи кластера не обязательно имеют право доступа к каждой базе данных в кластере. Совместное использование имен пользователей означает, что не может быть разных пользователей с именем, скажем, joe в двух базах данных в одном кластере, но система может быть настроена так, чтобы позволить joe доступ только к некоторым базам данных.

База данных содержит одну или несколько именованных схем, которые в свою очередь содержат таблицы. Схемы также содержат другие виды именованных объектов, включая типы данных, функции и операторы. Одно и то же имя объекта может использоваться в разных схемах без конфликтов - например, и schema1 и myschema могут содержать таблицы с именем mytable. В отличие от баз данных, схемы жестко не разделены: пользователь может получить доступ к объектам в любой из схем в базе данных, к которой он подключен, если у него есть права для этого.

Существует несколько причин, по которым можно использовать схемы:

позволить многим пользователям использовать одну базу данных, не мешая друг другу;

организовать объекты базы данных в логические группы, чтобы сделать их более управляемыми;

сторонние приложения могут быть помещены в отдельные схемы, чтобы они не конфликтовали с именами других объектов.

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

Создание схемы

Чтобы создать схему, используйте команду CREATE SCHEMA. Дайте схеме имя по вашему выбору. Например:

CREATE SCHEMA myschema;

Чтобы создать или получить доступ к объектам в схеме, напишите полное имя, состоящее из имени схемы и имени таблицы, разделенных точкой:

schema.table

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

На самом деле, даже более общий синтаксис

database.schema.table

тоже можно использовать, но в настоящее время это только для формального соответствия стандарту SQL. Если вы пишете имя базы данных, оно должно совпадать с именем базы данных, к которой вы подключены.

Итак, чтобы создать таблицу в новой схеме, используйте:

CREATE TABLE myschema.mytable (
 ...
);

Чтобы удалить схему, если она пуста (все объекты в ней удалены), используйте:

DROP SCHEMA myschema;

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

DROP SCHEMA myschema CASCADE;

См. раздел Отслеживание зависимостей для знакомства с общим механизмом отслеживания зависимостей.

Часто необходимо создать схему, принадлежащую кому-то другому (поскольку это один из способов ограничить действия ваших пользователей четко определенными пространствами имен). Синтаксис для этого следующий:

CREATE SCHEMA schema_name AUTHORIZATION user_name;

Вы даже можете опустить имя схемы, и в этом случае имя схемы будет таким же, как имя пользователя. См. раздел Шаблоны использования о том, как это может быть полезно.

Имена схем, начинающиеся с pg_, зарезервированы для системных целей и не могут быть созданы пользователями.

Схема Public

В предыдущих разделах мы создавали таблицы без указания имен схем. По умолчанию такие таблицы (и другие объекты) автоматически помещаются в схему с именем public. Каждая новая база данных содержит такую схему. Таким образом, следующее эквивалентно:

CREATE TABLE products (...);

и:

CREATE TABLE public.products (...);

Путь поиска схемы

Квалифицированные имена загромождают текст, и зачастую лучше не связывать конкретное имя схемы с приложениями. Поэтому таблицы часто называются неквалифицированными именами, которые состоят только из имени таблицы. Система определяет, какая таблица имеется в виду, следуя "пути поиска" (search_path), который представляет собой список схем для поиска нужного объекта. Первая подходящая таблица в пути поиска считается обнаруженной. Если в пути поиска нет совпадений, сообщается об ошибке, даже если совпадающие имена таблиц существуют в других схемах в базе данных.

Возможность создавать объекты с одинаковыми именами в разных схемах усложняет написание запроса, который каждый раз ссылается на одни и те же объекты. Это также открывает возможность для пользователей изменять поведение запросов других пользователей, злонамеренно или случайно. Из-за преобладания неквалифицированных имен в запросах и их использования во внутренних структурах QHB, добавление схемы в search_path эффективно доверяет всем пользователям, имеющим право CREATE для этой схемы. Когда вы запускаете обычный запрос, злонамеренный пользователь, способный создавать объекты в схеме вашего пути поиска, может взять на себя управление и выполнять произвольные функции SQL, как если бы вы их выполняли.

Первая схема, названная в пути поиска, называется текущей схемой. Помимо первой найденной схемы, это также схема, в которой будут создаваться новые таблицы, если команда CREATE TABLE не задает имя схемы.

Чтобы показать текущий путь поиска, используйте следующую команду:

SHOW search_path;

В настройках по умолчанию это возвращает:

search_path
--------------
"$user", public

Первый элемент указывает, что схема с тем же именем, что и текущий пользователь, должна быть найдена. Если такой схемы не существует, запись игнорируется. Второй элемент относится к публичной схеме, которую мы уже видели.

Первая схема в пути поиска является местоположением по умолчанию для создания новых объектов. По этой причине по умолчанию объекты создаются в общедоступной схеме. Когда на объекты ссылаются в любом другом контексте без квалификации схемы (изменение таблицы, изменение данных или команды запроса), путь поиска проходится до тех пор, пока не будет найден соответствующий объект. Следовательно, в конфигурации по умолчанию любой неквалифицированный доступ снова может ссылаться только на общедоступную схему.

Чтобы поместить нашу новую схему в путь, необходимо использовать:

SET search_path TO myschema,public;

(Мы опускаем здесь $user потому что нам это не нужно). И тогда мы можем получить доступ к таблице без уточнения схемы:

DROP TABLE mytable;

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

Мы могли бы также написать:

SET search_path TO myschema;

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

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

Путь поиска работает таким же образом для имен типов данных, имен функций и операторов, как и для имен таблиц. Типы данных и имена функций могут быть определены точно так же, как и имена таблиц. Если вам нужно записать в выражение квалифицированное имя оператора, существует специальное условие: вы должны написать:

OPERATOR(schema.operator)

Это необходимо, чтобы избежать синтаксической неоднозначности. Примером является:

SELECT 3 OPERATOR(pg_catalog.+) 4;

На практике каждый обычно полагается на путь поиска операторов, чтобы не было необходимости писать что-то наподобие этого.

Схемы и права

По умолчанию пользователи не могут получить доступ к объектам в схемах, которыми они не владеют. Для этого владелец схемы должен предоставить право USAGE для этой схемы. Чтобы разрешить пользователям использовать объекты в схеме, могут потребоваться дополнительные права в зависимости от объекта.

Пользователю также может быть разрешено создавать объекты в чужой схеме. Для этого необходимо предоставить право CREATE для схемы. Обратите внимание, что по умолчанию все имеют права CREATE и USAGE в схеме public. Это позволяет всем пользователям, которые могут подключаться к определенной базе данных, создавать объекты в ее схеме public. Некоторые шаблоны использования требуют отзыва этого права:

REVOKE CREATE ON SCHEMA public FROM PUBLIC;

(Первый «public» — это схема, второй «public» означает «каждый пользователь». В первую очередь это идентификатор, во вторую очередь это ключевое слово, отсюда и другая заглавная буква см. раздел Идентификаторы и ключевые слова.

Схема системного каталога

В дополнение к public и созданным пользователем схемам каждая база данных содержит схему pg_catalog, которая содержит системные таблицы и все встроенные типы данных, функции и операторы. pg_catalog всегда является эффективной частью пути поиска. Если он не указан явно в пути, тогда он неявно ищется перед поиском схем пути. Это гарантирует, что встроенные имена всегда будут доступны для поиска. Тем не менее, вы можете явно разместить pg_catalog в конце вашего пути поиска, если вы предпочитаете, чтобы определяемые пользователем имена переопределяли встроенные имена.

Поскольку имена системных таблиц начинаются с pg_, лучше избегать таких имен, чтобы избежать конфликта, если в какой-то будущей версии будет определена системная таблица с именем, совпадающим с вашей таблицей. (При использовании пути поиска по умолчанию неквалифицированная ссылка на имя вашей таблицы будет разрешена как системная таблица). Системные таблицы будут продолжать следовать условию иметь имена, начинающиеся с pg_, так что они не будут конфликтовать с неквалифицированными именами пользовательских таблиц, если пользователи избегают префикса pg_.

Шаблоны использования

Схемы могут быть использованы для организации ваших данных различными способами. Шаблон использования защищенной схемы не позволяет ненадежным пользователям изменять поведение запросов других пользователей. Когда база данных не использует шаблон использования защищенной схемы, пользователи, желающие безопасно запросить объекты этой базы данных, будут предпринимать необходимые действия в начале каждого сеанса. В частности, они будут начинать каждый сеанс, устанавливая search_path в пустую строку или иным образом удаляя схемы, не доступные для записи суперпользователем, из search_path. Существует несколько шаблонов использования, поддерживаемых конфигурацией по умолчанию:

Ограничить обычных пользователей частными схемами. Для этого выполните REVOKE CREATE ON SCHEMA public FROM PUBLIC и создайте схему для каждого пользователя с тем же именем, что и у этого пользователя. Напомним, что путь поиска по умолчанию начинается с $user, который преобразуется в имя пользователя. Поэтому, если у каждого пользователя есть отдельная схема, он получает доступ к своим собственным схемам по умолчанию. После принятия этого шаблона в базе данных, в которую уже вошли ненадежные пользователи, рассмотрите возможность аудита схемы public для объектов с именами, подобными объектам в схеме pg_catalog. Этот шаблон является шаблоном использования защищенной схемы, если только недоверенный пользователь не является владельцем базы данных или не имеет права CREATEROLE, в этом случае шаблон использования защищенной схемы не работает.

Удалите общедоступную схему из пути поиска по умолчанию, изменив qhb.conf или введя команду ALTER ROLE ALL SET search_path = "$user". Каждый пользователь сохраняет возможность создавать объекты в схеме public, но выбирать эти объекты возможно только используя квалифицированные имена. Несмотря на то, что ссылки на квалифицированные таблицы в порядке, вызовы функций в схеме public будут небезопасными или ненадежными. Если вы создаете функции или расширения в схеме public, используйте вместо этого первый шаблон. В противном случае, как и в первом шаблоне, это безопасно, если только ненадежный пользователь не является владельцем базы данных или не имеет права CREATEROLE.

Конфигурация по умолчанию. Все пользователи имеют доступ к схеме public неявно. Это моделирует ситуацию, когда схемы вообще недоступны, обеспечивая плавный переход из мира, не знающего схемы. Однако это не является безопасным шаблоном. Это допустимо только в том случае, если база данных имеет одного пользователя или несколько взаимно доверяющих пользователей.

Для любого шаблона, чтобы установить общие приложения (таблицы, которые будут использоваться всеми, дополнительные функции, предоставленные третьими лицами и т. д.), поместите их в отдельные схемы. Не забудьте предоставить соответствующие права, чтобы позволить другим пользователям доступ к ним. Затем пользователи могут ссылаться на эти дополнительные объекты, квалифицируя имена с помощью имени схемы, или они могут помещать дополнительные схемы в свой путь поиска по своему выбору.

Возможность переноса

В стандарте SQL понятие объектов в одной и той же схеме, принадлежащих разным пользователям, не существует. Более того, некоторые реализации не позволяют создавать схемы, имена которых отличаются от их владельцев. Фактически, понятия схемы и пользователя почти эквивалентны в системе баз данных, которая реализует только базовую поддержку схемы, указанную в стандарте. Поэтому многие пользователи считают, что квалифицированные имена действительно состоят из user_name.table_name. Именно так будет вести себя QHB, если вы создадите схему для каждого пользователя.

Кроме того, в стандарте SQL отсутствует понятие public схемы. Для максимального соответствия стандарту не следует использовать public схему.

Конечно, некоторые системы баз данных SQL могут вообще не реализовывать схемы или предоставлять поддержку пространства имен, разрешая (возможно ограниченный) доступ к базе данных. Если вам нужно работать с этими системами, максимальная переносимость будет достигнута, если вообще не использовать схемы.

Наследование

QHB реализует наследование таблиц, что может быть полезным инструментом для разработчиков баз данных. (SQL: 1999 и более поздние версии определяют функцию наследования типов, которая во многих отношениях отличается от функций, описанных здесь).

Давайте начнем с примера: предположим, что мы пытаемся построить модель данных для городов. В каждом штате много городов, но только одна столица. Мы хотим иметь возможность быстро получить столицу для любого конкретного штата. Это можно сделать, создав две таблицы: одну для столиц штатов и одну для городов, которые не являются столицами. Однако, что происходит, когда мы хотим запросить данные о городе, независимо от того, является ли он столицей или нет? Функция наследования может помочь решить эту проблему. Мы определяем таблицу capitals чтобы она наследовала от cities:

CREATE TABLE cities (
    name            text,
    population      float,
    altitude        int     -- in feet
);

CREATE TABLE capitals (
    state           char(2)
) INHERITS (cities);

В этом случае таблица capitals наследует все столбцы своей родительской таблицы, cities. Столицы штатов также имеют дополнительный столбец, в котором указано их состояние.

В QHB таблица может наследовать от нуля или более других таблиц, а запрос может ссылаться либо на все строки таблицы, либо на все строки таблицы плюс все ее потомки. Последнее поведение по умолчанию. Например, следующий запрос находит названия всех городов, включая столицы штатов, которые расположены на высоте более 500 футов:

SELECT name, altitude
    FROM cities
    WHERE altitude > 500;

Учитывая пример данных из руководства (см. раздел Вступление), этот запрос возвращает:

name      | altitude
----------+----------
Las Vegas |     2174
Mariposa  |     1953
Madison   |      845

С другой стороны, следующий запрос находит все города, которые не являются столицами штатов и расположены на высоте более 500 футов:

SELECT name, altitude
    FROM ONLY cities
    WHERE altitude > 500;

   name    | altitude
-----------+----------
 Las Vegas |     2174
 Mariposa  |     1953

Здесь ключевое слово ONLY указывает, что запрос должен применяться только к cities, а не к таблицам ниже cities в иерархии наследования. Многие из команд, которые мы уже обсуждали - SELECT, UPDATE и DELETE - поддерживают ключевое слово ONLY.

Вы также можете написать имя таблицы с завершающим * чтобы явно указать, что таблицы-потомки включены:

SELECT name, altitude
    FROM cities*
    WHERE altitude > 500;

Запись * не обязательна, так как это поведение всегда по умолчанию.

В некоторых случаях вы можете узнать, из какой таблицы произошла конкретная строка. В каждой таблице есть системный столбец с именем tableoid который может указать tableoid таблицу:

SELECT c.tableoid, c.name, c.altitude
FROM cities c
WHERE c.altitude > 500;

который возвращает:

tableoid |   name    | altitude
----------+-----------+----------
   139793 | Las Vegas |     2174
   139793 | Mariposa  |     1953
   139798 | Madison   |      845

(Если вы попытаетесь воспроизвести этот пример, вы, вероятно, получите разные числовые OID) объединив oid с pg_class вы увидите фактические имена таблиц:

SELECT p.relname, c.name, c.altitude
FROM cities c, pg_class p
WHERE c.altitude > 500 AND c.tableoid = p.oid;

который возвращает:

relname  |   name    | altitude
----------+-----------+----------
cities   | Las Vegas |     2174
cities   | Mariposa  |     1953
capitals | Madison   |      845

Другой способ получить тот же эффект - использовать псевдоним типа regclass, который будет символически печатать OID таблицы:

SELECT c.tableoid::regclass, c.name, c.altitude
FROM cities c
WHERE c.altitude > 500;

Наследование автоматически не распространяет данные из команд INSERT или COPY в другие таблицы в иерархии наследования. В нашем примере следующая INSERT завершится ошибкой:

INSERT INTO cities (name, population, altitude, state)
VALUES ('Albany', NULL, NULL, 'NY');

Мы можем надеяться, что данные будут каким-то образом перенаправлены в таблицу capitals, но этого не происходит: INSERT всегда вставляет именно в указанную таблицу.

Все проверочные ограничения и ограничения not-null родительской таблицы автоматически наследуются ее дочерними элементами, если явно не указано иное с предложениями NO INHERIT. Другие типы ограничений (уникальный, первичный ключ и ограничения внешнего ключа) не наследуются.

Таблица может наследоваться от нескольких родительских таблиц, в этом случае она имеет объединение столбцов, определенных родительскими таблицами. К ним добавляются все столбцы, объявленные в определении дочерней таблицы. Если одно и то же имя столбца появляется в нескольких родительских таблицах или в родительской таблице, и в определении дочернего элемента, то эти столбцы «объединяются» таким образом, чтобы в дочерней таблице был только один такой столбец. Для объединения столбцы должны иметь одинаковые типы данных, иначе возникает ошибка. Наследуемые проверочные ограничения и ограничения not-null объединяются аналогичным образом. Так, например, объединенный столбец будет помечен как not-null, если любое из определений столбца, из которого он получен, будет помечен как not-null. проверочные ограничения объединяются, если они имеют одинаковые имена, и объединение не будет выполнено, если их условия различны.

Наследование таблиц обычно устанавливается при создании дочерней таблицы, используя предложение INHERITS оператора CREATE TABLE. В качестве альтернативы, к таблице, которая уже определена совместимым способом, может быть добавлено новое родительское отношение, используя INHERIT вариант ALTER TABLE. Для этого новая дочерняя таблица должна уже включать столбцы с теми же именами и типами, что и столбцы родительской таблицы. Она также должна включать проверочные ограничения с теми же именами и контрольными выражениями, что и у родительской. Аналогично ссылка наследования может быть удалена из дочернего элемента, используя NO INHERIT вариант ALTER TABLE. Динамическое добавление и удаление таких связей на наследование, может быть полезно, когда отношение наследования используется для разбиения таблиц (см. раздел Партиционирование таблиц).

Одним из удобных способов создания совместимой таблицы, которая впоследствии станет новым дочерним элементом, является использование предложения LIKE в CREATE TABLE. Это создаст новую таблицу с теми же столбцами, что и исходная таблица. Если в исходной таблице есть какие-либо проверочные ограничения, то следует указать параметр INCLUDING CONSTRAINTS для LIKE, поскольку новый потомок должен иметь ограничения, соответствующие родительским чтобы считаться совместимым.

Родительская таблица не может быть удалена, пока остаются ее дочерние элементы. Столбцы или проверочные ограничения дочерних таблиц также не могут быть удалены или изменены, если они унаследованы от каких-либо родительских таблиц. Если вы хотите удалить таблицу и всех ее потомков, одним из простых способов является удаление родительской таблицы с опцией CASCADE(см. раздел Отслеживание зависимостей).

ALTER TABLE будет распространять любые изменения в определениях данных столбцов и проверит ограничения вниз по иерархии наследования. Опять же, удаление столбцов, зависящих от других таблиц, возможно только при использовании опции CASCADE. ALTER TABLE следует тем же правилам объединения и отклонения дубликатов столбцов, которые применяются в команде CREATE TABLE.

Унаследованные запросы выполняют проверки прав доступа только для родительской таблицы. Так, например, предоставление разрешения UPDATE для таблицы cities подразумевает также разрешение на обновление строк в таблице capitals при обращении к ним через cities. Это сохраняет видимость того, что данные находятся также и в родительской таблице. Но таблица capitals не может быть обновлена напрямую без дополнительных прав. Аналогичным образом политики защиты строк родительской таблицы (см. раздел Политики защиты строк) применяются к строкам, приходящим из дочерних таблиц во время унаследованного запроса. Политики дочерней таблицы, если таковые имеются, применяются только в том случае, если она является таблицей с явным именем в запросе и в этом случае любые политики, связанные с его родителем (или родителями), игнорируются.

Внешние таблицы (см. раздел Внешние данные) также могут быть частью иерархий наследования, как родительские или дочерние таблицы так же, как обычные таблицы. Если сторонняя таблица является частью иерархии наследования, то любые операции, не поддерживаемые сторонней таблицей, также не поддерживаются во всей иерархии.

Предостережения при наследовании

Обратите внимание, что не все команды SQL могут работать в иерархиях наследования. Команды, которые используются для запроса данных, изменения данных или изменения схемы (например,SELECT, UPDATE, DELETE, большинство вариантов ALTER TABLE, но не INSERT или ALTER TABLE ... RENAME), обычно по умолчанию включают дочерние таблицы и поддерживают ONLY обозначение, чтобы исключить их. Команды, которые выполняют обслуживание и настройку базы данных (например, REINDEX, VACUUM), обычно работают только с отдельными физическими таблицами и не поддерживают рекурсию по иерархиям наследования. Соответствующее поведение каждой отдельной команды задокументировано на ее справочной странице ( глава Команды SQL).

Серьезным ограничением функции наследования является то, что индексы (включая ограничения уникальности) и ограничения внешнего ключа применяются только к отдельным таблицам, а не к их дочерним элементам. Это верно как для ссылающихся, так и для указываемых сторон ограничения внешнего ключа. Таким образом, в условиях приведенного выше примера:

В случае объявления cities.name как UNIQUE или PRIMARY KEY, это не помешает таблице capitals иметь строки с именами, дублирующими строки в cities. И эти повторяющиеся строки по умолчанию будут отображаться в запросах из cities. На самом деле, по умолчанию таблица capitals вообще не имеет уникального ограничения и поэтому может содержать несколько строк с одинаковыми именами. Вы можете добавить уникальное ограничение для capitals, но это не предотвратит дублирование в связке с таблицей cities.

Точно так же, если бы мы указали что cities.name ссылается на какую-то другую таблицу, это ограничение не будет автоматически распространяться на capitals. В этом случае вы можете обойти это, вручную добавив такое же ограничение REFERENCES в capitals.

Указание на то, что столбец из другой таблицы ссылается на cities(name) позволит другой таблице содержать названия городов, но не названия столиц. Для этого случая нет хорошего пути решения.

Некоторые функции, не реализованные для иерархий наследования, реализованы для декларативного партиционирования. При принятии решения о том, полезно ли разбиение на партиции с помощью наследования для вашего приложения, требуется осторожность.

Партиционирование таблиц

QHB поддерживает базовое партиционирование таблиц. В этом разделе описывается, почему и как реализовать партиционирование в вашей базе данных.

Обзор

Партиционирование позволяет логически разделить одну большую таблицу на более мелкие физические части. Партиционирование может дать несколько преимуществ:

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

Когда запросы или обновления обращаются к большому проценту одной партиции, производительность можно улучшить, воспользовавшись преимуществом последовательного сканирования этой партиции вместо использования индекса и чтения с произвольным доступом, разбросанного по всей таблице.

Массовая загрузка и удаление могут быть выполнены путем добавления или удаления партиций, если это требование запланировано в проекте партиционирования. Выполнение команды ALTER TABLE DETACH PARTITION или удаление отдельной партиции с помощью DROP TABLE намного быстрее, чем массовая операция. Эти команды также избавлены от накладных расходов вакуума, вызванных массовым удалением.

Редко используемые данные могут быть перенесены на более дешевые и медленные носители данных.

Преимущества партиционирования, как правило, имеют смысл только тогда, когда целая таблица будет очень большой. Точный размер, при достижении которого таблица получит выгоду от партиционирования, зависит от приложения, хотя эмпирическое правило заключается в том, что размер таблицы должен превысить физическую память сервера базы данных.

QHB предлагает встроенную поддержку для следующих форм партиционирования:

Форма партиционирования | Описание
По диапазону | Таблица разбивается на «диапазоны», определяемые ключевым столбцом или набором столбцов, без перекрытия диапазонов значений, назначенных различным партициям. Например, можно разделить по диапазонам дат или диапазонам идентификаторов для определенных бизнес-объектов.
По списку | Таблица разбивается путем явного перечисления значений ключей, которые появляются в каждой партиции.
По хешу | Таблица разбивается путем указания модуля и остатка для каждой партиции. Каждая партиция будет содержать строки, для которых хеш-значение ключа разбиения, разделенное на указанный модуль, приведет к указанному остатку.

Если вашему приложению нужно использовать другие формы партиционирования, не перечисленные выше, вместо них можно использовать альтернативные методы, такие как наследование и представления UNION ALL. Такие методы предлагают гибкость, но не имеют некоторых преимуществ в производительности по сравнению с партиционированием.

Декларативное партиционирование

QHB предлагает способ указать, как поделить таблицу на части, называемые партициями. Такая таблица называется партиционированной таблицей. Определение партиции состоит из метода партиционирования и списка столбцов или выражений, которые будут использоваться в качестве ключа разбиения.

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

В настоящее время поддерживаются следующие методы партиционирования: по диапазонам (range), по списку (list) и по хешу (hash).

Сами партиции могут быть определены как партиционированные таблицы, используя то, что называется субпартиционированием. Партиции могут иметь свои собственные индексы, ограничения и значения по умолчанию, отличные от других партиций. Смотрите CREATE TABLE для более подробной информации о создании партиционированных таблиц и субпартиций.

Невозможно превратить обычную таблицу в партиционированную таблицу или наоборот. Тем не менее, можно добавить обычную или партиционированную таблицу, содержащую данные, как партицию партиционированной таблицы, или удалить партицию из партиционированной таблицы, превратив его в отдельную таблицу см. ALTER TABLE, чтобы узнать больше о командах ATTACH PARTITION и DETACH PARTITION.

Отдельные партиции связаны с партиционированной таблицей с помощью скрытого наследования- однако, невозможно использовать общие свойства наследования (обсуждаемые ниже) с декларативно партиционированными таблицами или их партициями. Например, партиция не может иметь никаких родителей, кроме партиции таблицы, частью которой она является, и обычная таблица не может наследовать от партиционированной таблицы. Это означает, что партиционированные таблицы и их партиции не участвуют в наследовании обычных таблиц. Поскольку иерархия партиций, состоящая из партиционированной таблицы и ее партиций, по-прежнему является иерархией наследования, все обычные правила наследования применяются, как описано в разделе Наследование, с некоторыми исключениями, в частности:

Ограничения CHECK и NOT NULL для партиционированной таблицы всегда наследуются всеми ее партициями. Проверочные ограничения, помеченные как NO INHERIT, не могут быть созданы для партиционированных таблиц.

Использование ONLY для добавления или удаления ограничения в партиционированной таблице поддерживается только до тех пор, пока у нее нет партиций. Как только партиции возникают, использование ONLY приведет к ошибке, поскольку добавление или удаление ограничений только для партиционированной таблицы, когда партиции существуют, не поддерживается. Вместо этого могут быть добавлены ограничения на сами партиции и (если они отсутствуют в родительской таблице) удалить их.

Поскольку партиционированная таблица не имеет никаких данных непосредственно, попытки использовать TRUNCATE ONLY в партиционированной таблице всегда будут возвращать ошибку.

Партиции не могут иметь столбцы, отсутствующие в родительском элементе. Невозможно указать столбцы при создании партиций с помощью CREATE TABLE, а также невозможно добавить столбцы к партициям после использования ALTER TABLE. Таблицы могут быть добавлены как партиции с помощью ALTER TABLE ... ATTACH PARTITION только если их столбцы точно соответствуют родительскому.

Вы не можете удалить ограничение NOT NULL для столбца партиции, если ограничение присутствует в родительской таблице.

Партиции также могут быть внешними таблицами, хотя у них есть некоторые ограничения, которых нет у обычных таблиц см. CREATE FOREIGN TABLE для получения дополнительной информации.

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

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

Предположим, мы создаем базу данных для большой компании по производству мороженого. Компания измеряет пиковые температуры каждый день, а также продажи мороженого в каждом регионе. Создадим таблицу:

CREATE TABLE measurement (
    city_id         int not null,
    logdate         date not null,
    peaktemp        int,
    unitsales       int
);

Мы знаем, что большинство запросов получат доступ только к данным за последнюю неделю, месяц или квартал, поскольку основное использование этой таблицы будет заключаться в подготовке онлайн-отчетов для руководства. Чтобы уменьшить объем старых данных, которые необходимо хранить, мы решили сохранить данные за последние 3 года. В начале каждого месяца мы будем удалять данные самого старого месяца. В этой ситуации мы можем использовать партиционирование, чтобы помочь нам удовлетворить все наши различные требования к таблице измерений (measurement).

Чтобы использовать декларативное партиционирование в этом случае, выполните следующие действия:

Создайте таблицу measurement как партиционированную таблицу, указав условие PARTITION BY, которое включает метод партиционирования (в данном случае RANGE) и список столбцов, которые следует использовать в качестве ключа разбиения.
При желании вы можете использовать несколько столбцов в ключе разбиения для разделения диапазона. Конечно, это часто приводит к большему количеству партиций, каждый из которых в отдельности меньше. С другой стороны, использование меньшего количества столбцов может привести к более грубым критериям партиционирования с меньшим числом партиций. Запрос, обращающийся к партиционированной таблице, должен будет сканировать меньшее количество партиций, если условия включают некоторые или все эти столбцы. Например, рассмотрите диапазон таблицы, разделенный с использованием столбцов lastname и firstname (в этом порядке) в качестве ключа разбиения.

CREATE TABLE measurement (
    city_id         int not null,
    logdate         date not null,
    peaktemp        int,
    unitsales       int
) PARTITION BY RANGE (logdate);

Создать партиции. Определение каждой партиции должно указывать границы, которые соответствуют методу и ключу разбиения родителя. Обратите внимание, что указание границ таким образом, что значения новой партиции будут совпадать со значениями в одном или нескольких существующих партициях, приведет к ошибке. Вставка данных в родительскую таблицу, которая не сопоставлена ни с одной из существующих партиций, приведет к ошибке - соответствующие партиции должны быть добавлены вручную. Созданные таким образом партиции во всех смыслах являются обычными таблицами QHB (или, возможно, внешними таблицами). Можно указать табличное пространство и параметры хранения для каждой партиции отдельно. Нет необходимости создавать ограничения таблицы, описывающие граничные условия для партиций. Напротив, ограничения партиции генерируются неявно из спецификации границ партиции, когда есть необходимость обратиться к ним.

CREATE TABLE measurement_y2006m02 PARTITION OF measurement
    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01');

CREATE TABLE measurement_y2006m03 PARTITION OF measurement
    FOR VALUES FROM ('2006-03-01') TO ('2006-04-01');

...
CREATE TABLE measurement_y2007m11 PARTITION OF measurement
    FOR VALUES FROM ('2007-11-01') TO ('2007-12-01');

CREATE TABLE measurement_y2007m12 PARTITION OF measurement
    FOR VALUES FROM ('2007-12-01') TO ('2008-01-01')
    TABLESPACE fasttablespace;

CREATE TABLE measurement_y2008m01 PARTITION OF measurement
    FOR VALUES FROM ('2008-01-01') TO ('2008-02-01')
    WITH (parallel_workers = 4)
    TABLESPACE fasttablespace;

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

CREATE TABLE measurement_y2006m02 PARTITION OF measurement
    FOR VALUES FROM ('2006-02-01') TO ('2006-03-01')
    PARTITION BY RANGE (peaktemp);

После создания партиции measurement_y2006m02 любые данные, вставленные в партицию, которая сопоставлена с measurement_y2006m02 (или данные, которые непосредственно вставлены в measurement_y2006m02, при условии, что оно удовлетворяет ограничению на партиции), будут дополнительно перенаправлены в одну из партиций на основе столбца peaktemp. Указанный ключ разбиения может перекрываться с ключом разбиения родителя, хотя следует соблюдать осторожность при указании границ партиции, чтобы набор данных, которые он принимает, составлял подмножество того, что позволяют собственные границы партиции - система не пытается проверить, так ли это на самом деле.

Создайте индекс для ключевого столбца (столбцов), а также любые другие индексы, которые вам могут понадобиться, для партиционированной таблицы. (Ключевой индекс не является строго необходимым, но в большинстве сценариев он полезен). Это автоматически создает один индекс для каждой партиции, и любые партиции, которые вы создаете или присоединяете позже, также будут содержать индекс.

CREATE INDEX ON measurement (logdate);

Убедитесь, что параметр конфигурации enable_partition_pruning не выключен в qhb.conf. Если это так, запросы не будут оптимизированы должным образом.

В приведенном выше примере мы будем создавать новую партицию каждый месяц, поэтому было бы разумно написать скрипт, который автоматически генерирует необходимый DDL.

Обслуживание партиций

Обычно набор партиций, созданный при первоначальном определении таблицы, не должен оставаться статическим. Обычно требуется удалить старые партиции и периодически добавлять новые партиции для новых данных. Одним из наиболее важных преимуществ партиционирования является именно то, что оно позволяет практически мгновенно выполнить эту болезненную задачу, манипулируя структурой партиции, а не физически перемещая большие объемы данных.

Самый простой вариант удаления старых данных - удалить ненужную партицию:

DROP TABLE measurement_y2006m02;

Так можно очень быстро удалить миллионы записей, потому что не нужно индивидуально удалять каждую запись. Однако обратите внимание, что приведенная выше команда требует установки блокировки ACCESS EXCLUSIVE на родительскую таблицу.

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

ALTER TABLE measurement DETACH PARTITION measurement_y2006m02;

Теперь можно выполнять дальнейшие операции с данными до их удаления. Например, это позволит провести резервное копирование данных с помощью COPY, qhb_dump или аналогичных инструментов также отдельная таблица может быть полезной для агрегации данных или выполнения других манипуляций с данными или запуска отчетов.

Точно так же мы можем добавить новую партицию для обработки новых данных. Мы можем создать пустую партицию в партиционированной таблице так же, как исходные партиции были созданы выше:

CREATE TABLE measurement_y2008m02 PARTITION OF measurement
    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01')
    TABLESPACE fasttablespace;

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

CREATE TABLE measurement_y2008m02
  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS)
  TABLESPACE fasttablespace;

ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
   CHECK ( logdate >= DATE '2008-02-01' AND logdate < DATE '2008-03-01' );

\copy measurement_y2008m02 from 'measurement_y2008m02'
-- possibly some other data preparation work

ALTER TABLE measurement ATTACH PARTITION measurement_y2008m02
    FOR VALUES FROM ('2008-02-01') TO ('2008-03-01' );

Перед запуском команды ATTACH PARTITION рекомендуется создать контрольное ограничение для присоединяемой таблицы, соответствующее требуемому ограничению партиции. Таким образом, система сможет пропустить сканирование, чтобы проверить неявное ограничение партиции. Без контрольного ограничения таблица будет сканироваться для проверки ограничения партиции, при этом удерживая блокировку ACCESS EXCLUSIVE для этой партиции и блокировку SHARE UPDATE EXCLUSIVE в родительской таблице. Может потребоваться сбросить избыточное контрольное ограничение после завершения ATTACH PARTITION.

Как объяснено выше, можно создавать индексы для партиционированных таблиц, и они автоматически применяются ко всей иерархии. Это очень удобно, так как не только существующие партиции будут проиндексированы, но и любые партиции, которые будут созданы в будущем. Одно ограничение заключается в том, что при создании такого партиционированного индекса невозможно использовать CONCURRENTLY. Чтобы преодолеть длительное время блокировки, можно использовать CREATE INDEX ON ONLY для партиционированной таблицы - такой индекс помечается как недействительный, и партиции не получают индекс автоматически. Индексы на партициях можно создавать отдельно с помощью CONCURRENTLY, а затем прикреплять к индексу на родительском CONCURRENTLY с помощью ALTER INDEX .. ATTACH PARTITION. Как только индексы для всех партиций присоединены к родительскому индексу, родительский индекс помечается как действительный автоматически. Пример:

CREATE INDEX measurement_usls_idx ON ONLY measurement (unitsales);

CREATE INDEX measurement_usls_200602_idx
    ON measurement_y2006m02 (unitsales);
ALTER INDEX measurement_usls_idx
    ATTACH PARTITION measurement_usls_200602_idx;
...

Этот метод также может использоваться с ограничениями UNIQUE и PRIMARY KEY индексы создаются неявно при создании ограничения. Пример:

ALTER TABLE ONLY measurement ADD UNIQUE (city_id, logdate);

ALTER TABLE measurement_y2006m02 ADD UNIQUE (city_id, logdate);
ALTER INDEX measurement_city_id_logdate_key
    ATTACH PARTITION measurement_y2006m02_city_id_logdate_key;
...

Ограничения декларативного партиционирования

К партиционированным таблицам применяются следующие ограничения:

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

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

Триггеры BEFORE ROW, если необходимо, должны быть определены на отдельных партициях, а не на партиционированной таблице.

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

Партиционирование с использованием наследования

Хотя встроенное декларативное партиционирование подходит для большинства общих случаев использования, в некоторых случаях может оказаться полезным более гибкий подход, партиционирование может быть реализовано с использованием наследования таблиц, что позволяет использовать несколько функций, не поддерживаемых декларативным партиционированием, таких как:

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

Наследование таблиц допускает множественное наследование.

Декларативное партиционирование поддерживает только партиционирование по диапазонам, спискам и хешам, тогда как наследование таблиц позволяет разделять данные способом, выбранным пользователем. (Тем не менее, обратите внимание, что, если ограничение исключения не может эффективно сократить дочерние таблицы, производительность запросов может быть низкой).

Некоторые операции требуют более сильной блокировки при использовании декларативного партиционирования, чем при использовании наследования таблиц. Например, добавление или удаление партиции в или из партиционированной таблицы требует установки блокировки ACCESS EXCLUSIVE на родительскую таблицу, тогда как в случае обычного наследования достаточно блокировки SHARE UPDATE EXCLUSIVE.

Пример партиционирования с использованием наследования

Мы используем ту же таблицу измерений, которую использовали выше. Чтобы реализовать партиционирование с использованием наследования, выполните следующие действия:

Создайте «главную» таблицу, от которой будут наследоваться все «дочерние» таблицы. Эта таблица не будет содержать данных. Не определяйте никакие проверочные ограничения для этой таблицы, если только вы не собираетесь применять их одинаково ко всем дочерним таблицам. Также нет смысла определять какие-либо индексы или ограничения уникальности для нее. В нашем примере главная таблица — это таблица measurement, как она была определена изначально.

Создайте несколько «дочерних» таблиц, каждая из которых наследуется от основной таблицы. Обычно эти таблицы не добавляют столбцы в набор, унаследованный от главной. Как и при декларативном разбиении, эти таблицы во всех отношениях являются обычными таблицами QHB (или внешними таблицами).

CREATE TABLE measurement_y2006m02 () INHERITS (measurement);
CREATE TABLE measurement_y2006m03 () INHERITS (measurement);
...
CREATE TABLE measurement_y2007m11 () INHERITS (measurement);
CREATE TABLE measurement_y2007m12 () INHERITS (measurement);
CREATE TABLE measurement_y2008m01 () INHERITS (measurement);

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

CHECK ( x = 1 )
CHECK ( county IN ( 'Oxfordshire', 'Buckinghamshire', 'Warwickshire' ))
CHECK ( outletID >= 100 AND outletID < 200 )

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

CHECK ( outletID BETWEEN 100 AND 200 )
CHECK ( outletID BETWEEN 200 AND 300 )

Это неправильно, так как неясно, к какой дочерней таблице относится значение ключа 200. Вместо этого было бы лучше создать дочерние таблицы следующим образом:

CREATE TABLE measurement_y2006m02 (
    CHECK ( logdate >= DATE '2006-02-01' AND logdate < DATE '2006-03-01' )
) INHERITS (measurement);

CREATE TABLE measurement_y2006m03 (
    CHECK ( logdate >= DATE '2006-03-01' AND logdate < DATE '2006-04-01' )
) INHERITS (measurement);

...
CREATE TABLE measurement_y2007m11 (
    CHECK ( logdate >= DATE '2007-11-01' AND logdate < DATE '2007-12-01' )
) INHERITS (measurement);

CREATE TABLE measurement_y2007m12 (
    CHECK ( logdate >= DATE '2007-12-01' AND logdate < DATE '2008-01-01' )
) INHERITS (measurement);

CREATE TABLE measurement_y2008m01 (
    CHECK ( logdate >= DATE '2008-01-01' AND logdate < DATE '2008-02-01' )
) INHERITS (measurement);

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

CREATE INDEX measurement_y2006m02_logdate ON measurement_y2006m02 (logdate);
CREATE INDEX measurement_y2006m03_logdate ON measurement_y2006m03 (logdate);
CREATE INDEX measurement_y2007m11_logdate ON measurement_y2007m11 (logdate);
CREATE INDEX measurement_y2007m12_logdate ON measurement_y2007m12 (logdate);
CREATE INDEX measurement_y2008m01_logdate ON measurement_y2008m01 (logdate);

Для того, чтобы приложение могло сказать INSERT INTO measurement... и чтобы данные были перенаправлены в соответствующую дочернюю таблицу можно прикрепить подходящую триггерную функцию к главной таблице. Если данные будут добавлены только к последнему дочернему элементу, можно использовать очень простую триггерную функцию:

CREATE OR REPLACE FUNCTION measurement_insert_trigger()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO measurement_y2008m01 VALUES (NEW.*);
    RETURN NULL;
END;
$$
LANGUAGE plpgsql;

После создания функции создаем триггер, который вызывает эту функцию:

CREATE TRIGGER insert_measurement_trigger
    BEFORE INSERT ON measurement
    FOR EACH ROW EXECUTE FUNCTION measurement_insert_trigger();

В данном случае необходимо переопределять "триггерную" функцию каждый месяц, чтобы она всегда указывала на текущую дочернюю таблицу. Однако определение самого триггера обновлять не нужно.

Для того чтобы при вставке данных, сервер автоматически находил дочернюю таблицу, в которую должна быть добавлена строка, можно сформировать более сложную триггерную функцию, например:

CREATE OR REPLACE FUNCTION measurement_insert_trigger()
RETURNS TRIGGER AS $$
BEGIN
    IF ( NEW.logdate >= DATE '2006-02-01' AND
         NEW.logdate < DATE '2006-03-01' ) THEN
        INSERT INTO measurement_y2006m02 VALUES (NEW.*);
    ELSIF ( NEW.logdate >= DATE '2006-03-01' AND
            NEW.logdate < DATE '2006-04-01' ) THEN
        INSERT INTO measurement_y2006m03 VALUES (NEW.*);
    ...
    ELSIF ( NEW.logdate >= DATE '2008-01-01' AND
            NEW.logdate < DATE '2008-02-01' ) THEN
        INSERT INTO measurement_y2008m01 VALUES (NEW.*);
    ELSE
        RAISE EXCEPTION 'Date out of range.  Fix the measurement_insert_trigger() function!';
    END IF;
    RETURN NULL;
END;
$$
LANGUAGE plpgsql;

Определение триггера такое же, как и раньше. Обратите внимание, что каждое условие IF должен точно соответствовать ограничению CHECK для его дочерней таблицы.

Хотя эта функция более сложная, чем в случае одного месяца, ее не нужно обновлять так часто, поскольку ветки исполнения могут быть добавлены заранее.

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

CREATE RULE measurement_insert_y2006m02 AS
ON INSERT TO measurement WHERE
    ( logdate >= DATE '2006-02-01' AND logdate < DATE '2006-03-01' )
DO INSTEAD
    INSERT INTO measurement_y2006m02 VALUES (NEW.*);
...
CREATE RULE measurement_insert_y2008m01 AS
ON INSERT TO measurement WHERE
    ( logdate >= DATE '2008-01-01' AND logdate < DATE '2008-02-01' )
DO INSTEAD
    INSERT INTO measurement_y2008m01 VALUES (NEW.*);

У правила значительно больше накладных расходов, чем у триггера, но накладные расходы оплачиваются один раз за запрос, а не один раз за строку, поэтому этот метод может быть полезен для ситуаций массовой вставки. В большинстве случаев, однако, метод триггера будет предлагать лучшую производительность. Помните, что COPY игнорирует правила. Если вы хотите использовать COPY для вставки данных, вам нужно будет копировать в правильную дочернюю таблицу, а не прямо в мастер. COPY запускает триггеры, поэтому вы можете использовать их как обычно, если вы используете триггерный подход.

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

Убедитесь, что параметр конфигурации constraint_exclusion не выключен в QHB.conf в противном случае к дочерним таблицам можно получить доступ без необходимости.

Как мы видим, сложная иерархия таблиц может потребовать значительного количества DDL. В приведенном выше примере необходимо создавать новую дочернюю таблицу каждый месяц, поэтому было бы разумно написать скрипт, который автоматически генерирует необходимый DDL.

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

Чтобы быстро удалить старые данные, просто удалите дочернюю таблицу, которая больше не нужна:

DROP TABLE measurement_y2006m02;

Чтобы удалить дочернюю таблицу из таблицы иерархии наследования, но сохранить доступ к ней как к отдельной таблице:

ALTER TABLE measurement_y2006m02 NO INHERIT measurement;

Чтобы добавить новую дочернюю таблицу для обработки новых данных, создайте пустую дочернюю таблицу так же, как исходные дочерние таблицы были созданы выше:

CREATE TABLE measurement_y2008m02 (
    CHECK ( logdate >= DATE '2008-02-01' AND logdate < DATE '2008-03-01' )
) INHERITS (measurement);

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

CREATE TABLE measurement_y2008m02
  (LIKE measurement INCLUDING DEFAULTS INCLUDING CONSTRAINTS);
ALTER TABLE measurement_y2008m02 ADD CONSTRAINT y2008m02
   CHECK ( logdate >= DATE '2008-02-01' AND logdate < DATE '2008-03-01' );
\copy measurement_y2008m02 from 'measurement_y2008m02'
-- possibly some other data preparation work
ALTER TABLE measurement_y2008m02 INHERIT measurement;

Предостережения

Следующие предостережения применяются к партиционированию, реализованному с использованием наследования:

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

Индексы и ограничения внешнего ключа применяются к отдельным таблицам, а не к их дочерним наследованиям, поэтому они должны учитывать некоторые предостережения.

Схемы, показанные здесь, предполагают, что значения ключевых столбцов строки никогда не меняются или, по крайней мере, не меняются настолько, чтобы требовать его перемещения в другую партицию. UPDATE которое пытается сделать это, закончится с ошибкой из-за ограничений CHECK. Если вам нужно обработать такие случаи, вы можете поместить подходящие триггеры обновления в дочерние таблицы, но это значительно усложняет управление структурой.

Если вы используете команды VACUUM или ANALYZE вручную, не забывайте, что вам нужно запускать их для каждой дочерней таблицы отдельно. Например, следующая команда будет обрабатывать только основную таблицу:

ANALYZE measurement;

Операторы INSERT с предложениями ON CONFLICT вряд ли будут работать должным образом, поскольку действие ON CONFLICT выполняется только в случае уникальных нарушений в указанном целевом отношении, а не в его дочерних отношениях.

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

Сокращение партиций

Сокращение партиций — это метод оптимизации запросов, который повышает производительность для декларативно партиционированных таблиц. В качестве примера:

SET enable_partition_pruning = on;                 -- the default
SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';

Без сокращения партиций вышеупомянутый запрос будет сканировать каждую из партиций таблицы measurement. При включенном сокращении партиций планировщик изучит определение каждой партиции и докажет, что ее не нужно сканировать, поскольку в ней не может быть строк, соответствующих условию WHERE из запроса. Когда планировщик может доказать это, он исключает (обрезает) партицию из плана запроса.

Используя команду EXPLAIN и параметр конфигурации enable_partition_pruning, можно показать разницу между планом, для которого партиции были сокращены, и планом, для которого они не сокращались. Типичный неоптимизированный план для этого типа установки таблицы:

SET enable_partition_pruning = off;
EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';
                                    QUERY PLAN
-----------------------------------------------------------------------------------
 Aggregate  (cost=188.76..188.77 rows=1 width=8)
   ->  Append  (cost=0.00..181.05 rows=3085 width=0)
         ->  Seq Scan on measurement_y2006m02  (cost=0.00..33.12 rows=617 width=0)
               Filter: (logdate >= '2008-01-01'::date)
         ->  Seq Scan on measurement_y2006m03  (cost=0.00..33.12 rows=617 width=0)
               Filter: (logdate >= '2008-01-01'::date)
...
         ->  Seq Scan on measurement_y2007m11  (cost=0.00..33.12 rows=617 width=0)
               Filter: (logdate >= '2008-01-01'::date)
         ->  Seq Scan on measurement_y2007m12  (cost=0.00..33.12 rows=617 width=0)
               Filter: (logdate >= '2008-01-01'::date)
         ->  Seq Scan on measurement_y2008m01  (cost=0.00..33.12 rows=617 width=0)
               Filter: (logdate >= '2008-01-01'::date)

Некоторые или все партиции могут использовать сканирование индекса вместо последовательного сканирования полной таблицы, но суть в том, что вообще нет необходимости сканировать более старые партиции, чтобы ответить на этот запрос. Когда мы включаем сокращение партиций, мы получаем значительно более дешевый план, который даст тот же ответ:

SET enable_partition_pruning = on;
EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';
                                    QUERY PLAN
-----------------------------------------------------------------------------------
 Aggregate  (cost=37.75..37.76 rows=1 width=8)
   ->  Seq Scan on measurement_y2008m01  (cost=0.00..33.12 rows=617 width=0)
         Filter: (logdate >= '2008-01-01'::date)

Обратите внимание, что сокращение партиций обусловлено только ограничениями, неявно определенными ключами разбиения, а не наличием индексов. Поэтому нет необходимости определять индексы по ключевым столбцам. Необходимость создания индекса для данной партиции зависит от того, ожидаете ли вы, что запросы, сканирующие эту партицию, обычно сканируют большую часть партиции или только небольшую часть. Индекс будет полезен в последнем случае, но не в первом.

Сокращение партиций может быть выполнено не только во время планирования данного запроса, но и во время его выполнения. Это полезно, поскольку позволяет сократить количество партиций, когда предложения содержат выражения, значения которых неизвестны во время планирования запроса, например, параметры, определенные в операторе PREPARE, с использованием значения, полученного из подзапроса, или используя параметризованное значение на внутренней стороне вложенного соединения цикла. Сокращение партиций во время выполнения может быть выполнено в любой из следующих моментов времени:

Во время инициализации плана запроса. Здесь можно выполнить сокращение партиций для значений параметров, которые известны на этапе инициализации выполнения. Партиции, которые были сокращены на этом этапе, не будут отображаться в запросе EXPLAIN или EXPLAIN ANALYZE. Можно определить количество партиций, которые были удалены на этом этапе, наблюдая за свойством «Subplans Removed» в выводе EXPLAIN.

Во время фактического выполнения плана запроса. Здесь также может быть выполнено удаление партиций, чтобы удалить партиции, используя значения, которые известны только во время фактического выполнения запроса. Это включает в себя значения из подзапросов и значения из параметров времени выполнения, например, из параметризованных объединений вложенных циклов. Поскольку значение этих параметров может изменяться много раз во время выполнения запроса, сокращение партиции выполняется всякий раз, когда один из параметров выполнения, используемых для сокращения партиции, изменяется. Определение того, были ли партиции сокращены на этом этапе, требует тщательной проверки loops свойства в выходных данных EXPLAIN ANALYZE. Подпланы, соответствующие различным партициям, могут иметь разные значения для него в зависимости от того, сколько раз каждый из них был сокращен во время выполнения. Некоторые могут быть показаны, как (never executed) если они были обрезаны каждый раз.

Сокращение партиций может быть выключено с помощью параметра enable_partition_pruning.

В настоящее время обрезка партиций во время выполнения выполняется только для типов узлов Append и MergeAppend. Он еще не реализован для типа узлов ModifyTable, но, вероятно, будет изменен в будущем выпуске QHB.

Партиционирование и исключение по ограничению

Исключение по ограничению — это метод оптимизации запросов, аналогичный сокращению партиций. Хотя он в основном используется для разбиения, реализованного с использованием унаследованного метода наследования, он может использоваться для других целей, в том числе с декларативным партиционированием.

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

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

Значение по умолчанию (и рекомендуемое) для параметра constraint_exclusion - не включен и не выключен, а является промежуточным параметром, называемым partition, который вызывает применение метода только к запросам, которые, вероятно, будут работать с партиционированными таблицами наследования Параметр on заставляет планировщик проверять проверочные ограничения во всех запросах, в том числе простых, что вряд ли принесет пользу.

Следующие предостережения относятся к исключению ограничений:

Исключение ограничений применяется только при планировании запросов, в отличие от сокращения партиций, которое также может применяться при выполнении запросов.

Исключение ограничений работает только тогда, когда предложение запроса WHERE содержит константы (или внешние параметры). Например, сравнение с неизменяемой функцией, например, CURRENT_TIMESTAMP не может быть оптимизировано, поскольку планировщик не может знать, в какую дочернюю таблицу может попасть значение функции во время выполнения.

Сохраняйте простые ограничения на партиции, иначе планировщик не сможет доказать, что дочерние таблицы могут не посещаться. Используйте простые условия равенства для разбиения списка или простые тесты диапазона для разбиения диапазона, как показано в предыдущих примерах. Хорошее эмпирическое правило заключается в том, что ограничения партиционирования должны содержать только сравнения столбца (-ов) партиционирования с константами с помощью операторов, индексируемых по B-дереву, поскольку в ключе разбиения допускаются только столбцы, индексируемые по B-дереву.

Все ограничения для всех дочерних элементов родительской таблицы проверяются во время исключения ограничений, поэтому большое количество дочерних элементов может значительно увеличить время планирования запросов. Таким образом, партиционирование с использованием наследования будет хорошо работать, возможно, с сотней дочерних таблиц - не пытайтесь использовать много тысяч дочерних таблиц.

Оптимальные методы декларативного партиционирования

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

Одним из наиболее важных проектных решений является выбор столбца (или столбцов), по которым вы партиционируете свои данные. Часто лучшим выбором будет партиционирование по столбцу или набору столбцов, которые чаще всего встречаются в WHERE предложениях запросов, выполняемых в партиционированной таблице. Элементы предложения WHERE, которые соответствуют и совместимы с ключом разбиения, могут использоваться для удаления ненужных партиций. Тем не менее, вы можете быть вынуждены принимать другие решения в соответствии с требованиями первичному ключу или уникальному ограничению. Удаление нежелательных данных также является фактором, который следует учитывать при планировании стратегии партиционирования. Партиция может быть отсоединена довольно быстро, поэтому может быть полезно разработать стратегию партиционирования таким образом, чтобы все данные, которые должны быть удалены одновременно, находились в одной партиции.

Выбор целевого числа партиций, на которые следует разделить таблицу, также является критически важным решением. Отсутствие достаточного количества партиций может означать, что индексы остаются слишком большими, а локальность данных остается плохой, что может привести к низким коэффициентам попадания в кэш. Однако разделение таблицы на слишком много партиций также может вызвать проблемы. Слишком большое количество партиций может привести к более длительному времени планирования запросов и более высокому потреблению памяти при планировании и выполнении запросов. При выборе способа партиционирования таблицы также важно учитывать, какие изменения могут произойти в будущем. Например, если вы выбрали одну партицию для каждого клиента и в настоящее время у вас небольшое количество крупных клиентов, подумайте о последствиях, если через несколько лет вы окажетесь с большим количеством мелких клиентов. В таком случае, может быть лучше выбрать разбиение по хешу и выбрать разумное количество партиций вместо того, чтобы пытаться разделить их, по списку и надеяться, что количество клиентов не превысит порог, по которому целесообразно разделить данные.

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

Также важно учитывать издержки партиционирования при планировании и выполнении запросов. Планировщик запросов обычно способен достаточно хорошо обрабатывать иерархии партиций с несколькими тысячами партиций, при условии, что типичные запросы позволяют планировщику запросов удалять все партиции, кроме небольшого числа. Время планирования увеличивается, а потребление памяти возрастает, когда остается много партиций после того, как планировщик выполнил удаление партиций. Это особенно верно для команд UPDATE и DELETE. Еще одна причина для беспокойства по поводу наличия большого количества партиций заключается в том, что потребление памяти сервером может значительно возрасти в течение определенного периода времени, особенно если многие сеансы касаются большого количества партиций. Это связано с тем, что каждая партиция требует загрузки своих метаданных в локальную память каждого сеанса, который его касается.

С рабочими нагрузками типа OLAP имеет смысл использовать большее количество партиций, чем с рабочей нагрузкой типа OLTP. Как правило, в хранилищах данных планирование запросов занимает меньше времени, так как большая часть времени обработки тратится на выполнение запросов. При любом из этих двух типов рабочих нагрузок важно принимать правильные решения как можно раньше, поскольку перераспределение больших объемов данных может быть болезненно медленным. Моделирование предполагаемой рабочей нагрузки часто полезно для оптимизации стратегии партиционирования. Никогда не думайте, что больше партиций лучше, чем меньше партиций, и наоборот.

Внешние данные

QHB реализует части спецификации SQL/MED, позволяя вам получать доступ к данным, находящимся вне QHB, используя обычные SQL-запросы. Такие данные называются «внешние данные». (Обратите внимание, что это использование не следует путать с внешними ключами, которые являются типом ограничения в базе данных).

Доступ к внешним данным осуществляется с помощью обертки сторонних данных (foreign data wrapper). Обертка сторонних данных — это библиотека, которая может взаимодействовать с внешним источником данных, скрывая детали подключения к источнику данных и получения данных из него. В качестве модулей contrib доступны некоторые сторонние обертки данных. Другие виды сторонних упаковщиков данных могут быть найдены как сторонние продукты. Если ни одна из существующих сторонних оберток данных не соответствует вашим потребностям, вы можете написать свою собственную см. главу Написание обертки сторонних данных.

Для доступа к сторонним данным необходимо создать объект внешнего сервера, который определяет способ подключения к определенному внешнему источнику данных в соответствии с набором параметров, используемых его поддерживающей оберткой сторонних данных. Затем нужно создать одну или несколько сторонних таблиц, которые определяют структуру удаленных данных. Внешняя таблица может использоваться в запросах так же, как обычная таблица, но внешняя таблица не хранит данные на сервере QHB. Всякий раз, когда он используется, QHB запрашивает обертку сторонних данных для выборки данных из внешнего источника или передачи данных во внешний источник в случае команд обновления.

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

Для получения дополнительной информации см.CREATE FOREIGN DATA WRAPPER, CREATE SERVER, CREATE USER MAPPING, CREATE FOREIGN TABLE и IMPORT FOREIGN SCHEMA.

Другие объекты базы данных

Таблицы являются центральными объектами в структуре реляционной базы данных, потому что они содержат ваши данные. Но они не единственные объекты, которые существуют в базе данных. Многие другие виды объектов могут быть созданы, чтобы сделать использование и управление данными более эффективным или удобным. Они не обсуждаются в этой главе:

Представления (views);

Функции, процедуры и операторы (functions, procedures, and operators);

Типы данных и домены (data types and domains);

Триггеры и правила перезаписи (triggers and rewrite rules).

Отслеживание зависимостей

Когда вы создаете сложные структуры базы данных, включающие множество таблиц с ограничениями внешнего ключа, представлениями, триггерами, функциями и т. д., вы неявно создаете сеть зависимостей между объектами. Например, таблица с ограничением внешнего ключа зависит от таблицы, на которую она ссылается.

Чтобы гарантировать целостность всей структуры базы данных, QHB гарантирует, что вы не можете удалить объекты, от которых все еще зависят другие объекты. Например, попытка удалить таблицу продуктов, которая рассматривалась в разделе Ограничение внешнего ключа, с таблицей заказов, зависящей от нее, приведет к появлению сообщения об ошибке, подобного этому:

DROP TABLE products;

ERROR:  cannot drop table products because other objects depend on it
DETAIL:  constraint orders_product_no_fkey on table orders depends on table products
HINT:  Use DROP ... CASCADE to drop the dependent objects too.

Сообщение об ошибке содержит полезную подсказку: если вы не хотите удалять все зависимые объекты по отдельности, вы можете запустить:

DROP TABLE products CASCADE;

и все зависимые объекты будут удалены, как и любые объекты, которые рекурсивно зависят от них. В этом случае не удаляется таблица заказов, производится только удаление ограничений внешнего ключа. На этом все заканчивается, потому что ничто не зависит от ограничения внешнего ключа. (Можно проверить, что будет делать DROP ... CASCADE, запустите DROP без CASCADE и прочитайте вывод DETAIL).

Почти все команды DROP в QHB поддерживают указание CASCADE. Конечно, характер возможных зависимостей зависит от типа объекта. Вы также можете написать RESTRICT вместо CASCADE чтобы получить поведение по умолчанию, которое должно предотвратить удаление объектов, от которых зависят любые другие объекты.

Если в команде DROP перечислено несколько объектов, CASCADE требуется только при наличии зависимостей вне указанной группы. Например, когда вы говорите DROP TABLE tab1, tab2 наличие внешнего ключа, ссылающегося на tab1 из tab2, не будет означать, что CASCADE необходим для успеха.

Для пользовательских функций QHB отслеживает зависимости, связанные с внешне видимыми свойствами функции, такими как ее аргументы и типы результатов, но не зависимости, которые могут быть обнаружены только путем анализа тела функции. В качестве примера рассмотрим следующую ситуацию:

CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow',
                             'green', 'blue', 'purple');

CREATE TABLE my_colors (color rainbow, note text);

CREATE FUNCTION get_color_note (rainbow) RETURNS text AS
  'SELECT note FROM my_colors WHERE color = $1'
  LANGUAGE SQL;

(См. раздел Функции на языке запросов (SQL) для объяснения функций языка SQL). QHB будет знать, что функция get_color_note зависит от типа rainbow: удаление типа приведет к принудительному удалению функции, поскольку ее тип аргумента больше не будет определяться. Но QHB не будет считать, что get_color_note зависит от таблицы my_colors, и поэтому не будет удалять функцию, если таблица будет удалена. Хотя у этого подхода есть недостатки, есть и преимущества. Функция все еще допустима в некотором смысле, если таблица отсутствует, хотя ее выполнение может вызвать ошибку - создание новой таблицы с тем же именем позволит функции работать снова.

Манипулирование данными

В этой главе рассматривается, как вставлять, обновлять и удалять данные таблиц. В этой главе, также, будет объяснено, как извлечь изменённые данные из базы данных.

Вставка данных

Обновление данных

Удаление данных

Возврат данных из измененных строк

Вставка данных

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

Чтобы создать новую строку, используйте команду INSERT. Команда требует имя таблицы и значение столбца. Например, рассмотрим таблицу продуктов (products) из главы Определение данных:

CREATE TABLE products (
    product_no integer,
    name text,
    price numeric
);

Пример команды для вставки строки:

INSERT INTO products VALUES (1, 'Cheese', 9.99);

Значения данных перечислены в порядке появления столбцов в таблице, разделенных запятыми. Обычно значения данных будут литералами (константами), но также допустимы скалярные выражения.

Вышеуказанный синтаксис имеет недостаток - нужно знать порядок столбцов в таблице. Чтобы избежать этого, вы можно явно перечислить столбцы. Например, обе следующие команды делают то же самок, что и приведенная выше:

INSERT INTO products (product_no, name, price) VALUES (1, 'Cheese', 9.99);
INSERT INTO products (name, price, product_no) VALUES ('Cheese', 9.99, 1);

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

Если у вас нет значений для всех столбцов, вы можете опустить некоторые из них. В этом случае столбцы будут заполнены значениями по умолчанию. Например:

INSERT INTO products (product_no, name) VALUES (1, 'Cheese');
INSERT INTO products VALUES (1, 'Cheese');

Вторая форма - это расширение QHB.Столбцы слева заполняются указанными значениями, а остальные столбцы получают значения по умолчанию.

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

INSERT INTO products (product_no, name, price) VALUES (1, 'Cheese', DEFAULT);
INSERT INTO products DEFAULT VALUES;

Можно вставить несколько строк одной командой:

INSERT INTO products (product_no, name, price) VALUES
    (1, 'Cheese', 9.99),
    (2, 'Bread', 1.99),
    (3, 'Milk', 2.99);

Также возможно вставить результат запроса (который может не иметь ни одной строки, состоять из одной строки или из нескольких строк):

INSERT INTO products (product_no, name, price)
  SELECT product_no, name, price FROM new_products
    WHERE release_date = 'today';

Поддерживаются все возможности SQL-запросов (глава Запросы) для вычисления строк, которые нужно вставить.

Обновление данных

Модификация данных, которые уже находятся в базе данных, называется обновлением. Можно обновить отдельные строки, все строки в таблице или подмножество всех строк. Каждый столбец может быть обновлен отдельно; другие столбцы не затрагиваются.

Чтобы обновить существующие строки, используйте команду UPDATE. Для этого требуется:

Имя таблицы и столбца для обновления;

Новое значение столбца;

Условия задающее список строк для обновления.

SQL, как правило, не предоставляет уникальный идентификатор для строк (см. главу Определение данных). Поэтому не всегда возможно напрямую указать, какую строку обновлять. Вместо этого указывается, каким условиям должна соответствовать строка для обновления. Только если существует первичный ключ в таблице (независимо от того, объявлен он или нет), можно надежно обращаться к отдельным строкам, выбирая условие, соответствующее первичному ключу. Графические инструменты доступа к базе данных полагаются на этот факт, чтобы стало возможно обновлять строки по отдельности.

Например, эта команда обновляет все продукты с ценой 5 до цены 10:

UPDATE products SET price = 10 WHERE price = 5;

Это может привести к обновлению нуля, одной или нескольких строк. Попытка обновления, которое не соответствует ни одной строке, не является ошибкой.

Давайте посмотрим на эту команду подробно. Сначала следует ключевое слово UPDATE за которым следует имя таблицы. Как обычно, имя таблицы может быть дополнено схемой, в противном случае оно ищется в пути по умолчанию (search_path). Далее следует ключевое слово SET за которым следуют имя столбца, знак равенства и новое значение столбца. Новое значение столбца может быть любым скалярным выражением или просто константой. Например, если нужно поднять цену на все товары на 10%, можно использовать:

UPDATE products SET price = price * 1.10;

Как видно, выражение для нового значения может ссылаться на существующие значения в строке. Мы также пропустили WHERE. Если условие не задано,
это означает, что все строки в таблице будут обновлены. Если условие присутствует, обновляются только те строки, которые соответствуют условию WHERE. Обратите внимание, что знак равенства в предложении SET является присваиванием, а знак в предложении WHERE - сравнением, но это не создает никакой двусмысленности. Конечно, условие WHERE не обязательно должно быть критерием равенства. Доступны многие другие операторы (см. главу Функции и операторы). Но выражение должно вычисляться до логического результата.

Вы можете обновить более одного столбца в команде UPDATE, перечислив более одного назначения в предложении SET. Например:

UPDATE mytable SET a = 5, b = 3, c = 1 WHERE a > 0;

Удаление данных

До сих пор рассматривалось, как добавлять данные в таблицы и как их изменять. Осталось обсудить, как удалить данные, которые больше не нужны. Так же, как добавление данных возможно только для целых строк, можно удалить только целые строки из таблицы. В предыдущем разделе было рассмотрено,
что SQL не предоставляет способ напрямую обращаться к отдельным строкам. Поэтому удаление строк может быть сделано только путем указания условий, которым должны соответствовать удаляемые строки.
Если в таблице есть первичный ключ, возможно указать точную строку. Но также возможно удалить группы строк, соответствующие условию, или удалить все строки в таблице одновременно.

Используйте команду DELETE для удаления строк; синтаксис очень похож на команду UPDATE. Например, чтобы удалить все строки из таблицы товаров с ценой 10, используйте:

DELETE FROM products WHERE price = 10;

Если просто написать:

DELETE FROM products;

тогда все строки в таблице будут удалены! БУДЬТЕ ОСТОРОЖНЫ.

Возврат данных из измененных строк

Иногда полезно получить данные из измененных строк, когда ими манипулируют. Все команды INSERT, UPDATE и DELETE имеют необязательное предложение RETURNING, для поддержки этой возможности. Использование RETURNING позволяет избежать выполнения дополнительного запроса к базе данных для сбора данных и является особенно ценным - в противном случае было бы трудно надежно идентифицировать измененные строки.

Разрешенное содержимое предложения RETURNING совпадает с выходным списком команды SELECT (см. раздел Списки выборки). Он может содержать имена столбцов целевой таблицы команды или выражения значений, использующие эти столбцы. Обычным сокращением является RETURNING *, которое выбирает все столбцы целевой таблицы по порядку.

В INSERT доступными для RETURNING данными является строка, в которой она была вставлена. Это не очень полезно в тривиальных вставках - возвращаются данные, предоставленные клиентом. Но это может быть очень удобно при использовании вычисленных значений по умолчанию. Например, при использовании serial столбца для предоставления уникальных идентификаторов RETURNING может вернуть идентификатор, назначенный новой строке:

CREATE TABLE users (firstname text, lastname text, id serial primary key);

INSERT INTO users (firstname, lastname) VALUES ('Joe', 'Cool') RETURNING id;

Предложение RETURNING также очень полезно с INSERT ... SELECT.

В UPDATE данные, доступные для RETURNING являются новым содержимым измененной строки. Например:

UPDATE products SET price = price * 1.10
  WHERE price <= 99.99
  RETURNING name, price AS new_price;

В DELETE данные, доступные для RETURNING являются содержимым удаленной строки. Например:

DELETE FROM products
  WHERE obsoletion_date = 'today'
  RETURNING *;

Если в целевой таблице есть триггеры (глава Триггеры), данные, доступные для RETURNING — это строка, измененная триггерами. Таким образом, проверка столбцов, вычисленных триггерами, является еще одним распространенным вариантом использования RETURNING.

Запросы

В предыдущих разделах описывалось, как создать таблицы, как наполнить их данными и как манипулировать этими данными. В данном разделе наконец объясняется, как получить данные из базы данных.

Обзор

Процесс или команда получения данных из базы данных называется запросом. В SQL для создания запросов используется команда SELECT. Общий синтаксис команды SELECT выглядит так:

[WITH запросы_with] SELECT список_выборки FROM табличное_выражение [определение_сортировки]

Далее будут подробно описаны список выборки, табличное выражение и определение сортировки. Запросы WITH являются расширенной функцией, поэтому рассматриваются в последнюю очередь.

Простой запрос имеет следующий вид:

SELECT * FROM table1;

Если предположить, что существует таблица с именем table1, то эта команда извлечет все строки и все определенные пользователем столбцы из table1. (Метод выдачи результата зависит от клиентского приложения. Например, программа psql отобразит на экране таблицу ASCII, хотя клиентские библиотеки предложат функции для извлечения отдельных значений из результата запроса). В качестве списка выборки указан символ *, означающий, что в результате выполнения запроса вернутся все столбцы табличного выражения. В списке выборки можно также указать подмножество доступных столбцов или выполнить вычисления с этими столбцами. Например, если в table1 есть столбцы с именами a, b и c (и, возможно, другие), можно выполнить следующий запрос:

SELECT a, b + c FROM table1;

(предполагая, что b и c имеют числовой тип данных). Более подробную информацию см. в разделе Списки выборки.

FROM table1 — это простейший вид табличного выражения, который читает только одну таблицу. В общем случае табличные выражения могут быть сложными комбинациями базовых таблиц, соединений и подзапросов. Но можно и полностью опустить табличное выражение и использовать команду SELECT как калькулятор:

SELECT 3 * 4;

Это будет более полезно, если выражения в списке выборки возвращают меняющиеся результаты. Например, можно вызвать функцию так:

SELECT random();

Табличные выражения

Табличное выражение вычисляет таблицу. Оно содержит предложение FROM, за которым могут следовать предложения WHERE, GROUP BY и HAVING. Простые табличные выражения просто ссылаются на таблицу на диске, также называемую базовой, но в более сложных выражениях базовые таблицы можно изменять или комбинировать различными способами.

Необязательные предложения WHERE, GROUP BY и HAVING в табличных выражениях задают последовательность преобразований, выполняемых с таблицей, полученной в предложении FROM. В результате этих преобразований создается виртуальная таблица, строки которой передаются в список выборки для вычисления выходных строк запроса.

Предложение FROM

Предложение FROM формирует таблицу из одной или нескольких ссылок на таблицы, разделённых запятыми.

FROM ссылка_на_таблицу [, ссылка_на_таблицу [, ...]]

Ссылка на таблицу может быть именем таблицы (возможно, дополненным схемой) или производной таблицей, например, подзапросом, соединением таблиц или их сложной комбинацией. Если в предложении FROM указано более одной такой ссылки, таблицы соединяются перекрестно (то есть образуется декартово произведение их строк; см. ниже). Результатом списка FROM является промежуточная виртуальная таблица, которая затем может преобразовываться с помощью предложений WHERE, GROUP BY и HAVING, и в итоге станет результатом общего табличного выражения.

Если табличная ссылка указывает на таблицу, являющуюся родительской в табличной иерархии наследования, то в результате выводятся строки не только этой таблицы, но и всех ее дочерних таблиц, если только не указать перед ее именем ключевое слово ONLY. Однако при этом ссылка выведет только столбцы именованной таблицы — дополнительные столбцы дочерних таблиц будут проигнорированы.

Вместо добавление ONLY перед именем таблицы можно написать * после имени, явно указав, что должны обрабатываться и дочерние таблицы. Однако практических причин использовать этот синтаксис больше нет, поскольку поиск в дочерних таблицах теперь производится по умолчанию.

Соединенные таблицы

Соединенная таблица — это таблица, полученная из двух других (реальных или производных) таблиц в соответствии с правилами соединения определенного типа. Допустимы внутренние, внешние и перекрестные соединения. Общий синтаксис соединения таблиц выглядит так:

T1 тип_соединения T2 [ условие_соединения ]

Соединения всех типов могут быть объединены в цепочку или вложены друг в друга: любая из таблиц или даже обе таблицы T1 и T2 могут быть уже соединенными таблицами. Для указания точного порядка соединения предложения JOIN можно заключать в скобки. При отсутствии скобок предложения JOIN обрабатываются слева направо.

Типы соединений

Перекрестное соединение

T1 CROSS JOIN T2

Для каждой возможной комбинации строк из T1 и T2 (т. е. их декартова произведения) соединенная таблица будет содержать строку, состоящую из всех столбцов T1 и следующих за ними столбцов T2. Если таблицы содержат N и M строк соответственно, соединенная таблица будет содержать N * M строк.

FROM T1 CROSS JOIN T2 равнозначно FROM T1 INNER JOIN T2 ON TRUE (см. ниже). Также эта запись равнозначна FROM T1, T2.

Примечание
Последний вариант не полностью равнозначен первым двум, если в соединении указано более двух таблиц, поскольку JOIN связывает сильнее, чем запятая. Например, FROM T1 CROSS JOIN T2 INNER JOIN T3 ON условие не равнозначно FROM T1, T2 INNER JOIN T3 ON условие так как условие может ссылаться на T1 в первом случае, но не во втором.

Соединения с сопоставлением строк

T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 ON логическое_выражение
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 USING ( список столбцов соединения )
T1 NATURAL { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2

Слова INNER и OUTER необязательны во всех формах. INNER — это значение по умолчанию; LEFT, RIGHT и FULL подразумевают внешнее соединение.

Условие соединения указывается в предложении ON или USING или неявно словом NATURAL. Это условие определяет, какие строки из двух исходных таблиц считаются «совпадающими», как подробно рассматривается ниже.

Возможные типы соединений с сопоставлением строк:

INNER JOIN

Для каждой строки R1 из T1 в соединенной таблице содержится строка для каждой строки из T2, которая удовлетворяет условию соединения с R1.

LEFT OUTER JOIN

Сначала выполняется внутреннее соединение. Затем для каждой строки T1, которая не удовлетворяют условию соединения ни с одной строкой в T2, добавляется соединенная строка со значениями NULL в столбцах T2. Таким образом, в соединенной таблице всегда будет присутствовать хотя бы одна строка для каждой строки T1.

RIGHT OUTER JOIN

Сначала выполняется внутреннее соединение. Затем для каждой строки T2, которая не удовлетворяют условию соединения ни с одной строкой в T1, добавляется соединенная строка со значениями NULL в столбцах T1. Таким образом, в соединенной таблице всегда будет присутствовать хотя бы одна строка для каждой строки T2.

FULL OUTER JOIN

Сначала выполняется внутреннее соединение. Затем для каждой строки T1, которая не удовлетворяют условию соединения ни с одной строкой в T2, добавляется соединенная строка со значениями NULL в столбцах T2. А для каждой строки T2, которая не удовлетворяют условию соединения ни с одной строкой в T1, добавляется соединенная строка со значениями NULL в столбцах T1.
.

Предложение ON является наиболее общим видом условия соединения: в нем указываются выражения логического типа, такие же, как те, что используются в предложении WHERE. Пара строк из T1 и T2 соответствуют друг другу, если выражение ON возвращает true.

Предложение USING — это сокращенная запись, позволяющая извлечь пользу в особой ситуации, когда обе стороны соединения используют одинаковые имена столбцов. Оно принимает разделенный запятыми список имен общих столбцов и формирует условие соединения с равенством этих столбцов. Например, соединение T1 и T2 с помощью USING (a, b) создает условие ON T1.a = T2.a AND T1.b = T2.b.

Кроме того, при выводе JOIN USING исключаются избыточные столбцы: нет необходимости выводить оба сопоставленных столбца, так как они имеют одинаковые значения. Тогда как JOIN ON выводит все столбцы из T1, а затем все столбцы из T2, JOIN USING создает один столбец для каждой из перечисленных пар столбцов (в указанном порядке), затем все оставшиеся столбцы из T1 и наконец все оставшиеся столбцы из T2.

Наконец, NATURAL — это сокращенная форма USING: оно формирует список USING, состоящий из всех имен столбцов, которые появляются в обеих входных таблицах. Как и с USING, в выходной таблице эти столбцы появляются только один раз. Если нет общих имен столбцов, NATURAL JOIN ведет себя как JOIN ... ON TRUE, выводя перекрестное соединение таблиц.

Примечание
Предложение USING разумно защищено от изменений столбцов в соединяемых отношениях, поскольку соединяются только перечисленные столбцы. Использование NATURAL гораздо более рискованно, поскольку любые изменения схем в любом из отношений, вызывающие появление нового совпадающего имени столбца, приведут к соединению и этого нового столбца.

Рассмотрим вышеизложенное на примерах. Допустим, у нас есть таблицы t1:

num | name
-----+------
  1 | a
  2 | b
  3 | c

и t2:

num | value
-----+-------
  1 | xxx
  3 | yyy
  5 | zzz

Для различных типов их соединений мы получим следующие результаты:

=> SELECT * FROM t1 CROSS JOIN t2;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   1 | a    |   3 | yyy
   1 | a    |   5 | zzz
   2 | b    |   1 | xxx
   2 | b    |   3 | yyy
   2 | b    |   5 | zzz
   3 | c    |   1 | xxx
   3 | c    |   3 | yyy
   3 | c    |   5 | zzz
(9 rows)

=> SELECT * FROM t1 INNER JOIN t2 ON t1.num = t2.num;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   3 | c    |   3 | yyy
(2 rows)

=> SELECT * FROM t1 INNER JOIN t2 USING (num);
 num | name | value
-----+------+-------
   1 | a    | xxx
   3 | c    | yyy
(2 rows)

=> SELECT * FROM t1 NATURAL INNER JOIN t2;
 num | name | value
-----+------+-------
   1 | a    | xxx
   3 | c    | yyy
(2 rows)

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   2 | b    |     |
   3 | c    |   3 | yyy
(3 rows)

=> SELECT * FROM t1 LEFT JOIN t2 USING (num);
 num | name | value
-----+------+-------
   1 | a    | xxx
   2 | b    |
   3 | c    | yyy
(3 rows)

=> SELECT * FROM t1 RIGHT JOIN t2 ON t1.num = t2.num;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   3 | c    |   3 | yyy
     |      |   5 | zzz
(3 rows)

=> SELECT * FROM t1 FULL JOIN t2 ON t1.num = t2.num;
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   2 | b    |     |
   3 | c    |   3 | yyy
     |      |   5 | zzz
(4 rows)

Условие соединения, указанное с помощью ON, может также содержать условия, не имеющие прямого отношения к соединению. Это может быть полезно для некоторых запросов, но должно быть тщательно продумано. Например:

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num AND t2.value = 'xxx';
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
   2 | b    |     |
   3 | c    |     |
(3 rows)

Обратите внимание, что это же ограничение в WHERE приводит к иному результату:

=> SELECT * FROM t1 LEFT JOIN t2 ON t1.num = t2.num WHERE t2.value = 'xxx';
 num | name | num | value
-----+------+-----+-------
   1 | a    |   1 | xxx
(1 row)

Это связано с тем, что ограничение в предложение ON обрабатывается до соединения, а ограничение в WHERE — после. Это не имеет значения при внутренних соединениях, но очень важно при внешних.

Псевдонимы таблиц и столбцов

Таблицам и сложным табличным ссылкам можно дать временные имена, по которым можно обращаться к таблицам в запросе. Это временное имя называется псевдонимом таблицы.

Псевдоним можно создать с помощью ключевого слова AS:

FROM табличная_ссылка AS псевдоним

Или:

FROM табличная_ссылка псевдоним

Ключевое слово AS является необязательным. Вместо псевдонима здесь может быть любой идентификатор.

Обычно псевдонимы создаются в качестве коротких идентификаторов для длинных имен таблиц, чтобы сохранить читаемость запросов на соединение. Например:

SELECT * FROM некое_очень_длинное_имя_таблицы s JOIN другое_довольно_длинное_имя a ON s.id = a.num;

Псевдоним становится новым именем табличной ссылки в текущем запросе — после объявления псевдонима нельзя сослаться на таблицу по исходному имени. Поэтому следующий запрос недопустим:

SELECT * FROM my_table AS m WHERE my_table.a > 5;    -- неправильно

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

SELECT * FROM people AS mother JOIN people AS child ON mother.id = child.mother_id;

Кроме того, псевдоним обязателен, если вы табличная ссылка является подзапросом (см. подраздел Подзапросы).

В случае неоднозначных определений псевдонимов стоит использовать скобки. В следующем примере первый оператор назначает псевдоним b второму экземпляру my_table, а второй оператор назначает псевдоним результату соединения:

SELECT * FROM my_table AS a CROSS JOIN my_table AS b ...
SELECT * FROM (my_table AS a CROSS JOIN my_table) AS b ...

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

FROM табличная_ссылка [AS] псевдоним ( столбец1 [, столбец2 [, ...]] )

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

Когда псевдоним применяется к результату JOIN, он скрывает изначальные имена таблиц внутри JOIN. Например, следующий запрос SQL возможен:

SELECT a.* FROM my_table AS a JOIN your_table AS b ON ...

а запрос:

SELECT a.* FROM (my_table AS a JOIN your_table AS b ON ...) AS c

выдаст ошибку, так как псевдоним таблицы a не виден снаружи псевдонима c.

Подзапросы

Подзапросы, образующие таблицу, должны быть заключены в круглые скобки, и им обязательно нужно назначать псевдонимы (как описано в подразделе Псевдонимы таблиц и столбцов). Например:

FROM (SELECT * FROM table1) AS псевдоним

Этот пример равнозначен записи FROM table1 AS псевдоним. Более интересные случаи, которые нельзя свести к простому соединению, возникают, когда подзапрос включает в себя группировку или агрегирующие функции.

Подзапрос также может быть списком VALUES:

FROM (VALUES ('anne', 'smith'), ('bob', 'jones'), ('joe', 'blow'))
     AS names(first, last)

Опять же, тут требуется псевдоним таблицы. Назначать псевдонимы столбцам списка VALUES необязательно, но это хорошая практика. Дополнительную информацию см. в разделе Списки VALUES.

Табличные функции

Табличные функции — это функции, выдающие набор строк, состоящий из базовых типов данных (скалярных типов) или составных типов данных (строк таблицы). Они используются как таблицы, представления или подзапросы в предложении FROM. Столбцы, возвращаемые табличными функциями, можно включать в предложения SELECT, JOIN или WHERE так же, как столбцы таблиц, представлений или подзапросов.

Табличные функции тоже можно соединять с помощью записи ROWS FROM; при этом результаты будут возвращены в параллельных столбцах. В таком случае количество строк в результате будет равно наибольшему из результатов функций, а результаты с меньшим количеством строк дополнятся значениями NULL.

вызов_функции [WITH ORDINALITY] [[AS] псевдоним_таблицы [(псевдоним_столбца [, ... ])]]
ROWS FROM( вызов_функции [, ... ] ) [WITH ORDINALITY] [[AS] псевдоним_таблицы [(псевдоним_столбца [, ... ])]]

Если указано предложение WITH ORDINALITY, то в результирующие столбцы функции будет добавлен дополнительный столбец типа bigint с пронумерованными строками результирующего набора (начиная с 1). (Это обобщение стандартного синтаксиса SQL для UNNEST ... WITH ORDINALITY). По умолчанию данный столбец называется ordinality, но с помощью предложения AS ему можно присвоить другое имя.

Специальную табличную функцию UNNEST можно вызвать с любым количеством параметров-массивов, а возвращает она соответствующее количество столбцов, как если бы UNNEST (см. раздел Функции и операторы для массивов) вызывалась для каждого параметра по отдельности, а результаты объединялись с помощью конструкции ROWS FROM.

UNNEST( выражение_массива [, ... ] ) [WITH ORDINALITY] [[AS] псевдоним_таблицы [(псевдоним_столбца [, ... ])]]

Если псевдоним_таблицы не указан, то в качестве имени таблицы используется имя функции, а в случае c конструкцией ROWS FROM() используется имя первой функции.

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

Несколько примеров:

CREATE TABLE foo (fooid int, foosubid int, fooname text);

CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$
    SELECT * FROM foo WHERE fooid = $1;
$$ LANGUAGE SQL;

SELECT * FROM getfoo(1) AS t1;

SELECT * FROM foo
    WHERE foosubid IN (
                        SELECT foosubid
                        FROM getfoo(foo.fooid) z
                        WHERE z.fooid = foo.fooid
                      );

CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1);

SELECT * FROM vw_getfoo;

В некоторых случаях полезно определить табличную функцию, которая может возвращать различные наборы столбцов при разных вариантах вызова. Для этого необходимо объявить табличную функцию как не имеющую выходных параметров (OUT) и возвращающую псевдотип record. Когда в запросе используется такая функция, ожидаемую структуру строк следует указать в самом запросе, чтобы система знала, как анализировать запрос и составить его план. Эта запись выглядит так:

вызов_функции [AS] псевдоним (определение_столбца [, ... ])
вызов_функции AS [псевдоним] (определение_столбца [, ... ])
ROWS FROM( ... вызов_функции AS (определение_столбца [, ... ]) [, ... ] )

Если синтаксис ROWS FROM() не используется, список определения_столбцов заменяет список псевдонимов столбцов, который в противном случае можно было бы добавить в предложение FROM; имена в определениях столбцов служат псевдонимами. При использовании ROWS FROM() список определения_столбцов можно добавить к каждой функции отдельно; или если имеется только одна функция и нет предложения WITH ORDINALITY, список определения_столбцов можно записать вместо списка псевдонимов после ROWS FROM().

Рассмотрим этот пример:

SELECT *
    FROM dblink('dbname=mydb', 'SELECT proname, prosrc FROM pg_proc')
      AS t1(proname name, prosrc text)
    WHERE proname LIKE 'bytea%';

Функция dblink (часть модуля dblink) выполняет удаленный запрос. Она объявлена как возвращающая record, поскольку его можно использовать для любого запроса. Здесь фактический набор столбцов необходимо указать в вызывающем запросе, чтобы анализатор запроса знал, например, как преобразовать *.

В этом примере используется ROWS FROM:

SELECT *
FROM ROWS FROM
    (
        json_to_recordset('[{"a":40,"b":"foo"},{"a":"100","b":"bar"}]')
            AS (a INTEGER, b TEXT),
        generate_series(1, 3)
    ) AS x (p, q, s)
ORDER BY p;

  p  |  q  | s
-----+-----+---
  40 | foo | 1
 100 | bar | 2
     |     | 3

Эта конструкция соединяет две функции в одном флаге FROM. Функции json_to_recordset() предписывается возвращать два столбца, первый integer и второй text. Результат generate_series() используется непосредственно. Предложение ORDER BY упорядочивает значения этого столбца как целочисленные.

Подзапросы LATERAL

Перед подзапросами в предложении FROM можно указать ключевое слово LATERAL. Оно позволит им ссылаться на столбцы предшествующих элементов FROM. (Без LATERAL каждый подзапрос вычисляется независимо и не может ссылаться на другие элементы FROM).

Ключевое слово LATERAL также можно указать перед табличными функциями в предложении FROM, но для них оно не необязательно; аргументы функций в любом случае могут содержать ссылки на столбцы в предыдущих элементах FROM.

Элемент LATERAL может находиться на верхнем уровне списка FROM или в дереве JOIN. В последнем случае он может также ссылаться на любые элементы в левой части JOIN, справа от которого он располагается.

Когда элемент FROM содержит перекрестные ссылки LATERAL, вычисление выполняется следующим образом: сначала для каждой строки элемента FROM, содержащей ссылочный столбец (или столбцы), или набора строк нескольких элементов FROM с этими столбцами с помощью значений столбцов этой строки или набора строк вычисляется элемент LATERAL. Затем результирующие строки соединяются со строками, из которых они были вычислены. Этот процесс повторяется для каждой строки или набора строк исходных таблиц.

Простой пример применения LATERAL:

SELECT * FROM foo, LATERAL (SELECT * FROM bar WHERE bar.id = foo.bar_id) ss;

Здесь это не очень полезно, поскольку тот же результат можно получить гораздо проще:

SELECT * FROM foo, bar WHERE bar.id = foo.bar_id;

Ключевое слово LATERAL полезно в первую очередь тогда, когда ссылочный столбец необходим для вычисления соединяемых строк. Обычно оно применяется с целью передачи значения аргумента функции, возвращающей набор данных. Например, если предположить, что vertices(polygon) возвращает набор вершин многоугольника, хранящиеся в таблице близкие вершины многоугольников можно выявить так:

SELECT p1.id, p2.id, v1, v2
FROM polygons p1, polygons p2,
     LATERAL vertices(p1.poly) v1,
     LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

Этот запрос можно также записать и так:

SELECT p1.id, p2.id, v1, v2
FROM polygons p1 CROSS JOIN LATERAL vertices(p1.poly) v1,
     polygons p2 CROSS JOIN LATERAL vertices(p2.poly) v2
WHERE (v1 <-> v2) < 10 AND p1.id != p2.id;

или с другими равнозначными формулировками. (Как уже упоминалось, в данном примере в ключевом слове LATERAL нет необходимости, но мы применили его для ясности.)

Зачастую особенно удобно бывает использовать LEFT JOIN с подзапросом LATERAL, чтобы исходные строки оказались в результате, даже если подзапрос LATERAL не выдает для них строк. Например, если get_product_names() возвращает названия продуктов, выпущенных определенным производителем, но некоторые производители в нашей таблице в настоящее время ничего не выпускают, мы можем определить, какие именно, таким образом:

SELECT m.name
FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true
WHERE pname IS NULL;

Предложение WHERE

Синтаксис предложения WHERE:

WHERE условие_поиска

где условие_поиска — это любое выражение значения (см. раздел Выражения значения), возвращающее значение типа boolean.

После обработки предложения FROM каждая строка получившейся виртуальной таблицы проверяется по условию поиска. Если результат условия равен true, строка остается в выходной таблице, а иначе (т. е. если результат равен false или NULL) отбрасывается. Обычно условие поиска ссылается как минимум на один столбец таблицы, получившейся в предложении FROM; это не требуется, но в противном случае предложение WHERE будет довольно бессмысленным.

Примечание
Условие для внутреннего соединения можно записать как в предложении WHERE, так и в предложении JOIN. Например, это табличное выражение:

FROM a, b WHERE a.id = b.id AND b.val > 5

равнозначно этому:

FROM a INNER JOIN b ON (a.id = b.id) WHERE b.val > 5

и, возможно, даже этому:

FROM a NATURAL JOIN b WHERE b.val > 5

Какой вариант использовать, в основном, дело вкуса. Синтаксис JOIN в предложении FROM, наверное, не самый портируемый на другие SQL-СУБД, хотя он и описан в стандарте SQL. Но для внешних соединений других вариантов нет: их нужно записывать в предложении FROM. Предложения ON и USING во внешних соединениях не равнозначны условию WHERE, так как они могут добавлять строки (для несовпадающих входных строк), а также удалять их из конечного результата.

Несколько примеров предложений WHERE:

SELECT ... FROM fdt WHERE c1 > 5

SELECT ... FROM fdt WHERE c1 IN (1, 2, 3)

SELECT ... FROM fdt WHERE c1 IN (SELECT c1 FROM t2)

SELECT ... FROM fdt WHERE c1 IN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10)

SELECT ... FROM fdt WHERE c1 BETWEEN (SELECT c3 FROM t2 WHERE c2 = fdt.c1 + 10) AND 100

SELECT ... FROM fdt WHERE EXISTS (SELECT c1 FROM t2 WHERE c2 > fdt.c1)

fdt — это таблица, получившаяся в предложении FROM. Строки, не соответствующие условию поиска предложения WHERE, исключаются из fdt. Обратите внимание на использование скалярных подзапросов в качестве выражений значения. Как и любые другие запросы, подзапросы могут реализовывать сложные табличные выражения. Также обратите внимание, как на fdt ссылаются подзапросы. Дополнение имени c1 в виде fdt.c1 требуется, только если c1 также является именем столбца в получившейся входной таблице этого подзапроса. Но дополнение именем столбца придает ясности, даже когда в ней нет необходимости. Этот пример показывает, как область именования столбцов внешнего запроса распространяется на его внутренние запросы.

Предложения GROUP BY и HAVING

После прохождения фильтра WHERE получившуюся входную таблицу можно подвергнуть группированию с помощью предложения GROUP BY, а затем исключить группу строк с помощью предложения HAVING.

SELECT список_выборки
    FROM ...
    [WHERE ...]
    GROUP BY ссылка_на_группирующий_столбец [, ссылка_на_группирующий_столбец]...

Предложение GROUP BY применяется для группирования тех строк в таблице, у которых во всех перечисленных столбцах совпадают значения. Порядок, в котором перечислены столбцы, значения не имеет. В результате каждый набор строк с одинаковыми значениями столбцов совмещается в одну групповую строку, представляющую все строки группы. Целью этого процесса является устранение избыточности в выходных данных и/или вычисление агрегатных функций, применимых к этим группам. Например:

=> SELECT * FROM test1;
 x | y
---+---
 a | 3
 c | 2
 b | 5
 a | 1
(4 rows)

=> SELECT x FROM test1 GROUP BY x;
 x
---
 a
 b
 c
(3 rows)

Во втором запросе мы не могли написать SELECT * FROM test1 GROUP BY x, поскольку для столбца y нет единого значения, которое можно было бы связать с каждой группой. Однако к группирующим столбцам можно обращаться в списке выборки, так как они имеют единственное значение в каждой группе.

В целом, если таблица сгруппирована, к столбцам, не перечисленным в GROUP BY, можно обращаться только в агрегатных выражениях. Пример с агрегатными выражениями:

=> SELECT x, sum(y) FROM test1 GROUP BY x;
 x | sum
---+-----
 a |   4
 b |   5
 c |   2
(3 rows)

Здесь sum — это агрегатная функция, вычисляющая единственное значение для всей группы. Подробную информацию о доступных агрегатных функциях см. в разделе Агрегатные функции.

Совет
Группирование без агрегатных выражений в сущности выдает набор различающихся значений в столбце. Этот же результат можно получить с помощью предложения DISTINCT (см. раздел DISTINCT ниже).

Еще один пример: в нем вычисляется общая сумма продаж по каждому продукту (а не общая сумма по всем продуктам):

SELECT product_id, p.name, (sum(s.units) * p.price) AS sales
    FROM products p LEFT JOIN sales s USING (product_id)
    GROUP BY product_id, p.name, p.price;

В этом примере столбцы product_id, p.name и p.price должны находиться в предложении GROUP BY, поскольку к ним обращаются в списке выборки запроса (но см. ниже). Столбцу s.units необязательно присутствовать в списке GROUP BY, так как он используется только в агрегатном выражении (sum(...)), представляющем сумму продаж продукта. Для каждого продукта этот запрос возвращает строку с итоговой суммой по всем продажам этого продукта.

Если бы таблица products была настроена так, чтобы, скажем, по столбцу product_id был создан первичный ключ, этого было бы достаточно, чтобы в вышеописанном примере сгруппировать строки по product_id, поскольку название и цена функционально зависят от ID продукта и можно однозначно определить, какие название и цену возвращать для каждой группы по ID продукта.

В стандарте SQL GROUP BY может группировать только по столбцам исходной таблицы, но QHB расширяет эту функциональность, позволяя GROUP BY группировать также и по столбцам в списке выборки. Также разрешается группировать по выражениям значения, а не просто по именам столбцов.

Если таблица была сгруппирована с помощью GROUP BY, но интерес представляют только определенные группы, можно воспользоваться предложением HAVING, действующим подобно предложению WHERE, и убрать лишние группы из результата. Синтаксис при этом такой:

SELECT список_выборки FROM ... [WHERE ...] GROUP BY ...
  HAVING логическое_выражение

Выражения в предложении HAVING могут обращаться как к группирующим выражениям, так и к выражениям, не участвующим в группировке (которые обязательно должны быть агрегирующими функциями).

Пример:

=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3;
 x | sum
---+-----
 a |   4
 b |   5
(2 rows)

=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c';
 x | sum
---+-----
 a |   4
 b |   5
(2 rows)

Более реалистичный пример:

SELECT product_id, p.name, (sum(s.units) * (p.price - p.cost)) AS profit
    FROM products p LEFT JOIN sales s USING (product_id)
    WHERE s.date > CURRENT_DATE - INTERVAL '4 weeks'
    GROUP BY product_id, p.name, p.price, p.cost
    HAVING sum(p.price * s.units) > 5000;

В примере выше предложение WHERE выбирает строки по несгруппированному столбцу (выражение истинно только для продаж за последние четыре недели), тогда как предложение HAVING ограничивает выходные данные группами с общей суммой валовой выручки больше 5000. Обратите внимание, что агрегатные выражения не обязательно должны быть одинаковыми во всех частях запроса.

Если в запросе есть вызовы агрегатных функций, но нет предложения GROUP BY, группирование все равно произойдет: результатом будет одна строка группы (или, возможно, ни одной строки, если эта строка будет исключена предложением HAVING). Это справедливо и для запросов, содержащих только предложение HAVING, но не вызовы агрегатных функции или предложение GROUP BY.

GROUPING SETS, CUBE и ROLLUP

Более сложные, чем описанные выше, операции группировки возможны с концептом группирующих наборов. Данные, выбранные предложениями FROM и WHERE группируются отдельно каждым заданным группирующим набором, затем для каждой группы вычисляются агрегатные функции, совсем как для простых предложений GROUP BY, после чего возвращаются результаты. Например:

=> SELECT * FROM items_sold;
 brand | size | sales
-------+------+-------
 Foo   | L    |  10
 Foo   | M    |  20
 Bar   | M    |  15
 Bar   | L    |  5
(4 rows)

=> SELECT brand, size, sum(sales) FROM items_sold GROUP BY GROUPING SETS ((brand), (size), ());
 brand | size | sum
-------+------+-----
 Foo   |      |  30
 Bar   |      |  20
       | L    |  15
       | M    |  35
       |      |  50
(5 rows)

В каждом подсписке GROUPING SETS можно задать ноль и более столбцов или выражений, которые воспринимаются так же, как если бы они находились непосредственно в предложении GROUP BY. Пустой группирующий набор означает, что все строки агрегируются до одной группы (которая выводится, даже если входных строк не было), как описано выше для агрегатных функций без предложения GROUP BY.

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

Для указания двух распространенных типов группирующих наборов предусмотрена краткая запись. Предложение формы

ROLLUP ( e1, e2, e3, ... )

представляет заданный список выражений и всех префиксов списка, включая пустой список; то есть оно равнозначно записи:

GROUPING SETS (
    ( e1, e2, e3, ... ),
    ...
    ( e1, e2 ),
    ( e1 ),
    ( )
)

Обычно это применяется для анализа иерархических данных, например, общей зарплаты по отделам, подразделениям и компании в целом.

Предложение формы

CUBE (e1, e2, ...)

представляет заданный список и все его возможные множества (т. е. степенное множество). Таким образом,

CUBE (a, b, c)

равнозначно

GROUPING SETS (
    ( a, b, c ),
    ( a, b    ),
    ( a,    c ),
    ( a       ),
    (    b, c ),
    (    b    ),
    (       c ),
    (         )
)

Отдельными элементами предложений CUBE и ROLLUP могут быть либо отдельные выражения, либо подсписки элементов в скобках. В последнем случае подсписки обрабатываются как атомарные единицы с целью создания отдельных группирующих наборов. Например:

CUBE ((a, b), (c, d))

равнозначно

GROUPING SETS (
    ( a, b, c, d ),
    ( a, b       ),
    (       c, d ),
    (            )
)

и

ROLLUP (a, (b, c), d)

равнозначно

GROUPING SETS (
    ( a, b, c, d ),
    ( a, b, c    ),
    ( a          ),
    (            )
)

Конструкции CUBE и ROLLUP можно использовать либо непосредственно в предложении GROUP BY, либо вложенными в предложение GROUPING SETS. Если одно предложение GROUPING SETS вкладывается внутрь другого, результат будет таким же, как если бы все элементы внутреннего предложения были записаны непосредственно во внешнем.

Если в одном предложении GROUP BY задается несколько группирующих элементов, то итоговый список группирующих наборов является векторным произведением этих отдельных элементов. Например:

GROUP BY a, CUBE (b, c), GROUPING SETS ((d), (e))

равнозначно

GROUP BY GROUPING SETS (
    (a, b, c, d), (a, b, c, e),
    (a, b, d),    (a, b, e),
    (a, c, d),    (a, c, e),
    (a, d),       (a, e)
)

Когда задается сразу несколько группирующих элементов, итоговый набор группирующих наборов может содержать дубликаты. Например:

GROUP BY ROLLUP (a, b), ROLLUP (a, c)
is equivalent to

GROUP BY GROUPING SETS (
    (a, b, c),
    (a, b),
    (a, b),
    (a, c),
    (a),
    (a),
    (a, c),
    (a),
    ()
)

Если эти дубликаты нежелательны, их можно удалить, добавив предложение DISTINCT непосредственно в GROUP BY. Таким образом,

GROUP BY DISTINCT ROLLUP (a, b), ROLLUP (a, c)

равнозначно

GROUP BY GROUPING SETS (
    (a, b, c),
    (a, b),
    (a, c),
    (a),
    ()
)

Это не то же самое, что SELECT DISTINCT, так как выходные строки все равно могут содержать дубликаты. Если любой из не участвующих в группировании столбцов содержит NULL, он будет неотличим от значения NULL, полученного при группировании этого же столбца.

Примечание
Конструкция (a, b) обычно воспринимается в выражениях как конструктор строки. Однако в предложении GROUP BY на верхних уровнях выражений это не работает, и запись (a, b) разбирается как список выражений (как описано выше). Если по какой-то причине вам нужен конструктор строки группирующем выражении, используйте запись ROW(a, b).

Обработка оконных функций

Если запрос содержит какие-либо оконные функции (см. разделы Руководство по оконным функциям, Оконные функции и Вызовы оконных функций), эти функции вычисляются после каждой группировки, агрегации и фильтрации HAVING. То есть, если запрос использует какие-либо агрегатные функции, предложения GROUP BY или HAVING, тогда строки, видимые для оконных функций, являются сгруппированными строками, а не исходными строками таблицы из FROM/WHERE.

При использовании нескольких оконных функций все функции, имеющие в определениях синтаксически равнозначные предложения PARTITION BY и ORDER BY, гарантированно обрабатывают данные за один проход. Следовательно, они увидят один порядок сортировки, даже если ORDER BY не определяет однозначный порядок. Однако для функций, имеющих разные предложения PARTITION BY и ORDER BY, никаких гарантий не дается. (В таких случаях между проходами вычислений оконных функций обычно требуется этап сортировки, и эта сортировка не гарантирует сохранение порядка строк, который ее ORDER BY считает равнозначным.)

На данный момент оконные функции всегда требуют предварительно отсортированные данные, поэтому результат запроса будет упорядочен в соответствии с теми или иными предложениями PARTITION BY/ORDER BY оконных функций. Однако полагаться на это не стоит. Если хотите добиться определенного порядка сортировки результатов, явно используйте предложение ORDER BY на верхнем уровне запроса.

Списки выборки

Как показано в предыдущем подразделе, табличное выражение в команде SELECT создает промежуточную виртуальную таблицу, возможно, объединяя таблицы, представления, исключая строки, группируя и т. д. В итоге эта таблица передается на обработку в список выборки. Этот список определяет, какие столбцы промежуточной таблицы фактически должны выводиться.

Элементы списка выборки

Простейшим списком выборки является элемент *, который выбирает все столбцы, выдаваемые табличным выражением. В других случаях список выборки представляет собой список выражений значения через запятую (как определено в разделе Выражения значения). Например, это может быть список имен столбцов:

SELECT a, b, c FROM ...

Имена столбцов a, b и c являются либо фактическими именами столбцов таблиц, указанных в предложении FROM, либо их псевдонимами, заданными как описано в подразделе Псевдонимы таблиц и столбцов. Пространство имен, доступное в списке выборки, то же, что и в предложении WHERE, если не используется группировка. В последнем случае оно совпадает с пространством имен в предложении HAVING.

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

SELECT tbl1.a, tbl2.a, tbl1.b FROM ...

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

SELECT tbl1.*, tbl2.a FROM ...

Подробную информацию о записи имя_таблицы.* см. в разделе Использование составных типов в запросах.

Если в списке выборки используется произвольное выражение значения, оно по сути добавляет в возвращаемую таблицу новый виртуальный столбец. Выражение значения вычисляется один раз для каждой результирующей строки; при этом все ссылки столбца заменяются значениями строки. Но выражения в списке выборки не обязательно должны обращаться к каким-либо столбцам табличного выражения из предложения FROM; они могут быть, к примеру, константными арифметическими выражениями.

Метки столбцов

Записям в списке выборки можно назначить имена для последующей обработки, например, для использования в предложении ORDER BY clause или для отображения в клиентском приложении. Например:

SELECT a AS value, b + c AS sum FROM ...

Если после AS не указано выходное имя столбца, система назначает ему имя по умолчанию. Для простых ссылок на столбцы это будет имя ссылочного столбца, а для вызовов функций — имя функции. Для сложных выражений система генерирует некое общее имя.

Ключевое слово AS обычно можно не указывать, но в некоторых случаях, когда желаемое имя столбца совпадает с ключевым словом QHB, следует написать AS или заключить имя столбца в кавычки, чтобы избежать неоднозначности. (В приложении Ключевые слова SQL перечислены ключевые слова, для которых нужно использовать AS в качестве метки столбца.) Например, FROM — ключевое слово, поэтому такая запись не будет работать:

SELECT a from, b + c AS sum FROM ...

а любая из этих сработает:

SELECT a AS from, b + c AS sum FROM ...
SELECT a "from", b + c AS sum FROM ...

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

Примечание
Именование выходных столбцов здесь отличается от того, что происходит в предложении FROM (см. подраздел Псевдонимы таблиц и столбцов). Один и тот же столбец можно переименовать дважды, но передастся то имя, которое было назначено в списке выборки.

DISTINCT

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

SELECT DISTINCT список_выборки ...

(Чтобы задать поведение по умолчанию, когда оставляются все строки, вместо DISTINCT можно указать ключевое слово ALL.)

Разумеется, две строки считаются разными, если они содержат различные значения как минимум в одном столбце. При таком сравнении значения NULL рассматриваются как равные.

Кроме того, определить, какие строки будут считаться различными, можно с помощью произвольного выражения:

SELECT DISTINCT ON (выражение [, выражение ...]) список_выборки ...

Здесь выражение — это произвольное выражение значения, вычисляемое для всех строк. Набор строк, для которых все эти выражения одинаковы, считаются дубликатами, и в выходных данных остается только первая строка из набора. Обратите внимание, что «первая строка» набора может быть любой, если только запрос не отсортирован по достаточному числу столбцов, чтобы гарантировать однозначный порядок строк, поступающих в фильтр DISTINCT. (Обработка DISTINCT ON производится после сортировки ORDER BY.)

Предложение DISTINCT ON не включено в стандарт SQL и иногда его применение считается дурным тоном из-за потенциальной непредсказуемости его результатов. При разумном использовании GROUP BY и подзапросов во FROM можно обойтись без этой конструкции, но зачастую она является наиболее удобной альтернативой.

Сочетание запросов (UNION, INTERSECT, EXCEPT)

Результаты двух запросов можно обрабатывать комплексно, используя операции над множествами: объединение, пересечение и вычитание. Синтаксис при этом следующий:

запрос1 UNION [ALL] запрос2
запрос1 INTERSECT [ALL] запрос2
запрос1 EXCEPT [ALL] запрос2

где запрос1 и запрос2 — это запросы, в которых могут использоваться все функциональные возможности, рассмотренные ранее.

UNION по сути добавляет результат запроса2 к результату запроса1 (хотя нет гарантии, что строки возвращаются именно в таком порядке). Более того, при этом из результата убираются все дублирующиеся строки, тем же способом, как это делает DISTINCT, если только не указано UNION ALL.

INTERSECT возвращает все строки, содержащиеся как в результате запроса1, так и в результате запроса2. Дублирующиеся строки убираются, если только не указано INTERSECT ALL.

EXCEPT возвращает все строки, содержащиеся в результате запроса1, но отсутствующие в результате запроса2. (Иногда это называется разницей двух запросов.) Опять же, дублирующиеся строки убираются, если только не указано EXCEPT ALL.

Чтобы вычислить объединение, пересечение или разницу двух запросов, эти два запроса должны быть «совместимыми для объединения», что означает, что они возвращают одинаковое количество столбцов и у соответствующих столбцов совместимые типы данных.

Операции над множествами можно сочетать, например:

запрос1 UNION запрос2 UNION запрос3

что равнозначно

(запрос1 UNION запрос2) UNION запрос3

Как показано выше, для управления порядком вычисления можно использовать скобки. Без скобок UNION и EXCEPT объединяются слева направо, но INTERSECT имеет больший приоритет, чем эти два оператора. Таким образом,

запрос1 UNION запрос2 INTERSECT запрос3

означает

запрос1 UNION (запрос2 INTERSECT запрос3)

Также можно заключить в скобки отдельный запрос. Это важно, если в запросе необходимо использовать любое из предложений, рассматриваемых в следующих разделах, например LIMIT. Без скобок возникнет синтаксическая ошибка или предложение будет восприниматься как применяющееся к результату операции над множествами, на не к одному из ее аргументов. Например,

SELECT a FROM b UNION SELECT x FROM y LIMIT 10

допустимо, но означает

(SELECT a FROM b UNION SELECT x FROM y) LIMIT 10

а не

SELECT a FROM b UNION (SELECT x FROM y LIMIT 10)

Сортировка строк (ORDER BY)

После того, как запрос выдал выходную таблицу (после обработки списка выборки), при необходимости ее можно отсортировать. Если сортировка не выбрана, строки вернутся в неопределенном порядке. В этом случае фактический порядок строк будет зависеть от вида плана сканирования и соединения, а также от порядка данных на диске, но на него не следует полагаться. Определенный порядок выводимых строк гарантируется, только если этап сортировки задан явно.

Порядок сортировки задает предложение ORDER BY:

SELECT список_выборки
    FROM табличное выражение
    ORDER BY выражение_сортировки1 [ASC | DESC] [NULLS { FIRST | LAST }]
           [, выражение_сортировки2 [ASC | DESC] [NULLS { FIRST | LAST }] ...]

Выражениями сортировки могут быть любые выражения, допустимые в списке выборки запроса. Например:

SELECT a, b FROM table1 ORDER BY a + b, c;

Когда задается более одного выражения, последующие значения используются для сортировки строк, в которых совпали предыдущие значения. После каждого выражения при необходимости можно указать ключевое слово ASC или DESC, которые устанавливают направление сортировки по возрастанию или убыванию соответственно. По умолчанию установлено ASC. При сортировке по возрастанию сначала идут меньшие значения, где понятие «меньшие» определяется в контексте оператора <. Аналогично сортировка по убыванию определяется оператором >. ¹

Для определения того, должны ли при сортировке значения NULL располагаться до или после отличных от NULL значений, можно применить параметры NULLS FIRST и NULLS LAST соответственно. По умолчанию значения NULL считаются больше всех остальных; то есть NULLS FIRST по умолчанию применяется для порядка DESC и NULLS LAST в противном случае.

Обратите внимание, что параметры сортировки рассматриваются независимо для каждого сортируемого столбца. Например, ORDER BY x, y DESC означает ORDER BY x ASC, y DESC, и это не то же самое, что ORDER BY x DESC, y DESC.

выражение_сортировки также может быть меткой столбца или номером выводимого столбца, как в данном примере:

SELECT a + b AS sum, c FROM table1 ORDER BY sum;
SELECT a, max(b) FROM table1 GROUP BY a ORDER BY 1;

где оба запроса сортируют результат по первому выводимому столбцу. Обратите внимание, что имя выводимого столбца должно оставаться само по себе, то есть его нельзя использовать в выражении — например, эта запись не корректна:

SELECT a + b AS sum, c FROM table1 ORDER BY sum + c;

Это ограничение введено для уменьшения неоднозначности. Тем не менее, неоднозначность возможна, если элементом ORDER BY является простое имя, которое может соответствовать либо имени выводимого столбца, либо столбцу из табличного выражения. В таких случаях используется выводимый столбец. Это может вызвать путаницу, только если переименовать выводимый столбец с помощью AS, чтобы его имя соответствовало имени столбца в какой-то другой таблице.

ORDER BY можно применить к результату сочетания UNION, INTERSECT и EXCEPT, однако в этом случае сортировка разрешена только по именам или номерам выводимых столбцов, но не по выражениям.

LIMIT и OFFSET

LIMIT и OFFSET позволяют получить только часть строк из тех, что сгенерировал остальной запрос:

SELECT список_выборки
    FROM табличное_выражение
    [ ORDER BY ... ]
    [ LIMIT { число | ALL } ] [ OFFSET число ]

Если задано предельное число (LIMIT), вернется не больше этого заданного числа строк (возможно, и меньше, если сам запрос выдает меньшее число строк). LIMIT ALL равнозначно исключению предложения LIMIT, как и LIMIT с аргументом NULL.

OFFSET указывает пропустить заданное число строк, прежде чем начать возвращать строки. OFFSET 0 равнозначно исключению предложения OFFSET, как и OFFSET с аргументом NULL.

Если в запросе содержатся и OFFSET, и LIMIT, то сначала система пропускает OFFSET строк, а затем начинает подсчитывать LIMIT строк, которые будут возвращены.

При использовании LIMIT важно применять также предложение ORDER BY, выстраивающее результирующие строки в определенном порядке. Иначе вы получите непредсказуемые подмножества строк запроса. Можно запросить строки с десятой по двадцатую, но в каком порядке они будут выданы? Порядок будет неизвестен, если не указать ORDER BY.

Оптимизатор запросов учитывает LIMIT при генерировании планов запросов, поэтому, скорее всего, в зависимости от значений LIMIT и OFFSET вы получите разные планы (выдающие строки в разном порядке). Таким образом, использование разных значений LIMIT/OFFSET для выбора разных подмножеств результата запроса приведет к несогласованности результатов, если не установить предсказуемую сортировку результатов с помощью ORDER BY. Это не ошибка, а закономерное следствие того, что SQL не гарантирует вывод результатов запроса в каком-либо определенном порядке, если этот порядок не навязан предложением ORDER BY.

Строки, пропускаемые согласно предложению OFFSET, все равно должны вычисляться на сервере; поэтому при больших значениях OFFSET может быть неэффективен.

Списки VALUES

VALUES дает возможность сгенерировать «константную таблицу», которую можно использовать в запросе без необходимости действительно создавать и наполнять таблицу на диске. Синтаксис этой команды таков:

VALUES ( выражение [, ...] ) [, ...]

Для каждого списка выражений в скобках создается строка в таблице. Во всех этих списках должно быть одинаковое число элементов (т. е. число столбцов в таблице) и соответствующие записи в каждом списке должны иметь совместимые типы данных. Фактический тип данных, назначенный каждому столбцу результата, определяется по тем же правилам, что и для UNION.

Например:

VALUES (1, 'one'), (2, 'two'), (3, 'three');

вернет таблицу из двух столбцов и трех строк. Это равнозначно следующему запросу:

SELECT 1 AS column1, 'one' AS column2
UNION ALL
SELECT 2, 'two'
UNION ALL
SELECT 3, 'three';

По умолчанию QHB назначает столбцам таблицы VALUES имена column1, column2 и т. д. Имена столбцов не определены в стандарте SQL и в других СУБД они могут быть другими, поэтому обычно лучше переопределять имена по умолчанию списком псевдонимов таблицы, например так:

=> SELECT * FROM (VALUES (1, 'one'), (2, 'two'), (3, 'three')) AS t (num,letter);
 num | letter
-----+--------
   1 | one
   2 | two
   3 | three
(3 rows)

Синтаксически команда VALUES с последующим списком выражений воспринимается как равнозначная этой записи:

SELECT список_выборки FROM табличное_выражение

и допускается везде, где допустим SELECT. К примеру, ее можно использовать как часть UNION или добавить к ней определение_сортировки (ORDER BY, LIMIT и/или OFFSET). Чаще всего VALUES используется как источник данных в команде INSERT либо в качестве подзапроса.

Дополнительную информацию см. в описании команды VALUES.

Запросы WITH (общие табличные выражения)

WITH дает возможность записывать вспомогательные операторы для применения в более крупных запросах. Эти операторы, также часто называемые общими табличными выражениями (Common Table Expressions, CTE), можно представить как определения временных таблиц, которые существуют только для одного запроса. Каждый вспомогательный оператор в предложении WITH может быть SELECT, INSERT, UPDATE или DELETE, а само предложение WITH присоединяется к основному оператору, которым также может быть SELECT, INSERT, UPDATE или DELETE.

SELECT в WITH

Основное назначение SELECT в WITH заключается в разбиении сложных запросов на более простые части. Например, запрос:

WITH regional_sales AS (
    SELECT region, SUM(amount) AS total_sales
    FROM orders
    GROUP BY region
), top_regions AS (
    SELECT region
    FROM regional_sales
    WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
)
SELECT region,
       product,
       SUM(quantity) AS product_units,
       SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;

отображает итоги по продажам каждого продукта только для регионов, лидирующих по продажам. Предложение WITH определяет два вспомогательных оператора, именуемых regional_sales и top_regions, где выходные данные regional_sales используются в top_regions, а выходные данные top_regions — в основном запросе SELECT. Этот пример можно было бы записать и без WITH, но тогда бы понадобилось два уровня вложенных подзапросов SELECT. Способ, показанный выше, немного проще.

Рекурсивные запросы

Необязательный модификатор RECURSIVE превращает WITH из просто удобной синтаксической конструкции в средство достижения того, что иным способом невозможно в стандартном SQL. Используя RECURSIVE, запрос WITH может обращаться к собственным выходным данным. Очень простой пример, суммирующий целые числа от 1 до 100:

WITH RECURSIVE t(n) AS (
    VALUES (1)
  UNION ALL
    SELECT n+1 FROM t WHERE n < 100
)
SELECT sum(n) FROM t;

В общем виде рекурсивный запрос WITH всегда является не рекурсивной частью, потом идет UNION (или UNION ALL), потом рекурсивная часть, где только в рекурсивной части можно обратиться к результату этого же запроса. Подобный запрос выполняется следующим образом:

Вычисление рекурсивного запроса

Вычисляется не рекурсивная часть. Для UNION (но не UNION ALL) отбрасываются дублирующиеся строки. Все оставшиеся строки включаются в результат рекурсивного запроса, а также помещаются во временную рабочую таблицу.

Пока рабочая таблица не опустеет, повторяются следующие этапы:

a. Вычисляется рекурсивная часть, заменяя текущее содержимое рабочей таблицы рекурсивными ссылками на этот же запрос. Для UNION (но не UNION ALL) отбрасываются дублирующиеся строки и строки, дублирующие предыдущие результирующие строки. Все оставшиеся строки включаются в результат рекурсивного запроса, а также помещаются во временную промежуточную таблицу.

b. Содержимое рабочей таблицы заменяется содержимым промежуточной таблицы, а затем промежуточная таблица опустошается.

Примечание
Строго говоря, этот процесс является итерацией, а не рекурсией, но комитетом по стандартам SQL был выбран термин RECURSIVE.

В показанном выше примере рабочая таблица на каждом этапе содержит всего одну строку, и в ней последовательно набираются значения от 1 до 100. На сотом этапе, благодаря предложению WHERE, не выводится ничего, поэтому запрос завершается.

Рекурсивные запросы обычно используются для работы с иерархическими данными или древовидными структурами данных. В качестве полезного примера можно привести этот запрос, который находит все непосредственные и косвенные части продукта, используя только таблицу, показывающую непосредственные составляющие:

WITH RECURSIVE included_parts(sub_part, part, quantity) AS (
    SELECT sub_part, part, quantity FROM parts WHERE part = 'our_product'
  UNION ALL
    SELECT p.sub_part, p.part, p.quantity
    FROM included_parts pr, parts p
    WHERE p.part = pr.sub_part
)
SELECT sub_part, SUM(quantity) as total_quantity
FROM included_parts
GROUP BY sub_part

Порядок поиска

При вычислении обхода дерева при помощи рекурсивного запроса может возникнуть необходимость отсортировать результаты в порядке «сначала в глубину» или «сначала в ширину». Для этого можно в дополнение к остальным столбцам данных вычислить упорядочивающий столбец и использовать его для сортировки полученных результатов. Обратите внимание, что это на самом деле не определяет порядок обхода строк запросом; этот порядок, как всегда, зависит от реализации SQL. Такой столбец просто предоставляет удобный способ упорядочить полученные результаты.

Чтобы отсортировать результаты в порядке «сначала в глубину», для каждой результирующей строки вычисляется массив уже просмотренных строк. Например, рассмотрим следующий запрос, выполняющий поиск в таблице tree по полю link:

WITH RECURSIVE search_tree(id, link, data) AS (
    SELECT t.id, t.link, t.data
    FROM tree t
  UNION ALL
    SELECT t.id, t.link, t.data
    FROM tree t, search_tree st
    WHERE t.id = st.link
)
SELECT * FROM search_tree;

Чтобы добавить информацию для упорядочивания «сначала в глубину», можно написать следующее:

WITH RECURSIVE search_tree(id, link, data, path) AS (
    SELECT t.id, t.link, t.data, ARRAY[t.id]
    FROM tree t
  UNION ALL
    SELECT t.id, t.link, t.data, path || t.id
    FROM tree t, search_tree st
    WHERE t.id = st.link
)
SELECT * FROM search_tree ORDER BY path;

В общем случае, когда для идентификации строки нужно использовать более одного поля, следует использовать массив строк. Например, если требуется отследить поля f1 и f2:

WITH RECURSIVE search_tree(id, link, data, path) AS (
    SELECT t.id, t.link, t.data, ARRAY[ROW(t.f1, t.f2)]
    FROM tree t
  UNION ALL
    SELECT t.id, t.link, t.data, path || ROW(t.f1, t.f2)
    FROM tree t, search_tree st
    WHERE t.id = st.link
)
SELECT * FROM search_tree ORDER BY path;

Совет
В этом общем случае, где нужно отслеживать только одно поле, элемент ROW() можно опустить. Это позволит использовать не массив составного типа, а простой массив, повысив тем самым эффективность.

Чтобы отсортировать результаты в порядке «сначала в ширину», можно добавить столбец, отслеживающий глубину поиска, например:

WITH RECURSIVE search_tree(id, link, data, depth) AS (
    SELECT t.id, t.link, t.data, 0
    FROM tree t
  UNION ALL
    SELECT t.id, t.link, t.data, depth + 1
    FROM tree t, search_tree st
    WHERE t.id = st.link
)
SELECT * FROM search_tree ORDER BY depth;

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

Совет
Этот алгоритм вычисления рекурсивного запроса выдает свой результат в порядке поиска «сначала в ширину». Однако это особенность реализации, и, вероятно, полагаться на него неразумно. Порядок строк внутри каждого уровня, разумеется, не определен, поэтому в любом случае может понадобиться явное упорядочивание.

Для вычисления столбца, упорядочивающего «сначала в глубину» или «сначала в ширину» существует встроенный синтаксис. Например:

WITH RECURSIVE search_tree(id, link, data) AS (
    SELECT t.id, t.link, t.data
    FROM tree t
  UNION ALL
    SELECT t.id, t.link, t.data
    FROM tree t, search_tree st
    WHERE t.id = st.link
) SEARCH DEPTH FIRST BY id SET ordercol
SELECT * FROM search_tree ORDER BY ordercol;

WITH RECURSIVE search_tree(id, link, data) AS (
    SELECT t.id, t.link, t.data
    FROM tree t
  UNION ALL
    SELECT t.id, t.link, t.data
    FROM tree t, search_tree st
    WHERE t.id = st.link
) SEARCH BREADTH FIRST BY id SET ordercol
SELECT * FROM search_tree ORDER BY ordercol;

Внутри этот синтаксис разворачивается в форму, похожую на вышеописанные. В предложении SEARCH указывается, какой тип поиска требуется — «сначала в глубину» или «сначала в ширину», список столбцов, которые будут отслеживаться для сортировки, и имя столбца, который будет содержать результирующие данные, которые можно использовать для сортировки. Этот столбец будет неявно добавлен в выходные строки CTE.

Выявление циклов

Работая с рекурсивными запросами, важно позаботиться о том, чтобы рекурсивная часть запроса в итоге не возвратила никаких кортежей, иначе этот запрос превратится в бесконечный цикл. Иногда, воспользовавшись UNION вместо UNION ALL, этого можно добиться, отбрасывая строки, дублирующие предыдущие выходные строки. Однако зачастую в цикле нет полностью совпадающих выходных строк: в таких случаях может понадобиться проверить одно или несколько полей, чтобы увидеть, не была ли эта же точка достигнута раньше. Стандартный метод решения таких задач — вычислить массив уже просмотренных значений. Например, снова рассмотрим следующий запрос, просматривающий таблицу graph по полю link:

WITH RECURSIVE search_graph(id, link, data, depth) AS (
    SELECT g.id, g.link, g.data, 1
    FROM graph g
  UNION ALL
    SELECT g.id, g.link, g.data, sg.depth + 1
    FROM graph g, search_graph sg
    WHERE g.id = sg.link
)
SELECT * FROM search_graph;

Этот запрос зациклится, если отношения link содержат циклы. Поскольку нам нужно получить данные, упорядоченные «в ширину», простое изменение UNION ALL на UNION не уберет угрозу зацикливания. Вместо этого нам необходимо определить, достигали ли мы уже этой же строки, пройдя некоторый путь из ссылок. Мы добавляем два столбца, is_cycle и path, в склонный к зацикливанию запрос:

WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (
    SELECT g.id, g.link, g.data, 1,
      ARRAY[g.id],
      false
    FROM graph g
  UNION ALL
    SELECT g.id, g.link, g.data, sg.depth + 1,
      path || g.id,
      g.id = ANY(path)
    FROM graph g, search_graph sg
    WHERE g.id = sg.link AND NOT cycle
)
SELECT * FROM search_graph;

Помимо предотвращения циклов, значение массива часто бывает полезно само по себе как представление «пути», приведшего к определенной строке.

В общем случае, когда для выявления цикла нужно проверить более одного поля, следует использовать массив строк. Например, если нужно сравнить поля f1 и f2:

WITH RECURSIVE search_graph(id, link, data, depth, path, cycle) AS (
    SELECT g.id, g.link, g.data, 1,
      ARRAY[ROW(g.f1, g.f2)],
      false
    FROM graph g
  UNION ALL
    SELECT g.id, g.link, g.data, sg.depth + 1,
      path || ROW(g.f1, g.f2),
      ROW(g.f1, g.f2) = ANY(path)
    FROM graph g, search_graph sg
    WHERE g.id = sg.link AND NOT cycle
)
SELECT * FROM search_graph;

Совет
В этом общем случае, где для выявления цикла нужно проверять только одно поле, элемент ROW() можно опустить. Это позволит использовать не массив составного типа, а простой массив, повысив тем самым эффективность.

Для упрощения выявления циклов существует встроенный синтаксис. Приведенный выше запрос можно также записать следующим образом:

WITH RECURSIVE search_graph(id, link, data, depth) AS (
    SELECT g.id, g.link, g.data, 1
    FROM graph g
  UNION ALL
    SELECT g.id, g.link, g.data, sg.depth + 1
    FROM graph g, search_graph sg
    WHERE g.id = sg.link
) CYCLE id SET is_cycle USING path
SELECT * FROM search_graph;

и внутри он будет переписан в вышеуказанную форму. В предложении CYCLE сначала задается список столбцов для отслеживания на предмет выявления цикла, потом имя столбца, который покажет, был ли выявлен цикл, и наконец, имя еще одного столбца, в котором будет отслеживаться путь. Столбцы, указывающие цикл и путь, будут неявно добавлены в выходные строки CTE.

Совет
Столбец, указывающий путь цикла, вычисляется тем же способом, что и столбец для упорядочивания «сначала в глубину», показанным в предыдущем подразделе. Запрос может содержать одновременно предложения SEARCH и CYCLE, но при этом будут производиться излишние вычисления, поэтому эффективнее использовать только предложение CYCLE и упорядочивать результаты по столбцу пути. Однако если требуется порядок «сначала в ширину», одновременное указание SEARCH и CYCLE может быть полезным.

Для тестирования запросов в отсутствие уверенности, что они могут зациклиться, есть полезный прием — поместить LIMIT в родительский запрос. Например, этот запрос войдет в бесконечный цикл, если не добавить LIMIT:

WITH RECURSIVE t(n) AS (
    SELECT 1
  UNION ALL
    SELECT n+1 FROM t
)
SELECT n FROM t LIMIT 100;

Но в данном случае этого не произойдет, потому что реализация QHB вычисляет ровно столько строк запроса WITH, сколько фактически выбрал родительский запрос. В производственной среде использовать этот прием не рекомендуется, так как другие системы могут работать по-другому. Кроме того, это обычно не работает, если внешний запрос сортирует результаты рекурсивного запроса или соединяет их с какой-нибудь другой таблицей, потому что в подобных случаях внешний запрос обычно все равно пытается выбрать результат запроса WITH полностью.

Материализация общих табличных выражений

У запросов WITH есть полезное свойство — обычно при выполнении всего родительского запроса они вычисляются только один раз, даже если этот запрос или соседние запросы WITH обращаются к ним неоднократно. Таким образом, затратные вычисления, которые требуются в нескольких местах, можно поместить в запрос WITH и избежать лишней работы. Другое возможное применение заключается в предотвращении нежелательных многократных вычислений функций с побочными эффектами. Однако обратная сторона медали заключается в том, что оптимизатор не может распространить ограничения родительского запроса на многократно задействуемый запрос WITH, поскольку это может повлиять на все эпизоды использования выходных данных запроса WITH, тогда как должно повлиять только на один. Многократно задействуемый запрос WITH будет вычисляться буквально, не пропуская даже те строки, которые потом может сбросить родительский запрос. (Но, как упоминалось выше, вычисление может остановиться раньше, если в ссылке на запрос будет затребовано только ограниченное число строк.)

Однако если запрос WITH не рекурсивный и свободен от побочных эффектов (то есть это SELECT, не содержащий изменчивых функций), то его можно свернуть в родительский запрос, что позволит совместно оптимизировать два уровня запросов. По умолчанию это происходит, если родительский запрос обращается к запросу WITH только один раз, но не при многократных обращениях. Это решение можно переопределить, указав MATERIALIZED, чтобы принудительно отделить вычисление запроса WITH, или NOT MATERIALIZED, чтобы присовокупить его к родительскому запросу. В последнем случае возникает риск дублирования вычисления запроса WITH, но это все равно может быть выгодно, если при каждом использовании запроса WITH требуется только небольшая часть из его полного результата.

Простой пример демонстрации этих правил:

WITH w AS (
    SELECT * FROM big_table
)
SELECT * FROM w WHERE key = 123;

Этот запрос WITH будет свернут, выполняя тот же план, что и этот запрос:

SELECT * FROM big_table WHERE key = 123;

В частности, если был создан индекс по столбцу key, вероятно, он будет использован для извлечения строк с key = 123. С другой стороны, в записи

WITH w AS (
    SELECT * FROM big_table
)
SELECT * FROM w AS w1 JOIN w AS w2 ON w1.key = w2.ref
WHERE w2.key = 123;

запрос WITH будет материализован, создавая временную копию таблицы big_table, которая затем будет соединена сама с собой — без участия какого-либо индекса. Этот запрос будет выполняться гораздо эффективнее, если записать его в следующем виде:

WITH w AS NOT MATERIALIZED (
    SELECT * FROM big_table
)
SELECT * FROM w AS w1 JOIN w AS w2 ON w1.key = w2.ref
WHERE w2.key = 123;

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

Пример, в котором NOT MATERIALIZED может быть нежелателен:

WITH w AS (
    SELECT key, very_expensive_function(val) as f FROM some_table
)
SELECT * FROM w AS w1 JOIN w AS w2 ON w1.f = w2.f;

Здесь благодаря материализации запроса WITH очень затратная функция very_expensive_function вычисляется для строки таблицы только один раз, а не два.

Примеры выше показывают только WITH, применяемое с SELECT, но таким же образом его можно добавлять в команды INSERT, UPDATE или DELETE. В каждом случае он фактически предоставляет временную таблицу (или таблицы), к которой можно обращаться в основной команде.

Операторы изменения данных в WITH

В предложении WITH можно использовать операторы, изменяющие данные (INSERT, UPDATE или DELETE). Это позволяет выполнять в одном запросе сразу несколько различных операций. Например:

WITH moved_rows AS (
    DELETE FROM products
    WHERE
        "date" >= '2010-10-01' AND
        "date" < '2010-11-01'
    RETURNING *
)
INSERT INTO products_log
SELECT * FROM moved_rows;

Этот запрос по сути перемещает строки из products в products_log. Оператор DELETE в WITH удаляет указанные строки из products, возвращая их содержимое посредством своего предложения RETURNING, а затем главный запрос читает эти данные и вставляет их в products_log.

В примере выше есть один нюанс — предложение WITH присоединяется к оператору INSERT, а не к SELECT, вложенному в INSERT. Это необходимо, поскольку операторы, изменяющие данные, допустимы только в предложениях WITH, присоединяемых к оператору верхнего уровня. Однако при этом применяются обычные правила видимости WITH, так что к результату оператора с WITH можно обратиться и из вложенного SELECT.

У операторов, изменяющих данные, в WITH обычно имеются предложения RETURNING (см. раздел Возврат данных из измененных строк), как показано в примере выше. Именно из результата предложения RETURNING, а не целевой таблицы оператора, изменяющего данные, формируется временная таблица, к которой можно будет обращаться в остальном запросе. Если у оператора, изменяющего данные, в WITH отсутствует предложение RETURNING, то временная таблица не создается и к ней нельзя будет обратиться в остальном запросе. Однако такой оператор все равно будет выполнен. Не особенно практичный пример:

WITH t AS (
    DELETE FROM foo
)
DELETE FROM bar;

Запрос в этом примере удалит все строки из таблиц foo и bar. В число обработанных строк, о котором будет сообщено клиенту, войдут только строки, удаленные из bar.

Рекурсивные ссылки в операторах, изменяющих данные, не допускаются. В некоторых случаях это ограничение можно обойти, обратившись к выходным данным рекурсивного WITH, например:

WITH RECURSIVE included_parts(sub_part, part) AS (
    SELECT sub_part, part FROM parts WHERE part = 'our_product'
  UNION ALL
    SELECT p.sub_part, p.part
    FROM included_parts pr, parts p
    WHERE p.part = pr.sub_part
)
DELETE FROM parts
  WHERE part IN (SELECT part FROM included_parts);

Этот запрос удалит все непосредственные и косвенные составные части продукта.

Операторы, изменяющие данные, в WITH выполняются ровно один раз и всегда полностью, независимо от того, прочитает ли основной запрос их результат полностью или хотя бы частично. Обратите внимание, что это отличается от правила для SELECT в WITH: как говорилось в предыдущем разделе, SELECT выполняется только до тех пор, пока основному запросу требуются его его выходные данные.

Вложенные операторы в WITH выполняются одновременно друг с другом и с основным запросом. Таким образом, при использовании в WITH операторов, изменяющих данные, порядок, в котором указанные изменения фактически происходят, непредсказуем. Все эти операторы выполняются с одним снимком данных (см. главу Управление параллельным доступом), так что они не могут «видеть», как каждый из них воздействует на целевые таблицы. Это сглаживает эффекты непредсказуемости фактического порядка изменения строк и означает, что данные из RETURNING являются единственным способом передачи изменений от различных вложенных операторов WITH основному запросу. Например, в данном случае:

WITH t AS (
    UPDATE products SET price = price * 1.05
    RETURNING *
)
SELECT * FROM products;

внешний SELECT вернет оригинальные цены, которые были до действия UPDATE, тогда как в запросе

WITH t AS (
    UPDATE products SET price = price * 1.05
    RETURNING *
)
SELECT * FROM t;

внешний SELECT вернет измененные данные.

Изменение одной и той же строки дважды в рамках одного оператора не поддерживается. Произойдет только одно из изменений, но надежно предсказать, какое именно, нелегко (а иногда и вовсе невозможно). Это же касается удаления строки, которая уже была изменена в том же операторе: выполнится только изменение. Поэтому следует избегать попыток изменить строку дважды в одном оператора. В частности, следует избегать добавления вложенных операторов WITH, которые могут воздействовать на строки, изменяемые основным оператором или соседними вложенными операторами. Результат действия такого оператора будет непредсказуем.

В настоящее время для целевой таблицы оператора, изменяющего данные в WITH, не должно быть определено условное правило или правило ALSO или INSTEAD, распространяющееся на несколько операторов.

¹

На самом деле, для определения порядка сортировки для ASC и DESC QHB использует класс операторов B-дерева по умолчанию. Обычно типы данных устанавливаются так, чтобы этому порядку сортировки соответствовали операторы < и >, однако можно разработать собственный тип данных с иным поведением.

Типы данных

Числовые типы

Денежные типы

Символьные типы

Двоичные типы данных

Типы даты/времени

Логический тип

Перечислимые типы

Геометрические типы

Типы сетевых адресов

Типы битовых строк

Типы текстового поиска

Тип UUID

Тип XML

Типы JSON

Массивы

Составные типы

Диапазонные типы

Типы доменов

Типы идентификаторов объектов

Тип pg_lsn

Псевдо-типы

QHB имеет богатый набор собственных типов данных, доступных пользователям. Пользователи могут добавлять новые типы в QHB с помощью команды CREATE TYPE.

В таблице 1 показаны все встроенные типы данных общего назначения. Большинство альтернативных имен, перечисленных в столбце «Псевдонимы», являются именами, используемыми внутри QHB по историческим причинам. Кроме того, доступны некоторые используемые или устаревшие типы, но они не перечислены здесь.

Таблица 1. Типы данных

Имя | Псевдонимы | Описание
bigint | int8 | восьмибайтное целое со знаком
bigserial | serial8 | автоинкрементное восьмибайтное целое число
bit [ (n) ] |  | битовая строка фиксированной длины
bit varying [ (n) ] | varbit [ (n) ] | битовая строка переменной длины
boolean | bool | логическое значение (true/false)
box |  | прямоугольник в плоскости
bytea |  | двоичные данные («байтовый массив»)
character [ (n) ] | char [ (n) ] | символьная строка фиксированной длины
character varying [ (n) ] | varchar [ (n) ] | символьная строка переменной длины
cidr |  | сетевой адрес IPv4 или IPv6
circle |  | круг в плоскости
date |  | календарная дата (год, месяц, день)
double precision | float8 | число с плавающей запятой двойной точности (8 байт)
inet |  | адрес хоста IPv4 или IPv6
integer | int, int4 | четырехбайтное целое со знаком
interval [ поля ] [ (p) ] |  | временной интервал
json |  | текстовые данные JSON
jsonb |  | двоичные данные JSON, разложенные
line |  | бесконечная линия в плоскости
lseg |  | отрезок в плоскости
macaddr |  | MAC (Media Access Control) адрес
macaddr8 |  | MAC (Media Access Control) адрес (в формате EUI-64)
mchar [ (n) ] |  | символьная строка фиксированной длины, совместимая с Microsoft SQL Server
mvarchar [ (n) ] |  | символьная строка переменной длины, совместимая с Microsoft SQL Server
money |  | денежная сумма
numeric [ (*p, s) ] | decimal [ (p, s) ] | точное числовое выбираемой точности
path |  | геометрический путь в плоскости
pg_lsn |  | регистрационный номер транзакции в журнале (LSN) QHB
point |  | геометрическая точка в плоскости
polygon |  | замкнутый геометрический путь в плоскости
real | float4 | число одинарной точности с плавающей запятой (4 байта)
smallint | int2 | двухбайтное целое со знаком
smallserial | serial2 | двухбайтное целое число с автоприращением
serial | serial4 | четырехбайтное целое число с автоприращением
text |  | символьная строка переменной длины
time [ (p) ] [ without time zone ] |  | время суток (без часового пояса)
time [ (p) ] with time zone | timetz | время суток с учетом часового пояса
timestamp [ (p) ] [ without time zone ] |  | дата и время (без часового пояса)
timestamp [ (p) ] with time zone | timestamptz | дата и время с учетом часового пояса
tsquery |  | запрос текстового поиска
tsvector |  | документ для текстового поиска
txid_snapshot |  | снимок идентификатора транзакции на уровне пользователя
uuid |  | универсально уникальный идентификатор
xml |  | XML-данные

Совместимость
В стандарте SQL описаны следующие типы (или их имена): bigint, bit, bit varying, boolean, char, character varying, character, varchar, *date, double precision, integer, interval, numeric, decimal, real, smallint, time (с часовым поясом и без), timestamp (с часовым поясом и без), xml.

Каждый тип данных имеет внешнее представление, определяемое его функциями ввода и вывода. Многие из встроенных типов имеют очевидные внешние форматы. Однако некоторые типы являются уникальными для QHB, например геометрические пути, или имеют несколько возможных форматов, таких как типы даты и времени. Некоторые из функций ввода и вывода не являются обратимыми, т. е. результат функции вывода может потерять точность и не совпадать с исходным входным значением.

Числовые типы

Числовые типы включают в себя двух-, четырех- и восьмибайтные целые числа, четырех- и восьмибайтные числа с плавающей запятой и десятичные числа с выбираемой точностью. В таблице 2 перечислены доступные типы.

Таблица 2. Числовые типы

Имя | Размер | Описание | Диапазон
smallint | 2 байта | целое число малого диапазона | от -32768 до +32767
integer | 4 байта | типичный выбор для целого числа | от -2147483648 до +2147483647
bigint | 8 байт | большое целое число | от -9223372036854775808 до +9223372036854775807
decimal | переменная | указанная пользователем точность, точная | до 131072 цифр перед десятичной запятой и до 16383 цифр — после
numeric | переменная | указанная пользователем точность, точная | до 131072 цифр перед десятичной запятой и до 16383 цифр — после
real | 4 байта | переменная точность, приближенная | точность в пределах 6 десятичных цифр
double precision | 8 байт | переменная точность, приближенная | точность в пределах 15 десятичных цифр
smallserial | 2 байта | небольшое целое число с автоприращением | от 1 до 32767
serial | 4 байта | целое число с автоприращением | от 1 до 2147483647
bigserial | 8 байт | большое целое число с автоприращением | от 1 до 9223372036854775807

Синтаксис констант для числовых типов описан в разделе Константы. Числовые типы имеют полный набор соответствующих арифметических операторов и функций. Дополнительную информацию см. в главе Функции и операторы. Подробнее эти типы описаны в следующих разделах.

Целочисленные типы

Типы smallint, integer и bigint хранят целые числа, то есть числа без дробной части, имеющих различные диапазоны. Попытки сохранить значения, выходящие за пределы допустимого диапазона, приведут к ошибке.

Тип integer является наиболее распространенным, поскольку он обеспечивает наилучший баланс между диапазоном, размером хранилища и производительностью. Тип smallint обычно используется только при нехватке объема дискового пространства. Тип bigint предназначен для тех случаев, когда диапазона типа integer недостаточно.

В SQL определены только целочисленные типы integer (или int), smallint и bigint. Имена типов int2, int4 и int8 являются расширениями, которые также используются некоторыми другими системами баз данных SQL.

Числа произвольной точности

Тип numeric может хранить числа с очень большим количеством цифр. Он особенно рекомендуется для хранения денежных сумм и других количественных величин, где требуется точность. Расчеты со значениями numeric дают точные результаты, где это возможно, например: сложение, вычитание, умножение. Однако операции со значениями numeric выполняются гораздо медленнее по сравнению с целочисленными типами или с типами с плавающей запятой, описываемыми в следующем разделе.

Ниже мы используем следующие термины: Точность значения numeric — это общее количество значимых цифр во всем числе, то есть количество цифр по обеим сторонам десятичной запятой. Масштаб значения numeric — это количество десятичных цифр в дробной части, справа от десятичной запятой. Таким образом, число 23,5141 имеет точность 6 и масштаб 4. Целые числа можно рассматривать как имеющие нулевой масштаб.

Для столбца numeric можно настроить как максимальную точность, так и максимальный масштаб. Чтобы объявить столбец типа numeric, используйте синтаксис:

NUMERIC(точность, масштаб)

Точность должна быть положительной, а масштаб — положительным или равным нулю. Альтернативный вариант:

NUMERIC(точность)

устанавливает масштаб 0. Форма:

NUMERIC

без указания точности или масштаба создает столбец, в котором можно сохранять числовые значения любой точности и масштаба, вплоть до предела реализации по точности. Столбец такого типа не будет приводить входные значения к какому-либо конкретному масштабу, тогда как столбцы numeric с объявленным масштабом будут приводить входные значения к этому масштабу. (Стандарт SQL требует, чтобы масштаб по умолчанию был равен 0, что соответствует приведению к целочисленной точности, но мы считаем это не очень полезным. Если вас заботит переносимость, всегда указывайте точность и масштаб явно.)

Примечание
Максимально допустимая точность, если она явно указана в объявлении типа, составляет 1000; NUMERIC без указанной точности подпадает под ограничения, описанные в таблице 2.

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

Числовые значения физически хранятся без каких-либо дополнительных нулей в начале или конце. Таким образом, заявленная точность и масштаб столбца определяют максимальный, а не фиксированный выделенный размер. (В этом смысле тип numeric больше похож на varchar(n) чем на char(n).) Фактическое требование к размеру хранения составляет два байта для каждой группы из четырех десятичных разрядов плюс дополнительные три-восемь байтов.

В дополнение к обычным числовым значениям, тип numeric допускает специальное значение NaN, означающее «не число». Любая операция с NaN в результате дает еще один NaN. При записи этого значения в качестве константы в команду SQL следует заключать его в кавычки, например, UPDATE table SET x = 'NaN'. При вводе строка NaN распознается без учета регистра.

Примечание
В большинстве реализаций концепции «не число» NaN не считается равным любому другому числовому значению (включая сам NaN). Чтобы разрешить сортировку и использование значений numeric в древовидных индексах, QHB рассматривает значения NaN как равные друг другу и превышающие все значения, отличные от NaN.

Типы decimal и numeric равнозначны. Оба типа являются частью стандарта SQL.

При округлении значений тип numeric выдает число, большее по модулю, в то время как (на большинстве машин) типы real и double precision выдают ближайшее четное число. Например:

SELECT x,
  round(x::numeric) AS num_round,
  round(x::double precision) AS dbl_round
FROM generate_series(-3.5, 3.5, 1) as x;
  x   | num_round | dbl_round
------+-----------+-----------
 -3.5 |        -4 |        -4
 -2.5 |        -3 |        -2
 -1.5 |        -2 |        -2
 -0.5 |        -1 |        -0
  0.5 |         1 |         0
  1.5 |         2 |         2
  2.5 |         3 |         2
  3.5 |         4 |         4
(8 rows)

Типы с плавающей запятой

Типы данных real и double precision хранят приближенные числовые значения переменной точности. На всех поддерживаемых в настоящее время платформах эти типы являются реализациями стандарта IEEE 754 для двоичной арифметики с плавающей запятой (одинарной и двойной точности соответственно) в той степени, в которой это поддерживается базовым процессором, операционной системой и компилятором.

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

Если вам требуется точное хранение и расчеты (например, для денежных сумм), используйте вместо этого тип numeric.

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

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

На всех поддерживаемых в настоящее время платформах тип real имеет диапазон от 1E-37 до 1E+37 с точностью не менее 6 десятичных цифр. Тип double precision имеет диапазон от 1E-307 до 1E+308 с точностью не менее 15 цифр. Слишком большие или слишком маленькие значения вызовут ошибку. Если точность вводимого числа слишком высока, возможно округление. Числа, слишком близкие к нулю и непредставимые отличными от нуля, вызовут ошибку обнуления.

По умолчанию значения с плавающей запятой выводятся в текстовой форме в кратчайшем точном десятичном представлении; полученное десятичное значение ближе к истинному сохраненному двоичному значению, чем к любому другому значению, представляемому с той же двоичной точностью. (Однако выводимое значение в настоящее время никогда не находится точно посередине между двумя представимыми значениями, во избежание широко распространенной ошибки, когда функции ввода не соблюдают должным образом правило округления до ближайшего четного.) Это значение будет использовать не более 17 значащих десятичных цифр для типа float8 и не более 9 цифр для типа float4.

Примечание
Этот кратчайший точный формат вывода генерируется намного быстрее, чем исторически сложившийся формат округления.

В целях снижения точности выводимых чисел, когда это требуется, в параметре extra_float_digits можно выбрать округление десятичного числа. При значении 0 восстанавливается предыдущее округление числа по умолчанию до 6 (для float4) или 15 (для float8) значащих десятичных цифр. При отрицательных значениях количество значащих цифр уменьшается; например, при -2 выводимое число будет округлено до 4 или 13 цифр соответственно.

При любом значении extra_float_digits больше 0 выбирается кратчайший точный формат.

В дополнение к обычным числовым значениям типы с плавающей запятой имеют несколько специальных значений:

Infinity

-Infinity

NaN

Они представляют специальные значения IEEE 754 «бесконечность», «отрицательная бесконечность» и «не число» соответственно. При записи этих значений в качестве констант в команде SQL необходимо заключить их в кавычки, например UPDATE table SET x = '-Infinity'. При вводе эти строки распознаются без учета регистра.

Примечание
IEEE754 указывает, что NaN нельзя сравнивать с любым другим значением с плавающей запятой (включая сам NaN). Чтобы значения с плавающей запятой можно было сортировать и использовать в древовидных индексах, QHB рассматривает значения NaN как равные друг другу и превышающие все значения, отличные от NaN.

QHB также поддерживает стандартные обозначения SQL float и float(p) для указания неточных числовых типов. Здесь p задает минимально допустимую точность в двоичных цифрах. QHB принимает значения от float(1) до float(24) как выбор типа real, а значения от float(25) до float(53) — как выбор типа double precision. Значения p вне допустимого диапазона вызывают ошибку. float без заданной точности считается double precision.

Последовательные типы

Примечание
В этом разделе описывается специфичный для QHB способ создания столбца с автоприращением. Другой способ — использовать функцию столбца идентификаторов стандарта SQL, рассматриваемую в описании команды CREATE TABLE.

Типы данных smallserial, serial и bigserial представляют собой не настоящие типы, а просто удобное средство для создания столбцов с уникальными идентификаторами (аналогично свойству AUTO_INCREMENT, поддерживаемому некоторыми базами данных). В текущей реализации указание:

CREATE TABLE имя_таблицы (
    имя_столбца SERIAL
);

равнозначно указанию:

CREATE SEQUENCE имя_таблицы_имя_столбца_seq AS integer;
CREATE TABLE имя_таблицы (
    имя_столбца integer NOT NULL DEFAULT nextval('имя_таблицы_имя_столбца_seq')
);
ALTER SEQUENCE имя_таблицы_имя_столбца_seq OWNED BY имя_таблицы.имя_столбца;

Таким образом, создается целочисленный столбец, у которого значения по умолчанию назначаются из генератора последовательности. Чтобы в этот столбец нельзя было вставить значение NULL, применяется ограничение NOT NULL. (В большинстве случаев имеет смысл также добавить ограничение UNIQUE или PRIMARY KEY, чтобы предотвратить случайную вставку дублирующихся значений, но автоматически это не происходит.) Последняя команда помечает последовательность как «принадлежащую» столбцу, поэтому при удалении столбца или таблицы она также будет удалена.

Примечание
Поскольку smallserial, serial и bigserial реализованы с использованием последовательностей, в последовательности значений, которая появляется в столбце, могут быть «дыры» или пробелы, даже если не удалять никакие строки. Значение, выделенное из последовательности, все равно «израсходуется», даже если строку, содержащую это значение, так и не удастся успешно вставить в столбец таблицы. Это может произойти, например, при откате транзакции вставки. См. подробное описание nextval() в разделе Функции управления последовательностями.

Чтобы вставить следующее значение последовательности в столбец serial, укажите, что этому столбцу следует присвоить его значение по умолчанию. Это можно сделать либо путем исключения столбца из списка столбцов в команде INSERT, либо с помощью ключевого слова DEFAULT.

Имена типов serial и serial4 равнозначны: оба создают столбцы integer. Имена типов bigserial и serial8 работают аналогично, за исключением того, что они создают столбец bigint. Тип bigserial следует использовать, если за время существования таблицы вы предполагаете использовать более 2³¹

идентификаторов. Имена типов smallserial и serial2 также работают аналогично, за исключением того, что они создают столбец smallint.

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

Денежные типы

Тип money хранит денежную сумму с фиксированной точностью дробной части; см. таблицу 3. Точность дробной части определяется настройкой параметра lc_monetary базы данных. Для указанного в таблице диапазона предполагается, что число содержит два знака после запятой. Входные данные принимаются в различных форматах, включая целочисленные литералы и литералы с плавающей запятой, а также типичные денежные форматы, например ’$1,000.00’. Выводятся данные обычно в денежном формате, зависящем от региональных настроек.

Таблица 3. Денежные типы

Имя | Размер | Описание | Диапазон
money | 8 байт | денежная сумма | от -92233720368547758,08 до +92233720368547758,07

Так как выходные данные этого типа зависимы от региональных настроек, попытка загрузить данные типа money в базу данных с другим значением параметра lc_monetary может закончиться неудачей. Во избежание подобных проблем перед восстановлением дампа в новую базу данных убедитесь, что в этой базе данных параметр lc_monetary имеет то же или эквивалентное значение, что и в исходной.

Значения типов данных numeric, int и bigint можно привести к типу money. Преобразование из типов данных real и double precision можно выполнить, если сначала привести их к типу numeric, например:

SELECT '12.34'::float8::numeric::money;

Однако это не рекомендуется. Числа с плавающей запятой не следует использовать для обработки денег из-за возможных ошибок округления.

К типу numeric значение money можно привести без потери точности. Преобразование в другие типы потенциально может привести к потере точности и также должно выполняться в два этапа:

SELECT '52093.89'::money::numeric::float8;

Деление значения money на целое число производится с усечением дробной части в сторону нуля. Чтобы получить округленный результат, разделите его на значение с плавающей запятой или преобразуйте значение money в numeric перед делением, а затем преобразуйте результат в тип money. (Последний вариант предпочтительнее во избежание риска потери точности.) Когда значение money делится на другое значение money, результатом будет double precision (т. е. чистое число, а не денежная величина); денежные единицы измерения при делении отменяют друг друга.

Символьные типы

Таблица 4. Символьные типы

Имя | Описание
character varying(n), varchar(n) | переменная длина с ограничением
character(n), char(n) | фиксированная длина, с дополнением
mvarchar(n) | переменная длина с ограничением; для совместимости с Microsoft SQL Server
mchar(n) | фиксированная длина, с дополнением; для совместимости с Microsoft SQL Server
text | переменная неограниченная длина

Таблица 4 показывает типы символов общего назначения, доступные в QHB.

SQL определяет два основных типа символов: character varying(n) и character(n), где n — положительное целое число. Оба этих типа могут хранить строки длиной до n символов (не байтов). Попытка сохранить более длинную строку в столбце такого типа приведет к ошибке, если только избыточные символы не являются пробелами — тогда строка будет усечена до максимальной длины. (Это несколько странное исключение продиктовано стандартом SQL.) Если сохраняемая строка короче объявленной длины, значения типа character будут дополнены пробелами; значения типа character varying просто сохранят более короткую строку.

Если явно преобразовывать значение в character varying(n) или character(n), то значение чрезмерной длины будет усечено до n символов, не вызывая ошибку. (Это также продиктовано стандартом SQL.)

Обозначения varchar(n) и char(n) являются псевдонимами для character varying(n) и character(n) соответственно. Запись character без указания длины равнозначна character(1). Если же записать без указания длины character varying, этот тип примет строки любого размера. Последнее является расширением QHB.

В версии QHB 1.2.0 добавлены типы данных mchar и mvarchar, являющиеся аналогами типов char и varchar, но имеющие ряд особенностей поведения, повторяющих поведение типов nchar и nvarchar в Microsoft SQL Server.

Отличия от стандартных типов char и varchar в QHB:

сравнение без учета регистра, в том числе, если один из аргументов mchar или mvarchar, а второй нет (для сравнения с учетом регистра вводятся специальные версии операторов сравнения: &<, &<=, &=, &>= и &>).

mchar фиксированной длины дополняется пробелами справа, но при сравнениях эти пробелы игнорируются;

данные типа mchar или mvarchar хранятся в кодировке utf-16 (для пользователя это не важно);

для сравнения и приведения к верхнему/нижнему регистру используется ICU, поэтому результат не зависит от операционной системы;

функция получения подстроки substr(str, pos [, length]), а не substring.

Отличия от поведения nchar и nvarchar в Microsoft SQL Server:

mchar и mvarchar без указания длины трактуются как строки неопределенной неограниченной длины (похоже на nvarchar(max));

mchar неопределенной длины не дополняется пробелами справа (но явно заданные пробелы все-таки хранятся);

функция вычисления длины строки length(string), а не len, как в Microsoft SQL Server.

Для типов данных mchar и mvarchar поддерживаются индексы B-деревья и хеш-индексы.

Кроме того, QHB предоставляет тип text, в котором хранятся строки любой длины. Хотя тип text не соответствует стандарту SQL, он есть и в некоторых других системах управления базами данных SQL.

Значения типа character физически дополняются пробелами до указанной ширины n и сохраняются и отображаются таким образом. Однако конечные пробелы считаются семантически несущественными и не учитываются при сравнении двух значений типа character. В сопоставлениях, где пробелы являются значащими, такое поведение может привести к неожиданным результатам; например, SELECT 'a '::CHAR(2) collate "C" < E'a\n'::CHAR(2) возвращает true, несмотря на то, что в локали C символ пробел считается больше символа новой строки. При преобразовании значения character в какой-либо другой тип конечные пробелы удаляются. Обратите внимание, что конечные пробелы являются семантически значимыми в значениях character varying и text, а также при сопоставлении с шаблоном, т. е. в операторе LIKE и в регулярных выражениях.

Какие конкретно символы можно сохранять в этих типах данных, зависит от набора символов базы данных, выбираемого при ее создании. Независимо от того, какой набор установлен, символ с кодом 0 (иногда называемый NULL) сохранить нельзя. Подробную информацию см. в разделе Поддержка набора символов.

Потребность в памяти для короткой строки (до 126 байт) составляет 1 байт плюс фактическая строка, которая в случае типа character включает заполнение пробелами. Более длинные строки требуют 4 байта служебной информации вместо 1. Длинные строки сжимаются системой автоматически, поэтому физический размер на диске может быть меньше. Очень длинные значения также хранятся в фоновых таблицах, чтобы не мешать быстрому доступу к более коротким значениям столбцов. В любом случае максимально возможный размер сохраняемой строки символов составляет около 1 ГБ. (Максимально допустимое значение n в объявлении типа данных меньше этого числа. Менять это бесполезно, поскольку в многобайтовых кодировках число символов и байтов может быть совершенно разным. Если вы хотите сохранить длинные строки без определенного верхнего предела, то вместо того чтобы задавать произвольный предел длины, используйте text или character varying без указания длины.)

Примечание
Между этими типами нет разницы в производительности, за исключением увеличения места для хранения при использовании типа с пробелом и нескольких дополнительных циклов ЦП для проверки длины при сохранении в столбце с ограниченной длиной. Хотя тип character(n) имеет преимущества в производительности в некоторых других системах баз данных, в QHB такого преимущества нет; на самом деле из-за дополнительных затрат на хранение character(n) обычно самый медленный из всех. В большинстве случаев вместо него следует использовать text или character varying.

Информацию о синтаксисе строковых литералов см. в разделе Строковые константы, а информацию о доступных операторах и функциях — в главе Функции и операторы.

Пример 1. Использование типов символов

CREATE TABLE test1 (a character(4));
INSERT INTO test1 VALUES ('ok');
SELECT a, char_length(a) FROM test1; -- (1)

  a   | char_length
------+-------------
 ok   |           2

CREATE TABLE test2 (b varchar(5));
INSERT INTO test2 VALUES ('ok');
INSERT INTO test2 VALUES ('good      ');
INSERT INTO test2 VALUES ('too long');
ERROR:  value too long for type character varying(5)
INSERT INTO test2 VALUES ('too long'::varchar(5)); -- явное усечение
SELECT b, char_length(b) FROM test2;

   b   | char_length
-------+-------------
 ok    |           2
 good  |           5
 too l |           5

(1) Функция char_length рассматривается в разделе Строковые функции и операторы.

В QHB есть еще два символьных типа фиксированной длины, показанных в таблице 5. Тип name существует только для хранения идентификаторов во внутренних системных каталогах и не предназначен для использования обычными пользователями. Его длина в настоящее время определяется как 64 байта (63 используемых символа плюс знак конца строки), но на него следует ссылаться, используя константу NAMEDATALEN в исходном коде C/RUST. Длина задается во время компиляции (и, следовательно, настраивается для специальных целей); максимальная длина по умолчанию может измениться в будущем выпуске. Тип "char" (обратите внимание на кавычки) отличается от char(1) тем, что требует только один байт памяти. Он используется внутри системных каталогов как упрощенный тип перечисления.

Таблица 5. Специальные типы символов

Имя | Размер | Описание
"char" | 1 байт | однобайтовый внутренний тип
name | 64 байта | внутренний тип для имен объектов

Двоичные типы данных

Тип данных bytea позволяет хранить двоичные строки; см. таблицу 6.

Таблица 6. Двоичные типы данных

имя | Размер | Описание
bytea | 1 или 4 байта плюс сама двоичная строка | двоичная строка переменной длины

Двоичная строка — это последовательность октетов (или байтов). Двоичные строки имеют два отличия от символьных строк. Во-первых, двоичные строки позволяют хранить октеты с нулевым значением и другие «непечатаемые» октеты (как правило, это октеты вне десятичного диапазона от 32 до 126). Строки символов запрещают нулевые октеты, а также любые другие значения октетов и последовательности значений октетов, недопустимые в соответствии с выбранной кодировкой набора символов базы данных. Во-вторых, операции над двоичными строками обрабатывают фактические байты, тогда как обработка символьных строк зависит от настроек локали. Иными словами, двоичные строки подходят для хранения данных, которые программист считает «необработанными байтами», тогда как символьные строки подходят для хранения текста.

Тип bytea поддерживает два формата для ввода и вывода: hex («шестнадцатеричный») и исторический для QHB формат escape («экранированный»). Входные данные всегда принимаются в обоих форматах. Формат вывода зависит от параметра конфигурации bytea_output; по умолчанию используется «шестнадцатеричный».

Стандарт SQL определяет другой тип двоичной строки, который называется BLOB или BINARY LARGE OBJECT. Формат ввода отличается от bytea, но предоставляемые функции и операторы в основном одинаковы.

Шестнадцатеричный формат bytea

«Шестнадцатеричный» формат кодирует двоичные данные в виде 2 шестнадцатеричных цифр на байт, при этом первая цифра представляет собой наиболее значимые 4 бита. Всей строке предшествует последовательность \x (чтобы отличить ее от экранированного формата). Возможно, в некоторых контекстах понадобится экранировать обратный слеш путем его дублирования (см. раздел Строковые константы). Вводимые шестнадцатеричные цифры могут быть в верхнем или нижнем регистре, и между парами цифр допускается пробел (но не внутри пары цифр и не в начальной последовательности \x). Шестнадцатеричный формат совместим с широким спектром внешних приложений и протоколов; кроме того, он, как правило, преобразуется быстрее экранированного формата, поэтому предпочтительнее использовать его.

Пример:

SELECT '\xDEADBEEF';

Экранированный формат bytea

Экранированный формат является традиционным форматом QHB для типа bytea. В нем используется подход, представляющий двоичную строку как последовательность символов ASCII, а те байты, которые нельзя представить в виде символа ASCII, преобразуются в специальные экранированные последовательности. Если с точки зрения приложения представление байтов в виде символов имеет смысл, то такое представление может быть удобным. Но на практике это обычно вносит путаницу, поскольку стирает различия между двоичными и символьными строками, а кроме того выбранный механизм экранирования последовательностей несколько громоздок. Поэтому в большинстве новых приложений этот формат, наверное, использовать не стоит.

При вводе значений bytea в экранированном формате октеты определенных значений нужно экранировать тогда как все октетные значения можно экранировать. В целом, чтобы экранировать октет, преобразуйте его в трехзначное восьмеричное значение и поставьте перед ним обратный слеш. Сам обратный слеш (символ с десятичным кодом 92) также можно записать в виде двойного обратного слеша. В таблице 7 показаны символы, которые нужно экранировать, а также приведены альтернативные экранированные последовательности, где это возможно.

Таблица 7. Экранированные октеты литералов bytea

Десятичное значение октета | Описание | Экранированное входное представление | Пример | Шестнадцатеричное представление
0 | нулевой октет | ’\000’ | SELECT ’\000’::bytea; | \x00
39 | апостроф | ”” или ’\047’ | SELECT ””::bytea; | \x27
92 | обратный слеш | ’\\’ или ’\134’ | SELECT ’\\’::bytea; | \x5c
От 0 до 31 и от 127 до 255 | «Непечатные» октеты | ’\ xxx’ (восьмеричное значение) | SELECT ’\001’::bytea; | \x01

Требование экранирования непечатаемых октетов зависит от настроек локали. В некоторых случаях их можно оставить без экранирования.

Причина, по которой апострофы необходимо дублировать, как показано в таблице 7, заключается в том, что это верно для любого строкового литерала в команде SQL. Универсальный синтаксический анализатор строковых литералов убирает внешние апострофы и сводит любую пару апострофов к одному символу данных. В результате функция ввода bytea видит всего лишь один апостроф, который она обрабатывает как обычный символ данных. Однако обратный слеш функция ввода bytea обрабатывает как спецсимвол, тем самым реализуя другие действия, показанные в таблице 7.

В некоторых контекстах обратный слеш необходимо дублировать (относительно показанных выше примеров), потому что общий анализатор строковых литералов также сократит пары обратных слешей до одного символа данных (см. раздел Строковые константы).

Октеты bytea по умолчанию выводятся в шестнадцатеричном формате. Если поменять значение bytea_output на escape, «непечатаемые» октеты преобразуются в равнозначные им трехзначные восьмеричные значения, перед которыми стоит один обратный слеш. Большинство «печатаемых» октетов выводятся в виде стандартного представления из клиентского набора символов, например:

SET bytea_output = 'escape';

SELECT 'abc \153\154\155 \052\251\124'::bytea;
     bytea
----------------
 abc klm *\251T

Октет с десятичным значением 92 (обратный слеш) при выводе дублируется. Подробности см. в таблице 8.

Таблица 8. Выходные экранированные октеты bytea

Десятичное значение октета | Описание | Выходное экранированное представление | Пример | Результат на выходе
92 | обратный слеш | \\ | SELECT ’\134’::bytea; | \\
От 0 до 31 и от 127 до 255 | «Непечатаемые» октеты | \xxx (восьмеричное значение) | SELECT ’\001’::bytea; | \001
От 32 до 126 | «Печатаемые» октеты | представление набора символов клиента | SELECT ’\176’::bytea; | ~

В зависимости от используемого интерфейса QHB, может понадобиться выполнить дополнительные действия, связанные с экранированием и деэкранированием строк bytea. Например, если интерфейс автоматически преобразует символы перевода строк и возврата каретки, возможно, их тоже придется экранировать.

Типы даты/времени

QHB поддерживает полный набор типов даты и времени SQL, показанных в таблице 9. Операции, доступные для этих типов данных, описаны в разделе Функции и операторы даты/времени. Даты подсчитываются в соответствии с григорианским календарем, даже для времени до его введения .

Таблица 9. Типы даты/времени

Имя | Размер | Описание | Наименьшее значение | Наибольшее значение | Точность
timestamp [ (p) ] [ without time zone ] | 8 байтов | дата и время (без часового пояса) | 4713 г. до н. э. | 294276 н. э. | 1 микросекунда
timestamp [ (p) ] with time zone | 8 байтов | дата и время с часовым поясом | 4713 г. до н. э. | 294276 н.э. | 1 микросекунда
date | 4 байта | дата (без времени суток) | 4713 г. до н. э. | 5874897 н. э. | 1 день
time [ (p) ] [ without time zone ] | 8 байтов | время суток (без даты) | 00:00:00 | 24:00:00 | 1 микросекунда
time [ (p) ] with time zone | 12 байтов | время суток (без даты) с часовым поясом | 00: 00: 00+1459 | 24: 00: 00-1459 | 1 микросекунда
interval [ поля ] [ (p) ] | 16 байтов | интервал времени | -178000000 лет | 178000000 лет | 1 микросекунда

Примечание
Стандарт SQL требует, чтобы запись одного только timestamp была равнозначна timestamp without time zone, и QHB следует этой логике. timestamptz принимается как сокращение от timestamp with time zone; это расширение QHB.

Типы time, timestamp и interval принимают необязательное значение точности p, которое указывает, сколько знаков после запятой должно сохраняться в поле секунд. По умолчанию явное ограничение точности отсутствует. Допустимый диапазон p составляет от 0 до 6.

Тип interval имеет дополнительный параметр, позволяющий ограничить набор хранимых полей путем записи одной из этих фраз:

YEAR
MONTH
DAY
HOUR
MINUTE
SECOND
YEAR TO MONTH
DAY TO HOUR
DAY TO MINUTE
DAY TO SECOND
HOUR TO MINUTE
HOUR TO SECOND
MINUTE TO SECOND

Обратите внимание, что если при выборе интервала указаны и поля, и p, то указание поля должно содержать SECOND, поскольку точность применяется только к секундам.

Тип time with time zone определяется стандартом SQL, но в определении описаны свойства сомнительной полезности. В большинстве случаев сочетание date, time, timestamp without time zone и timestamp with time zone должно обеспечивать полный диапазон функциональных возможностей даты/времени, требуемых для любого приложения.

Ввод даты/времени

Ввод даты и времени допускается практически в любом приемлемом формате, включая ISO 8601, SQL-совместимый, традиционный формат POSTGRES и другие. Для некоторых форматов порядок дня, месяца и года в вводимой дате является неоднозначным, поэтому поддерживается явное определения ожидаемого порядка этих полей. Установите для параметра DateStyle значение MDY для выбора интерпретации месяц-день-год, DMY для день-месяц-год или YMD для год-месяц-день.

Помните, что любые вводимые литералы даты или времени должны быть заключены в апострофы, как текстовые строки. Дополнительную информацию см. в разделе Константы других типов. SQL требует следующий синтаксис:

тип [ (p) ] 'значение'

где p — необязательное указание точности, задающее количество знаков после запятой в поле секунд. Точность можно указать для типов time, timestamp и interval в диапазоне от 0 до 6. Если в определении константы точность не указана, то по умолчанию используется точность литерального значения (но не более 6 цифр).

Даты

Таблица 10 показывает некоторые возможные входные данные для типа date.

Таблица 10. Ввод даты

Пример | Описание
1999-01-08 | ISO 8601; 8 января в любом режиме (рекомендуемый формат)
January 8, 1999 | определяется однозначно в любом режиме ввода datestyle
1/8/1999 | 8 января в режиме MDY; 1 августа в режиме DMY
1/18/1999 | 18 января в режиме MDY; недопустимая дата в других режимах
01/02/03 | 2 января 2003 г. в режиме MDY; 1 февраля 2003 г. в режиме DMY; 3 февраля 2001 г. в режиме YMD
1999-Jan-08 | 8 января в любом режиме
Jan-08-1999 | 8 января в любом режиме
08-Jan-1999 | 8 января в любом режиме
99-Jan-08 | 8 января в режиме YMD; ошибка в других режимах
08-Jan-99 | 8 января; ошибка в режиме YMD
Jan-08-99 | 8 января; ошибка в режиме YMD
19990108 | ISO 8601; 8 января 1999 года в любом режиме
990108 | ISO 8601; 8 января 1999 года в любом режиме
1999.008 | год и день года
J2451187 | Юлианская дата
January 8, 99 BC | 99 год до нашей эры

Время

Типы времени суток — это time [ (p) ] without time zone и time [ (p) ] with time zone. Указание time без уточнения равнозначно time without time zone.

Допустимые входные данные для этих типов состоят из времени суток, за которым следует необязательный часовой пояс. (См. таблицу 11 и таблицу 12.) Если часовой пояс указан во входных данных для time without time zone, он игнорируется. Также можно указать дату, но она будет игнорироваться, если только не использовать имя часового пояса, включающее правило перехода на летнее время, например America/New_York. В этом случае указание даты требуется для определения того, какое время применяется: стандартное или летнее. Соответствующее смещение часового пояса записывается в поле со значением time with time zone.

Таблица 11. Ввод времени

Пример | Описание
04:05:06.789 | ISO 8601
04:05:06 | ISO 8601
04:05 | ISO 8601
040506 | ISO 8601
04:05 AM | то же, что и 04:05; AM не влияет на значение времени
04:05 PM | то же, что и 16:05; вводимый час должен быть <= 12
04:05:06.789-8 | ISO 8601
04:05:06-08:00 | ISO 8601
04:05-08:00 | ISO 8601
040506-08 | ISO 8601
04:05:06 PST | часовой пояс задан аббревиатурой
2003-04-12 04:05:06 America/New_York | часовой пояс задан полным именем

Таблица 12. Ввод часового пояса

Пример | Описание
PST | Аббревиатура (для тихоокеанского стандартного времени)
America/New_York | Полное название часового пояса
PST8PDT | Указание часового пояса в стиле POSIX
-8:00 | Смещение PST по ISO-8601
-800 | Смещение PST по ISO-8601
-8 | Смещение PST по ISO-8601
zulu | Аббревиатура для UTC, принятая у военных
z | Краткая форма zulu

Дополнительную информацию о том, как задавать часовые пояса см. в разделе Часовые пояса.

Отметки времени

Допустимые входные данные для типов отметок времени состоят из объединенной записи даты и времени, за которой следует необязательный часовой пояс и необязательное уточнение AD или BC. (Как вариант, AD/BC можно указать и перед часовым поясом, но это нежелательно.) Таким образом:

1999-01-08 04:05:06

и:

1999-01-08 04:05:06 -8:00

оба являются допустимыми вариантами, соответствующими стандарту ISO 8601. Кроме того, поддерживается общий формат:

January 8 04:05:06 1999 PST

Стандарт SQL различает литералы timestamp without time zone и timestamp with time zone по наличию символа «+» или «-» и смещению часового пояса, указанному после времени. Следовательно, согласно стандарту,

TIMESTAMP '2004-10-19 10:23:54'

это timestamp without time zone, а

TIMESTAMP '2004-10-19 10:23:54+02'

это timestamp with time zone. QHB никогда не проверяет содержимое литеральной строки перед определением ее типа, поэтому она будет обрабатывать оба приведенных выше выражения как timestamp without time zone. Чтобы литерал обрабатывался timestamp with time zone, присвойте ему правильный явный тип:

TIMESTAMP WITH TIME ZONE '2004-10-19 10:23:54+02'

В литерале, который был определен как timestamp without time zone, QHB будет молча игнорировать любую индикацию часового пояса. Таким образом, результирующее значение получается из полей даты/времени во входном значении и не корректируется для часового пояса.

Для timestamp with time zone внутреннее сохраненное значение всегда указывается в формате UTC (универсальное координированное время, традиционно известное как среднее время по Гринвичу, GMT.) Входное значение с явно указанным часовым поясом преобразуется в UTC с использованием соответствующего смещения для этого часового пояса. Если во входной строке часовой пояс не указан, подразумевается часовой пояс, заданный системным параметром TimeZone, который преобразуется в UTC с использованием смещения для зоны timezone.

Когда выводится значение timestamp without time zone, оно всегда преобразуется из UTC в текущую зону часового пояса и отображается как местное время в этой зоне. Чтобы увидеть время в другом часовом поясе, следует изменить часовой пояс или воспользоваться конструкцией AT TIME ZONE (см. раздел AT TIME ZONE).

Преобразования между timestamp without time zone и timestamp with time zone обычно предполагают, что значение timestamp without time zone должно быть принято или задано как местное время зоны timezone. Для преобразования в другой часовой пояс можно использовать AT TIME ZONE.

Специальные значения

Для удобства QHB поддерживает несколько специальных значений ввода даты/времени, как показано в таблице 13. Значения infinity и -infinity имеют особое представление внутри системы и будут отображаться без изменений; другие же являются простыми сокращениями, которые при чтении будут преобразованы в обычные значения даты/времени. (В частности, now и схожие строки преобразуются в актуальные на момент прочтения значения времени.) При использовании в качестве констант в командах SQL все эти значения следует заключать в апострофы.

Таблица 13. Специальные входные значения даты/времени

Строка ввода | Действительные типы | Описание
epoch | date, timestamp | 1970-01-01 00:00:00+00 (начало отсчета времени в Unix)
infinity | date, timestamp | время после всех остальных отметок времени
-infinity | date, timestamp | время до всех остальных отметок времени
now | date, time, timestamp | время начала текущей транзакции
today | date, timestamp | время начала текущих суток (00:00)
tomorrow | date, timestamp | время начала следующих суток (00:00)
yesterday | date, timestamp | время начала предыдущих суток (00:00)
allballs | time | 00:00:00.00 UTC

Для получения текущего значения времени для соответствующего типа данных можно также воспользоваться следующими SQL-совместимыми функциями: CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, LOCALTIME, LOCALTIMESTAMP. Последние четыре функции принимают необязательную спецификацию с точностью до секунды. (См. раздел Текущая дата/время.) Обратите внимание, что это функции SQL, которые не распознаются в строках ввода данных.

Внимание!
Хотя использование входных строк now, today, tomorrow и yesterday при взаимодействии с командами SQL вполне допустимо, при сохранении команд для последующего выполнения, например в подготовленных операторах, представлениях или определениях функций, их поведение может быть неожиданным. Такая строка может преобразоваться в конкретное значение времени, которое будет продолжать использоваться даже после того, как потеряет свою актуальность. В таких случаях вместо этих строк лучше прибегнуть к помощи одной из функций SQL. Например, CURRENT_DATE + 1 надежнее, чем 'tomorrow'::date.

Вывод даты/времени

Формат вывода типов даты/времени можно установить в одном из четырех стилей: ISO 8601, SQL (Ingres), традиционный POSTGRES (формат даты Unix) или немецкий. По умолчанию используется формат ISO. (Стандарт SQL требует, чтобы использовался именно формат ISO 8601. Название «SQL» дано другому формату только по историческим причинам и носит случайный характер.) В таблице 14 приведены примеры каждого стиля вывода. Согласно приведенным примерам, выходные данные типов date и time, как правило, представляют собой только часть даты или времени. Однако в стиле POSTGRES значение даты без времени выводится в формате ISO.

Таблица 14. Стили вывода даты/времени

Стиль | Описание | пример
ISO | ISO 8601, стандарт SQL | 1997-12-17 07:37:16-08
SQL | традиционный стиль | 12/17/1997 07:37:16.00 PST
PostgreSQL | оригинальный стиль | Wed Dec 17 07:37:16 1997 PST
German | региональный стиль | 17.12.1997 07:37:16.00 PST

Примечание
ISO 8601 указывает, что для разделения даты и времени используется буква Т в верхнем регистре. QHB принимает этот формат при вводе, но на выходе, как показано выше, использует пробел, а не T. Это сделано для удобочитаемости и для соответствия RFC 3339, а также некоторым другим системам баз данных.

В стилях SQL и POSTGRES день отображается перед месяцем, если указан порядок полей DMY, в противном случае месяц отображается перед днем. (Информацию о том, как этот параметр также влияет на интерпретацию входных значений см. в подразделе Ввод даты/времени.) В таблице 15 приведены примеры.

Таблица 15. Соглашение о порядке отображения компонентов даты

Установка datestyle | Порядок ввода | Пример вывода
SQL, DMY | день/месяц/год | 17/12/1997 15:37:16.00 CET
SQL, MDY | месяц/день/год | 12/17/1997 07:37:16.00 PST
PostgreSQL, DMY | день/месяц/год | Wed 17 Dec 07:37:16 1997 PST

Пользователь может выбрать стиль даты/времени с помощью команды SET datestyle, параметра DateStyle в файле конфигурации qhb.conf или переменной среды PGDATESTYLE на сервере или клиенте.

Также в качестве более гибкого способа форматирования выходных данных даты/времени доступна функция to_char (см. раздел Функции форматирования типов данных).

Часовые пояса

Часовые пояса и соглашения об их применении зависят от политических решений, а не только от географии. В начале прошлого века часовые пояса во всем мире были в некоторой степени стандартизированы, но продолжают претерпевать произвольные изменения, особенно в отношении правил перехода на летнее время. Для получения информации об отмененных правилах, касающихся часовых поясов, QHB использует широко распространенную базу данных часовых поясов IANA (Olson). Относительно будущего времени предполагается, что в заданном часовом поясе последние известные правила так и останутся неизменными.

В большинстве случаев QHB стремится к совместимости с определениями стандарта SQL. Однако стандарт SQL включает странное сочетание типов и возможностей даты и времени. Есть две очевидные проблемы:

Хотя у типа date не может быть связанного часового пояса, для типа time его можно указать. В реальном мире часовые пояса не имеют большого значения, если они не связаны одновременно с датой и с временем, поскольку смещение может изменяться в течение года вместе с границами летнего времени.

По умолчанию часовой пояс задается в виде постоянного числового смещения от UTC. Таким образом, при выполнении арифметических операций с датой/временем, пересекающими границы летнего времени, его невозможно учесть.

Во избежание этих трудностей рекомендуется при использовании часовых поясов выбирать типы даты/времени, содержащие и дату, и время. Мы не рекомендуем использовать тип time with time zone (хотя он и поддерживается QHB для соответствия стандарту SQL). Для всех типов, содержащих только дату или только время, в QHB предполагается местный часовой пояс.

Все значения даты и времени с учетом часового пояса хранятся внутри в формате UTC. Перед предоставлением клиенту они преобразуются в местное время в зоне, указанной параметром конфигурации TimeZone.

QHB позволяет указывать часовые пояса в трех разных формах:

Полное название часового пояса, например America/New_York. Распознанные имена часовых поясов перечислены в представлении pg_timezone_names. Для этой цели QHB использует широко распространенные данные часового пояса IANA, поэтому имена часовых поясов QHB распознаются также и другими приложениями.

Аббревиатура часового пояса, например PST. В отличие от полных имен часовых поясов, которые также могут подразумевать набор правил перехода на летнее время, такая спецификация просто определяет конкретное смещение от UTC. Распознаваемые аббревиатуры перечислены в представлении pg_timezone_abbrevs. Аббревиатуры часового пояса нельзя задать в параметрах конфигурации TimeZone или log_timezone, но можно использовать во входных значениях даты/времени и в операторе AT TIME ZONE.

В дополнение к названиям и аббревиатурам часовых поясов QHB будет принимать спецификации часовых поясов в стиле POSIX в виде

STD смещение [ DST ]

(пробелы добавлены для наглядности; в реальном указании их быть не должно)
Здесь STD — аббревиатура стандартного часового пояса, смещение — стандартное смещение пояса от UTC, а DST — необязательная аббревиатура часового пояса при переходе на летнее время (смещение обычно опускается, поскольку по умолчанию оно равно стандартному смещению минус один час).
Например, если EST5EDT еще не является распознанным названием часового пояса, оно будет принято и будет функционально равнозначно времени Восточного побережья США. В этом синтаксисе аббревиатура часового пояса может быть буквенной или произвольной строкой, заключенной в угловые скобки (<>). При наличии аббревиатуры DST предполагается, что она будет использоваться в соответствии с теми же правилами перехода на летнее время, которые применяются в записи posixrules базы данных часовых поясов IANA. В стандартной установке QHB posixrules совпадает с US/Eastern, поэтому спецификации часовых поясов в стиле POSIX соответствуют правилам перехода на летнее время в США. При необходимости это поведение можно изменить, заменив файл posixrules.

Иными словами, разница между аббревиатурами и полными названиями состоит в том, что аббревиатуры представляют конкретное смещение от UTC, тогда как многие полные названия подразумевают еще и местное правило перехода на летнее время и, соответственно, два возможных смещения UTC. Например, 2014-06-04 12:00 America/New_York представляет полдень по местному времени в Нью-Йорке, что для этого конкретного дня было восточным летним временем (UTC-4). Так что 2014-06-04 12:00 EDT обозначает тот же момент времени. Но 2014-06-04 12:00 EST обозначает полдень по восточному стандартному времени (UTC-5) независимо от того, действовало ли в этот день летнее время.

Дело осложняется тем, что в некоторых юрисдикциях одна и та же аббревиатура часового пояса используется для обозначения разных смещений UTC в разное время; например, в Москве MSK несколько лет означало UTC+3, а потом — UTC+4. QHB интерпретирует такие аббревиатуры в соответствии с тем, что они означают (или означали совсем недавно) в указанную дату; но, как и в приведенном выше примере с EST, это не обязательно будет соответствовать местному гражданскому времени на эту дату.

Следует помнить, что при введении названия часового пояса в стиле POSIX легко опечататься, поскольку проверка на правильность аббревиатур часовых поясов не проводится. Например, команда SET TIMEZONE TO FOOBAR0 выполнится успешно, приведя к тому, что система будет эффективно использовать довольно своеобразную аббревиатуру для UTC. Другая проблема заключается в том, что в стиле POSIX положительный знак (+) смещения имеют часовые пояса к западу от Гринвича. В других областях QHB следует соглашению ISO-8601, согласно которому положительные смещения часовых поясов находятся к востоку от Гринвича.

Во всех случаях названия и сокращения часовых поясов распознаются без учета регистра.

Ни имена часовых поясов, ни аббревиатуры не зашиты в сервер; они берутся из файлов конфигурации, хранящихся в папках .../share/timezone/ и .../share/timezonesets/ установочного каталога .

Параметр конфигурации TimeZone можно установить в файле qhb.conf или любым другим стандартным способом, описанным в главе Конфигурация сервера. Есть также несколько специальных способов установить его:

SQL-команда SET TIME ZONE устанавливает часовой пояс для текущего сеанса. Это альтернативное написание команды SET TIMEZONE TO, более совместимое со стандартом SQL.

Переменная среды PGTZ используется клиентами libpq для отправки команды SET TIME ZONE на сервер при подключении.

Ввод интервалов

Значения типа interval можно записать с помощью следующего подробного синтаксиса:

[@] количество единица [количество единица...] [направление]

где количество — это число (возможно, со знаком); единица — это одно из значений: microsecond, millisecond, second, minute, hour, day, week, month, year, decade, century, millennium (микросекунды, миллисекунды, секунды, минуты, часы, дни, недели, месяцы, годы, десятилетия, века и тысячелетия соответственно), либо сокращения или множественные формы этих слов; направление может принимать значение ago (назад) или быть пустым. Знак @ является необязательным. Величины различных единиц неявно суммируются с учетом соответствующего знака чисел. Указание ago меняет знак всех полей на противоположный. Этот синтаксис также используется при выводе интервала, если для параметра IntervalStyle установлено значение postgres_verbose.

Количество дней, часов, минут и секунд можно задать без явного указания единиц измерения. Например, '1 12:59:10’ читается так же, как '1 day 12 hours 59 min 10 sec'. Кроме того, комбинацию лет и месяцев можно указать с тире; например, '200-10' читается так же, как '200 years 10 months'. (Фактически только эти более короткие формы и разрешены стандартом SQL, и они используются при выводе, когда для IntervalStyle установлено значение sql_standard).

Значения типа interval также можно записать в виде временных интервалов, определенных в ISO 8601, используя либо «формат с кодами раздела 4.4.3.2 этого стандарта, либо «альтернативный формат» раздела 4.4.3.3. Формат с кодами выглядит так:

P количество единица [ количество единица ...] [ T [ количество единица ...]]

Строка должна начинаться с буквы P и может содержать букву T, предваряющую единицы времени суток. Доступные сокращения единиц приведены в таблице 16. Единицы можно опускать и указывать в любом порядке, но после T должны идти единицы меньше дня. В частности, значение кода M зависит от того, находится ли он до или после T

Таблица 16. Сокращения единиц временных интервалов ISO 8601

Сокращенное название | Значение
Y | Годы
M | Месяцы (в дате)
W | Недели
D | Дни
H | Часы
M | Минуты (во времени)
S | Секунды

В альтернативном формате:

P [ годы-месяцы-дни ] [ T часы:минуты:секунды ]

строка должна начинаться с P, а T разделяет компоненты даты и времени. Значения приведены в виде чисел, аналогичных датам ISO 8601.

При записи интервальной константы со указанием полей или присвоении интервальному столбцу строки с полями интерпретация немаркированных величин зависит от полей. Например, INTERVAL '1' YEAR читается как 1 год, а INTERVAL '1' — как 1 секунда. Кроме того, значения поля «справа» от наименее значимого поля, разрешенного в определении полей, просто отбрасываются. Например, в записи INTERVAL '1 day 2:03:04' HOUR TO MINUTE будет удалено поле секунд, но не дня.

Согласно стандарту SQL, все поля значения interval должны иметь одинаковый знак, поэтому ведущий отрицательный знак применяется ко всем полям; например, знак минус в интервальном литерале '-1 2:03:04' применяется как к дням, так и к часам/минутам/секундам. QHB позволяет полям иметь разные знаки и традиционно независимо обрабатывает знак каждого поля в текстовом представлении, так что в этом примере часы/минуты/секунды считаются положительными. Если для IntervalStyle установлено значение sql_standard, то ведущий знак считается применимым ко всем полям (но только при отсутствии дополнительных знаков). В противном случае используется традиционная интерпретация QHB. Во избежание неоднозначности, если какое-либо поле является отрицательным, рекомендуется прикреплять явный знак к каждому полю.

В подробном формате ввода и в некоторых полях более компактных форматов ввода значения полей могут иметь дробные части; например, '1.5 week' или '01:02:03.45'. Такое входное значение при сохранении преобразуется в соответствующее число месяцев, дней и секунд. Когда в результате у месяцев или дней появляется дробное число, эта дробь добавляется в поля младшего разряда с использованием коэффициентов преобразования 1 месяц = 30 дней и 1 день = 24 часа. Например, '1.5 month' превращается в 1 месяц и 15 дней. В виде дробного числа будут отображаться только секунды.

В таблице 17 приведены некоторые примеры допустимых входных значений типа interval.

Таблица 17. Ввод интервалов

Пример | Описание
1-2 | Стандартный формат SQL: 1 год 2 месяца
3 4:05:06 | Стандартный формат SQL: 3 дня 4 часа 5 минут 6 секунд
1 year 2 months 3 days 4 hours 5 minutes 6 seconds | Традиционный формат PostgreSQL: 1 год 2 месяца 3 дня 4 часа 5 минут 6 секунд
P1Y2M3DT4H5M6S | «Формат с кодами» ISO 8601: то же значение, что и выше
P0001-02-03T04:05:06 | «Альтернативный формат» ISO 8601: то же значение, что и выше

Внутренние значения типа interval хранятся в виде месяцев, дней и секунд. Это сделано потому, что число дней в месяце варьируется, и в сутках может быть 23 или 25 часов, если происходит корректировка перехода на летнее время. Поля месяца и дня являются целыми числами, а поле секунд может быть дробным. Поскольку интервалы обычно получаются из константных строк или при вычитании меток времени, этот метод хранения применим в большинстве случаев, но может привести к неожиданным результатам:

SELECT EXTRACT(hours from '80 minutes'::interval);
 date_part
-----------
         1

SELECT EXTRACT(days from '80 hours'::interval);
 date_part
-----------
         0

Для настройки дней и часов, выходящих за пределы нормальных диапазонов доступны функции justify_days и justify_hours.

Вывод интервалов

Формат вывода типа interval можно установить в одном из четырех стилей: sql_standard, postgres, postgres_verbose или iso_8601 с помощью команды SET intervalstyle. По умолчанию используется формат postgres. В таблице 18 приведены примеры каждого стиля вывода.

Стиль sql_standard создает выходные данные, соответствующие определению стандарта SQL для литеральных строк интервалов, если значение интервала соответствует ограничениям стандарта (только год-месяц или только день-время, без смешивания положительных и отрицательных компонентов). В противном случае выходные данные выглядят как стандартная литеральная строка год-месяц, за которой следует литеральная строка день-время с явными знаками, добавленными для устранения неоднозначности интервалов со смешанными знаками.

Вывод в стиле postgres соответствует формату в QHB, когда для параметра DateStyle установлено значение ISO.

Вывод в стиле postgres_verbose соответствует формату в QHB, когда для параметра DateStyle установлено значение, отличное от ISO.

Вывод в стиле iso_8601 соответствует «формату с кодами, описанному в разделе 4.4.3.2 стандарта ISO 8601.

Таблица 18. Примеры стилей вывода интервалов

Стиль | Интервал год-месяц | Интервал день-время | Смешанный интервал
sql_standard | 1-2 | 3 4:05:06 | -1-2 +3 -4: 05: 06
postgres | 1 year 2 mons | 3 days 04:05:06 | -1 year -2 mons +3 days -04:05:06
postgres_verbose | @ 1 year 2 mons | @ 3 days 4 hours 5 mins 6 secs | @ 1 year 2 mons -3 days 4 hours 5 mins 6 secs ago
iso_8601 | P1Y2M | P3DT4H5M6S | Р-1Y-2M3DT-4H-5M-6S

Логический тип

QHB предоставляет стандартный SQL-тип boolean (логический); см. таблицу 19. Тип boolean может иметь несколько состояний: «true» (истина), «false» (ложь) и третье состояние «unknown» (неизвестно), которое представляется SQL-значением NULL.

Таблица 19. Логический тип данных

Имя | Размер | Описание
boolean | 1 байт | состояние: истина или ложь

Логические константы могут представляться в запросах SQL ключевыми словами SQL: TRUE, FALSE и NULL.

Функция ввода данных типа boolean принимает следующие строковые представления для состояния «true»:

true
yes
on
1

и следующие представления для состояния «false»:

false
no
off
0

Также допускаются уникальные префиксы этих строк, например t или n. Начальные или конечные пробелы игнорируются, а регистр символов не имеет значения.

Функция вывода данных типа boolean всегда выдает t или f, как показано в примере 2.

Пример 2. Использование типа boolean

CREATE TABLE test1 (a boolean, b text);
INSERT INTO test1 VALUES (TRUE, 'sic est');
INSERT INTO test1 VALUES (FALSE, 'non est');
SELECT * FROM test1;
 a |    b
---+---------
 t | sic est
 f | non est

SELECT * FROM test1 WHERE a;
 a |    b
---+---------
 t | sic est

Ключевые слова TRUE и FALSE являются предпочтительным (SQL-совместимым) способом записи логических констант в запросах SQL. Но также можно использовать строковые представления, следуя общему синтаксису строковой литеральной константы, описанному в разделе Константы других типов, например, 'yes'::boolean.

Обратите внимание, что синтаксический анализатор автоматически понимает, что TRUE и FALSE относятся к типу boolean, но для NULL это не так, потому что ему может соответствовать любой тип. Поэтому в некоторых контекстах вам, возможно, придется явным образом привести NULL к типу boolean, например NULL::boolean. И, наоборот, приведение можно исключить из строково-литерального логического значения в тех контекстах, где анализатор может сделать вывод, что литерал должен иметь тип boolean.

Перечислимые типы

Перечислимые (enum) типы — это типы данных, которые содержат статический упорядоченный набор значений. Они равнозначны типам enum, поддерживаемым в ряде языков программирования. Примером данных перечислимого типа могут быть дни недели или набор значений состояния для фрагмента данных.

Объявление перечислимых типов

Перечислимые типы создаются с помощью команды CREATE TYPE, например:

CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');

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

CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
CREATE TABLE person (
    name text,
    current_mood mood
);
INSERT INTO person VALUES ('Moe', 'happy');
SELECT * FROM person WHERE current_mood = 'happy';
 name | current_mood
------+--------------
 Moe  | happy
(1 row)

Порядок

Порядок значений в перечислимом типе — это порядок, в котором значения были перечислены при его создании. Перечисления поддерживаются всеми стандартными операторами сравнения и связанными агрегатными функциями. Например:

INSERT INTO person VALUES ('Larry', 'sad');
INSERT INTO person VALUES ('Curly', 'ok');
SELECT * FROM person WHERE current_mood > 'sad';
 name  | current_mood
-------+--------------
 Moe   | happy
 Curly | ok
(2 rows)

SELECT * FROM person WHERE current_mood > 'sad' ORDER BY current_mood;
 name  | current_mood
-------+--------------
 Curly | ok
 Moe   | happy
(2 rows)

SELECT name
FROM person
WHERE current_mood = (SELECT MIN(current_mood) FROM person);
 name
-------
 Larry
(1 row)

Надежность типа

Каждый перечислимый тип данных является уникальным и не может сравниваться с другими перечислимыми типами. Взгляните на этот пример:

CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');
CREATE TABLE holidays (
    num_weeks integer,
    happiness happiness
);
INSERT INTO holidays(num_weeks,happiness) VALUES (4, 'happy');
INSERT INTO holidays(num_weeks,happiness) VALUES (6, 'very happy');
INSERT INTO holidays(num_weeks,happiness) VALUES (8, 'ecstatic');
INSERT INTO holidays(num_weeks,happiness) VALUES (2, 'sad');
ERROR:  invalid input value for enum happiness: "sad"
SELECT person.name, holidays.num_weeks FROM person, holidays
  WHERE person.current_mood = holidays.happiness;
ERROR:  operator does not exist: mood = happiness

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

SELECT person.name, holidays.num_weeks FROM person, holidays
  WHERE person.current_mood::text = holidays.happiness::text;
 name | num_weeks
------+-----------
 Moe  |         4
(1 row)

Детали реализации

Метки перечисления чувствительны к регистру, поэтому 'happy' не равнозначно 'HAPPY'. Пробелы в метках тоже значимы.

Хотя перечислимые типы в основном предназначены для статических наборов значений, для них поддерживается добавление новых значений в существующий перечислимый тип и переименование значений (см. ALTER TYPE). А вот удалить или изменить порядок сортировки существующих значений в перечислении нельзя; для этого придется удалить и воссоздать это перечисление.

Значение перечислимого типа занимает на диске четыре байта. Длина текстовой метки перечислимого значения ограничена настройкой NAMEDATALEN, скомпилированной в QHB; в стандартных сборках длина ограничена 63 байтами.

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

Геометрические типы

Геометрические типы данных представляют собой объекты в двумерном пространстве. В таблице 20 представлены геометрические типы, существующие в QHB.

Таблица 20. Геометрические типы

Имя | Размер | Описание | Представление
point | 16 байт | Точка на плоскости | (x,y)
line | 32 байта | Бесконечная прямая | {A, B, C},
lseg | 32 байта | Конечный отрезок | ((x1,y1),(x2,y2))
box | 32 байта | Прямоугольник | ((x1,y1),(x2,y2))
path | 16+16n байт | Закрытый путь (подобный многоугольнику) | ((x1,y1),...)
path | 16+16n байт | Открытый путь | [(x1,y1),...]
polygon | 40+16n байт | Многоугольник (подобный замкнутому пути) | ((x1,y1),...)
circle | 24 байта | Окружность | <(x,y),r> (центр окружности и радиус)

Для выполнения различных геометрических операций, таких как масштабирование, перемещение, вращение и определение пересечений QHB предоставляет широкий спектр функций и операторов. Они рассматриваются в разделе Геометрические функции и операторы.

Точки

Точки являются основной двумерной структурной единицей для геометрических типов. Значения типа point указываются в одном из следующих форматов:

( x, y )
  x, y

где x и y — соответствующие координаты в виде чисел с плавающей запятой.

Выводятся точки в первом формате.

Прямые

Прямые представлены линейным уравнением Ax + By + C = 0, где A и B не равны нулю. Значения типа line вводятся и выводятся в следующем виде:

{ A, B, C }

Как вариант, для ввода можно использовать любую из следующих форм:

[ ( x1, y1 ), ( x2, y2 ) ]
( ( x1, y1 ), ( x2, y2 ) )
  ( x1, y1 ), ( x2, y2 )
    x1, y1  ,   x2, y2

где (x1, y1) и (x2, y2) — это две разные точки на данной прямой.

Отрезки

Отрезок представляется парой точек, которые определяют его конец. Значения типа lseg указываются в любом из следующих форматов:

[ ( x1, y1 ), ( x2, y2 ) ]
( ( x1, y1 ), ( x2, y2 ) )
  ( x1, y1 ), ( x2, y2 )
    x1, y1  ,   x2, y2

где (x1, y1) и (x2, y2) — это конечные точки отрезка.

Выводятся отрезки в первом формате.

Прямоугольники

Прямоугольник представляется парой точек, которые находятся в противоположных его углах. Значения типа box указываются в любом из следующих форматов:

( ( x1, y1 ), ( x2, y2 ) )
  ( x1, y1 ), ( x2, y2 )
    x1, y1  ,   x2, y2

где (x1, y1) и (x2, y2) — это любые два противоположных угла прямоугольника.

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

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

Пути

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

Значения типа path указываются в любом из следующих форматов:

[ ( x1, y1 ), ..., ( xn, yn ) ]
( ( x1, y1 ), ..., ( xn, yn ) )
  ( x1, y1 ), ..., ( xn, yn )
  ( x1, y1  , ...,   xn, yn )
    x1, y1  , ...,   xn, yn

где точки являются конечными точками отрезков, составляющих путь. Квадратные скобки ([]) указывают открытый путь, а круглые скобки (()) — закрытый. Когда внешние скобки опущены, как в форматах с третьего по пятый, предполагается закрытый путь.

Выводятся пути в первом или втором формате, в зависимости от ситуации.

Многоугольники

Многоугольники представляются списками точек (вершин). Многоугольники очень похожи на закрытые пути, но хранятся по-другому и имеют собственный набор вспомогательных функций.

Значения типа polygon указываются в любом из следующих форматов:

( ( x1, y1 ), ..., ( xn, yn ) )
  ( x1, y1 ), ..., ( xn, yn )
  ( x1, y1  , ...,   xn, yn )
    x1, y1  , ...,   xn, yn

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

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

Круги

Круги представляются центральной точкой и радиусом. Значения типа circle указываются в любом из следующих форматов:

< ( x, y ), r >
( ( x, y ), r )
  ( x, y ), r
    x, y  , r

где (x, y) — это центральная точка, а r — радиус окружности.

Выводятся круги в первом формате.

Типы сетевых адресов

QHB предлагает типы данных для хранения адресов IPv4, IPv6 и MAC, как показано в таблице 21. Для хранения сетевых адресов лучше вместо обычных текстовых типов использовать эти типы, потому что они предлагают проверку ошибок ввода, а также специализированные операторы и функции (см. раздел Функции и операторы сетевых адресов).

Таблица 21. Типы сетевых адресов

Имя | Размер | Описание
cidr | 7 или 19 байт | Сети IPv4 и IPv6
inet | 7 или 19 байт | Хосты и сети IPv4 и IPv6
macaddr | 6 байт | MAC-адреса
macaddr8 | 8 байт | MAC-адреса (формат EUI-64)

При сортировке типов данных inet или cidr адреса IPv4 всегда будут сортироваться до адресов IPv6, в том числе адреса IPv4, включенные или сопоставленные с адресами IPv6, например ::10.2.3.4 или ::ffff:10.4.3.2.

inet

Тип inet содержит адрес хоста IPv4 или IPv6 и, при необходимости, его подсеть, все в одном поле. Подсеть представляется количеством битов адреса сети в адресе хоста (или «маской сети»). Если маска сети равна 32, а адрес — IPv4, то это значение указывает не на подсеть, а только на отдельный хост. В IPv6 длина адреса составляет 128 бит, поэтому уникальный адрес хоста задают 128 бит. Обратите внимание, что, если вам требуется, чтобы принимались только адреса сетей, следует использовать тип cidr а не inet.

Формат ввода для этого типа: IP-адрес/y где IP-адрес — это адрес IPv4 или IPv6, а y — количество битов в маске сети. Если часть /y отсутствует, то маска сети равна 32 для IPv4 и 128 для IPv6, поэтому значение будет представлять только один хост. На экране часть /y опускается, если маска сети определяет адрес одного хоста.

cidr

Тип cidr содержит определение сети IPv4 или IPv6. Форматы ввода и вывода соответствуют соглашениям CIDR (Classless Internet Domain Routing, бесклассовая междоменная маршрутизация). Сети определяются в формате IP-адрес/y, где IP-адрес — это сеть, представленная в виде адреса IPv4 или IPv6, а y — количество битов в маске сети. Если y опущен, он рассчитывается с использованием допущений из старой классовой системы нумерации сети, с той разницей, что он будет как минимум достаточно большим, чтобы в него вошли все октеты, записанные во входных данных. Сетевой адрес, биты которого указаны справа от заданной маски, будет считаться ошибочным.

В таблице 22 показано несколько примеров.

Таблица 22. Примеры вводимых значений типа cidr

Вводимое значение cidr | Выводимое значение cidr | abbrev(cidr)
192.168.100.128/25 | 192.168.100.128/25 | 192.168.100.128/25
192.168/24 | 192.168.0.0/24 | 192.168.0/24
192.168/25 | 192.168.0.0/25 | 192.168.0.0/25
192.168.1 | 192.168.1.0/24 | 192.168.1/24
192.168 | 192.168.0.0/24 | 192.168.0/24
128.1 | 128.1.0.0/16 | 128.1/16
128 | 128.0.0.0/16 | 128.0/16
128.1.2 | 128.1.2.0/24 | 128.1.2/24
10.1.2 | 10.1.2.0/24 | 10.1.2/24
10.1 | 10.1.0.0/16 | 10.1/16
10 | 10.0.0.0/8 | 10/8
10.1.2.3/32 | 10.1.2.3/32 | 10.1.2.3/32
2001:4f8:3:ba::/64 | 2001:4f8:3:ba::/64 | 2001:4f8:3:ba::/64
2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128 | 2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128 | 2001:4f8:3:ba:2e0:81ff:fe22:d1f1
::ffff:1.2.3.0/120 | ::ffff:1.2.3.0/120 | ::ffff:1.2.3/120
::ffff:1.2.3.0/128 | ::ffff:1.2.3.0/128 | ::ffff:1.2.3.0/128

Различия между inet и cidr

Существенное различие между типами данных inet и cidr заключается в том, что inet принимает значения с ненулевыми битами справа от маски сети, а cidr — нет. Например, значение 192.168.0.1/24 допустимо для inet но не для cidr.

Совет
Если вас не устраивает формат вывода inet или cidr, попробуйте функции host, text и abbrev.

macaddr

В типе macaddr хранятся MAC-адреса, примером которых являются аппаратные адреса Ethernet-платы (хотя MAC-адреса используются и для других целей). Входные значения принимается в следующих форматах:

'08:00:2b:01:02:03'

'08-00-2b-01-02-03'

'08002b:010203'

'08002b-010203'

'0800.2b01.0203'

'0800-2b01-0203'

'08002b010203'

Все эти примеры определяют один и тот же адрес. Верхний и нижний регистр допускается для шестнадцатеричных цифр от a до f. Выводится macaddr всегда в первой из показанных форм.

Стандарт IEEE 802-2001 считает вторую из показанных форм (с дефисами) канонической формой для MAC-адресов, а первую форму (с двоеточиями) определяет как запись с обратным порядком бит, поэтому 08-00-2b-01-02-03 = 01:00:4D:08:04:0C. В настоящее время это соглашение в основном игнорируется и имеет значение только для устаревших сетевых протоколов (таких как Token Ring). QHB не поддерживает изменение порядка битов, и в всех принятых форматах используется канонический порядок LSB.

Остальные пять форматов ввода не описаны ни в каких стандартах.

macaddr8

В типе macaddr8 хранятся MAC-адреса в формате EUI-64, примером которых являются аппаратные адреса Ethernet-платы (хотя MAC-адреса также используются и для других целей). Этот тип может принимать MAC-адреса длиной 6 и 8 байтов и сохранять их в формате длиной 8 байтов. MAC-адреса, заданные в 6-байтовом формате, будут сохраняться в формате 8 байт, а 4-й и 5-й байты будут отданы FF и FE соответственно. Обратите внимание, что IPv6 использует модифицированный формат EUI-64, где 7-й бит должен быть установлен в 1 после преобразования из EUI-48. Для этого изменения предусмотрена функция macaddr8_set7bit. Вообще говоря, тип macaddr8 принимает любые входные строки, состоящие из пар шестнадцатеричных цифр (на границах байтов), при необходимости согласованно разделяемых одним из следующих символов: ':', '-' или '.'. Количество шестнадцатеричных цифр должно быть 16 (8 байт) или 12 (6 байт). Начальные и конечные пробелы игнорируются. Ниже приведены примеры допустимых форматов ввода:

'08:00:2b:01:02:03:04:05'

'08-00-2b-01-02-03-04-05'

'08002b:0102030405'

'08002b-0102030405'

'0800.2b01.0203.0405'

'0800-2b01-0203-0405'

'08002b01:02030405'

'08002b0102030405'

Все эти примеры определяют один и тот же адрес. Верхний и нижний регистр допускается для шестнадцатеричных цифр от a до f. Выводится macaddr8 всегда в первой из показанных форм. Последние шесть вышеуказанных форматов ввода не описаны ни в каких стандартах. Чтобы преобразовать традиционный 48-битный MAC- адрес в формате EUI-48 в модифицированный формат EUI-64, который будет включен в адрес IPv6 качестве адреса хоста, используйте функцию macaddr8_set7bit, как показано ниже:

SELECT macaddr8_set7bit('08:00:2b:01:02:03');

    macaddr8_set7bit
-------------------------
 0a:00:2b:ff:fe:01:02:03
(1 row)

Типы битовых строк

Битовые строки — это строки, состоящие из 1 и 0. Их можно использовать для хранения или визуализации битовых масок. В SQL существует два типа битовых строк: bit(n) и bit varying(n), где n — это положительное целое число.

Данные типа bit должны точно соответствовать длине n; попытка сохранить более короткие или более длинные битовые строки приведет к ошибке. Данные типа bit varying имеют переменную длину вплоть до максимальной длины n; более длинные строки будут отклонены. Запись bit без указания длины равнозначна bit(1), тогда как bit varying без указания длины означает неограниченную длину.

Примечание
Если явно привести значение битовой строки к типу bit(n), оно будет усечено или дополнено нулями справа до длины ровно n битам, не приводя к ошибке. Аналогичным образом, если явно привести значение битовой строки к типу bit varying(n), она будет усечено справа, если ее длина превышает n бит.

Информацию о синтаксисе констант битовых строк см. в разделе Константы битовых строк. Доступные битовые логические операторы и функции обработки строк описаны в разделе Функции и операторы битовых строк.

Пример 3. Использование типов битовых строк

CREATE TABLE test (a BIT(3), b BIT VARYING(5));
INSERT INTO test VALUES (B'101', B'00');
INSERT INTO test VALUES (B'10', B'101');

ERROR:  bit string length 2 does not match type bit(3)
(ОШИБКА: длина битовой строки 2 не соответствует типу bit(3))

INSERT INTO test VALUES (B'10'::bit(3), B'101');
SELECT * FROM test;

  a  |  b
-----+-----
 101 | 00
 100 | 101

Для хранения битовых значений требуется по 1 байту для каждой группы из 8 битов, плюс 5 или 8 байтов дополнительно в зависимости от длины строки (но длинные значения могут быть сжаты или вынесены за пределы строки, как описано в разделе Символьные типы применительно к символьным строкам).

Типы для текстового поиска

QHB предоставляет два типа данных, предназначенных для поддержки полнотекстового поиска. Текстовый поиск — это операция по выполнению поиска в коллекции документов на естественном языке с целью обнаружения тех из них, что лучше всего соответствуют запросу. Тип tsvector представляет документ в форме, оптимизированной для текстового поиска; тип tsquery аналогичным образом представляет текстовый запрос. Связанные с этим функции и операторы перечислены в разделе Функции текстового поиска и операторы.

tsvector

Значение типа tsvector — это упорядоченный список дискретных лексем, представляющих собой слова, которые были нормализованы для сведения различных вариантов одного и того же слова в один. Сортировка и удаление дубликатов выполняются автоматически во время ввода данных, как показано в этом примере:

SELECT 'a fat cat sat on a mat and ate a fat rat'::tsvector;
                      tsvector
----------------------------------------------------
 'a' 'and' 'ate' 'cat' 'fat' 'mat' 'on' 'rat' 'sat'

Чтобы представить лексемы, содержащие пробелы или знаки пунктуации, заключите их в кавычки:

SELECT $$the lexeme '    ' contains spaces$$::tsvector;
                 tsvector
-------------------------------------------
 '    ' 'contains' 'lexeme' 'spaces' 'the'

(В этом и следующем примерах мы используем строковые литералы, заключенные в двойные знаки доллара, во избежание путаницы с кавычками внутри литералов). Встроенные кавычки и обратные слэши необходимо удвоить:

SELECT $$the lexeme 'Joe''s' contains a quote$$::tsvector;
                    tsvector
------------------------------------------------
 'Joe''s' 'a' 'contains' 'lexeme' 'quote' 'the'

По желанию, к лексемам можно добавить их целочисленные позиции:

SELECT 'a:1 fat:2 cat:3 sat:4 on:5 a:6 mat:7 and:8 ate:9 a:10 fat:11 rat:12'::tsvector;
                                  tsvector
-------------------------------------------------------------------------------
 'a':1,6,10 'and':8 'ate':9 'cat':3 'fat':2,11 'mat':7 'on':5 'rat':12 'sat':4

Позиция обычно указывает местоположение исходного слова в документе. Информацию о положении слова можно использовать для ранжирования близости. Значения позиции могут варьироваться от 1 до 16383; числа выше максимума автоматически устанавливаются на 16383. Дублирующие позиции для одной и той же лексемы отбрасываются.

Лексемы с позициями можно дополнительно пометить весом, который можно выразить буквами A, B, C или D. D является значением по умолчанию и, следовательно, не отображается при выводе:

SELECT 'a:1A fat:2B,4C cat:5D'::tsvector;
          tsvector
----------------------------
 'a':1A 'cat':5 'fat':2B,4C

Веса, как правило, используются для отражения структуры документа, например, помечая слова заголовка не так, как слова основного текста. Функции ранжирования текстового поиска могут назначать разные приоритеты различным маркерам веса.

Важно понимать, что тип tsvector сам по себе не выполняет нормализацию слов; предполагается, что заданные слова уже соответствующим образом нормализованы для приложения. Например,

SELECT 'The Fat Rats'::tsvector;
      tsvector
--------------------
 'Fat' 'Rats' 'The'

Для большинства англоязычных приложений для поиска текста вышеприведенные слова будут считаться ненормализованными, но для типа tsvector это не важно. Обычно для нормализации слов, подходящих для поиска, необработанный текст документа следует обработать функцией to_tsvector:

SELECT to_tsvector('english', 'The Fat Rats');
   to_tsvector
-----------------
 'fat':2 'rat':3

tsquery

Значение tsquery хранит искомые лексемы и может комбинировать их, используя логические операторы & (И), | (ИЛИ) и ! (НЕ), а также оператор поиска фразы <-> (ПРЕДШЕСТВУЕТ). Существует также вариант оператора ПРЕДШЕСТВУЕТ вида <N> , где N — это целочисленная константа, которая определяет расстояние между двумя искомыми лексемами. Запись <-> равнозначна <1>.

Для принудительной группировки этих операторов можно использовать скобки. Без скобок эти операторы имеют разную степень привязки, в порядке убывания: ! (НЕТ), <-> (ПРЕДШЕСТВУЕТ), & (И) и | (ИЛИ).

Вот некоторые примеры:

SELECT 'fat & rat'::tsquery;
    tsquery
---------------
 'fat' & 'rat'

SELECT 'fat & (rat | cat)'::tsquery;
          tsquery
---------------------------
 'fat' & ( 'rat' | 'cat' )

SELECT 'fat & rat & ! cat'::tsquery;
        tsquery
------------------------
 'fat' & 'rat' & !'cat'

При желании лексемы в tsquery можно дополнить одной или несколькими весовыми буквами, при этом они будут соответствовать только тем лексемам tsvector, которые имеют один из этих весов:

SELECT 'fat:ab & cat'::tsquery;
    tsquery
------------------
 'fat':AB & 'cat'

Кроме того, лексемы в tsquery можно дополнить *, чтобы указать соответствия префикса:

SELECT 'super:*'::tsquery;
  tsquery
-----------
 'super':*

Этот запрос будет соответствовать любому слову в tsvector, которое начинается с «super».

Правила цитирования для лексем такие же, как описано ранее для лексем в tsvector, и, как и в случае с tsvector, любую необходимую нормализацию слов следует выполнять перед преобразованием в тип tsquery. Для выполнения такой нормализации подходит функция to_tsquery:

SELECT to_tsquery('Fat:ab & Cats');
    to_tsquery
------------------
 'fat':AB & 'cat'

Обратите внимание, что to_tsquery будет обрабатывать префиксы так же, как и другие слова, поэтому это сравнение возвращает true:

SELECT to_tsvector( 'postgraduate' ) @@ to_tsquery( 'postgres:*' );
 ?column?
----------
 t

потому что postgres будет связан с postgr:

SELECT to_tsvector( 'postgraduate' ), to_tsquery( 'postgres:*' );
  to_tsvector  | to_tsquery
---------------+------------
 'postgradu':1 | 'postgr':*

который будет соответствовать основной форме postgraduate.

Тип UUID

Тип данных uuid сохраняет универсальные уникальные идентификаторы (Universally Unique Identifiers, UUID) в соответствии с определением в RFC 4122, ISO/IEC 9834-8: 2005 и связанных стандартах. (В некоторых системах этот тип данных называется глобально уникальным идентификатором, или GUID.) Этот идентификатор является 128-битной величиной, генерируемой специально выбранным алгоритмом, практически гарантирующим, что этот же идентификатор не будет сгенерирован кем-либо еще в известной вселенной с использованием того же алгоритма. Поэтому для распределенных систем эти идентификаторы обеспечивают лучшую гарантию уникальности, чем генераторы последовательностей, которые уникальны только в пределах одной базы данных.

UUID записывается в виде последовательности шестнадцатеричных цифр в нижнем регистре, разделенных дефисами на несколько групп, в следующем порядке: группа из 8 цифр, затем три группы из 4 цифр, а следом группа из 12 цифр — всего 32 цифры, представляющие 128 битов. Пример UUID в этой стандартной форме:

a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11

QHB также принимает следующие альтернативные формы для ввода: использование цифр в верхнем регистре, стандартный формат, заключенный в фигурные скобки, пропуск некоторых или всех дефисов, добавление дефиса после любой группы из четырех цифр. Примеры:

A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11
{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}
a0eebc999c0b4ef8bb6d6bb9bd380a11
a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11
{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}

Выводятся значения типа uuid всегда в стандартной форме.

QHB предоставляет для UUID функции хранения и сравнения, но основная база данных не содержит функции для генерации UUID, поскольку не существует такого алгоритма, который подходил бы для всех приложений. Несколько стандартных алгоритмов можно реализовать при помощи функций, предоставляемых модулем uuid-ossp. Также функцию генерации случайных UUID предоставляет модуль pgcrypto. Как вариант, UUID могут генерировать клиентские приложения или другими библиотеки, вызываемые через функцию на стороне сервера.

Тип XML

Тип данных xml можно использовать для хранения данных XML. Его преимущество перед хранением данных XML в текстовом поле состоит в том, что он проверяет вводимые значения на корректность, а кроме того для выполнения над ним типобезопасных операций существуют вспомогательные функции; см. раздел Функции XML. Для использования этого типа данных инсталляцию необходимо скомпилировать с конфигурацией configure --with-libxml.

Тип xml может сохранять правильно оформленные «документы», в соответствии со стандартом XML, а также фрагменты «содержимого», характеризующиеся как менее ограниченные «узлы документа» в модели данных XQuery и XPath. Грубо говоря, это означает, что в фрагментах содержимого может быть более одного элемента верхнего уровня или символьного узла. Определить, является ли конкретное значение xml полным документом или только фрагментом содержимого, можно при помощи выражения xml-значение IS DOCUMENT.

Создание значений XML

Чтобы получить значение типа xml из символьных данных, используйте функцию xmlparse:

XMLPARSE ( { DOCUMENT | CONTENT } значение)

Примеры:

XMLPARSE (DOCUMENT '<?xml version="1.0"?><book><title>Manual</title><chapter>...</chapter></book>')
XMLPARSE (CONTENT 'abc<foo>bar</foo><bar>foo</bar>')

Хотя в соответствии со стандартом SQL это единственный способ преобразовать символьные строки в значения XML, также можно использовать форматы, специфичные для QHB:

xml '<foo>bar</foo>'
'<foo>bar</foo>'::xml

Тип xml не проверяет входные значения по схеме объявления типа документа (DTD), даже когда они указывают на DTD. В настоящее время также отсутствует встроенная поддержка проверки на соответствие другим языкам схем XML, например XML Schema.

Обратная операция, получение символьного строкового значения из xml, выполняется с помощью функции xmlserialize:

XMLSERIALIZE ( { DOCUMENT | CONTENT } значение AS тип )

тип может быть иметь значение character, character varying или text (или их псевдонимы). Опять же, согласно стандарту SQL, это единственный способ преобразования между типами xml и символьными типами, но QHB позволяет просто приводить значение к нужному типу.

Когда значение символьной строки приводится к типу xml или наоборот без помощи функций XMLPARSE или XMLSERIALIZE соответственно, выбор режима DOCUMENT или CONTENT определяется параметром конфигурации сеанса «XML option», который можно установить с помощью стандартной команды:

SET XML OPTION { DOCUMENT | CONTENT };

или записью, более характерной для QHB:

SET xmloption TO { DOCUMENT | CONTENT };

По умолчанию установлен режим CONTENT, поэтому допускаются все формы XML-данных.

Обработка кодировки

При работе с разными кодировками символов на стороне клиента и сервера, а также в передаваемых через них XML-данных необходимо соблюдать осторожность. Когда передача запросов на сервер и возврат результатов клиенту происходит в текстовом режиме (это нормальный режим), QHB преобразует все символьные данные, передаваемые от клиента серверу и наоборот, в кодировку для соответствующей стороны; см. раздел Поддержка набора символов. Сюда входят и строковые представления значений XML, такие как, например, в приведенных выше примерах. Обычно это означает, что объявления кодировки, содержащиеся в XML-данных, могут стать недействительными при преобразовании символьных данных в другие кодировки в процессе перемещения между клиентом и сервером, поскольку объявление встроенной кодировки не изменяется. Для решения этой проблемы объявления кодировки, содержащиеся в символьных строках, представленных для ввода в тип xml, игнорируются, и предполагается, что содержимое находится в текущей серверной кодировке. Следовательно, для правильной обработки таких строк XML-данных клиент должен передавать их в своей текущей кодировке. Именно клиент перед отправкой документов на сервер должен либо преобразовать их в свою текущую кодировку, либо изменить эту кодировку соответствующим образом. При выводе значения типа xml не будут содержать объявления кодировки, и клиент должен предполагать, что все данные находятся в его текущей кодировке.

Когда передача параметров запроса на сервер и возврат результатов клиенту происходит в двоичном режиме, преобразование кодировки не выполняется, поэтому возникает другая ситуация. В этом случае будет наблюдаться объявление кодировки в XML-данных, а при его отсутствии предполагается, что данные закодированы в UTF-8 (что соответствует стандарту XML; обратите внимание, что QHB не поддерживает UTF-16). При выводе данные будут содержать объявление кодировки, выбранной на стороне клиента, однако если это UTF-8, объявление будет опущено.

Нет необходимости говорить, что обработка XML-данных с помощью QHB будет менее подвержена ошибкам и более эффективна, если и в XML-данных, и на стороне клиента, и на стороне сервера будет использована одна кодировка. Поскольку внутри XML-данные закодированы в UTF-8, вычисления будут наиболее эффективными, если на сервере также выбрана кодировка UTF-8.

Предупреждение!
Некоторые функции, связанные с XML, могут вообще не работать с данными, отличными от ASCII, если кодировка сервера не соответствует UTF-8. В частности, известно, что эта проблема есть у функций xmltable() и xpath().

Обращение к значениям XML

Тип данных xml необычен тем, что не предоставляет операторов сравнения. Это связано с тем, что для XML-данных не существует четко определенного и универсально полезного алгоритма сравнения. Одним из следствий этого является то, что вы не можете получить строки, сравнивая столбец xml со значением поиска. Поэтому обычно значения XML должны дополняться отдельным ключевым полем, например ID. Альтернативное решение для сравнения значений XML — сначала преобразовать их в символьные строки, но обратите внимание, что сравнение этих строк совершенно бесполезно для сравнения XML-данных.

Поскольку для типа данных xml нет операторов сравнения, невозможно создать индекс непосредственно для столбца этого типа. Если требуется быстрый поиск в XML-данных, это ограничение можно обойти, приведя выражение к типу символьной строки и проиндексировав его, либо проиндексировав выражение XPath. Конечно, фактический запрос следует скорректировать для поиска по индексируемому выражению.

Также для ускорения поиска документов в XML-данных можно использовать функциональность полнотекстового поиска в QHB. Однако это требует поддержки предварительной обработки, а в дистрибутиве QHB ее пока нет.

Типы JSON

Типы данных JSON предназначены для хранения данных JSON (JavaScript Object Notation, запись объектов JavaScript), в соответствии со стандартом RFC 7159. Подобные данные можно хранить и в виде текста, но преимущество типов JSON в том, что они проверяют каждое сохраненное значение на соответствие правилам JSON. Также для работы с данными, хранящимися в этих типах, существуют различные JSON-специфические функции и операторы; см. раздел Функции и операторы JSON.

QHB предлагает два типа для хранения данных JSON: json и jsonb. Для реализации эффективных механизмов запросов к этим типам данных QHB также предоставляет тип данных jsonpath, описанный в подразделе Тип jsonpath.

Типы данных json и jsonb принимают на ввод почти идентичные наборы значений. Основное практическое отличие заключается в эффективности. Тип данных json хранит точную копию введенного текста, которую функции обработки должны заново анализировать при каждом выполнении; тогда как данные jsonb хранятся в разобранном двоичном формате, что немного замедляет его ввод из-за дополнительных затрат на преобразование, но значительно ускоряет обработку, так как не требуется повторный анализ. Помимо этого, тип jsonb поддерживает индексирование, что может быть существенным преимуществом.

Поскольку тип json хранит точную копию введенного текста, он сохраняет семантически незначимые пробелы между токенами, а также порядок ключей в объектах JSON. Кроме того, если объект JSON внутри значения содержит повторяющиеся ключи, то сохранятся все пары ключ/значение. (Функции обработки считают действительным последнее значение.) Тип jsonb, напротив, не сохраняет ни пробел, ни порядок ключей объекта, ни их дубликаты. Если во входных данных указаны дублирующиеся ключи, сохраняется только последнее значение.

В целом, в большинстве приложений предпочтительнее хранить данные JSON в формате jsonb, если нет специализированных требований, таких как устаревшие предположения о порядке расположения ключей объектов.

В RFC 7159 говорится, что строки JSON должны быть представлены в кодировке UTF8. Поэтому типы JSON не могут жестко соответствовать спецификации, если кодировка базы данных не UTF8. Напрямую вставить символы, непредставимые в кодировке базы данных, не получится; и наоборот, символы, представимые в кодировке базы данных, но не в UTF8, будут допустимыми.

RFC 7159 позволяет строкам JSON содержать экранированные последовательности Unicode, обозначенные через \u__XXXX__. В функции ввода для типа json экранирование Unicode разрешено независимо от кодировки базы данных и проверяется только на синтаксическую корректность (то есть, после \u должны идти четыре шестнадцатеричные цифры). Однако функция ввода для jsonb более строгая: она запрещает экранирование Unicode для символов, не относящихся к ASCII (т. е. символов выше U+007F), если кодировка базы данных не UTF8. Тип jsonb также не принимает \u0000 (поскольку это значение не может быть представлено в типе text QHB) и требует, чтобы суррогатные пары Unicode для обозначения символов вне основной многоязычной плоскости (BMP) Unicode использовались правильно. Допустимые управляющие символы Unicode преобразуются для хранения в эквивалентные символы ASCII или UTF8 (сюда относится и сворачивание суррогатных пар в один символ).

Примечание
Многие из функций обработки JSON, описанные в разделе Функции и операторы JSON, преобразуют управляющие символы Unicode в обычные и поэтому будут выдавать ошибки того же типа, что и вышеописанные, даже если им ввести данные типа json, а не jsonb. То, что функция ввода в тип json не выполняет эти проверки, можно рассматривать как исторический артефакт, хотя это и позволяет просто сохранять (без обработки) управляющие символы JSON Unicode в кодировке базы данных, не поддерживающей представленные символы.

При преобразовании вводимого текста JSON в jsonb примитивные типы, описанные в RFC 7159, в сущности отображаются в собственные типы QHB, как показано в Таблице 23. Следовательно, к допустимым данным типа jsonb предъявляются некоторые незначительные дополнительные требования, относящиеся к ограничениям представления нижележащего типа данных, которые не применимы ни к типу json, ни к формату JSON в целом. В частности, jsonb будет отклонять числа, выходящие за пределы типа numeric QHB, а json — нет. Подобные ограничения, накладываемые реализацией, допустимы согласно RFC 7159. Однако на практике такие проблемы гораздо более вероятны в других реализациях, поскольку обычно примитивный тип JSON number представляется в виде числа с плавающей запятой двойной точности IEEE 754 (что RFC 7159 явно предусматривает и допускает). При использовании JSON в качестве формата обмена данных с такими системами следует учитывать опасность потери точности чисел относительно данных, изначально сохраняемых в QHB.

И наоборот, как отмечено в таблице, существуют некоторые незначительные ограничения на формат ввода примитивных типов JSON, которые не применяются к соответствующим типам QHB.

Таблица 23. Примитивные типы JSON и соответствующие им типы QHB

Примитивный тип JSON | Тип QHB | Примечания
string | text | \u0000 не допускается, так как управляющие символы Unicode недоступны в этой кодировке базы данных
number | numeric | Значения NaN и infinity не допускаются
boolean | boolean | Допускается только строчное написание true и false
null | (none) | В SQL NULL имеет другой смысл

Синтаксис вводимых и выводимых значений JSON

Синтаксис ввода/вывода типов данных JSON соответствует RFC 7159.

Ниже приведены все допустимые выражения json (или jsonb):

-- Простое скалярное/примитивное значение
-- Примитивные значения могут быть числа, строки в кавычках, true, false или null
SELECT '5'::json;

-- Массив из нуля и более элементов (элементы могут быть разных типов)
SELECT '[1, 2, "foo", null]'::json;

-- Объект, содержащий пары ключей и значений
-- Обратите внимание, что ключи объекта всегда должны быть строками в кавычках
SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json;

-- Массивы и объекты могут вкладываться произвольным образом
SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json;

Как было сказано ранее, когда значение JSON вводится, а затем выводится без какой-либо дополнительной обработки, тип json выводит тот же текст, который был введен, тогда как тип jsonb не сохраняет семантически незначимые детали, такие как пробелы. Например, обратите внимание на эти различия:

SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::json;
                      json
-------------------------------------------------
 {"bar": "baz", "balance": 7.77, "active":false}
(1 row)

SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::jsonb;
                      jsonb
--------------------------------------------------
 {"bar": "baz", "active": false, "balance": 7.77}
(1 row)

Одна семантически незначимая деталь, на которую стоит обратить внимание: в jsonb числа будут выводиться исходя из поведения нижележащего типа numeric. На практике это означает, что числа, введенные в записи с E, будут выведены без нее, например:

SELECT '{"reading": 1.230e-5}'::json, '{"reading": 1.230e-5}'::jsonb;
         json          |          jsonb
-----------------------+-------------------------
 {"reading": 1.230e-5} | {"reading": 0.00001230}
(1 row)

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

Список встроенных функций и операторов, доступных для построения и обработки значений JSON, см. в разделе Функции и операторы JSON.

Разработка документов JSON

Представлять данные в JSON можно значительно более гибко, чем в традиционной модели реляционных данных, что особенно важно в средах с изменчивыми требованиями. Оба подхода вполне могут сосуществовать и дополнять друг друга в одном приложении. Однако даже для приложений, где требуется максимальная гибкость, все же рекомендуется, чтобы документы JSON имели некоторую фиксированную структуру. Эта структура, как правило, не навязывается принудительно (хотя возможно декларативное внедрение некоторых бизнес-правил), но ее предсказуемость облегчает написание запросов, которые эффективно извлекают данные из набора «документов» (информационных блоков) в таблице.

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

Проверка на вложение и существование в jsonb

Проверка вложения является важной особенностью jsonb, не имеющей аналога для типа json. Эта проверка определяет, вложен ли один документ jsonb в другой. Приведенные ниже примеры возвращают значение true, за исключением отмеченных случаев:

-- Простые скалярные/примитивные значения включают только одно идентичное значение:
SELECT '"foo"'::jsonb @> '"foo"'::jsonb;

-- Массив с правой стороны вложен в массив слева:
SELECT '[1, 2, 3]'::jsonb @> '[1, 3]'::jsonb;

-- Порядок элементов в массиве не важен, поэтому эта запись тоже верна:
SELECT '[1, 2, 3]'::jsonb @> '[3, 1]'::jsonb;

-- Повторяющиеся элементы массива тоже не имеют значения:
SELECT '[1, 2, 3]'::jsonb @> '[1, 2, 2]'::jsonb;

-- Объект с одной парой с правой стороны вложен в объект слева:
SELECT '{"product": "PostgreSQL", "version": 9.4, "jsonb": true}'::jsonb @> '{"version": 9.4}'::jsonb;

-- Массив с правой стороны не считается вложенным в массив слева, хотя в
-- последний и вложен подобный массив:
SELECT '[1, 2, [1, 3]]'::jsonb @> '[1, 3]'::jsonb;  -- выдает false

-- Но если добавить уровень вложенности, массив считается вложенным:
SELECT '[1, 2, [1, 3]]'::jsonb @> '[[1, 3]]'::jsonb;

-- Аналогично это вложением не считается:
SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"bar": "baz"}'::jsonb;  -- выдает false

-- Ключ на верхнем уровне с пустым объектом вложен в объект с таким ключом:
SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"foo": {}}'::jsonb;

Общий принцип этой проверки заключается в том, что вложенный объект должен соответствовать содержащему его объекту по структуре и содержимому данных, возможно, после исключения из содержащего объекта некоторых неподходящих элементов массива или пар ключ/значение объекта. Но помните, что при сопоставлении вложений порядок элементов массива не имеет значения, а повторяющиеся элементы массива учитываются только один раз.

В качестве специального исключения из общего принципа соответствия структур, массив может содержать примитивное значение:

-- В этот массив вложено примитивное строковое значение:
SELECT '["foo", "bar"]'::jsonb @> '"bar"'::jsonb;

-- Это исключение не действует в обратную сторону -- здесь вложения нет:
SELECT '"bar"'::jsonb @> '["bar"]'::jsonb;  -- выдает false

У типа jsonb также имеется оператор существования, являющийся вариацией темы вложения: он проверяет, отображается ли строка (заданная как значение text) как ключ объекта или элемент массива на верхнем уровне значения jsonb. Приведенные ниже примеры возвращают значение true, за исключением отмеченных случаев:

-- Строка существует в качестве элемента массива:
SELECT '["foo", "bar", "baz"]'::jsonb ? 'bar';

-- Строка существует в качестве ключа объекта:
SELECT '{"foo": "bar"}'::jsonb ? 'foo';

-- Значения объектов не рассматриваются:
SELECT '{"foo": "bar"}'::jsonb ? 'bar';  -- выдает false

-- Как и вложение, существование должно определяться на верхнем уровне:
SELECT '{"foo": {"bar": "baz"}}'::jsonb ? 'bar'; -- выдает false

-- Строка считается существующей, если она соответствует примитивной строке JSON:
SELECT '"foo"'::jsonb ? 'foo';

Объекты JSON больше подходят для проверок на вложение или существование со множеством ключей или элементов, нежели массивы, поскольку, в отличие от массивов, они внутренне оптимизированы для поиска и не требуют линейного поиска.

Совет
Поскольку содержимое JSON является вложенным, правильно составленный запрос может пропустить явную выборку внутренних объектов. В качестве примера предположим, что у нас есть столбец doc, содержащий объекты на верхнем уровне, и большинство этих объектов содержит поля tags с массивами внутренних объектов. Данный запрос находит записи, в которых внутренние объекты содержат как "term":"paris", так и "term":"food", при этом пропуская такие ключи вне массива tags:

SELECT doc->'site_name' FROM websites
  WHERE doc @> '{"tags":[{"term":"paris"}, {"term":"food"}]}';

Можно сделать то же самое, скажем, так:

SELECT doc->'site_name' FROM websites
  WHERE doc->'tags' @> '[{"term":"paris"}, {"term":"food"}]';

Но такой подход менее гибок, а зачастую и менее эффективен.

С другой стороны, оператор существования JSON не является вложенным: он будет искать указанный ключ или элемент массива только на верхнем уровне значения JSON.

Различные операторы вложений и существования, а также все остальные операторы и функции JSON описаны в разделе Функции и операторы JSON.

Индексация jsonb

Для эффективного поиска ключей или пар ключ/значение, встречающихся в большом количестве документов jsonb (информационных блоков), можно использовать индексы GIN. Для этого предоставляются два «класса операторов» GIN, предлагающих различные компромиссы между производительностью и гибкостью.

Класс операторов GIN по умолчанию для jsonb поддерживает запросы с операторами существования ключа на верхнем уровне: ?, ?& и ?| и оператор существования пути/значения @>. (Подробнее семантика, которую реализуют эти операторы, описана в Таблице 45.) Пример создания индекса с этим классом операторов:

CREATE INDEX idxgin ON api USING GIN (jdoc);

Нестандартный класс операторов GIN jsonb_path_ops поддерживает индексирование только оператора @>. Пример создания индекса с этим классом операторов:

CREATE INDEX idxginp ON api USING GIN (jdoc jsonb_path_ops);

Рассмотрим пример таблицы, в которой хранятся документы JSON, полученные от стороннего веб-сервиса, с документированным определением схемы. Типичный документ:

{
    "guid": "9c36adc1-7fb5-4d5b-83b4-90356a46061a",
    "name": "Angela Barton",
    "is_active": true,
    "company": "Magnafone",
    "address": "178 Howard Place, Gulf, Washington, 702",
    "registered": "2009-11-07T08:53:22 +08:00",
    "latitude": 19.793713,
    "longitude": 86.513373,
    "tags": [
        "enim",
        "aliquip",
        "qui"
    ]
}

Мы сохраняем эти документы в таблице с именем api, в столбце jsonb с именем jdoc. Если для этого столбца создается индекс GIN, он может использоваться в таких запросах:

-- Найти документы, в которых ключ "company" имеет значение "Magnafone"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"company": "Magnafone"}';

Однако в запросах, подобных следующему, этот индекс применять нельзя, потому что хотя оператор ? и индексируется, он не применяется непосредственно к индексируемому столбцу jdoc:

-- Найти документы, в которых ключ "tags" содержит ключ или элемент массива "qui"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc -> 'tags' ? 'qui';

Тем не менее, при надлежащем использовании индексов выражений в этом запросе можно использовать индекс. Если запросы к определенным элементам в ключе "tags" выполняются часто, возможно, будет целесообразно определить такой индекс:

CREATE INDEX idxgintags ON api USING GIN ((jdoc -> 'tags'));

Теперь предложение WHERE jdoc -> 'tags' ? 'qui' будет распознаваться как приложение индексируемого оператора ? к индексируемому выражению jdoc -> 'tags'. (Более подробную информацию об индексах выражений можно найти в разделе Индексы по выражениям).

Кроме того, индекс GIN поддерживает операторы @@ и @?, которые выполняют сопоставление jsonpath.

SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @@ '$.tags[*] == "qui"';

SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @@ '$.tags[*] ? (@ == "qui")';

Индекс GIN извлекает из jsonpath операторы следующей формы: цепочка_аксессоров = константа. Цепочка аксессоров может включать аксессоры .ключ, [*] и [индекс]. jsonb_ops дополнительно поддерживает аксессоры .* и .**.

Другой подход к запросам заключается в использовании вложений, например:

-- Найти документы, в которых ключ "tags" содержит элемент массива "qui"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qui"]}';

Этот запрос может использовать простой индекс GIN для столбца jdoc. Но учтите, что такой индекс будет хранить копии каждого ключа и значения в столбце jdoc, тогда как индекс выражения из предыдущего примера хранит только данные, найденные в объекте с ключом tags. Хотя подход с простым индексом гораздо более гибок (поскольку он поддерживает запросы по любому ключу), индексы целевых выражений скорее всего будут меньше и быстрее при поиске, чем простой индекс.

Хотя класс операторов jsonb_path_ops поддерживает только запросы с операторами @>, @@ и @?, его производительность заметно выше по сравнению с классом операторов по умолчанию jsonb_ops. Индекс jsonb_path_ops обычно намного меньше индекса jsonb_ops для тех же данных и более точен при поиске, особенно когда запросы содержат ключи, часто встречающиеся в данных. Поэтому поисковые операции с индексом jsonb_path_ops обычно выполняются лучше, чем с классом операторов по умолчанию.

Техническое различие между индексами GIN jsonb_ops и jsonb_path_ops состоит в том, что первый создает независимые элементы индекса для всех ключей/значений в данных, а второй — только для значений. ¹ По сути, каждый элемент индекса jsonb_path_ops является хешем значения и приводящего к нему ключа (или ключей); например, для индексации {"foo": {"bar": "baz"}}, будет создан один элемент индекса, объединяющий в хеш все три элемента: foo, bar и baz. Таким образом, запрос вложений, ищущий эту структуру, приведет к максимально точному поиску по индексу; но определить, является ли foo ключом, с помощью этого индекса невозможно. С другой стороны, индекс jsonb_ops создаст три элемента индекса, представляющих foo, bar и baz по отдельности, а затем для проверки на вложения будет искать строки, содержащие все три этих элемента. Хотя индексы GIN могут выполнять поиск с AND довольно эффективно, он все равно будет менее точным и более медленным, чем равнозначный поиск с jsonb_path_ops, особенно если любой из этих трех элементов индекса содержится в очень большом количестве строк.

Недостаток класса операторов jsonb_path_ops заключается в том, что он не создает индексных записей для структур JSON, не содержащих никаких значений, таких как {"a": {}}. При запросе на поиск документов, содержащих такую структуру, потребуется полное индексное сканирование, которое выполняется довольно медленно. Поэтому jsonb_path_ops не подходит для приложений, часто выполняющих такой поиск.

Тип jsonb также поддерживает индексы btree и hash. Обычно они полезны, только если необходимо проверить равенство документов JSON в целом. Порядок сортировки btree для данных jsonb редко представляет большой интерес, но для полноты он приводится ниже:

Объект > Массив > Логическое значение > Число > Строка > Null

Объект с n парами > объект с n - 1 парами

Массив с n элементами > массив с n - 1 элементами

Объекты с равным количеством пар сравниваются в следующем порядке:

ключ-1, значение-1, ключ-2 ...

Обратите внимание, что ключи объектов сравниваются в порядке их хранения; в частности, поскольку более короткие ключи хранятся перед более длинными, в итоге результаты могут оказаться не интуитивными. Например:

{ "aa": 1, "c": 1} > {"b": 1, "d": 1}

Массивы с равным количеством элементов сравниваются аналогично:

элемент-1, элемент-2 ...

Примитивные значения JSON сравниваются по тем же правилам сравнения, что и нижележащие типы данных QHB. Строки сравниваются с учетом порядка сортировки по умолчанию в этой базе данных.

Обращение по индексу к элементам jsonb

Тип данных jsonb поддерживает извлечение и изменение элементов в стиле обращения по индексу к элементам массива. Указывать на вложенные значения можно, задавая цепочку выражений, обращающихся к элементам, следуя при этом тем же правилам, что и аргумент path в функции jsonb_set. Если значение jsonb является массивом, числовые индексы начинаются с нуля, а отрицательные целые числа отсчитывают от последнего элемента в массива к первому. Обращения к срезам массивов не поддерживаются. Результат обращающегося по индексу выражения всегда имеет тип данных jsonb.

Для изменения значений jsonb можно воспользоваться обращением по индексу в предложении SET оператора UPDATE. Пути такого обращения должны быть преодолимыми для всех задействованных значений, если они существуют. Например, путь val['a']['b']['c'] можно пройти полностью до c, если val, val['a'] и val['a']['b'] являются объектами. Если же значение val['a'] или val['a']['b'] не определено, оно будет создано в виде пустого объекта и должным образом заполнено. Однако если само значение val или любое из промежуточных значений существует и является не объектом, а, к примеру, строкой, числом или элементом jsonb null, пройти этот путь невозможно, поэтому возникает ошибка и транзакция прерывается.

Пример синтаксиса обращения по индексу:

-- Извлечь значение объекта по ключу
SELECT ('{"a": 1}'::jsonb)['a'];

-- Извлечь значение вложенного объекта по пути ключа
SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c'];

-- Извлечь элемент массива по индексу
SELECT ('[1, "2", null]'::jsonb)[1];

-- Изменить значение объекта по ключу. Обратите внимание на апострофы вокруг '1':
-- присваиваемое значение также должно быть типа jsonb
UPDATE table_name SET jsonb_field['key'] = '1';

-- Это вызовет ошибку, если jsonb_field['a']['b'] в какой-либо записи не является
-- объектом. Например, в значении {"a": 1} ключу 'a' соответствует числовое
-- значение.
UPDATE table_name SET jsonb_field['a']['b']['c'] = '1';

-- Отфильтровать записи предложением WHERE с обращением по индексу. Поскольку результат
-- обращения имеет тип jsonb, значение, с которым мы его сравниваем, должно быть того же типа.
-- Благодаря кавычкам строка "value" тоже стала допустимой строкой jsonb.
SELECT * FROM table_name WHERE jsonb_field['key'] = '"value"';

Присваивание jsonb посредством обращения по индексу в некоторых особых случаях работает не так, как с функцией jsonb_set. Когда исходное значение jsonb равно NULL, присваивание при обращении по индексу происходит так, будто это значение было пустым значением JSON (типа объект или массив, в зависимости от ключа обращения):

-- Там, где поле jsonb_field было NULL, оно станет {"a": 1}
UPDATE table_name SET jsonb_field['a'] = '1';

-- Там, где поле jsonb_field было NULL, оно станет [1]
UPDATE table_name SET jsonb_field[0] = '1';

Если индекс указан для массива, содержащего недостаточно элементов, в него будут добавляться элементы со значением NULL до тех пор, пока индекс не станет достижимым и пока не станет возможным задать значение.

-- Там, где поле jsonb_field было [], оно станет [null, null, 2];
-- Там, где поле jsonb_field было [0], оно станет [0, null, 2]
UPDATE table_name SET jsonb_field[2] = '2';

Значение jsonb будет принимать присваивания по несуществующим путям обращения, пока последний существующий элемент, который нужно пройти, является объектом или массивом, в зависимости от соответствующего компонента пути (элемент, на который указывает последний компонент пути, не проходится и может быть любым). Будут созданы вложенные структуры массивов и объектов, и для массивов они дополнятся элементами NULL до позиции, заданной в пути обращения, в которую можно будет поместить присваиваемое значение.

-- Там, где поле jsonb_field было {}, оно станет {'a': [{'b': 1}]}
UPDATE table_name SET jsonb_field['a'][0]['b'] = '1';

-- Там, где поле jsonb_field было [], оно станет [null, {'a': 1}]
UPDATE table_name SET jsonb_field[1]['a'] = '1';

Преобразования

Для различных процедурных языков доступны дополнительные расширения, которые реализуют преобразования для типа jsonb.

Тип jsonpath

Тип jsonpath реализует поддержку языка путей SQL/JSON в QHB, позволяющего эффективно выполнять запросы данных JSON. Он обеспечивает двоичное представление разобранного выражения пути SQL/JSON, которое определяет, какие элементы нужно извлечь механизмом пути из данных JSON для дальнейшей обработки в функциях запроса SQL/JSON.

Семантика предикатов и операторов пути SQL/JSON в целом соответствует SQL. В то же время, чтобы обеспечить наиболее естественный способ работы с данными JSON, в синтаксисе пути SQL/JSON приняты некоторые соглашения JavaScript:

Точка (.) используется для доступа к члену объекта.

Квадратные скобки ([]) используются для доступа к массиву.

Массивы SQL/JSON нумеруются, начиная с 0, тогда как обычные массивы SQL — с 1.

Выражение пути SQL/JSON обычно записывается в запросе SQL в виде символьного строкового литерала, поэтому его необходимо заключать в апострофы, а любые апострофы, требуемые в этом значении, необходимо удвоить (см. подраздел Строковые константы). Строковые литералы также требуется использовать и внутри некоторых форм выражений пути. На такие встроенные строковые литералы распространяются соглашения JavaScript/ECMAScript: они должны заключаться в кавычки, а для представления символов, которые трудно ввести иначе, можно использовать экранированные символы с обратным слэшем. В частности, символ кавычки во встроенном строковом литерале записывается как \", а сам обратный слэш — как \\. Другие спецпоследовательности с обратным слэшем включают в себя те, которые распознаются в строках JSON: \b, \f, \n, \r, \t, \v для различных управляющих символов ASCII и \uNNNN для символа Unicode, идентифицируемого кодом из 4 шестнадцатеричных цифр. Синтаксис обратного слэша также включает два варианта, которые не допускаются JSON: \xNN для символьного кода в виде только двух шестнадцатеричных цифр и \u{N...} для символьного кода, записанного с использованием от 1 до 6 шестнадцатеричных цифр.

Выражение пути состоит из последовательности элементов пути, которые могут быть следующими:

Литералы пути примитивных типов JSON:Unicode text, numeric, true, false или null.

Переменные пути, перечисленные в Таблице 24.

Операторы аксессоров, перечисленные в Таблице 25.

Операторы и методы jsonpath, перечисленные в подразделе Операторы пути и методы SQL/JSON.

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

Подробную информацию об использовании выражений jsonpath с функциями запросов SQL/JSON см. в подразделе Язык путей SQL/JSON.

Таблица 24. Переменные jsonpath

Переменная | Описание
$ | Переменная, представляющая текст JSON для запроса (элемент контекста).
$varname | Именованная переменная. Ее значение можно задать в параметре vars некоторых функций обработки JSON. Подробную информацию см. в таблице 47 и ее примечаниях.
@ | Переменная, представляющая результат вычисления пути в выражениях фильтров.

Таблица 25. Операторы аксессоров jsonpath

Оператор аксессора
Описание

.ключ
."$имя_переменной"
Оператор аксессора к члену, который возвращает член объекта с указанным ключом. Если имя ключа задается именованной переменной, начинающейся с $, или не соответствует правилам JavaScript относительно идентификаторов, оно должно быть заключено в кавычки как символьный строковый литерал.

.*
Оператор аксессора по подстановочному символу, который возвращает значения всех элементов, расположенных на верхнем уровне текущего объекта.

.**
Рекурсивный оператор аксессора по подстановочному символу, который обрабатывает все уровни иерархии JSON текущего объекта и возвращает все значения элементов, независимо от уровня их вложенности. Это реализованное в QHB расширение стандарта SQL/JSON.

.**{уровень}
.**{начальный_уровень to конечный_уровень}
То же, что и .**, но с фильтром по уровням вложенности иерархии JSON. Уровни вложенности указываются как целые числа. Нулевой уровень соответствует текущему объекту. Чтобы получить доступ к самому низкому уровню вложенности, можно использовать ключевое слово last. Это реализованное в QHB расширение стандарта SQL/JSON.

[нижний_индекс, ...]
Оператор аксессора к элементу массива. Нижний_индекс можно задать в двух формах: индекс или начальный_индекс to конечный_индекс. Первая форма возвращает единственный элемент массива по его индексу. Вторая форма возвращает срез массива по диапазону индексов, включая элементы, которые соответствуют предоставленным начальному_индексу и конечному_индексу.

Задаваемый индекс может быть целым числом или выражением, возвращающим единственное числовое значение, которое автоматически приводится к целому числу. Нулевой индекс соответствует первому элементу массива. Также для обозначения последнего элемента массива можно использовать ключевое слово last, что полезно при обработке массивов неизвестной длины.

[*]
Оператор аксессора к элементам массива по подстановочному символу, который возвращает все элементы массива.

¹

Поэтому термин «значение» включает элементы массива, хотя в терминологии JSON иногда элементы массива считаются отличными от значений внутри объектов.

Массивы

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

Объявление типов массивов

Чтобы проиллюстрировать использование типов массивов, создадим эту таблицу:

CREATE TABLE sal_emp (
    name            text,
    pay_by_quarter  integer[],
    schedule        text[][]
);

Как показано, тип данных массива объявляется путем добавления квадратных скобок ([]) к имени типа данных элементов массива. Приведенная выше команда создаст таблицу с именем sal_emp со столбцом типа text (name), одномерный массив типа integer (pay_by_quarter), представляющий квартальную зарплату сотрудника, и двумерный массив text (schedule), представляющий недельный график сотрудника.

Синтаксис CREATE TABLE позволяет указывать точный размер массивов, например:

CREATE TABLE tictactoe (
    squares   integer[3][3]
);

Однако текущая реализация игнорирует любые предоставленные ограничения размера массива, т. е. все массивы обрабатываются как имеющие неопределенную длину.

Текущая реализация также не ограничивает число измерений. Все массивы с элементами определенного типа считаются одного типа, независимо от размера или числа измерений. Поэтому объявление размера массива или числа измерений в CREATE TABLE нужно только для документирования; на логику работы с массивом это не влияет.

Для одномерных массивов можно использовать альтернативный синтаксис с ключевым словом ARRAY, который соответствует стандарту SQL. Столбец pay_by_quarter можно было бы определить так:

    pay_by_quarter  integer ARRAY[4],

Или, если не указан размер массива:

    pay_by_quarter  integer ARRAY,

Однако, как и в предыдущем случае, QHB не налагает ограничения на фактический размер массива.

Ввод значений массива

Чтобы записать значение массива в виде литеральной константы, заключите значения элементов в фигурные скобки и разделите их запятыми. (Если вы знаете C/RUST, это очень похоже на синтаксис для инициализации структур в C/RUST.) Вы можете заключить в кавычки значение любого элемента, а если он содержит запятые или фигурные скобки, то должны сделать это обязательно. (Подробнее см. ниже.) Таким образом, общий формат константы массива выглядит следующим образом:

'{ значение1 разделитель значение2 разделитель ... }'

где разделитель — это символ, указанный в качестве разделителя для данного типа в его записи в таблице pg_type. Все стандартные типы данных, представленные в дистрибутиве QHB, используют запятую (,), за исключением типа box, в котором используется точка с запятой (;). Каждое значение является либо константой типа элемента массива, либо вложенным массивом. Пример константы массива:

'{{1,2,3},{4,5,6},{7,8,9}}'

Эта константа представляет собой двумерный массив размером 3x3, состоящий из трех подмножеств целых чисел.

Чтобы присвоить элементу константы массива значение NULL, нужно написать NULL для его значения. (При этом регистр символов не имеет значения.) Если же нужно добавить в массив строку со словом «NULL», нужно заключить его в кавычки.

(Эти виды констант массива на самом деле являются лишь частным случаем констант универсального типа, рассматриваемых в разделе Константы других типов. Константа первоначально обрабатывается как строка и передается процедуре преобразования входных данных массива. Может потребоваться явное указание целевого типа).

Теперь мы можем показать некоторые операторы INSERT:

INSERT INTO sal_emp
    VALUES ('Bill',
    '{10000, 10000, 10000, 10000}',
    '{{"meeting", "lunch"}, {"training", "presentation"}}');

INSERT INTO sal_emp
    VALUES ('Carol',
    '{20000, 25000, 25000, 25000}',
    '{{"breakfast", "consulting"}, {"meeting", "lunch"}}');

Результат двух предыдущих добавлений выглядит так:

SELECT * FROM sal_emp;
 name  |      pay_by_quarter       |                 schedule
-------+---------------------------+-------------------------------------------
 Bill  | {10000,10000,10000,10000} | {{meeting,lunch},{training,presentation}}
 Carol | {20000,25000,25000,25000} | {{breakfast,consulting},{meeting,lunch}}
(2 rows)

У многомерных массивов все размерности должны быть одинаковой величины, иначе возникнет ошибка. Например:

INSERT INTO sal_emp
    VALUES ('Bill',
    '{10000, 10000, 10000, 10000}',
    '{{"meeting", "lunch"}, {"meeting"}}');
ERROR:  multidimensional arrays must have array expressions with matching dimensions
(ОШИБКА: выражения многомерных массивов должны задаваться с соответствующими размерностями)

Также можно использовать синтаксис конструктора ARRAY:

INSERT INTO sal_emp
    VALUES ('Bill',
    ARRAY[10000, 10000, 10000, 10000],
    ARRAY[['meeting', 'lunch'], ['training', 'presentation']]);

INSERT INTO sal_emp
    VALUES ('Carol',
    ARRAY[20000, 25000, 25000, 25000],
    ARRAY[['breakfast', 'consulting'], ['meeting', 'lunch']]);

Обратите внимание, что элементы массива являются обычными константами или выражениями SQL; например, строковые литералы заключаются в апострофы, а не в кавычки, как это было бы в литерале массива. Синтаксис конструктора ARRAY более подробно рассматривается в разделе Конструкторы массивов.

Доступ к массивам

Теперь можно выполнить несколько запросов к таблице. Сначала мы покажем, как обратиться к одному элементу массива. Этот запрос выводит имена сотрудников, зарплата которых изменилась во втором квартале:

SELECT name FROM sal_emp WHERE pay_by_quarter[1] <> pay_by_quarter[2];

 name
-------
 Carol
(1 row)

Номера индексов массива пишутся в квадратных скобках. По умолчанию в QHB действует соглашение о нумерации массивов, начиная с единицы, то есть массив из n элементов начинается с array[1] и заканчивается array[n].

Этот запрос выводит зарплату всех сотрудников за третий квартал:

SELECT pay_by_quarter[3] FROM sal_emp;

 pay_by_quarter
----------------
          10000
          25000
(2 rows)

Также возможно получить доступ к произвольным прямоугольным срезам массива, или подмассивам. Срез массива обозначается записью нижняя-граница:верхняя- граница для одной или нескольких размерностей. Например, этот запрос выводит первые пункты в расписании Билла за первые два дня недели:

SELECT schedule[1:2][1:1] FROM sal_emp WHERE name = 'Bill';

        schedule
------------------------
 {{meeting},{training}}
(1 row)

Если какая-либо размерность записывается как срез, т. е. содержит двоеточие, то все размерности рассматриваются как срезы. Если при этому у размерности указано только одно число (без двоеточия), в срез войдут элементы от 1 до указанного числа. Например, [2] обрабатывается как [1:2], как показано в этом примере:

SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';

                 schedule
-------------------------------------------
 {{meeting,lunch},{training,presentation}}
(1 row)

Во избежание путаницы с обращением к элементу, лучше использовать синтаксис срезов для всех размерностей, например, писать [1:2][1:1], а не [2][1:1].

Значения нижняя-граница и/или верхняя-граница в указании среза можно опустить; отсутствующая граница заменяется нижним или верхним пределом индексов массива. Например:

SELECT schedule[:2][2:] FROM sal_emp WHERE name = 'Bill';

        schedule
------------------------
 {{lunch},{presentation}}
(1 row)

SELECT schedule[:][1:1] FROM sal_emp WHERE name = 'Bill';

        schedule
------------------------
 {{meeting},{training}}
(1 row)

Выражение индекса массива вернет значение NULL, если либо сам массив, либо любое из выражений индексов его элемента равно NULL. Кроме того, NULL возвращается, если индекс находится за пределами границ массива (это не вызывает ошибку). Например, если массив schedule в настоящее время имеет размеры [1:3][1:2], то обращение к schedule[3][3] выдаст NULL. Точно так же при обращении к массиву с неправильным числом индексов возвращается NULL, а не ошибка.

Выражение среза массива также вернет NULL, если сам массив или любое из выражений индексов его элемента равно NULL. Однако в других случаях, например, при выборе среза, который находится за пределами текущих границ массива, возвращается не NULL, пустой массив (с размерностью 0). (Это отличается от обращений к элементам и выполняется по историческим причинам). Если запрашиваемый срез частично выходит за границы массива, то возвращается не NULL, а срез, автоматически сокращенный до области пересечения.

Текущие размеры любого значения массива можно получить с помощью функции array_dims:

SELECT array_dims(schedule) FROM sal_emp WHERE name = 'Carol';

 array_dims
------------
 [1:2][1:2]
(1 row)

array_dims выдает результат типа text, что удобно скорее для людей, чем для программ. Размеры массива также можно получить с помощью функций array_upper и array_lower, которые возвращают верхнюю и нижнюю границу указанной размерности соответственно:

SELECT array_lower(schedule, 1), array_upper(schedule, 1) FROM sal_emp WHERE name = 'Carol';

  array_lower array_upper
--------------------------
  1           2
(1 row)

Функция array_length вернет число элементов указанной размерности массива:

SELECT array_length(schedule, 1) FROM sal_emp WHERE name = 'Carol';

 array_length
--------------
            2
(1 row)

Функция cardinality возвращает общее количество элементов в массиве по всем измерениям. По сути это число строк, которое возвращает функция unnest:

SELECT cardinality(schedule) FROM sal_emp WHERE name = 'Carol';

 cardinality
-------------
           4
(1 row)

Изменение массивов

Значение массива можно полностью заменить следующим образом:

UPDATE sal_emp SET pay_by_quarter = '{25000,25000,27000,27000}'
    WHERE name = 'Carol';

или используя синтаксис выражения ARRAY:

UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
    WHERE name = 'Carol';

Также можно изменить один элемент массива:

UPDATE sal_emp SET pay_by_quarter[4] = 15000
    WHERE name = 'Bill';

или срез:

UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
    WHERE name = 'Carol';

При этом в указании среза можно опустить значение нижняя-граница и/или верхняя-граница, только при изменении значения массива, отличного от NULL и не имеющего нулевой размерности (в противном случае не будет граничных значений индекса, которые должны подставлять вместо опущенных).

Сохраненный массив можно расширять, присваивая значения элементам, которых в нем еще нет. Все позиции между уже существующими и новыми элементами будут заполнены значениями NULL. Например, если массив myarray содержит 4 элемента, после присвоения значение элементу myarray[6] в нем будет 6 элементов, а myarray[5]будет содержать NULL. В настоящее время подобное расширение допускается только для одномерных, но не многомерных массивов.

Определение элементов по индексам позволяет создавать массивы, в которых нумерация элементов начинается не с 1. Например, можно записать выражение myarray[-2:7], тем самым создав массив с индексами от -2 до 7.

Новые значения массива также можно создать с помощью оператора конкатенации, ||:

SELECT ARRAY[1,2] || ARRAY[3,4];
 ?column?
-----------
 {1,2,3,4}
(1 row)

SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
      ?column?
---------------------
 {{5,6},{1,2},{3,4}}
(1 row)

Оператор конкатенации позволяет вставить отдельный элемент в начало или конец одномерного массива. Он также принимает два N-мерных массива или N-мерный и N+1-мерный массивы.

Когда отдельный элемент помещается в начало или конец одномерного массива, в результаты получается массив с тем же индексом нижней границы, что и у массива- операнда. Например:

SELECT array_dims(1 || '[0:1]={2,3}'::int[]);
 array_dims
------------
 [0:2]
(1 row)

SELECT array_dims(ARRAY[1,2] || 3);
 array_dims
------------
 [1:3]
(1 row)

Когда объединяются два массива с равным числом размерностей, в результате сохраняется индекс нижней границы внешней размерности левого операнда. Получившийся массив содержит все элементы левого операнда, за которыми следуют все элементы правого операнда. Например:

SELECT array_dims(ARRAY[1,2] || ARRAY[3,4,5]);
 array_dims
------------
 [1:5]
(1 row)

SELECT array_dims(ARRAY[[1,2],[3,4]] || ARRAY[[5,6],[7,8],[9,0]]);
 array_dims
------------
 [1:5][1:2]
(1 row)

Когда N-мерный массив помещается в начало или конец N+1-мерного массива, он вставляется так же, как элемент (описано выше). Каждый N-мерный подмассив по сути является элементом внешней размерности N+1-мерного массива. Например:

SELECT array_dims(ARRAY[1,2] || ARRAY[[3,4],[5,6]]);
 array_dims
------------
 [1:3][1:2]
(1 row)

Также массив можно создать с помощью функций array_prepend, array_append или array_cat. Первые две функции поддерживают только одномерные массивы, а array_cat поддерживает и многомерные. Несколько примеров:

SELECT array_prepend(1, ARRAY[2,3]);
 array_prepend
---------------
 {1,2,3}
(1 row)

SELECT array_append(ARRAY[1,2], 3);
 array_append
--------------
 {1,2,3}
(1 row)

SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
 array_cat
-----------
 {1,2,3,4}
(1 row)

SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
      array_cat
---------------------
 {{1,2},{3,4},{5,6}}
(1 row)

SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
      array_cat
---------------------
 {{5,6},{1,2},{3,4}}

В простых случаях вместо прямого вызова этих функций предпочтительнее рассмотренный выше оператор конкатенации. Однако, поскольку оператор конкатенации перегружен для выполнения всех трех заданий, в некоторых ситуациях во избежание неоднозначности лучше воспользоваться одной из этих функций. Например, рассмотрим:

SELECT ARRAY[1, 2] || '{3, 4}';  -- нетипизированный литерал воспринимается как массив
 ?column?
-----------
 {1,2,3,4}

SELECT ARRAY[1, 2] || '7';                 -- как и этот
ERROR:  malformed array literal: "7"

SELECT ARRAY[1, 2] || NULL;                -- как и буквальный NULL
 ?column?
----------
 {1,2}
(1 row)

SELECT array_append(ARRAY[1, 2], NULL);    -- возможно, это и имелось в виду
 array_append
--------------
 {1,2,NULL}

В приведенных выше примерах анализатор видит целочисленный массив на одной стороне оператора конкатенации и константу неопределенного типа на другой. Эвристический алгоритм, который он использует для разрешения типа константы, должен полагать, что она имеет тот же тип, что и другие входные данные оператора — в данном случае целочисленный массив. Поэтому предполагается, что оператор конкатенации представляет array_cat, а не array_append. Если это неправильный выбор, его можно исправить, приведя константу к типу элемента массива; но, возможно, лучше будет явно использовать array_append.

Поиск в массивах

Для поиска значения в массиве необходимо проверить каждое значение. Это можно сделать вручную, если вы знаете размер массива. Например:

SELECT * FROM sal_emp WHERE pay_by_quarter[1] = 10000 OR
                            pay_by_quarter[2] = 10000 OR
                            pay_by_quarter[3] = 10000 OR
                            pay_by_quarter[4] = 10000;

Однако с большими массивами этот метод быстро становится утомительным, а если размер массива неизвестен, то и бесполезным. Альтернативный метод описан в разделе Сравнение строк и массивов. Приведенный выше запрос можно заменить на:

SELECT * FROM sal_emp WHERE 10000 = ANY (pay_by_quarter);

А таким образом можно найти строки, в которых массивы содержат только значения, равные 10000:

SELECT * FROM sal_emp WHERE 10000 = ALL (pay_by_quarter);

В качестве альтернативы можно использовать функцию generate_subscripts. Например:

SELECT * FROM
   (SELECT pay_by_quarter,
           generate_subscripts(pay_by_quarter, 1) AS s
      FROM sal_emp) AS foo
 WHERE pay_by_quarter[s] = 10000;

Эта функция описана в таблице Функции возврата наборов (SET).

Также поиск в массиве можно выполнить с помощью оператора &&, который проверяет, перекрывается ли левый операнд с правым. Например:

SELECT * FROM sal_emp WHERE pay_by_quarter && ARRAY[10000];

Этот и другие операторы для работы с массивами более подробно описаны в разделе Функции и операторы массива. Его можно ускорить с помощью соответствующего индекса, как описано в разделе Типы индексов.

Также в массиве можно искать конкретные значения, используя функции array_position и array_positions. Первая возвращает индекс первого вхождения значения в массив, вторая возвращает массив с индексами всех его вхождений. Например:

SELECT array_position(ARRAY['sun','mon','tue','wed','thu','fri','sat'], 'mon');
 array_positions
-----------------
 2

SELECT array_positions(ARRAY[1, 4, 3, 1, 3, 4, 2, 1], 1);
 array_positions
-----------------
 {1,4,8}

Совет
Массивы не являются множествами; необходимость поиска определенных элементов массива может быть признаком неправильного проектирования базы данных. Рассмотрите возможность использования отдельной таблицы со строками для всех элементов массива. Это облегчит поиск и, скорее всего, позволит лучше оперировать большим количеством элементов.

Синтаксис вводимых и выводимых значений массива

Внешнее текстовое представление значения массива состоит из элементов, которые интерпретируются в соответствии с правилами преобразования ввода-вывода для типа элемента массива, а также элементов оформления, которые указывают на структуру массива. Оформление состоит из фигурных скобок ({ и }) вокруг значения массива и символов-разделителей между соседними элементами. Символом- разделителем обычно служит запятая (,), но это может быть и другой знак: он определяется настройкой параметра typdelim для типа элемента массива. Для стандартных типов данных, представленных в дистрибутиве QHB, в качестве разделителя используется запятая, за исключением типа box, в котором используется точка с запятой (;). В многомерном массиве у каждой размерности (строка, плоскость, куб и т. д.) есть свой уровень фигурных скобок, а между соседними объектами в фигурных скобках на одном уровне должны стоять разделители.

Функция вывода массива помещает значения элементов в кавычки, если те являются пустыми строками, содержат фигурные скобки, символы-разделители, кавычки, обратный слэш, пробел или это слово NULL. Кавычки и обратные слэши, включенные в значения элементов, экранируются обратным слэшем. Можно с уверенностью предположить, что числовые типы данных никогда не будут выводиться в кавычках, но следует быть готовыми к тому, что в текстовых типах они могут появиться.

По умолчанию значение индекса нижней границы размерностей массива равно 1. Для представления массивов с другими нижними границами можно явно указать диапазоны индексов перед содержимым массива. Это оформление включает в себя квадратные скобки ([]) вокруг нижней и верхней границ каждой размерности с двоеточием (:) в качестве разделителя между ними. За этим выражением следует знак равенства (=). Например:

SELECT f1[1][-2][3] AS e1, f1[1][-1][5] AS e2
 FROM (SELECT '[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}'::int[] AS f1) AS ss;

 e1 | e2
----+----
  1 |  6
(1 row)

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

Если в качестве значения элемента записывается NULL (в любом варианте), этот элемент считается равным NULL. Если же запись включает кавычки или обратный слэш, это позволяет вводить буквальное строковое значение литерала «NULL».

Как было показано ранее, при записи значения массива любой его отдельный элемент можно заключить в кавычки. Это нужно делать в том случае, если значение элемента без кавычек может запутать синтаксический анализатор значений массива. Например, в кавычки нужно заключать элементы, содержащие фигурные скобки, запятые (или символ-разделитель, определенный для этого типа данных), кавычки, обратные слэши или пробелы в начале или конце строки. Пустые строки и строки, содержащие слову NULL, также должны быть заключены в кавычки. Чтобы поместить кавычку или обратный слэш в значение элемента массива, заключенного в кавычки, поставьте перед этим символом обратный слэш. Или же можно обойтись без кавычек и использовать обратный слэш для экранирования всех символов в данных, которые в противном случае могут быть восприняты как синтаксис массива.

Перед открывающей или после закрывающей скобки можно добавить пробел. Также можно добавить пробел до или после любой отдельной строки элемента. Во всех этих случаях пробелы будут игнорироваться. Однако пробелы в элементах, заключенных в кавычки, или окруженные с обеих сторон отличными от пробелов символами, учитываются.

Совет
При записи значений массива в командах SQL зачастую удобнее работать с синтаксисом конструктора ARRAY (см. раздел Конструкторы массивов), чем с синтаксисом литерала массива. В ARRAY значения отдельных элементов записываются так же, как если бы они не были членами массива.

Составные типы

Составной тип представляет структуру строки или записи; по существу, это просто список имен полей и их типов данных. QHB позволяет использовать составные типы во многом так же, как и простые типы. Например, столбец таблицы можно объявить как составной тип.

Объявление составных типов

Вот два простых примера определения составных типов:

CREATE TYPE complex AS (
    r       double precision,
    i       double precision
);

CREATE TYPE inventory_item AS (
    name            text,
    supplier_id     integer,
    price           numeric
);

Синтаксис похож на CREATE TABLE, за исключением того, что можно указывать только имена и типы полей; какие-либо ограничения (такие как NOT NULL) в настоящее время не поддерживаются. Обратите внимание, что ключевое слово AS имеет важное значение; без него система будет думать, что подразумевается другой вид команды CREATE TYPE, и вы получите странные синтаксические ошибки.

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

CREATE TABLE on_hand (
    item      inventory_item,
    count     integer
);

INSERT INTO on_hand VALUES (ROW('fuzzy dice', 42, 1.99), 1000);

или функций:

CREATE FUNCTION price_extension(inventory_item, integer) RETURNS numeric
AS 'SELECT $1.price * $2' LANGUAGE SQL;

SELECT price_extension(item, 10) FROM on_hand;

Каждый раз при создании таблицы вместе с ней автоматически создается составной тип с тем же именем, что и таблица. Этот тип представляет тип строки таблицы. Например, при создании таблицы inventory_item:

CREATE TABLE inventory_item (
    name            text,
    supplier_id     integer REFERENCES suppliers,
    price           numeric CHECK (price > 0)
);

в качестве побочного результата возникнет такой же составной тип inventory_item, что и в примере выше, и использовать его можно так же. Однако обратите внимание на важное ограничение текущей реализации: поскольку с составным типом не связаны никакие ограничения, те ограничения, что показаны в определении таблицы, не применяются к значениям составного типа вне таблицы. (Чтобы обойти это, создайте поверх составного типа домен и добавьте желаемые ограничения в виде ограничений CHECK для домена.)

Построение составных значений

Чтобы записать составное значение в виде литеральной константы, заключите значения полей в круглые скобки и разделите их запятыми. Любое значения поля можно заключить в кавычки, и это необходимо делать, если оно содержит запятые или скобки. (Подробнее см. ниже). Таким образом, в общем формате составная константа записывается следующим образом:

'( значение1, значение2, ... )'

Примером является:

'("fuzzy dice",42,1.99)'

что будет допустимым значением типа inventory_item, определенного выше. Чтобы присвоить полю NULL, его позицию в списке нужно оставить пустой. Например, эта константа задает NULL для третьего поля:

'("fuzzy dice",42,)'

Если нужна пустая строка, а не NULL, напишите кавычки:

'("",42,)'

Здесь в первом поле будет пустая строка, а в третьем — NULL.

(Эти константы на самом деле являются лишь частным случаем констант универсального типа, рассматриваемых в разделе Константы других типов. Константа изначально воспринимается как строка и передается программе преобразования входных данных составного типа. Чтобы определить, к какому типу следует привести константу, может потребоваться явно указать тип.)

Также для создания составных значений можно использовать синтаксис выражения ROW. В большинстве случаев он гораздо удобнее, чем синтаксис строкового литерала, поскольку нет нужды беспокоиться о нескольких уровнях цитирования. Мы уже использовали этот метод ранее:

ROW('fuzzy dice', 42, 1.99)
ROW('', 42, NULL)

На самом деле, если в выражении указано более одного поля, ключевое слово ROW указывать необязательно, поэтому эту запись можно упростить до:

('fuzzy dice', 42, 1.99)
('', 42, NULL)

Синтаксис выражения ROW более подробно рассматривается в разделе Конструкторы строк.

Доступ к составным типам

Чтобы получить доступ к полю составного столбца, нужно написать точку и имя поля, как при выборе поля из имени таблицы. На самом деле, эти обращения почти неотличимы, поэтому зачастую необходимо использовать круглые скобки, чтобы не запутать синтаксический анализатор. Например, можно попробовать выбрать несколько подполей из тестовой таблицы on_hand следующим образом:

SELECT item.name FROM on_hand WHERE item.price > 9.99;

Это не будет работать, так как согласно правилам синтаксиса SQL имя item считается именем таблицы, а не столбца on_hand. Следует написать запрос так:

SELECT (item).name FROM on_hand WHERE (item).price > 9.99;

или, если требуется также указать имя таблицы (например, в запросе с несколькими таблицами), то так:

SELECT (on_hand.item).name FROM on_hand WHERE (on_hand.item).price > 9.99;

Теперь заключенный в скобки объект правильно интерпретируется как ссылка на столбец item, из которого затем можно выбрать субполе.

Подобные синтаксические проблемы возникают всякий раз, когда вы выбираете поле из составного значения. Например, чтобы выбрать только одно поле из результата функции, возвращающей составное значение, нужно написать что-то вроде:

SELECT (my_func(...)).field FROM ...

Без дополнительных скобок это приведет к синтаксической ошибке.

Специальное имя поля * означает «все поля»; более подробно это описано в подразделе Использование составных типов в запросах.

Изменение составных типов

Вот несколько примеров правильного написания команд для добавления и изменения составных столбцов. Прежде всего, команды для добавления и изменения всего столбца:

INSERT INTO mytab (complex_col) VALUES((1.1,2.2));

UPDATE mytab SET complex_col = ROW(1.1,2.2) WHERE ...;

В первом примере ключевое слово ROW опущено, во втором оно есть; оставить или опустить его можно в обоих случаях.

Можно изменить отдельное подполе составного столбца:

UPDATE mytab SET complex_col.r = (complex_col).r + 1 WHERE ...;

Обратите внимание, что при этом не нужно (и, более того, нельзя) заключать в скобки имя столбца, следующего за предложением SET, но при обращении к тому же столбцу в выражении справа от знака равенства скобки обязательны.

Также можно указать подполя как цели команды INSERT:

INSERT INTO mytab (complex_col.r, complex_col.i) VALUES(1.1, 2.2);

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

Использование составных типов в запросах

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

В QHB ссылка на имя (или псевдоним) таблицы в запросе фактически является ссылкой на составное значение текущей строки в этой таблице. Например, имея таблицу inventory_item, показанную выше, мы можем написать:

SELECT c FROM inventory_item c;

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

c
------------------------
("fuzzy dice",42,1.99)
(1 row)

Однако обратите внимание, что простые имена сопоставляются сначала с именами столбцов и только потом с именами таблиц, поэтому данный пример работает только потому, что в таблицах запроса нет столбца с именем c.

Обычный синтаксис полного имени столбца вида имя_таблицы.имя_столбца можно понимать как применение выбора поля к составному значению текущей строки таблицы. (Из соображений эффективности на самом деле это реализовано по-другому.)

Когда мы пишем

SELECT c.* FROM inventory_item c;

то, согласно стандарту SQL, мы должны получить содержимое таблицы, развернутое в отдельные столбцы:

name       | supplier_id | price
-----------+-------------+-------
fuzzy dice |          42 |  1.99
(1 row)

как будто выполнялся запрос

SELECT c.name, c.supplier_id, c.price FROM inventory_item c;

QHB будет применять это развертывание к любому составному выражению, хотя, как показано выше, значение, к которому применяется .*, следует заключать в скобки в случаях, когда это не простое имя таблицы. Например, если myfunc() — это функция, возвращающая составной тип со столбцами a, b и c, то эти два запроса выдадут одинаковый результат:

SELECT (myfunc(x)).* FROM some_table;
SELECT (myfunc(x)).a, (myfunc(x)).b, (myfunc(x)).c FROM some_table;

Совет
QHB выполняет развертывание столбцов, фактически превращая первую форму во вторую. Таким образом, в этом примере myfunc() будет вызываться три раза в каждой строке с любым из этих синтаксисов. Если это затратная функция и вы хотите избежать лишних вызовов, можно написать такой запрос:

SELECT m.* FROM some_table, LATERAL myfunc(x) AS m;

Размещение функции в элемент LATERAL FROM позволяет вызывать ее для строки не более одного раза. Элемент m.* по-прежнему разворачивается в m.a, m.b и m.c, но теперь эти переменные являются просто ссылками на выходные значения элемента FROM. (Ключевое слово LATERAL здесь необязательно, но мы указываем его, чтобы подчеркнуть, что функция получает x из some_table.)

Синтаксис составное_значение.* приводит к такому развертыванию столбца, что он появляется на верхнем уровне выходного списка SELECT, списка RETURNING команд INSERT/UPDATE/DELETE, предложения VALUES или конструктора строки. Во всех других контекстах (в том числе вложенные в одну из этих конструкций) добавление .* к составному значению не меняет его, поскольку это означает «все столбцы», и поэтому снова выдается то же составное значение. Например, если somefunc() принимает составное значение в качестве аргумента, эти запросы равносильны:

SELECT somefunc(c.*) FROM inventory_item c;
SELECT somefunc(c) FROM inventory_item c;

В обоих случаях текущая строка inventory_item передается функции в виде одного аргумента с составным значением. Несмотря на то, что в таких случаях .* ни на что не влияет, его использование считается хорошим тоном, поскольку это ясно показывает, что здесь имеется в виду составное значение. В частности, синтаксический анализатор воспримет c в записи c.* как ссылку на имя или псевдоним таблицы, а не имя столбца, что убирает неоднозначность; тогда как без .* неясно, означает ли c имя таблицы или имя столбца, и на самом деле если есть столбец с именем c, будет выбрана вторая интерпретация.

Эти концепции демонстрирует следующий пример, в котором все запросы означают одно и то же:

SELECT * FROM inventory_item c ORDER BY c;
SELECT * FROM inventory_item c ORDER BY c.*;
SELECT * FROM inventory_item c ORDER BY ROW(c.*);

Все эти предложения ORDER BY задают составное значение строки, в результате чего строки сортируются в соответствии с правилами, описанными в разделе Сравнение составных типов. Однако если строка inventory_item содержит столбец с именем c, то первый запрос будет отличаться от других, поскольку подразумевает сортировку только по этому столбцу. Учитывая показанные выше имена столбцов, эти запросы также равнозначны предыдущим:

SELECT * FROM inventory_item c ORDER BY ROW(c.name, c.supplier_id, c.price);
SELECT * FROM inventory_item c ORDER BY (c.name, c.supplier_id, c.price);

(В последнем случае используется конструктор строки с опущенным ключевым словом ROW).

Другая особенность синтаксиса, связанная с составными значениями, состоит в том, что для извлечения поля составного значения можно использовать функциональную запись. Это легко объяснить тем, что записи поле(таблица) и таблица.поле взаимозаменяемы. Например, эти запросы равнозначны:

SELECT c.name FROM inventory_item c WHERE c.price > 1000;
SELECT name(c) FROM inventory_item c WHERE price(c) > 1000;

Более того, если у нас есть функция, которая принимает один аргумент составного типа, мы можем вызвать ее любой записью. Все эти запросы равнозначны:

SELECT somefunc(c) FROM inventory_item c;
SELECT somefunc(c.*) FROM inventory_item c;
SELECT c.somefunc FROM inventory_item c;

Эта равнозначность между функциональной записью и записью полей позволяет использовать с составными типами функции, реализующие «вычисляемые поля». Приложению, использующему последний из запросов выше, не нужно явно знать, что somefunc — не настоящий столбец таблицы.

Синтаксис вводимых и выводимых значений составного типа

Внешнее текстовое представление составного значения состоит из элементов, которые интерпретируются в соответствии с правилами преобразования ввода-вывода для отдельных типов полей, а также элементов оформления, которые указывают на составную структуру. Оформление состоит из круглых скобок ((и)) вокруг всего значения и запятых (,) между соседними элементами. Пробелы вне скобок игнорируются, но в скобках они считаются частью значения поля и могут учитываться или не учитываться в зависимости от правил преобразования входных данных для типа данных поля. Например, в записи:

'(  42)'

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

Как было показано ранее, при написании составного значения любой его отдельный элемент можно заключить в кавычки. Это нужно делать в том случае, если значение элемента без кавычек может запутать синтаксический анализатор составных значений. Например, в кавычки нужно заключать поля, содержащие круглые скобки, запятые, кавычки или обратные слэши. Чтобы поместить кавычку или обратный слэш в элемент составного значения, заключенного в кавычки, поставьте перед этим символом обратный слэш. (Кроме того, пара кавычек в значении поля, заключенного в кавычки, используется для представления символа одинарной кавычки аналогично правилам для апострофов в строковых литералах SQL.) Или же можно обойтись без кавычек и использовать обратный слэш для экранирования всех символов в данных, которые в противном случае могут быть восприняты как синтаксис составного значения.

Абсолютно пустое значение поля (без запятых или круглых скобок) означает NULL. Чтобы ввести именно пустую строку а не NULL, напишите "".

Программа вывода составных значений помещает кавычки вокруг значений полей, если те являются пустыми строками или содержат круглые скобки, запятые, кавычки, обратный слэш или пробел. (Для пробелов это делать необязательно, но это способствует удобочитаемости.) Кавычки и обратные слэши, заключенные в значения полей, будут удвоены.

Примечание
Помните, что все, что вы пишете в команде SQL, сначала будет интерпретироваться как строковый литерал, а затем как составное значение. Из-за этого количество необходимых обратных слэшей удваивается (при условии использования синтаксиса с экранированием строки). Например, чтобы вставить в составное значение поле text с кавычкой и обратным слэшем, нужно написать:

INSERT ... VALUES ('("\"\\")');

Обработчик строковых литералов удаляет один уровень обратного слэша, так что синтаксический анализатор составных значений получает на вход ("\"\\"). В свою очередь, в программу ввода типа данных text попадает уже строка "\. (Если бы мы работали с типом данных, у которого программа ввода также обрабатывает обратный слэш особым образом, например bytea, нам могло бы потребоваться уже восемь обратных слэшей в команде, чтобы получить один в сохраненном поле составного значения.) Во избежание необходимости такого дублирования символов строки можно заключать в знаки доллара (см. раздел Строковые константы с экранированием знаками доллара).

Совет
При записи составных значений в командах SQL зачастую удобнее работать с синтаксисом конструктора ROW, чем с синтаксисом литерала составного типа. В ROW значения отдельных элементов записываются так же, как если бы они не были членами составного выражения.

Диапазонные типы

Диапазонные типы — это типы данных, представляющие диапазон значений типа некоторого элемента (также называемого подтипом диапазона). Например, диапазоны типа timestamp можно использовать для представления временного интервала, когда зарезервирована переговорная. В этом случае типом данных будет tsrange (сокращение от «timestamp range»), а подтипом — timestamp. Подтип должен быть полностью упорядочен, чтобы было четко определено, где находятся значения элемента: внутри, до или после диапазона.

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

Встроенные диапазонные типы

QHB поставляется со следующими встроенными диапазонными типами:

int4range — диапазон подтипа integer

int8range — диапазон подтипа bigint

numrange — диапазон подтипа numeric

tsrange — диапазон подтипа timestamp without time zone

tstzrange — диапазон подтипа timestamp with time zone

daterange — диапазон подтипа date

Кроме того, вы можете определить собственные диапазонные типы; подробную информацию см. в описании команды CREATE TYPE.

Примеры

CREATE TABLE reservation (room int, during tsrange);
INSERT INTO reservation VALUES
    (1108, '[2010-01-01 14:30, 2010-01-01 15:30)');

-- Вхождение
SELECT int4range(10, 20) @> 3;

-- Перекрытие
SELECT numrange(11.1, 22.2) && numrange(20.0, 30.0);

-- Получение верхней границы
SELECT upper(int8range(15, 25));

-- Вычисление пересечения
SELECT int4range(10, 20) * int4range(15, 25);

-- Является ли диапазон пустым?
SELECT isempty(numrange(1, 5));

Полный список операторов и функций для диапазонных типов приведен в разделе Функции диапазона и операторы.

Включение и исключение границ

Каждый непустой диапазон имеет две границы: нижнюю и верхнюю. Все точки между этими значениями включены в диапазон. Включение границы означает, что сама граничная точка также включена в диапазон, а исключение — что она не включена.

В текстовой записи диапазона включение нижней границы обозначается символом "[", а исключение — символом "(". Аналогично включение верхней границы обозначается символом "]", а исключение — символом ")". (Подробную информацию см. в подразделе Ввод/вывод диапазонов).

Функции lower_inc и upper_inc проверяют, включается ли нижняя или верхняя граница в диапазон, соответственно.

Бесконечные (неограниченные) диапазоны

Нижнюю границу диапазона можно опустить, обозначая тем самым, что в диапазон включены все значения, лежащие ниже верхней границы, например (,3]. Аналогичным образом, если опустить верхнюю границу диапазона, то в него войдут все значения, лежащие выше нижней границы. Если же опустить обе границы, то в диапазон войдут все значения подтипа. Указание отсутствующей границы как включаемой в диапазон автоматически преобразуется в ее исключение, например, [,] преобразуется в (,). Эти отсутствующие значения можно воспринимать как +/- бесконечность, но все же это особые значения типа диапазона, которые считаются выходящими за пределы значений +/- бесконечность любого подтипа.

Для подтипов, в которых есть понятие «бесконечность», infinity можно использовать в качестве явного значения границы. Например, в диапазонах меток времени запись [today,infinity) подтипа timestamp исключает его особое значение infinity, тогда как запись [today,infinity] его включает, как и записи [today,) и [today,].

Функции lower_inf и upper_inf проверяют, опущена ли нижняя или верхняя границы диапазона, соответственно.

Ввод/вывод диапазонов

Вводимое значение диапазона должно записываться по одному из следующих шаблонов:

(нижняя-граница,верхняя-граница)
(нижняя-граница,верхняя-граница]
[нижняя-граница,верхняя-граница)
[нижняя-граница,верхняя-граница]
empty

Тип скобок (круглые или квадратные) указывает, включены ли в диапазон нижняя и верхняя границы, как описано выше. Обратите внимание, что последний шаблон содержит только слово empty и обозначает пустой диапазон (диапазон, не содержащий точек).

Нижняя-граница может быть либо строкой с допустимым входным значением подтипа, либо быть пустой, указывая на отсутствие нижней границы. Подобным же образом верхняя-граница может задаваться допустимым входным значением подтипа или быть пустой (неограниченной).

Каждое значение границы можно заключить в кавычки ("). Это необходимо делать, если значение границы содержит круглые или квадратные скобки, запятые, кавычки или обратный слэш, поскольку иначе эти символы будут рассматриваться как часть синтаксиса диапазона. Чтобы поместить в значение диапазона, заключенное в кавычки, кавычку или обратный слэш, нужно вставить перед ними перед ним обратный слэш. (Кроме того, пара кавычек в значении диапазона, заключенного в кавычки, используется для представления символа одинарной кавычки аналогично правилам для апострофов в строковых литералах SQL.) Или же можно обойтись без кавычек и использовать обратный слэш для экранирования всех символов в данных, которые в противном случае могут быть восприняты как синтаксис диапазона. Кроме того, чтобы задать в качестве значения границы пустую строку, следует написать "", так как пустая строка означает отсутствие границы.

Пробел допускается до и после значения диапазона, но любой пробел внутри круглых или квадратных скобок воспринимается как часть значения нижней или верхней границы. (Хотя в некоторых подтипах такие пробелы могут игнорироваться.)

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

Примеры:

-- в диапазон включается 3, не включается 7 и включаются все точки между ними
SELECT '[3,7)'::int4range;

-- в диапазон не включаются ни 3, ни 7, но включаются все точки между ними
SELECT '(3,7)'::int4range;

-- в диапазон включается только значение 4
SELECT '[4,4]'::int4range;

-- диапазон не включает никаких точек (нормализация заменит эту запись на 'empty')
SELECT '[4,4)'::int4range;

Конструирование диапазонов

Для каждого типа диапазонов имеется функция-конструктор с тем же именем, что и данный тип. Использование этого конструктора зачастую более удобно, чем запись литеральной константы диапазона, поскольку это избавляет от необходимости заключать значения границ в дополнительные кавычки. Функция-конструктор может принимать два или три аргумента. Вариант с двумя аргументами создает диапазон в стандартной форме (нижняя граница включена, верхняя граница исключена), тогда как вариант с тремя аргументами создает диапазон, у которого включение границ определяется третьим аргументом. Этот аргумент должен содержать одну из строк: "()", "(]", "[)" или "[]". Например:

-- Полная форма: нижняя граница, верхняя граница и текстовый аргумент, определяющий
-- включение/исключение границ.
SELECT numrange(1.0, 14.0, '(]');

-- Если третий аргумент опущен, подразумевается '[)'.
SELECT numrange(1.0, 14.0);

-- Хотя здесь указывается '(]', при выводе значение будет преобразовано в
-- каноническую форму, так как int8range — это тип дискретного диапазона (см. ниже).
SELECT int8range(1, 14, '(]');

-- Если указать NULL вместо любой из границ, этой границы у диапазона не будет.
SELECT numrange(NULL, 2.2);

Типы дискретных диапазонов

Дискретный диапазон — это диапазон, для подтипа которого четко определен «шаг», как, например, для типов integer и date. Элементы этих типов можно назвать соседними, когда между ними нет никаких допустимых значений. Это отличает дискретные диапазоны от непрерывных, в которых всегда (или почти всегда) можно найти другие значения элемента между двумя данными. Например, непрерывными является диапазон с подтипом numeric или timestamp. (Хотя timestamp имеет ограниченную точность и поэтому теоретически может рассматриваться как дискретный, лучше считать его непрерывным, поскольку обычно размер его шага не представляет интереса.)

Другой способ определения типа дискретного диапазона состоит в том, что в нем существует четкое представление о «следующем» или «предыдущем» элементе для каждого значения. Зная это, можно преобразовывать границы диапазона из включаемых в исключаемые, выбрав следующий или предыдущий элемент вместо заданного изначально. Например, в диапазоне целочисленного типа [4,8] и (3,9) обозначают одно и то же множество значений; но для диапазона числового типа это не так.

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

Для встроенных диапазонных типов int4range, int8range и daterange используется каноническая форма, которая включает нижнюю границу и исключает верхнюю, то есть [). Однако для пользовательских типов можно использовать другие соглашения.

Определение новых диапазонных типов

Пользователи могут определять собственные диапазонные типы. Наиболее распространенная причина для этого — использование диапазонов с подтипами, для которых нет встроенных диапазонных типов. Например, можно определить новый диапазонный тип для подтипа float8:

CREATE TYPE floatrange AS RANGE (
    subtype = float8,
    subtype_diff = float8mi
);

SELECT '[1.234, 5.678]'::floatrange;

Поскольку float8 не имеет значимого «шага», функция канонизации в этом примере не задана.

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

Если считается, что значения подтипа дискретные, а не непрерывные, в команде CREATE TYPE следует указать функцию canonical. Эта функция принимает на вход значение диапазона и должна возвращать равнозначное значение, у которого могут быть другие границы и форматирование. Для двух диапазонов, представляющих одно множество значений, например целочисленные диапазоны [1, 7] и [1, 8), функция канонизации должна выдавать одинаковый результат. Неважно, какое представление вы выберете как каноническое при условии, что два равнозначных значения с разным форматированием всегда преобразуются в одно значение с одинаковым форматированием. Кроме настройки формата включаемых/исключаемых границ функция канонизации также может округлять граничные значения, если желаемый размер шага превышает емкость хранения подтипа. Например, в диапазонном типе для подтипа timestamp можно определить размер шага, равный часу, тогда функция канонизации должна будет округлить границы, не кратные часу, или, возможно, выдать вместо этого ошибку.

Кроме того, для любого диапазонного типа, предназначенного к использованию с индексами GiST или SP-GiST, должна быть определена разница подтипов, или функция subtype_diff. (Индекс будет работать и без subtype_diff, но, скорее всего, будет гораздо менее эффективен.) Эта функция принимает на вход два значения подтипа и возвращает их разницу (то есть X минус Y) в значении типа float8. В примере выше можно использовать функцию float8mi, которая является нижележащей версией обычного оператора «минус» для типа float8, но для другого подтипа может понадобиться преобразование. Кроме того, для представления разницы в числовом виде может потребоваться творческий подход. Функция subtype_diff должна, насколько это возможно, согласовываться с порядком сортировки, подразумеваемым выбранным классом оператора и правилами сортировки; то есть ее результат должен быть положительным, если, согласно порядку сортировки, первый аргумент больше второго.

Менее упрощенный пример функции subtype_diff:

CREATE FUNCTION time_subtype_diff(x time, y time) RETURNS float8 AS
'SELECT EXTRACT(EPOCH FROM (x - y))' LANGUAGE sql STRICT IMMUTABLE;

CREATE TYPE timerange AS RANGE (
    subtype = time,
    subtype_diff = time_subtype_diff
);

SELECT '[11:10, 23:00]'::timerange;

Дополнительную информацию о создании диапазонных типов см. в описании команды CREATE TYPE.

Индексирование

Для столбцов с диапазонным типом можно создать индексы GiST и SP-GiST. Например, так создается индекс GiST:

CREATE INDEX reservation_idx ON reservation USING GIST (during);

Индекс GiST или SP-GiST может ускорять запросы с участием следующих диапазонных операторов: =, &&, <@, @>, <<, >>, -|-, &< и &> (дополнительную информацию см. в разделе Функции диапазона и операторы).

Кроме того, для таких столбцов можно создать индексы В-деревья или хеш-индексы. Для индексов таких типов полезна, собственно, только одна операция диапазона — равенство. Для значений диапазона определен порядок сортировки B-деревьев с соответствующими операторами < и >, но этот порядок довольно произвольный и обычно бесполезен в реальном мире. Поддержка B-деревьев и хешей диапазонными типами предназначена в первую очередь для внутренней сортировки и хеширования при выполнении запросов, а не для создания собственно индексов.

Ограничения для диапазонов

Тогда как для скалярных значений UNIQUE является естественным ограничением, для диапазонных типов оно обычно не подходит. Более подходящими чаще всего являются ограничения-исключения (см. CREATE TABLE ... CONSTRAINT ... EXCLUDE). Такие ограничения позволяют задавать для диапазонов такие условия, как, например, «непересечение». Например:

CREATE TABLE reservation (
    during tsrange,
    EXCLUDE USING GIST (during WITH &&)
);

Это ограничение не позволит сохранить в таблице перекрывающиеся значения:

INSERT INTO reservation VALUES
    ('[2010-01-01 11:30, 2010-01-01 15:00)');
INSERT 0 1

INSERT INTO reservation VALUES
    ('[2010-01-01 14:45, 2010-01-01 15:45)');
ERROR:  conflicting key value violates exclusion constraint "reservation_during_excl"
DETAIL:  Key (during)=(["2010-01-01 14:45:00","2010-01-01 15:45:00")) conflicts
with existing key (during)=(["2010-01-01 11:30:00","2010-01-01 15:00:00")).
(ОШИБКА:  конфликтующее значение ключа нарушает ограничение-исключение "reservation_during_excl"
ПОДРОБНОСТИ: Ключ (during)=(["2010-01-01 14:45:00","2010-01-01 15:45:00"))
конфликтует с существующим ключом (during)=(["2010-01-01 11:30:00","2010-01-01 15:00:00")).)

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

CREATE EXTENSION btree_gist;
CREATE TABLE room_reservation (
    room text,
    during tsrange,
    EXCLUDE USING GIST (room WITH =, during WITH &&)
);

INSERT INTO room_reservation VALUES
    ('123A', '[2010-01-01 14:00, 2010-01-01 15:00)');
INSERT 0 1

INSERT INTO room_reservation VALUES
    ('123A', '[2010-01-01 14:30, 2010-01-01 15:30)');
ERROR:  conflicting key value violates exclusion constraint "room_reservation_room_during_excl"
DETAIL:  Key (room, during)=(123A, ["2010-01-01 14:30:00","2010-01-01 15:30:00")) conflicts
with existing key (room, during)=(123A, ["2010-01-01 14:00:00","2010-01-01 15:00:00")).
(ОШИБКА:  конфликтующее значение ключа нарушает ограничение-исключение
  "room_reservation_room_during_excl"
ПОДРОБНОСТИ:  Ключ (room, during)=(123A, [ 2010-01-01 14:30:00,
  2010-01-01 15:30:00 )) конфликтует
с существующим ключом (room, during)=(123A, ["2010-01-01 14:00:00","2010-01-01 15:00:00")).)

INSERT INTO room_reservation VALUES
    ('123B', '[2010-01-01 14:30, 2010-01-01 15:30)');
INSERT 0 1

Типы доменов

Домен — это пользовательский тип данных, основанный на другом нижележащем типе. При необходимости для домена можно задать условия, ограничивающие его допустимые значения подмножеством значений, допустимых для нижележащего типа. Во всем остальном он ведет себя как нижележащий тип — например, с типом домена будет работать любой оператор или функция, применимые к нижележащему типу. Нижележащим типом может быть любой встроенный или пользовательский базовый тип, перечислимый тип, тип массива, составной тип, диапазонный тип или другой домен.

Например, можно создать домен поверх целых чисел, принимающий только положительные целые числа:

CREATE DOMAIN posint AS integer CHECK (VALUE > 0);
CREATE TABLE mytable (id posint);
INSERT INTO mytable VALUES(1);   -- работает
INSERT INTO mytable VALUES(-1);  -- ошибка

Когда к значению домена применяется оператор или функция нижележащего типа, домен автоматически преобразуется в нижележащий тип. Так, например, результат операции mytable.id - 1 будет считаться имеющим тип integer, а не posint. Мы могли бы написать (mytable.id - 1)::posint, чтобы привести результат обратно к posint, вызвав перепроверку ограничений домена. В этом случае, если выражение было применено к значению id, равному 1, произойдет ошибка. Допускается присвоение значения нижележащего типа полю или переменной типа домена без написания явного приведения, но ограничения домена все равно будут проверены.

Дополнительную информацию см. в описании команды CREATE DOMAIN.

Типы идентификаторов объектов

Идентификаторы объектов (OID) используются внутри QHB в качестве первичных ключей для различных системных таблиц. Идентификатор объекта представляется типом oid. Также для oid определены следующие типы- псевдонимы: regproc, regprocedure, regoper, regoperator, regclass, regtype, regrole, regnamespace, regconfig и regdictionary. Обзор этих псевдонимов представлен в таблице 26.

В настоящее время тип oid реализован как четырехбайтное целое беззнаковое число. Следовательно, он недостаточно велик, чтобы полностью обеспечить уникальность в больших базах данных или даже в отдельных больших таблицах.

Для самого типа oid помимо сравнения существует всего несколько операций. Однако его можно привести к целому числу, а затем обработать с помощью стандартных целочисленных операторов. (При этом следует опасаться возможной путаницы между знаковыми и беззнаковыми значениями.)

У типов-псевдонимов OID нет собственных операций, кроме специализированных функций ввода и вывода. Эти функции могут принимать и отображать не исходное числовое значение, как тип oid, а символические имена системных объектов. Типы-псевдонимы позволяют упростить поиск объектов по значениям OID. Например, чтобы выбрать из pg_attribute строки, относящиеся к таблице mytable, можно написать:

SELECT * FROM pg_attribute WHERE attrelid = 'mytable'::regclass;

вместо:

SELECT * FROM pg_attribute
  WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = 'mytable');

Хотя второй запрос сам по себе не так уж плох, он все равно чересчур упрощен. Если в разных схемах есть несколько таблиц с именем mytable, то для выбора правильного OID потребуется гораздо более сложный субзапрос. Преобразователь вводимого значения типа regclass находит таблицу в соответствии с настройкой пути к схеме, поэтому автоматически выполняет «правильные действия». Точно так же приведение OID таблицы к типу regclass удобно для символического отображения числового OID.

Таблица 26. Типы идентификаторов объектов

Имя | Ссылки | Описание | Пример значения
oid | Любые | числовой идентификатор объекта | 564182
regproc | pg_proc | имя функции | sum
regprocedure | pg_proc | функция с типами аргументов | sum(int4)
regoper | pg_operator | имя оператора | +
regoperator | pg_operator | оператор с типами аргументов | *(integer,integer) или -(NONE,integer)
regclass | pg_class | имя отношения | pg_type
regtype | pg_type | имя типа данных | integer
regrole | pg_authid | имя роли | smithee
regnamespace | pg_namespace | имя пространства имен | pg_catalog
regconfig | pg_ts_config | конфигурация текстового поиска | english
regdictionary | pg_ts_dict | словарь текстового поиска | simple

Все типы-псевдонимы OID для объектов, сгруппированных в пространстве имен, принимают имена, дополненные именем схемы, и будут выводить имена со схемой, если этот объект нельзя найти в текущем пути поиска без имени схемы. Типы- псевдонимы regproc и regoper принимают только уникальные вводимые имена (не перегруженные), поэтому их использование ограничено; в большинстве случаев больше подходят regprocedure или regoperator. Для regoperator при определении унарных операторов вместо неиспользуемого операнда записывается слово NONE.

Дополнительным свойством большинства типов-псевдонимов OID является создание зависимостей. Если в сохраненном выражении (например, в выражении столбца по умолчанию или в представлении) появляется константа одного из этих типов, она создает зависимость от ссылочного объекта. Например, если у столбца выражение по умолчанию nextval(’my_seq’::regclass), QHB понимает, что это выражение зависит от последовательности my_seq; система не позволит удалить последовательность, не удалив предварительно выражение по умолчанию. Тип regrole является единственным исключением для этого свойства. Константы этого типа в таких выражениях не допускаются.

Примечание
Типы-псевдонимы OID не вполне соответствуют правилам изоляции транзакций. Планировщик также воспринимает их как простые константы, что может привести к неоптимальному планированию.

Другим типом идентификатора, используемым системой, является xid, или идентификатор транзакции (сокращенно xact). Это тип данных системных столбцов xmin и xmax. Идентификаторы транзакции являются 32-битными величинами.

Третий тип идентификатора, используемый системой, — cid, или идентификатор команды. Это тип данных системных столбцов cmin и cmax. Идентификаторы команд также являются 32-битными величинами.

Последний тип идентификатора, используемый системой, — это tid, или идентификатор кортежа (идентификатор строки). Это тип данных системного столбца ctid. ID кортежа — это пара (номер блока, индекс кортежа в блоке), которая определяет физическое расположение строки в таблице.

(Более подробно системные столбцы рассматриваются в разделе Системные столбцы).

Тип pg_lsn

Тип данных pg_lsn может использоваться для хранения значения LSN (порядковый номер в журнале, Log Sequence Number), которое является указателем на позицию в WAL (журнале с упреждающей записью). Этот тип является представлением XLogRecPtr и внутренним системным типом QHB.

Внутренне LSN представляет собой 64-разрядное целое число, представляющее позицию байта в потоке WAL. Он выводится в виде двух шестнадцатеричных чисел длиной до 8 цифр каждое, разделенных косой чертой; например 16/B374D848. Тип pg_lsn поддерживает стандартные операторы сравнения, такие как = и >. Можно вычесть один номер LSN из другого с помощью оператора -; результатом будет число байтов между этими позициями в WAL.

Псевдо-типы

Система типов QHB содержит ряд записей специального назначения, которые в совокупности называются псевдотипами. Псевдотип нельзя использовать в качестве типа данных столбца, но можно объявить функцию с аргументом или результатом такого типа. Каждый из доступных псевдотипов полезен в ситуациях, когда поведение функции не соответствует простому принятию или возвращению значения определенного типа данных SQL. Все существующие псевдотипы перечислены в таблице 27.

Таблица 27. Псевдо-типы

Имя | Описание
any | Указывает, что функция принимает любой тип входных данных.
anyelement | Указывает, что функция принимает любой тип данных (см. раздел Полиморфные типы).
anyarray | Указывает, что функция принимает любой тип данных массива (см. раздел Полиморфные типы).
anynonarray | Указывает, что функция принимает любой тип данных, кроме массива (см. раздел Полиморфные типы).
anyenum | Указывает, что функция принимает любой перечисляемый тип данных (см. разделы Полиморфные типы и Перечислимые типы).
anyrange | Указывает, что функция принимает любой диапазонный тип данных (см. разделы Полиморфные типы и Диапазонные типы).
cstring | Указывает, что функция принимает или возвращает строку в стиле C с NULL в конце.
internal | Указывает, что функция принимает или возвращает внутренний серверный тип данных.
language_handler | Обработчик вызова процедурного языка объявляется как возвращающий тип language_handler.
fdw_handler | Обработчик обертки сторонних данных объявляется как возвращающий тип fdw_handler.
index_am_handler | Обработчик метода индексного доступа объявляется как возвращающий тип index_am_handler.
tsm_handler | Обработчик метода выборки из таблицы объявляется как возвращающий тип tsm_handler.
record | Определяет функцию, принимающую или возвращающую неопределенный тип строки.
trigger | Триггерная функция объявляется как возвращающая тип trigger.
event_trigger | Функция событийного триггера объявляется как возвращающая тип event_trigger.
pg_ddl_command | Определяет представление команд DDL, доступных для триггеров событий.
void | Указывает, что функция не возвращает значение.
unknown | Определяет еще не определенный тип, например, недекорированный строковый литерал.
opaque | Имя устаревшего типа, который раньше применялся для многих из вышеперечисленных целей.

Функции, написанные на C/Rust (встроенные или динамически загружаемые), могут быть объявлены для принятия или возврата любого из этих псевдотипов. Ответственность за то, что функция будет безопасна с псевдотипом в качестве аргумента, несет разработчик функции.

Функции, написанные на процедурных языках, могут использовать псевдотипы, только если это позволяет их язык реализации. В настоящее время большинство процедурных языков запрещают использование псевдотипа в качестве аргумента, а для результата допускают только типы void и record (и trigger или event_trigger, когда функция реализует триггер или триггер события). Некоторые языки также поддерживают полиморфные функции, использующие типы anyelement, anyarray, anynonarray, anyenum и anyrange.

Псевдотип internal используется для объявления функций, предназначенных только для внутреннего вызова системой базы данных, а не путем прямого вызова в запросе SQL. Если у функции есть хотя бы один аргумент типа internal, ее нельзя вызвать из SQL. Чтобы сохранить типобезопасность данного ограничения, нужно следовать этому правилу кодирования: не создавайте функцию, которая объявляется как возвращающая internal, если у нее нет ни одного аргумента internal.

Функции и операторы

QHB предоставляет огромное количество функций и операторов для встроенных типов данных. В этой главе описаны основные из них, тогда как некоторые специальные функции описываются в соответствующих разделах данного руководства. Кроме того, пользователи могут определять свои собственные функции и операторы, как описано в главе Серверное программирование. Для вывода списка всех доступных функций и операторов можно воспользоваться командами psql \df и \do соответственно.

Обозначения, используемые в этой главе для описания аргумента и результирующего типа данных функции или оператора, выглядят так:

repeat ( text, integer ) → text

В данной записи говорится, что функция repeat принимает один текстовый и один целочисленный аргумент и возвращает результат текстового типа. Стрелка вправо также служит указанием на результат, например:

repeat('Pg', 4) → PgPgPgPg

Если для вас важна переносимость, учтите, что большинство функций и операторов, описанных в этой главе, за исключением самых простых арифметических операторов и операторов сравнения и некоторых явно отмеченных функций, не указаны в стандарте SQL. Эта расширенная функциональность отчасти присутствует и в других СУБД SQL, и во многих случаях эта функциональность совместима и согласована между различными реализациями.

Логические операторы

Среди логических операторов доступны обычные:

boolean AND boolean → boolean
boolean OR boolean → boolean
NOT boolean → boolean

SQL использует трехзначную логическую систему: true (истина), false (ложь) и NULL («не определено»). Рассмотрите следующие таблицы истинности:

a | b | a AND b | a OR b
TRUE | TRUE | TRUE | TRUE
TRUE | FALSE | FALSE | TRUE
TRUE | NULL | NULL | TRUE
FALSE | FALSE | FALSE | FALSE
FALSE | NULL | FALSE | NULL
NULL | NULL | NULL | NULL

a | NOT a
TRUE | FALSE
FALSE | TRUE
NULL | NULL

Операторы AND и OR являются коммутативными, то есть левый и правый операнд можно менять местами, не влияя на результат. (Однако нет гарантии, что левый операнд будет вычисляться перед правым. Дополнительную информацию о порядке вычисления подвыражений см. в разделе Правила вычисления выражений.)

Функции и операторы сравнения

Среди операторов сравнения доступны обычные операторы, перечисленные в Таблице 1.

Таблица 1. Операторы сравнения

Оператор | Описание
тип_данных < тип_данных → boolean | Меньше
тип_данных > тип_данных → boolean | Больше
тип_данных <= тип_данных → boolean | Меньше или равно
тип_данных >= тип_данных → boolean | Больше или равно
тип_данных = тип_данных → boolean | Равно
тип_данных <> тип_данных → boolean | Не равно
тип_данных != тип_данных → boolean | Не равно

Примечание
Запись <> принята в стандарте SQL для условия «не равно». Запись != является псевдонимом, который преобразуется в <> на самом раннем этапе синтаксического анализа. Как следствие, реализовать операторы != и <> так, чтобы они работали по-разному, невозможно.

Эти операторы сравнения доступны для всех встроенных типов данных с естественной сортировкой, включая числовые, строковые типы и типы даты/времени. Кроме того, сравниваться могут массивы, составные типы и диапазоны, если сравнимы типы данных их компонентов.

Обычно также можно сравнивать значения связанных типов данных; например, возможно сравнение integer > bigint. Некоторые подобные операции реализуются непосредственно «межтиповыми» операторами сравнения, но если такой оператор недоступен, синтаксический анализатор принудительно приведет менее распространенные тип к более распространенному и применит подходящий для последнего оператор сравнения.

Как показано выше, все операторы сравнения являются бинарными операторами, которые возвращают значения типа boolean. Таким образом, выражения типа 1 < 2 < 3 недопустимы (потому что нет оператора < для сравнения логического значения с 3). Для проверки принадлежности диапазону используйте предикат BETWEEN, описанный ниже

Существует также несколько предикатов сравнения, перечисленных в Таблице 2. Они ведут себя подобно операторам, но имеют специальный синтаксис, предписанный стандартом SQL.

Таблица 2. Предикаты сравнения

Предикат

Описание

Пример(ы)

тип_данных BETWEEN тип_данных AND тип_данных → boolean

Между (включая границы диапазона).

2 BETWEEN 1 AND 3 → t

2 BETWEEN 3 AND 1 → f

тип_данных NOT BETWEEN тип_данных AND тип_данных → boolean

Не между (обратное к BETWEEN).

2 NOT BETWEEN 1 AND 3 → f

тип_данных BETWEEN SYMMETRIC тип_данных AND тип_данных → boolean

Между, после сортировки граничных значений.

2 BETWEEN SYMMETRIC 3 AND 1 → t

тип_данных NOT BETWEEN SYMMETRIC тип_данных AND тип_данных → boolean

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

2 NOT BETWEEN SYMMETRIC 3 AND 1 → f

тип_данных IS DISTINCT FROM тип_данных → boolean

Не равно, при этом рассматривая NULL как обычное значение.

1 IS DISTINCT FROM NULL → t (а не NULL)

NULL IS DISTINCT FROM NULL → f (а не NULL)

тип_данных IS NOT DISTINCT FROM тип_данных → boolean

Равно, при этом рассматривая NULL как обычное значение.

1 IS NOT DISTINCT FROM NULL → f (а не NULL)

NULL IS NOT DISTINCT FROM NULL → t (а не NULL)

тип_данных IS NULL → boolean

Проверяет, равно ли значение NULL.

1.5 IS NULL → f

тип_данных IS NOT NULL → boolean

Проверяет, отличается ли значение от NULL.

'null' IS NOT NULL → t

тип_данных ISNULL → boolean

Проверяет, равно ли значение NULL (нестандартный синтаксис).

тип_данных NOTNULL → boolean

Проверяет, отличается ли значение от NULL (нестандартный синтаксис).

boolean IS TRUE → boolean

Проверяет, является ли результат логического выражения значением true.

true IS TRUE → t

NULL::boolean IS TRUE → f (а не NULL)

boolean IS NOT TRUE → boolean

Проверяет, является ли результат логического выражения значением false или неизвестным.

true IS NOT TRUE → f

NULL::boolean IS NOT TRUE → t (а не NULL)

boolean IS FALSE → boolean

Проверяет, является ли результат логического выражения значением false.

true IS FALSE → f

NULL::boolean IS FALSE → f (а не NULL)

boolean IS NOT FALSE → boolean

Проверяет, является ли результат логического выражения значением true или неизвестным.

true IS NOT FALSE → t

NULL::boolean IS NOT FALSE → t (а не NULL)

boolean IS UNKNOWN → boolean

Проверяет, является ли результат логического выражения неизвестным значением.

true IS UNKNOWN → f

NULL::boolean IS UNKNOWN → t (а не NULL)

boolean IS NOT UNKNOWN → boolean

Проверяет, является ли результат логического выражения значением true или false.

true IS NOT UNKNOWN → t

NULL::boolean IS NOT UNKNOWN → f (а не NULL)

Предикат BETWEEN упрощает проверки принадлежности диапазону:

a BETWEEN x AND y

равнозначно

a >= x AND a <= y

Обратите внимание, что BETWEEN обрабатывает граничные значения как включенные в диапазон. BETWEEN SYMMETRIC аналогичен BETWEEN за исключением того, что аргумент слева от AND не обязательно должен быть меньше или равен аргументу справа. Если это не так, эти два аргумента автоматически меняются местами, поэтому всегда подразумевается непустой диапазон.

Различные варианты BETWEEN реализуются посредством обычных операторов сравнения, и поэтому они будут работать с любыми типами данных, которые можно сравнивать.

Примечание
Использование AND в синтаксисе BETWEEN создает неоднозначность с использованием AND как логического оператора. Для ее устранения в качестве второго аргумента предложения BETWEEN допускается только ограниченный набор типов выражений. Если вам нужно записать в BETWEEN более сложное подвыражение, заключите его в скобки.

Обычные операторы сравнения выдают NULL (что означает «неопределенность»), а не true или false, когда любой из аргументов NULL. Например, 7 = NULL возвращает NULL, как и 7 <> NULL. Когда это поведение не устраивает, можно использовать предикаты IS [NOT] DISTINCT FROM:

a IS DISTINCT FROM b
a IS NOT DISTINCT FROM b

Для входных значений не NULL условие IS DISTINCT FROM равнозначно оператору <>. Однако если оба аргумента NULL, он возвращает false, а если только один из аргументов NULL, он возвращает true. Аналогично условие IS NOT DISTINCT FROM* идентично = для аргументов не NULL, но возвращает true, если оба аргумента NULL, и false, если только один аргумент NULL. Таким образом, эти предикаты по сути ведут себя так, как если бы NULL был нормальным значением данных, а не «неопределенным».

Чтобы проверить, является ли значение NULL, используются предикаты:

выражение IS NULL
выражение IS NOT NULL

или равнозначные, но нестандартные предикаты:

выражение ISNULL
выражение NOTNULL

Не пишите выражение = NULL, потому что NULL «не равно» NULL. (Значение NULL представляет неопределенность, и неизвестно, равны ли два неопределенных значения).

Совет
Некоторые приложения могут ожидать, что выражение = NULL возвращает true, если результатом выражения является значение NULL. Настоятельно рекомендуется изменить эти приложения в соответствии со стандартом SQL. Однако, если это невозможно, доступна переменная конфигурации transform_null_equals. Если она установлена, QHB преобразует предложения x = NULL в x IS NULL.

Если выражение имеет строковое значение, то IS NULL равно true, если само выражение строки NULL или когда все поля строки NULL, а IS NOT NULL равно true, если само выражение строки не NULL и все поля строки не NULL. Из-за этого поведения IS NULL и IS NOT NULL не всегда возвращают обратные результаты для таких выражений; в частности, строковое выражение, содержащее поля NULL и не NULL, вернет false для обоих проверок. В некоторых случаях может быть предпочтительнее записать строка IS DISTINCT FROM NULL или строка IS NOT DISTINCT FROM NULL, чтобы просто проверить, равно ли NULL все значение строки, без каких-либо дополнительных проверок полей строки.

Логические значения также можно проверить с использованием предикатов

логическое_выражение IS TRUE
логическое_выражение IS NOT TRUE
логическое_выражение IS FALSE
логическое_выражение IS NOT FALSE
логическое_выражение IS UNKNOWN
логическое_выражение IS NOT UNKNOWN

Они всегда будут возвращать true или false и никогда NULL, даже если операнд — NULL. Аргумент NULL рассматривается как логическое значение «неопределенность». Обратите внимание, что IS UNKNOWN и IS NOT UNKNOWN фактически то же самое, что и IS NULL и IS NOT NULL, соответственно, за исключением того, что входное выражение должно иметь логический тип.

Также существует несколько связанных со сравнениями функций, перечисленных в Таблице 3.

Таблица 3. Функции сравнения

Функция

Описание

Пример(ы)

num_nonnulls ( VARIADIC "any" ) → integer

Возвращает количество аргументов, отличных от NULL

num_nonnulls(1, NULL, 2) → 2

num_nulls ( VARIADIC "any" ) → integer

Возвращает количество аргументов NULL

num_nulls(1, NULL, 2) → 1

Математические функции и операторы

Математические операторы предоставляются для многих типов QHB. Фактическое поведение с типами без стандартных математических соглашений (например, типами даты/времени) мы опишем в последующих разделах.

В Таблице 4 перечислены математические операторы, доступные для стандартных числовых типов. Если не указано иное, операторы, показанные как принимающие числовой_тип, доступны для типов smallint, integer, bigint, numeric, real и double precision. Операторы, показанные как принимающие целочисленный_тип, доступны для типов smallint, integer и bigint. За исключением отмеченных случаев, все формы операторов возвращают тот же тип данных, какой имеет их аргумент (или аргументы). Вызовы с неодинаковыми типами аргументов, например integer + numeric, разрешаются использованием типа, идущего в этих списках последним.

Таблица 4. Математические операторы

Оператор

Описание

Пример(ы)

числовой_тип + числовой_тип → числовой_тип

Сложение

2 + 3 → 5

+ числовой_тип → числовой_тип

Унарный плюс (нет операции)

+ 3.5 → 3.5

числовой_тип - числовой_тип → числовой_тип

Вычитание

2 - 3 → -1

- числовой_тип → числовой_тип

Смена знака

- (-4) → 4

числовой_тип * числовой_тип → числовой_тип

Умножение

2 * 3 → 6

числовой_тип / числовой_тип → числовой_тип

Деление (при делении целочисленных типов результат округляется в сторону нуля)

5.0 / 2 → 2.5000000000000000

5 / 2 → 2

(-5) / 2 → -2

числовой_тип % числовой_тип → числовой_тип

Остаток целочисленного деления (по модулю); доступен для типов smallint, integer, bigint и numeric

5 % 4 → 1

numeric ^ numeric → numeric

double precision ^ double precision → double precision

Возведение в степень

2 ^ 3 → 8

В отличие от обычной математической практики, ^ по умолчанию вычисляется слева направо:

2 ^ 3 ^ 3 → 512

2 ^ (3 ^ 3) → 134217728

|/ double precision → double precision

Квадратный корень

|/ 25.0 → 5

||/ double precision → double precision

Кубический корень

||/ 64.0 → 4

@ числовой_тип → числовой_тип

Абсолютное значение (модуль)

@ -5.0 → 5

целочисленный_тип & целочисленный_тип → целочисленный_тип

Битовое И

91 & 15 → 11

целочисленный_тип | целочисленный_тип → целочисленный_тип

Битовое ИЛИ

32 | 3 → 35

целочисленный_тип # целочисленный_тип → целочисленный_тип

Битовое исключающее ИЛИ

17 # 5 → 20

~ целочисленный_тип → целочисленный_тип

Битовое НЕ

~1 → -2

целочисленный_тип << integer → целочисленный_тип

Битовый сдвиг влево

1 << 4 → 16

целочисленный_тип >> integer → целочисленный_тип

Битовый сдвиг вправо

8 >> 2 → 2

В Таблице 5 перечислены все доступные математические функции. Многие из этих функций представлены в нескольких формах с различными типами аргументов. За исключением отмеченных случаев, все формы функций возвращают тот же тип данных, какой имеет их аргумент (или аргументы); случаи с неодинаковыми типами разрешаются тем же способом, который был описан выше для операторов. Функции, работающие с данными double precision, в основном реализованы поверх C-библиотеки системы сервера, поэтому точность и поведение в граничных случаях могут варьироваться в зависимости от системы сервера.

Таблица 5. Математические функции

Функция

Описание

Пример(ы)

abs ( числовой_тип ) → числовой_тип

Абсолютное значение (модуль)

abs(-17.4) → 17.4

cbrt ( double precision ) → double precision

Кубический корень

cbrt(64.0) → 4

ceil ( numeric ) → numeric

ceil ( double precision ) → double precision

Ближайшее целое число, большее или равное аргументу

ceil(42.2) → 43

ceil(-42.8) → -42

ceiling ( numeric ) → numeric

ceiling ( double precision ) → double precision

Ближайшее целое число, большее или равное аргументу (то же, что и ceil)

ceiling(95.3) → 96

degrees ( double precision ) → double precision

Преобразует радианы в градусы

degrees(0.5) → 28.64788975654116

div ( y numeric, x numeric ) → numeric

Целочисленное частное y/x (округленное в сторону нуля)

div(9, 4) → 2

exp ( numeric ) → numeric

exp ( double precision ) → double precision

Экспоненциал (e возводится в заданную степень)

exp(1.0) → 2.7182818284590452

factorial ( bigint ) → numeric

Факториал

factorial(5) → 120

floor ( numeric ) → numeric

floor ( double precision ) → double precision

Ближайшее целое число, меньшее или равное аргументу

floor(42.8) → 42

floor(-42.8) → -43

gcd ( числовой_тип, числовой_тип ) → числовой\_тип

Наибольший общий делитель (наибольшее положительное число, на которое оба аргумента делятся без остатка); возвращает 0, если оба аргумента равны нулю; доступно для типов integer, bigint и numeric

gcd(1071, 462) → 21

lcm ( числовой_тип, числовой_тип ) → числовой_тип

Наименьшее общее кратное (наименьшее строго положительное число, которое делится на оба аргумента без остатка; возвращает 0, если оба аргумента равны нулю; доступно для типов integer, bigint и numeric

lcm(1071, 462) → 23562

ln ( numeric ) → numeric

ln ( double precision ) → double precision

Натуральный логарифм

ln(2.0) → 0.6931471805599453

log ( numeric ) → numeric

log ( double precision ) → double precision

Десятичный логарифм

log(100) → 2

log10 ( numeric ) → numeric

log10 ( double precision ) → double precision

Десятичный логарифм (то же, что и log)

log10(1000) → 3

log ( b numeric, x numeric ) → numeric

Логарифм x по основанию b

log(2.0, 64.0) → 6.0000000000

min_scale ( numeric ) → integer

Минимальный масштаб (количество цифр в дробной части), необходимый для точного представления заданного значения

min_scale(8.4100) → 2

mod ( y числовой_тип, x числовой_тип ) → числовой_тип

Остаток от y/x; доступно для типов integer, bigint и numeric

mod(9, 4) → 1

pi ( ) → double precision

Примерное значение π

pi() → 3.141592653589793

power ( a numeric, b numeric ) → numeric

power ( a double precision, b double precision ) → double precision

a возводится в степень b

power(9, 3) → 729

radians ( double precision ) → double precision

Преобразует градусы в радианы

radians(45.0) → 0.7853981633974483

round ( numeric ) → numeric

round ( double precision ) → double precision

Округляет до ближайшего целого числа. Для numeric половина (.5) округляется до одного по модулю. Для double precision способ округление половины зависит от платформы, но, как правило, выполняется «округление до ближайшего четного».

round(42.4) → 42

round ( v numeric, s integer ) → numeric

Округляет v до s десятичных знаков. Половина (.5) округляется до одного по модулю.

round(42.4382, 2) → 42.44

scale ( numeric ) → integer

Масштаб аргумента (количество десятичных цифр в дробной части)

scale(8.4100) → 4

sign ( numeric ) → numeric

sign ( double precision ) → double precision

Знак аргумента (-1, 0 или +1)

sign(-8.4) → -1

sqrt ( numeric ) → numeric

sqrt ( double precision ) → double precision

Квадратный корень

sqrt(2) → 1.4142135623730951

trim_scale ( numeric ) → numeric

Сокращает масштаб значения (количество десятичных цифр в дробной части), убирая конечные нули

trim_scale(8.4100) → 8.41

trunc ( numeric ) → numeric

trunc ( double precision ) → double precision

Округляет до целого (в сторону нуля)

trunc(42.8) → 42

trunc(-42.8) → -42

trunc ( v numeric, s integer ) → numeric

Округляет v до s десятичных знаков

trunc(42.4382, 2) → 42.43

width_bucket ( операнд numeric, нижний numeric, верхний numeric, число integer ) → integer

width_bucket ( операнд double precision, нижний double precision, верхний double precision, число integer ) → integer

Возвращает номер блока, в который попадает операнд в гистограмме с заданным числом блоков равного размера, расположенных в диапазоне от нижний до верхний. Возвращает 0 или число+1, если операнд лежит вне этого диапазона.

width_bucket(5.35, 0.024, 10.06, 5) → 3

width_bucket ( операнд anycompatible, границы anycompatiblearray ) → integer

Возвращает номер блока, в который попадает операнд (при этом нижние границы блоков перечислены в заданном массиве границы). Возвращает 0, если операнд меньше первой нижней границы. Аргумент операнд и элементы массива могут быть любого типа, для которого определены стандартные операторы сравнения. Массив границы должен быть отсортирован по возрастанию, иначе будут получены неожиданные результаты.

width_bucket(now(), array['yesterday', 'today', 'tomorrow']::timestamptz[]) → 2

В Таблице 6 перечислены функции для генерации случайных чисел.

Таблица 6. Случайные функции

Функция

Описание

Пример(ы)

random ( ) → double precision

Возвращает случайное значение в диапазоне 0.0 <= x < 1.0

random() → 0.897124072839091

setseed ( double precision ) → void

Задает начальное значение для последующих вызовов random(); аргументом должно быть число от -1.0 до 1.0, включая границы

setseed(0.12345)

Функция random() использует простой линейный конгруэнтный алгоритм. Она работает быстро, но не подходит для криптографических приложений; более безопасная альтернатива имеется в модуле pgcrypto. Если вызывается setseed(), то результаты последующих вызовов random() в текущем сеансе можно повторить путем повторных вызовов setseed() с тем же аргументом.

В Таблице 7 перечислены доступные тригонометрические функции. Все эти функции представлены в двух вариантах: один измеряет углы в радианах, а другой — в градусах.

Таблица 7. Тригонометрические функции

Функция

Описание

Пример(ы)

acos ( double precision ) → double precision

Арккосинус в радианах

acos(1) → 0

acosd ( double precision ) → double precision

Арккосинус в градусах

acosd(0.5) → 60

asin ( double precision ) → double precision

Арксинус в радианах

asin(1) → 1.5707963267948966

asind ( double precision ) → double precision

Арксинус в градусах

asind(0.5) → 30

atan ( double precision ) → double precision

Арктангенс в радианах

atan(1) → 0.7853981633974483

atand ( double precision ) → double precision

Арктангенс в градусах

atand(1) → 45

atan2 ( y double precision, x double precision ) → double precision

Арктангенс отношения y/x в радианах

atan2(1, 0) → 1.5707963267948966

atan2d ( y double precision, x double precision ) → double precision

Арктангенс отношения y/x в градусах

atan2d(1, 0) → 90

cos ( double precision ) → double precision

Косинус в радианах

cos(0) → 1

cosd ( double precision ) → double precision

Косинус в градусах

cosd(60) → 0.5

cot ( double precision ) → double precision

Котангенс в радианах

cot(0.5) → 1.830487721712452

cotd ( double precision ) → double precision

Котангенс в градусах

cotd(45) → 1

sin ( double precision ) → double precision

Синус в радианах

sin(1) → 0.8414709848078965

sind ( double precision ) → double precision

Синус в градусах

sind(30) → 0.5

tan ( double precision ) → double precision

Тангенс в радианах

tan(1) → 1.5574077246549023

tand ( double precision ) → double precision

Тангенс в градусах

tand(45) → 1

Примечание
Другой способ работы с углами, измеряемыми в градусах, состоит в использовании функций преобразования единиц измерения radians() и degrees(), описанных ранее. Однако предпочтительнее использовать тригонометрические функции с градусами, поскольку этот способ позволяет избежать ошибки округления в особых случаях, например, при вычислении sind(30).

В Таблице 8 перечислены доступные гиперболические функции.

Таблица 8. Гиперболические функции

Функция

Описание

Пример(ы)

sinh ( double precision ) → double precision

Гиперболический синус

sinh(1) → 1.1752011936438014

cosh ( double precision ) → double precision

Гиперболический косинус

cosh(0) → 1

tanh ( double precision ) → double precision

Гиперболический тангенс

tanh(1) → 0.7615941559557649

asinh ( double precision ) → double precision

Ареасинус

asinh(1) → 0.881373587019543

acosh ( double precision ) → double precision

Ареакосинус

acosh(1) → 0

atanh ( double precision ) → double precision

Ареатангенс

atanh(0.5) → 0.5493061443340548

Строковые функции и операторы

В этом разделе описываются функции и операторы для проверки и манипулирования строковыми значениями. Строки в этом контексте включают значения типов character, character varying и text. Если не указано иное, эти функции и операторы объявлены принимающими и возвращающими тип text. Также они будут взаимозаменяемо принимать аргументы типа character varying. А значения типа character до вызова функции или оператора будут преобразовываться в text, в результате чего все завершающие пробелы в значении character будут обрезаться.

В SQL определены некоторые строковые функции, в которых для разделения аргументов вместо запятых используются ключевые слова. Подробнее эти слова описаны в Таблице 9. QHB также предоставляет версии этих функций, использующие обычный синтаксис вызова функций (см. Таблицу 10).

Примечание
Оператор конкатенации строк (\|\|) примет нестроковый аргумент, если хотя бы один аргумент имеет строковый тип, как показано в Таблице 9. В других случаях, чтобы оператор принял нестроковый аргумент, можно использовать явное преобразование в text.

Таблица 9. Строковые функции и операторы SQL

Функция/оператор

Описание

Пример(ы)

text || text → text

Конкатенирует две строки.

'Quan' || 'tum' → Quantum

text || anynonarray → text

anynonarray || text → text

Преобразует нестроковый аргумент в текст, а затем конкатенирует две строки. (Нестроковый аргумент не может быть типом-массивом, поскольку это может вызвать неоднозначность с операторами массивов ||. Если требуется конкатенировать эквивалент текста в массиве, следует явно привести его к типу text.)

'Value: ' || 42 → Value: 42

text IS [NOT] [форма] NORMALIZED → boolean

Проверяет, соответствует ли строка определенной форме нормализации Unicode. Форма указывается в необязательном ключевом слове форма: NFC (по умолчанию), NFD, NFKC или NFKD. Это выражение можно использовать, только если кодировка сервера — UTF8. Обратите внимание, что проверка нормализации с помощью этого выражения зачастую бывает быстрее, чем нормализация уже, возможно, нормализованных строк.

U&'\0061\0308bc' IS NFD NORMALIZED → t

bit_length ( text ) → integer

Возвращает количество битов в строке (это число в 8 раз больше octet_length).

bit_length('jose') → 32

char_length ( text ) → integer

character_length ( text ) → integer

Возвращает количество символов в строке.

char_length('josé') → 4

lower ( text ) → text

Переводит символы строки в нижний регистр в соответствии с правилами локали базы данных.

lower('TOM') → tom

normalize ( text [, форма ] ) → text

Преобразует строку в заданную форму нормализации Unicode. Форма указывается в необязательном ключевом слове форма: NFC (по умолчанию), NFD, NFKC или NFKD. Эту функцию можно использовать, только если кодировка сервера — UTF8.

normalize(U&'\0061\0308bc', NFC) → U&'\00E4bc'

octet_length ( text ) → integer

Возвращает количество байтов в строке.

octet_length('josé') → 5 (если кодировка сервера — UTF8)

octet_length ( character ) → integer

Возвращает количество байтов в строке. Поскольку эта версия функции принимает непосредственно тип character, она не обрезает пробелы, завершающие строку.

octet_length('abc '::character(4)) → 4

overlay ( строка text PLACING новая_подстрока text FROM начало integer [FOR число integer] ) → text

Заменяет подстроку в строке, начиная с символа на позиции начало, длиной в число символов, на новую_подстроку. Если число опущено, по умолчанию количество заменяемых символов определяется длиной новой_подстроки.

overlay('Txxxxas' placing 'hom' from 2 for 4) → Thomas

position ( подстрока text IN строка text ) → integer

Возвращает начальную позицию первого вхождения заданной подстроки в строке или ноль, если такого вхождения нет.

position('om' in 'Thomas') → 3

substring ( строка text [ FROM начало integer ] [ FOR число integer ] ) → text

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

substring('Thomas' from 2 for 3) → hom

substring('Thomas' from 3) → omas

substring('Thomas' for 2) → Th

substring ( строка text FROM шаблон text ) → text

Извлекает первую подстроку, соответствующую регулярному выражению POSIX; см. подраздел Регулярные выражения POSIX.

substring('Thomas' from '...$') → mas

substring ( строка text SIMILAR шаблон text ESCAPE управляющий_символ text ) → text

substring ( строка text FROM шаблон text FOR управляющий_символ text ) → text

Извлекает первую подстроку, соответствующую регулярному выражению SQL; см. подраздел Регулярные выражения SIMILAR TO. Первая форма была определена в стандарте, начиная с SQL:2003; вторая форма существовала только в стандарте SQL:1999, и ее следует считать устаревшей.

substring('Thomas' similar '%#"o_a#"_' escape '#') → oma

trim ( [LEADING | TRAILING | BOTH] [символы text] FROM строка text ) → text

Удаляет самую длинную подстроку, содержащую только заданные символы (по умолчанию пробелы), с начала, конца или с обеих сторон (по умолчанию последнее — BOTH) строки.

trim(both 'xyz' from 'yxTomxx') → Tom

trim ( [ LEADING | TRAILING | BOTH ] [ FROM ] строка text [, символы text ] ) → text

Это нестандартный синтаксис для trim().

trim(both 'xyz' from 'yxTomxx') → Tom

upper ( text ) → text

Переводит символы строки в верхний регистр в соответствии с правилами локали базы данных.

upper('tom') → TOM

Кроме этого, в QHB доступны дополнительные функции работы со строками, перечисленные в Таблице 10. Некоторые из них используются для внутренней реализации стандартных строковых функций SQL, перечисленных в Таблице 9.

Таблица 10. Другие строковые функции

Функция

Описание

Пример(ы)

ascii ( text ) → integer

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

ascii('x') → 120

btrim ( строка text [, символы text ] ) → text

Удаляет самую длинную подстроку, содержащую только заданные символы (по умолчанию пробелы) с начала и конца строки.

btrim('xyxtrimyyx', 'xyz') → trim

chr ( integer ) → text

Возвращает символ с заданным кодом. Для кодировки UTF8 возвращает код символа в Unicode. Для других многобайтовых кодировок аргумент должен быть символом ASCII. Эта функция не может выдать chr(0), поскольку этот символ нельзя сохранить в текстовых типах данных.

chr(65) → A

concat ( знач1 "any" [, знач2 "any" [, ...] ] ) → text

Конкатенирует текстовые представления всех аргументов. Аргументы NULL игнорируются.

concat('abcde', 2, NULL, 22) → abcde222

concat_ws ( разд text, знач1 "any" [, знач2 "any" [, ...] ] ) → text

Конкатенирует все аргументы, кроме первого, с разделителями. Первый аргумент используется как строка-разделитель и должен быть отличен от NULL. Другие аргументы NULL игнорируются.

concat_ws(',', 'abcde', 2, NULL, 22) → abcde,2,22

format ( стр_формата text [, форматир_аргумент "any" [, ...] ] ) → text

Форматирует аргументы в соответствии со строкой формата; см. подраздел format. Эта функция схожа с функцией sprintf в языке C.

format('Hello %s, %1$s', 'World') → Hello World, World

initcap ( text ) → text

Переводит первую букву каждого слова в верхний регистр, а остальные — в нижний. Словами являются последовательности буквенно-цифровых символов, разделенных любыми отличными от них символами.

initcap('hi THOMAS') → Hi Thomas

left ( строка text, n integer ) → text

Возвращает первые n символов в строке или, если n — отрицательное число, возвращает все символы, кроме последних |n| символов.

left('abcde', 2) → ab

length ( text ) → integer

Возвращает количество символов в строке.

length('jose') → 4

lpad ( строка text, длина integer [, наполнитель text ] ) → text

Расширяет строку до заданной длины путем добавления слева символов наполнителей (по умолчанию пробелов). Если строка уже длиннее заданной длины, она обрезается (справа).

lpad('hi', 5, 'xy') → xyxhi

ltrim ( строка text [, символы text ] ) → text

Удаляет самую длинную подстроку, содержащую только заданные символы (по умолчанию пробелы) с начала строки.

ltrim('zzzytest', 'xyz') → test

md5 ( text ) → text

Вычисляет MD5-хеш аргумента с результатом, написанным в шестнадцатеричном формате.

md5('abc') → 900150983cd24fb0​d6963f7d28e17f72

parse_ident ( полный_идентификатор text [, строгий_режим boolean DEFAULT true ] ) → text[]

Разделяет полный_идентификатор на массив идентификаторов, удаляя кавычки, в которые заключены отдельные идентификаторы. По умолчанию лишние символы после последнего идентификатора считаются ошибкой, но если установить второй параметр в false, то такие лишние символы игнорируются. (Это поведение полезно для синтаксического анализа имен таких объектов, как функции.) Обратите внимание, что эта функция не усекает чрезмерно длинные идентификаторы. Если есть необходимость в усеченных именах, можно привести результат к name[].

parse_ident('"SomeSchema".someTable') → {SomeSchema,sometable}

pg_client_encoding ( ) → name

Возвращает имя текущей кодировки клиента.

pg_client_encoding() → UTF8

quote_ident ( text ) → text

Возвращает заданную строку, надлежащим образом подготовленную для использования в качестве идентификатора в строке оператора SQL. Кавычки добавляются только при необходимости (т. е. если строка содержит символы, недопустимые в идентификаторе, или буквы в разном регистре). Кавычки, уже содержащиеся в строке, дублируются. См. также пример Заключение в кавычки значений в динамических запросах.

quote_ident('Foo bar') → "Foo bar"

quote_literal ( text ) → text

Возвращает заданную строку, надлежащим образом подготовленную для использования в качестве строкового литерала в строке оператора SQL. Апострофы и обратные слэши, уже содержащиеся в строке, дублируются. Обратите внимание, что quote_literal возвращает NULL, когда входящее значение NULL; если аргумент может оказаться NULL, то зачастую лучше вызвать quote_nullable. См также пример Заключение в кавычки значений в динамических запросах.

quote_literal(E'O\'Reilly') → 'O''Reilly'

quote_literal ( anyelement ) → text

Преобразует заданное значение в текст, а затем заключает в апострофы как литерал. Апострофы и обратные слэши, уже содержащиеся в значении, дублируются.

quote_literal(42.5) → '42.5'

quote_nullable ( text ) → text

Возвращает заданную строку, подходящим образом подготовленную для использования в качестве строкового литерала в строке оператора SQL, или, если аргумент NULL, возвращает NULL. Апострофы и обратные слэши, уже содержащиеся в строке, дублируются. См. также пример Заключение в кавычки значений в динамических запросах.

quote_nullable(NULL) → NULL

quote_nullable ( anyelement ) → text

Преобразует заданное значение в текст, а затем заключает в апострофы как литерал или, если аргумент NULL, возвращает NULL. Апострофы и обратные слэши, уже содержащиеся в значении, дублируются.

quote_nullable(42.5) → '42.5'

regexp_match ( строка text, шаблон text [, флаги text ] ) → text[]

Возвращает фиксированные подстроки, полученные из первого вхождения регулярного выражения POSIX в строке; см. подраздел Регулярные выражения POSIX.

regexp_match('foobarbequebaz', '(bar)(beque)') → {bar,beque}

regexp_matches ( строка text, шаблон text [, флаги text ] ) → setof text[]

Возвращает фиксированные подстроки, полученные из первого вхождения регулярного выражения POSIX в строке или из нескольких вхождений, если используется флаг g; см. подраздел Регулярные выражения POSIX.

    regexp_matches('foobarbequebaz', 'ba.', 'g') →
         {bar}
         {baz}

regexp_replace ( строка text, шаблон text, замена text [, флаги text ] ) → text

Заменяет подстроки, полученные из первого вхождения регулярного выражения POSIX в строке или из нескольких вхождений подстроки, если используется флаг g; см. подраздел Регулярные выражения POSIX.

regexp_replace('Thomas', '.[mN]a.', 'M') → ThM

regexp_split_to_array ( строка text, шаблон text [, флаги text ] ) → text[]

Разделяет строку, используя в качестве разделителя регулярное выражение POSIX и выдавая массив результатов; см. подраздел Регулярные выражения POSIX.

regexp_split_to_array('hello world', '\s+') → {hello,world}

regexp_split_to_table ( строка text, шаблон text [, флаги text ] ) → setof text

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

    regexp_split_to_table('hello world', '\s+') →
         hello
         world

repeat ( строка text, число integer ) → text

Повторяет строку заданное число раз.

repeat('Pg', 4) → PgPgPgPg

replace ( строка text, цель text, замена text ) → text

Заменяет все вхождения в строку подстроки цель подстрокой замена.

replace('abcdefabcdef', 'cd', 'XX') → abXXefabXXef

reverse ( text ) → text

Переставляет символы в строке в обратном порядке.

reverse('abcde') → edcba

right ( строка text, n integer ) → text

Возвращает последние n символов в строке или, когда n — отрицательное число, возвращает все символы, кроме первых |n| символов.

right('abcde', 2) → de

rpad ( строка text, длина integer [, наполнитель text ] ) → text

Расширяет строку до заданной длины путем добавления справа символов наполнителей (по умолчанию пробелов). Если строка уже длиннее заданной длины, она обрезается (слева).

rpad('hi', 5, 'xy') → hixyx

rtrim ( строка text [, символы text ] ) → text

Удаляет самую длинную подстроку, содержащую только заданные символы (по умолчанию пробелы), с конца строки.

rtrim('testxxzx', 'xyz') → test

split_part ( строка text, разделитель text, n integer ) → text

Разделяет строку по разделителю и возвращает n-ое поле (считая с первого) или, когда n — отрицательное число, возвращает |n|-ое поле с конца.

split_part('abc~@~def~@~ghi', '~@~', 2) → def

split_part('abc,def,ghi,jkl', ',', -2) → ghi

strpos ( строка text, подстрока text ) → integer

Возвращает начальную позицию первого вхождения заданной подстроки в строке или ноль, если такого вхождения нет. (То же, что и функция position(подстрока in строка), но обратите внимание на обратный порядок аргументов.)

strpos('high', 'ig') → 2

substr ( строка text, начало integer [, число integer ] ) → text

Извлекает из строки подстроку, начиная с символа на позиции начало и длиной в число символов, если это значение указано. (То же, что и функция substring(строка from начало for число).)

substr('alphabet', 3) → phabet

substr('alphabet', 3, 2) → ph

starts_with ( строка text, префикс text ) → boolean

Возвращает true, если строка начинается с префикса.

starts_with('alphabet', 'alph') → t

string_to_array ( строка text, разделитель text [, нулевая_строка text ] ) → text[]

Разделяет строку по разделителю и формирует из результирующих полей текстовый массив. Если в разделителе передается NULL, каждый символ в строке станет отдельным элементом массива. Если в разделителе передается пустая строка, то вся строка рассматривается как одно поле. Если задана нулевая_строка и она отлична от NULL, то поля, соответствующие этой строке, заменяются значениями NULL.

string_to_array('xx~yy~zz', '~', 'yy') → {xx,NULL,zz}

string_to_table ( строка text, разделитель text [, нулевая_строка text ] ) → setof text

Разделяет строку по разделителю и возвращает результирующие поля в виде набора текстовых строк. Если в разделителе передается NULL, каждый символ в строке станет отдельной строкой результата. Если в разделителе передается пустая строка, то вся строка рассматривается как одно поле. Если задана нулевая_строка и она отлична от NULL, то поля, соответствующие этой строке, заменяются значениями NULL.

    string_to_table('xx~^~yy~^~zz', '~^~', 'yy') →
         xx
         NULL
         zz

to_ascii ( строка text ) → text

to_ascii ( строка text, кодировка name ) → text

to_ascii ( строка text, кодировка integer ) → text

Преобразует строку в ASCII из другой кодировки, которая может задаваться по имени или по номеру. Если кодировка опущена, подразумевается кодировка базы данных (что на практике является единственным полезным вариантом). В основном преобразование сводится к отбрасыванию диакритических знаков. Преобразование поддерживается только для кодировок LATIN1, LATIN2, LATIN9 и WIN1250. (Другое, более гибкое, решение реализовано в модуле unaccent.)

to_ascii('Karél') → Karel

to_hex ( integer ) → text

to_hex ( bigint ) → text

Преобразует число в равнозначное ему шестнадцатеричное представление.

to_hex(2147483647) → 7fffffff

translate ( строка text, цель text, замена text ) → text

Заменяет каждый символ в строке, совпадающий с символом из множества цель, на соответствующий символ в множестве замена. Если цель длиннее замены, вхождения лишних символов в цели удаляются.

translate('12345', '143', 'ax') → a2x5

unistr ( text ) → text

Определяет управляющие символы Unicode в аргументе. Символы Unicode можно задать в виде \XXXX (4 шестнадцатеричные цифры), \+XXXXXX (6 шестнадцатеричных цифр), \uXXXX (4 шестнадцатеричные цифры) или \UXXXXXXXX (8 шестнадцатеричных цифр). Чтобы указать обратный слэш, напишите два обратных слэша. Все остальные символы берутся буквально.
Если кодировка сервера не UTF-8, символ с кодом Unicode, заданный одной из этих управляющих последовательностей, преобразуется в фактическую кодировку сервера; если это невозможно, выдается ошибка.
Эта функция предоставляет (нестандартную) альтернативу строковым константам с управляющими последовательностями Unicode (см. подраздел Строковые константы с экранированием Unicode).

unistr('d\0061t\+000061') → data

unistr('d\u0061t\U00000061') → data

Функции concat, concat_ws и format принимают переменное число аргументов, поэтому им можно передавать значения для конкатенирования или форматирования в виде массива, помеченного ключевым словом VARIADIC (см. раздел Функции SQL с переменным числом аргументов). Элементы такого массива обрабатываются так, как если бы они были отдельными обычными аргументами функции. Если вместо массива в аргументе передается NULL, функции concat и concat_ws возвращают NULL, а format воспринимает NULL как массив с нулевым элементом.

Также обратите внимание на агрегатную функцию string_agg в разделе Агрегатные функции и функции для преобразования текста в bytea и наоборот в Таблице 13.

format

Функция format создает выходные данные, отформатированные в соответствии со строкой формата, в стиле, аналогичном функции C sprintf.

format(стр_формата text [, форматир_аргумент "any" [, ...] ])

стр_формата — строка формата, которая определяет, как результат должен быть отформатирован. Текст в строке формата копируется непосредственно в результат, кроме случаев, когда используются спецификаторы формата. Спецификаторы формата действуют как заполнители в строке, определяя, как именно последующие аргументы функции должны быть отформатированы и вставлены в результат. Каждый аргумент форматир_аргумент преобразуется в текст в соответствии с обычными правилами вывода для его типа данных, а затем форматируется и вставляется в результирующую строку в соответствии со спецификаторами формата.

Спецификаторы формата вводятся символом % и имеют форму

%[позиция][флаги][ширина]тип

где полями компонентов являются:

позиция (необязательный)
Строка вида n$, где n — индекс выводимого аргумента. Индекс, равный 1, означает первый аргумент после стр_формата. Если поле позиция опущено, по умолчанию используется следующий аргумент в последовательности.

флаги (необязательный)
Дополнительные параметры, управляющие форматированием вывода спецификатора формата. В настоящее время единственным поддерживаемым флагом является знак минус (-), который приведет к выравниванию вывода спецификатора формата по левому краю. Этот флаг не имеет никакого эффекта, если не указано поле ширина.

ширина (необязательный)
Задает минимальное количество символов, которое используется для отображения выходных данных спецификатора формата. Выходные данные дополняются слева или справа (в зависимости от флага -) пробелами, необходимыми для заполнения до заданной ширины. Слишком маленькая ширина не приводит к усечению вывода, а просто игнорируется. Ширина можно указать любым из следующих способов: положительным целым числом, звездочкой (*) для использования в качестве ширины следующего аргумента функции или строкой вида *n$ для использования в качестве ширины n-го аргумента функции.

Если ширина берется из аргумента функции, этот аргумент задействуется раньше аргумента, используемого для значения спецификатора формата. Если аргумент ширина отрицательный, результат выравнивается по левому краю (как если бы был указан флаг -) в поле длиной abs(ширина).

тип (обязательный)
Тип преобразования формата, используемый для создания выходных данных спецификатора формата. Поддерживаются следующие типы:

s форматирует значение аргумента как простую строку. Значение NULL рассматривается как пустая строка.

I обрабатывает значение аргумента как идентификатор SQL, при необходимости заключая его в кавычки. Здесь значение NULL является ошибкой (так же, как и для quote_ident).

L заключает значение аргумента в апострофы, как литерал SQL. Значение NULL отображается в виде строки NULL, без кавычек (так же, как и у quote_nullable).

В дополнение к спецификаторам формата, описанным выше, можно использовать специальную последовательность %% для вывода буквального символа %.

Вот несколько примеров основных преобразований формата:

SELECT format('Hello% s', 'World');
Результат: Hello World

SELECT format('Testing %s, %s, %s, %%', 'one', 'two', 'three');
Результат: Testing one, two, three, %

SELECT format('INSERT INTO %I VALUES(%L)', 'Foo bar', E'O\'Reilly');
Результат: INSERT INTO "Foo bar" VALUES('O''Reilly')

SELECT format('INSERT INTO %I VALUES(%L)', 'locations', 'C:\Program Files');
Результат: INSERT INTO locations VALUES('C:\Program Files')

Вот примеры использования полей ширина и флага -:

SELECT format('|%10s|', 'foo');
Результат: |       foo|

SELECT format('|%-10s|', 'foo');
Результат: |foo       |

SELECT format('|%*s|', 10, 'foo');
Результат: |       foo|

SELECT format('|%*s|', -10, 'foo');
Результат: |foo       |

SELECT format('|%-*s|', 10, 'foo');
Результат: |foo       |

SELECT format('|%-*s|', -10, 'foo');
Результат: |foo       |

Эти примеры показывают использование полей позиция:

SELECT format('Testing %3$s, %2$s, %1$s', 'one', 'two', 'three');
Результат: Testing three, two, one

SELECT format('|%*2$s|', 'foo', 10, 'bar');
Результат: |       bar|

SELECT format('|%1$*2$s|', 'foo', 10, 'bar');
Результат: |       foo|

В отличие от стандартной функции C sprintf, функция format в QHB позволяет сочетать в одной строке формата спецификаторы формата с полями позиция и без них. Спецификатор формата без поля позиция всегда использует следующий аргумент после последнего задействованного аргумента. Кроме того, функция format не требует использования в строке формата всех аргументов функции. Например:

SELECT format('Testing %3$s, %2$s, %s', 'one', 'two', 'three');
Результат: Testing three, two, three

Спецификаторы формата %I и %L особенно полезны для безопасного построения динамических операторов SQL. См. пример Заключение в кавычки значений в динамических запросах.

Функции и операторы для двоичных строк

В этом разделе описываются функции и операторы для проверки и обработки двоичных строк, то есть значений типа bytea. Многие из них идентичны по назначению и синтаксису функциям для текстовых строк, описанным в предыдущем разделе.

В SQL определены некоторые строковые функции, в которых для разделения аргументов вместо запятых используются ключевые слова. Подробнее это показано в Таблице 11. QHB также предоставляет версии этих функций, которые используют обычный синтаксис вызова функций (см. Таблицу 12).

Таблица 11. Функции и операторы SQL для двоичных строк

Функция/оператор

Описание

Пример(ы)

bytea || bytea → bytea

Конкатенирует две двоичные строки.

'\x123456'::bytea || '\x789a00bcde'::bytea → \x123456789a00bcde

bit_length ( bytea ) → integer

Возвращает количество битов в двоичной строке (это число в 8 раз больше octet_length).

bit_length('\x123456'::bytea) → 24

octet_length ( bytea ) → integer

Возвращает количество байтов в двоичной строке.

octet_length('\x123456'::bytea) → 3

overlay ( двоичная_строка bytea PLACING новая_подстрока bytea FROM начало integer [ FOR число integer ] ) → bytea

Заменяет подстроку в двоичной_строке, начиная с байта на позиции начало и длиной в число байтов, на новую_подстроку. Если число опущено, по умолчанию количество заменяемых байтов определяется длиной новой_подстроки.

overlay('\x1234567890'::bytea placing '\002\003'::bytea from 2 for 3) → \x12020390

position ( подстрока bytea IN двоичная_строка bytea ) → integer

Возвращает начальную позицию первого вхождения заданной подстроки в двоичной_строке или ноль, если такого вхождения нет.

position('\x5678'::bytea in '\x1234567890'::bytea) → 3

substring ( двоичная_строка bytea [ FROM начало integer ] [ FOR число integer ] ) → bytea

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

substring('\x1234567890'::bytea from 3 for 2) → \x5678

trim ( [ LEADING | TRAILING | BOTH ] удаляемые_байты bytea FROM двоичная_строка bytea ) → bytea

Удаляет самую длинную подстроку, содержащую только байты, заданные в аргументе удаляемые_байты, с начала, конца или обеих сторон (по умолчанию подразумевается последнее — BOTH) двоичной_строки.

trim('\x9012'::bytea from '\x1234567890'::bytea) → \x345678

trim ( [ LEADING | TRAILING | BOTH ] [ FROM ] двоичная_строка bytea, удаляемые_байты bytea ) → bytea

Это нестандартный синтаксис функции trim().

trim(both from '\x1234567890'::bytea, '\x9012'::bytea) → \x345678

В QHB доступны также дополнительные функции для обработки двоичных строк, перечисленные в Таблице 12. Некоторые из них используются для внутренней реализации стандартных строковых функций SQL, перечисленных в Таблице 11.

Таблица 12. Другие функции для двоичных строк

Функция

Описание

Пример(ы)

bit_count ( двоичная_строка bytea ) → bigint

Возвращает количество установленных битов (единиц) в двоичной строке (эта операция также известна как «popcount»).

bit_count('\x1234567890'::bytea) → 15

btrim ( двоичная_строка bytea, удаляемые_байты bytea ) → bytea

Удаляет самую длинную строку, содержащую только байты, заданные в аргументе удаляемые_байты, с начала и конца двоичной_строки.

btrim('\x1234567890'::bytea, '\x9012'::bytea) → \x345678

get_bit ( двоичная_строка bytea, n bigint ) → integer

Извлекает n-й бит из двоичной строки.

get_bit('\x1234567890'::bytea, 30) → 1

get_byte ( двоичная_строка bytea, n integer ) → integer

Извлекает n-й байт из двоичной строки.

get_byte('\x1234567890'::bytea, 4) → 144

length ( bytea ) → integer

Возвращает количество байтов в двоичной строке.

length('\x1234567890'::bytea) → 5

length ( двоичная_строка bytea, кодировка name ) → integer

Возвращает количество символов в двоичной строке, предполагая, что она содержит текст в заданной кодировке.

length('jose'::bytea, 'UTF8') → 4

ltrim ( двоичная_строка bytea, удаляемые_байты bytea ) → bytea

Удаляет самую длинную подстроку, содержащую только байты, заданные в аргументе удаляемые_байты, с начала двоичной_строки.

ltrim('\x1234567890'::bytea, '\x9012'::bytea) → \x34567890

md5 ( bytea ) → text

Вычисляет MD5-хеш двоичной строки с результатом в шестнадцатеричном виде.

md5('Th\000omas'::bytea) → 8ab2d3c9689aaf18​b4958c334c82d8b1

rtrim ( двоичная_строка bytea, удаляемые_байты bytea ) → bytea

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

rtrim('\x1234567890'::bytea, '\x9012'::bytea) → \x12345678

set_bit ( двоичная_строка bytea, n bigint, новое_значение integer ) → bytea

Задает для n-го бита в двоичной строке новое_значение.

set_bit('\x1234567890'::bytea, 30, 0) → \x1234563890

set_byte ( двоичная_строка bytea, n integer, новое_значение integer ) → bytea

Задает для n-го байта в двоичной строке новое_значение.

set_byte('\x1234567890'::bytea, 4, 64) → \x1234567840

sha224 ( bytea ) → bytea

Вычисляет хеш SHA-224 для двоичной строки.

sha224('abc'::bytea) → \x23097d223405d8228642a477bda2​55b32aadbce4bda0b3f7e36c9da7

sha256 ( bytea ) → bytea

Вычисляет хеш SHA-256 для двоичной строки.

sha256('abc'::bytea) → \xba7816bf8f01cfea414140de5dae2223​b00361a396177a9cb410ff61f20015ad

sha384 ( bytea ) → bytea

Вычисляет хеш SHA-384 для двоичной строки.

sha384('abc'::bytea) → \xcb00753f45a35e8bb5a03d699ac65007​272c32ab0eded1631a8b605a43ff5bed​8086072ba1e7cc2358baeca134c825a7

sha512 ( bytea ) → bytea

Вычисляет хеш SHA-512 для двоичной строки.

sha512('abc'::bytea) → \xddaf35a193617abacc417349ae204131​12e6fa4e89a97ea20a9eeee64b55d39a​2192992a274fc1a836ba3c23a3feebbd​454d4423643ce80e2a9ac94fa54ca49f

substr ( двоичная_строка bytea, начало integer [, число integer ] ) → bytea

Извлекает из двоичной_строки подстроку, начиная с байта на позиции начало, длиной в число байт, если оно указано. (То же, что и функция substring(двоичная_строка from начало for число).)

substr('\x1234567890'::bytea, 3, 2) → \x5678

Функции get_byte и set_byte нумеруют первый байт двоичной строки как байт 0. Функции get_bit и set_bit нумеруют биты справа налево внутри каждого байта; например, бит 0 является самым младшим значащим битом первого байта, а бит 15 — самым старшим значащим битом второго байта.

По историческим причинам функция md5 возвращает шестнадцатеричное значение типа text тогда как функции SHA-2 возвращают тип bytea. Для преобразования этих значений одно в другое используйте функции encode и decode. Например, можно написать encode(sha256(’abc’), ’hex’) для получения шестнадцатеричного текстового представления или decode(md5('abc'), 'hex') для получения значения bytea.

Функции для преобразования строк из одного набора символов (кодировки) в другой, а также для представления произвольных двоичных данных в текстовой форме перечислены в Таблице 13. Для этих функций аргумент или результат типа text выражается в кодировке базы данных по умолчанию, тогда как аргументы или результаты типа bytea — в кодировке, заданной другим аргументом.

Таблица 13. Функции для преобразования текстовых/двоичных строк

Функция

Описание

Пример(ы)

convert ( двоичная_строка bytea, исходная_кодировка name, целевая_кодировка name ) → bytea

Преобразует двоичную строку, представляющую текст в кодировке исходная_кодировка, в двоичную строку в кодировке целевая_кодировка (допустимые преобразования см. в подразделе Доступные перекодировки).

convert('text_in_utf8', 'UTF8', 'LATIN1') → \x746578745f696e5f75746638

convert_from ( двоичная_строка bytea, исходная_кодировка name ) → text

Преобразует двоичную строку, представляющую текст в кодировке исходная_кодировка, в текст в кодировке базы данных (допустимые преобразования см. в подразделе Доступные перекодировки).

convert_from('text_in_utf8', 'UTF8') → text_in_utf8

convert_to ( строка text, целевая_кодировка name ) → bytea

Преобразует строку типа text (в кодировке базы данных) в двоичную строку в кодировке целевая_кодировка (допустимые преобразования см. в подразделе Доступные перекодировки).

convert_to('some_text', 'UTF8') → \x736f6d655f74657874

encode ( двоичная_строка bytea, формат text ) → text

Кодирует двоичные данные в текстовое представление; поддерживаются следующие значения формата: base64, escape, hex.

encode('123\000\001', 'base64') → MTIzAAE=

decode ( строка text, формат text ) → bytea

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

decode('MTIzAAE=', 'base64') → \x3132330001

Функции encode и decode поддерживают следующие текстовые форматы:

base64
Формат base64 описан в RFC 2045, разделе 6.8. Согласно этому RFC, закодированные строки разбиваются по 76 символов. Однако вместо маркеров CRLF (как положено по стандарту MIME) строки завершаются только символом новой строки. Функция decode игнорирует символы возврата каретки, новой строки, пробелы и табуляции. В иных случаях если на вход decode поступают некорректные данные base64, возникает ошибка — в том числе, когда некорректно завершающее заполнение.

escape
Формат escape преобразует нулевые байты и байты с установленным старшим битом в восьмеричные экранированные последовательности (\nnn), а обратные слэши дублируются. Другие байтовые значения представляются в буквальном виде. Если за обратным слэшем не следует еще один обратный слэш или три восьмеричных цифры, функция decode выдаст ошибку; другие байтовые значения она принимает без изменений

hex
Формат hex представляет каждые 4 бита данных в виде одной шестнадцатеричной цифры, от 0 до f, первой записывая старший бит каждого байта. Функция encode выводит шестнадцатеричные цифры a-f в нижнем регистре. Поскольку наименьшая единица данных — 8 битов, функция encode всегда возвращает четное количество символов. Функция decode принимает символы a-f либо в верхнем, либо в нижнем регистре. Если на вход decode поступают некорректные шестнадцатеричные данные, возникает ошибка — в том числе, когда вводится нечетное количество символов.

См. также агрегатную функцию string_agg в разделе Агрегатные функции и функции для больших объектов в разделе Серверные функции.

Функции и операторы для битовых строк

В этом разделе описываются функции и операторы для проверки и обработки битовых строк, то есть значений типов bit и bit varying. (Хотя в этих таблицах упоминается только тип bit, вместо них можно использовать значения типа bit varying) Битовые строки поддерживают обычные операторы сравнения, показанные в Таблице 1, а также операторы, перечисленные в Таблице 14.

Таблица 14. Операторы для битовых строк

Оператор

Описание

Пример(ы)

bit || bit → bit

Конкатенация

B'10001' || B'011' → 10001011

bit & bit → bit

Побитовое И (операнды должны быть одинаковой длины)

B'10001' & B'01101' → 00001

bit | bit → bit

Побитовое ИЛИ (операнды должны быть одинаковой длины)

B'10001' | B'01101' → 11101

bit # bit → bit

Побитовое исключающее ИЛИ (операнды должны быть одинаковой длины)

B'10001' # B'01101' → 11100

~ bit → bit

Побитовое НЕ

~ B'10001' → 01110

bit << integer → bit

Битовый сдвиг влево (с сохранением длины строки)

B'10001' << 3 → 01000

bit >> integer → bit

Битовый сдвиг вправо (с сохранением длины строки)

B'10001' >> 2 → 00100

Некоторые из функций, доступных для двоичных строк, работают также и с битовыми строками, как показано в Таблице 15.

Таблица 15. Функции для битовых строк

Функция

Описание

Пример(ы)

bit_count ( bit ) → bigint

Возвращает количество установленных битов (единиц) в битовой строке (эта операция также известна как «popcount»).

bit_count(B'10111') → 4

bit_length ( bit ) → integer

Возвращает количество битов в битовой строке.

bit_length(B'10111') → 5

length ( bit ) → integer

Возвращает количество битов в битовой строке.

length(B'10111') → 5

octet_length ( bit ) → integer

Возвращает количество байтов в битовой строке.

octet_length(B'1011111011') → 2

overlay ( битовая_строка bit PLACING новая_подстрока bit FROM начало integer [ FOR число integer ] ) → bit

Заменяет подстроку в битовой\_строке, начиная с бита на позиции начало, длиной в число битов, на новую_подстроку. Если число опущено, по умолчанию количество заменяемых битов определяется длиной новой_подстроки.

overlay(B'01010101010101010' placing B'11111' from 2 for 3) → 0111110101010101010

position ( подстрока bit IN битовая_строка bit ) → integer

Возвращает начальную позицию первого вхождения подстроки в битовой_строке или ноль, если такого вхождения нет.

position(B'010' in B'000001101011') → 8

substring ( битовая_строка bit [ FROM начало integer ] [ FOR число integer ] ) → bit

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

substring(B'110010111111' from 3 for 2) → 00

get_bit ( битовая_строка bit, n integer ) → integer

Извлекает из битовой строки n-й бит; первый (самый левый) бит имеет номер 0.

get_bit(B'101010101010101010', 6) → 1

set_bit ( битовая_строка bit, n integer, новое_значение integer ) → bit

Устанавливает для n-го бита в битовой строке новое_значение; первый (самый левый) бит имеет номер 0.

set_bit(B'101010101010101010', 6, 0) → 101010001010101010

Кроме того, в тип bit и обратно можно преобразовывать целочисленные значения. Приведение целого числа к типу bit(n) копирует в него n самых правых бит. Когда целое число приводится к битовой строке с большей длиной, чем размер самого целого, результат дополняется слева знаком. Некоторые примеры:

44::bit(10)                    0000101100
44::bit(3)                     100
cast(-44 as bit(12))           111111010100
'1110'::bit(4)::integer        14

Обратите внимание, что приведение к типу «bit» без длины означает приведение к bit(1), и выдаст только один младший значащий бит целого.

Сопоставление с шаблоном

QHB предлагает три разных подхода к сопоставлению с шаблоном: традиционный оператор SQL LIKE, более современный оператор SIMILAR TO (добавленный в SQL:1999) и регулярные выражения в стиле POSIX. Помимо основного «эта строка соответствует этому шаблону?», имеются также операторы и функции для извлечения или замены соответствующих подстрок и для разделения строки по совпадающим местам.

Совет
Если для сопоставления с шаблоном вам недостаточно вышеперечисленных средств, рассмотрите возможность написания пользовательской функции на Perl или Tcl.

ВНИМАНИЕ!
Хотя по большей части поиск по регулярным выражениям может выполняться очень быстро, есть вероятность создания регулярных выражений, обработка которых занимает некоторое количество времени и памяти. Остерегайтесь шаблонов поиска по регулярным выражениям, поступающих из неблагоприятных источников. Если вам приходится это делать, рекомендуется установить тайм-аут для операторов.

Поиск с использованием шаблонов SIMILAR TO сопряжен с теми же угрозами безопасности, поскольку SIMILAR TO во многом предоставляет те же возможности, что и регулярные выражения в стиле POSIX.

Поскольку поиск с LIKE намного проще двух других вариантов, его безопаснее использовать с предположительно неблагоприятными источниками шаблонов.

Операторы сопоставления с шаблоном всех трех видов не поддерживают недетерминированные сопоставления. При необходимости обойти это ограничение примените к выражению другое сопоставление.

LIKE

строка LIKE шаблон [ESCAPE управляющий_символ]
строка NOT LIKE шаблон [ESCAPE управляющий_символ]

Выражение LIKE возвращает true, если строка соответствует предоставленному шаблону. (Как и следовало ожидать, выражение NOT LIKE возвращает false, если LIKE возвращает true, и наоборот. Этому выражению равнозначно выражение NOT (строка LIKE шаблон).)

Если шаблон не содержит знаков процента или подчеркивания, то шаблон представляет только саму строку; в этом случае LIKE действует как оператор равенства. Подчеркивание (_) в шаблоне обозначает (подменяет) любой отдельный символ; знак процента (%) соответствует любой последовательности из нуля и более символов.

Несколько примеров:

'abc' LIKE 'abc'    true
'abc' LIKE 'a%'     true
'abc' LIKE '_b_'    true
'abc' LIKE 'c'      false

При сопоставлении с шаблоном с LIKE всегда охватывается вся строка. Поэтому если требуется сопоставить последовательность где-то внутри строки, шаблон должен начинаться и заканчиваться знаком процента.

Чтобы сопоставить буквальное подчеркивание или знак процента, не сопоставляя другие символы, соответствующему символу в шаблоне должен предшествовать управляющий символ. Управляющим символом по умолчанию является обратный слеш, но с помощью предложения ESCAPE можно выбрать любой другой символ. Чтобы сопоставить сам управляющий символ, напишите его дважды.

Примечание
Если у вас выключен параметр standard_conforming_strings, любые обратные слеши, которые вы пишете в константах литеральных строк, нужно будет дублировать. Дополнительную информацию см. в подразделе Строковые константы.

Также можно отказаться от управляющих символов, написав ESCAPE ''. По сути это выключает механизм экранирования, что делает невозможным выключение специального значения знаков подчеркивания и процента в шаблоне.

Согласно стандарту SQL, отсутствие указания ESCAPE означает, что управляющий символ не определен (а не становится по умолчанию обратным слэшем), а пустое значение в ESCAPE не допускается. Таким образом, в этом отношении поведение QHB значительно отличается от стандартного.

Вместо LIKE можно использовать ключевое слово ILIKE, чтобы при сопоставлении не учитывался регистр в соответствии с текущей локалью. Это ключевое слово отсутствует в стандарте SQL и является расширением QHB.

Оператор ~~ равнозначен LIKE, а ~~* соответствует ILIKE. Также имеются операторы !~~ и !~~*, которые представляют NOT LIKE и NOT ILIKE соответственно. Все эти операторы специфичны для QHB. Имена этих операторов можно увидеть в выводе команды EXPLAIN и в иных подобных местах, поскольку синтаксический анализатор фактически заменяет LIKE и прочие на эти операторы.

Фразы LIKE, ILIKE, NOT LIKE и NOT ILIKE в синтаксисе QHB обычно обрабатываются как операторы; например, их можно использовать в конструкциях выражение оператор ANY (подзапрос), хотя предложение ESCAPE сюда включить нельзя. В некоторых сложных случаях может понадобиться использовать вместо них имена нижележащих операторов.

Также есть оператор префикса ^@ и соответствующая функция starts_with, которые полезны в случаях, когда необходимо сопоставление только с началом строки.

Регулярные выражения SIMILAR TO

строка SIMILAR TO шаблон [ESCAPE управляющий_символ]
строка NOT SIMILAR TO шаблон [ESCAPE управляющий_символ]

Оператор SIMILAR TO возвращает true или false в зависимости от того, соответствует ли его шаблон заданной строке. Он похож на LIKE, за исключением того, что он интерпретирует шаблон, используя определение регулярного выражения в стандарте SQL. Регулярные выражения SQL представляют собой любопытную смесь между нотацией LIKE и нотацией обычного (POSIX) регулярного выражения.

Как и LIKE, оператор SIMILAR TO успешно выполняется, только если его шаблон соответствует всей строке; это не похоже на обычное поведение регулярных выражений, когда шаблон может соответствовать любой части строки. Также подобно LIKE, SIMILAR TO использует символы _ и % в качестве подстановочных символов, обозначающих любой отдельный символ и любую строку соответственно (они сопоставимы с . и .* в регулярных выражениях POSIX).

В дополнение к этим возможностям, заимствованным из LIKE, SIMILAR TO поддерживает метасимволы сопоставления с шаблоном, заимствованные из регулярных выражений POSIX:

| обозначает чередование (любой из двух вариантов).

* обозначает повторение предыдущего пункта ноль и более раз.

+ обозначает повторение предыдущего пункта один и более раз.

? обозначает повторение предыдущего пункта ноль или один раз.

{m} обозначает повторение предыдущего элемента ровно m раз.

{m,} обозначает повторение предыдущего элемента m и более раз.

{m,n} обозначает повторение предыдущего элемента не менее m и не более n раз.

Круглые скобки () могут использоваться для группировки элементов в один логический элемент.

Выражение в квадратных скобках [...] определяет класс символов, как и в регулярных выражениях POSIX.

Обратите внимание, что точка (.) не является метасимволом для SIMILAR TO.

Как и в случае с LIKE, обратный слэш отменяет специальное значение любого из этих метасимволов. Можно указать другой управляющий символ с помощью ESCAPE или отказаться от экранирования, написав ESCAPE ''.

Согласно стандарту SQL, отсутствие указания ESCAPE означает, что управляющий символ не определен (а не становится по умолчанию обратным слэшем), а пустое значение в ESCAPE не допускается. Таким образом, в этом отношении поведение QHB значительно отличается от стандартного.

Еще одно нестандартное расширение заключается в том, что следующая за управляющим символом буква или цифра открывает доступ к управляющим последовательностям, определенным для регулярных выражений POSIX; см. Таблицу 20, Таблицу 21 и Таблицу 22 ниже.

Несколько примеров:

'abc' SIMILAR TO 'abc'      true
'abc' SIMILAR TO 'a'        false
'abc' SIMILAR TO '%(b|d)%'  true
'abc' SIMILAR TO '(b|c)%'   false

Функция substring с тремя параметрами извлекает подстроку, соответствующую шаблону регулярного выражения SQL. Эту функцию можно написать в соответствии со стандартным синтаксисом SQL:

substring(строка similar шаблон escape управляющий_символ)

или используя синтаксис ныне устаревшего SQL:1999:

substring(строка from шаблон for управляющий_символ)

или как простую функцию с тремя аргументами:

substring(строка, шаблон, управляющий_символ)

Как и в случае с SIMILAR TO, указанный шаблон должен соответствовать всей строке данных, иначе функция не сработает и вернет значение NULL. Чтобы указать часть шаблона, которому соответствует интересующая подстрока данных, шаблон должен содержать два вхождения управляющего символа и следующей за ним кавычки ("). При успешном сопоставлении текст, соответствующий части шаблона, возвращается, заключенный в эти разделители.

Разделители, состоящие из управляющего символа и кавычек, фактически делят шаблон substring на три независимых регулярных выражения; например, вертикальная черта (|) в любом из трех разделов влияет только на этот раздел. Кроме того, когда есть некоторая неоднозначность относительно того, какая часть строки данных соответствует какому шаблону, первое и третье из этих регулярных выражений определяются так, чтобы соответствовать наименьшему возможному объему текста, а не наибольшему. (Говоря на жаргоне POSIX, первое и третье регулярные выражения должны быть «нежадными»).

В качестве расширения стандарта SQL QHB позволяет использовать только один разделитель из управляющего символа и кавычек, и в этом случае третье регулярное выражение считается пустым; или вовсе не использовать разделители, и в этом случае считаются пустыми первое и третье регулярные выражения.

Несколько примеров с разделителями #", ограничивающими возвращаемую строку:

substring('foobar' from '%#"o_b#"%' for '#')   oob
substring('foobar' from '#"o_b#"%' for '#')    NULL

Регулярные выражения POSIX

В Таблице 16 перечислены доступные операторы для сопоставления с шаблоном с использованием регулярных выражений POSIX.

Таблица 16. Операторы сопоставления регулярных выражений

Оператор

Описание

Пример(ы)

text ~ text → boolean

Строка соответствует регулярному выражению, с учетом регистра

'thomas' ~ 't.*ma' → t

text ~* text → boolean

Строка соответствует регулярному выражению, без учета регистра

'thomas' ~* 'T.*ma' → t

text !~ text → boolean

Строка не соответствует регулярному выражению, с учетом регистра

'thomas' !~ 't.*max' → t

text !~* text → boolean

Строка не соответствует регулярному выражению, без учета регистра

'thomas' !~* 'T.*ma' → f

Регулярные выражения POSIX предоставляют более мощные средства для сопоставления с шаблоном, чем операторы LIKE и SIMILAR TO. Многие инструменты Unix, такие как egrep, sed или awk, используют язык сопоставления с шаблоном, похожий на описанный здесь.

Регулярное выражение — это последовательность символов, представляющая собой сокращенное определение некоего множества строк (регулярного множества). Считается, что строка соответствует регулярному выражению, если она является членом регулярного множества, описанного регулярным выражением. Как и для LIKE, символы шаблона точно соответствуют строковым символам, если те не являются специальными символами в языке регулярных выражений — но специальные символы в регулярных выражениях отличаются от тех, что используются в LIKE. В отличие от шаблонов LIKE, регулярному выражению может совпадать с любым местом строки, если только оно не привязано явно к началу или концу строки.

Несколько примеров:

'abcd' ~ 'bc'     true
'abcd' ~ 'a.c'    true — точка соответствует любому символу
'abcd' ~ 'a.*d'   true — * повторяет предыдущий элемент шаблона
'abcd' ~ '(b|x)'  true — | означает ИЛИ для группы в скобках
'abcd' ~ '^a'     true — ^ привязывает шаблон к началу строки
'abcd' ~ '^(b|c)' false — совпадение не найдено по причине привязки

Более подробно язык шаблонов POSIX описан ниже.

Функция substring с двумя параметрами substring(строка from шаблон) извлекает подстроку, соответствующую шаблону регулярного выражения POSIX. Возвращает NULL, если совпадения нет, либо первый фрагмент текста, соответствующий шаблону, в противном случае. Но если шаблон содержит круглые скобки, возвращается часть текста, соответствующая первому подвыражению в скобках (та, чья левая скобка стоит первой). Если вы хотите использовать в выражении круглые скобки, не запуская это исключение, можно заключить в них все выражение. Если вам нужны круглые скобки в шаблоне перед подвыражением, которое вы хотите извлечь, смотрите ниже описание использования скобок без захвата.

Несколько примеров:

substring('foobar' from 'o.b')     oob
substring('foobar' from 'o(.)b')   o

Функция regexp_replace подставляет новый текст вместо подстрок, соответствующих шаблонам регулярных выражений POSIX. Она имеет синтаксис regexp_replace(исходная_строка, шаблон, замена [, флаги ]). Если нет совпадения с шаблоном, исходная_строка возвращается без изменений. Если совпадение есть, исходная_строка возвращается с заменой вместо соответствующей подстроки. Строка замена может содержать \n, где n — число от 1 до 9, указывающее, что должна быть вставлена исходная подстрока, соответствующая n-му подвыражению шаблона в скобках, а также может содержать \&, указывающее, что должна быть вставлена подстрока, соответствующая всему шаблону. Если нужно вставить обратный слеш в текст замены буквально, напишите \\. Параметр флаги представляет собой необязательную текстовую строку, содержащую ноль или более однобуквенных флагов, которые изменяют поведение функции. Флаг i указывает, что сопоставление должно проводиться без учета регистра, а флаг g указывает, что заменяться должны все соответствующие подстроки, а не только первая из них. Поддерживаемые флаги (кроме g) описаны в Таблице 24.

Несколько примеров:

regexp_replace('foobarbaz', 'b..', 'X')
                                   fooXbaz
regexp_replace('foobarbaz', 'b..', 'X', 'g')
                                   fooXX
regexp_replace('foobarbaz', 'b(..)', 'X\1Y', 'g')
                                   fooXarYXazY

Функция regexp_match возвращает текстовый массив захваченных подстрок, полученных в результате первого сопоставления шаблона регулярного выражения POSIX со строкой. Она имеет синтаксис regexp_match(строка, шаблон [, флаги ]). Если совпадений нет, результатом будет NULL. Если совпадение найдено и шаблон не содержит подвыражений в скобках, то результатом будет текстовый массив из одного элемента, содержащий подстроку, соответствующую всему шаблону. Если совпадение найдено и шаблон содержит подвыражения в скобках, то результатом будет текстовый массив, чей n-й элемент является подстрокой, соответствующей n-му подвыражению в скобках шаблона (не считая «не захватывающих» скобок; подробности см. ниже). Параметр флаги представляет собой необязательную текстовую строку, содержащую ноль или более однобуквенных флагов, изменяющих поведение функции. Поддерживаемые флаги описаны в Таблице 24.

Несколько примеров:

SELECT regexp_match('foobarbequebaz', 'bar.*que');
 regexp_match
--------------
 {barbeque}
(1 row)

SELECT regexp_match('foobarbequebaz', '(bar)(beque)');
 regexp_match
--------------
 {bar,beque}
(1 row)

В общем случае, когда вы просто хотите получить всю соответствующую подстроку или NULL, если совпадений нет, напишите что-то вроде

SELECT (regexp_match('foobarbequebaz', 'bar.*que'))[1];
 regexp_match
--------------
 barbeque
(1 row)

Функция regexp_matches возвращает набор текстовых массивов захваченных подстрок, полученных в результате сопоставления шаблона регулярного выражения POSIX со строкой. Она имеет тот же синтаксис, что и regexp_match. Эта функция не возвращает ни одной строки, если совпадения нет, одну строка, если есть совпадение и не задан флаг g, или N строк, если есть N совпадений и задан флаг g. Каждая возвращаемая строка является текстовым массивом, содержащим всю сопоставляемую подстроку или подстроки, соответствующие подвыражениям в скобках шаблона, как описано выше для regexp_match. Функция regexp_matches принимает все флаги, перечисленные в Таблице 24, плюс флаг g, который указывает ей возвращать все совпавшие подстроки, а не только первую.

Несколько примеров:

SELECT regexp_matches('foo', 'not there');
 regexp_matches
----------------
(0 rows)

SELECT regexp_matches('foobarbequebazilbarfbonk', '(b[^b]+)(b[^b]+)', 'g');
 regexp_matches
----------------
 {bar,beque}
 {bazil,barf}
(2 rows)

Функция regexp_split_to_table разбивает строку, используя в качестве разделителя шаблон регулярного выражения POSIX. Она имеет синтаксис regexp_split_to_table(строка, шаблон [, флаги ]). Если нет совпадения с шаблоном, функция возвращает строку. Если найдено хотя бы одно совпадение, для каждого его вхождения она возвращает текст от конца предыдущего вхождения (или начала строки) до начала этого вхождения. Дойдя до последнего вхождения, она возвращает текст от конца этого последнего вхождения до конца строки. Параметр флаги представляет собой необязательную текстовую строку, содержащую ноль или более однобуквенных флагов, изменяющих поведение функции. regexp_split_to_table поддерживает флаги, описанные в Таблице 24.

Функция regexp_split_to_array ведет себя так же, как regexp_split_to_table, за исключением того, что regexp_split_to_array возвращает свой результат в виде массива элементов типа text. Она имеет синтаксис regexp_split_to_array(строка, шаблон [, флаги ]). Параметры те же, что и для regexp_split_to_table.

Несколько примеров:

SELECT foo FROM regexp_split_to_table('the quick brown fox jumps over the lazy dog', '\s+') AS foo;
  foo
-------
 the
 quick
 brown
 fox
 jumps
 over
 the
 lazy
 dog
(9 rows)

SELECT regexp_split_to_array('the quick brown fox jumps over the lazy dog', '\s+');
              regexp_split_to_array
-----------------------------------------------
 {the,quick,brown,fox,jumps,over,the,lazy,dog}
(1 row)

SELECT foo FROM regexp_split_to_table('the quick brown fox', '\s*') AS foo;
 foo
-----
 t
 h
 e
 q
 u
 i
 c
 k
 b
 r
 o
 w
 n
 f
 o
 x
(16 rows)

Как показывает последний пример, функции разбиения по регулярным выражениям игнорируют вхождения нулевой длины, которые находятся в начале или конце строки или сразу после предыдущего вхождения. Это противоречит строгому определению сопоставления по регулярным выражениям, которое реализуется с помощью функций regexp_match и regexp_matches, но обычно на практике является наиболее удобным поведением. Аналогичные определения используют и другие программные системы, например Perl.

Подробное описание регулярных выражений

Регулярные выражения в QHB реализованы с использованием программного пакета, написанного Генри Спенсером (Henry Spencer). Большая часть представленного ниже описания регулярных выражений дословно скопирована из его руководства.

Регулярные выражения (Regular expression, RE), как определено в POSIX 1003.2, имеют две формы: расширенные RE, или ERE (грубо говоря, те, что предназначены для egrep), и базовые RE или BRE (грубо говоря, те, что предназначены для ed). QHB поддерживает обе формы, а также реализует некоторые расширения, которые не входят в стандарт POSIX, но стали широко использоваться благодаря их доступности в таких языках программирования, как Perl и Tcl. RE, использующие эти не предусмотренные в POSIX расширения, в данной документации называются продвинутыми RE, или ARE. ARE являются почти точным надмножеством ERE, но у BRE имеется некоторая несовместимость в нотации (кроме того, они гораздо более ограничены). Сначала мы опишем формы ARE и ERE, отметив черты, характерные только для ARE, а затем опишем, чем отличаются BRE.

Примечание
QHB всегда изначально предполагает, что регулярное выражение следует правилам ARE. Однако можно выбрать более ограниченные правила ERE или BRE, добавив в шаблон RE встроенный параметр, как описано в подразделе Метасинтаксис регулярных выражений. Это может быть полезно для совместимости с приложениями, ожидающими неукоснительного следования правилам POSIX 1003.2.

Регулярное выражение определяется как одна или несколько ветвей, разделенных символами |. Оно соответствует всему, что соответствует одной из этих ветвей.

Ветвь — это ноль или более конкатенированных количественных атомов или ограничений. Она соответствует вхождению первого элемента, за которым следует вхождение второго элемента и т. д.; пустая ветвь соответствует пустой строке.

Количественный атом — это атом, за которым может следовать один квантификатор. Без квантификатора он соответствует одному вхождению атома. С квантификатором он может соответствовать некоторому количеству вхождений атома. Атом может быть любой из возможностей, показанных в Таблице 17. Возможные квантификаторы и их значения приведены в Таблице 18.

Ограничение соответствует пустой строке, но соответствует только при выполнении определенных условий. Ограничение можно использовать там же, где можно использовать атом, за исключением того, что за ним не может следовать квантификатор. Простые ограничения показаны в Таблице 19; некоторые дополнительные ограничения описаны ниже.

Таблица 17. Атомы регулярного выражения

Атом | Описание
(re) | (где *re - любое регулярное выражение) соответствует вхождению re, причем вхождение отмечено для возможного сообщения
(?: re) | то же, что и выше, но вхождение не отмечено для отчетности (набор «не захватывающих» скобок) (только для ARE)
. | соответствует любому отдельному символу
[ chars ] | выражение в квадратных скобках, соответствующее любому из chars (более подробную информацию см. в подразделе Выражения в квадратных скобках)
\k | (где k не буквенно-цифровой символ) соответствует этому символу, взятому как обычный символ, например, \\ соответствует символу обратного слэша
\c | где c — буквенно-цифровой символ (за которым, возможно, следуют другие символы), является управляющим символом, см. подраздел Управляющие символы в регулярных выражениях (только для ARE; в ERE и BRE это соответствует c)
{ | когда за ним следует символ, отличный от цифры, соответствует символу левой фигурной скобки {; после цифры следует начало границы (см. ниже)
x | где x — это один символ, не имеющий другого значения, соответствует этому символу

RE не может заканчиваться обратным слэшем (\).

Примечание
Если у вас выключен параметр standard_conforming_strings, любые обратные слеши, которые вы пишете в константах литеральных строк, нужно будет дублировать. Дополнительную информацию см. в подразделе Строковые константы.

Таблица 18. Квантификаторы регулярных выражений

Квантификатор | Совпадения
* | последовательность из 0 или более совпадений атома
+ | последовательность из 1 или более совпадений атома
? | последовательность из 0 или 1 совпадения атома
{m} | последовательность из ровно m совпадений атома
{m,} | последовательность из m или более совпадений атома
{m,n} | последовательность из от m до n (включительно) совпадений атома; m не может превышать n
*? | нежадная версия *
+? | нежадная версия +
?? | нежадная версия ?
{m}? | нежадная версия {m}
{m,}? | нежадная версия {m,}
{m,n}? | нежадная версия {m,n}

Формы, использующие {...}, называются границами. Числа m и n внутри границы являются десятичными целыми числами без знака с допустимыми значениями от 0 до 255 включительно.

Нежадные квантификаторы (доступные только в ARE) соответствуют тем же возможностям, что и их соответствующие нормальные (жадные) аналоги, но предпочитают наименьшее, а не наибольшее количество совпадений. Более подробную информацию см. в подразделе Правила сопоставления регулярных выражений.

Примечание
Квантификатор не может следовать сразу за другим квантификатором, например, запись ** недействительна. Квантификатор не может начинать выражение или подвыражение или следовать за символом ^ или |.

Таблица 19. Ограничения регулярного выражения

Ограничение | Описание
^ | соответствует началу строки
$ | соответствует концу строки
(?=re) | позитивный просмотр вперед соответствует любой точке, где начинается подстрока, соответствующая re (только для ARE)
(?!re) | негативный просмотр вперед соответствует любой точке, где не начинается подстрока, соответствующая re (только для ARE)
(?<=re) | позитивный просмотр назад соответствует любой точке, где заканчивается подстрока, соответствующая re (только для ARE)
(?<!re) | негативный просмотр назад соответствует любой точке, где не заканчивается подстрока, соответствующая re (только для ARE)

Ограничения просмотра вперед и назад не могут содержать обратных ссылок (см. подраздел Управляющие символы в регулярных выражениях), и все скобки в них считаются не захватывающими.

Выражения в квадратных скобках

Выражение в квадратных скобках — это список символов, заключенных в []. Обычно оно соответствует любому отдельному символу из этого списка (но см. ниже). Если список начинается с ^, оно соответствует любому отдельному символу, не относящемуся к остальной части этого списка. Если два символа в списке разделены знаком -, это сокращение для полного диапазона символов между этими двумя символами (включая их) в последовательности сортировки, например, запись [0-9] в ASCII соответствует любой десятичной цифре. Недопустимо, чтобы у двух диапазонов была общая граничная точка, например, a-c-e. Диапазоны очень сильно зависят от последовательности сортировки, поэтому переносимые программы не должны полагаться на них.

Чтобы включить в список литерал ], сделайте его первым символом (после ^, если он используется). Чтобы включить литерал -, сделайте его первым или последним символом или второй граничной точкой диапазона. Чтобы использовать литерал - в качестве первой граничной точки диапазона, заключите его в [. и .], чтобы сделать его упорядочивающим элементом (см. ниже). За исключением этих символов, некоторых комбинаций с использованием [ (см. следующие абзацы) и управляющих символов (только для ARE), все другие специальные символы внутри выражения в квадратных скобках теряют свое особое значение. В частности, символ \ является обычным, если следовать правилам ERE или BRE, хотя и является специальным (как обозначающий экранирование) в ARE.

Внутри выражения в квадратных скобках элемент сортировки (символ, многосимвольная последовательность, которая сортируется, как если бы это был один символ, или имя последовательности сортировки для любого из них), заключенный в [. и .], обозначает последовательность символов этого элемента сортировки. Последовательность рассматривается как отдельный элемент списка выражения в квадратных скобках. Это позволяет выражению в квадратных скобках, содержащему многосимвольный элемент сортировки, соответствовать более чем одному символу, например, если последовательность сортировки включает в себя элемент сортировки ch, то RE [[.ch.]]*c соответствует первым пяти символам строки chchcc.

Примечание
В настоящее время QHB не поддерживает многосимвольные элементы сортировки. Эта информация описывает возможное будущее поведение.

В выражении в квадратных скобках элемент сортировки, заключенный в [= и =], является классом эквивалентности, обозначающим последовательности символов всех элементов сортировки, эквивалентных этому элементу, включая его самого. (Если нет других эквивалентных элементов сортировки, он обрабатывается так, как если бы был заключен в разделители [. и .]). Например, если o и ^ являются членами класса эквивалентности, то [[=o=]], [[=^=]] и [o^] являются синонимами. Класс эквивалентности не может быть граничной точкой диапазона.

В выражении в квадратных скобках имя класса символов, заключенное в [: и :], обозначает список всех символов, принадлежащих этому классу. Класс символов не может использоваться в качестве граничной точки диапазона. Стандарт POSIX определяет следующие имена классов символов: alnum (буквы и цифры), alpha (буквы), blank (пробел и табуляция), cntrl (символы управления), digit (цифры), graph (печатные символы, кроме пробела), lower (строчные буквы), print (печатаемые символы, включая пробел), punct (пунктуация), space (любой пробел), upper (заглавные буквы) и xdigit (шестнадцатеричные цифры). Поведение этих стандартных классов символов обычно одинаково на разных платформах для символов в 7-битном наборе ASCII. То, считается ли данный символ, не относящийся к ASCII, принадлежащим одному из этих классов, зависит от правила сортировки, используемого для функции или оператора регулярного выражения (см. раздел Поддержка правил сортировки), или, по умолчанию, от настройки языка LC_CTYPE базы данных (см. раздел Поддержка языковых стандартов). Классификация символов, отличных от ASCII, может варьироваться в зависимости от платформы даже в локалях с одинаковыми именами. (Но локаль C никогда не считает, что любые не относящиеся к ASCII символы принадлежат какому-либо из этих классов). В дополнение к этим стандартным классам символов, QHB определяет класс символов word, который аналогичен alnum, но вдобавок включает символ подчеркивания (_), и класс символов ascii, который содержит только и исключительно 7-битный набор ASCII.

Существует два особых случая выражений в квадратных скобках: выражения [[:<:]] и [[:>:]] являются ограничениями, соответствующими пустым строкам в начале и конце слова соответственно. Слово определяется как последовательность словообразующих символов, которая не предшествует и не сопровождается символами слова. Словообразующий символ — это любой символ, принадлежащий классу символов word, то есть любая буква, цифра или знак подчеркивания. Это расширение совместимо с POSIX 1003.2, но не указано в нем, и его следует использовать с осторожностью в программном обеспечении, предназначенном для переноса на другие системы. Обычно лучше использовать описанные ниже символы ограничений; они тоже не вполне стандартны, но их легче набирать.

Управляющие символы в регулярных выражениях

Управляющие символы — это специальные последовательности, начинающиеся с \, за которым следует буквенно-цифровой символ. Управляющие символы могут быть нескольких видов: обозначения символов, коды классов, символы ограничений и обратные ссылки. Символ \, сопровождаемый буквенно-цифровым символом, но не образующий допустимый управляющий символ, запрещен в ARE. В ERE нет управляющих символов: вне выражения в квадратных скобках \ и последующий буквенно-цифровой символ просто обозначают этот символ как обычный, а внутри выражения в квадратных скобках и сам \ — обычный символ. (Последнее является единственной фактической несовместимостью между ERE и ARE).

Особые обозначения символов существуют, чтобы упростить указание непечатаемых и других неудобных символов в RE. Они приведены в Таблице 20.

Коды классов предоставляют сокращенные обозначения для некоторых часто используемых классов символов. Они приведены в Таблице 21.

Символы ограничений — это ограничения, которые при выполнении определенных условий соответствуют пустой строке и записываются в виде управляющих символов. Они приведены в Таблице 22.

Обратная ссылка (\n) соответствует той же строке, которой соответствовало предыдущее подвыражение в скобках, указанное числом n (см. Таблицу 22). Например, ([bc])\1 соответствует bb или cc, но не bc или cb. Подвыражение должно полностью предшествовать обратной ссылке в RE. Подвыражения нумеруются в порядке их открывающих скобок. Не захватывающие скобки не определяют подвыражения. Обратные ссылки учитывают только строковые символы, соответствующие указанному подвыражению, но не содержащиеся в нем ограничения. Например, выражению (^\d)\1 будет соответствовать 22.

Таблица 20. Особые обозначения символов в регулярных выражениях

Управляющий символ | Описание
\a | символ оповещения (звонка), как в C
\b | возврат на символ, как в C
\B | синоним обратного слэша (\) для сокращения необходимости его удвоения
\cX | (где X — любой символ) символ, чьи 5 младших битов такие же, как у X, а все остальные биты равны нулю
\e | символ с именем последовательности сортировки ESC или, если это не так, символ с восьмеричным значением 033
\f | подача формы, как в C
\n | перевод строки, как в C
\r | возврат каретки, как в C
\t | горизонтальная табуляция, как в C
\uwxyz | (где wxyz — ровно четыре шестнадцатеричные цифры) символ, шестнадцатеричное значение которого равно 0xwxyz
\Ustuvwxyz | (где stuvwxyz — ровно восемь шестнадцатеричных цифр) символ, шестнадцатеричное значение которого равно 0xstuvwxyz
\v | вертикальная табуляция, как в C
\xhhh | (где hhh — любая последовательность шестнадцатеричных цифр) символ, шестнадцатеричное значение которого равно 0xhhh (один символ независимо от того, сколько шестнадцатеричных цифр используется)
\0 | символ, значение которого равно 0 (нулевой байт)
\xy | (где xy — ровно две восьмеричные цифры, а не обратная ссылка) символ, восьмеричное значение которого равно 0xy
\xyz | (где xyz — ровно три восьмеричных цифры, а не обратная ссылка) символ, восьмеричное значение которого равно 0xyz

Шестнадцатеричные цифры — это 0-9, a-f и A-F. Восьмеричные цифры — это 0-7.

Цифровые особые обозначения символов, задающие значения вне диапазона ASCII (0-127), меняют семантику в зависимости от кодировки базы данных. При кодировке UTF-8 управляющие значения равнозначны кодовым точкам Unicode, например, \u1234 означает символ U+1234. Для других многобайтовых кодировок особые обозначения символов обычно просто задают конкатенацию байтовых значений для символа. Если управляющее значение не соответствует никакому допустимому символу в кодировке базы данных, ошибки не возникнет, но это значение никогда не будет соответствовать никаким данным.

Особые обозначения символов всегда принимаются как обычные символы. Например, \135 является символом ] в ASCII, но \135 не завершает выражение в квадратных скобках.

Таблица 21. Коды классов в регулярных выражениях

Управляющий символ | Описание
\d | соответствует любой цифре; равнозначно [[:digit:]]
\s | соответствует любому пробельному символу; равнозначно [[:space:]]
\w | соответствует любому словообразующему символу; равнозначно [[:word:]]
\D | соответствует любому нецифровому символу; равнозначно [^[:digit:]]
\S | соответствует любому непробельному символу; равнозначно [^[:space:]]
\W | соответствует любому несловообразующему символу; равнозначно [^[:word:]]

Коды классов также работают в выражениях в квадратных скобках, хотя показанные выше определения не вполне синтаксически допустимы в этом контексте. Например, выражение [a-c\d] равнозначно [a-c[:digit:]].

Таблица 22. Символы ограничений в регулярных выражениях

Управляющий символ | Описание
\A | соответствует только началу строки (чем это отличается от ^, см. в подразделе Правила сопоставления регулярных выражений)
\m | соответствует только началу слова
\M | соответствует только концу слова
\y | соответствует только началу или концу слова
\Y | соответствует только точке, которая не является началом или концом слова
\Z | соответствует только концу строки (чем это отличается от $, см. в подразделе Правила сопоставления регулярных выражений)

Слово определяется как в спецификации [[:<:]] и [[:>:]] выше. В выражениях в квадратных скобках символы ограничений недопустимы .

Таблица 23. Обратные ссылки в регулярных выражениях

Управляющий символ | Описание
\m | (где m — ненулевая цифра) обратная ссылка на m-е подвыражение
\mnn | (где m — ненулевая цифра, а nn — еще несколько цифр, и десятичное значение mnn не превышает число закрывающих захватывающих скобок, замеченных до сих пор) обратная ссылка на mnn-е подвыражение

Примечание
Между обозначениями восьмеричных символов и обратными ссылками имеется существенная неоднозначность, которая разрешается следующим эвристическим алгоритмом (как намекалось выше). Ведущий ноль всегда указывает на восьмеричное обозначение. Одна ненулевая цифра, за которой не следует другая цифра, всегда воспринимается как обратная ссылка. Последовательность из нескольких цифр, не начинающаяся с нуля, воспринимается как обратная ссылка, если она идет после подходящего подвыражения (т. е. число находится в допустимом диапазоне для обратной ссылки), а в противном случае воспринимается как восьмеричное обозначение.

Метасинтаксис регулярных выражений

В дополнение к основному синтаксису, описанному выше, существуют некоторые специальные формы и различные синтаксические средства.

RE может начинаться с одного из двух специальных префиксов-направителей. Если RE начинается с ***:, остальная его часть воспринимается как ARE. (Обычно в QHB это не имеет никакого значения, поскольку предполагается, что все RE являются ARE; но это имеет смысл, если параметром flags для функции regex был задан режим ERE или BRE). Если RE начинается с ***=, остальная его часть воспринимается как литеральная строка, причем все символы считаются обычными символами.

ARE может начинаться со встроенных параметров: последовательность (?xyz) (где xyz — один или несколько буквенных символов) задает параметры, влияющие на остальную часть RE. Эти параметры переопределяют любые ранее определенные параметры — в частности, они могут переопределять чувствительность к регистру, подразумеваемую оператором регулярного выражения, или параметр flags для функции регулярного выражения. Доступные буквы параметров приведены в Таблице 24. Обратите внимание, что эти же буквы параметров используются в параметрах flags функций регулярных выражений.

Таблица 24. Буквы встроенных параметров ARE

Параметр | Описание
b | остальная часть RE — BRE
c | сопоставление с учетом регистра (переопределяет тип оператора)
e | остальная часть RE — ERE
i | сопоставление без учета регистра (см. подраздел Правила сопоставления регулярных выражений) (переопределяет тип оператора)
m | исторический синоним для n
n | сопоставление с учетом перевода строк (см. подраздел Правила сопоставления регулярных выражений)
p | сопоставление с частичным учетом перевода строк (см. подраздел Правила сопоставления регулярных выражений)
q | остальная часть RE является литеральной строкой («в кавычках»), все символы обычные
s | сопоставление, не зависящее от перевода строки (по умолчанию)
t | жесткий синтаксис (по умолчанию; см. ниже)
w | обратное сопоставление с частичным учетом перевода строк («странное») (см. подраздел Правила сопоставления регулярных выражений)
x | расширенный синтаксис (см. ниже)

Встроенные параметры вступают в силу после символа ), завершающего последовательность. Они могут появляться только в начале ARE (после направителя ***: если таковой имеется).

В дополнение к обычному (строгому) синтаксису RE, в котором значимы все символы, существует расширенный синтаксис, доступный при указании встроенного параметра x. В расширенном синтаксисе символы пробела в RE игнорируются, как и все символы между знаком # и следующим переводом строки (или концом RE). Это позволяет создавать абзацы и комментировать сложные RE. Из этого основного правила есть три исключения:

пробельный символ или #, которому предшествует \, сохраняется

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

пробельный символ и комментарии не могут появляться в многосимвольных обозначениях, таких как (?:

В данном случае пробельными символами являются собственно пробелы, символы табуляции, перевода строки и любые символы, принадлежащие классу символов space.

Наконец, в ARE, вне выражений в квадратных скобках, последовательность (?#ttt) (где ttt - любой текст, не содержащий )) — это комментарий, который полностью игнорируется. Опять же, эта последовательность не допускается между символами многосимвольных обозначений, например (?:. Такие комментарии являются скорее историческим артефактом, чем полезным средством, и их использование не рекомендуется; вместо этого используйте расширенный синтаксис.

Ни одно из этих расширений метасинтаксиса не доступно, если начальным направителем ***= задано, что вводимые пользователем данные будут обрабатываться как литеральная строка, а не как RE.

Правила сопоставления регулярных выражений

В случае, когда RE может соответствовать более чем одной подстроке заданной строки, это RE соответствует той подстроке, которая начинается в ней первой. Если RE может соответствовать более чем одной подстроке, начинающейся в этой точке, то будет взято либо самое длинное, либо самое короткое вхождение из возможных, в зависимости от того, является ли RE жадным или нежадным.

Является ли RE жадным или нет, определяется следующими правилами:

Большинство атомов и все ограничения не имеют атрибута жадности (поскольку они в любом случае не могут соответствовать переменным объемам текста).

Добавление скобок вокруг RE не влияет на его жадность.

Кванторный атом с квантификатором фиксированного повторения ({m} или {m}?) имеет такую же жадность (возможно, ее отсутствие), как сам атом.

Кванторный атом с другими нормальными квантификаторами (включая {m,n} с m равным n) является жадным (предпочитает самое длинное вхождение).

Кванторный атом с нежадным квантификатором (включая {m,n}? с m равным n) не является жадным (предпочитает самое короткое вхождение).

Ветвь — то есть RE, у которого нет оператора | на верхнем уровне — имеет ту же жадность, что и первый кванторный атом в ней, имеющий атрибут жадности.

RE, состоящее из двух или более ветвей, соединенных оператором |, всегда жадное.

Приведенные выше правила связывают атрибуты жадности не только с отдельными кванторными атомами, но и с ветвями и целыми RE, содержащими кванторные атомы. Это означает, что сопоставление выполняется таким образом, что ветвь или целое RE совпадает с самой длинной или самой короткой из возможных подстрок в целом. Как только длина всего вхождения определена, его часть, соответствующая любому конкретному подвыражению, определяется на основе атрибута жадности этого подвыражения, причем подвыражения, начинающиеся в RE раньше, имеют приоритет над теми, что начинаются дальше.

Пример того, что это значит:

SELECT SUBSTRING('XY1234Z', 'Y*([0-9]{1,3})');
Result: 123
SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
Result: 1

В первом случае RE в целом жадное, потому что атом Y* жадный. Он может совпадать, начиная с Y, и соответствовать самой длинной из возможных строке, начинающейся там, то есть Y123. Выводимым результатом является ее часть в скобках, или 123. Во втором случае RE в целом нежадное, потому что атом Y*? нежадный. Он может совпадать, начиная с Y, и соответствовать самой короткой строке, начинающейся там, то есть Y1. Подвыражение [0-9]{1,3} является жадным, но не может изменить выбор относительно общей длины вхождения, поэтому ему приходится совпадать только с подстрокой 1.

Вкратце, когда RE содержит как жадные, так и нежадные подвыражения, все вхождение будет либо максимально длинным, либо максимально коротким, в соответствии с атрибутом, назначенным всему RE. Атрибуты, назначенные подвыражениям, влияют только на то, сколько из этого вхождения им разрешено «съесть» относительно друг друга.

Чтобы задать атрибут жадности или нежадности для подвыражения или целого RE, можно использовать квантификаторы {1,1} и {1,1}? соответственно. Это полезно, когда требуется, чтобы весь RE имел атрибут жадности, отличный от того, что выводится из его элементов. В качестве примера предположим, что мы пытаемся разделить строку, содержащую несколько цифр, на цифры и части до и после них. Мы можем попытаться сделать это так:

SELECT regexp_match('abc01234xyz', '(.*)(\d+)(.*)');
Result: {abc0123,4,xyz}

Это не сработало: первый атом .* жадный, поэтому он «съедает» столько, сколько может, оставляя \d+ совпадать с последним возможным местом, последней цифрой. Мы можем попытаться исправить это, сделав его нежадным:

SELECT regexp_match('abc01234xyz', '(.*?)(\d+)(.*)');
Result: {abc,0,""}

Это тоже не сработало, потому что теперь RE в целом нежадный, и поэтому он заканчивает все сравнение как можно скорее. Мы можем получить то, что хотим, заставив RE в целом быть жадным:

SELECT regexp_match('abc01234xyz', '(?:(.*?)(\d+)(.*)){1,1}');
Result: {abc,01234,xyz}

Контроль общей жадности RE отдельно от жадности его компонентов обеспечивает большую гибкость при работе с шаблонами переменной длины.

При принятии решения о том, какое вхождение длиннее или короче, длины вхождений измеряются в символах, а не в элементах сортировки. Пустая строка считается длиннее, чем отсутствие соответствия. Например: выражение bb* соответствует трем средним символам в строке abbbc; выражение (week|wee) (night|knights) — всем десяти символам в строке weeknights; когда выражение (.*).* сопоставляется со строкой abc, подвыражение в скобках соответствует всем трем символам; а когда выражение (a*)* сопоставляется со строкой bc, то и все RE, и подвыражение в скобках соответствуют пустой строке.

Если задано сопоставление без учета регистра, эффект будет таким же, как если бы из алфавита исчезли все различия строчных и прописных букв. Когда буквенный символ, существующий в нескольких регистрах, вне выражения в квадратных скобках находится как обычный символ, он по сути преобразуется в выражение в квадратных скобках, содержащее буквы в обоих регистрах, например, x становится [xX]. Когда же он находится внутри выражения в квадратных скобках, в это выражение добавляются все его варианты, например, [x] становится [xX], а [^x] становится [^xX].

Если задано сопоставление с учетом перевода строк, атом . и выражения в скобках, использующие ^, никогда не будут соответствовать символу перевода строки (так что вхождения никогда не будут заходить за границу строки, если RE явно не включит эти символы), а ^ и $ будут соответствовать не только началу и концу строки соответственно, но и пустой строке после и до символа перевода строки соответственно. Однако управляющие символы ARE \A и \Z продолжают соответствовать только началу или концу строки. Кроме того, коды классов символов \D и \W будут соответствовать переводу строки независимо от этого режима. (В старых версиях QHB они не соответствовали переводу строки в режиме с учетом перевода строк. Для включения старого поведения напишите [^[:digit:]] или [^[:word:]].)

Если указано сопоставление с частичным учетом перевода строк, то перевод строки влияет на атом . и выражения в скобках, но не на ^ и $.

Если указано обратное сопоставление с частичным учетом перевода строк, то перевод строки влияет на ^ и $, но не на атом . и выражения в скобках. Это не очень полезно, но предусмотрено для симметрии.

Пределы и совместимость

В этой реализации нет особых ограничений на длину RE. Однако программы, у которых предполагается высокая переносимость, не должны использовать RE длиной более 256 байт, поскольку реализация, совместимая с POSIX, может отказаться принимать такие RE.

Единственная особенность ARE, которая действительно несовместима с POSIX ERE, заключается в том, что \ не теряет своего особого значения в выражениях в квадратных скобках. Все остальные функциональные возможности ARE используют синтаксис, который является недопустимым или имеет эффекты, не определенные или не предусмотренные в POSIX ERE; синтаксис направителей *** также выходит за рамки синтаксиса POSIX как для BRE, так и для ERE.

Многие расширения ARE заимствованы из Perl, но некоторые были изменены в целях оптимизации, а некоторые расширения Perl отсутствуют. Заметные несовместимости включают в себя атомы \b, \B, отсутствие специальной обработки для завершающего перевода строки, добавление дополненных выражений в квадратных скобках в число случаев, на которые влияет сопоставление с учетом перевода строк, особые условия для круглых скобок и обратных ссылок в ограничениях просмотра вперед/назад, а также семантику «сопоставление самого длинного/самого короткого вхождения» (а не «первого вхождения»).

Базовые регулярные выражения

BRE отличаются от ERE в нескольких отношениях. В BRE |, + и ? являются обычными символами, и их функциональность не имеет аналогов. Разделителями для границ являются \{ и \}, причем сами по себе { и } — обычные символы. Скобками для вложенных подвыражений являются \( и \), а сами по себе ( и ) — обычные символы. ^ — обычный символ, если не стоит в начале RE или в начале подвыражения в скобках, $ — обычный символ, если не стоит в конце RE или в конце подвыражения в скобках, а * — обычный символ, если находится в начале RE или в начале подвыражения в скобках (возможно, после начального ^). Наконец, в BRE доступны обратные ссылки с одной цифрой, а \< и \> являются синонимами для [[:<:]] и [[:>:]] соответственно; в BRE нет других управляющих символов.

Отличия от XQuery (LIKE_REGEX)

Начиная с SQL:2008, стандарт SQL включает в себя оператор LIKE_REGEX, выполняющий сопоставление с шаблоном в соответствии со стандартом регулярных выражений XQuery. QHB еще не реализует этот оператор, но очень похожее поведение можно получить с помощью функции regexp_match(), поскольку регулярные выражения XQuery довольно близки к описанному выше синтаксису ARE.

Имеются заметные различия между существующей функцией регулярных выражений на основе POSIX и регулярными выражениями XQuery, в том числе:

Вычитание классов символов XQuery не поддерживается. Примером этой особенности является использование для сопоставление только с английскими согласными следующего выражения: [a-z-[aeiou]].

Коды классов символов XQuery \c, \C, \i и \I не поддерживаются.

Элементы классов символов XQuery, использующие \p{UnicodeProperty} или обратное \P{UnicodeProperty}, не поддерживаются.

В POSIX классы символов, такие как \w (см. Таблицу 21), интерпретируются в соответствии с преобладающей локалью (которой можно управлять, добавляя предложение COLLATE к оператору или функции). В XQuery эти классы определяются с помощью ссылок на свойства символов Unicode, поэтому равнозначное поведение возможно только с локалью, подчиняющейся правилам Unicode.

Стандарт SQL (не сам XQuery) пытается учесть больше вариантов «перевода строки», чем POSIX. Описанные выше варианты сопоставления с учетом перевода строк считают символом перевода строки только ASCII NL (\n), тогда как, следуя стандарту SQL, пришлось бы считать символами перевода строки также CR (\r), CRLF (\r\n) (перевод строки в стиле Windows) и некоторые символы, характерные только для Unicode, например LINE SEPARATOR (U+2028). Примечательно, что, согласно SQL, атомы . и \s должны считать последовательность \r\n одним символом, а не двумя.

Из особых обозначений символов, описанных в Таблице 20, XQuery поддерживает только \n, \r и \t.

XQuery не поддерживает синтаксис [:имя:] для классов символов в выражениях в квадратных скобках.

В XQuery нет условий для просмотра вперед или назад, а также ограничивающих символов, описанных в таблице 22.

Метасинтаксические формы, описанные в подразделе Метасинтаксис регулярных выражений, в XQuery не существуют.

Буквы флагов регулярного выражения, определенные в XQuery, имеют нечто общее с буквами параметров для POSIX (Таблица 24), но отличаются них. Только параметры i и q одинаковы, остальные ведут себя совершенно по-другому:

Флаги XQuery s (разрешить соответствие точки переводу строки) и m (разрешить соответствие ^ и $ переводам строк) позволяют получить то же поведение, что и флаги POSIX n, p и w, но они не соответствуют поведению флагов POSIX s и m. В частности, обратите внимание, что по умолчанию точка соответствует переводу строки в POSIX, но не в XQuery.

Флаг XQuery x (игнорировать пробельные символы в шаблоне) заметно отличается от флага расширенного режима POSIX. В POSIX флаг x также допускает символ #, с которого начинается комментарий в шаблоне, и пробельный символ после обратного слэша в POSIX не игнорируется.

Функции форматирования типов данных

Функции форматирования QHB предоставляют мощный набор инструментов для преобразования различных типов данных (дата/время, целые числа, числа с плавающей запятой, числовые) в форматированные строки и форматированных строк в конкретные типы данных. Все они перечислены в Таблице 25. Эти функции следуют общему соглашению о вызовах: первый аргумент — это значение, которое нужно отформатировать, а второй аргумент — это шаблон, который определяет формат вывода или ввода.

Таблица 25. Функции форматирования

Функция

Описание

Пример(ы)

to_char ( timestamp, text ) → text

to_char ( timestamp with time zone, text ) → text

Преобразует метку времени в строку согласно заданному формату.

to_char(timestamp '2002-04-20 17:31:12.66', 'HH12:MI:SS') → 05:31:12

to_char ( interval, text ) → text

Преобразует интервал в строку согласно заданному формату.

to_char(interval '15h 2m 12s', 'HH24:MI:SS') → 15:02:12

to_char ( числовой_тип, text ) → text

Преобразует число в строку согласно заданному формату; доступно для типов integer, bigint, numeric, real, double precision.

to_char(125, '999') → 125

to_char(125.8::real, '999D9') → 125.8

to_char(-125.8, '999D99S') → 125.80-

to_date ( text, text ) → date

Преобразует строку в дату согласно заданному формату.

to_date('05 Dec 2000', 'DD Mon YYYY') → 2000-12-05

to_number ( text, text ) → numeric

Преобразует строку в число согласно заданному формату.

to_number('12,454.8-', '99G999D9S') → -12454.8

to_timestamp ( text, text ) → timestamp with time zone

Преобразует строку в метку времени согласно заданному формату. (См. также to_timestamp(double precision) в Таблице 32.)

to_timestamp('05 Dec 2000', 'DD Mon YYYY') → 2000-12-05 00:00:00-05

Совет
Функции to_timestamp и to_date существуют для обработки входных форматов, которые нельзя преобразовать простым приведением. Для большинства стандартных форматов даты/времени работает простое приведение исходной строки к требуемому типу данных, и это намного проще. Аналогично функция to_number не нужна для стандартных представлений чисел.

В строке шаблона вывода to_char есть определенные коды, которые распознаются и заменяются данными соответствующего формата на основе заданного значения. Любой текст, который не является кодом шаблона, просто дословно копируется. Точно так же в строке шаблона ввода (для других функций) эти коды идентифицируют значения, которые будут предоставлены строкой входных данных. Если в строке шаблона есть символы, не относящиеся к кодам, соответствующие символы во входной строке данных просто пропускаются (независимо от того, совпадают ли они с символам в строке шаблона).

В Таблице 26 показаны коды шаблонов, доступные для форматирования значений даты и времени.

Таблица 26. Кода шаблонов для форматирования даты/времени

Код | Описание
HH | час дня (01-12)
HH12 | час дня (01-12)
HH24 | час дня (00-23)
MI | минута (00-59)
SS | секунда (00-59)
MS | миллисекунда (000-999)
US | микросекунда (000000-999999)
FF1 | десятая доля секунды (0-9)
FF2 | сотая доля секунды (00-99)
FF3 | миллисекунда (000-999)
FF4 | десятая доля миллисекунды (0000-9999)
FF5 | сотая доля миллисекунды (00000-99999)
FF6 | микросекунда (000000-999999)
SSSS, SSSSS | секунды после полуночи (0-86399)
AM, am, PM или pm | индикатор времени до/после полудня (без точек)
A.M., a.m., P.M. или p.m. | индикатор времени до/после полудня (с точками)
Y,YYY | год (4 или более цифр) с запятой
YYYY | год (4 или более цифр)
YYY | последние 3 цифры года
YY | последние 2 цифры года
Y | последняя цифра года
IYYY | недельный год по ISO 8601 (4 или более цифр)
IYY | последние 3 цифры недельного года по ISO 8601
IY | последние 2 цифры недельного года по ISO 8601
I | последняя цифра недельного года по ISO 8601
BC, bc, AD или ad | индикатор эры (без точек)
B.C., b.c., A.D. или a.d. | индикатор эры (с точками)
MONTH | полное название месяца в верхнем регистре (дополненное пробелами до 9 символов)
Month | полное название месяца с заглавной буквы (дополненное пробелами до 9 символов)
month | полное название месяца в нижнем регистре (дополненное пробелами до 9 символов)
MON | сокращенное название месяца в верхнем регистре (3 буквы в английском языке; в других языках длина может отличаться)
Mon | сокращенное название месяца с заглавной буквы (3 буквы в английском языке; в других языках длина может отличаться)
mon | сокращенное название месяца в нижнем регистре (3 буквы в английском языке; в других языках длина может отличаться)
MM | номер месяца (01-12)
DAY | полное название дня недели в верхнем регистре (дополненное пробелами до 9 символов)
Day | полное название дня недели с заглавной буквы (дополненное пробелами до 9 символов)
day | полное название дня недели в нижнем регистре (дополненное пробелами до 9 символов)
DY | сокращенное название дня недели в верхнем регистре (3 буквы в английском языке; в других языках длина может отличаться)
Dy | сокращенное название дня недели с заглавной буквы (3 буквы в английском языке; в других языках длина может отличаться)
dy | сокращенное название дня недели в нижнем регистре (3 буквы в английском языке; в других языках длина может отличаться)
DDD | номер дня в году (001-366)
IDDD | номер дня в недельном году по ISO 8601 (001-371; 1-й день года — понедельник первой недели по ISO)
DD | день месяца (01-31)
D | номер дня недели, с воскресенья (1) по субботу (7)
ID | номер дня недели по ISO 8601, с понедельника (1) по воскресенье (7)
W | номер недели в месяце (1-5) (первая неделя начинается в первый день месяца)
WW | номер недели в году (1-53) (первая неделя начинается в первый день года)
IW | номер недели в недельном году по ISO 8601 (01-53; первый четверг года относится к неделе 1)
CC | век (2 цифры) (двадцать первый век начинается с 2001-01-01)
J | юлианская дата (целое число дней с 24 ноября 4714 г. до н.э. в полночь по местному времени; см. раздел Юлианские даты)
Q | квартал
RM | месяц в верхнем регистре римскими цифрами (I-XII; I=январь)
rm | месяц в нижнем регистре римскими цифрами (i-xii; i=январь)
TZ | сокращенное наименование часового пояса в верхнем регистре (поддерживается только в to_char)
tz | сокращенное наименование часового пояса в нижнем регистре (поддерживается только в to_char)
TZH | часы часового пояса
TZM | минуты часового пояса
OF | смещение часового пояса от UTC (поддерживается только в to_char)

Модификаторы, изменяющие поведение, можно применять к любому коду шаблона. Например, FMMonth — это код Month с модификатором FM. В Таблице 27 приведены коды модификаторов для форматирования даты/времени.

Таблица 27. Модификаторы кодов шаблонов для форматирования даты/времени

Модификатор | Описание | Пример
Префикс FM | режим заполнения (подавление начальных нулей и заполняющих пробелов) | FMMonth
Суффикс TH | суффикс порядкового номера в верхнем регистре | DDTH, например 12TH
Суффикс th | суффикс порядкового номера в нижнем регистре | DDth, например 12th
Префикс FX | глобальный параметр фиксированного формата (см. примечания по использованию) | FX Month DD Day
Префикс TM | режим перевода (использовать локализованные названия дней и месяцев на основе lc_time) | TMMonth
Суффикс SP | режим числа прописью (не реализован) | DDSP

Замечания по использованию форматирования даты/времени:

FM подавляет начальные нули и конечные пробелы, которые в противном случае были бы добавлены, чтобы результат кода имел фиксированную ширину. В QHB FM изменяет только следующую спецификацию, тогда как в Oracle этот префикс влияет на все последующие спецификации, а повторные применения данного модификатора включают и выключают режим заполнения.

TM подавляет конечные пробелы независимо от указания FM.

Функции to_timestamp и to_date игнорируют регистр букв во входных данных; поэтому, например, для кодов MON, Mon и mon подойдут одни и те же строки. При использовании модификатора TM смена регистра производится согласно правилам сортировки, заданным в функции для входных данных (см. раздел Поддержка правил сортировки).

Функции to_timestamp и to_date пропускают несколько пробелов в начале вводимой строки и вокруг значений даты и времени, если только не используется префикс FX. Например, to_timestamp(’ 2000 JUN’, ’YYYY MON’) и to_timestamp(’2000 - JUN’, ’YYYY-MON’) работают, но to_timestamp('2000 JUN', 'FXYYYY MON') возвращает ошибку, поскольку to_timestamp ожидает только один пробел. FX должен быть указан первым элементом шаблона.

Разделитель (пробел или символ, отличный от буквы/цифры) в строке шаблона функций to_timestamp и to_date соответствует любому отдельному разделителю в строке ввода или пропускается, если только не используется префикс FX. Например, to_timestamp('2000JUN', 'YYYY///MON') и to_timestamp('2000/JUN', 'YYYY MON') работают, но to_timestamp('2000//JUN', 'YYYY/MON') возвращает ошибку, поскольку количество разделителей во входной строке превышает количество разделителей в шаблоне.

Если указан префикс FX, то разделитель в строке шаблона соответствует ровно одному символу во входной строке. Но обратите внимание, что символ во входной строке не обязательно должен совпадать с разделителем из строки шаблона. Например, to_timestamp('2000/JUN', 'FXYYYY MON') работает, но to_timestamp('2000/JUN', 'FXYYYY MON') возвращает ошибку, поскольку второй пробел в строке шаблона занимает место буквы J из строки ввода.

Коду шаблона TZH может соответствовать номер со знаком. Без префикса FX знаки минус могут быть неоднозначными и интерпретироваться как разделитель. Эта неоднозначность разрешается следующим образом: если число разделителей перед TZH в строке шаблона меньше, чем число разделителей перед знаком минус во входной строке, знак минус интерпретируется как часть TZH. В противном случае знак минус считается разделителем между значениями. Например, в to_timestamp('2000 -10', 'YYYY TZH') полю TZH соответствует -10, но в to_timestamp('2000 -10', 'YYYY TZH') полю TZH соответствует 10.

В шаблонах to_char разрешен обычный текст, который будет выводиться как есть. Чтобы строка заведомо интерпретировалась как буквальный текст, даже если она содержит коды шаблона, ее можно заключить в кавычки. Например, в строке '"Hello Year "YYYY' YYYY будет заменен цифрами года, а одиночная Y в слове Year — нет. В функциях to_date, to_number и to_timestamp при обработке буквального текста и строк в кавычках в результате будет пропущено то количество символов, которое содержится в этой строке; например, в строке "XX" будет пропущено два входных символа (независимо от того, являются ли они XX).

Совет
Чтобы пропустить во входной строке произвольный текст, можно использовать в шаблоне буквы. Например, шаблоны to_timestamp('2000y6m1d', 'yyyytMMtDDt') и to_timestamp('2000y6m1d', 'yyyy"y"MM"m"DD"d"') пропускают буквы y, m и d.

Если вы хотите получить в выводе кавычку, следует поставить перед ней обратный слэш, например '\"YYYY Month\"'. В остальном символ обратного слэша вне кавычек не имеет специального значения. Внутри строки в кавычках следующий за обратным слэшем символ воспринимается буквально, каким бы он ни был (но это не имеет особого смысла, если следующий символ не является кавычкой или обратным слэшем).

Если в функциях to_timestamp и to_date формат года задан менее чем четырьмя цифрами, например YYY и в предоставленном значении года тоже меньше четырех цифр, год будет скорректирован так, чтобы быть ближайшим к 2020 году, например, 95 станет 1995 годом.

В функциях to_timestamp and to_date отрицательные значения годов обрабатываются как обозначающие годы до н. э. Если одновременно написать отрицательный год и явно указать код BC (индикатор годов до н. э.), год будет относиться к н. э. Введение нулевого года воспринимается как 1 год до н. э.

В функциях to_timestamp и to_date преобразование YYYY имеет ограничение, когда обрабатывается год, состоящий из более чем 4 цифр. Следует написать после YYYY какой-либо нецифровой символ или подходящий код, иначе год всегда будет интерпретироваться как 4 цифры. Например, to_date('200001131', 'YYYYMMDD') (с годом 20000) будет интерпретироваться как год с 4 цифрами; вместо этого после года нужно использовать нецифровой разделитель, например to_date('20000-1131', 'YYYY-MMDD') или to_date('20000Nov31', 'YYYYMonDD').

В функциях to_timestamp и to_date поле CC (столетие) принимается, но игнорируется, если есть поле YYY, YYYY или Y,YYY. Если CC используется с YY или Y, то результат вычисляется как этот год в указанном столетии. Если указано столетие, а год не указан, то подразумевается первый год этого столетия.

В функциях to_timestamp и to_date названия или номера дней недели (DAY, D и связанные типы полей) принимаются, но игнорируются в контексте вычисления результата. То же самое верно для полей квартала (Q).

В функциях to_timestamp и to_date недельную дату по ISO 8601 (в отличие от григорианской даты) можно указать одним из двух способов:

Год, номер недели и день недели: например, to_date('2006-42-4', 'IYYY-IW-ID') возвращает дату 2006-10-19. Если опустить день недели, он считается равным 1 (понедельник).

Год и день года: например, to_date(’2006-291’, ’IYYY-IDDD’) также возвращает 2006-10-19.

Попытка ввести дату, используя сочетание полей недельного календаря по ISO 8601 и полей григорианской даты, не имеет смысла и приведет к ошибке. В контексте недельного года по ISO 8601 понятие «месяц» или «день месяца» не имеет смысла. В контексте григорианского года не имеет смысла неделя по ISO.

Внимание!
В то время как to_date будет отклонять смесь полей григорианской даты и недельного календаря по ISO, to_char примет их, поскольку спецификации выходного формата наподобие YYYY-MM-DD (IYYY-IDDD) могут быть полезны. Но избегайте введения чего-то вроде IYYY-MM-DD; в начале года это может привести к неожиданным результатам. (Дополнительную информацию см. в подразделе EXTRACT, date_part.)

В функции to_timestamp миллисекунды (MS) или микросекунды (US) используются как дробные части количества секунд после запятой. Например, to_timestamp('12.3', 'SS.MS') представляет собой не 3 миллисекунды, а 300, потому что преобразование обрабатывает его как 12 + 0,3 секунды. Таким образом, для формата SS.MS входные значения 12.3, 12.30 и 12.300 задают одинаковое количество миллисекунд. Чтобы получить три миллисекунды, нужно записать 12.003, что при преобразовании воспринимается как 12 + 0,003 = 12,003 секунды.

Вот более сложный пример: to_timestamp('15:12:02.020.001230', 'HH24:MI:SS.MS.US') составляет 15 часов, 12 минут и 2 секунды + 20 миллисекунд + 1230 микросекунд = 2,021230 секунд.

Нумерация дня недели в функции to_char(..., ’ID’) соответствует функции extract(isodow from ...), но в функции to_char(..., ’D’) эта нумерация не соответствует таковой в функции extract(dow from ...).

Функция to_char(interval) форматирует HH и HH12 в рамках 12 часов, например, ноль и 36 часов выводятся как 12, в то время как HH24 выводит значение часа полностью, так что в значении interval результат может быть выше 23.

В Таблице 28 показаны коды шаблонов, доступные для форматирования числовых значений.

Таблица 28. Коды шаблонов для форматирования чисел

Код | Описание
9 | позиция цифры (может быть опущена, если цифра незначащая)
0 | позиция цифры (не будет опущена, даже если цифра незначащая)
. (точка) | десятичная точка
, (запятая) | разделитель групп (тысяч)
PR | отрицательное значение в угловых скобках
S | знак привязанный к числу (использует локаль)
L | символ валюты (использует локаль)
D | десятичная точка (использует локаль)
G | разделитель группы (использует локаль)
MI | знак минус в заданной позиции (если число < 0)
PL | знак плюс в заданной позиции (если число > 0)
SG | знак плюс/минус в заданной позиции
RN | римская цифра (вводимое значение от 1 до 3999)
TH или th | суффикс порядкового номера
V | сдвиг на заданное количество цифр (см. примечания)
EEEE | экспонента для экспоненциальной формы записи

Замечания по применению числового форматирования:

0 указывает позицию цифры, которая будет всегда выводиться, даже если она содержит начальный/конечный ноль. 9 также указывает позицию цифры, но если это начальный ноль, то он будет заменен пробелом, а если это конечный ноль и включен режим заполнения, то он будет удален. (Для функции to_number() эти два кода шаблона равнозначны.)

Кодовые символы S, L, D и G представляют знак, символ валюты, десятичную точку и разделитель тысяч, определенные текущей локалью (см. lc_monetary и lc_numeric). Кодовые символы точка и запятая представляют те же самые символы со значениями десятичной точки и разделителя тысяч, но не зависят от локали.

Если в функции to_char() не предусмотрено явного положения для знака, то для него будет зарезервирован один столбец, и этот знак будет привязан к числу (будет находиться слева от него). Если слева от некоторых 9 находится S, знак тоже будет привязан к числу.

Знак, отформатированный с помощью кодов SG, PL или MI, не привязан к числу; например, to_char(-12, 'MI9999') выдает '- 12', но to_char(-12, 'S9999') выдает ' -12'. (Реализация Oracle не позволяет ставить MI перед 9, а наоборот, требует, чтобы 9 шел перед MI.)

TH не преобразует значения меньше нуля и дробные числа.

PL, SG и TH являются расширениями QHB.

Если в функции to_number используются коды шаблонов, не относящиеся к данным, например L или TH, пропускается соответствующее количество входных символов, вне зависимости от того, совпадают ли они с кодом, если только это не символы данных (то есть цифры, знак, десятичная точка или запятая). Например, при указании TH будет пропущено два символа, не обозначающие данные.

Код V в функции to_char умножает входные значения на 10^n, где n — количество цифр после V. В функции to_number код V аналогичным образом делит значения. Функции to_char и to_number не поддерживают использование V в сочетании с десятичной точкой (например, 99.9V99 не допускается).

Код EEEE (экспоненциальная запись) нельзя сочетать с другими кодами форматирования или модификаторами, за исключением кодов цифр и десятичных точек, и он должен находиться в конце строки формата (например, 9.99EEEE — допустимый код).

Некоторые модификаторы, изменяющие поведение, можно применять к любому коду шаблона. Например, FM99.99 — это код 99.99 с модификатором FM. В Таблице 29 приведены коды модификаторов для форматирования чисел.

Таблица 29. Модификаторы кодов шаблонов для форматирования чисел

Модификатор | Описание | Пример
Префикс FM | режим заполнения (подавление концевых нулей и заполняющих пробелов) | FM99.99
Суффикс TH | суффикс порядкового номера в верхнем регистре | 999TH
Суффикс th | суффикс порядкового номера в нижнем регистре | 999th

В Таблице 30 приведены некоторые примеры использования функции to_char.

Таблица 30. Примеры to_char

Выражение | Результат
to_char(current_timestamp, 'Day, DD HH12:MI:SS') | 'Tuesday , 06 05:39:18'
to_char(current_timestamp, 'FMDay, FMDD HH12:MI:SS') | 'Tuesday, 6 05:39:18'
to_char(-0.1, '99.99') | ' -.10'
to_char(-0.1, 'FM9.99') | '-.1'
to_char(-0.1, 'FM90.99') | '-0.1'
to_char(0.1, '0.9') | ' 0.1'
to_char(12, '9990999.9') | ' 0012.0'
to_char(12, 'FM9990999.9') | '0012.'
to_char(485, '999') | ' 485'
to_char(-485, '999') | '-485'
to_char(485, '9 9 9') | ' 4 8 5'
to_char(1485, '9,999') | ' 1,485'
to_char(1485, '9G999') | ' 1 485'
to_char(148.5, '999.999') | ' 148.500'
to_char(148.5, 'FM999.999') | '148.5'
to_char(148.5, 'FM999.990') | '148.500'
to_char(148.5, '999D999') | ' 148,500'
to_char(3148.5, '9G999D999') | ' 3 148,500'
to_char(-485, '999S') | '485-'
to_char(-485, '999MI') | '485-'
to_char(485, '999MI') | '485 '
to_char(485, 'FM999MI') | '485'
to_char(485, 'PL999') | '+485'
to_char(485, 'SG999') | '+485'
to_char(-485, 'SG999') | '-485'
to_char(-485, '9SG99') | '4-85'
to_char(-485, '999PR') | '<485>'
to_char(485, 'L999') | 'DM 485'
to_char(485, 'RN') | ' CDLXXXV'
to_char(485, 'FMRN') | 'CDLXXXV'
to_char(5.2, 'FMRN') | 'V'
to_char(482, '999th') | ' 482nd'
to_char(485, '"Good number:"999') | 'Good number: 485'
to_char(485.8, '"Pre:"999" Post:" .999') | 'Pre: 485 Post: .800'
to_char(12, '99V999') | ' 12000'
to_char(12.4, '99V999') | ' 12400'
to_char(12.45, '99V9') | ' 125'
to_char(0.0004859, '9.99EEEE') | ' 4.86e-04'

Функции и операторы даты/времени

В Таблице 32 приведены доступные функции для обработки значений даты/времени; подробные описания представлены в следующих подразделах. Таблица 31 иллюстрирует поведение основных арифметических операторов (+, * и т. д.). Функции форматирования описаны в разделе Функции форматирования типов данных. С основной информацией о типах данных даты/времени можно ознакомиться в разделе Типы даты/времени.

Кроме того, для типов даты/времени доступны обычные операторы сравнения, перечисленные в Таблице 1. Даты и метки времени (с часовым поясом или без него) можно сравнивать как угодно, тогда как время (с часовым поясом или без него) и интервалы можно сравнивать только со значениями того же типа. При сравнении метки времени без часового пояса и метки времени с часовым поясом предполагается, что первое значение задано в часовом поясе, установленном параметром конфигурации TimeZone, и оно меняется на UTC для сравнения со вторым значением (которое внутри уже представлено в UTC). Аналогично при сравнении даты с меткой времени предполагается, что значение даты представляет полночь в часовом поясе TimeZone.

Все описанные ниже функции и операторы, принимающие входные данные time или timestamp, фактически представлены в двух вариантах: один принимает time with time zone или timestamp with time zone, а второй — time without time zone или timestamp without time zone. Для краткости эти варианты показаны вместе. Кроме того, операторы + и * представлены в коммутационных (переместительных) парах (например, и date + integer, и integer + date); мы приводим только один вариант из каждой такой пары.

Таблица 31. Операторы даты/времени

Оператор

Описание

Пример(ы)

date + integer → date

Добавляет к дате указанное число дней

date '2001-09-28' + 7 → 2001-10-05

date + interval → timestamp

Добавляет к дате интервал

date '2001-09-28' + interval '1 hour' → 2001-09-28 01:00:00

date + time → timestamp

Добавляет к дате время

date '2001-09-28' + time '03:00' → 2001-09-28 03:00:00

interval + interval → interval

Складывает интервалы

interval '1 day' + interval '1 hour' → 1 day 01:00:00

timestamp + interval → timestamp

Добавляет к временной метке интервал

timestamp '2001-09-28 01:00' + interval '23 hours' → 2001-09-29 00:00:00

time + interval → time

Добавляет к времени интервал

time '01:00' + interval '3 hours' → 04:00:00

- interval → interval

Инвертирует интервал

- interval '23 hours' → -23:00:00

date - date → integer

Вычитает из одной даты другую, выдавая число прошедших дней

date '2001-10-01' - date '2001-09-28' → 3

date - integer → date

Вычитает из даты указанное число дней

date '2001-10-01' - 7 → 2001-09-24

date - interval → timestamp

Вычитает из даты интервал

date '2001-09-28' - interval '1 hour' → 2001-09-27 23:00:00

time - time → interval

Вычитает из одного времени другое

time '05:00' - time '03:00' → 02:00:00

time - interval → time

Вычитает из времени интервал

time '05:00' - interval '2 hours' → 03:00:00

timestamp - interval → timestamp

Вычитает из временной метки интервал

timestamp '2001-09-28 23:00' - interval '23 hours' → 2001-09-28 00:00:00

interval - interval → interval

Вычитает из одного интервала другой

interval '1 day' - interval '1 hour' → 1 day -01:00:00

timestamp - timestamp → interval

Вычитает из одной временной метки другую (преобразуя 24-часовые интервалы в дни подобно justify_hours())

timestamp '2001-09-29 03:00' - timestamp '2001-07-27 12:00' → 63 days 15:00:00

interval * double precision → interval

Умножает интервал на скалярное значение

interval '1 second' * 900 → 00:15:00

interval '1 day' * 21 → 21 days

interval '1 hour' * 3.5 → 03:30:00

interval / double precision → interval

Делит интервал на скалярное значение

interval '1 hour' / 1.5 → 00:40:00

Таблица 32. Функции даты/времени

Функция

Описание

Пример(ы)

age ( timestamp, timestamp ) → interval

Вычитает аргументы, выдавая «символический» результат, который использует годы и месяцы, а не просто дни

age(timestamp '2001-04-10', timestamp '1957-06-13') → 43 years 9 mons 27 days

age ( timestamp ) → interval

Вычитает аргумент из current_date (в полночь)

age(timestamp '1957-06-13') → 62 years 6 mons 10 days

clock_timestamp ( ) → timestamp with time zone

Текущая дата и время (меняется во время выполнения операторов); см. подраздел Текущая дата/время

clock_timestamp() → 2019-12-23 14:39:53.662522-05

current_date → date

Текущая дата; см. подраздел Текущая дата/время

current_date → 2019-12-23

current_time → time with time zone

Текущее время суток; см. подраздел Текущая дата/время

current_time → 14:39:53.662522-05

current_time ( integer ) → time with time zone

Текущее время суток, с ограниченной точностью; см. подраздел Текущая дата/время

current_time(2) → 14:39:53.66-05

current_timestamp → timestamp with time zone

Текущая дата и время (начало текущей транзакции); см. подраздел Текущая дата/время

current_timestamp → 2019-12-23 14:39:53.662522-05

current_timestamp ( integer ) → timestamp with time zone

Текущая дата и время (начало текущей транзакции), с ограниченной точностью; см. подраздел Текущая дата/время

current_timestamp(0) → 2019-12-23 14:39:53-05

date_bin ( interval, timestamp, timestamp ) → timestamp

Укладывает входное значение в заданный интервал, установленный с указанным начальным моментом; см. подраздел date_bin

date_bin('15 minutes', timestamp '2001-02-16 20:38:40', timestamp '2001-02-16 20:05:00') → 2001-02-16 20:35:00

date_part ( text, timestamp ) → double precision

Выдает подполе временной метки (равнозначно extract); см. подраздел EXTRACT, date_part

date_part('hour', timestamp '2001-02-16 20:38:40') → 20

date_part ( text, interval ) → double precision

Выдает подполе интервала (равнозначно extract); см. подраздел EXTRACT, date_part

date_part('month', interval '2 years 3 months') → 3

date_trunc ( text, timestamp ) → timestamp

Усекает временную метку до указанной точности; см. подраздел date_trunc

date_trunc('hour', timestamp '2001-02-16 20:38:40') → 2001-02-16 20:00:00

date_trunc ( text, timestamp with time zone, text ) → timestamp with time zone

Усекает временную метку до указанной точности в заданном часовом поясе; см. подраздел date_trunc

date_trunc('day', timestamptz '2001-02-16 20:38:40+00', 'Australia/Sydney') → 2001-02-16 13:00:00+00

date_trunc ( text, interval ) → interval

Усекает интервал до указанной точности; см. подраздел date_trunc

date_trunc('hour', interval '2 days 3 hours 40 minutes') → 2 days 03:00:00

extract ( поле from timestamp ) → numeric

Выдает подполе временной метки; см. подраздел EXTRACT, date_part

extract(hour from timestamp '2001-02-16 20:38:40') → 20

extract ( поле from interval ) → numeric

Выдает подполе интервала; см. подраздел EXTRACT, date_part

extract(month from interval '2 years 3 months') → 3

isfinite ( date ) → boolean

Проверяет дату на конечность (не +/- бесконечность)

isfinite(date '2001-02-16') → true

isfinite ( timestamp ) → boolean

Проверяет временную метку на конечность (не +/- бесконечность)

isfinite(timestamp 'infinity') → false

isfinite ( interval ) → boolean

Проверяет интервал на конечность (в настоящее время всегда true)

isfinite(interval '4 hours') → true

justify_days ( interval ) → interval

Настраивает интервал так, чтобы 30-дневные периоды времени представлялись как месяцы

justify_days(interval '35 days') → 1 mon 5 days

justify_hours ( interval ) → interval

Настраивает интервал так, чтобы 24-часовые периоды времени представлялись как дни

justify_hours(interval '27 hours') → 1 day 03:00:00

justify_interval ( interval ) → interval

Настраивает интервал, используя justify_days и justify_hours, с дополнительной коррекцией знака

justify_interval(interval '1 mon -1 hour') → 29 days 23:00:00

localtime → time

Текущее время суток; см. подраздел Текущая дата/время

localtime → 14:39:53.662522

localtime ( integer ) → time

Текущее время суток, с ограниченной точностью; см. подраздел Текущая дата/время

localtime(0) → 14:39:53

localtimestamp → timestamp

Текущая дата и время (начало текущей транзакции); см. подраздел Текущая дата/время

localtimestamp → 2019-12-23 14:39:53.662522

localtimestamp ( integer ) → timestamp

Текущая дата и время (начало текущей транзакции), с ограниченной точностью; см. подраздел Текущая дата/время

localtimestamp(2) → 2019-12-23 14:39:53.66

make_date ( год int, месяц int, день int ) → date

Создает дату из полей года, месяца и дня (отрицательное значение года означает год до н. э.)

make_date(2013, 7, 15) → 2013-07-15

make_interval ( [ годы int [, месяцы int [, недели int [, дни int [, часы int [, минуты int [, секунды double precision ]]]]]]] ) → interval

Создает интервал из полей годов, месяцев, неделей, дней, часов, минут и секунд, каждое из которых по умолчанию равно нулю

make_interval(days => 10) → 10 days

make_time ( час int, минута int, секунда double precision ) → time

Создает время из полей часа, минуты и секунды

make_time(8, 15, 23.5) → 08:15:23.5

make_timestamp ( год int, месяц int, день int, час int, минута int, секунда double precision ) → timestamp

Создает метку времени из полей года, месяца, дня, часа, минуты и секунды (отрицательное значение года означает год до н. э.)

make_timestamp(2013, 7, 15, 8, 15, 23.5) → 2013-07-15 08:15:23.5

make_timestamptz ( год int, месяц int, день int, час int, минута int, секунда double precision [, часовой_пояс text ] ) → timestamp with time zone

Создает метку времени с часовым поясом из полей года, месяца, недели, дня, часа, минуты и секунды (отрицательное значение года означает год до н. э.). Если часовой_пояс не указан, используется текущий часовой пояс; в примерах предполагается, что часовой пояс сеанса — Europe/London (Европа/Лондон)

make_timestamptz(2013, 7, 15, 8, 15, 23.5) → 2013-07-15 08:15:23.5+01

make_timestamptz(2013, 7, 15, 8, 15, 23.5, 'America/New\_York') → 2013-07-15 13:15:23.5+01

now ( ) → timestamp with time zone

Текущая дата и время (начало текущей транзакции); см. подраздел Текущая дата/время

now() → 2019-12-23 14:39:53.662522-05

statement_timestamp ( ) → timestamp with time zone

Текущая дата и время (начало текущего оператора); см. подраздел Текущая дата/время

statement_timestamp() → 2019-12-23 14:39:53.662522-05

timeofday ( ) → text

Текущая дата и время (как clock_timestamp, но в виде текстовой строки); см. подраздел Текущая дата/время

timeofday() → Mon Dec 23 14:39:53.662522 2019 EST

transaction_timestamp ( ) → timestamp with time zone

Текущая дата и время (начало текущей транзакции); см. подраздел Текущая дата/время

transaction_timestamp() → 2019-12-23 14:39:53.662522-05

to_timestamp ( double precision ) → timestamp with time zone

Преобразовывает эпоху Unix (секунды с 1970-01-01 00: 00: 00 + 00) в метку времени с часовым поясом

to_timestamp(1284352323) → 2010-09-13 04:32:03+00

В дополнение к этим функциям поддерживается оператор SQL OVERLAPS:

(начало1, конец1) OVERLAPS (начало2, конец2)
(начало1, длительность1) OVERLAPS (начало2, длительность2)

Это выражение возвращает true, когда два периода времени (определенные своими граничными точками) пересекаются, и false, когда они не пересекаются. Граничные точки могут быть указаны как пары дат, времени или меток времени или как дата, время или отметка времени с последующим интервалом. Когда предоставляется пара значений, первым можно записать как начало так и конец периода; OVERLAPS автоматически принимает в качестве начала более раннее значение из пары. Каждый период времени считается представляющим полуоткрытый интервал начало <= время < конец, если только начало и конец не равны — тогда он представляет этот единственный момент времени. Это означает, например, что два периода времени с общей граничной точкой не пересекаются.

SELECT (DATE '2001-02-16', DATE '2001-12-21') OVERLAPS
       (DATE '2001-10-30', DATE '2002-10-30');
Результат: true
SELECT (DATE '2001-02-16', INTERVAL '100 days') OVERLAPS
       (DATE '2001-10-30', DATE '2002-10-30');
Результат: false
SELECT (DATE '2001-10-29', DATE '2001-10-30') OVERLAPS
       (DATE '2001-10-30', DATE '2001-10-31');
Результат: false
SELECT (DATE '2001-10-30', DATE '2001-10-30') OVERLAPS
       (DATE '2001-10-30', DATE '2001-10-31');
Результат: true

При добавлении к значению timestamp with time zone значения interval (или при вычитании из него значения interval) компонент дней увеличивает (или уменьшает) дату timestamp with time zone на указанное количество суток, а время суток не меняется. При пересечении границы летнего времени (когда часовой пояс сеанса распознает летнее время) это означает, что interval '1 day' не обязательно равен interval '24 hours'. Например, в часовом поясе America/Denver (Америка/Денвер):

SELECT timestamp with time zone '2005-04-02 12:00:00-07' + interval '1 day';
Результат: 2005-04-03 12:00:00-06
SELECT timestamp with time zone '2005-04-02 12:00:00-07' + interval '24 hours';
Результат: 2005-04-03 13:00:00-06

Это происходит потому, что вследствие произошедшего 2005-04-03 02:00:00 в часовом поясе America/Denver перехода на летнее время был пропущен час.

Обратите внимание на возможную неоднозначность в поле months возвращаемом функцией age, потому что в разных месяцах разное количество дней. При расчете неполных месяцев QHB использует месяц от более ранней из двух дат. Например, age('2004-06-01', '2004-04-30') использует апрель, и получается 1 mon 1 day, тогда как использование мая даст 1 mon 2 days, потому что в мае 31 день, а в апреле только 30.

Вычитание дат и меток времени также может быть сложным. Один концептуально простой способ выполнить вычитание — преобразовать каждое значение в количество секунд, используя EXTRACT(EPOCH FROM ...), а затем вычислить разницу результаты; результатом будет количество секунд между этими двумя значениями. Это позволит учесть количество дней в каждом месяце, смену часовых поясов и переходы на летнее время. Вычитание значений даты или метки времени с помощью оператора «-» возвращает количество дней (24 часа) и часов/минут/секунд между значениями, учитывая те же настройки. Функция age возвращает годы, месяцы, дни и часы/минуты/секунды, выполняя вычитание от поля к полю и затем корректируя отрицательные значения полей. Следующие запросы иллюстрируют различия в этих подходах. Результаты примера были получены для timezone = ’US/Eastern’; между двумя использованными датами существует переход на летнее время:

SELECT EXTRACT(EPOCH FROM timestamptz '2013-07-01 12:00:00') -
       EXTRACT(EPOCH FROM timestamptz '2013-03-01 12:00:00');
Результат: 10537200
SELECT (EXTRACT(EPOCH FROM timestamptz '2013-07-01 12:00:00') -
        EXTRACT(EPOCH FROM timestamptz '2013-03-01 12:00:00'))
        / 60 / 60 / 24;
Результат: 121.958333333333
SELECT timestamptz '2013-07-01 12:00:00' - timestamptz '2013-03-01 12:00:00';
Результат: 121 days 23:00:00
SELECT age(timestamptz '2013-07-01 12:00:00', timestamptz '2013-03-01 12:00:00');
Результат: 4 mons

EXTRACT, date_part

EXTRACT(поле FROM источник)

Функция extract извлекает из значений даты/времени подполя, такие как год или час. Аргумент источник должен быть выражением типа timestamp, time или interval. (Выражения типа date приводятся к timestamp и поэтому также могут использоваться). Аргумент поле — это идентификатор или строка, выбирающая, какое поле извлечь из исходного значения. Функция extract возвращает значения типа numeric. Ниже приведены допустимые имена поля:

century

Век

SELECT EXTRACT(CENTURY FROM TIMESTAMP ’2000-12-16 12:21:13’);
Результат: 20
SELECT EXTRACT(CENTURY FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 21

Первый век начинается в 0001-01-01 00:00:00 н. э., хотя люди в то время этого не знали. Это определение относится ко всем странам с григорианским календарем. Нет века с номером 0, после -1-го века наступает 1-й век.

day

Для значений timestamp это поле дня (месяца) (1-31); для значений interval это количество дней

SELECT EXTRACT(DAY FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 16
SELECT EXTRACT(DAY FROM INTERVAL ’40 days 1 minute’);
Результат: 40

decade

Поле года, разделенное на 10

SELECT EXTRACT(DECADE FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 200

dow

День недели с воскресенья (0) по субботу (6)

SELECT EXTRACT(DOW FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 5

Обратите внимание, что нумерация дней недели в extract отличается от таковой в функции to_char(..., 'D').

doy

День года (1-365/366)

SELECT EXTRACT(DOY FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 47

epoch

Для значений timestamp with time zone это количество секунд с 1970-01-01 00:00:00 UTC (отрицательное для меток времени ранее этой даты); для значений date и timestamp это номинальное количество секунд с 1970-01-01 00:00:00 без учета часового пояса или переходов на летнее время; для значений interval это общее количество секунд в интервале

SELECT EXTRACT(EPOCH FROM TIMESTAMP WITH TIME ZONE ’2001-02-16
20:38:40.12-08’);
Результат: 982384720.12
SELECT EXTRACT(EPOCH FROM INTERVAL ’5 days 3 hours’);
Результат: 442800

Значение эпохи можно преобразовать обратно в timestamp with time zone с помощью функции to_timestamp:

SELECT to_timestamp(982384720.12);
Результат: 2001-02-17 04:38:40.12+00

Имейте в виду, что применение to_timestamp к времени эпохи, извлеченному из значения date или timestamp, может выдать искаженный результат: по сути этот результат будет получен, исходя из предположения, что оригинальное значение было задано в часовом поясе UTC, а это может быть не так.

hour

Поле часа (0-23)

SELECT EXTRACT(HOUR FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 20

isodow

День недели с понедельника (1) по воскресенье (7)

SELECT EXTRACT(ISODOW FROM TIMESTAMP ’2001-02-18 20:38:40’);
Результат: 7

Идентично dow, за исключением воскресенья. Соответствует нумерации дней недели по ISO 8601.

isoyear

Недельный год по ISO 8601, к которому относится дата (неприменимо к интервалам)

SELECT EXTRACT(ISOYEAR FROM DATE ’2006-01-01’);
Результат: 2005
SELECT EXTRACT(ISOYEAR FROM DATE ’2006-01-02’);
Результат: 2006

Каждый недельный год по ISO 8601 начинается с понедельника недели, содержащей 4-е января, поэтому в начале января или конце декабря год по ISO может отличаться от григорианского года. Дополнительную информацию см. в описании поля week.

julian

Юлианская дата, соответствующая дате или метке времени (неприменимо к интервалам). Метки времени, отличающиеся от полуночи по местному времени, приведут к дробным результатам. Дополнительную информацию см. в разделе Юлианские даты.

SELECT EXTRACT(JULIAN FROM DATE '2006-01-01');
Результат: 2453737
SELECT EXTRACT(JULIAN FROM TIMESTAMP '2006-01-01 12:00');
Результат: 2453737.50000000000000000000

microseconds

Поле секунд, включая дробные части, умноженное на 1 000 000; обратите внимание, что оно включает в себя и целые секунды

SELECT EXTRACT(MICROSECONDS FROM TIME ’17:12:28.5’);
Результат: 28500000

millennium

Тысячелетие

SELECT EXTRACT(MILLENNIUM FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 3

Годы в 20 века относятся ко второму тысячелетию. Третье тысячелетие началось 1 января 2001 года.

milliseconds

Поле секунд, включая дробные части, умноженное на 1000. Обратите внимание, что оно включает в себя и целые секунды.

SELECT EXTRACT(MILLISECONDS FROM TIME ’17:12:28.5’);
Результат: 28500

minute

Поле минут (0-59)

SELECT EXTRACT(MINUTE FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 38

month

Для значений timestamp это номер месяца в году (1-12); для значений interval это остаток от деления числа месяцев (по модулю) на 12 (0-11)

SELECT EXTRACT(MONTH FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 2
SELECT EXTRACT(MONTH FROM INTERVAL ’2 years 3 months’);
Результат: 3
SELECT EXTRACT(MONTH FROM INTERVAL ’2 years 13 months’);
Результат: 1

quarter

Квартал года (1-4), к которому относится дата

SELECT EXTRACT(QUARTER FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 1

second

Поле секунд, включая дробные части

SELECT EXTRACT(SECOND FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 40
SELECT EXTRACT(SECOND FROM TIME ’17:12:28.5’);
Результат: 28.5

timezone

Смещение часового пояса от UTC, измеряется в секундах. Положительные значения соответствуют часовым поясам к востоку от UTC, отрицательные — к западу от UTC. (Строго говоря, QHB не использует UTC, потому что корректировочные секунды не обрабатываются).

timezone_hour

Часовая составляющая смещения часового пояса

timezone_minute

Минутная составляющая смещения часового пояса

week

Номер недели в году по недельному календарю ISO 8601. По определению, недели ISO начинаются с понедельника, а первая неделя года содержит 4 января этого года. Другими словами, первый четверг года находится в 1 неделе этого года.

В системе нумерации недель ISO первые числа января могут быть частью 52-й или 53-й недели предыдущего года, а последние числа декабря — частью первой недели следующего года. Например, 2005-01-01 является частью 53-й недели 2004 года, 2006-01-01 — частью 52-й недели 2005 года, а 2012-12-31 — частью первой недели 2013 года. Для получения согласованных результатов рекомендуется вместе с полем week использовать поле isoyear.

SELECT EXTRACT(WEEK FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 7

year

Поле года. Имейте в виду, что 0-го год н. э. (AD) не существует, поэтому вычитать годы до н. э. (BC) из годов после н. э. следует с осторожностью.

SELECT EXTRACT(YEAR FROM TIMESTAMP ’2001-02-16 20:38:40’);
Результат: 2001

Примечание
Когда входное значение равно +/- бесконечность, функция extract возвращает +/- бесконечность для монотонно увеличивающихся полей (epoch, julian, year, isoyear, decade, century и millennium). Для других полей возвращается NULL.

Функция extract в первую очередь предназначена для вычислительной обработки. Форматирование значений даты/времени для отображения описано в разделе Функции форматирования типов данных.

Функция date_part смоделирована на традиционном Ingres, равнозначном стандартной функции SQL extract:

date_part('поле', источник)

Обратите внимание, что здесь параметр поле должен быть строковым значением, а не именем. Допустимые имена полей для date_part такие же, как и для extract. По историческим причинам функция date_part возвращает значения типа double precision, что может в некоторых случаях привести к потере точности. Поэтому вместо нее рекомендуется использовать extract.

SELECT date_part('day', TIMESTAMP '2001-02-16 20:38:40');
Результат: 16

SELECT date_part('hour', INTERVAL '4 hours 3 minutes');
Результат: 4

date_trunc

Функция date_trunc концептуально схожа с функцией trunc для чисел.

date_trunc(поле, источник [, часовой_пояс ])

Здесь источник — это выражение значения типа timestamp, timestamp with time zone или interval. (Значения типа date и time автоматически приводятся к типам timestamp и interval соответственно). Аргумент поле определяет, с какой точностью усечь входное значение. Возвращаемое значение также имеет тип timestamp, timestamp with time zone или interval, а кроме того, все его поля, менее значимые, чем заданное, установлены в значение ноль (или один для дня и месяца).

Допустимые значения для аргумента поле:

microseconds
milliseconds
second
minute
hour
day
week
month
quarter
year
decade
century
millennium

Когда входное значение имеет тип timestamp with time zone, усечение выполняется относительно определенного часового пояса; например, усечение до поля day (день) выдает значение полуночи в этой зоне. По умолчанию усечение выполняется относительно текущего значения параметра TimeZone, но можно задать другой часовой пояс, указав необязательный аргумент часовой_пояс. Название часового пояса можно задать любым из способов, описанных в разделе Часовые пояса.

Часовой пояс нельзя указывать при обработке входных данных типа timestamp without time zone или interval.

Примеры (предполагается, что местный часовой пояс — America/New_York (Америка/Нью-Йорк)):

SELECT date_trunc('hour', TIMESTAMP '2001-02-16 20:38:40');
Результат: 2001-02-16 20:00:00

SELECT date_trunc('year', TIMESTAMP '2001-02-16 20:38:40');
Результат: 2001-01-01 00:00:00

SELECT date_trunc('day', TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40+00');
Результат: 2001-02-16 00:00:00-05

SELECT date_trunc('day', TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40+00', 'Australia/Sydney');
Результат: 2001-02-16 08:00:00-05

SELECT date_trunc('hour', INTERVAL '3 days 02:47:33');
Результат: 3 days 02:00:00

date_bin

Функция date_bin «укладывает» входную метку времени в заданный интервал (шаг), установленный с указанным начальным моментом.

date_bin(шаг, источник, начальный_момент)

Здесь источник — это выражение значения типа timestamp или timestamp with time zone. (Значения типа date автоматически приводятся к типу timestamp.) Под шагом подразумевается выражение значения типа interval. Возвращаемое значение, также имеющее тип timestamp или timestamp with time zone, отмечает начало позиции, в которую помещается источник.

Примеры:

SELECT date_bin('15 minutes', TIMESTAMP '2020-02-11 15:44:17', TIMESTAMP '2001-01-01');
Результат: 2020-02-11 15:30:00

SELECT date_bin('15 minutes', TIMESTAMP '2020-02-11 15:44:17', TIMESTAMP '2001-01-01 00:02:30');
Результат: 2020-02-11 15:32:30

В случае целых единиц измерения (1 минута, 1 час и т. д.), она дает тот же результат, что и аналогичный вызов функции date_trunc, но разница в том, что date_bin может усечь дату до произвольного интервала.

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

AT TIME ZONE

Оператор AT TIME ZONE преобразует метку времени без часового пояса в метку времени с часовым поясом и обратно, а значения типа time with time zone пересчитывает для разных часовых поясов. В Таблице 33 показаны его варианты.

Таблица 33. Варианты AT TIME ZONE

Оператор

Описание

Пример(ы)

timestamp without time zone AT TIME ZONE часовой_пояс → timestamp with time zone

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

timestamp '2001-02-16 20:38:40' at time zone 'America/Denver' → 2001-02-17 03:38:40+00

timestamp with time zone AT TIME ZONE часовой_пояс → timestamp without time zone

Преобразует заданную метку времени с часовым поясом в метку времени без часового пояса, соответствующую времени в указанном часовом поясе.

timestamp with time zone '2001-02-16 20:38:40-05' at time zone 'America/Denver' → 2001-02-16 18:38:40

time with time zone AT TIME ZONE часовой_пояс → time with time zone

Пересчитывает заданное время с часовым поясом для нового часового пояса. Поскольку дата не предоставляется, в расчете используется действующее в настоящий момент смещение целевого часового пояса от UTC.

time with time zone '05:34:17-05' at time zone 'UTC' → 10:34:17+00

В этих выражениях желаемый часовой_пояс можно задать либо в виде текстовой строки (например, 'America/Los_Angeles'), либо в виде интервала (например, INTERVAL '-08:00'). В случае с текстом наименование часового пояса можно указать любым из способов, описанных в разделе Часовые пояса. Вариант с интервалом полезен только для часовых поясов с фиксированным смещением от UTC, поэтому на практике он не очень распространен

Примеры (предполагается, что текущее значение параметра TimeZone — America/ Los_Angeles (Америка/Лос-Анджелес)):

SELECT TIMESTAMP '2001-02-16 20:38:40' AT TIME ZONE 'America/Denver';
Результат: 2001-02-16 19:38:40-08

SELECT TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40-05' AT TIME ZONE 'America/Denver';
Результат: 2001-02-16 18:38:40

SELECT TIMESTAMP '2001-02-16 20:38:40-05' AT TIME ZONE 'Asia/Tokyo' AT TIME ZONE 'America/Chicago';
Результат: 2001-02-16 05:38:40

В первом примере часовой пояс добавляется к значению, в котором он отсутствует, и полученное значение отображается с использованием текущего значения параметра TimeZone. Во втором примере значение метки времени с часовым поясом перемещается в указанный часовой пояс, и возвращается значение без часового пояса. Это позволяет хранить и отображать значения, отличные от текущего значения параметра TimeZone. В третьем примере время Токио преобразуется во время Чикаго.

Функция timezone (часовой_пояс, метка_времени) равнозначна совместимой с SQL конструкции метка_времени AT TIME ZONE часовой_пояс.

Текущая дата/время

QHB предоставляет ряд функций, которые возвращают значения, зависящие от текущей даты и времени. Все эти функции, соответствующие стандарту SQL, возвращают значения, опирающиеся на время начала текущей транзакции:

CURRENT_DATE
CURRENT_TIME
CURRENT_TIMESTAMP
CURRENT_TIME(точность)
CURRENT_TIMESTAMP(точность)
LOCALTIME
LOCALTIMESTAMP
LOCALTIME(точность)
LOCALTIMESTAMP(точность)

CURRENT_TIME и CURRENT_TIMESTAMP выдают значения с часовым поясом; LOCALTIME и LOCALTIMESTAMP выдают значения без часового пояса.

CURRENT_TIME, CURRENT_TIMESTAMP, LOCALTIME и LOCALTIMESTAMP могут дополнительно принимать параметр точности, благодаря которому результат округляется до заданного в нем знака после запятой в поле секунд. Без параметра точности результат дается со всей возможной точностью.

Несколько примеров:

SELECT CURRENT_TIME;
Результат: 14:39:53.662522-05

SELECT CURRENT_DATE;
Результат: 2001-12-23

SELECT CURRENT_TIMESTAMP;
Результат: 2001-12-23 14:39:53.662522-05

SELECT CURRENT_TIMESTAMP(2);
Результат: 2001-12-23 14:39:53.66-05

SELECT LOCALTIMESTAMP;
Результат: 2001-12-23 14:39:53.662522

Поскольку эти функции возвращают время начала текущей транзакции, их значения во время транзакции не изменяются. Это считается особенностью реализации: ее цель состоит в том, чтобы позволить одной транзакции иметь согласованное представление о «текущем» времени, благодаря чему у разных изменений в рамках одной транзакции будут одинаковые метки времени.

Примечание
В других СУБД эти значения могут меняться чаще.

также предоставляет функции, которые возвращают время начала текущего оператора, а также фактическое текущее время в момент вызова функции. Полный список функций времени, не соответствующих стандарту SQL:

transaction_timestamp()
statement_timestamp()
clock_timestamp()
timeofday()
now()

Функция transaction_timestamp() равнозначна функции CURRENT_TIMESTAMP, но названа так, чтобы явно отражать, что именно она возвращает. Функция statement_timestamp() возвращает время начала текущего оператора (а точнее, время получения последнего командного сообщения от клиента). Функции statement_timestamp() и transaction_timestamp() возвращают одинаковое значение в ходе первой команды транзакции, но при выполнении последующих команд их результаты могут отличаться. Функция clock_timestamp() возвращает фактическое текущее время, и поэтому ее значение меняется даже в рамках одной команды SQL. Функция timeofday() существует в QHB по историческим причинам. Как и clock_timestamp(), она возвращает фактическое текущее время, но в виде форматированной строки типа text, а не значения timestamp with time zone. Функция now() представляет собой традиционный для QHB эквивалент функции transaction_timestamp().

Все типы данных даты/времени также принимают специальное буквальное значение now для указания текущей даты и времени (опять же, интерпретируемых как время начала транзакции). Таким образом, все три следующих запроса возвращают один и тот же результат:

SELECT CURRENT_TIMESTAMP;
SELECT now();
SELECT TIMESTAMP ’now’; -- но см. совет ниже

Совет
Не используйте третью форму при указании значения, которое будет вычисляться позднее, например, в предложении DEFAULT для столбца таблицы. Система преобразует now в значение timestamp сразу после анализа константы, поэтому когда потребуется такое значение по умолчанию, будет использовано время создания таблицы! Первые две формы не будут вычисляться, пока не понадобится значение по умолчанию, так как они являются вызовами функций. Таким образом, они выдадут желаемое поведение к моменту добавления строки в таблицу. (См. также подраздел Специальные значения)

Задержка выполнения

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

pg_sleep ( double precision )
pg_sleep_for ( interval )
pg_sleep_until ( timestamp with time zone )

Функция pg_sleep переводит процесс текущего сеанса в спящий режим вплоть до истечения указанного числа секунд (можно указать дробное число). Вспомогательная функция pg_sleep_for позволяет задать длительность спящего режима в виде значения типа interval. Вспомогательная функция pg_sleep_until позволяет задать определенное время выходя из спящего режима. Например:

SELECT pg_sleep(1.5);
SELECT pg_sleep_for('5 minutes');
SELECT pg_sleep_until('tomorrow 03:00');

Примечание
Эффективное разрешение интервала задержки зависит от платформы; обычно это 0,01 секунды. Длительность спящего режима не будет меньше указанного времени. Задержка может быть и дольше, в зависимости от таких факторов, как, например, нагрузка на сервер. В частности, функция pg_sleep_until не гарантирует выход из спящего режима именно в указанное время, но это точно не случится раньше.

Предупреждение!
При вызове pg_sleep или ее вариантов убедитесь, что ваш сеанс не содержит больше блокировок, чем это необходимо. В противном случае другим сеансам, возможно, придется ждать завершения вашего спящего режима, замедляя работу всей системы.

Функции поддержки перечислений

Для перечислимых типов (описанных в разделе Перечислимые типы), существует несколько функций, позволяющих программировать чище, без жесткого кодирования конкретных значений перечислимого типа. Эти функции приведены в Таблице 34. В примерах предполагается, что перечислимый тип создан как:

CREATE TYPE rainbow AS ENUM (’red’, ’orange’, ’yellow’, ’green’, ’blue’, ’purple’);

Таблица 34. Функции поддержки перечислений

Функция

Описание

Пример(ы)

enum_first ( anyenum ) → anyenum

Возвращает первое значение входных данных перечислимого типа.

enum_first(null::rainbow) → red

enum_last ( anyenum ) → anyenum

Возвращает последнее значение входных данных перечислимого типа.

enum_last(null::rainbow) → purple

enum_range ( anyenum ) → anyarray

Возвращает все значения входных данных перечислимого типа в упорядоченном массиве.

enum_range(null::rainbow) → {red,orange,yellow,​green,blue,purple}

enum_range ( anyenum, anyenum ) → anyarray

Возвращает диапазон между двумя заданными значениями перечисления в виде упорядоченного массива. Значения должны быть принадлежать одному перечислимому типу. Если первый параметр имеет значение NULL, результат будет начинаться с первого значения перечислимого типа. Если второй параметр имеет значение NULL, результат будет заканчиваться последним значением перечислимого типа.

enum_range('orange'::rainbow, 'green'::rainbow) → {orange,yellow,green}
enum_range(NULL, 'green'::rainbow) → {red,orange,​yellow,green}
enum_range('orange'::rainbow, NULL) → {orange,yellow,green,​blue,purple}

Обратите внимание, что за исключением формы enum_range с двумя аргументами, эти функции игнорируют конкретное переданное им значение; их интересует только его объявленный тип данных. Может быть передан либо NULL, либо конкретное значение этого типа, при этом результат будет одинаковым. Как правило, эти функции применяются к столбцу таблицы или аргументу функции, а не к жестко закодированному имени типа, как показано в примерах.

Геометрические функции и операторы

Для геометрических типов point (точка), box (прямоугольник), lseg (отрезок), line (прямая), path (путь), polygon (многоугольник) и circle (окружность) существует большой набор встроенных вспомогательных функций и операторов, приведенных в Таблице 35, Таблице 36 и Таблице 37.

Таблица 35. Геометрические операторы

Оператор

Описание

Пример(ы)

геометрический_тип + point → геометрический_тип

Добавляет координаты второго аргумента типа point к координатам каждой точки первого аргумента, тем самым осуществляя перенос объекта. Доступен для типов point, box, path, circle.

box '(1,1),(0,0)' + point '(2,0)' → (3,1),(2,0)

path + path → path

Конкатенирует два открытых пути (возвращает NULL, если один из путей замкнут).

path '[(0,0),(1,1)]' + path '[(2,2),(3,3),(4,4)]' → [(0,0),(1,1),(2,2),(3,3),(4,4)]

геометрический_тип - point → геометрический_тип

Вычитает координаты второго аргумента типа point из координат каждой точки первого аргумента, тем самым осуществляя перенос объекта. Доступен для типов point, box, path, circle.

box '(1,1),(0,0)' - point '(2,0)' → (-1,1),(-2,0)

геометрический_тип * point → геометрический_тип

Умножает каждую точку первого аргумента на второй аргумент типа point (воспринимая точки как комплексные числа, представленные действительной и мнимой частями, и проводя стандартное комплексное умножение). Если интерпретировать второй аргумент типа point как вектор, это будет равнозначно увеличению размера этого объекта и расстояния от начала координат при помощи длины этого вектора с поворотом объекта против часовой стрелки относительно начала координат на угол, равный углу между вектором и осью x. Доступно для типов point, box,[a] path, circle.

path '((0,0),(1,0),(1,1))' * point '(3.0,0)' → ((0,0),(3,0),(3,3))

path '((0,0),(1,0),(1,1))' * point(cosd(45), sind(45)) → ((0,0),​(0.7071067811865475,0.7071067811865475),​(0,1.414213562373095))

геометрический_тип / point → геометрический_тип

Делит каждую точку первого аргумента на второй аргумент типа point (воспринимая точки как комплексные числа, представленные действительной и мнимой частями, и проводя стандартное комплексное деление). Если интерпретировать второй аргумент типа point как вектор, это будет равнозначно уменьшению размера этого объекта и расстояния от начала координат при помощи длины этого вектора с поворотом объекта по часовой стрелки относительно начала координат на угол, равный углу между вектором и осью x. Доступно для типов point, box,[a] path, circle.

path '((0,0),(1,0),(1,1))' / point '(2.0,0)' → ((0,0),(0.5,0),(0.5,0.5))

path '((0,0),(1,0),(1,1))' / point(cosd(45), sind(45)) → ((0,0),​(0.7071067811865476,-0.7071067811865476),​(1.4142135623730951,0))

@-@ геометрический_тип → double precision

Вычисляет общую длину. Доступен для типов lseg, path.

@-@ path '[(0,0),(1,0),(1,1)]' → 2

@@ геометрический_тип → point

Вычисляет центральную точку. Доступен для типов box, lseg, polygon, circle.

@@ box '(2,2),(0,0)' → (1,1)

box # box → box

Вычисляет пересечение двух прямоугольников или возвращает NULL, если такого пересечения нет.

box '(2,2),(-1,-1)' # box '(1,1),(-2,-2)' → (1,1),(-1,-1)

геометрический_тип ## геометрический_тип → point

Вычисляет ближайшую к первому объекту точку на втором объекте. Доступен для этих пар типов: (point, box), (point, lseg), (point, line), (lseg, box), (lseg, lseg), (line, lseg).

point '(0,0)' ## lseg '[(2,0),(0,2)]' → (1,1)

геометрический_тип <-> геометрический_тип → double precision

Вычисляет расстояние между объектами. Доступен для всех геометрических типов, кроме polygon, для всех сочетаний типа point с другим геометрическим типом, а также для этих пар типов: (box, lseg), (lseg, line), (polygon, circle) (и для этих пар в обратном порядке).

circle '<(0,0),1>' <-> circle '<(5,0),1>' → 3

геометрический_тип @> геометрический_тип → boolean

Первый объект содержит второй? Доступен для этих пар типов: (box, point), (box, box), (path, point), (polygon, point), (polygon, polygon), (circle, point), (circle, circle).

circle '<(0,0),2>' @> point '(1,1)' → t

геометрический_тип <@ геометрический_тип → boolean

Первый объект содержится в (находится на) втором? Доступен для этих пар типов: (point, box), (point, lseg), (point, line), (point, path), (point, polygon), (point, circle), (box, box), (lseg, box), (lseg, line), (polygon, polygon), (circle, circle).

point '(1,1)' <@ circle '<(0,0),2>' → t

геометрический_тип && геометрический_тип → boolean

Эти объекты пересекаются? (Для результата true достаточно одной общей точки.) Доступен для типов box, polygon, circle.

box '(1,1),(0,0)' && box '(2,2),(0,0)' → t

геометрический_тип << геометрический_тип → boolean

Первый объект строго слева от второго? Доступен для типов point, box, polygon, circle.

circle '<(0,0),1>' << circle '<(5,0),1>' → t

геометрический_тип >> геометрический_тип → boolean

Первый объект строго справа от второго? Доступен для типов point, box, polygon, circle.

circle '<(5,0),1>' >> circle '<(0,0),1>' → t

геометрический_тип &< геометрический_тип → boolean

Первый объект не простирается правее второго? Доступен для типов box, polygon, circle.

box '(1,1),(0,0)' &< box '(2,2),(0,0)' → t

геометрический_тип &> геометрический_тип → boolean

Первый объект не простирается левее второго? Доступен для типов box, polygon, circle.

box '(3,3),(0,0)' &> box '(2,2),(0,0)' → t

геометрический_тип <<| геометрический_тип → boolean

Первый объект строго ниже второго? Доступен для типов point, box, polygon, circle.

box '(3,3),(0,0)' <<| box '(5,5),(3,4)' → t

геометрический_тип |>> геометрический_тип → boolean

Первый объект строго выше второго? Доступен для типов point, box, polygon, circle.

box '(5,5),(3,4)' |>> box '(3,3),(0,0)' → t

геометрический_тип &<| геометрический_тип → boolean

Первый объект не простирается выше второго? Доступен для типов box, polygon, circle.

box '(1,1),(0,0)' &<| box '(2,2),(0,0)' → t

геометрический_тип |&> геометрический_тип → boolean

Первый объект не простирается ниже второго? Доступен для типов box, polygon, circle.

box '(3,3),(0,0)' |&> box '(2,2),(0,0)' → t

box <^ box → boolean

Первый объект ниже второго (объекты могут соприкасаться краями)?

box '((1,1),(0,0))' <^ box '((2,2),(1,1))' → t

box >^ box → boolean

Первый объект выше второго (объекты могут соприкасаться краями)?

box '((2,2),(1,1))' >^ box '((1,1),(0,0))' → t

геометрический_тип ?# геометрический_тип → boolean

Эти объекты пересекаются? Доступен для этих пар типов: (box, box), (lseg, box), (lseg, lseg), (lseg, line), (line, box), (line, line), (path, path).

lseg '[(-1,0),(1,0)]' ?# box '(2,2),(-2,-2)' → t

?- line → boolean

?- lseg → boolean

Линия горизонтальна?

?- lseg '[(-1,0),(1,0)]' → t

point ?- point → boolean

Точки выровнены по горизонтали (то есть имеют одинаковую координату по y)?

point '(1,0)' ?- point '(0,0)' → t

?| line → boolean

?| lseg → boolean

Линия вертикальна?

?| lseg '[(-1,0),(1,0)]' → f

point ?| point → boolean

Точки выровнены по вертикали (то есть имеют одинаковую координату по x)?

point '(0,1)' ?| point '(0,0)' → t

line ?-| line → boolean

lseg ?-| lseg → boolean

Линии перпендикулярны?

lseg '[(0,0),(0,1)]' ?-| lseg '[(0,0),(1,0)]' → t

line ?|| line → boolean

lseg ?|| lseg → boolean

Линии параллельны?

lseg '[(-1,0),(1,0)]' ?|| lseg '[(-1,2),(1,2)]' → t

геометрический_тип ~= геометрический_тип → boolean

Эти объекты совпадают? Доступен для типов point, box, polygon, circle.

polygon '((0,0),(1,1))' ~= polygon '((1,1),(0,0))' → t

[a] При «повороте» прямоугольника с помощью этих операторов перемещаются только его угловые точки: стороны этого прямоугольника по-прежнему считаются параллельными осям. Как следствие, в отличие от истинного поворота, при этой операции размер прямоугольника меняется.

Внимание!
Обратите внимание, что оператор «тождественности», ~=, представляет собой обычную проверку равенства для типов point, box, polygon и circle. Для некоторых геометрических типов также имеется оператор =, но = проверяет только равенство площадей. Другие скалярные операторы сравнения (<= и т. п.) для тех типов, для которых они доступны, также сравнивают площади.

Таблица 36. Геометрические функции

Функция

Описание

Пример(ы)

area ( геометрический_тип ) → double precision

Вычисляет площадь. Доступна для типов box, path, circle. Входное значение типа path должно быть замкнутым, иначе возвращается NULL. Кроме того, если этот путь является самопересекающимся, результат может не иметь смысла.

area(box '(2,2),(0,0)') → 4

center ( геометрический_тип ) → point

Вычисляет центральную точку. Доступна для типов box, circle.

center(box '(1,2),(0,0)') → (0.5,1)

diagonal ( box ) → lseg

Извлекает диагональ прямоугольника в виде отрезка (аналогично lseg(box)).

diagonal(box '(1,2),(0,0)') → [(1,2),(0,0)]

diameter ( circle ) → double precision

Вычисляет диаметр окружности.

diameter(circle '<(0,0),2>') → 4

height ( box ) → double precision

Вычисляет вертикальный размер прямоугольника.

height(box '(1,2),(0,0)') → 2

isclosed ( path ) → boolean

Путь замкнутый?

isclosed(path '((0,0),(1,1),(2,0))') → t

isopen ( path ) → boolean

Путь открытый?

isopen(path '[(0,0),(1,1),(2,0)]') → t

length ( геометрический_тип ) → double precision

Вычисляет общую длину. Доступна для типов lseg, path.

length(path '((-1,0),(1,0))') → 4

npoints ( геометрический_тип ) → integer

Возвращает количество точек. Доступна для типов path, polygon.

npoints(path '[(0,0),(1,1),(2,0)]') → 3

pclose ( path ) → path

Преобразует путь в замкнутую форму.

pclose(path '[(0,0),(1,1),(2,0)]') → ((0,0),(1,1),(2,0))

popen ( path ) → path

Преобразует путь в открытую форму.

popen(path '((0,0),(1,1),(2,0))') → [(0,0),(1,1),(2,0)]

radius ( circle ) → double precision

Вычисляет радиус окружности.

radius(circle '<(0,0),2>') → 2

slope ( point, point ) → double precision

Вычисляет наклон линии, проведенной через две точки.

slope(point '(0,0)', point '(2,1)') → 0.5

width ( box ) → double precision

Вычисляет горизонтальный размер прямоугольника.

width(box '(1,2),(0,0)') → 1

Таблица 37. Функции преобразования геометрических типов

Функция

Описание

Пример(ы)

box ( circle ) → box

Вычисляет прямоугольник, вписанный в окружность.

box(circle '<(0,0),2>') → (1.414213562373095,1.414213562373095),​(-1.414213562373095,-1.414213562373095)

box ( point ) → box

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

box(point '(1,0)') → (1,0),(1,0)

box ( point, point ) → box

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

box(point '(0,1)', point '(1,0)') → (1,1),(0,0)

box ( polygon ) → box

Вычисляет окружающий прямоугольник для многоугольника.

box(polygon '((0,0),(1,1),(2,0))') → (2,1),(0,0)

bound_box ( box, box ) → box

Вычисляет окружающий прямоугольник для двух прямоугольников.

bound_box(box '(1,1),(0,0)', box '(4,4),(3,3)') → (4,4),(0,0)

circle ( box ) → circle

Вычисляет наименьшую окружность, вмещающую прямоугольник.

circle(box '(1,1),(0,0)') → <(0.5,0.5),0.7071067811865476>

circle ( point, double precision ) → circle

Строит окружность по центру и радиусу.

circle(point '(0,0)', 2.0) → <(0,0),2>

circle ( polygon ) → circle

Преобразует многоугольник в окружность. Центром окружности является среднее арифметическое координат вершин многоугольника, а радиусом — среднее расстояние от вершин многоугольника до этого центра.

circle(polygon '((0,0),(1,3),(2,0))') → <(1,1),1.6094757082487299>

line ( point, point ) → line

Преобразует две точки в проходящую через них линию.

line(point '(-1,0)', point '(1,0)') → {0,-1,0}

lseg ( box ) → lseg

Извлекает диагональ прямоугольника в виде отрезка.

lseg(box '(1,0),(-1,0)') → [(1,0),(-1,0)]

lseg ( point, point ) → lseg

Строит отрезок по двум конечным точкам.

lseg(point '(-1,0)', point '(1,0)') → [(-1,0),(1,0)]

path ( polygon ) → path

Преобразует многоугольник в замкнутый путь с тем же списком точек.

path(polygon '((0,0),(1,1),(2,0))') → ((0,0),(1,1),(2,0))

point ( double precision, double precision ) → point

Строит точку по заданным координатам.

point(23.4, -44.5) → (23.4,-44.5)

point ( box ) → point

Вычисляет центр прямоугольника.

point(box '(1,0),(-1,0)') → (0,0)

point ( circle ) → point

Вычисляет центр окружности.

point(circle '<(0,0),2>') → (0,0)

point ( lseg ) → point

Вычисляет центр отрезка.

point(lseg '[(-1,0),(1,0)]') → (0,0)

point ( polygon ) → point

Вычисляет центр многоугольника (среднее арифметическое координат его вершин).

point(polygon '((0,0),(1,1),(2,0))') → (1,0.3333333333333333)

polygon ( box ) → polygon

Преобразует прямоугольник в многоугольник с 4 вершинами.

polygon(box '(1,1),(0,0)') → ((0,0),(0,1),(1,1),(1,0))

polygon ( circle ) → polygon

Преобразует окружность в многоугольник с 12 вершинами.

polygon(circle '<(0,0),2>') → ((-2,0),​(-1.7320508075688774,0.9999999999999999),​(-1.0000000000000002,1.7320508075688772),​(-1.2246063538223773e-16,2),​(0.9999999999999996,1.7320508075688774),​(1.732050807568877,1.0000000000000007),​(2,2.4492127076447545e-16),​(1.7320508075688776,-0.9999999999999994),​(1.0000000000000009,-1.7320508075688767),​(3.673819061467132e-16,-2),​(-0.9999999999999987,-1.732050807568878),​(-1.7320508075688767,-1.0000000000000009))

polygon ( integer, circle ) → polygon

Преобразует окружность в многоугольник с n вершинами.

polygon(4, circle '<(3,0),1>') → ((2,0),​(3,1),​(4,1.2246063538223773e-16),​(3,-1))

polygon ( path ) → polygon

Преобразует замкнутый путь в многоугольник с тем же списком вершин.

polygon(path '((0,0),(1,1),(2,0))') → ((0,0),(1,1),(2,0))

К двум числам, составляющим значение типа point можно обратиться как к элементам массива с индексами 0 и 1. Например, если t.p — столбец типа point, то SELECT p[0] FROM t возвращает координату X, а UPDATE t SET p[1] = ... изменяет координату Y. Таким же образом значение типа box или lseg можно рассматривать как массив из двух значений типа point.

Функции и операторы для сетевых адресов

Типы cidr и inet, предназначенные для сетевых IP-адресов, поддерживают обычные операторы сравнения, приведенные в Таблице 1, а также специальные операторы и функции, приведенные в Таблице 38 и Таблице 39.

Любое значение типа cidr можно привести к типу inet неявно, поэтому операторы и функции, показанные ниже для типа inet, также работают со значениями типа cidr. (Там, где для inet cidr показаны разные функции, причина этого в том, что в данном случае их поведение с этими двумя типами различается.) Кроме того, разрешено приведение значения типа inet к типу cidr. При этому все биты справа от сетевой маски молча обнуляются, чтобы создать допустимое значение типа cidr.

Таблица 38. Операторы для IP-адресов

Оператор

Описание

Пример(ы)

inet << inet → boolean

Первая подсеть строго содержится во второй? Этот и следующие четыре оператора проверяют вхождение одной подсети в другую. Они рассматривают только сетевые части двух заданных адресов (игнорируя все биты справа от сетевой маски) и определяют, идентична ли одна сеть другой или является ее подсетью.

inet '192.168.1.5' << inet '192.168.1/24' → t

inet '192.168.0.5' << inet '192.168.1/24' → f

inet '192.168.1/24' << inet '192.168.1/24' → f

inet <<= inet → boolean

Первая подсеть содержится во второй или равна ей?

inet '192.168.1/24' <<= inet '192.168.1/24' → t

inet >> inet → boolean

Первая подсеть строго содержит вторую?

inet '192.168.1/24' >> inet '192.168.1.5' → t

inet >>= inet → boolean

Первая подсеть содержит вторую или равна ей?

inet '192.168.1/24' >>= inet '192.168.1/24' → t

inet && inet → boolean

Одна из двух подсетей содержит другую или равна ей?

inet '192.168.1/24' && inet '192.168.1.80/28' → t

inet '192.168.1/24' && inet '192.168.2.0/28' → f

~ inet → inet

Вычисляет результат побитового НЕ.

~ inet '192.168.1.6' → 63.87.254.249

inet & inet → inet

Вычисляет результат побитового И.

inet '192.168.1.6' & inet '0.0.0.255' → 0.0.0.6

inet | inet → inet

Вычисляет результат побитового ИЛИ.

inet '192.168.1.6' | inet '0.0.0.255' → 192.168.1.255

inet + bigint → inet

Добавляет смещение к адресу.

inet '192.168.1.6' + 25 → 192.168.1.31

bigint + inet → inet

Добавляет смещение к адресу.

200 + inet '::ffff:fff0:1' → ::ffff:255.240.0.201

inet - bigint → inet

Вычитает смещение из адреса.

inet '192.168.1.43' - 36 → 192.168.1.7

inet - inet → bigint

Вычисляет разность двух адресов.

inet '192.168.1.43' - inet '192.168.1.19' → 24
inet '::1' - inet '::ffff:1' → -4294901760

Таблица 39. Функции для IP-адресов

Функция

Описание

Пример(ы)

abbrev ( inet ) → text

Создает сокращенный формат отображения адреса в виде текста. (Результат получается тот же, что и у функции вывода inet; «сокращенным» он является только в сравнении с результатом явного приведения к типу text, которое по историческим причинам никогда не будет убирать часть с сетевой маской.)

abbrev(inet '10.1.0.0/32') → 10.1.0.0

abbrev ( cidr ) → text

Создает сокращенный формат отображения адреса в виде текста. (Это сокращение заключается в отбрасывании полностью нулевых октетов справа от сетевой маски; другие примеры см. в Таблице 22.)

abbrev(cidr '10.1.0.0/16') → 10.1/16

broadcast ( inet ) → inet

Вычисляет широковещательный адрес для сети в заданном адресе.

broadcast(inet '192.168.1.5/24') → 192.168.1.255/24

family ( inet ) → integer

Возвращает семейство адреса: 4 для адресов IPv4, 6 для адресов IPv6.

family(inet '::1') → 6

host ( inet ) → text

Возвращает IP-адрес в виде текста, игнорируя сетевую маску.

host(inet '192.168.1.0/24') → 192.168.1.0

hostmask ( inet ) → inet

Вычисляет маску хоста для сети в заданном адресе.

hostmask(inet '192.168.23.20/30') → 0.0.0.3

inet_merge ( inet, inet ) → cidr

Вычисляет наименьшую сеть, содержащую обе заданные сети.

inet_merge(inet '192.168.1.5/24', inet '192.168.2.5/24') → 192.168.0.0/22

inet_same_family ( inet, inet ) → boolean

Проверяет, принадлежат ли адреса одному семейству IP.

inet_same_family(inet '192.168.1.5/24', inet '::1') → f

masklen ( inet ) → integer

Возвращает длину сетевой маски в битах.

masklen(inet '192.168.1.5/24') → 24

netmask ( inet ) → inet

Вычисляет сетевую маску для сети в заданном адресе.

netmask(inet '192.168.1.5/24') → 255.255.255.0

network ( inet ) → cidr

Возвращает сетевую часть адреса, обнуляя все биты справа от сетевой маски. (Это равнозначно приведению значения к типу cidr.)

network(inet '192.168.1.5/24') → 192.168.1.0/24

set_masklen ( inet, integer ) → inet

Задает длину сетевой маски для значения типа inet. Адресная часть при этом не меняется.

set_masklen(inet '192.168.1.5/24', 16) → 192.168.1.5/16

set_masklen ( cidr, integer ) → cidr

Задает длину сетевой маски для значения cidr. Биты адреса справа от новой сетевой маски при этом устанавливаются в ноль.

set_masklen(cidr '192.168.1.0/24', 16) → 192.168.0.0/16

text ( inet ) → text

Возвращает несокращенный IP-адрес и длину сетевой маски в виде текста. (Такой же результат получается при явном приведении к типу text.)

text(inet '192.168.1.5') → 192.168.1.5/32

Совет
Функции abbrev, host и text предназначены в основном для отображения IP-адресов в альтернативных форматах.

Типы для MAC-адресов, macaddr и macaddr8, поддерживают обычные операторы сравнения, , приведенные в Таблице 1, а также специальные функции, приведенные в Таблице 40. Кроме того, они поддерживают побитовые логические операторы ~, & и | (НЕ, И и ИЛИ), показанные выше для IP-адресов.

Таблица 40. Функции для MAC-адресов

Функция

Описание

Пример(ы)

trunc ( macaddr ) → macaddr

Устанавливает в ноль 3 последних байта адреса. Оставшийся префикс можно сопоставить с конкретным производителем (используя информацию, отсутствующую в QHB).

trunc(macaddr '12:34:56:78:90:ab') → 12:34:56:00:00:00

trunc ( macaddr8 ) → macaddr8

Устанавливает в ноль 5 последних байта адреса. Оставшийся префикс можно сопоставить с конкретным производителем (используя информацию, отсутствующую в QHB).

trunc(macaddr8 '12:34:56:78:90:ab:cd:ef') → 12:34:56:00:00:00:00:00

macaddr8_set7bit ( macaddr8 ) → macaddr8

Устанавливает единицу в 7-м бите адреса, создавая так называемый модифицированный идентификатор EUI-64 для включения в адрес IPv6.

macaddr8_set7bit(macaddr8 '00:34:56:ab:cd:ef') → 02:34:56:ff:fe:ab:cd:ef

Функции и операторы текстового поиска

В Таблице 41, Таблице 42 и Таблице 43 собраны функции и операторы, предоставляемые для полнотекстового поиска.

Таблица 41. Операторы текстового поиска

Оператор

Описание

Пример(ы)

tsvector @@ tsquery → boolean

tsquery @@ tsvector → boolean

Аргумент типа tsvector соответствует аргументу типа tsquery? (Эти аргументы можно передавать в любом порядке.)

to_tsvector('fat cats ate rats') @@ to_tsquery('cat & rat') → t

text @@ tsquery → boolean

Текстовая строка, преобразованная после неявного вызова функции to_tsvector(), соответствует tsquery?

'fat cats ate rats' @@ to_tsquery('cat & rat') → t

tsvector @@@ tsquery → boolean

tsquery @@@ tsvector → boolean

Это устаревший синоним для @@.

to_tsvector('fat cats ate rats') @@@ to_tsquery('cat & rat') → t

tsvector || tsvector → tsvector

Конкатенирует два значения tsvector. Если в обоих аргументах содержатся позиции лексем, позиции второго аргумента корректируются соответствующим образом.

'a:1 b:2'::tsvector || 'c:1 d:2 b:3'::tsvector → 'a':1 'b':2,5 'c':3 'd':4

tsquery && tsquery → tsquery

Выполняет логическую операцию И с двумя значениями tsquery, создавая запрос, который соответствует документам, соответствующим обоим входным запросам.

'fat | rat'::tsquery && 'cat'::tsquery → ( 'fat' | 'rat' ) & 'cat'

tsquery || tsquery → tsquery

Выполняет логическую операцию ИЛИ с двумя значениями tsquery, создавая запрос, который соответствует документам, соответствующим любому из входных запросов.

'fat | rat'::tsquery || 'cat'::tsquery → 'fat' | 'rat' | 'cat'

!! tsquery → tsquery

Выполняет логическую операцию НЕ со значением tsquery, создавая запрос, который соответствует документам, не соответствующим входному запросу.

!! 'cat'::tsquery → !'cat'

tsquery <-> tsquery → tsquery

Конструирует фразовый запрос, который будет соответствовать документам, если два входных запроса соответствуют идущим подряд лексемам.

to_tsquery('fat') <-> to_tsquery('rat') → 'fat' <-> 'rat'

tsquery @> tsquery → boolean

Первый аргумент типа tsquery содержит второй? (Здесь учитывается только факт нахождения всех лексем одного запроса в другом, а объединение операторов игнорируется.)

'cat'::tsquery @> 'cat & rat'::tsquery → f

tsquery <@ tsquery → boolean

Первый аргумент типа tsquery содержится во втором? (Здесь учитывается только факт нахождения всех лексем одного запроса в другом, а объединение операторов игнорируется.)

'cat'::tsquery <@ 'cat & rat'::tsquery → t

'cat'::tsquery <@ '!cat & rat'::tsquery → t

Помимо этих специальных операторов, для типов tsvector и tsquery доступны обычные операторы сравнения, приведенные в Таблице 1. Они не очень полезны для текстового поиска, но позволяют, к примеру, создавать уникальные индексы по столбцам этих типов.

Таблица 42. Функции текстового поиска

Функция

Описание

Пример(ы)

array_to_tsvector ( text[] ) → tsvector

Преобразует массив лексем в tsvector. Заданные строки используются как есть без дополнительной обработки.

array_to_tsvector('{fat,cat,rat}'::text[]) → 'cat' 'fat' 'rat'

get_current_ts_config ( ) → regconfig

Возвращает OID текущей конфигурации текстового поиска по умолчанию (заданной параметром default_text_search_config).

get_current_ts_config() → english

length ( tsvector ) → integer

Возвращает количество лексем в заданном tsvector.

length('fat:2,4 cat:3 rat:5A'::tsvector) → 3

numnode ( tsquery ) → integer

Возвращает количество лексем и операторов в заданном tsquery.

numnode('(fat & rat) | cat'::tsquery) → 5

plainto_tsquery ( [ конфигурация regconfig, ] запрос text ) → tsquery

Преобразует текст в tsquery, нормализуя слова согласно указанной или стандартной конфигурации. Все пунктуационные знаки в этой строке при этом игнорируются (она не определяет операторы запроса). Результирующий запрос соответствует документам, содержащим все слова текста, отличные от стоп-слов.

plainto_tsquery('english', 'The Fat Rats') → 'fat' & 'rat'

phraseto_tsquery ( [ конфигурация regconfig, ] запрос text ) → tsquery

Преобразует текст в tsquery, нормализуя слова согласно указанной или стандартной конфигурации. Все пунктуационные знаки в этой строке при этом игнорируются (она не определяет операторы запроса). Результирующий запрос соответствует фразам, содержащим все слова текста, отличные от стоп-слов.

phraseto_tsquery('english', 'The Fat Rats') → 'fat' <-> 'rat'

phraseto_tsquery('english', 'The Cat and Rats') → 'cat' <2> 'rat'

websearch_to_tsquery ( [ конфигурация regconfig, ] запрос text ) → tsquery

Преобразует текст в tsquery, нормализуя слова согласно указанной или стандартной конфигурации. Последовательности слов в кавычках преобразуются в проверки фраз. Слово «or» воспринимается как формирующее оператор OR (ИЛИ), а символ минуса формирует оператор NOT (НЕ); другие пунктуационные знаки игнорируются. Это примерно соответствует поведению некоторых распространенных инструментов поиска интернете.

websearch_to_tsquery('english', '"fat rat" or cat dog') → 'fat' <-> 'rat' | 'cat' & 'dog'

querytree ( tsquery ) → text

Создает представление индексируемой части tsquery. Пустой результат или просто T означают, что запрос неиндексируемый.

querytree('foo & ! bar'::tsquery) → 'foo'

setweight ( вектор tsvector, вес "char" ) → tsvector

Присваивает заданный вес каждому элементу вектора.

setweight('fat:2,4 cat:3 rat:5B'::tsvector, 'A') → 'cat':3A 'fat':2A,4A 'rat':5A

setweight ( вектор tsvector, вес "char", лексемы text[] ) → tsvector

Присваивает заданный вес элементам вектора, перечисленным в списке лексемы.

setweight('fat:2,4 cat:3 rat:5,6B'::tsvector, 'A', '{cat,rat}') → 'cat':3A 'fat':2,4 'rat':5A,6A

strip ( tsvector ) → tsvector

Убирает позиции и веса из значения tsvector.

strip('fat:2,4 cat:3 rat:5A'::tsvector) → 'cat' 'fat' 'rat'

to_tsquery ( [ конфигурация regconfig, ] запрос text ) → tsquery

Преобразует текст в tsquery, нормализуя слова согласно указанной или стандартной конфигурации. Слова должны соединяться допустимыми операторами tsquery.

to_tsquery('english', 'The & Fat & Rats') → 'fat' & 'rat'

to_tsvector ( [ конфигурация regconfig, ] документ text ) → tsvector

Преобразует текст в tsquery, нормализуя слова согласно указанной или стандартной конфигурации. В результат будет включена информация о позициях слов.

to_tsvector('english', 'The Fat Rats') → 'fat':2 'rat':3

to_tsvector ( [ конфигурация regconfig, ] документ json ) → tsvector

to_tsvector ( [ конфигурация regconfig, ] документ jsonb ) → tsvector

Преобразует все строковые значения в документе JSON в tsvector, нормализуя слова согласно указанной или стандартной конфигурации. Затем результаты конкатенируются в порядке следования в документе, формируя выходное значение. Информация о позициях слов генерируется таким образом, словно между каждой парой строковых значений находится одно стоп-слово. (Учтите, что в случае jsonb «порядок следования в документе» полей объекта JSON зависит от реализации; разница показана в примерах.)

to_tsvector('english', '{"aa": "The Fat Rats", "b": "dog"}'::json) → 'dog':5 'fat':2 'rat':3

to_tsvector('english', '{"aa": "The Fat Rats", "b": "dog"}'::jsonb) → 'dog':1 'fat':4 'rat':5

json_to_tsvector ( [ конфигурация regconfig, ] документ json, фильтр jsonb ) → tsvector

jsonb_to_tsvector ( [ конфигурация regconfig, ] документ jsonb, фильтр jsonb ) → tsvector

Выбирает в документе JSON все элементы, запрошенные фильтром и преобразует каждый в tsvector, нормализуя слова согласно указанной или стандартной конфигурации. Затем результаты конкатенируются в порядке следования в документе, формируя выходное значение. Информация о позициях слов генерируется таким образом, словно между каждой парой строковых значений находится одно стоп-слово. (Учтите, что в случае jsonb «порядок следования в документе» полей объекта JSON зависит от реализации.) Аргумент фильтр должен быть массивом jsonb, содержащим ноль и более следующих ключевых слов: "string" (включить все строковые значения), "numeric" (включить все числовые значения), "boolean" (включить все логические значения), "key" (включить все ключи) или "all" (включить всё вышеперечисленное). В особых случаях фильтр также может быть простым значением JSON, представляющим собой одно из этих ключевых слов.

json_to_tsvector('english', '{"a": "The Fat Rats", "b": 123}'::json, '["string", "numeric"]') → '123':5 'fat':2 'rat':3

json_to_tsvector('english', '{"cat": "The Fat Rats", "dog": 123}'::json, '"all"') → '123':9 'cat':1 'dog':7 'fat':4 'rat':5

ts_delete ( вектор tsvector, лексема text ) → tsvector

Удаляет все вхождения заданной лексемы из вектора.

ts_delete('fat:2,4 cat:3 rat:5A'::tsvector, 'fat') → 'cat':3 'rat':5A

ts_delete ( вектор tsvector, лексемы text[] ) → tsvector

Удаляет все вхождения лексем, перечисленных в списке лексемы, из вектора.

ts_delete('fat:2,4 cat:3 rat:5A'::tsvector, ARRAY['fat','rat']) → 'cat':3

ts_filter ( вектор tsvector, веса "char"[] ) → tsvector

Выбирает из вектора только элементы с заданными весами.

ts_filter('fat:2,4 cat:3b,7c rat:5A'::tsvector, '{a,b}') → 'cat':3B 'rat':5A

ts_headline ( [ конфигурация regconfig, ] документ text, запрос tsquery [, параметры text ] ) → text

Отображает в сокращенной форме соответствующие запросу вхождения в содержимом документа, которое должно быть просто текстом, а не значением типа tsvector. Перед сопоставлением с запросом слова в этом документе нормализуются согласно указанной или стандартной конфигурации.

cat ate the rat.

ts_headline ( [ json, запрос tsquery [, параметры text ] ) → text

ts_headline ( [ конфигурация regconfig, ] документ jsonb, запрос tsquery [, параметры text ] ) → text

Отображает в сокращенной форме соответствующие запросу вхождения, найденные в строковых значениях внутри документа JSON.

ts_headline('{"cat":"raining cats and dogs"}'::jsonb, 'cat') → {"cat": "raining cats and dogs"}

ts_rank ( [ веса real[], ] вектор tsvector, запрос tsquery [, нормализация integer ] ) → real

Вычисляет количественный показатель, демонстрирующий, в какой степени вектор соответствует данному запросу.

ts_rank(to_tsvector('raining cats and dogs'), 'cat') → 0.06079271

ts_rank_cd ( [ веса real[], ] вектор tsvector, запрос tsquery [, нормализация integer ] ) → real

С помощью алгоритма расчета плотности покрытия вычисляет количественный показатель, демонстрирующий, в какой степени вектор соответствует данному запросу.

ts_rank_cd(to_tsvector('raining cats and dogs'), 'cat') → 0.1

ts_rewrite ( запрос tsquery, цель tsquery, подстановка tsquery ) → tsquery

Заменяет в запросе вхождения значения цель значением подстановка.

ts_rewrite('a & b'::tsquery, 'a'::tsquery, 'foo|bar'::tsquery) → 'b' & ( 'foo' | 'bar' )

ts_rewrite ( запрос tsquery, выборка text ) → tsquery

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

SELECT ts_rewrite('a & b'::tsquery, 'SELECT t,s FROM aliases') → 'b' & ( 'foo' | 'bar' )

tsquery_phrase ( запрос1 tsquery, запрос2 tsquery ) → tsquery

Конструирует фразовый запрос, который ищет вхождения, соответствующие запросу1 и запросу2, в идущих подряд лексемах (аналогично оператору <->).

tsquery_phrase(to_tsquery('fat'), to_tsquery('cat')) → 'fat' <-> 'cat'

tsquery_phrase ( запрос1 tsquery, запрос2 tsquery, расстояние integer ) → tsquery

Конструирует фразовый запрос, который ищет вхождения, соответствующие запросу1 и запросу2, на определенном, заданном в лексемах, расстоянии друг от друга.

tsquery_phrase(to_tsquery('fat'), to_tsquery('cat'), 10) → 'fat' <10> 'cat'

tsvector_to_array ( tsvector ) → text[]

Преобразует значение типа tsvector в массив лексем.

tsvector_to_array('fat:2,4 cat:3 rat:5A'::tsvector) → {cat,fat,rat}

unnest ( tsvector ) → setof record ( лексема text, позиции smallint[], веса text )

Разворачивает tsvector в набор строк, по одной на каждую лексему.

select * from unnest('cat:3 fat:2,4 rat:5A'::tsvector) →
     lexeme | positions | weights
    --------+-----------+---------
     cat    | {3}       | {D}
     fat    | {2,4}     | {D,D}
     rat    | {5}       | {A}

Совет
Все функции текстового поиска, принимающие необязательный аргумент regconfig, будут использовать конфигурацию, заданную параметром default_text_search_config, когда этот аргумент опущен.

Функции в Таблице 43 перечислены отдельно, потому что обычно они не используются в повседневных операциях текстового поиска. Они полезны в основном для разработки и отладки новых конфигураций текстового поиска.

Таблица 43. Функции отладки текстового поиска

Функция

Описание

Пример(ы)

ts_debug ( [ конфигурация regconfig, ] документ text ) → setof record ( псевдоним text, описание text, фрагмент text, словари regdictionary[], словарь regdictionary, лексемы text[] )

Извлекает из документа фрагменты, нормализует их согласно заданной или стандартной конфигурации текстового поиска и возвращает информацию о том, как был обработан каждый фрагмент.

ts_debug('english', 'The Brightest supernovaes') → (asciiword,"Word, all ASCII",The,{english_stem},english_stem,{}) ...

ts_lexize ( словарь regdictionary, фрагмент text ) → text[]

Возвращает массив заменяющих лексем, если входной фрагмент есть в словаре, пустой массив, если фрагмент есть в словаре, но является стоп-словом, или NULL, если такого слова нет.

ts_lexize('english_stem', 'stars') → {star}

ts_parse ( имя_анализатора text, документ text ) → setof record ( код_фрагмента integer, фрагмент text )

Извлекает фрагменты из документа, используя именованный анализатор.

ts_parse('default', 'foo - bar') → (1,foo) ...

ts_parse ( oid_анализатора oid, документ text ) → setof record ( код_фрагмента integer, фрагмент text )

Извлекает фрагменты из документа, используя анализатор с указанным OID.

ts_parse(3722, 'foo - bar') → (1,foo) ...

ts_token_type ( имя_анализатора text ) → setof record ( код_фрагмента integer, псевдоним text, описание text )

Возвращает таблицу, описывающую все типы фрагментов, которые может распознать именованный анализатор.

ts_token_type('default') → (1,asciiword,"Word, all ASCII") ...

ts_token_type ( oid_анализатора oid ) → setof record ( код_фрагмента integer, псевдоним text, описание text )

Возвращает таблицу, описывающую все типы фрагментов, которые может распознать анализатор с указанным OID.

ts_token_type(3722) → (1,asciiword,"Word, all ASCII") ...

ts_stat ( sql_запрос text [, веса text ] ) → setof record ( слово text, число_док integer, число_вхожд integer )

Выполняет sql_запрос, который должен возвращать единственный столбец tsvector, и выдает статистику по каждой отдельной лексеме, содержащейся в данных.

ts_stat('SELECT vector FROM apod') → (foo,10,15) ...

Функции генерирования UUID

В QHB имеется одна функция для генерирования UUID:

gen_random_uuid () → uuid

Эта функция возвращает случайный UUID версии 4. Это наиболее часто используемый тип UUID, который подходит для большинства приложений.

Модуль uuid-ossp предоставляет дополнительные функции, реализующие другие стандартные алгоритмы генерирования UUID.

Кроме того, QHB предоставляет для UUID приведенные в Таблице 1 простые операторы сравнения.

Функции XML

Функции и подобные им выражения, описанные в этом разделе, работают со значениями типа xml. Информацию о типе xml см. в разделе Тип XML. Подобные функциям выражения xmlparse и xmlserialize, преобразовывающие значения в тип xml и из него, описаны там, а не в этом разделе.

Использование большинства этих функций требует, чтобы QHB был собран с библиотекой configure --with-libxml.

Создание XML-контента

Для создания XML-контента из данных SQL существует набор функций и подобных им выражений. Соответственно, они особенно подходят для форматирования результатов запросов в XML-документы для обработки в клиентских приложениях.

xmlcomment

xmlcomment ( text ) → xml

Функция xmlcomment создает значение XML, содержащее комментарий XML с заданным текстом. Текст не может содержать «--» или заканчиваться на «-», иначе полученная конструкция не будет допустимым комментарием XML. Если аргумент равен NULL, результат тоже будет NULL.

Пример:

SELECT xmlcomment('hello');

  xmlcomment
--------------
 <!--hello-->

xmlconcat

xmlconcat ( xml [, ...] ) → xml

Функция xmlconcat конкатенирует список отдельных значений XML, выдавая одно значение, содержащее фрагмент содержимого XML. Значения NULL отбрасываются; результат будет NULL, только если все аргументы равны NULL.

Пример:

SELECT xmlconcat('<abc/>', '<bar>foo</bar>');

      xmlconcat
----------------------
 <abc/><bar>foo</bar>

Объявления XML, если они есть, объединяются следующим образом. Если все значения аргументов имеют объявление одной версии XML, эта версия используется в результате, иначе версия вообще не используется. Если у всех значений аргумента имеется объявление standalone со значением «yes», то это же значение используется в результате. Если у всех значений аргумента имеется объявление standalone и по крайней мере одно из них равно «no», тогда в результате используется это значение. Иначе у результата не будет объявления standalone. Если определено, что в результате должно быть объявление standalone, но нет объявления версии, будет выведена версия 1.0, поскольку для XML требуется, чтобы объявление XML содержало объявление версии. Объявления кодировок игнорируются и удаляются во всех случаях.

Пример:

SELECT xmlconcat('<?xml version="1.1"?><foo/>', '<?xml version="1.1" standalone="no"?><bar/>');

             xmlconcat
-----------------------------------
 <?xml version="1.1"?><foo/><bar/>

xmlelement

xmlelement ( NAME имя [, XMLATTRIBUTES ( значение_атрибута [ AS имя_атрибута ] [, ...] ) ] [, содержимое [, ...]] ) → xml

Выражение xmlelement создает элемент XML с заданным именем, атрибутами и содержимым. Показанные в синтаксисе элементы имя и имя_атрибута представляют собой простые идентификаторы, а не значения. Элементы значение_атрибута и содержимое являются выражениями, которые могут выдавать любой тип данных QHB. Аргументы внутри XMLATTRIBUTES генерируют атрибуты элемента XML; значения содержимого конкатенируют с ними, формируя содержимое элемента.

Примеры:

SELECT xmlelement(name foo);

 xmlelement
------------
 <foo/>

SELECT xmlelement(name foo, xmlattributes('xyz' as bar));

    xmlelement
------------------
 <foo bar="xyz"/>

SELECT xmlelement(name foo, xmlattributes(current_date as bar), 'cont', 'ent');

             xmlelement
-------------------------------------
 <foo bar="2007-01-26">content</foo>

Имена элементов и атрибутов, недопустимые в XML, экранируются путем замены нарушающих символов последовательностью _xHHHH_, где HHHH — это кодовая точка символа Unicode в шестнадцатеричной записи. Например:

SELECT xmlelement(name "foo$bar", xmlattributes('xyz' as "a&b"));

            xmlelement
----------------------------------
 <foo_x0024_bar a_x0026_b="xyz"/>

Если значение атрибута является ссылкой на столбец, то указывать явно имя атрибута необязательно — в качестве этого имени по умолчанию будет использовано имя столбца. В остальных случаях атрибуту нужно явно задать имя. Поэтому этот пример применим:

CREATE TABLE test (a xml, b xml);
SELECT xmlelement(name test, xmlattributes(a, b)) FROM test;

А эти — нет:

SELECT xmlelement(name test, xmlattributes('constant'), a, b) FROM test;
SELECT xmlelement(name test, xmlattributes(func(a, b))) FROM test;

Содержимое элемента, если оно задано, будет отформатировано в соответствии с его типом данных. Если само содержимое имеет тип xml, есть возможность конструировать сложные XML-документы. Например:

SELECT xmlelement(name foo, xmlattributes('xyz' as bar),
                            xmlelement(name abc),
                            xmlcomment('test'),
                            xmlelement(name xyz));

                  xmlelement
----------------------------------------------
 <foo bar="xyz"><abc/><!--test--><xyz/></foo>

Содержимое других типов будет отформатировано в допустимые символьные данные XML. В частности, это означает, что символы <, > и & будут преобразованы в сущностные объекты. Двоичные данные (тип данных bytea) будут представлены в кодировке base64 или в шестнадцатеричной кодировке, в зависимости от настройки параметра конфигурации xmlbinary. Как ожидается, конкретное поведение для отдельных типов данных будет развиваться, чтобы согласовать сопоставления QHB с сопоставлениями, указанными в стандарте SQL:2006 и новее, как описано в подразделе Сопоставления типов данных и значений между SQL и XML.

xmlforest

xmlforest ( содержимое [ AS имя ] [, ...] ) → xml

Выражение xmlforest создает XML-лес (последовательность) элементов, используя заданные имена и содержимое. Как и в выражении xmlelement, каждое имя должно быть простым идентификатором, тогда как выражения содержимое могут иметь любой тип данных.

Примеры:

SELECT xmlforest('abc' AS foo, 123 AS bar);

          xmlforest
------------------------------
 <foo>abc</foo><bar>123</bar>

SELECT xmlforest(table_name, column_name)
FROM information_schema.columns
WHERE table_schema = 'pg_catalog';

                                         xmlforest
-------------------------------------------------------------------------------------------
 <table_name>pg_authid</table_name><column_name>rolname</column_name>
 <table_name>pg_authid</table_name><column_name>rolsuper</column_name>
 ...

Как видно из второго примера, если значение содержимого является ссылкой на столбец, имя элемента можно опустить, поскольку в этом случае по умолчанию им станет имя столбца. В противном случае имя следует указывать.

Имена элементов, недопустимые в XML, экранируются, как показано выше для выражения xmlelement. Точно так же данные содержимого экранируются, делая его допустимым для XML (за исключением тех данные, которые уже имеют тип xml).

Обратите внимание, что XML-леса не являются допустимыми документами XML, если они состоят из более чем одного элемента, поэтому может быть полезно вложить выражения xmlforest в xmlelement.

xmlpi

xmlpi ( NAME имя [, содержимое ] ) → xml

Выражение xmlpi создает инструкцию по обработке XML. Как и в выражении xmlelement, имя должно быть простым идентификатором, тогда как выражение содержимое может иметь любой тип данных. Это содержимое, если оно задано, не должно содержать последовательность символов ?>.

Пример:

SELECT xmlpi(name php, 'echo "hello world";');

            xmlpi
-----------------------------
 <?php echo "hello world";?>

xmlroot

xmlroot ( xml, VERSION {text|NO VALUE} [, STANDALONE {YES|NO|NO VALUE} ] ) → xml

Выражение xmlroot изменяет свойства корневого узла значения XML. Если задается версия, она заменяет значение в объявлении версии корневого узла; если задается значение параметра standalone, оно заменяет значение в объявлении standalone корневого узла.

SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'),
               version '1.0', standalone yes);

                xmlroot
----------------------------------------
 <?xml version="1.0" standalone="yes"?>
 <content>abc</content>

xmlagg

xmlagg ( xml ) → xml

Функция xmlagg, в отличие от других описанных здесь функций, является агрегатной. Он конкатенирует входные значения с вызовом агрегатной функции, во многом подобно xmlconcat, за исключением того, что конкатенация затрагивает строки, а не выражения в одной строке. Дополнительную информацию об агрегатных функциях см. в разделе Агрегатные функции.

Пример:

CREATE TABLE test (y int, x xml);
INSERT INTO test VALUES (1, '<foo>abc</foo>');
INSERT INTO test VALUES (2, '<bar/>');
SELECT xmlagg(x) FROM test;
        xmlagg
----------------------
 <foo>abc</foo><bar/>

Чтобы определить порядок конкатенации, в агрегатный вызов можно добавить предложение ORDER BY, как описано в разделе Агрегатные выражения. Например:

SELECT xmlagg(x ORDER BY y DESC) FROM test;
        xmlagg
----------------------
 <bar/><foo>abc</foo>

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

SELECT xmlagg(x) FROM (SELECT * FROM test ORDER BY y DESC) AS tab;
        xmlagg
----------------------
 <bar/><foo>abc</foo>

Предикаты XML

Выражения, описанные в этом разделе, проверяют свойства значений типа xml.

IS DOCUMENT

xml IS DOCUMENT → boolean

Выражение IS DOCUMENT возвращает true, если значение аргумента XML является правильным документом XML, false, если это не так (то есть если это фрагмент содержимого), или NULL, если аргумент равен NULL. Разница между документами и фрагментами содержимого описана в разделе Тип XML.

IS NOT DOCUMENT

xml IS NOT DOCUMENT → boolean

Выражение IS NOT DOCUMENT возвращает false, если значение аргумента XML является правильным документом XML, true, если это не так (то есть если это фрагмент содержимого), или NULL, если аргумент равен NULL.

XMLEXISTS

XMLEXISTS ( text PASSING [BY {REF|VALUE}] xml [BY {REF|VALUE}] ) → boolean

Функция xmlexists вычисляет выражение XPath 1.0 (первый аргумент), используя в качестве элемента контекста переданное значение XML. Функция возвращает false, если в результате этого вычисления выдается пустой набор узлов, true, если выдается любое другое значение, или NULL, если любой аргумент равен NULL. Отличное от NULL значение, передаваемое в качестве элемента контекста, должно быть документом XML, а не фрагментом содержимого или любым значением, отличным от XML.

Пример:

SELECT xmlexists('//town[text() = ''Toronto'']' PASSING BY VALUE '<towns><town>Toronto</town><town>Ottawa</town></towns>');

 xmlexists
------------
 t
(1 row)

Предложения BY REF и BY VALUE в QHB принимаются, но игнорируются (это рассматривается в подразделе Непреднамеренные ограничения реализации).

В стандарте SQL функция xmlexists вычисляет выражения на языке XML Query, но QHB допускает только выражение XPath 1.0 (это рассматривается в подразделе Запросы ограничены XPath 1.0).

xml_is_well_formed

xml_is_well_formed ( text ) → boolean
xml_is_well_formed_document ( text ) → boolean
xml_is_well_formed_content ( text ) → boolean

Эти функции проверяют, представляет ли строка типа text синтаксически корректным XML, возвращая логический результат. Функция xml_is_well_formed_document проверяет аргумент как синтаксически корректный документ, а функция xml_is_well_formed_content — как синтаксически корректное содержимое. Функция xml_is_well_formed выполняет первое, если в параметре конфигурации xmloption задано значение DOCUMENT, или второе, если задано CONTENT. Это означает, что xml_is_well_formed полезна для определения успешности простого приведения к типу xml, тогда как две другие функции полезны для определения того, будут ли успешными соответствующие варианты XMLPARSE.

Примеры:

SET xmloption TO DOCUMENT;
SELECT xml_is_well_formed('<>');
 xml_is_well_formed
--------------------
 f
(1 row)

SELECT xml_is_well_formed('<abc/>');
 xml_is_well_formed
--------------------
 t
(1 row)

SET xmloption TO CONTENT;
SELECT xml_is_well_formed('abc');
 xml_is_well_formed
--------------------
 t
(1 row)

SELECT xml_is_well_formed_document('<pg:foo xmlns:pg="http://postgresql.org/stuff">bar</pg:foo>');
 xml_is_well_formed_document
-----------------------------
 t
(1 row)

SELECT xml_is_well_formed_document('<pg:foo xmlns:pg="http://postgresql.org/stuff">bar</my:foo>');
 xml_is_well_formed_document
-----------------------------
 f
(1 row)

Последний пример показывает, что при проверке учитывается корректное сопоставление пространств имен.

Обработка XML

Для обработки значений типа данных xml QHB предлагает функции xpath и xpath_exists, которые вычисляют выражения XPath 1.0, и табличную функцию XMLTABLE.

xpath

xpath ( xpath text, xml xml [, массив_пространств_имен text[] ] ) → xml[]

Функция xpath вычисляет выражение XPath 1.0 xpath (заданное в виде текста) для значения XML xml. Она возвращает массив значений XML, соответствующих набору узлов, созданному выражением XPath. Если выражение XPath возвращает скалярное значение, а не набор узлов, то возвращается массив из одного элемента.

Второй аргумент должен быть синтаксически корректным документом XML. В частности, он должен иметь всего один элемент корневого узла.

Необязательный третий аргумент функции — это массив сопоставлений пространств имен. Этот массив должен быть двумерным массивом типа text с длиной второй оси, равной 2 (т.е. это должен быть массив массивов, каждый из которых состоит ровно из 2 элементов). Первый элемент каждой записи массива — это имя пространства имен (псевдоним), второй — его URI. Необязательно, чтобы предоставленные в этом массиве псевдонимы совпадали с используемыми в самом документе XML (другими словами, как в документе XML, так и в контексте функции xpath псевдонимы локальны).

Пример:

SELECT xpath('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>',
             ARRAY[ARRAY['my', 'http://example.com']]);

 xpath
--------
 {test}
(1 row)

Чтобы работать с пространствами имен по умолчанию (анонимными), напишите что-то вроде этого:

SELECT xpath('//mydefns:b/text()', '<a xmlns="http://example.com"><b>test</b></a>',
             ARRAY[ARRAY['mydefns', 'http://example.com']]);

 xpath
--------
 {test}
(1 row)

xpath_exists

xpath_exists ( xpath text, xml xml [, массив_пространств_имен text[] ] ) → boolean

Функция xpath_exists является специализированной формой функции xpath. Вместо того чтобы возвращать отдельные значения XML, которые удовлетворяют выражению XPath 1.0, эта функция возвращает логическое значение, показывающее, был ли удовлетворен запрос или нет (в частности, было ли получено какое-либо значение, отличное от пустого набора узлов). Эта функция равнозначна предикату XMLEXISTS, за исключением того, что она также поддерживает аргумент сопоставления пространств имен.

Пример:

SELECT xpath_exists('/my:a/text()', '<my:a xmlns:my="http://example.com">test</my:a>',
                     ARRAY[ARRAY['my', 'http://example.com']]);

 xpath_exists
--------------
 t
(1 row)

xmltable

XMLTABLE (
    [XMLNAMESPACES ( uri_пространства_имен AS имя_пространства_имен [, ...] ),]
    выражение_строки PASSING [BY {REF|VALUE}] выражение_документа [BY {REF|VALUE}]
    COLUMNS имя { тип [PATH выражение_столбца] [DEFAULT выражение_по_умолчанию] [NOT NULL | NULL]
                  | FOR ORDINALITY }
            [, ...]
) → setof record

Выражение xmltable создает таблицу на основе значения XML, фильтра XPath для извлечения строк и набора определений столбцов. Хотя синтаксически оно напоминает функцию, оно может присутствовать в запросе только в качестве таблицы в предложении FROM.

Необязательное предложение XMLNAMESPACES задает список разделенных запятыми пространств имен, где каждое uri_пространства_имен является выражением типа text, а каждое имя_пространства_имен — простой идентификатор. Оно определяет используемые в документе пространства имен XML и их псевдонимы. Специфицирование пространства имен по умолчанию в настоящее время не поддерживается.

Обязательный аргумент выражение_строки — это выражение XPath 1.0 (заданное в виде значения типа text), которое вычисляется с использованием в качестве элемента контекста переданного значения XML, для получения набора узлов XML. Эти узлы — то, что xmltable превращает в выходные строки. Если выражение_документа равно NULL или выражение_строки создает пустой набор узлов или любое значение, отличное от набора узлов, строки выдаваться не будут.

Аргумент выражение_документа предоставляет элемент контекста для аргумента выражение_строки. Этим элементом должен быть синтаксически корректный документ XML; фрагменты/леса не принимаются. Предложения BY REF и BY VALUE принимаются, но игнорируются (это рассматривается в подразделе Непреднамеренные ограничения реализации).

В стандарте SQL функция xmltable вычисляет выражения на языке XML Query, но QHB допускает только выражение XPath 1.0 (это рассматривается в подразделе Запросы ограничены XPath 1.0).

Обязательное предложение COLUMNS задает столбцы, которые будут созданы в выходной таблице. Его формат показан выше в шаблоне синтаксиса. Для каждого столбца необходимо задать имя и тип данных (если только не указано FOR ORDINALITY, что подразумевает тип integer). Предложения, задающие путь, значение по умолчанию и допустимость значений NULL, являются необязательными.

Столбец, помеченный FOR ORDINALITY, будет заполнен номерами строк, начиная с 1, в порядке узлов, полученных из результирующего набора узлов выражения_строки. Указанием FOR ORDINALITY можно пометить максимум один столбец.

Примечание
В XPath 1.0 не указывается порядок узлов в наборе, поэтому код, полагающийся на определенный порядок результатов, будет зависеть от реализации. Подробную информацию об этом см. в подразделе Ограничение XPath до версии 1.0.

Аргумент выражение_столбца для столбца — это выражение XPath 1.0, которое вычисляется для каждой строки, с использованием в качестве элемента контекста текущего узла из результата выражения_строки, чтобы найти значение этого столбца. Если выражение_столбца не задано, то в качестве неявного пути используется имя столбца.

Если выражение XPath столбца возвращает значение, отличное от XML (может быть только строкой, логическим значением или числовым значением двойной точности в XPath 1.0) и столбец имеет тип QHB, отличный от xml, значение этому столбцу будет присвоено так, как если бы типу QHB было присвоено строковое представление значения. (Если это логическое значение, его строковое представление принимается равным 1 или 0, если тип выходного столбца относится к числовой категории, и true или false в противном случае.)

Если выражение XPath столбца возвращает непустой набор узлов XML и этот столбец в QHB имеет тип xml, столбцу будет точно присвоен результат выражения, если он имеет форму документа или содержимого.

Присвоенный выходному столбцу xml результат, отличный от XML, выдает содержимое, представляющее собой отдельный текстовый узел со строковым значением результата. Результат XML, назначенный столбцу любого другого типа, может содержать не более одного узла, или возникает ошибка. Если существует ровно один узел, значение этому столбцу будет присвоено так, как если бы типу QHB было присвоено строковое значение узла (как определено для функции string в XPath 1.0).

Строковое значение элемента XML представляет собой конкатенацию всех текстовых узлов, содержащихся в этом элементе и его потомках, в порядке следования этих узлов в документе. Строковым значением элемента без текстовых узлов-потомков является пустая строка (не NULL). Любые атрибуты xsi:nil игнорируются. Обратите внимание, что состоящий только из пробелов узел text() между двумя нетекстовыми элементами при этом сохраняется и начальные пробелы в узле text() не убираются. Правила, определяющие строковое значение других типов узлов XML и значений, отличный от XML, можно посмотреть в описании функции string в XPath 1.0.

Представленные здесь правила преобразования не совсем соответствуют стандарту SQL, как описано в подразделе Сопоставления типов данных и значений между SQL и XML.

Если выражение пути возвращает пустой набор узлов (обычно когда ему нет соответствия) для данной строки, столбцу будет присвоено значение NULL, если только не задано выражение_по_умолчанию — в таком случае используется значение, полученное в результате вычисления этого выражения.

Это выражение_по_умолчанию вычисляется не сразу же при вызове xmltable, а каждый раз, когда для столбца требуется значение по умолчанию. Если выражение квалифицируется как стабильное или неизменное, повторное вычисление может быть пропущено. Это означает, что в выражении_по_умолчанию можно с пользой использовать изменчивые функции, такие как nextval.

Столбцы могут быть помечены признаком NOT NULL. Если выражение_столбца для столбца NOT NULL ничему не соответствует и при этом отсутствует указание DEFAULT или результатом вычисления выражения_столбца тоже является NULL, выдается сообщение об ошибке.

Примеры:

CREATE TABLE xmldata AS SELECT
xml $$
<ROWS>
  <ROW id="1">
    <COUNTRY_ID>AU</COUNTRY_ID>
    <COUNTRY_NAME>Australia</COUNTRY_NAME>
  </ROW>
  <ROW id="5">
    <COUNTRY_ID>JP</COUNTRY_ID>
    <COUNTRY_NAME>Japan</COUNTRY_NAME>
    <PREMIER_NAME>Shinzo Abe</PREMIER_NAME>
    <SIZE unit="sq_mi">145935</SIZE>
  </ROW>
  <ROW id="6">
    <COUNTRY_ID>SG</COUNTRY_ID>
    <COUNTRY_NAME>Singapore</COUNTRY_NAME>
    <SIZE unit="sq_km">697</SIZE>
  </ROW>
</ROWS>
$$ AS data;

SELECT xmltable.*
  FROM xmldata,
       XMLTABLE('//ROWS/ROW'
                PASSING data
                COLUMNS id int PATH '@id',
                        ordinality FOR ORDINALITY,
                        "COUNTRY_NAME" text,
                        country_id text PATH 'COUNTRY_ID',
                        size_sq_km float PATH 'SIZE[@unit = "sq_km"]',
                        size_other text PATH
                             'concat(SIZE[@unit!="sq_km"], " ", SIZE[@unit!="sq_km"]/@unit)',
                        premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified') ;

 id | ordinality | COUNTRY_NAME | country_id | size_sq_km |  size_other  | premier_name
----+------------+--------------+------------+------------+--------------+---------------
  1 |          1 | Australia    | AU         |            |              | not specified
  5 |          2 | Japan        | JP         |            | 145935 sq_mi | Shinzo Abe
  6 |          3 | Singapore    | SG         |        697 |              | not specified

В следующем примере показана конкатенация нескольких узлов text (), применение имени столбца в качестве фильтра XPath, а также обработка пробелов, комментариев XML и инструкций по обработке:

CREATE TABLE xmlelements AS SELECT
xml $$
  <root>
   <element>  Hello<!-- xyxxz -->2a2<?aaaaa?> <!--x-->  bbb<x>xxx</x>CC  </element>
  </root>
$$ AS data;

SELECT xmltable.*
  FROM xmlelements, XMLTABLE('/root' PASSING data COLUMNS element text);
         element
-------------------------
   Hello2a2   bbbxxxCC

Следующий пример иллюстрирует, как с помощью предложения XMLNAMESPACES можно задать список пространств имен, используемых в документе XML и в выражениях XPath:

WITH xmldata(data) AS (VALUES ('
<example xmlns="http://example.com/myns" xmlns:B="http://example.com/b">
 <item foo="1" B:bar="2"/>
 <item foo="3" B:bar="4"/>
 <item foo="4" B:bar="5"/>
</example>'::xml)
)
SELECT xmltable.*
  FROM XMLTABLE(XMLNAMESPACES('http://example.com/myns' AS x,
                              'http://example.com/b' AS "B"),
             '/x:example/x:item'
                PASSING (SELECT data FROM xmldata)
                COLUMNS foo int PATH '@foo',
                  bar int PATH '@B:bar');
 foo | bar
-----+-----
   1 |   2
   3 |   4
   4 |   5
(3 rows)

Отображение таблиц в XML

Следующие функции отображают содержимое реляционных таблиц в значения XML. Их можно рассматривать как средства экспорта в XML:

table_to_xml ( table regclass, nulls boolean,
               tableforest boolean, targetns text ) → xml
query_to_xml ( query text, nulls boolean,
               tableforest boolean, targetns text ) → xml
cursor_to_xml ( cursor refcursor, count integer, nulls boolean,
                tableforest boolean, targetns text ) → xml

Функция table_to_xml отображает содержимое именованной таблицы, передаваемой в параметр table. Тип regclass принимает строки, идентифицирующие таблицы, используя обычную запись, включая необязательное указание схемы и кавычки (подробную информацию см. в разделе Типы идентификаторов объектов). Функция query_to_xml выполняет запрос, текст которого передается в параметре query, и отображает набор результатов. Функция cursor_to_xml извлекает указанное количество строк из курсора, заданного параметром cursor. Этот вариант рекомендуется, если необходимо отобразить большие таблицы, поскольку каждой функцией создается в памяти результирующее значение.

Если параметр tableforest равен false, результирующий документ XML выглядит следующим образом:

<имя_таблицы>
  <row>
    <имя_столбца1>данные</имя_столбца1>
    <имя_столбца2>данные</имя_столбца2>
  </row>

  <row>
    ...
  </row>

  ...
</имя_таблицы>

Если же tableforest равен true, результатом является фрагмент содержимого XML, который выглядит следующим образом:

<имя_таблицы>
  <имя_столбца1>данные</имя_столбца1>
  <имя_столбца2>данные</имя_столбца2>
</имя_таблицы>

<имя_таблицы>
  ...
</имя_таблицы>

...

Если имя таблицы неизвестно, то есть при отображении запроса или курсора, то в первом формате используется строка table, а во втором — row.

Выбор между этими форматами остается за пользователем. Первый формат — это правильный XML-документ, который будет важен во многих приложениях. Второй формат скорее будет более полезен в функции cursor_to_xml, если результирующие значения будут собраны в один документ позже. Для изменения результатов по вкусу можно воспользоваться описанными выше функциями для создания содержимого XML, в частности xmlelement.

Значения данных отображаются так же, как описано выше для функции xmlelement.

Параметр nulls определяет, нужно ли включать в выходные данные значения NULL. Если он равен true, значения NULL в столбцах представляются как:

<имя_столбца xsi:nil="true"/>

где xsi — префикс пространства имен для XML Schema Instance. Соответствующее объявление пространства имен будет добавлено к значению результата. Если он равен false, столбцы, содержащие значения NULL, просто не включаются в вывод.

Параметр targetns задает целевое пространство имен XML для результата. Если конкретное пространство имен не требуется, в этом параметре должна быть передана пустая строка.

Следующие функции возвращают документы XML Schema, описывающие отображения, выполняемые соответствующими функциями, описанными выше:

table_to_xmlschema ( table regclass, nulls boolean,
                     tableforest boolean, targetns text ) → xml
query_to_xmlschema ( query text, nulls boolean,
                     tableforest boolean, targetns text ) → xml
cursor_to_xmlschema ( cursor refcursor, nulls boolean,
                      tableforest boolean, targetns text ) → xml

Для получения соответствующих отображений данных XML и документов XML Schema важно, чтобы в этих функциях передавались одинаковые параметры.

Следующие функции выдают связанные вместе отображения данных XML и соответствующую XML Schema в одном документе (или лесу). Они могут быть полезны, когда требуются автономные и самоописываемые результаты:

table_to_xml_and_xmlschema ( table regclass, nulls boolean,
                             tableforest boolean, targetns text ) → xml
query_to_xml_and_xmlschema ( query text, nulls boolean,
                             tableforest boolean, targetns text ) → xml

Кроме того, доступны следующие функции для вывода аналогичных отображений целых схем или всей текущей базы данных:

schema_to_xml ( schema name, nulls boolean,
                tableforest boolean, targetns text ) → xml
schema_to_xmlschema ( schema name, nulls boolean,
                      tableforest boolean, targetns text ) → xml
schema_to_xml_and_xmlschema ( schema name, nulls boolean,
                              tableforest boolean, targetns text ) → xml

database_to_xml ( nulls boolean,
                  tableforest boolean, targetns text ) → xml
database_to_xmlschema ( nulls boolean,
                        tableforest boolean, targetns text ) → xml
database_to_xml_and_xmlschema ( nulls boolean,
                                tableforest boolean, targetns text ) → xml

Эти функции игнорируют таблицы, которые не может читать текущий пользователь. Функции уровня базы данных дополнительно игнорируют схемы, для которых у текущего пользователя нет права USAGE (использование).

Обратите внимание, что они потенциально производят большой объем данных, которые должны быть встроены в память. При необходимости отображений содержимого больших схем или баз данных может оказаться целесообразным вместо этого рассмотреть отображение таблиц по отдельности, возможно даже через курсор.

Результат отображения содержимого схемы выглядит следующим образом:

<имя_схемы>

отображение-таблицы1

отображение-таблицы2

...

</имя_схемы>

где формат отображения таблицы зависит от параметра tableforest, описанного выше.

Результат отображения содержимого базы данных выглядит следующим образом:

<имя_бд>

<имя_схемы1>
  ...
</имя_схемы1>

<имя_схемы2>
  ...
</имя_схемы2>

...

</имя_бд>

где отображение схемы такое же, как указано выше.

В качестве примера использования выходных данных, создаваемых этими функциями, на Примере 1 показана таблица стилей XSLT, преобразующая выходные данные table_to_xml_and_xmlschema в документ HTML, содержащий табличное представление данных таблицы. Аналогичным образом результаты этих функций могут быть преобразованы в другие форматы на основе XML.

Пример 1. Таблица стилей XSLT для преобразования вывода SQL/XML в HTML

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns="http://www.w3.org/1999/xhtml"
>

  <xsl:output method="xml"
      doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
      doctype-public="-//W3C/DTD XHTML 1.0 Strict//EN"
      indent="yes"/>

  <xsl:template match="/*">
    <xsl:variable name="schema" select="//xsd:schema"/>
    <xsl:variable name="tabletypename"
                  select="$schema/xsd:element[@name=name(current())]/@type"/>
    <xsl:variable name="rowtypename"
                  select="$schema/xsd:complexType[@name=$tabletypename]/xsd:sequence/xsd:element[@name='row']/@type"/>

    <html>
      <head>
        <title><xsl:value-of select="name(current())"/></title>
      </head>
      <body>
        <table>
          <tr>
            <xsl:for-each select="$schema/xsd:complexType[@name=$rowtypename]/xsd:sequence/xsd:element/@name">
              <th><xsl:value-of select="."/></th>
            </xsl:for-each>
          </tr>

          <xsl:for-each select="row">
            <tr>
              <xsl:for-each select="*">
                <td><xsl:value-of select="."/></td>
              </xsl:for-each>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

Расширенные XML функции

Расширенные XML функции в QHB это функции, представляющие дополнительные операции над данными, представленными в виде XML документов.

xml_append_child

xml_append_child ( XMLType_instance xml, XPath_string text, value_expr xml [, namespace_string text])

xml_append_child добавляет введенное пользователем значение к целевому XML в качестве дочернего элемента узла, указанного выражением XPath:

XMLType_instance — это экземпляр XMLType.

XPath_string — это выражение Xpath, указывающее один или несколько узлов, к которым должны быть присоединены один или несколько дочерних узлов. Вы можете указать абсолютную строку XPath_string с начальной косой чертой или относительную строку XPath_string, опуская начальную косую черту. Если вы опускаете начальную косую черту, контекст относительного пути по умолчанию соответствует корневому узлу.

value_expr указывает один или несколько узлов XML. Он должен разрешаться в строку.

Необязательный параметр namespace_string предоставляет информацию о пространстве имен для XPath_string. Этот параметр должен иметь тип text.

Пример:

select xml_append_child(
  '<body>
    <wow id="1">
      <lol/>
    </wow>
    <wow id="2">
      <lol/>
    </wow>
   </body>'::xml,
  '/body/wow/lol'::text,
  '<New>Append</New>'::xml
  )
;

Результат:

<body>
  <wow id="1">
    <lol>
      <New>Append</New>
    </lol>
  </wow>
  <wow id="2">
    <lol>
      <New>Append</New>
    </lol>
  </wow>
</body>

xml_insert_before

xml_insert_before ( XMLType_instance xml, XPath_string text, value_expr xml [, namespace_string text] )

xml_insert_before вставляет введенное пользователем значение в целевой XML перед узлом, указанным выражением XPath. Эта функция похожа на xml_insert_after, но вставляет до, а не после целевого узла.

XMLType_instance — это экземпляр XMLType.

XPath_string — это выражение Xpath, указывающее один или несколько узлов, в которые необходимо вставить один или несколько дочерних узлов. Вы можете указать абсолютную строку XPath_string с начальной косой чертой или относительную строку XPath_string, опуская начальную косую черту. Если вы опускаете начальную косую черту, контекст относительного пути по умолчанию соответствует корневому узлу.

value_expr — это фрагмент XML, определяющий один или несколько вставляемых узлов и их положение в родительском узле. Он должен разрешаться в строку.

Необязательный параметр namespace_string предоставляет информацию о пространстве имен для XPath_string. Этот параметр должен иметь тип text.

Пример:

select xml_insert_before(
  '<body><wow id="1"><lol/></wow><wow id="2"><lol/></wow></body>'::xml,
  '/body/wow[2]'::text,
  '<wow id="BEFORE New one">WOOOW</wow>'::xml
  )
;

Результат:

<body>
  <wow id="1">
    <lol/>
  </wow>
  <wow id="BEFORE New one">WOOOW</wow>
  <wow id="2">
    <lol/>
  </wow>
</body>

xml_insert_after

xml_insert_after ( XMLType_instance xml, XPath_string text, value_expr xml [, namespace_string text] )

xml_insert_after вставляет один или несколько узлов любого типа сразу после целевого узла, который не является узлом атрибута. Эта функция похожа на xml_insert_before, но вставляет после, а не перед целевым узлом:

XMLType_instance указывает целевой узел вставки.

XPath_string — это выражение XPath 1.0, которое находит в целевом узле ноль или более узлов любого типа, кроме узлов атрибутов. XML-данные вставляются сразу после каждого из этих узлов; то есть каждый указанный узел становится предшествующим одноуровневым узлом узла, указанного в value_expr.

value_expr — это XML-данные, которые необходимо вставить. Вы можете указать один или несколько узлов любого типа. Порядок узлов сохраняется после вставки.

Необязательная строка namespace_string — это пространство имен для целевого узла.

Пример:

select xml_insert_after(
  '<body>
    <wow id="1">
     <lol/>
    </wow>
    <wow id="2">
     <lol/>
    </wow>
  </body>'::xml,
  '/body/wow[2]'::text,
  '<wow id="BEFORE new one">WOOOW</wow>'::xml
  )
;

Результат:

<body>
  <wow id="1">
    <lol/>
  </wow>
  <wow id="2">
    <lol/>
  </wow>
  <wow id="BEFORE new one">WOOOW</wow>
</body>

xml_insert_child_after

xml_insert_child_after ( XMLType_instance xml, XPath_string text, XPathChild_string text, value_expr xml [, namespace_string text] )

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

XMLType_instance указывает целевой узел вставки.

XPath_string — это выражение XPath 1.0, которое находит в целевом узле ноль или более узлов любого типа, кроме узлов атрибутов. XML-данные вставляются в содержимое целевого узла, после узла задаваемого выражением XPathChild_string

XPathChild_string - это выражение XPath 1.0, которое находит в содержимом целевого узла ноль или более узлов после которых будет вставлено value_expr

value_expr — это XML-данные, которые необходимо вставить. Вы можете указать один или несколько узлов любого типа. Порядок узлов сохраняется после вставки.

Необязательная строка namespace_string — это пространство имен для целевого и дочернего узла.

Пример:

select xml_insert_child_after(
  '<books xmlns=\"http://example.com/\">\
            <shelf>\
                <book>Book A</book>\
                <book>Book B</book>\
            </shelf>\
            <shelf>\
                <book>Book C</book>\
                <book>Book D</book>\
            </shelf>\
        </books>'::xml,
  '/books/shelf'::text,
  'book[2]'::text,
  '<book>Third book in a shelf</book>'::xml,
  'xmlns="http://example.com/"'::text
  )
;

Результат:

<books xmlns="http://example.com/">
  <shelf>
    <book>Book A</book>
    <book>Book B</book>
    <book>Third book in a shelf</book>
  </shelf>
  <shelf>
    <book>Book C</book>
    <book>Book D</book>
    <book>Third book in a shelf</book>
  </shelf>
</books>

xml_update

xml_update(XMLType_instance,Update_pair[],[, namespace_string ])

xml_update принимает в качестве аргументов экземпляр XMLType и массив пар XPath-значение: value_expr и возвращает экземпляр XMLType с обновленными значениями для каждой пары. Если XPath_string является элементом XML, то соответствующее value_expr должно быть экземпляром XMLType. Если XPath_string является атрибутом или текстовым узлом, то value_expr может быть любым скалярным типом данных. Можно указать абсолютную строку XPath_string с начальной косой чертой или относительную строку XPath_string, опуская начальную косую черту. Если вы опускаете начальную косую черту, контекст относительного пути по умолчанию соответствует корневому узлу.

Типы данных цели каждой строки XPath_string и соответствующего ей value_expr должны совпадать. Необязательная строка namespace_string должна разрешаться в значение символьного типа, указывающее сопоставление по умолчанию или сопоставление пространства имен для префиксов, которые база данных QHB использует при оценке выражений XPath.

Если вы обновите элемент XML до нуля, QHB удалит атрибуты и дочерние элементы элемента, и элемент станет пустым. Если вы обновите текстовый узел элемента до нуля, QHB удалит текстовое значение элемента, а сам элемент останется, но будет пустым.

Эта функция материализует XML-документ в памяти и обновляет значение. Для использования в операторе UPDATE для модификации содержимого в столбце таблицы необходимо использовать результат функции в выражении UPDATE ... SET.

Пример:

select xml_update(
  '
    <body>
     <wow id="1">WOW1</wow>
     <wow id="2">WOW2</wow>
     <foo id="1">FOO1</foo>
     <foo id="2">FOO2</foo>
    </body>'::xml,
  '{"/body/wow[1]","<NewNode>I am new</NewNode>","/body/foo[2]","<NewNode>Another new node</NewNode>"}'
  )

Результат:

<body>
  <NewNode>I am new</NewNode>
  <wow id="2">WOW2</wow>
  <foo id="1">FOO1</foo>
  <NewNode>Another new node</NewNode>
</body>

xml_delete

xml_delete ( XMLType_instance, XPath_string [, namespace_string ])

xml_delete удаляет узел или узлы, соответствующие выражению XPath в целевом XML.

XMLType_instance — это экземпляр XMLType.

XPath_string — это выражение Xpath, указывающее один или несколько узлов, которые необходимо удалить. Вы можете указать абсолютную строку XPath_string с начальной косой чертой или относительную строку XPath_string, опуская начальную косую черту. Если вы опускаете начальную косую черту, контекст относительного пути по умолчанию соответствует корневому узлу. Любые дочерние узлы узлов, указанных в XPath_string, также удаляются.

Необязательный параметр namespace_string предоставляет информацию о пространстве имен для XPath_string. Этот параметр должен иметь тип text.

Пример:

select xml_delete(
  '<body>
    <wow id="1">WOW1</wow>
    <wow id="2">WOW2</wow>
   </body>'::xml,
  '/body/wow[2]'::text
  )
;

Результат:

<body>
  <wow id="1">WOW1</wow>
</body>

xml_extract_value

xml_ ( XMLType_instance, XPath_string [, namespace_string ])

xml_extract_value возвращает скалярное значение, соответствующие выражению XPath в целевом XML, если выражение XPath соответствует несколько узлов функция завершится с ошибкой.

XMLType_instance — это экземпляр XMLType.

XPath_string — это выражение Xpath, указывающее один или несколько узлов, для поиска. Вы можете указать абсолютную строку XPath_string с начальной косой чертой или относительную строку XPath_string, опуская начальную косую черту. Если вы опускаете начальную косую черту, контекст относительного пути по умолчанию соответствует корневому узлу.

Необязательный параметр namespace_string предоставляет информацию о пространстве имен для XPath_string. Этот параметр должен иметь тип text.

Пример:

select xml_extract_value(
  '<books>\
            <shelf>\
                <book>Book A</book>\
                <book>Book B</book>\
            </shelf>\
            <shelf>\
                <book>Book C</book>\
                <book>Book D</book>\
                <book>Book D</book>\
            </shelf>\
        </books>"'::xml,
  '/books/shelf/book[3]'::text
  )
;

Результат:

Book D

xml_extract

xml_ ( XMLType_instance, XPath_string [, namespace_string ])

xml_extract возвращает один или несколько узлов, соответствующих выражению XPath в целевом XML.

XMLType_instance — это экземпляр XMLType.

XPath_string — это выражение Xpath, указывающее один или несколько узлов, которые необходимо добавить в возвращаемый документ. Вы можете указать абсолютную строку XPath_string с начальной косой чертой или относительную строку XPath_string, опуская начальную косую черту. Если вы опускаете начальную косую черту, контекст относительного пути по умолчанию соответствует корневому узлу.

Необязательный параметр namespace_string предоставляет информацию о пространстве имен для XPath_string. Этот параметр должен иметь тип text.

Пример:

select xml_extract(
  '<books>\
            <shelf>\
                <book>Book A</book>\
                <book>Book B</book>\
            </shelf>\
            <shelf>\
                <book>Book C</book>\
                <book>Book D</book>\
            </shelf>\
        </books>"'::xml,
  '/books/shelf/book'::text
  )
;

Результат:

<book>Book A</book>
<book>Book B</book>
<book>Book C</book>
<book>Book D</book>

xml_exists_node

xml_ ( XMLType_instance, XPath_string [, namespace_string ])

xml_exists_node проверяет существует ли узел или узлы, соответствующие выражению XPath в целевом XML, возвращает 1 если хотя бы один узел найден и 0 в другом случае.

XMLType_instance — это экземпляр XMLType.

XPath_string — это выражение Xpath, указывающее один или несколько узлов, которые необходимо удалить. Вы можете указать абсолютную строку XPath_string с начальной косой чертой или относительную строку XPath_string, опуская начальную косую черту. Если вы опускаете начальную косую черту, контекст относительного пути по умолчанию соответствует корневому узлу.

Необязательный параметр namespace_string предоставляет информацию о пространстве имен для XPath_string. Этот параметр должен иметь тип text.

Пример:

select xml_exists_node(
  '<books>\
            <shelf>\
                <book1>Book A</book1>\
                <book2>Book B</book2>\
            </shelf>\
            <shelf>\
                <book1>Book C</book1>\
                <book2>Book D</book2>\
                <book3>Book D</book3>\
            </shelf>\
        </books>"'::xml,
  '/books/shelf/book3'::text
  )
;

Результат:

1

Функции и операторы JSON

В этом разделе описываются:

функции и операторы для обработки и создания данных JSON

язык путей SQL/JSON

Чтобы узнать больше о стандарте SQL/JSON, обратитесь к стандарту ISO/IEC TR 19075-6. Подробную информацию о типах JSON, поддерживаемых в QHB, см. в разделе Типы JSON.

Обработка и создание данных JSON

В Таблице 44 приведены операторы, доступные для использования с типами данных JSON (см. раздел Типы JSON). Кроме того, для типа jsonb (но не для json) доступны обычные операторы сравнения, приведенные в Таблице 1. Эти операторы сравнения следуют правилам упорядочивания для операций B-дерева, изложенным в подразделе Индексация jsonb.

Таблица 44. Операторы для типов json и jsonb

Оператор

Описание

Пример(ы)

json -> integer → json

jsonb -> integer → jsonb

Извлекает n-й элемент массива JSON (элементы массива индексируются с нуля, но отрицательные числа считаются с конца).

'[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json -> 2 → {"c":"baz"}

'[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json -> -3 → {"a":"foo"}

json -> text → json

jsonb -> text → jsonb

Извлекает поле объекта JSON по заданному ключу.

'{"a": {"b":"foo"}}'::json -> 'a' → {"b":"foo"}

json ->> integer → text

jsonb ->> integer → text

Извлекает n-й элемент массива JSON в виде значения типа text.

'[1,2,3]'::json ->> 2 → 3

json ->> text → text

jsonb ->> text → text

Извлекает поле объекта JSON по заданному ключу в виде значения типа text.

'{"a":1,"b":2}'::json ->> 'b' → 2

json #> text[] → json

jsonb #> text[] → jsonb

Извлекает дочерний объект JSON по заданному пути, где элементами пути могут быть ключи полей или индексы массивов.

'{"a": {"b": ["foo","bar"]}}'::json #> '{a,b,1}' → "bar"

json #>> text[] → text

jsonb #>> text[] → text

Извлекает дочерний объект JSON по заданному пути в виде значения типа text.

'{"a": {"b": ["foo","bar"]}}'::json #>> '{a,b,1}' → bar

Примечание
Если структура входных данных JSON не соответствует запросу, например, если заданного ключа или элемента массива не существует, операторы извлечения поля/элемента/пути не выдают ошибку, а возвращают NULL.

Некоторые из следующих операторов существуют только для jsonb, как показано в Таблице 45. В подразделе Индексация jsonb описывается, как эти операторы можно использовать для эффективного поиска индексированных данных jsonb.

Таблица 45. Дополнительные операторы jsonb

Оператор

Описание

Пример(ы)

jsonb @> jsonb → boolean

В первое значение JSON вложено второе? (Подробную информацию о вложении см. в подразделе Проверка на вложение и существование в jsonb.)

'{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb → t

jsonb <@ jsonb → boolean

Первое значение JSON вложено во второе?

'{"b":2}'::jsonb <@ '{"a":1, "b":2}'::jsonb → t

jsonb ? text → boolean

Текстовая строка существует в значении JSON в качестве ключа верхнего уровня или элемента массива?

'{"a":1, "b":2}'::jsonb ? 'b' → t
'["a", "b", "c"]'::jsonb ? 'b' → t

jsonb ?| text[] → boolean

Какие-либо строки в текстовом массиве существуют в качестве ключей верхнего уровня или элементов массива?

'{"a":1, "b":2, "c":3}'::jsonb ?| array['b', 'd'] → t

jsonb ?& text[] → boolean

Все строки в текстовом массиве существуют в качестве ключей верхнего уровня или элементов массива?

'["a", "b", "c"]'::jsonb ?& array['a', 'b'] → t

jsonb || jsonb → jsonb

Конкатенирует два значения jsonb. Конкатенация двух массивов генерирует массив, содержащий все элементы каждого аргумента. Конкатенация двух объектов генерирует объект, содержащий объединение их ключей, при этом при наличии дубликатов ключей выбирается значение из второго объекта. Все другие случаи обрабатываются путем преобразования аргументов, отличных от массивов, в массивы с одним элементом, а затем конкатенируя их как два массива. Не работает рекурсивно: объединяются только массивы или структуры объекта на верхнем уровне.

'["a", "b"]'::jsonb || '["a", "d"]'::jsonb → ["a", "b", "a", "d"]

'{"a": "b"}'::jsonb || '{"c": "d"}'::jsonb → {"a": "b", "c": "d"}

'[1, 2]'::jsonb || '3'::jsonb → [1, 2, 3]

'{"a": "b"}'::jsonb || '42'::jsonb → [{"a": "b"}, 42]

Чтобы вставить один массив в другой за одну запись, оберните его в дополнительный слой массива, например:

'[1, 2]'::jsonb || jsonb_build_array('[3, 4]'::jsonb) → [1, 2, [3, 4]]

jsonb - text → jsonb

Удаляет ключ (и его значение) из объекта JSON или соответствующие строковые значения из массива JSON.

'{"a": "b", "c": "d"}'::jsonb - 'a' → {"c": "d"}

'["a", "b", "c", "b"]'::jsonb - 'b' → ["a", "c"]

jsonb - text[] → jsonb

Удаляет все соответствующие ключи или элементы массива из левого операнда.

'{"a": "b", "c": "d"}'::jsonb - '{a,c}'::text[] → {}

jsonb - integer → jsonb

Удаляет элемент массива с заданным индексом (отрицательные числа считаются с конца). Выдает ошибку, если значение JSON — не массив.

'["a", "b"]'::jsonb - 1 → ["a"]

jsonb #- text[] → jsonb

Удаляет поле или элемент массива по заданному пути, где элементы пути могут быть ключами полей или индексами массивов.

'["a", {"b":1}]'::jsonb #- '{1,b}' → ["a", {}]

jsonb @? jsonpath → boolean

Возвращает ли путь JSON какой-либо элемент для заданного значения JSON?

'{"a":[1,2,3,4,5]}'::jsonb @? '$.a[*] ? (@ > 2)' → t

jsonb @@ jsonpath → boolean

Возвращает результат проверки предиката пути JSON для заданного значения JSON. Учитывается только первый элемент результата. Если результат не является логическим, возвращается NULL.

'{"a":[1,2,3,4,5]}'::jsonb @@ '$.a[*] > 2' → t

Примечание
Операторы jsonpath @? и @@ подавляют следующие ошибки: отсутствие поля объекта или элемента массива, неожиданный тип элемента JSON и ошибки в числах и дате/времени. Это может быть полезно при поиске в коллекциях документов JSON с различной структурой.

В Таблице 46 приведены функции, доступные для создания значений json и jsonb.

Таблица 46. Функции для создания JSON

Функция

Описание

Пример(ы)

to_json ( anyelement ) → json

to_jsonb ( anyelement ) → jsonb

Преобразует любое значение SQL в json или jsonb. Массивы и составные объекты преобразуются рекурсивно в массивы и объекты (многомерные массивы становятся в JSON массивами массивов). В иных случаях, если задано приведение типа данных SQL к json, то для выполнения этого преобразования используется эта функция приведения;[b] для всех остальных выдается скалярное значение JSON. Значения всех скалярных типов, кроме числового, логического и NULL, будут представлены в виде текста, в который при необходимости будет добавлено экранирование символов для получения допустимого строкового значения JSON.

to_json('Fred said "Hi."'::text) → "Fred said \"Hi.\""

to_jsonb(row(42, 'Fred said "Hi."'::text)) → {"f1": 42, "f2": "Fred said \"Hi.\""}

array_to_json ( anyarray [, boolean ] ) → json

Преобразует массив SQL в массив JSON. Поведение аналогично функции to_json, за исключением того, что если необязательный логический параметр равен true, между элементами массива верхнего уровня добавятся переводы строк.

array_to_json('{{1,5},{99,100}}'::int[]) → [[1,5],[99,100]]

row_to_json ( record [, boolean ] ) → json

Преобразует составное значение SQL в объект JSON. Поведение аналогично функции to_json, за исключением того, что если необязательный логический параметр равен true, между элементами верхнего уровня добавятся переводы строк.

row_to_json(row(1,'foo')) → {"f1":1,"f2":"foo"}

json_build_array ( VARIADIC "any" ) → json

jsonb_build_array ( VARIADIC "any" ) → jsonb

Формирует массив JSON (возможно, неоднородный по типам) из переменного списка аргументов. Каждый аргумент преобразуется методом to_json или to_jsonb.

json_build_array(1, 2, 'foo', 4, 5) → [1, 2, "foo", 4, 5]

json_build_object ( VARIADIC "any" ) → json

jsonb_build_object ( VARIADIC "any" ) → jsonb

Формирует объект JSON из переменного списка аргументов. По соглашению, в списке аргументов по очереди перечисляются ключи и значения. Аргументы-ключи приводятся к текстовому типу; аргументы-значения преобразуются методом to_json или to_jsonb.

json_build_object('foo', 1, 2, row(3,'bar')) → {"foo" : 1, "2" : {"f1":3,"f2":"bar"}}

json_object ( text[] ) → json

jsonb_object ( text[] ) → jsonb

Формирует объект JSON из текстового массива. Этот массив должен иметь либо ровно одну размерность с четным числом членов (в этом случае они воспринимаются как чередующиеся пары ключ/значение), либо две размерности, и при этом каждый внутренний массив содержит ровно два элемента, которые воспринимаются как пара ключ/значение. Все значения преобразуются в строки JSON.

json_object('{a, 1, b, "def", c, 3.5}') → {"a" : "1", "b" : "def", "c" : "3.5"}

json_object('{{a, 1}, {b, "def"}, {c, 3.5}}') → {"a" : "1", "b" : "def", "c" : "3.5"}

json_object ( ключи text[], значения text[] ) → json

jsonb_object ( ключи text[], значения text[] ) → jsonb

Эта форма json_object принимает ключи и значения по парам из отдельных текстовых массивов. В остальном она идентична форме с одним аргументом.

json_object('{a,b}', '{1,2}') → {"a": "1", "b": "2"}

[b] Например, в расширении hstore определено приведение типа hstore к json, поэтому значения hstore, преобразованные посредством функций создания JSON, будут представлены в виде объектов JSON, а не примитивных строковых значений.

В Таблице 47 показаны функции, доступные для обработки значений json и jsonb.

Таблица 47. Функции для обработки JSON

Функция

Описание

Пример(ы)

json_array_elements ( json ) → setof json

jsonb_array_elements ( jsonb ) → setof jsonb

Разворачивает массив JSON верхнего уровня в набор значений JSON.

    select * from json_array_elements('[1,true, [2,false]]') →
       value
     -----------
     1
     true
     [2,false]

json_array_elements_text ( json ) → setof text

jsonb_array_elements_text ( jsonb ) → setof text

Разворачивает массив JSON верхнего уровня в набор значений text.

    select * from json_array_elements_text('["foo", "bar"]') →
       value
    -----------
    foo
    bar

json_array_length ( json ) → integer

jsonb_array_length ( jsonb ) → integer

Возвращает количество элементов в массиве JSON верхнего уровня.

json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]') → 5

json_each ( json ) → setof record ( ключ text, значение json )

jsonb_each ( jsonb ) → setof record ( ключ text, значение jsonb )

Разворачивает объект JSON верхнего уровня в набор пар ключ/значение (key/value).

    select * from json_each('{"a":"foo", "b":"bar"}') →
     key | value
    -----+-------
     a   | "foo"
     b   | "bar"

json_each_text ( json ) → setof record ( ключ text, значение text )

jsonb_each_text ( jsonb ) → setof record ( ключ text, значение text )

Разворачивает объект JSON верхнего уровня в набор пар ключ/значение (key/value). Возвращаемые значения будут иметь тип text.

    select * from json_each_text('{"a":"foo", "b":"bar"}') →
     key | value
    -----+-------
     a   | foo
     b   | bar

json_extract_path ( целевой_объект_json json, VARIADIC элементы_пути text[] ) → json

jsonb_extract_path ( целевой_объект_json jsonb, VARIADIC элементы_пути text[] ) → jsonb

Извлекает подобъект JSON по заданному пути. (Функционально это равнозначно действию оператора #>, но в некоторых случаях может быть удобнее записать путь в виде списка с переменным числом аргументов.)

json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4', 'f6') → "foo"

json_extract_path_text ( целевой_объект_json json, VARIADIC элементы_пути text[] ) → text

jsonb_extract_path_text ( целевой_объект_json jsonb, VARIADIC элементы_пути text[] ) → text

Извлекает подобъект JSON по заданному пути в виде значения text. (Функционально это равнозначно действию оператора #>>.)

json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4', 'f6') → foo

json_object_keys ( json ) → setof text

jsonb_object_keys ( jsonb ) → setof text

Возвращает набор ключей в объекте JSON верхнего уровня.

    select * from json_object_keys('{"f1":"abc","f2":{"f3":"a", "f4":"b"}}') →
     json_object_keys
    ------------------
     f1
     f2

json_populate_record ( заполнение anyelement, целевой_объект_json json ) → anyelement

jsonb_populate_record ( заполнение anyelement, целевой_объект_json jsonb ) → anyelement

Разворачивает объект JSON верхнего уровня в строку, имеющую составной тип аргумента заполнение. Объект JSON сканируется на наличие полей, чьи имена соответствуют именам столбцов типа выходной строки, и их значения вставляются в эти столбцы вывода. (Поля, не соответствующие именам никаких выходных столбцов, игнорируются.) Как правило, значение заполнения просто равно NULL, что означает, что выходные столбцы не соответствующие полям объекта, будут заполнены значениями NULL. Однако если заполнение отлично от NULL, то для заполнения таких столбцов будут использованы содержащиеся в нем значения.

Для преобразования значения JSON в SQL-тип выходного столбца последовательно применяются следующие правила:

Значение NULL в JSON в любом случае преобразуется в значение NULL в SQL.

Если выходной столбец имеет тип json или jsonb, значение JSON воспроизводится без изменений.

Если выходной столбец имеет составной тип (тип строки) и значение JSON является объектом JSON, поля этого объекта преобразуются в столбцы типа выходной строки посредством рекурсивного применения этих правил.

Аналогично если выходной столбец имеет тип массива и значение JSON является массивом JSON, элементы этого массива JSON преобразуются в столбцы типа выходной строки посредством рекурсивного применения этих правил.

Иначе, если значение JSON является строкой, содержимое этой строки передается входной функции преобразования для типа данных целевого столбца.

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

Хотя в следующем примере используется неизменное значение JSON, обычно выполняется обращение (с помощью LATERAL) к столбцу json или jsonb из другой таблицы, находящейся в предложении FROM того же запроса. Включение функции json_populate_record в предложение FROM — удачный прием, поскольку все извлекаемые столбцы доступны для использования без необходимости в повторных вызовах функции.

    create type subrowtype as (d int, e text); create type myrowtype as (a int, b text[],
    c subrowtype);
    select * from json_populate_record(null::myrowtype, '{"a": 1, "b": ["2", "a b"],
    "c": {"d": 4, "e": "a b c"}, "x": "foo"}') →
     a |   b       |      c
    ---+-----------+-------------
     1 | {2,"a b"} | (4,"a b c")

json_populate_recordset ( заполнение anyelement, целевой_объект_json json ) → setof anyelement

jsonb_populate_recordset ( заполнение anyelement, целевой_объект_json jsonb ) → setof anyelement

Разворачивает массив JSON верхнего уровня с объектами в набор строк, имеющих составной тип аргумента заполнение. Каждый элемент массива JSON обрабатывается так же, как описано выше для json[b]_populate_record.

    create type twoints as (a int, b int);
    select * from json_populate_recordset(null::twoints, '[{"a":1,"b":2}, {"a":3,
    "b":4}]') →
     a | b
    ---+---
     1 | 2
     3 | 4

json_to_record ( json ) → record

jsonb_to_record ( jsonb ) → record

Разворачивает объект JSON верхнего уровня в строку, имеющую составной тип, определенный в предложении AS. (Как и со всеми функциями, возвращающими значение record, вызывающий запрос должен явно определить структуру записи в предложении AS.) Выходная запись заполняется значениями полей объекта JSON так же, как описано выше для json[b]_populate_record. Поскольку этой функции не передается значение записи, несоответствующие столбцы всегда заполняются значениями NULL.

    create type myrowtype as (a int, b text);
    select * from json_to_record('{"a":1,"b":[1,2,3],"c":[1,2,3],"e":"bar","r": {"a":
    123, "b": "a b c"}}') as x(a int, b text, c int[], d text, r myrowtype) →
     a |    b    |    c    | d |       r
    ---+---------+---------+---+---------------
     1 | [1,2,3] | {1,2,3} |   | (123,"a b c")

json_to_recordset ( json ) → setof record

jsonb_to_recordset ( jsonb ) → setof record

Разворачивает массив JSON верхнего уровня с объектами в набор строк, имеющих составной тип, определенный в предложении AS. (Как и со всеми функциями, возвращающими значение record, вызывающий запрос должен явно определить структуру записи в предложении AS.) Каждый элемент массива JSON обрабатывается так же, как описано выше для json[b]_populate_record.

    select * from json_to_recordset('[{"a":1,"b":"foo"}, {"a":"2","c":"bar"}]') as x(a
    int, b text) →
     a |  b
    ---+-----
     1 | foo
     2 |

jsonb_set ( цель jsonb, путь text[], новое_значение jsonb [, создать_если_отсутствует boolean ] ) → jsonb

Возвращает цель, в которой элемент, на который указывает путь, заменяется новым_значением либо новое_значение добавляется, если создать_если_отсутствует равен true (это значение по умолчанию) и элемент, на который указывает путь, не существует. Все предыдущие звенья пути должны существовать, иначе цель будет возвращена без изменений. Как и с операторами, ориентированными на пути, отрицательные целые числа, находящиеся в пути, считают элементы с конца массивов JSON. Если последнее звено пути является индексом, выходящим за границы массива, и параметр создать_если_отсутствует равен true, новое значение добавляется в начало массива, когда индекс отрицательный, или в его конец, когда индекс положительный.

jsonb_set('[{"f1":1,"f2":null},2,null,3]', '{0,f1}', '[2,3,4]', false) → [{"f1": [2, 3, 4], "f2": null}, 2, null, 3]

jsonb_set('[{"f1":1,"f2":null},2]', '{0,f3}', '[2,3,4]') → [{"f1": 1, "f2": null, "f3": [2, 3, 4]}, 2]

jsonb_set_lax ( цель jsonb, путь text[], новое_значение jsonb [, создать_если_отсутствует boolean [, обращение_с_null text ]] ) → jsonb

Если новое_значение отлично от NULL, эта функция ведет себя идентично jsonb_set. В противном случае она действует согласно аргументу обращение_с_null, который может принимать значения 'raise_exception' (сгенерировать исключение), 'use_json_null' (использовать NULL в JSON), 'delete_key' (удалить ключ) или 'return_target' (вернуть цель). Значение по умолчанию — 'use_json_null'.

jsonb_set_lax('[{"f1":1,"f2":null},2,null,3]', '{0,f1}', null) → [{"f1":null,"f2":null},2,null,3]

jsonb_set_lax('[{"f1":99,"f2":null},2]', '{0,f3}', null, true, 'return_target') → [{"f1": 99, "f2": null}, 2]

jsonb_insert ( цель jsonb, путь text[], новое_значение jsonb [, вставить_после boolean ] ) → jsonb

Возвращает цель с вставленным в нее новым_значением. Если элемент, на который указывает путь, является элементом массива, новое_значение будет вставлено перед этим элементом, если параметр вставить_после равен false (это значение по умолчанию), или после него, если вставить_после равен true. Если элемент, на который указывает путь, является полем объекта, новое_значение будет вставлено, только если объект еще не содержит этот ключ. Все предыдущие звенья пути должны существовать, иначе цель будет возвращена без изменений. Как и с операторами, ориентированными на пути, отрицательные целые числа, находящиеся в пути, считают элементы с конца массивов JSON. Если последнее звено пути является индексом, выходящим за границы массива, новое значение добавляется в начало массива, когда индекс отрицательный, или в его конец, когда тот положительный.

jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"') → {"a": [0, "new_value", 1, 2]}

jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"', true) → {"a": [0, 1, "new_value", 2]}

json_strip_nulls ( json ) → json

jsonb_strip_nulls ( jsonb ) → jsonb

Рекурсивно удаляет из заданного значения JSON все поля объектов, имеющие значения NULL. Значения NULL, не относящиеся к полям объектов, не затрагиваются.

json_strip_nulls('[{"f1":1, "f2":null}, 2, null, 3]') → [{"f1":1},2,null,3]

jsonb_path_exists ( цель jsonb, путь jsonpath [, переменные jsonb [, молча boolean ]] ) → boolean

Проверяет, возвращает ли путь JSON какой-либо элемент для заданного значения JSON. Если задается аргумент переменные, он должен быть объектом JSON, и его поля предоставляют именованные значения, подставляемые в выражение jsonpath. Если задается аргумент молча и он равен true, эта функция подавляет те же ошибки, что и операторы @? и @@.

jsonb_path_exists('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <= $max)', '{"min":2, "max":4}') → t

jsonb_path_match ( цель jsonb, путь jsonpath [, переменные jsonb [, молча boolean ]] ) → boolean

Возвращает результат проверки предиката пути JSON для заданного значения JSON. Учитывается только первый элемент результата. Если результат не является логическим, возвращается NULL. Необязательные аргументы переменные и молча действуют так же, как и для jsonb_path_exists.

jsonb_path_match('{"a":[1,2,3,4,5]}', 'exists($.a[*] ? (@ >= $min && @ <= $max))', '{"min":2, "max":4}') → t

jsonb_path_query ( цель jsonb, путь jsonpath [, переменные jsonb [, молча boolean ]] ) → setof jsonb

Возвращает все элементы JSON, полученные по данному пути JSON для заданного значения JSON. Необязательные аргументы переменные и молча действуют так же, как и для jsonb_path_exists.

    select * from jsonb_path_query('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <=
    $max)', '{"min":2, "max":4}') →
     jsonb_path_query
    ------------------
     2
     3
     4

jsonb_path_query_array ( цель jsonb, путь jsonpath [, переменные jsonb [, молча boolean ]] ) → jsonb

Возвращает все элементы JSON, полученные по данному пути JSON для заданного значения JSON, в виде массива JSON. Необязательные аргументы переменные и молча действуют так же, как и для jsonb_path_exists.

jsonb_path_query_array('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <= $max)', '{"min":2, "max":4}') → [2, 3, 4]

jsonb_path_query_first ( цель jsonb, путь jsonpath [, переменные jsonb [, молча boolean ]] ) → jsonb

Возвращает первый элемент JSON, полученный по данному пути JSON для заданного значения JSON. Если результатов нет, возвращает NULL. Необязательные аргументы переменные и молча действуют так же, как и для jsonb_path_exists.

jsonb_path_query_first('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <= $max)', '{"min":2, "max":4}') → 2

jsonb_path_exists_tz ( цель jsonb, путь jsonpath [, переменные jsonb [, молча boolean ]] ) → boolean

jsonb_path_match_tz ( цель jsonb, путь jsonpath [, переменные jsonb [, молча boolean ]] ) → boolean

jsonb_path_query_tz ( цель jsonb, путь jsonpath [, переменные jsonb [, молча boolean ]] ) → setof jsonb

jsonb_path_query_array_tz ( цель jsonb, путь jsonpath [, переменные jsonb [, молча boolean ]] ) → jsonb

jsonb_path_query_first_tz ( цель jsonb, путь jsonpath [, переменные jsonb [, молча boolean ]] ) → jsonb

Данные функции действуют подобно их двойникам без суффикса _tz, за исключением того, что эти функции поддерживают сравнение значений даты/времени, которое требует преобразований с учетом часовых поясов. В следующем примере требуется интерпретация значения даты без указания времени 2015-08-02 в виде метки времени с часовым поясом, поэтому результат зависит от текущего значения параметра TimeZone. Из-за этой зависимости данные функции помечаются как стабильные, что означает, что их нельзя использовать в индексах. Их двойники являются постоянными, и поэтому могут применяться в индексах, но при запросе на подобные сравнения они будут выдавать ошибку.

jsonb_path_exists_tz('["2015-08-01 12:00:00 -05"]', '$[*] ? (@.datetime() < "2015-08-02".datetime())') → t

jsonb_pretty ( jsonb ) → text

Преобразует заданное значение JSON в структурированный текст с отступами.

jsonb_pretty('[{"f1":1,"f2":null}, 2]') →
    [
        {
            "f1": 1,
            "f2": null
        },
        2
    ]

json_typeof ( json ) → text

jsonb_typeof ( jsonb ) → text

Возвращает тип значения JSON на верхнем уровне в виде текстовой строки. Возможные типы: object, array, string, number, boolean и null. (Не следует путать результат null со значением NULL в SQL; см. примеры.)

json_typeof('-123.4') → number

json_typeof('null'::json) → null

json_typeof(NULL::json) IS NULL → t

См. также раздел Агрегатные функции, где описывается агрегатная функция json_agg, агрегирующая значения записей в виде JSON, и агрегатная функция json_object_agg, агрегирующая пары значений в объект JSON, а также их аналоги для типа jsonb: jsonb_agg и jsonb_object_agg.

Язык путей SQL/JSON

Выражения пути SQL/JSON задают элементы, которые нужно извлечь из данных JSON аналогично выражениям XPath, используемым для доступа SQL к XML. В QHB выражения пути реализованы как тип данных jsonpath и могут использовать любые элементы, описанные в разделе Тип jsonpath.

Функции и операторы запросов JSON передают предоставленное выражение пути механизму пути для вычисления. Если выражение соответствует запрашиваемым данным JSON, возвращается соответствующий элемент или набор элементов JSON. Выражения пути написаны на языке путей SQL/JSON и могут включать в себя арифметические выражения и функции.

Выражение пути состоит из последовательности элементов, допустимых для типа данных jsonpath. Обычно выражение пути вычисляется слева направо, но при желании изменить порядок операций можно воспользоваться скобками. Если вычисление прошло успешно, создается последовательность элементов JSON и результат вычисления возвращается в функцию запроса JSON, которая завершает заданную обработку.

Для обращения к запрошенному значению JSON (элементу контекста) в выражении пути используется переменная $. За ней могут следовать один или несколько операторов аксессоров, которые опускаются в структуре JSON уровень за уровнем, чтобы извлечь подэлементы содержимого элемента контекста. Каждый последующий оператор имеет дело с результатом предыдущего этапа вычисления.

Например, предположим, у вас есть некоторые данные JSON с GPS-трекера, которые вы хотели бы проанализировать, например:

{
  "track": {
    "segments": [
      {
        "location": [ 47.763, 13.4034 ],
        "start time": "2018-10-14 10:05:14",
        "HR": 73
      },
      {
        "location": [ 47.706, 13.2635 ],
        "start time": "2018-10-14 10:39:21",
        "HR": 135
      }
    ]
  }
}

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

$.track.segments

Для извлечения содержимого массива обычно применяется оператор [*]. Например, следующий путь вернет координаты местоположения для всех доступных сегментов треков:

$.track.segments[*].location

Чтобы вернуть координаты только первого сегмента, можно указать соответствующий нижний индекс в операторе аксессора []. Обратите внимание, что индексы массивов JSON считаются с 0:

$.track.segments[0].location

Результат каждого этапа вычисления пути может обрабатываться одним или несколькими операторами и методами jsonpath, перечисленными в разделе Операторы и методы пути SQL/JSON. Каждому имени метода должна предшествовать точка. Например, так можно получить размер массива:

$.track.segments.size()

Дополнительные примеры использования операторов и методов jsonpath в выражениях пути см. ниже в подразделе Операторы и методы пути SQL/JSON.

При определении пути также можно использовать одно или несколько выражений фильтра, которые работают аналогично предложению WHERE в SQL. Выражение фильтра начинается со знака вопроса и содержит условие в скобках:

? (условие)

Выражения фильтра должны быть указаны сразу после того этапа вычисления пути, к которому они должны применяться. Результат этого этапа фильтруется, чтобы содержать только те элементы, которые удовлетворяют предоставленному условию. В SQL/JSON определена трехзначная логику, поэтому условие может быть true, false или unknown. Значение unknown играет ту же роль, что и NULL в SQL, и его можно проверить с помощью предиката unknown. На дальнейших этапах вычисления пути задействуются только те элементы, для которых выражение фильтра вернуло true.

Функции и операторы, которые можно использовать в выражениях фильтра, перечислены в Таблице 49. Значение, подлежащее фильтрации (т. е. результат предыдущего этапа пути), обозначается в выражении фильтра переменной @. Для получения элементов этого компонента можно написать после @ операторы аксессоров.

Например, предположим, вы хотите получить все значения частоты сердцебиений, превышающие 130. Это можно сделать с помощью следующего выражения:

$.track.segments[*].HR ? (@ > 130)

Чтобы получить время начала сегментов с такими значениями, следует перед возвратом времени начала отфильтровать ненужные сегменты, поэтому выражение фильтра применяется к предыдущему этапу, а путь, используемый в условии, отличается:

$.track.segments[*] ? (@.HR > 130)."start time"

При необходимости можно последовательно использовать несколько выражений фильтра. Например, следующее выражение выбирает время начала всех сегментов, содержащих местоположения с соответствующими координатами и высокими значениями частоты сердцебиений:

$.track.segments[*] ? (@.location[1] < 13.4) ? (@.HR > 130)."start time"

Также допускается использование выражений фильтра на разных уровнях вложенности. В следующем примере сначала все сегменты фильтруются по местоположению, а затем для подходящих сегментов, если таковые имеются, возвращаются высокие значения частоты сердцебиений:

$.track.segments[*] ? (@.location[1] < 13.4).HR ? (@ > 130)

Кроме того, можно вкладывать выражения фильтра друг в друга:

$.track ? (exists(@.segments[*] ? (@.HR > 130))).segments.size()

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

Реализация языка путей SQL/JSON в QHB имеет следующие отклонения от стандарта SQL/JSON:

Выражение пути может быть логическим предикатом, хотя стандарт SQL/JSON допускает предикаты только в фильтрах. Это необходимо для реализации оператора @@. Например, в QHB допустимо следующее выражение jsonpath:

$.track.segments[*].HR < 70

Существуют небольшие различия в интерпретации шаблонов регулярных выражений, используемых в фильтрах like_regex; эти различия описаны в подразделе Регулярные выражения SQL/JSON.

Строгий и нестрогий режимы

При запросе данных JSON выражение пути может не соответствовать фактической структуре данных JSON. Попытка получить доступ к несуществующему члену объекта или элементу массива приводит к структурной ошибке. Для выражений пути SQL/JSON существует два режима обработки структурных ошибок:

lax (по умолчанию) — нестрогий режим, при котором механизм пути неявно адаптирует запрошенные данные к указанному пути. Все оставшиеся структурные ошибки подавляются и преобразуются в пустые последовательности SQL/JSON.

strict — строгий режим, в котором при возникновении структурной ошибки она выдается как есть.

Нестрогий режим облегчает сопоставление структуры документа JSON и выражения пути, если данные JSON не соответствуют ожидаемой схеме. Если операнд не соответствует требованиям конкретной операции, перед выполнением этой операции он может автоматически оборачиваться в массив SQL/JSON или разворачиваться путем преобразования его элементов в последовательность SQL/JSON. Кроме того, операторы сравнения автоматически разворачивают свои операнды в нестрогом режиме, поэтому массивы SQL/JSON можно сравнивать, не прикладывая дополнительных усилий. Массив размером 1 считается равным своему единственному элементу. Автоматическое разворачивание не выполняется только в тех случаях, когда:

Выражение пути содержит методы type() или size(), которые возвращают тип и количество элементов в массиве соответственно.

Запрашиваемые данные JSON содержат вложенные массивы. В этом случае разворачивается только самый внешний массив, а все внутренние массивы остаются неизменными. Таким образом, на каждом этапе вычисления пути неявное развертывание может опускаться только на один уровень.

Например, при запросе перечисленных выше данных GPS в нестрогом режиме можно абстрагироваться от того факта, что в них хранится массив сегментов:

lax $.track.segments.location

В строгом режиме указанный путь должен точно соответствовать структуре запрашиваемого документа JSON, чтобы вернуть элемент SQL/JSON, поэтому использование этого выражения пути приведет к ошибке. Чтобы получить тот же результат, что и в нестрогом режиме, следует явно развернуть массив segments:

strict $.track.segments\[\*\].location

В нестрогом режиме аксессор .** может вызвать неожиданные результаты. Например, следующий запрос выберет каждое значение HR дважды:

lax $.**.HR

Это происходит из-за того, что аксессор .**выбирает и массив segments, и каждый из его элементов, тогда как аксессор .HR в нестрогом режиме автоматически разворачивает массивы. Чтобы избежать неожиданных результатов, рекомендуется использовать аксессор .** только в строгом режиме. Следующий запрос выбирает каждое значение HR только один раз:

strict $.**.HR

Операторы и методы пути SQL/JSON

В Таблице 48 показаны операторы и методы, доступные в jsonpath. Обратите внимание, что унарные операторы и методы можно применять к нескольким значениям, полученным на предыдущем этапе пути, тогда как бинарные операторы (сложение и т. п.) можно применять только к отдельным значениям.

Таблица 48. Операторы и методы jsonpath

Оператор/Метод

Описание

Пример(ы)

число + число → число

Сложение

jsonb_path_query('[2]', '$[0] + 3') → 5

+ число → число

Унарный плюс (нет операции); в отличие от сложения, он может выполнить проход по нескольким значениям

jsonb_path_query_array('{"x": [2,3,4]}', '+ $.x') → [2, 3, 4]

число - число → число

Вычитание

jsonb_path_query('[2]', '7 - $[0]') → 5

- число → число

Смена знака; в отличие от вычитания, он может выполнить проход по нескольким значениям

jsonb_path_query_array('{"x": [2,3,4]}', '- $.x') → [-2, -3, -4]

число * число → число

Умножение

jsonb_path_query('[4]', '2 * $[0]') → 8

число / число → число

Деление

jsonb_path_query('[8.5]', '$[0] / 2') → 4.2500000000000000

число % число → число

Остаток целочисленного деления (по модулю)

jsonb_path_query('[32]', '$[0] % 10') → 2

значение . type() → строка

Тип элемента JSON (см. json_typeof)

jsonb_path_query_array('[1, "2", {}]', '$[*].type()') → ["number", "string", "object"]

значение . size() → число

Размер элемента JSON (число элементов массива или 1, если это не массив)

jsonb_path_query('{"m": [11, 15]}', '$.m.size()') → 2

значение . double() → число

Приблизительное число с плавающей запятой, преобразованное из числа JSON или строки

jsonb_path_query('{"len": "1.9"}', '$.len.double() * 2') → 3.8

число . ceiling() → число

Ближайшее целое число, большее или равное заданному числу

jsonb_path_query('{"h": 1.3}', '$.h.ceiling()') → 2

число . floor() → число

Ближайшее целое число, меньшее или равное заданному числу

jsonb_path_query('{"h": 1.7}', '$.h.floor()') → 1

число . abs() → число

Абсолютное значение (модуль) заданного числа

jsonb_path_query('{"z": -0.3}', '$.z.abs()') → 0.3

строка . datetime() → тип_даты_времени (см. примечание)

Значение даты/времени, преобразованное из строки

jsonb_path_query('["2015-8-1", "2015-08-12"]', '$[*] ? (@.datetime() < "2015-08-2".datetime())') → "2015-8-1"

строка . datetime(шаблон) → тип_даты_времени (see note)

Значение даты/времени, преобразованное из строки с помощью заданного шаблона to_timestamp

jsonb_path_query_array('["12:30", "18:40"]', '$[*].datetime("HH24:MI")') → ["12:30:00", "18:40:00"]

объект . keyvalue() → массив

Пары ключ-значение объекта, представленные в виде массива объектов, содержащего три поля: "key" (ключ), "value" (значение) и "id"; "id" представляет собой уникальный идентификатор объекта, которому принадлежит данная пара ключ-значение

jsonb_path_query_array('{"x": "20", "y": 32}', '$.keyvalue()') → [{"id": 0, "key": "x", "value": "20"}, {"id": 0, "key": "y", "value": 32}]

Примечание
Результирующим типом методов datetime() и datetime(шаблон) может быть date, timetz, time, timestamptz или timestamp. Оба метода определяют свой результирующий тип динамически.

Метод datetime() последовательно пытается сопоставить свою входную строку с форматами ISO типов date, timetz, time, timestamptz и timestamp. На первом же подходящем формате он останавливается и выдает соответствующий тип данных.

Метод datetime(шаблон) определяет результирующий тип в соответствии с полями в предоставленной строке шаблона.

Методы datetime() и datetime(шаблон) применяют те же правила синтаксического анализа, что и функция SQL to_timestamp (см. раздел Функции форматирования типов данных), но с тремя исключениями. Во-первых, эти методы не позволяют использовать в шаблоне коды без соответствия. Во-вторых, в строке шаблона допускаются только следующие разделители: знак минуса, точка, косая черта (слэш), запятая, апостроф, точка с запятой, двоеточие и пробел. В-третьих, разделители в строке шаблона должны в точности соответствовать входной строке.

Если нужно сравнить разные типы даты/времени, применяется неявное приведение. Значение date можно привести к типу timestamp или timestamptz, timestamp — к типу timestamptz, а time — к типу timetz. Однако все эти преобразования, кроме первого, зависят от текущего значения TimeZone и поэтому могут выполняться только в функциях jsonpath, учитывающих часовой пояс.

В Таблице 49 приведены доступные элементы выражения фильтра.

Таблица 49. Элементы выражения фильтра jsonpath

Предикат/Значение

Описание

Пример(ы)

значение == значение → boolean

Сравнение равенства (этот и другие операторы сравнения работают со всеми скалярными значениями JSON)

jsonb_path_query_array('[1, "a", 1, 3]', '$[*] ? (@ == 1)') → [1, 1]

jsonb_path_query_array('[1, "a", 1, 3]', '$[*] ? (@ == "a")') → ["a"]

значение != значение → boolean

значение <> значение → boolean

Сравнение неравенства

jsonb_path_query_array('[1, 2, 1, 3]', '$[*] ? (@ != 1)') → [2, 3]

jsonb_path_query_array('["a", "b", "c"]', '$[*] ? (@ <> "b")') → ["a", "c"]

значение < значение → boolean

Сравнение «меньше»

jsonb_path_query_array('[1, 2, 3]', '$[*] ? (@ < 2)') → [1]

значение <= значение → boolean

Сравнение «меньше или равно»

jsonb_path_query_array('["a", "b", "c"]', '$[*] ? (@ <= "b")') → ["a", "b"]

значение > значение → boolean

Сравнение «больше»

jsonb_path_query_array('[1, 2, 3]', '$[*] ? (@ > 2)') → [3]

значение >= значение → boolean

Сравнение «больше или равно»

jsonb_path_query_array('[1, 2, 3]', '$[*] ? (@ >= 2)') → [2, 3]

true → boolean

JSON-константа true

jsonb_path_query('[{"name": "John", "parent": false}, {"name": "Chris", "parent": true}]', '$[*] ? (@.parent == true)') → {"name": "Chris", "parent": true}

false → boolean

JSON-константа false

jsonb_path_query('[{"name": "John", "parent": false}, {"name": "Chris", "parent": true}]', '$[*] ? (@.parent == false)') → {"name": "John", "parent": false}

null → значение

JSON-константа null (обратите внимание, что, в отличие от SQL, сравнение с null работает нормально)

jsonb_path_query('[{"name": "Mary", "job": null}, {"name": "Michael", "job": "driver"}]', '$[*] ? (@.job == null) .name') → "Mary"

логическое_значение && логическое_значение → boolean

Логическое И

jsonb_path_query('[1, 3, 7]', '$[*] ? (@ > 1 && @ < 5)') → 3

логическое_значение || логическое_значение → boolean

Логическое ИЛИ

jsonb_path_query('[1, 3, 7]', '$[*] ? (@ < 1 || @ > 5)') → 7

! логическое_значение → boolean

Логическое НЕ

jsonb_path_query('[1, 3, 7]', '$[*] ? (!(@ < 5))') → 7

логическое_значение is unknown → boolean

Проверяет, является ли unknown логическим условием.

jsonb_path_query('[-1, 2, 7, "foo"]', '$[*] ? ((@ > 0) is unknown)') → "foo"

строка like_regex строка [ flag строка ] → boolean

Проверяет, соответствует ли первый операнд регулярному выражению, заданному во втором операнде, с необязательными модификациями, описываемыми строкой символов flag (см. подраздел Регулярные выражения SQL/JSON).

jsonb_path_query_array('["abc", "abd", "aBdC", "abdacb", "babc"]', '$[*] ? (@ like_regex "^ab.*c")') → ["abc", "abdacb"]

jsonb_path_query_array('["abc", "abd", "aBdC", "abdacb", "babc"]', '$[*] ? (@ like_regex "^ab.*c" flag "i")') → ["abc", "aBdC", "abdacb"]

строка starts with строка → boolean

Проверяет, является ли второй операнд начальной подстрокой первого.

jsonb_path_query('["John Smith", "Mary Stone", "Bob Johnson"]', '$[*] ? (@ starts with "John")') → "John Smith"

exists ( выражение_пути ) → boolean

Проверяет, соответствует ли выражение пути хотя бы одному элементу SQL/JSON. Возвращает unknown, если это выражение пути могло вызвать ошибку; это используется во втором примере, чтобы избежать ошибки «ключ не найден» в строгом режиме.

jsonb_path_query('{"x": [1, 2], "y": [2, 4]}', 'strict $.* ? (exists (@ ? (@[*] > 2)))') → [2, 4]

jsonb_path_query_array('{"value": 41}', 'strict $ ? (exists (@.name)) .name') → []

Регулярные выражения SQL/JSON

Выражения путей SQL/JSON позволяют сопоставлять текст с регулярным выражением при помощи фильтра like_regex. Например, следующий запрос пути SQL/JSON будет соответствовать всем строкам в массиве, которые начинаются с английской гласной, без учета регистра:

$[*] ? (@ like_regex "^[aeiou]" flag "i")

Необязательная строка flag может включать один или несколько следующих символов: i — сопоставления без учета регистра, m — допускается сопоставление ^ и $ с переводами строк, s — допускается сопоставление . с переводом строки, и q — заключить в кавычки весь шаблон (сводя поиск к простому сопоставлению подстроки).

Стандарт SQL/JSON заимствует свое определение для регулярных выражений из оператора LIKE_REGEX, в котором, в свою очередь, используется стандарт XQuery. В настоящее время в QHB оператор LIKE_REGEX не поддерживается. Поэтому фильтр like_regex реализован с использованием механизма регулярных выражений POSIX, описанного в подразделе Регулярные выражения POSIX. Это приводит к различным незначительным несоответствиям стандартному поведению SQL/JSON, которые каталогизированы в подразделе Отличия от XQuery (LIKE_REGEX). Тем не менее, обратите внимание, что описанные там несовместимости флаговых букв не применяются к SQL/JSON, поскольку в SQL/JSON флаговые буквы XQuery переводятся во флаги, подходящие механизму POSIX.

Не забывайте, что аргумент шаблона like_regex является строковым литералом пути JSON, написанным в соответствии с правилами, приведенными в подразделе Тип jsonpath. В частности, это означает, что все обратные слеши в регулярном выражении необходимо дублировать. Например, чтобы сопоставить строковые значения корневого документа, содержащие только цифры, нужно написать следующее:

$.* ? (@ like_regex "^\\d+$")

Функции управления последовательностями

В этом разделе описываются функции для работы с объектами последовательности, также называемыми генераторами последовательностей или просто последовательностями. Объекты последовательности — это специальные однострочные таблицы, созданные с помощью команды CREATE SEQUENCE. Эти объекты обычно используются для генерирования уникальных идентификаторов для строк таблицы. Функции для последовательностей, перечисленные в Таблице 50, предоставляют простые и безопасные для одновременного использования методы получения значений последовательности из таких объектов.

Таблица 50. Функции для последовательностей

Функция

Описание

nextval ( regclass ) → bigint

Продвигает объект последовательности к его следующему значению и возвращает это значение. Это происходит атомарно: даже если nextval выполняется параллельно в нескольких сеансах, в каждом вызове будет беспрепятственно получено отдельное значение последовательности. Если объект последовательности был создан с параметрами по умолчанию, при последующих вызовах nextval будут возвращаться следующие по порядку значения, начиная с 1. Изменить это поведение можно с помощью соответствующих параметров команды CREATE SEQUENCE.
Этой функции требуется право USAGE или UPDATE для последовательности.

setval ( regclass, bigint [, boolean ] ) → bigint

Устанавливает для объекта последовательности текущее значение и при необходимости флаг is_called. Форма с двумя параметрами устанавливает у последовательности заданное значение в поле last_value, а также устанавливает в поле is_called значение true, подразумевая, что при следующем вызове nextval последовательность продвинется к очередному значению, после чего вернет его. При этом currval также выдаст заданное значение. В форме с тремя параметрами в поле is_called можно установить true или false. У значения true будет тот же эффект, что и в форме с двумя параметрами. Если же присвоено значение false, то при первом вызове nextval после этого вернется именно заданное значение, а продвижение последовательности произойдет при следующем вызове nextval. Более того, в этом случае значение, выдаваемое currval, не изменится. Например:

    SELECT setval('myseq', 42);           При следующем вызове nextval вернется 43
    SELECT setval('myseq', 42, true);     То же самое
    SELECT setval('myseq', 42, false);    При следующем вызове nextval вернется 42

Результатом, возвращаемым setval, является просто значение ее второго аргумента.
Этой функции требуется право UPDATE для последовательности.

currval ( regclass ) → bigint

Возвращает значение, полученное при последнем вызове nextval для этой последовательности в текущем сеансе. (Если nextval никогда не вызывалась для этой последовательности в этом сеансе, будет выдана ошибка.) Поскольку возвращаемое значение ограничено рамками сеанса, данная функция выдает предсказуемый ответ, независимо от того, выполнялась ли потом nextval в других сеансах или нет.
Этой функции требуется право USAGE или UPDATE для последовательности.

lastval () → bigint

Возвращает значение, выданное при последнем вызове nextval в текущем сеансе. Эта функция идентична currval, за исключением того, что она не принимает в качестве аргумента имя последовательности, а обращается к той последовательности, к которой в последний раз в текущем сеансе применялась nextval. Если nextval в текущем сеансе еще не вызывалась, то при вызове lastval возникнет ошибка.
Этой функции требуется право USAGE или UPDATE для последней использованной последовательности.

ВНИМАНИЕ!
Во избежание блокировки параллельных транзакций, получающих числа из одной последовательности, значение, полученное функцией nextval, не освобождается обратно для повторного использования, если вызывающая транзакция впоследствии прерывается. Это означает, что прерывание транзакции или сбой базы данных могут привести к появлению пропусков в последовательности задействованных значений. Это может случиться и без прерывания транзакции. Например, команда INSERT с предложением ON CONFLICT вычислит добавляемый кортеж, в том числе произведя все необходимые вызовы nextval, а потом обнаружит какой-нибудь конфликт, из-за которого, возможно, вместо добавления будет следовать правилу ON CONFLICT. Таким образом объекты последовательности QHB нельзя использовать для получения «сплошных» последовательностей.

Аналогично изменения состояния последовательности, произведенные функцией setval, немедленно становятся видимыми для других транзакций и не отменяются при откате транзакции, вызвавшей эту функцию.

Если сбой кластера баз данных происходит до фиксации транзакции, содержащей вызов nextval или setval, изменение состояния последовательности может не попасть в постоянное хранилище, поэтому неизвестно, в каком состоянии будет находиться последовательность после перезапуска кластера: оригинальном или измененном. Это безвредно при использовании последовательности внутри базы данных, поскольку другие эффекты незафиксированных транзакции тоже не будут видны. Однако если вы хотите постоянно использовать значение последовательности вне базы данных, предварительно убедитесь, что вызов nextval был зафиксирован.

Последовательность, которой должна управлять функция для последовательностей, задается аргументом regclass, представляющим собой просто OID последовательности в системном каталоге pg_class. Однако нет необходимости искать этот OID вручную, так как преобразователь вводимого значения типа regclass сделает всю работу за вас. Подробную информацию см. в разделе Типы идентификаторов объектов.

Условные выражения

В этом разделе описываются SQL-совместимые условные выражения, доступные в QHB.

Совет
Если вам требуется нечто, выходящее за пределы возможностей этих условных выражений, как вариант, попробуйте написать серверные функции на более выразительном языке программирования.

Примечание
Хотя COALESCE, GREATEST и LEAST синтаксически похожи на функции, они не являются обычными функциями и поэтому не могут использоваться с аргументами, в которых явно описан массив VARIADIC.

CASE

Выражение CASE в SQL является общим условным выражением, схожим с операторами if/else в других языках программирования:

CASE WHEN условие THEN результат
   [WHEN ...]
   [ELSE результат]
END

Предложения CASE можно использовать везде, где допускаются выражения. Каждое условие здесь — это выражение, возвращающее результат типа boolean. Если результат условия равен true, значением выражения CASE является результат, следующий за условием, а остальная часть выражения CASE не обрабатывается. Если результат условия не равен true, любые последующие предложения WHEN проверяются таким же образом. Если ни одно из условий WHEN не возвращает true, значением выражения CASE является результат предложения ELSE. Если предложение ELSE опущено и ни одно из условий не равно true, результат будет равен NULL.

Пример:

SELECT * FROM test;

 a
---
 1
 2
 3

SELECT a,
       CASE WHEN a=1 THEN 'one'
            WHEN a=2 THEN 'two'
            ELSE 'other'
       END
    FROM test;

 a | case
---+-------
 1 | one
 2 | two
 3 | other

Типы данных всех выражений результатов должны быть преобразованы в один выходной тип.

Существует «простая» форма выражения CASE, которая представляет собой вариант общей формы, описанной выше:

CASE выражение
    WHEN значение THEN результат
  [WHEN ...]
  [ELSE результат]
END

Первое выражение вычисляется, а затем сравнивается с каждым из выражений значение в предложениях WHEN, пока не будет найдено равное ему. Если совпадений не найдено, возвращается результат предложения ELSE (или значение NULL). Эта форма похожа на оператор switch в языке C.

Приведенный выше пример можно записать с простым синтаксисом CASE:

SELECT a,
       CASE a WHEN 1 THEN 'one'
              WHEN 2 THEN 'two'
              ELSE 'other'
       END
    FROM test;

 a | case
---+-------
 1 | one
 2 | two
 3 | other

Выражение CASE не вычисляет подвыражения, которые не нужны для определения результата. Например, таким способом возможно избежать ошибки деления на ноль:

SELECT ... WHERE CASE WHEN x <> 0 THEN y/x > 1.5 ELSE false END;

Примечание
Как описано в разделе Правила вычисления выражений, существуют различные ситуации, в которых подвыражения выражения вычисляются в разное время, поэтому соблюдение принципа «CASE вычисляет только необходимые подвыражения» не гарантировано. Например, константное подвыражение 1/0 обычно приводит к ошибке деления на ноль во время планирования, даже если оно находится в ветви CASE, которая вовсе не задействуется во время выполнения.

COALESCE

COALESCE(значение [, ...])

Функция COALESCE возвращает первый же из своих аргументов, отличный от NULL. NULL возвращается, только если все аргументы равны NULL. Эта функция часто используется для замены значения по умолчанию значениями NULL при извлечении данных для отображения, например:

SELECT COALESCE(description, short_description, '(none)') ...

Этот запрос возвращает description, если оно не равно NULL, либо short_description, если оно не равно NULL, либо (none), если они оба равны NULL.

Все аргументы должны быть приводимыми к одному общему типу данных, который и будет типом результата.

Как и выражение CASE, COALESCE вычисляет только аргументы, которые необходимы для определения результата; то есть аргументы справа от первого отличного от NULL аргумента не вычисляются. Эта соответствующая стандарту SQL функция предоставляет возможности, схожие с таковыми NVL и IFNULL, использующихся в некоторых других СУБД.

NULLIF

NULLIF(значение1, значение2)

Функция NULLIF возвращает значение NULL, если значение1 равно значению2; в противном случае она возвращает значение1. Это можно использовать для выполнения операции, обратной той, что показана в приведенном выше примере COALESCE:

SELECT NULLIF(value, ’(none)’) ...

В этом примере если value равно (none), возвращается NULL, в противном случае возвращается значение value.

Два аргумента этой функции должны быть сравнимых типов. Если говорить конкретнее, они сравниваются точно так же, как в записи значение1 = значение2, поэтому для этих типов должен быть доступен подходящий оператор =.

Результат имеет тот же тип, что и первый аргумент — но есть одна тонкость. Фактически возвращается первый аргумент подразумеваемого оператора =, и в некоторых случаях он преобразуется, чтобы соответствовать типу второго аргумента. К примеру, NULLIF(1, 2.2) выдает numeric, поскольку оператора integer = numeric не существует, есть лишь оператор numeric = numeric.

GREATEST и LEAST

GREATEST(значение [, ...])

LEAST(значение [, ...])

Функции GREATEST и LEAST выбирают наибольшее или наименьшее значение из списка с любым числом выражений. Все выражения должны быть преобразуемыми в общий тип данных, который и будет типом результата. Значения NULL в списке игнорируются. Результатом будет NULL, только если все выражения вычисляются как равные NULL.

Обратите внимание, что GREATEST и LEAST не входят в стандарт SQL, но относятся к распространенным расширениям. В некоторых другие базах данных они возвращают NULL, если любой из аргументов равен NULL, а не только когда все аргументы равны NULL.

Функции и операторы для массивов

В Таблице 51 приведены специальные операторы, доступные для типов массивов. Кроме них для массивов доступны обычные операторы сравнения, перечисленные в Таблице 1. Эти операторы сравнения сопоставляют содержимое массивов поэлементно, используя функцию сравнения для B-дерева, определенную для типа данных этого элемента по умолчанию, и сортируют по принципу первого различия. В многомерных массивах элементы просматриваются в построчном порядке (последний индекс меняется в первую очередь). Если содержимое двух массивов одинаково, но размерности отличаются, порядок сортировки определяется первым отличием в информации о размерности.

Таблица 51. Операторы для массивов

Оператор

Описание

Пример(ы)

anyarray @> anyarray → boolean

Первый массив содержит второй, то есть каждый элемент, находящийся во втором массиве, равен какому-либо элементу первого? (Дубликаты обрабатываются на общих основаниях, поэтому массивы ARRAY[1] и ARRAY[1,1] считаются содержащими друг друга.)

ARRAY[1,4,3] @> ARRAY[3,1,3] → t

anyarray <@ anyarray → boolean

Первый массив содержится во втором?

ARRAY[2,2,7] <@ ARRAY[1,7,4,2,6] → t

anyarray && anyarray → boolean

Массивы пересекаются, то есть имеют какие-либо общие элементы?

ARRAY[1,4,3] && ARRAY[2,1] → t

anycompatiblearray || anycompatiblearray → anycompatiblearray

Конкатенирует два массива. Со значениями NULL или пустыми массивами эта операция не проводится; в иных случаях массивы должны иметь одинаковое число размерностей (как показано в первом примере) или отличаться на одну размерность (как показано во втором примере). Эти эти массивы не одинаковых типов элементов, они будут приведены к общему типу.

ARRAY[1,2,3] || ARRAY[4,5,6,7] → {1,2,3,4,5,6,7}

ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9.9]] → {{1,2,3},{4,5,6},{7,8,9.9}}

anycompatible || anycompatiblearray → anycompatiblearray

Конкатенирует элемент с началом массива (который должен быть пустым или одномерным).

3 || ARRAY[4,5,6] → {3,4,5,6}

anycompatiblearray || anycompatible → anycompatiblearray

Конкатенирует элемент с концом массива (который должен быть пустым или одномерным).

ARRAY[4,5,6] || 7 → {4,5,6,7}

Более подробную информацию о поведении операторов для массивов см. в разделе Массивы. Более подробную информацию о том, какие операторы поддерживают операции с индексами, см. в разделе Типы индексов.

В Таблице 52 приведены функции, доступные для использования с типами массивами. Дополнительную информацию и примеры использования этих функций см. в разделе Массивы.

Таблица 52. Функции для массивов

Функция

Описание

Пример(ы)

array_append ( anycompatiblearray, anycompatible ) → anycompatiblearray

Добавляет элемент в конец массива (так же, как оператор anycompatiblearray || anycompatible).

array_append(ARRAY[1,2], 3) → {1,2,3}

array_cat ( anycompatiblearray, anycompatiblearray ) → anycompatiblearray

Конкатенирует два массива (так же, как оператор anycompatiblearray || anycompatiblearray).

array_cat(ARRAY[1,2,3], ARRAY[4,5]) → {1,2,3,4,5}

array_dims ( anyarray ) → text

Возвращает текстовое представление размерностей массива.

array_dims(ARRAY[[1,2,3], [4,5,6]]) → [1:2][1:3]

array_fill ( anyelement, integer[] [, integer[] ] ) → anyarray

Возвращает массив, заполненный копиями заданного значения и имеющий размерности той длины, которая указана во втором аргументе. Необязательный третий аргумент предоставляет значения нижней границы для каждой размерности (по умолчанию все они равны 1).

array_fill(11, ARRAY[2,3]) → {{11,11,11},{11,11,11}}

array_fill(7, ARRAY[3], ARRAY[2]) → [2:4]={7,7,7}

array_length ( anyarray, integer ) → integer

Возвращает длину запрошенной размерности массива.

array_length(array[1,2,3], 1) → 3

array_lower ( anyarray, integer ) → integer

Возвращает нижнюю границу запрошенной размерности массива.

array_lower('[0:2]={1,2,3}'::integer[], 1) → 0

array_ndims ( anyarray ) → integer

Возвращает количество размерностей массива.

array_ndims(ARRAY[[1,2,3], [4,5,6]]) → 2

array_position ( anycompatiblearray, anycompatible [, integer ] ) → integer

Возвращает индекс первого вхождения второго аргумента в массиве или NULL, если таковой отсутствует. Если задан третий аргумент, поиск начинается с этого индекса. Массив должен быть одномерным. Сравнения производятся с применением семантики IS NOT DISTINCT FROM, что позволяет искать значения NULL.

array_position(ARRAY['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'], 'mon') → 2

array_positions ( anycompatiblearray, anycompatible ) → integer[]

Возвращает массив индексов всех вхождений второго аргумента в массиве, заданном в первом аргументе. Массив должен быть одномерным. Сравнения производятся с применением семантики IS NOT DISTINCT FROM, что позволяет искать значения NULL. NULL возвращается, только массив равен NULL; если заданное значение отсутствует в массиве, возвращается пустой массив.

array_positions(ARRAY['A','A','B','A'], 'A') → {1,2,4}

array_prepend ( anycompatible, anycompatiblearray ) → anycompatiblearray

Добавляет элемент в начало массива (так же, как оператор anycompatible || anycompatiblearray).

array_prepend(1, ARRAY[2,3]) → {1,2,3}

array_remove ( anycompatiblearray, anycompatible ) → anycompatiblearray

Удаляет из массива все элементы, равные заданному значению. Массив должен быть одномерным. Сравнения производятся с применением семантики IS NOT DISTINCT FROM, что позволяет удалять значения NULL.

array_remove(ARRAY[1,2,3,2], 2) → {1,3}

array_replace ( anycompatiblearray, anycompatible, anycompatible ) → anycompatiblearray

Заменяет каждый элемент массива, равный второму аргументу, значением третьего аргумента.

array_replace(ARRAY[1,2,5,4], 5, 3) → {1,2,3,4}

array_to_string ( массив anyarray, разделитель text [, нулевая_строка text ] ) → text

Преобразует каждый элемент массива в текстовое представление и конкатенирует те из них, что разделены строкой разделитель. Если задана нулевая_строка и она отлична от NULL, то эта строка представляет содержащиеся в массиве записи NULL; в противном случае такие записи опускаются.

array_to_string(ARRAY[1, 2, 3, NULL, 5], ',', '*') → 1,2,3,*,5

array_upper ( anyarray, integer ) → integer

Возвращает верхнюю границу запрошенной размерности массива.

array_upper(ARRAY[1,8,3,7], 1) → 4

cardinality ( anyarray ) → integer

Возвращает общее количество элементов в массиве или 0, если это пустой массив.

cardinality(ARRAY[[1,2],[3,4]]) → 4

trim_array ( массив anyarray, n integer ) → anyarray

Усекает массив, удаляя последние n элементов. Если массив многомерный, усекается только первая размерность.

trim_array(ARRAY[1,2,3,4,5,6], 2) → {1,2,3,4}

unnest ( anyarray ) → setof anyelement

Разворачивает массив в набор строк. Элементы массива прочитываются в порядке хранения.

    unnest(ARRAY[1,2]) →
         1
         2

    unnest(ARRAY[['foo','bar'],['baz','quux']]) →
         foo
         bar
         baz
         quux

unnest ( anyarray, anyarray [, ... ] ) → setof anyelement, anyelement [, ... ]

Разворачивает несколько массивов (возможно, разных типов данных) в набор строк. Если у массивов неодинаковая длина, то более короткие заполняются значениями NULL. Эта форма допускается только в предложении FROM; см. подраздел Табличные функции.

    select * from unnest(ARRAY[1,2], ARRAY['foo','bar','baz']) as x(a,b) →
         a |  b
        ---+-----
         1 | foo
         2 | bar
           | baz

Описание работающей с массивами функции array_agg см. в разделе Агрегатные функции.

Диапазонные/мультидиапазонные функции и операторы

Описание диапазонных типов данных см. в разделе Диапазонные типы.

В Таблице 53 приведены специальные операторы, доступные для диапазонных типов. В Таблице 54 приведены специальные операторы, доступные для мультидиапазонных типов. Помимо них для диапазонных и мультидиапазонных типов доступны обычные операторы сравнения, перечисленные в Таблице 1. Операторы сравнения сначала сравнивают нижние границы диапазонов, и только если они равны, сравнивают верхние границы. Мультидиапазонные операторы сравнивают каждый диапазон, пока не найдут неравный. Такие операции обычно не приводят к полезному в целом упорядочиванию, но зато позволяют строить по диапазонам уникальные индексы.

Таблица 53. Диапазонные операторы

Оператор

Описание

Пример(ы)

anyrange @> anyrange → boolean

Первый диапазон содержит второй?

int4range(2,4) @> int4range(2,3) → t

anyrange @> anyelement → boolean

Диапазон содержит этот элемент?

'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp → t

anyrange <@ anyrange → boolean

Первый диапазон содержится во втором?

int4range(2,4) <@ int4range(1,7) → t

anyelement <@ anyrange → boolean

Элемент содержится в этом диапазоне?

42 <@ int4range(1,7) → f

anyrange && anyrange → boolean

Диапазоны пересекаются, то есть имеют какие-либо общие элементы?

3 || ARRAY[4,5,6] → {3,4,5,6}

anyrange << anyrange → boolean

Первый диапазон находится строго слева от второго?

int8range(1,10) << int8range(100,110) → t

anyrange >> anyrange → boolean

Первый диапазон находится строго справа от второго?

int8range(50,60) >> int8range(20,30) → t

anyrange &< anyrange → boolean

Первый диапазон не простирается правее второго?

int8range(1,20) &< int8range(18,20) → t

anyrange &> anyrange → boolean

Первый диапазон не простирается левее второго?

int8range(7,20) &> int8range(5,10) → t

anyrange -|- anyrange → boolean

Диапазоны граничат друг с другом?

numrange(1.1,2.2) -|- numrange(2.2,3.3) → t

anyrange + anyrange → anyrange

Вычисляет объединение диапазонов. Диапазоны должны пересекаться или граничить друг с другом, чтобы их объединение было одним диапазоном (но см. range_merge()).

numrange(5,15) + numrange(10,20) → [5,20)

anyrange * anyrange → anyrange

Вычисляет пересечение диапазонов.

int8range(5,15) * int8range(10,20) → [10,15)

anyrange - anyrange → anyrange

Вычисляет разность диапазонов. Второй диапазон не должен содержаться в первом таким образом, чтобы разностью был не один диапазон.

int8range(5,15) - int8range(10,20) → [5,10)

Таблица 54. Мультидиапазонные операторы

Оператор

Описание

Пример(ы)

anymultirange @> anymultirange → boolean

Первый мультидиапазон содержит второй?

'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange → t

anymultirange @> anyrange → boolean

Мультидиапазон содержит этот диапазон?

'{[2,4)}'::int4multirange @> int4range(2,3) → t

anymultirange @> anyelement → boolean

Мультидиапазон содержит этот элемент?

'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp → t

anyrange @> anymultirange → boolean

Диапазон содержит этот мультидиапазон?

'[2,4)'::int4range @> '{[2,3)}'::int4multirange → t

anymultirange <@ anymultirange → boolean

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

'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange → t

anymultirange <@ anyrange → boolean

Мультидиапазон содержится в этом диапазоне?

'{[2,4)}'::int4multirange <@ int4range(1,7) → t

anyrange <@ anymultirange → boolean

Диапазон содержится в этом мультидиапазоне?

int4range(2,4) <@ '{[1,7)}'::int4multirange → t

anyelement <@ anymultirange → boolean

Элемент содержится в этом мультидиапазоне?

42 <@ '{[1,7)}'::int4multirange → t

anymultirange && anymultirange → boolean

Мультидиапазоны пересекаются, то есть имеют какие-либо общие элементы?

'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange → t

anymultirange && anyrange → boolean

Мультидиапазон пересекает этот диапазон?

'{[3,7)}'::int8multirange && int8range(4,12) → t

anyrange && anymultirange → boolean

Диапазон пересекает этот мультидиапазон?

int8range(3,7) && '{[4,12)}'::int8multirange → t

anymultirange << anymultirange → boolean

Первый мультидиапазон находится строго слева от второго?

'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange → t

anymultirange << anyrange → boolean

Мультидиапазон находится строго слева от этого диапазона?

'{[1,10)}'::int8multirange << int8range(100,110) → t

anyrange << anymultirange → boolean

Диапазон находится строго слева от этого мультидиапазона?

int8range(1,10) << '{[100,110)}'::int8multirange → t

anymultirange >> anymultirange → boolean

Первый мультидиапазон находится строго справа от второго?

'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange → t

anymultirange >> anyrange → boolean

Мультидиапазон находится строго справа от этого диапазона?

'{[50,60)}'::int8multirange >> int8range(20,30) → t

anyrange >> anymultirange → boolean

Диапазон находится строго справа от этого мультидиапазона?

int8range(50,60) >> '{[20,30)}'::int8multirange → t

anymultirange &< anymultirange → boolean

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

'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange → t

anymultirange &< anyrange → boolean

Мультидиапазон не простирается правее этого диапазона?

'{[1,20)}'::int8multirange &< int8range(18,20) → t

anyrange &< anymultirange → boolean

Диапазон не простирается правее этого мультидиапазона?

int8range(1,20) &< '{[18,20)}'::int8multirange → t

anymultirange &> anymultirange → boolean

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

'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange → t

anymultirange &> anyrange → boolean

Мультидиапазон не простирается левее этого диапазона?

'{[7,20)}'::int8multirange &> int8range(5,10) → t

anyrange &> anymultirange → boolean

Диапазон не простирается левее этого мультидиапазона?

int8range(7,20) &> '{[5,10)}'::int8multirange → t

anymultirange -|- anymultirange → boolean

Мультидиапазоны граничат друг с другом?

'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange → t

anymultirange -|- anyrange → boolean

Мультидиапазон граничит с этим диапазоном?

'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3) → t/code>

anyrange -|- anymultirange → boolean

Диапазон граничит с этим мультидиапазоном?

numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange → t

anymultirange + anymultirange → anymultirange

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

'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange → {[5,10), [15,20)}

anymultirange * anymultirange → anymultirange

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

'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange → {[10,15)}

anymultirange - anymultirange → anymultirange

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

'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange → {[5,10), [15,20)}

Операторы слева/справа/граничит всегда возвращают false, когда один из диапазонов или мультидиапазонов пуст; то есть считается, что пустой диапазон находится не перед и не после какого-либо другого диапазона.

В остальных случаях пустые диапазоны и мультидиапазоны воспринимаются как нулевой элемент: все, что объединяется с пустым значением, остается неизменным. Все, из чего вычитается пустое значение, остается неизменным. Пустой мультидиапазон имеет точно такие же особенности, что и пустой диапазон. Каждый диапазон содержит пустой диапазон. Каждый мультидиапазон содержит сколько угодно пустых диапазонов.

Операторы объединения и разности выдадут ошибку, если результирующему диапазону нужно содержать два непересекающихся поддиапазона, поскольку у таких диапазонов не может быть представления. Существуют отдельные операторы для объединения и разности, которые принимают мультидиапазонные параметры и возвращают мультидиапазон, и они не выдают ошибку, даже если их аргументы не пересекаются. Поэтому если вам требуется объединение или разность диапазонов, которые могут быть непересекающимися, ошибки можно избежать, если сначала привести диапазоны к мультидиапазонам.

В Таблице 55 приведены функции, доступные для диапазонных типов. В Таблице 56 приведены функции для мультидиапазонных типов.

Таблица 55. Диапазонные функции

Функция

Описание

Пример(ы)

lower ( anyrange ) → anyelement

Извлекает нижнюю границу диапазона (NULL, если диапазон пуст или его нижняя граница бесконечна).

lower(numrange(1.1,2.2)) → 1.1

upper ( anyrange ) → anyelement

Извлекает верхнюю границу диапазона (NULL, если диапазон пуст или его верхняя граница бесконечна).

upper(numrange(1.1,2.2)) → 2.2

isempty ( anyrange ) → boolean

Диапазон пустой?

isempty(numrange(1.1,2.2)) → f

lower_inc ( anyrange ) → boolean

Нижняя граница диапазона включена в него?

lower_inc(numrange(1.1,2.2)) → t

upper_inc ( anyrange ) → boolean

Верхняя граница диапазона включена в него?

upper_inc(numrange(1.1,2.2)) → f

lower_inf ( anyrange ) → boolean

Нижняя граница диапазона бесконечна?

lower_inf('(,)'::daterange) → t

upper_inf ( anyrange ) → boolean

Верхняя граница диапазона бесконечна?

upper_inf('(,)'::daterange) → t

range_merge ( anyrange, anyrange ) → anyrange

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

range_merge('[1,2)'::int4range, '[3,4)'::int4range) → [1,4)

Таблица 56. Мультидиапазонные функции

Функция

Описание

Пример(ы)

lower ( anymultirange ) → anyelement

Извлекает нижнюю границу мультидиапазона (NULL, если мультидиапазон пуст или его нижняя граница бесконечна).

lower('{[1.1,2.2)}'::nummultirange) → 1.1

upper ( anymultirange ) → anyelement

Извлекает верхнюю границу мультидиапазона (NULL, если мультидиапазон пуст или его верхняя граница бесконечна).

upper('{[1.1,2.2)}'::nummultirange) → 2.2

isempty ( anymultirange ) → boolean

Мультидиапазон пустой?

isempty('{[1.1,2.2)}'::nummultirange) → f

lower_inc ( anymultirange ) → boolean

Нижняя граница мультидиапазона включена в него?

lower_inc('{[1.1,2.2)}'::nummultirange) → t

upper_inc ( anymultirange ) → boolean

Верхняя граница мультидиапазона включена в него?

upper_inc('{[1.1,2.2)}'::nummultirange) → f

lower_inf ( anymultirange ) → boolean

Нижняя граница мультидиапазона бесконечна?

lower_inf('{(,)}'::datemultirange) → t

upper_inf ( anymultirange ) → boolean

Верхняя граница мультидиапазона бесконечна?

upper_inf('{(,)}'::datemultirange) → t

range_merge ( anymultirange ) → anyrange

Вычисляет наименьший диапазон, включающий весь заданный мультидиапазон.

range_merge('{[1,2), [3,4)}'::int4multirange) → [1,4)

multirange ( anyrange ) → anymultirange

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

multirange('[1,2)'::int4range) → {[1,2)}

unnest ( anymultirange ) → setof anyrange

Разворачивает мультидиапазон в набор диапазонов. Диапазоны прочитываются в порядке хранения (по возрастанию)

    unnest('{[1,2), [3,4)}'::int4multirange) →
         [1,2)
         [3,4)

Функции lower_inc, upper_inc, lower_inf и upper_inf возвращают false, если диапазон или мультидиапазон пуст.

Агрегатные функции

Агрегатные функции вычисляют единственный результат из набора входных значений. Встроенные агрегатные функции общего назначения перечислены в Таблице 57, а статистические агрегаты — в Таблице 58. Встроенные внутригрупповые упорядочивающие агрегатные функции перечислены в Таблице 59, а встроенные внутригрупповые гипотезирующие — в Таблице 60. Операции группировки, тесно связанные с агрегатными функциями, перечислены в Таблице 61. Особые рекомендации по синтаксису для агрегатных функций приведены в подразделе Агрегатные выражения. Дополнительную вводную информацию см. в разделе введения в язык SQL Агрегатные функции.

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

Таблица 57. Агрегатные функции общего назначения

Функция

Частичный режим

Описание

array_agg ( anynonarray ) → anyarray

Нет

Собирает всех входные значения, включая NULL, в массив.

array_agg ( anyarray ) → anyarray

Нет

Конкатенирует все входные массивы в массив с размерностью больше на один. (Входные массивы должны иметь одинаковую размерность и не могут быть пустыми или равны NULL.)

avg ( smallint ) → numeric

avg ( integer ) → numeric

avg ( bigint ) → numeric

avg ( numeric ) → numeric

avg ( real ) → double precision

avg ( double precision ) → double precision

avg ( interval ) → interval

Да

Вычисляет среднее арифметическое все входных значений, отличных от NULL.

bit_and ( smallint ) → smallint

bit_and ( integer ) → integer

bit_and ( bigint ) → bigint

bit_and ( bit ) → bit

Да

Вычисляет побитовое И для всех входных значений, отличных от NULL.

bit_or ( smallint ) → smallint

bit_or ( integer ) → integer

bit_or ( bigint ) → bigint

bit_or ( bit ) → bit

Да

Вычисляет побитовое ИЛИ для всех входных значений, отличных от NULL.

bit_xor ( smallint ) → smallint

bit_xor ( integer ) → integer

bit_xor ( bigint ) → bigint

bit_xor ( bit ) → bit

Да

Вычисляет побитовое исключающее ИЛИ для всех входных значений, отличных от NULL. Может быть полезна в качестве контрольной суммы для неупорядоченного набора значений.

bool_and ( boolean ) → boolean

Да

Возвращает true, если все отличные от NULL входные значения равны true, в противном случае — false.

bool_or ( boolean ) → boolean

Да

Возвращает true, если какое-либо отличное от NULL входное значение равно true, в противном случае — false.

count ( * ) → bigint

Да

Вычисляет количество входных строк.

count ( "any" ) → bigint

Да

Вычисляет количество входных строк, в которых входное значение отлично от NULL.

every ( boolean ) → boolean

Да

Это соответствующий стандарту SQL аналог bool_and.

json_agg ( anyelement ) → json

jsonb_agg ( anyelement ) → jsonb

Нет

Собирает все входные значения, включая NULL, в массив JSON. Значения преобразуются в JSON методом to_json или to_jsonb.

json_object_agg ( ключ "any", значение "any" ) → json

jsonb_object_agg ( ключ "any", значение "any" ) → jsonb

Нет

Собирает все пары ключ/значение в объект JSON. Аргументы-ключи приводятся к текстовому типу; аргументы-значения преобразуются методом to_json или to_jsonb. Значения могут быть NULL, но ключи — нет.

max ( см. описание ) → тот же тип, что и на входе

Да

Вычисляет максимальное из входных значений, отличных от NULL. Доступна для всех числовых, строковых, перечислимых типов и типов даты/времени, а также для типов inet, interval, money, oid, pg_lsn, tid и массивов любого из этих типов.

min ( см. описание ) → тот же тип, что и на входе

Да

Вычисляет минимальное из входных значений, отличных от NULL. Доступна для всех числовых, строковых, перечислимых типов и типов даты/времени, а также для типов inet, interval, money, oid, pg_lsn, tid и массивов любого из этих типов.

range_agg ( значение anyrange ) → anymultirange

Нет

Вычисляет объединение входных значений, отличных от NULL.

range_intersect_agg ( значение anyrange ) → anymultirange

Нет

Вычисляет пересечение входных значений, отличных от NULL.

string_agg ( значение text, разделитель text ) → text

string_agg ( значение bytea, разделитель bytea ) → bytea

Нет

Конкатенирует отличные от NULL входные значения в строку. Перед каждым значением, кроме первого, добавляется соответствующий разделитель (если он отличен от NULL).

sum ( smallint ) → bigint

sum ( integer ) → bigint

sum ( bigint ) → numeric

sum ( numeric ) → numeric

sum ( real ) → real

sum ( double precision ) → double precision

sum ( interval ) → interval

sum ( money ) → money

Да

Вычисляет сумму входных значений, отличных от NULL.

xmlagg ( xml ) → xml

Нет

Конкатенирует отличные от NULL входные значения XML (см. подраздел xmlagg).

Следует отметить, что, за исключением count, эти функции возвращают значение NULL, когда не выбрана ни одна строка. В частности, в отсутствие строк sum возвращает это значение вместо нуля (как можно было бы ожидать), а array_agg — вместо пустого массива. Для замены нуля или пустого массива на NULL можно при необходимости воспользоваться функцией coalesce.

Агрегатные функции array_agg, json_agg, jsonb_agg, json_object_agg, jsonb_object_agg, string_agg и xmlagg, а также аналогичные им пользовательские агрегатные функции выдают значимо разные результирующие значения в зависимости от порядка входных значений. Этот порядок не задается по умолчанию, но им можно управлять, написав в вызове агрегатной функции предложение ORDER BY, как показано в подразделе Агрегатные выражения. Как вариант, также обычно срабатывает предоставление входных значений из подзапроса с сортировкой. Например:

SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab;

Помните, что этот подход может дать сбой, если внешний уровень запроса содержит дополнительную обработку, например объединение, поскольку это может привести к переупорядочению выходных значений подзапроса перед вычислением агрегатной функции.

Примечание
Логические агрегаты bool_and и bool_or аналогичны описанным в стандарте SQL агрегатам every и any или some. QHB поддерживает every, но не any или some, поскольку в стандартном синтаксисе присутствует неоднозначность:

SELECT b1 = ANY((SELECT b2 FROM t2 ...)) FROM t1 ...;

Здесь ANY можно рассматривать и как добавление подзапроса, и как агрегатную функцию, если подзапрос возвращает одну строку с логическим значением. Таким образом, этим агрегатам нельзя было дать стандартные имена.

Примечание
Пользователи, привыкшие работать с другими СУБД SQL, могут быть разочарованы производительностью агрегата count, когда он применяется к целой таблице. Подобный запрос:

SELECT count(*) FROM sometable;

потребует затрат, пропорциональных размеру таблицы: QHB потребуется просканировать либо таблицу целиком, либо индекс, включающий все строки этой таблицы.

В Таблице 58 приведены агрегатные функции, обычно используемые в статистическом анализе. (Они отделены просто для того, чтобы избежать загромождения списка более часто используемых агрегатных функций). Функции, показанные как принимающие числовой_тип, доступны для типов smallint, integer, bigint, numeric, real и double precision. Когда в описании упоминается N, это означает количество входных строк, для которых все входные выражения отличны от NULL. Во всех случаях, когда вычисление не имеет смысла, например, когда N равно нулю, возвращается NULL.

Таблица 58. Агрегатные функции для статистики

Функция

Частичный режим

Описание

corr ( Y double precision, X double precision ) → double precision

Да

Вычисляет коэффициент корреляции.

covar_pop ( Y double precision, X double precision ) → double precision

Да

Вычисляет ковариацию совокупности.

covar_samp ( Y double precision, X double precision ) → double precision

Да

Вычисляет выборочную ковариацию.

regr_avgx ( Y double precision, X double precision ) → double precision

Да

Вычисляет среднее для независимой переменной, sum(X)/N.

regr_avgy ( Y double precision, X double precision ) → double precision

Да

Вычисляет среднее для зависимой переменной, sum(Y)/N.

regr_count ( Y double precision, X double precision ) → bigint

Да

Вычисляет число строк, в которых оба входных выражения отличны от NULL.

regr_intercept ( Y double precision, X double precision ) → double precision

Да

Вычисляет пересечение с осью y линейного уравнения, решенного подбором по методу наименьших квадратов по парам (X, Y).

regr_r2 ( Y double precision, X double precision ) → double precision

Да

Вычисляет квадрат коэффициента корреляции.

regr_slope ( Y double precision, X double precision ) → double precision

Да

Вычисляет наклон линейного уравнения, решенного подбором по методу наименьших квадратов по парам (X, Y).

regr_sxx ( Y double precision, X double precision ) → double precision

Да

Вычисляет «сумму квадратов» независимой переменной, sum(X^2) - sum(X)^2/N.

regr_sxy ( Y double precision, X double precision ) → double precision

Да

Вычисляет «сумму произведений» независимой и зависимой переменных, sum(X*Y) - sum(X) * sum(Y)/N.

regr_syy ( Y double precision, X double precision ) → double precision

Да

Вычисляет «сумму квадратов» зависимой переменной, sum(Y^2) - sum(Y)^2/N.

stddev ( числовой_тип ) → double precision для real или double precision, иначе numeric

Да

Это исторический псевдоним stddev_samp.

числовой_тип ) → double precision для real или double precision, иначе numeric

Да

Вычисляет стандартное отклонение по совокупности входных значений.

stddev_samp ( числовой_тип ) → double precision для real или double precision, иначе numeric

Да

Вычисляет стандартное отклонение по выборке входных значений.

variance ( числовой_тип ) → double precision для real или double precision, иначе numeric

Да

Это исторический псевдоним var_samp.

var_pop ( числовой_тип ) → double precision для real или double precision, иначе numeric

Да

Вычисляет дисперсию совокупности входных значений (квадрат стандартного отклонения по совокупности).

var_samp ( числовой_тип ) → double precision для real или double precision, иначе numeric

Да

Вычисляет дисперсию выборки входных значений (квадрат стандартного отклонения по выборке).

В Таблице 59 приведены некоторые агрегатные функции, использующие синтаксис упорядочивающих агрегатов. Эти функции иногда называют функциями «обратного распределения». Их агрегированные входные данные добавляются указанием ORDER BY, а кроме того они могут принимать непосредственный аргумент, который не агрегируется, но вычисляется только один раз. Все эти функции игнорируют значения NULL в агрегируемых входных данных. Для функций, принимающих параметр fraction, его значение должно быть между 0 и 1; в противном случае возникнет ошибка. Однако если fraction имеет значение NULL, то и результат будет NULL.

Таблица 59. Упорядочивающие функции

Функция

Частичный режим

Описание

mode () WITHIN GROUP ( ORDER BY anyelement ) → anyelement

Нет

Вычисляет моду, наиболее часто встречающееся значение в агрегируемом аргументе (если одинаково часто встречаются несколько значений, произвольно выбирая первое из них). Агрегируемый аргумент должен быть сортируемого типа.

percentile_cont ( дробь double precision ) WITHIN GROUP ( ORDER BY double precision ) → double precision

percentile_cont ( дробь double precision ) WITHIN GROUP ( ORDER BY interval ) → interval

Нет

Вычисляет непрерывный процентиль, значение, соответствующее заданной дроби в отсортированном множестве значений агрегируемого аргумента. При необходимости соседние входные элементы будут интерполироваться.

percentile_cont ( дроби double precision[] ) WITHIN GROUP ( ORDER BY double precision ) → double precision[]

percentile_cont ( дроби double precision[] ) WITHIN GROUP ( ORDER BY interval ) → interval[]

Нет

Вычисляет несколько непрерывных процентилей. Результатом является массив той же размерности, что и параметр дроби, а все отличные от NULL элементы в нем заменяются значением (возможно, интерполированным), соответствующим этому процентилю.

percentile_disc ( дробь double precision ) WITHIN GROUP ( ORDER BY anyelement ) → anyelement

Нет

Вычисляет дискретный процентиль, первое значение в упорядоченном множестве значений агрегируемого аргумента, позиция которого в этом множестве равна или превышает заданную дробь. Агрегируемый аргумент должен быть сортируемого типа.

percentile_disc ( дроби double precision[] ) WITHIN GROUP ( ORDER BY anyelement ) → anyarray

Нет

Вычисляет несколько дискретных процентилей. Результатом является массив той же размерности, что и параметр дроби, а все отличные от NULL элементы в нем заменяются входным значением, соответствующим этому процентилю. Агрегируемый аргумент должен быть сортируемого типа.

Все «гипотезирующие» агрегаты, перечисленные в Таблице 60, связаны с одноименными оконными функциями, определенными в разделе Оконные функции. Во всех случаях результат этих агрегатов представляет собой значение, которое связанная оконная функция вернула бы для «гипотетической» строки, сформированной из аргументов, если бы такая строка была добавлена в упорядоченную группу строк, представленную отсортированными_аргументами. Для каждой из этих функций список непосредственных аргументов, заданный в параметре аргументы, должен соответствовать числу и типу агрегируемых аргументов, заданных в параметре отсортированные_аргументы. В отличие от большинства встроенных агрегатов, эти агрегаты не являются строгими, то есть они не опускают входные строки, содержащие NULL. Значения NULL сортируются согласно правилу, заданному в предложении ORDER BY.

Таблица 60. Гипотезирующие агрегатные функции

Функция

Частичный режим

Описание

rank ( аргументы ) WITHIN GROUP ( ORDER BY отсортированные_аргументы ) → bigint

Нет

Вычисляет ранг гипотетической строки с пропусками, то есть номер первой строки в одноранговой с ней группе.

dense_rank ( аргументы ) WITHIN GROUP ( ORDER BY отсортированные_аргументы ) → bigint

Нет

Вычисляет ранг гипотетической строки без пропусков; в сущности, эта функция считает одноранговые группы.

percent_rank ( аргументы ) WITHIN GROUP ( ORDER BY отсортированные_аргументы ) → double precision

Нет

Вычисляет относительный ранг гипотетической строки, то есть (rank - 1) / (общее число строк - 1). Таким образом, это значение лежит в диапазоне от 0 до 1, включая границы.

cume_dist ( аргументы ) WITHIN GROUP ( ORDER BY отсортированные_аргументы ) → double precision

Нет

Вычисляет кумулятивное распределение, то есть (число строк, предшествующих или одноранговых с гипотетической строкой) / (общее число строк). Это значение лежит в диапазоне от 1/N до 1.

Таблица 61. Операции группировки

Функция

Описание

GROUPING ( выражение(я)_group_by ) → integer

Возвращает битовую маску, показывающую, какие выражения GROUP BY не включены в текущий группирующий набор. Биты присваиваются самому правому аргументу, соответствующему младшему значащему биту; каждый бит равен 0, если соответствующее выражение включено в критерий группировки группирующего набора, генерирующего текущую результирующую строку, и 1, если оно туда не включено.

Операции группировки, приведенные в Таблице 61, применяются совместно с группирующими наборами (см. подраздел GROUPING SETS, CUBE и ROLLUP) для различения результирующих строк. Аргументы функции GROUPING на самом деле не вычисляются, но они должны в точности соответствовать выражениям, заданным в предложении GROUP BY на их уровне запроса. Например:

=> SELECT * FROM items_sold;
 make  | model | sales
-------+-------+-------
 Foo   | GT    |  10
 Foo   | Tour  |  20
 Bar   | City  |  15
 Bar   | Sport |  5
(4 rows)

=> SELECT make, model, GROUPING(make,model), sum(sales) FROM items_sold GROUP BY ROLLUP(make,model);
 make  | model | grouping | sum
-------+-------+----------+-----
 Foo   | GT    |        0 | 10
 Foo   | Tour  |        0 | 20
 Bar   | City  |        0 | 15
 Bar   | Sport |        0 | 5
 Foo   |       |        1 | 30
 Bar   |       |        1 | 20
       |       |        3 | 50
(7 rows)

Здесь значение grouping, равное 0 в первых четырех строках, показывает, что эти строки были сгруппированы обычным образом, по обоим группирующим столбцам. Значение 1 показывает, что столбец model не был группирующим в двух предпоследних строках, а значение 3 показывает, что в последней строке не были группирующими ни make, ни model (то есть агрегирование проводилось по всем входным строкам).

Оконные функции

Оконные функции предоставляют возможность выполнять вычисления по наборам строк, связанных с текущей строкой запроса. Вводную информацию по этой функциональной возможности см. в разделе Руководство по оконным функциям, а подробную информацию о синтаксисе — в подразделе Вызовы оконных функций.

Встроенные оконные функции перечислены в Таблице 62. Обратите внимание, что эти функции должны вызываться с использованием синтаксиса оконных функций, то есть необходимо предложение OVER.

В дополнение к этим функциям в качестве оконной функции может использоваться любой встроенный или обычный пользовательский агрегат (т. е. не упорядочивающие или гипотезирующие агрегаты); список встроенных агрегатов см. в разделе Агрегатные функции. Агрегатные функции действуют как оконные только тогда, когда за вызовом следует предложение OVER; в противном случае они ведут себя как простые агрегаты и возвращают единственную строку для всего набора.

Таблица 62. Универсальные оконные функции

Функция

Описание

row_number () → bigint

Возвращает номер текущей строки в ее партиции, считая с 1.

rank () → bigint

Возвращает ранг текущей строки с пропусками, то есть row_number первой строки в одноранговой с ней группе.

dense_rank () → bigint

Возвращает ранг текущей строки без пропусков; в сущности, эта функция считает одноранговые группы.

percent_rank () → double precision

Возвращает относительный ранг текущей строки, то есть (rank - 1) / (общее число строк партиции - 1). Таким образом, значение лежит в диапазоне от 0 до 1, включая границы.

cume_dist () → double precision

Возвращает кумулятивное распределение, то есть (число строк партиции, предшествующих или одноранговых с текущей строкой) / (общее число строк партиции). Таким образом, значение лежит в диапазоне от 1/N до 1.

ntile ( число_участков_памяти integer ) → integer

Возвращает целое в диапазоне от 1 до значения аргумента, разделяя партицию на максимально равные по размеру части.

lag ( значение anycompatible [, сдвиг integer [, значение_по_умолчанию anycompatible ]] ) → anycompatible

Возвращает значение, вычисленное для строки, отстоящей на сдвиг строк от текущей к началу партиции; если такой строки нет, вместо этого возвращает значение_по_умолчанию (которое должно быть совместимого со значением типа). Как сдвиг, так и значение_по_умолчанию вычисляются относительно текущей строки. Если они опускаются, по умолчанию сдвиг равен 1, а значение_по_умолчанию — NULL.

lead ( значение anycompatible [, сдвиг integer [, значение_по_умолчанию anycompatible ]] ) → anycompatible

Возвращает значение, вычисленное для строки, отстоящей на сдвиг строк от текущей к концу партиции; если такой строки нет, вместо этого возвращает значение_по_умолчанию (которое должно быть совместимого со значением типа). Как сдвиг, так и значение_по_умолчанию вычисляются относительно текущей строки. Если они опускаются, по умолчанию сдвиг равен 1, а значение_по_умолчанию — NULL.

first_value ( значение anyelement ) → anyelement

Возвращает значение, вычисленное для первой строки в рамке окна.

last_value ( значение anyelement ) → anyelement

Возвращает значение, вычисленное для последней строки в рамке окна.

nth_value ( значение anyelement, n integer ) → anyelement

Возвращает значение, вычисленное для n-й строки в рамке окна (считая с 1); возвращает NULL, если такой строки нет.

Все функции, перечисленные в Таблице 62, зависят от порядка сортировки, заданного в предложении ORDER BY соответствующего определения окна. Строки, не различающиеся при рассмотрении только столбцов ORDER BY, называются одноранговыми. Четыре функции ранжирования (включая cume_dist) определены так, чтобы давать одинаковый ответ для всех строк одноранговой группы.

Обратите внимание, что функции first_value, last_value и nth_value учитывают только строки в «рамке окна», которая по умолчанию содержит строки от начала партиции до последней строки, одноранговой с текущей. Скорее всего, у last_value, а иногда и nth_value, это приведет к бесполезным результатам. Рамку можно переопределить, добавив подходящую спецификацию рамки (RANGE, ROWS или GROUPS) в предложение OVER. Дополнительную информацию о спецификациях рамок см. в разделе Вызовы оконных функций.

Когда в качестве оконной функции используется агрегатная, она агрегирует по строкам в рамке окна текущей строки. Агрегат, используемый с ORDER BY и определением рамки окна по умолчанию, создает тип поведения «суммирование с нарастающим итогом», что может как соответствовать, так и не соответствовать желаемому результату. Чтобы получить агрегацию по всей партиции, опустите ORDER BY или воспользуйтесь ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING. Для получения других результатов можно использовать другие спецификации рамок.

Примечание
В стандарте SQL определены параметры RESPECT NULLS или IGNORE NULLS для функций lead, lag, first_value, last_value и nth_value. В QHB это не реализовано: поведение этих функций всегда соответствует описанному в стандарте как поведение по умолчанию, а именно, с параметром RESPECT NULLS. Аналогичным образом для функции nth_value не реализованы стандартные параметры FROM FIRST или FROM LAST: поддерживается только поведение по умолчанию — с параметром FROM FIRST. (Результата как у параметра FROM LAST можно добиться, изменив порядок ORDER BY на обратный.)

Выражения подзапроса

В этом разделе описываются SQL- совместимые выражения подзапроса, доступные в QHB. Все формы выражений, описанные в этом разделе, возвращают логические (true/false) результаты.

EXISTS

EXISTS (subquery)

Аргумент EXISTS — это произвольный оператор SELECT или подзапрос. Подзапрос оценивается, чтобы определить, возвращает ли он какие-либо строки. Если он возвращает хотя бы одну строку, результат EXISTS равен true; если подзапрос не возвращает строк, результатом EXISTS будет false.

Подзапрос может ссылаться на переменные из окружающего запроса, которые будут действовать как константы во время любой оценки подзапроса.

Подзапрос, как правило, выполняется только до момента, когда можно определить, возвращена ли хотя бы одна строка, а не до завершения. Неразумно писать подзапрос с побочными эффектами (например, вызывая функции последовательности); возникновение побочных эффектов может быть непредсказуемым.

Поскольку результат зависит только от того, возвращаются ли какие-либо строки, а не от содержимого этих строк, выходной список подзапроса обычно не важен. Обычное соглашение о кодировании - записывать все тесты EXISTS в форме EXISTS(SELECT 1 WHERE ...). Однако есть исключения из этого правила, такие как подзапросы, которые используют INTERSECT.

Этот простой пример похож на внутреннее соединение по col2, но он генерирует не более одной выходной строки для каждой строки tab1, даже если есть несколько совпадающих строк tab2:

SELECT col1
FROM tab1
WHERE EXISTS (SELECT 1 FROM tab2 WHERE col2 = tab1.col2);

IN

expression IN (subquery)

Правая часть — это вложенный в скобки подзапрос, который должен возвращать ровно один столбец. Левое выражение оценивается и сравнивается с каждой строкой результата подзапроса. Результатом IN является «истина», если найдена какая-либо одинаковая строка подзапроса. Результатом является «ложь», если не найдено ни одной равной строки (включая случай, когда подзапрос не возвращает строк).

Обратите внимание, что если левое выражение возвращает null или если нет равных правых значений и хотя бы одна правая строка возвращает null, результатом конструкции IN будет null, а не ложь. Это соответствует нормальным правилам SQL для логических комбинаций null значений.

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

row_constructor IN (subquery)

Левая часть этой формы IN является конструктором строки, как описано в «Руководстве пользователя» в разделе Конструкторы строк. Правая часть — это вложенный в скобки подзапрос, который должен возвращать ровно столько столбцов, сколько имеется выражений в левой строке. Левые выражения оцениваются и сравниваются построчно с каждой строкой результата подзапроса. Результатом IN является «истина», если найдена какая-либо одинаковая строка подзапроса. Результатом является «ложь», если не найдено ни одной равной строки (включая случай, когда подзапрос не возвращает строк).

Как обычно, null значения в строках объединяются в соответствии с обычными правилами логических выражений SQL. Две строки считаются равными, если все их соответствующие члены ненулевые и равные; строки являются неравными, если любые соответствующие члены ненулевые и неравные; в противном случае результат сравнения строк неизвестен (null). Если все результаты для каждой строки являются неравными или null, хотя бы с одним null, то результат IN является null.

NOT IN

expression NOT IN (subquery)

Правая часть — это вложенный в скобки подзапрос, который должен возвращать ровно один столбец. Левое выражение оценивается и сравнивается с каждой строкой результата подзапроса. Результатом NOT IN является «истина», если найдены только неравные строки подзапроса (включая случай, когда подзапрос не возвращает строк). Результатом является «ложь», если найдена какая-либо равная строка.

Обратите внимание, что если левое выражение возвращает null или если нет равных правых значений и хотя бы одна правая строка дает null, результатом конструкции NOT IN будет null, а не истина. Это соответствует нормальным правилам SQL для логических комбинаций null значений.

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

row_constructor NOT IN (subquery)

Левая часть этой формы NOT IN является конструктором строки, как описано в «Руководстве пользователя» в разделе Конструкторы строк. Правая часть — это вложенный в скобки подзапрос, который должен возвращать ровно столько столбцов, сколько имеется выражений в левой строке. Левые выражения оцениваются и сравниваются построчно с каждой строкой результата подзапроса. Результатом NOT IN является «истина», если найдены только неравные строки подзапроса (включая случай, когда подзапрос не возвращает строк). Результатом является «ложь», если найдена какая-либо равная строка.

Как обычно, null значения в строках объединяются в соответствии с обычными правилами логических выражений SQL. Две строки считаются равными, если все их соответствующие члены ненулевые и равные; строки являются неравными, если любые соответствующие члены ненулевые и неравные; в противном случае результат сравнения строк неизвестен (null). Если все результаты для каждой строки являются неравными или null, хотя бы с одним null, то результат NOT IN будет null.

ANY/SOME

expression operator ANY (subquery)

expression operator SOME (subquery)

Правая часть — это вложенный в скобки подзапрос, который должен возвращать ровно один столбец. Левое выражение оценивается и сравнивается с каждой строкой результата подзапроса с использованием данного оператора, который должен давать логический результат. Результат ANY является «истинным», если получен какой-либо истинный результат. Результатом является «ложь», если истинный результат не найден (включая случай, когда подзапрос не возвращает строк).

SOME является синонимом для ANY. IN эквивалентно = ANY.

Обратите внимание, что, если нет никаких успехов, и хотя бы одна правая строка возвращает null для результата оператора, результат ANY конструкции будет null, а не ложным. Это соответствует нормальным правилам SQL для логических комбинаций null значений.

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

row_constructor operator ANY (subquery)
row_constructor operator SOME (subquery)

Левая часть этой формы ANY является конструктором строки, как описано в «Руководстве пользователя» в разделе Конструкторы строк. Правая часть — это вложенный в скобки подзапрос, который должен возвращать ровно столько столбцов, сколько имеется выражений в левой строке. Левые выражения оцениваются и сравниваются построчно с каждой строкой результата подзапроса, используя данный оператор. Результатом ANY является true, если сравнение возвращает true для любой строки подзапроса. Результат - «ложь», если сравнение возвращает ложь для каждой строки подзапроса (включая случай, когда подзапрос не возвращает строк). Результат равен NULL, если сравнение со строкой подзапроса не возвращает true, и хотя бы одно сравнение возвращает NULL.

См. раздел Сравнение конструкторов строк для получения подробной информации о значении сравнения конструктора строки.

ALL

expression operator ALL (subquery)

Правая часть — это вложенный в скобки подзапрос, который должен возвращать ровно один столбец. Левое выражение оценивается и сравнивается с каждой строкой результата подзапроса с использованием данного оператора, который должен давать логический результат. Результатом ALL является true, если все строки возвращают true (включая случай, когда подзапрос не возвращает строк). Результат «ложь», если найден какой-либо ложный результат. Результат равен NULL, если сравнение со строкой подзапроса не возвращает false, и хотя бы одно сравнение возвращает NULL.

NOT IN эквивалентно <> ALL.

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

row_constructor operator ALL (subquery)

Левая часть этой формы ALL является конструктором строк, как описано в «Руководстве пользователя» в разделе Конструкторы строк. Правая часть — это вложенный в скобки подзапрос, который должен возвращать ровно столько столбцов, сколько имеется выражений в левой строке. Левые выражения оцениваются и сравниваются построчно с каждой строкой результата подзапроса, используя данный оператор. Результатом ALL является true, если сравнение возвращает true для всех строк подзапроса (включая случай, когда подзапрос не возвращает строк). Результатом является «ложь», если сравнение возвращает ложь для любой строки подзапроса. Результат равен NULL, если сравнение со строкой подзапроса не возвращает false, и хотя бы одно сравнение возвращает NULL.

См. раздел Сравнение конструкторов строк для получения подробной информации о значении сравнения конструктора строки.

Сравнение по одной строке

row_constructor operator (subquery)

Левая часть — это конструктор строк, как описано в «Руководстве пользователя» в разделе Конструкторы строк. Правая часть — это вложенный в скобки подзапрос, который должен возвращать ровно столько столбцов, сколько имеется выражений в левой строке. Кроме того, подзапрос не может возвращать более одной строки. (Если он возвращает ноль строк, результат принимается равным null). Левая часть вычисляется и сравнивается по строкам с одной строкой результата подзапроса.

См. раздел Сравнение конструкторов строк для получения подробной информации о значении сравнения конструктора строки.

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

В этом разделе описывается несколько специализированных конструкций для множественных сравнений между группами значений. Эти формы синтаксически связаны с формами подзапросов предыдущего раздела, но не включают подзапросы. Формы, включающие подвыражения массива, являются расширениями QHB; Остальные SQL- совместимы. Все формы выражений, описанные в этом разделе, возвращают логические (true/false) результаты.

IN

expression IN (value [, ...])

Правая часть — это заключенный в скобки список скалярных выражений. Результат true, если результат левого выражения равен любому из правых выражений. Это сокращенное обозначение для

expression = value1
OR
expression = value2
OR
...

Обратите внимание, что если левое выражение возвращает null или если нет равных правых значений и хотя бы одно правое выражение дает null, результатом конструкции IN будет null, а не ложь. Это соответствует нормальным правилам SQL для логических комбинаций null значений.

NOT IN

expression NOT IN (value [, ...])

Правая часть — это заключенный в скобки список скалярных выражений. Результат true, если результат левого выражения не равен всем правым выражениям. Это сокращенное обозначение для

expression <> value1
AND
expression <> value2
AND
...

Обратите внимание, что если левое выражение возвращает null или если нет равных правых значений и хотя бы одно правое выражение возвращает null, результат конструкции NOT IN будет null, а не истинным, как можно было ожидать. Это соответствует нормальным правилам SQL для логических комбинаций null значений.

x NOT IN y во всех случаях эквивалентно NOT (x IN y). Тем не менее, нулевые значения гораздо чаще приводят в замешательство новичка при работе с NOT IN чем при работе с IN. Лучше всего выразить ваше состояние положительно, если это возможно.

ANY/SOME (массив)

expression operator ANY (array expression)
expression operator SOME (array expression)

Правая часть — это выражение в скобках, которое должно давать значение массива. Левое выражение вычисляется и сравнивается с каждым элементом массива с использованием данного оператора, который должен давать логический результат. Результат ANY является «истинным», если получен какой-либо истинный результат. Результатом является «ложь», если истинный результат не найден (включая случай, когда массив имеет нулевые элементы).

Если выражение массива дает null массив, результат ANY будет null. Если левое выражение возвращает null, то результат ANY обычно равен null (хотя оператор нестрогого сравнения может привести к другому результату). Кроме того, если правый массив содержит какие-либо null элементы и не получен истинный результат сравнения, результат ANY будет null, а не ложным (опять-таки, предполагая оператор строгого сравнения). Это соответствует нормальным правилам SQL для логических комбинаций null значений.

SOME является синонимом для ANY.

ALL (массив)

expression operator ALL (array expression)

Правая часть — это выражение в скобках, которое должно давать значение массива. Левое выражение вычисляется и сравнивается с каждым элементом массива с использованием данного оператора, который должен давать логический результат. Результатом ALL является «истина», если все сравнения дают истину (включая случай, когда массив имеет нулевые элементы). Результат «ложь», если найден какой-либо ложный результат.

Если выражение массива дает null массив, результат ALL будет null. Если левое выражение возвращает null, результат ALL обычно равен null (хотя оператор нестрогого сравнения может привести к другому результату). Кроме того, если правый массив содержит какие-либо null элементы и не получен ложный результат сравнения, результат ALL будет null, а не истинным (опять-таки, при условии использования оператора строгого сравнения). Это соответствует нормальным правилам SQL для логических комбинаций null значений.

Сравнение конструкторов строк

row_constructor operator row_constructor

Каждая сторона является конструктором строки, как описано в «Руководстве пользователя» в разделе Конструкторы строк. Два значения строки должны иметь одинаковое количество полей. Каждая сторона оценивается, и они сравниваются по строкам. Сравнение конструкторов строк допускается, когда оператором является =, &lt;&gt;, &lt;, &lt;=, &gt; или &gt;= . Каждый элемент строки должен иметь тип, который имеет класс оператора B-дерева по умолчанию, иначе попытка сравнения может вызвать ошибку.

Случаи = и <> работают немного по-другому. Две строки считаются равными, если все их соответствующие члены ненулевые и равные; строки являются неравными, если любые соответствующие члены ненулевые и неравные; в противном случае результат сравнения строк неизвестен (null).

Для случаев <, <=, > и >= элементы строки сравниваются слева направо, останавливаясь, как только обнаруживается неравная или null пара элементов. Если любой из этой пары элементов является null, результат сравнения строк неизвестен (null); в противном случае сравнение этой пары элементов определяет результат. Например, ROW(1,2,NULL) &lt; ROW(1,3,0) дает значение true, а не null, поскольку третья пара элементов не рассматривается.

row_constructor IS DISTINCT FROM row_constructor

Эта конструкция похожа на сравнение строк <>, но не дает null значения для null входных данных. Вместо этого любое null значение считается неравным (отличным от) любого ненулевого значения, а любые два null значения считаются равными (не отличными). Таким образом, результат будет либо истинным, либо ложным, а не null.

row_constructor IS NOT DISTINCT FROM row_constructor

Эта конструкция похожа на сравнение строк =, но она не дает null значения для null входных данных. Вместо этого любое null значение считается неравным (отличным от) любого ненулевого значения, а любые два null значения считаются равными (не отличными). Таким образом, результат всегда будет либо истинным, либо ложным, а не null.

Сравнение составных типов

record operator record

Спецификация SQL требует сравнения строк для возврата NULL, если результат зависит от сравнения двух значений NULL или NULL и не NULL. QHB делает это только при сравнении результатов двух конструкторов строк (как в разделе Сравнение конструкторов строк) или при сравнении конструктора строк с выходными данными подзапроса (как в разделе Выражения подзапроса). В других контекстах, где сравниваются два значения составного типа, два значения поля NULL считаются равными, а NULL считается большим, чем ненулевое. Это необходимо для согласованного поведения сортировки и индексации составных типов.

Каждая сторона оценивается, и они сравниваются по строкам. Сравнения составных типов допускаются, когда оператором является =, <>, <, <=, > или >= или имеет семантику, аналогичную одной из них. (Точнее говоря, оператор может быть оператором сравнения строк, если он является членом класса операторов B-дерева или является отрицателем члена = класса операторов B-дерева). Поведение по умолчанию вышеупомянутых операторов такое же, как для IS [NOT] DISTINCT FROM для конструкторов строк (см. раздел Сравнение конструкторов строк).

Для поддержки сопоставления строк, которые включают элементы без класса операторов B-дерева по умолчанию, для сравнения составных типов определены следующие операторы: *=, *<>, *<, *<=, *> и *>=. Эти операторы сравнивают внутреннее двоичное представление двух строк. Две строки могут иметь различное двоичное представление, даже если сравнение двух строк с оператором равенства верно. Порядок строк в этих операторах сравнения является детерминированным, но не имеет иного смысла. Эти операторы используются внутри для материализованных представлений и могут быть полезны для других специализированных целей, таких как репликация, но не предназначены для общего использования при написании запросов.

Функции возврата наборов (SET)

В этом разделе описываются функции, которые могут возвращать более одной строки. Наиболее широко используемые функции в этом классе — это функции, генерирующие ряды, как подробно описано в таблице 61 и таблице 62. Другие, более специализированные функции, возвращающие множество, описаны в «Руководстве пользователя», например в разделе Табличные функции приведены способы объединения нескольких функций возврата набора.

Таблица 61. Функции генерации серий

Функция | Тип аргумента | Тип ответа | Описание
generate_series(start, stop) | int, bigint или numeric | setof int, setof bigint или setof numeric (аналогично типу аргумента) | Создайте серию значений от start до stop с размером шага один
generate_series(start, stop, step) | int, bigint или numeric | setof int, setof bigint или setof numeric (аналогично типу аргумента) | Генерация серии значений от start до stop с шагом step
generate_series(start, stop, step interval) | timestamp или timestamp with time zone | setof timestamp или setof timestamp with time zone (такой же, как тип аргумента) | Генерация серии значений от start до stop с шагом step

Если шаг положителен, возвращается ноль строк, если start больше, чем stop. И наоборот, когда шаг отрицательный, нулевые строки возвращаются, если start меньше stop. Нулевые строки также возвращаются для NULL входных данных. Это ошибка, когда шаг равен нулю. Вот несколько примеров:

SELECT * FROM generate_series(2,4);
 generate_series
-----------------
               2
               3
               4
(3 rows)

SELECT * FROM generate_series(5,1,-2);
 generate_series
-----------------
               5
               3
               1
(3 rows)

SELECT * FROM generate_series(4,3);
 generate_series
-----------------
(0 rows)

SELECT generate_series(1.1, 4, 1.3);
 generate_series
-----------------
             1.1
             2.4
             3.7
(3 rows)

-- this example relies on the date-plus-integer operator
SELECT current_date + s.a AS dates FROM generate_series(0,14,7) AS s(a);
   dates
------------
 2004-02-05
 2004-02-12
 2004-02-19
(3 rows)

SELECT * FROM generate_series('2008-03-01 00:00'::timestamp,
                              '2008-03-04 12:00', '10 hours');
   generate_series
---------------------
 2008-03-01 00:00:00
 2008-03-01 10:00:00
 2008-03-01 20:00:00
 2008-03-02 06:00:00
 2008-03-02 16:00:00
 2008-03-03 02:00:00
 2008-03-03 12:00:00
 2008-03-03 22:00:00
 2008-03-04 08:00:00
(9 rows)

Таблица 62. Функции генерации нижнего индекса

Функция | Тип ответа | Описание
generate_subscripts(array anyarray, dim int) | setof int | Создайте серию, содержащую индексы данного массива.
generate_subscripts(array anyarray, dim int, reverse boolean) | setof int | Создайте серию, содержащую индексы данного массива. Когда reverse верно, серия возвращается в обратном порядке.

generate_subscripts — это вспомогательная функция, которая генерирует набор допустимых подписок для указанного измерения данного массива. Нулевые строки возвращаются для массивов, у которых нет запрошенного измерения, или для массивов NULL (но действительные индексы возвращаются для элементов массива NULL). Вот несколько примеров:

-- basic usage
SELECT generate_subscripts('{NULL,1,NULL,2}'::int[], 1) AS s;
 s
---
 1
 2
 3
 4
(4 rows)

-- presenting an array, the subscript and the subscripted
-- value requires a subquery
SELECT * FROM arrays;
         a
--------------------
 {-1,-2}
 {100,200,300}
(2 rows)

SELECT a AS array, s AS subscript, a[s] AS value
FROM (SELECT generate_subscripts(a, 1) AS s, a FROM arrays) foo;
     array     | subscript | value
---------------+-----------+-------
 {-1,-2}       |         1 |    -1
 {-1,-2}       |         2 |    -2
 {100,200,300} |         1 |   100
 {100,200,300} |         2 |   200
 {100,200,300} |         3 |   300
(5 rows)

-- unnest a 2D array
CREATE OR REPLACE FUNCTION unnest2(anyarray)
RETURNS SETOF anyelement AS $$
select $1[i][j]
   from generate_subscripts($1,1) g1(i),
        generate_subscripts($1,2) g2(j);
$$ LANGUAGE sql IMMUTABLE;
CREATE FUNCTION
SELECT * FROM unnest2(ARRAY[[1,2],[3,4]]);
 unnest2
---------
       1
       2
       3
       4
(4 rows)

Когда к функции в предложении FROM добавляется суффикс WITH ORDINALITY, к bigint добавляется столбец WITH ORDINALITY, который начинается с 1 и увеличивается на 1 для каждой строки вывода функции. Это наиболее полезно в случае набора возвращающих функций, таких как unnest().

-- set returning function WITH ORDINALITY
SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n);
       ls        | n
-----------------+----
 pg_serial       |  1
 pg_twophase     |  2
 postmaster.opts |  3
 pg_notify       |  4
 qhb.conf        |  5
 pg_tblspc       |  6
 logfile         |  7
 base            |  8
 qhbmaster.pid   |  9
 qhb_ident.conf  | 10
 global          | 11
 pg_xact         | 12
 pg_snapshots    | 13
 pg_multixact    | 14
 QHB_VERSION     | 15
 pg_wal          | 16
 qhb_hba.conf    | 17
 pg_stat_tmp     | 18
 pg_subtrans     | 19
(19 rows)

Системные информационные функции и операторы

Таблица 63 показывает несколько функций, которые извлекают информацию о сеансе и системе.

В дополнение к функциям, перечисленным в этом разделе, существует ряд функций, связанных с системой статистики, которые также предоставляют системную информацию. См. раздел Просмотр статистики для получения дополнительной информации.

Таблица 63. Информационные функции сеанса

Имя | Тип ответа | Описание
current_catalog | name | имя текущей базы данных (в стандарте SQL называется « каталог »)
current_database() | name | название текущей базы данных
current_query() | text | текст текущего выполняемого запроса, отправленный клиентом (может содержать более одного оператора)
current_role | name | эквивалентно current_user
current_schema [()] | name | имя текущей схемы
current_schemas(boolean) | name[] | имена схем в пути поиска, необязательно включая неявные схемы
current_user | name | имя пользователя текущего контекста выполнения
inet_client_addr() | inet | адрес удаленного соединения
inet_client_port() | int | порт удаленного подключения
inet_server_addr() | inet | адрес локальной связи
inet_server_port() | int | порт локальной связи
pg_backend_pid() | int | Идентификатор процесса сервера, присоединенного к текущему сеансу
pg_blocking_pids(int) | int[] | Идентификаторы процесса, которые блокируют указанный идентификатор процесса сервера от получения блокировки
pg_conf_load_time() | timestamp with time zone | время загрузки конфигурации
pg_current_logfile([ text ]) | text | Основное имя файла журнала или журнал в запрошенном формате, который в данный момент используется сборщиком журналов.
pg_my_temp_schema() | oid | OID временной схемы сеанса или 0, если нет
pg_is_other_temp_schema(oid) | boolean | Схема временная схема другого сеанса?
pg_jit_available() | boolean | доступна ли компиляция JIT в этом сеансе? Возвращает false если для jit установлено значение false.
pg_listening_channels() | setof text | названия каналов, которые слушает в данный момент сеанс
pg_notification_queue_usage() | double | доля асинхронной очереди уведомлений, занятой в данный момент (0-1)
pg_postmaster_start_time() | timestamp with time zone | время запуска сервера
pg_safe_snapshot_blocking_pids(int) | int[] | Идентификаторы процессов, которые блокируют указанный идентификатор процесса сервера от получения безопасного снимка
pg_trigger_depth() | int | текущий уровень вложенности триггеров QHB (0, если не вызывается, прямо или косвенно, из триггера)
session_user | name | имя пользователя сеанса
user | name | эквивалентно current_user
version() | text | Информация о версии QHB. Смотрите также server_version_num для машиночитаемой версии.
qhb_commit_id() | text | Возвращает текущий номер зафиксированной транзакции.

current_catalog, current_role, current_schema, current_user, session_user и user имеют специальный синтаксический статус в SQL: они должны вызываться без завершающих скобок. (В QHB скобки можно использовать с current_schema, но не с остальными).

session_user обычно является пользователем, который инициировал текущее соединение с базой данных; но суперпользователи могут изменить эту настройку с помощью SET SESSION AUTHORIZATION. current_user — это идентификатор пользователя, который применим для проверки прав доступа. Обычно он равен пользователю сеанса, но его можно изменить с помощью SET ROLE. Он также изменяется во время выполнения функций с атрибутом SECURITY DEFINER. На языке Unix пользователь сеанса - это «реальный пользователь», а текущий пользователь - «эффективный пользователь». current_role и user являются синонимами для current_user. (Стандарт SQL проводит различие между current_role и current_user, но QHB этого не делает, поскольку объединяет пользователей и роли в единый вид сущностей).

current_schema возвращает имя схемы, которая является первой в пути поиска (или нулевое значение, если путь поиска пуст). Это схема, которая будет использоваться для любых таблиц или других именованных объектов, которые создаются без указания целевой схемы. current_schemas(boolean) возвращает массив имен всех схем, присутствующих в настоящее время в пути поиска. Опция Boolean определяет, включены ли в возвращаемый путь поиска неявно включенные системные схемы, такие как pg_catalog.

inet_client_addr возвращает IP-адрес текущего клиента, а inet_client_port возвращает номер порта. inet_server_addr возвращает IP-адрес, на котором сервер принял текущее соединение, а inet_server_port возвращает номер порта. Все эти функции возвращают NULL, если текущее соединение происходит через сокет Unix-домена.

pg_blocking_pids возвращает массив идентификаторов процессов сеансов, которые блокируют серверный процесс с указанным идентификатором процесса, или пустой массив, если такого серверного процесса нет или он не заблокирован. Один серверный процесс блокирует другой, если он либо содержит блокировку, которая конфликтует с запросом блокировки заблокированного процесса (жесткий блок), либо ожидает блокировку, которая будет конфликтовать с запросом блокировки заблокированного процесса, и находится впереди него в очереди ожидания (мягкий блок). При использовании параллельных запросов результат всегда содержит видимые клиентом идентификаторы процессов (то есть результаты pg_backend_pid), даже если фактическая блокировка удерживается или ожидается дочерним рабочим процессом. Соответственно в результате могут быть продублированы идентификаторы PID. Также обратите внимание, когда подготовленная транзакция содержит конфликтующую блокировку, она будет представлена нулевым идентификатором процесса в результате выполнения этой функции. Частые вызовы этой функции могут оказать некоторое влияние на производительность базы данных, поскольку для нее требуется кратковременный эксклюзивный доступ к общему состоянию диспетчера блокировки.

pg_conf_load_time возвращает временную метку с часовым поясом, когда файлы конфигурации сервера были загружены в последний раз. (Если текущий сеанс был жив в то время, это будет время, когда сам сеанс перечитывал файлы конфигурации, поэтому чтение будет немного отличаться в разных сеансах. В противном случае это время, когда процесс postmaster перечитывал файлы конфигурации).

pg_current_logfile возвращает в виде текста путь к файлам журналов, которые в данный момент используются сборщиком журналов. Путь включает каталог log_directory и имя файла журнала. Сбор журнала должен быть включен или возвращаемое значение равно NULL. Когда существует несколько файлов журнала, каждый в своем формате, pg_current_logfile вызываемый без аргументов, возвращает путь к файлу с первым форматом, найденным в упорядоченном списке: stderr, csvlog. NULL возвращается, когда ни один файл журнала не имеет ни одного из этих форматов. Чтобы запросить конкретный формат файла, укажите текстовое значение csvlog или stderr в качестве значения необязательного параметра. Возвращаемое значение равно NULL если запрошенный формат журнала не настроен как log_destination. pg_current_logfile отражает содержимое файла current_logfiles.

pg_my_temp_schema возвращает OID временной схемы текущего сеанса или ноль, если ее нет (потому что она не создала никаких временных таблиц). pg_is_other_temp_schema возвращает true, если данный OID является OID временной схемы другого сеанса. (Это может быть полезно, например, для исключения временных таблиц других сеансов из каталога).

pg_listening_channels возвращает набор имен асинхронных каналов уведомлений, которые прослушивает текущий сеанс. pg_notification_queue_usage возвращает долю от общего доступного пространства для уведомлений, которое в данный момент занято уведомлениями, ожидающими обработки, в виде double в диапазоне 0-1. См LISTEN и NOTIFY для получения дополнительной информации.

pg_postmaster_start_time возвращает временную метку с часовым поясом при запуске сервера.

pg_safe_snapshot_blocking_pids возвращает массив идентификаторов процессов сеансов, которые блокируют процесс сервера с указанным идентификатором процесса от получения безопасного снимка, или пустой массив, если такого серверного процесса нет или он не заблокирован. Сеанс, выполняющий транзакцию SERIALIZABLE блокирует транзакцию SERIALIZABLE READ ONLY DEFERRABLE от получения моментального снимка до тех пор, пока последний не определит, что безопасно избегать любых предикатных блокировок. См. раздел Уровень изоляции Serializable для получения дополнительной информации о сериализуемых и отложенных транзакциях. Частые вызовы этой функции могут оказать некоторое влияние на производительность базы данных, поскольку для нее требуется кратковременный доступ к общему состоянию менеджера блокировки предикатов.

version возвращает строку, описывающую версию сервера QHB. Вы также можете получить эту информацию из server_version или для машиночитаемой версии server_version_num. Разработчики программного обеспечения должны использовать server_version_num или PQserverVersion вместо анализа текстовой версии.

В таблице 64 перечислены функции, которые позволяют пользователю запрашивать права доступа к объекту программным способом. См. раздел Права для получения дополнительной информации о правах.

Таблица 64. Доступ к функциям запроса прав

Имя | Тип ответа | Описание
has_any_column_privilege(user, table, privilege) | boolean | имеет ли пользователь право для любого столбца таблицы
has_any_column_privilege (table, privilege) | boolean | имеет ли текущий пользователь право для любого столбца таблицы
has_column_privilege (user, table, column, privilege) | boolean | имеет ли пользователь право для столбца
has_column_privilege (table, column, privilege) | boolean | имеет ли текущий пользователь право для столбца
has_database_privilege (user, database, privilege) | boolean | имеет ли пользователь привилегию для базы данных
has_database_privilege (database, privilege) | boolean | имеет ли текущий пользователь привилегию для базы данных
has_foreign_data_wrapper_privilege (user, fdw, privilege) | boolean | имеет ли пользователь привилегию для обёртки сторонних данных
has_foreign_data_wrapper_privilege (fdw, privilege) | boolean | имеет ли текущий пользователь привилегию для обёртки сторонних данных
has_function_privilege (user, function, privilege) | boolean | имеет ли пользователь привилегию для функции
has_function_privilege (function, privilege) | boolean | имеет ли текущий пользователь привилегию для функции
has_language_privilege (user, language, privilege) | boolean | имеет ли пользователь право на язык
has_language_privilege (language, privilege) | boolean | имеет ли текущий пользователь привилегию для языка
has_schema_privilege (user, schema, privilege) | boolean | имеет ли пользователь привилегию для схемы
has_schema_privilege (schema, privilege) | boolean | имеет ли текущий пользователь привилегию для схемы
has_sequence_privilege (user, sequence, privilege) | boolean | имеет ли пользователь привилегию для последовательности
has_sequence_privilege (sequence, privilege) | boolean | имеет ли текущий пользователь привилегию для последовательности
has_server_privilege (user, server, privilege) | boolean | имеет ли пользователь привилегию для стороннего сервера
has_server_privilege (server, privilege) | boolean | имеет ли текущий пользователь привилегию для стороннего сервера
has_table_privilege (user, table, privilege) | boolean | имеет ли пользователь привилегию для таблицы
has_table_privilege (table, privilege) | boolean | имеет ли текущий пользователь привилегию для таблицы
has_tablespace_privilege (user, tablespace, privilege) | boolean | имеет ли пользователь привилегию для табличного пространства
has_tablespace_privilege (tablespace, privilege) | boolean | имеет ли текущий пользователь привилегию для табличного пространства
has_type_privilege (user, type, privilege) | boolean | имеет ли пользователь привилегию для типа
has_type_privilege (type, privilege) | boolean | имеет ли текущий пользователь привилегию для типа
pg_has_role (user, role, privilege) | boolean | имеет ли пользователь право на роль
pg_has_role (role, privilege) | boolean | имеет ли текущий пользователь право на роль
row_security_active (table) | boolean | у текущего пользователя активна защита на уровне строк для таблицы

has_table_privilege проверяет, может ли пользователь получить доступ к таблице определенным образом. Пользователь может быть указан по имени, по OID (pg_authid.oid), public чтобы указать псевдоролевую роль PUBLIC, или, если аргумент опущен, предполагается current_user. Таблица может быть указана по имени или по OID. (Таким образом, на самом деле существует шесть вариантов has_table_privilege, которые можно различить по количеству и типам их аргументов). При указании по имени имя может быть дополнено схемой при необходимости. Требуемый тип привилегий доступа указывается текстовой строкой, которая должна принимать одно из значений SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES или TRIGGER. Опционально, с WITH GRANT OPTION можно добавить тип права, чтобы проверить, удерживается ли право с опцией grant. Кроме того, несколько типов прав могут быть перечислены через запятую, и в этом случае результат будет true если удерживается какое-либо из перечисленных прав. (Случай строки права не имеет значения, и допускается наличие дополнительного пробела между именами прав, но не внутри них). Некоторые примеры:

SELECT has_table_privilege('myschema.mytable', 'select');
SELECT has_table_privilege('joe', 'mytable', 'INSERT, SELECT WITH GRANT OPTION');

has_sequence_privilege проверяет, может ли пользователь получить доступ к последовательности определенным образом. Возможности его аргументов аналогичны has_table_privilege. Требуемый тип привилегий доступа должен оцениваться как USAGE, SELECT или UPDATE.

has_any_column_privilege проверяет, может ли пользователь получить доступ к любому столбцу таблицы определенным образом. Возможности его аргументов аналогичны has_table_privilege, за исключением того, что требуемый тип привилегий доступа должен соответствовать некоторой комбинации SELECT, INSERT, UPDATE или REFERENCES. Обратите внимание, что наличие любой из этих привилегий на уровне таблицы неявно предоставляет ее для каждого столбца таблицы, поэтому has_any_column_privilege всегда будет возвращать true если has_table_privilege делает для это тех же аргументов. Но has_any_column_privilege также успешно выполняется если существует предоставление права на уровне столбца хотя бы для одного столбца.

has_column_privilege проверяет, может ли пользователь получить доступ к столбцу определенным образом. Возможности его аргументов аналогичны has_table_privilege, с добавлением, что столбец может быть указан либо по имени, либо по номеру атрибута. Требуемый тип привилегий доступа должен соответствовать некоторой комбинации SELECT, INSERT, UPDATE или REFERENCES. Обратите внимание, что наличие любой из этих привилегий на уровне таблицы неявно предоставляет ее для каждого столбца таблицы.

has_database_privilege проверяет, может ли пользователь получить доступ к базе данных определенным образом. Возможности аргументов аналогичны has_table_privilege. Требуемый тип привилегий доступа должен соответствовать некоторой комбинации CREATE, CONNECT, TEMPORARY или TEMP (которая эквивалентна TEMPORARY).

has_function_privilege проверяет, может ли пользователь получить доступ к функции определенным образом. Возможности аргументов аналогичны has_table_privilege. При указании функции текстовой строкой, а не OID, допустимый ввод такой же, как для типа данных regprocedure (см. раздел Типы идентификаторов объектов). Требуемый тип привилегий доступа должен оцениваться как EXECUTE. Примером является:

SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');

has_foreign_data_wrapper_privilege проверяет, может ли пользователь получить доступ к оболочке внешних данных определенным образом. Возможности аргументов аналогичны has_table_privilege. Требуемый тип привилегий доступа должен быть USAGE.

has_language_privilege проверяет, может ли пользователь получить доступ к процедурному языку определенным образом. Возможности аргументов аналогичны has_table_privilege. Требуемый тип привилегий доступа должен быть USAGE.

has_schema_privilege проверяет, может ли пользователь получить доступ к схеме определенным образом. Возможности аргументов аналогичны has_table_privilege. Требуемый тип привилегий доступа должен соответствовать некоторой комбинации CREATE или USAGE.

has_server_privilege проверяет, может ли пользователь получить доступ к внешнему серверу определенным образом. Возможности аргументов аналогичны has_table_privilege. Требуемый тип привилегий доступа должен быть USAGE.

has_tablespace_privilege проверяет, может ли пользователь получить доступ к табличному пространству определенным образом. Возможности аргументов аналогичны has_table_privilege. Требуемый тип привилегий доступа должен оцениваться как CREATE.

has_type_privilege проверяет, может ли пользователь получить доступ к типу определенным образом. Возможности аргументов аналогичны has_table_privilege. При указании типа текстовой строкой, а не OID, допустимый ввод такой же, как для типа данных regtype (см. раздел Типы идентификаторов объектов). Требуемый тип привилегий доступа должен быть USAGE.

pg_has_role проверяет, может ли пользователь получить доступ к роли определенным образом. Возможности его аргументов аналогичны has_table_privilege, за исключением того, что public не допускается в качестве имени пользователя. Требуемый тип привилегий доступа должен соответствовать некоторой комбинации MEMBER или USAGE. MEMBER обозначает прямое или косвенное членство в роли (то есть право делать SET ROLE), а USAGE обозначает, доступны ли права роли немедленно без выполнения SET ROLE.

row_security_active проверяет, активна ли защита на уровне строк для указанной таблицы в контексте current_user и среды. Таблица может быть указана по имени или по OID.

В таблице 65 показаны операторы, доступные для типа aclitem, который представляет собой представление прав доступа в каталоге. См. раздел Права для получения информации о том, как читать значения привилегий доступа.

Таблица 65. Операторы aclitem

Оператор | Описание | Пример | Результат
= | равный | ’calvin=r*w/hobbes’::aclitem = ’calvin=r*w*/hobbes’::aclitem | f
@> | содержит элемент | ’{calvin=r*w/hobbes, hobbes=r*w*/postgres}’::aclitem[] @> ’calvin=r*w/hobbes’::aclitem | t
~ | содержит элемент | ’{calvin=r*w/hobbes, hobbes=r*w*/postgres}’::aclitem[] ~ ’calvin=r*w/hobbes’::aclitem | t

В таблице 66 показаны некоторые дополнительные функции для управления типом aclitem.

Таблица 66. Функции aclitem

Имя | Тип ответа | Описание
acldefault (type, ownerId) | aclitem[] | получить права доступа по умолчанию для объекта, принадлежащего ownerId
aclexplode (aclitem[]) | setof record | получить массив aclitem виде кортежей
makeaclitem (grantee, grantor, privilege, grantable) | aclitem | построить aclitem из ввода

acldefault возвращает встроенные права доступа по умолчанию для объекта типа type принадлежащего роли ownerId. Они представляют права доступа, которые будут приняты, когда запись ACL объекта равна нулю. (Права доступа по умолчанию описаны в разделе Права). Типом параметра является CHAR: напишите ’c’ для COLUMN, ’r’ для TABLE и табличных объектов, ’s’ для SEQUENCE, ’d’ для DATABASE, ’f’ для FUNCTION или PROCEDURE, ’l’ для LANGUAGE, ’L’ для LARGE OBJECT, ’n для SCHEMA,’t ’ для TABLESPACE, ’F’ для FOREIGN DATA WRAPPER, ’S’ для FOREIGN SERVER или ’T' для TYPE или DOMAIN.

aclexplode возвращает массив aclitem в виде набора строк. В качестве выходных столбцов указываются oid grantor, oid grantee (0 для PUBLIC), предоставляется привилегия в виде текста (SELECT, ...) и является ли привилегия логически допустимой. makeaclitem выполняет обратную операцию.

В таблице 67 показаны функции, которые определяют, является ли определенный объект видимым в текущем пути поиска схемы. Например, таблица считается видимой, если содержащая ее схема находится в пути поиска, и таблица с таким именем не появляется ранее в пути поиска. Это эквивалентно утверждению, что на таблицу можно ссылаться по имени без явного уточнения схемы. Чтобы перечислить имена всех видимых таблиц:

SELECT relname FROM pg_class WHERE pg_table_is_visible(oid);

Таблица 67. Функции запроса видимости схемы

Имя | Тип ответа | Описание
pg_collation_is_visible(collation_oid) | boolean | сортировка видна в пути поиска
pg_conversion_is_visible(conversion_oid) | boolean | конверсия видна в пути поиска
pg_function_is_visible(function_oid) | boolean | функция видна в пути поиска
pg_opclass_is_visible(opclass_oid) | boolean | класс оператора виден в пути поиска
pg_operator_is_visible(operator_oid) | boolean | оператор виден в пути поиска
pg_opfamily_is_visible(opclass_oid) | boolean | видно ли семейство операторов в пути поиска
pg_statistics_obj_is_visible(stat_oid) | boolean | виден ли объект статистики в пути поиска
pg_table_is_visible(table_oid) | boolean | таблица видна в пути поиска
pg_ts_config_is_visible(config_oid) | boolean | видна ли конфигурация текстового поиска в пути поиска
pg_ts_dict_is_visible(dict_oid) | boolean | виден словарь текстового поиска в пути поиска
pg_ts_parser_is_visible(parser_oid) | boolean | виден ли анализатор текстового поиска в пути поиска
pg_ts_template_is_visible(template_oid) | boolean | виден ли шаблон текстового поиска в пути поиска
pg_type_is_visible(type_oid) | boolean | тип (или домен) виден в пути поиска

Каждая функция выполняет проверку видимости для одного типа объекта базы данных. Обратите внимание, что pg_table_is_visible также может использоваться с представлениями, материализованными представлениями, индексами, последовательностями и внешними таблицами; pg_function_is_visible также может использоваться с процедурами и агрегатами; pg_type_is_visible также может использоваться с доменами. Для функций и операторов объект в пути поиска виден, если ранее в пути нет объекта с таким же именем и типом данных аргумента. Для классов операторов рассматриваются как имя, так и связанный метод доступа к индексу.

Все эти функции требуют OID объекта для идентификации объекта, подлежащего проверке. Если вы хотите проверить объект по имени, удобно использовать псевдонимы OID (regclass, regtype, regprocedure, regoperator, regconfig или regdictionary), например:

SELECT pg_type_is_visible('myschema.widget'::regtype);

Обратите внимание, что не имеет смысла тестировать имя типа, не являющееся схемой, таким образом - если имя вообще можно распознать, оно должно быть видимым.

В таблице 68 перечислены функции, которые извлекают информацию из системных каталогов.

Таблица 68. Информационные функции системного каталога

Имя | Тип ответа | Описание
format_type(type_oid, typemod) | text | получить имя SQL типа данных
pg_get_constraintdef(constraint_oid) | text | получить определение ограничения
pg_get_constraintdef(constraint_oid, pretty_bool) | text | получить определение ограничения
pg_get_expr(pg_node_tree, relation_oid) | text | декомпилировать внутреннюю форму выражения, предполагая, что любые переменные в нем относятся к отношению, указанному вторым параметром
pg_get_expr(pg_node_tree, relation_oid, pretty_bool) | text | декомпилировать внутреннюю форму выражения, предполагая, что любые переменные в нем относятся к отношению, указанному вторым параметром
pg_get_functiondef(func_oid) | text | получить определение функции или процедуры
pg_get_function_arguments(func_oid) | text | получить список аргументов определения функции или процедуры (со значениями по умолчанию)
pg_get_function_identity_arguments(func_oid) | text | получить список аргументов для идентификации функции или процедуры (без значений по умолчанию)
pg_get_function_result(func_oid) | text | получить предложение RETURNS для функции (возвращает ноль для процедуры)
pg_get_indexdef(index_oid) | text | получить команду CREATE INDEX для индекса
pg_get_indexdef(index_oid, column_no, pretty_bool) | text | получить команду CREATE INDEX для индекса или определение только одного столбца индекса, когда column_no не равно нулю
pg_get_keywords() | setof record | получить список ключевых слов SQL и их категорий
pg_get_ruledef(rule_oid) | text | получить команду CREATE RULE для правила
pg_get_ruledef(rule_oid, pretty_bool) | text | получить команду CREATE RULE для правила
pg_get_serial_sequence(table_name, column_name) | text | получить имя последовательности, которую использует столбец серийного номера или идентификатора
pg_get_statisticsobjdef(statobj_oid) | text | получить команду CREATE STATISTICS для объекта расширенной статистики
pg_get_triggerdef (trigger_oid) | text | CREATE [ CONSTRAINT ] TRIGGER get CREATE [ CONSTRAINT ] TRIGGER для триггера
pg_get_triggerdef (trigger_oid, pretty_bool) | text | CREATE [ CONSTRAINT ] TRIGGER get CREATE [ CONSTRAINT ] TRIGGER для триггера
pg_get_userbyid(role_oid) | name | получить имя роли с заданным OID
pg_get_viewdef(view_name) | text | получить базовую команду SELECT для представления или материализованного представления (устарело)
pg_get_viewdef(view_name, pretty_bool) | text | получить базовую команду SELECT для представления или материализованного представления (устарело)
pg_get_viewdef(view_oid) | text | получить базовую команду SELECT для представления или материализованного представления
pg_get_viewdef(view_oid, pretty_bool) | text | получить базовую команду SELECT для представления или материализованного представления
pg_get_viewdef(view_oid, wrap_column_int) | text | получить базовую команду SELECT для представления или материализованного представления; строки с полями переносятся в указанное количество столбцов, подразумевается симпатичная печать
pg_index_column_has_property(index_oid, column_no, prop_name) | boolean | проверить, имеет ли столбец индекса указанное свойство
pg_index_has_property(index_oid, prop_name) | boolean | проверить, имеет ли индекс указанное свойство
pg_indexam_has_property(am_oid, prop_name) | boolean | проверить, есть ли у метода доступа к индексу указанное свойство
pg_options_to_table(reloptions) | setof record | получить набор пар имя / значение опции хранения
pg_tablespace_databases(tablespace_oid) | setof oid | получить набор OID базы данных, которые имеют объекты в табличном пространстве
pg_tablespace_location(tablespace_oid) | text | получить путь в файловой системе, в которой находится это табличное пространство
pg_typeof(any) | regtype | получить тип данных любого значения
collation for (any) | text | получить сопоставление аргумента
to_regclass(rel_name) | regclass | получить OID названного отношения
to_regproc(func_name) | regproc | получить OID названной функции
to_regprocedure(func_name) | regprocedure | получить OID названной функции
to_regoper(operator_name) | regoper | получить OID указанного оператора
to_regoperator(operator_name) | regoperator | получить OID указанного оператора
to_regtype(type_name) | regtype | получить OID названного типа
to_regnamespace(schema_name) | regnamespace | получить OID названной схемы
to_regrole(role_name) | regrole | получить OID указанной роли

format_type возвращает имя SQL типа данных, идентифицируемого его типом OID и, возможно, модификатором типа. Передайте NULL для модификатора типа, если конкретный модификатор не известен.

pg_get_keywords возвращает набор записей, описывающих ключевые слова SQL, распознаваемые сервером. Столбец word содержит ключевое слово. catcode содержит код категории: U для незарезервированного, C для имени столбца, T для имени типа или функции или R для зарезервированного. catdesc содержит возможно локализованную строку, описывающую категорию.

pg_get_constraintdef, pg_get_indexdef, pg_get_ruledef, pg_get_statisticsobjdef и pg_get_triggerdef соответственно восстанавливают команду создания для ограничения, индекса, правила, расширенного объекта статистики или триггера. (Обратите внимание, что это декомпилированная реконструкция, а не исходный текст команды). pg_get_expr декомпилирует внутреннюю форму отдельного выражения, например значение по умолчанию для столбца. Это может быть полезно при проверке содержимого системных каталогов. Если выражение может содержать Vars, укажите OID отношения, на которое они ссылаются, как на второй параметр; если не ожидаются Vars, достаточно нуля. pg_get_viewdef восстанавливает запрос SELECT который определяет представление. Большинство этих функций представлены в двух вариантах, один из которых может при желании «красиво распечатать» результат. Формат pretty-printed более читабелен, но формат по умолчанию, скорее всего, будет интерпретирован аналогичным образом в будущих версиях QHB; Избегайте использования pretty-printed вывода для дампа. Передача значения false для параметра pretty-print дает тот же результат, что и для варианта, в котором этот параметр вообще отсутствует.

pg_get_functiondef возвращает полный оператор CREATE OR REPLACE FUNCTION для функции. pg_get_function_arguments возвращает список аргументов функции в той форме, в которой она должна отображаться в CREATE FUNCTION. pg_get_function_result аналогичным образом возвращает соответствующее предложение RETURNS для функции. pg_get_function_identity_arguments возвращает список аргументов, необходимый для идентификации функции, например, в той форме, в которой она должна отображаться в ALTER FUNCTION. Эта форма опускает значения по умолчанию.

pg_get_serial_sequence возвращает имя последовательности, связанной со столбцом, или NULL, если последовательность не связана со столбцом. Если столбец является столбцом идентификаторов, связанная последовательность представляет собой последовательность, внутренне созданную для столбца идентификаторов. Для столбцов, созданных с использованием одного из серийных типов (serial, smallserial, bigserial), это последовательность, созданная для этого определения последовательного столбца. В последнем случае эта связь может быть изменена или удалена с помощью команды ALTER SEQUENCE OWNED BY. Первый входной параметр — это имя таблицы с необязательной схемой, а второй параметр - имя столбца. Поскольку первый параметр потенциально является схемой и таблицей, он не обрабатывается как идентификатор в двойных кавычках, то есть по умолчанию он находится в нижнем регистре, а второй параметр, являющийся просто именем столбца, рассматривается как дважды заключенный в кавычки и его регистр сохраняется. Функция возвращает значение, соответствующим образом отформатированное для передачи в функции последовательности (см. раздел Функции управления последовательностями). Типичное использование - чтение текущего значения последовательности для идентификатора или последовательного столбца, например:

SELECT currval(pg_get_serial_sequence('sometable', 'id'));

pg_get_userbyid извлекает имя роли с учетом ее OID.

pg_index_column_has_property, pg_index_has_property и pg_indexam_has_property возвращают, обладает ли указанный столбец индекса, метод индекса или метод доступа к индексу указанным свойством. NULL возвращается, если имя свойства неизвестно или не относится к конкретному объекту, или если OID или номер столбца не идентифицируют допустимый объект. См. Таблицу 8.69 для свойств столбца, Таблицу 8.70 для свойств индекса и Таблицу 8.71 для свойств метода доступа. (Обратите внимание, что методы доступа расширения могут определять дополнительные имена свойств для своих индексов).

Таблица 69. Свойства столбца индекса

Имя | Описание
asc | Сортирует ли столбец в порядке возрастания при сканировании вперед?
desc | Сортирует ли столбец в порядке убывания при сканировании вперед?
nulls_first | Сортирует ли столбец сначала пустые значения при сканировании вперед?
nulls_last | Сортируется ли столбец с нулями в последний раз при сканировании вперед?
orderable | Имеет ли столбец какой-либо определенный порядок сортировки?
distance_orderable | Может ли столбец сканироваться по порядку оператором « расстояния », например, ORDER BY col <-> constant ?
returnable | Может ли значение столбца быть возвращено при сканировании только по индексу?
search_array | Поддерживает ли данный столбец поиск col = ANY(array) ?
search_nulls | Поддерживает ли столбец поиск IS NULL и IS NOT NULL ?

Таблица 70. Свойства индекса

Имя | Описание
clusterable | Можно ли использовать индекс в команде CLUSTER ?
index_scan | Поддерживает ли индекс обычное (не растровое) сканирование?
bitmap_scan | Индекс поддерживает растровое сканирование?
backward_scan | Можно ли изменить направление сканирования в середине сканирования (для поддержки FETCH BACKWARD на курсоре без необходимости материализации)?

Таблица 71. Свойства метода доступа индекса

Имя | Описание
can_order | Поддерживает ли метод доступа ASC, DESC и связанные ключевые слова в CREATE INDEX ?
can_unique | Поддерживает ли метод доступа уникальные индексы?
can_multi_col | Поддерживает ли метод доступа индексы с несколькими столбцами?
can_exclude | Поддерживает ли метод доступа ограничения исключения?
can_include | Поддерживает ли метод доступа предложение INCLUDE в CREATE INDEX ?

pg_options_to_table возвращает набор пар имя/значение параметра хранения (option_name/option_value) при передаче pg_class.reloptions или pg_attribute.attoptions.

pg_tablespace_databases позволяет исследовать табличное пространство. Он возвращает набор OID баз данных, имеющих объекты, хранящиеся в табличном пространстве. Если эта функция возвращает какие-либо строки, табличное пространство не является пустым и не может быть удалено. Для отображения конкретных объектов, заполняющих табличное пространство, вам необходимо подключиться к базам данных, идентифицированным pg_tablespace_databases и запросить их каталоги pg_class.

pg_typeof возвращает OID типа данных переданного ему значения. Это может быть полезно для устранения неполадок или динамического построения запросов SQL. Функция объявлена как возвращающая regtype, который является типом псевдонима OID (см. раздел Типы идентификаторов объектов); это означает, что он совпадает с OID для сравнения, но отображается как имя типа. Например:

SELECT pg_typeof(33);

 pg_typeof
-----------
 integer
(1 row)

SELECT typlen FROM pg_type WHERE oid = pg_typeof(33);
 typlen
--------
      4
(1 row)

Выражение collation for возвращает сличение переданного ему значения. Пример:

SELECT collation for (description) FROM pg_description LIMIT 1;
 pg_collation_for
------------------
 "default"
(1 row)

SELECT collation for ('foo' COLLATE "de_DE");
 pg_collation_for
------------------
 "de_DE"
(1 row)

Значение может быть заключено в кавычки и дополнено схемой. Если параметры сортировки не выводятся для выражения аргумента, то возвращается null значение. Если аргумент не имеет сопоставимого типа данных, то возникает ошибка.

Функции to_regclass, to_regproc, to_regprocedure, to_regoper, to_regoperator, to_regtype, to_regnamespace и to_regrole выполняют преобразование имен отношений, функций, операторов, типов, схем, и ролей (заданных в виде текста) в объекты типа regclass, regproc, regprocedure, regoper, regoperator, regtype, regnamespace и regrole соответственно. Эти функции отличаются от преобразования из текста тем, что они не принимают числовой OID, и что они возвращают null а не выдают ошибку, если имя не найдено (или, например, to_regproc и to_regoper, если данное имя соответствует нескольким объектам).

В таблице 72 перечислены функции, связанные с идентификацией и адресацией объекта базы данных.

Таблица 72. Информация об объекте и функции адресации

Имя | Тип ответа | Описание
pg_describe_object(classid oid, objid oid, objsubid integer) | text | получить описание объекта базы данных
pg_identify_object(classid oid, objid oid, objsubid integer) | type text, text schema text name text identity | получить идентификатор объекта базы данных
pg_identify_object_as_address(classid oid, objid oid, objsubid integer) | type text, object_names text[], object_args text[] | получить внешнее представление адреса объекта базы данных
pg_get_object_address(type text, object_names text[], object_args text[]) | classid, objid, objsubid integer | получить адрес объекта базы данных из его внешнего представления

pg_describe_object возвращает текстовое описание объекта базы данных, указанного в OID каталога, OID объекта и идентификаторе pg_describe_object (например, номер столбца в таблице; идентификатор подобъекта равен нулю при обращении к целому объекту). Это описание предназначено для чтения человеком и может быть переведено в зависимости от конфигурации сервера. Это полезно для определения идентичности объекта, хранящегося в каталоге pg_depend.

pg_identify_object возвращает строку, содержащую достаточно информации для уникальной идентификации объекта базы данных, указанного в OID каталога, OID объекта и идентификатора pg_identify_object. Эта информация предназначена для машинного чтения и никогда не переводится. type определяет тип объекта базы данных; schema - это имя схемы, к которой принадлежит объект, или NULL для типов объектов, которые не принадлежат схемам; name - это имя объекта, которое при необходимости указывается в кавычках, если имя (вместе с именем схемы, если это уместно) достаточно для однозначной идентификации объекта, в противном случае NULL; identity - это полная идентичность объекта, с точным форматом, зависящим от типа объекта, и каждое имя в формате уточняется схемой и при необходимости указывается в кавычках.

pg_identify_object_as_address возвращает строку, содержащую достаточно информации, чтобы однозначно идентифицировать объект базы данных, указанный в OID каталога, OID объекта и ID подобъекта. Возвращаемая информация не зависит от текущего сервера, то есть ее можно использовать для идентификации объекта с таким же именем на другом сервере. type определяет тип объекта базы данных; object_names и object_args - это текстовые массивы, которые вместе образуют ссылку на объект. Эти три значения могут быть переданы в pg_get_object_address для получения внутреннего адреса объекта. Эта функция является обратной к pg_get_object_address.

pg_get_object_address возвращает строку, содержащую достаточно информации для уникальной идентификации объекта базы данных, указанного его типом, именем объекта и массивами аргументов. Возвращаемые значения

это те, которые будут использоваться в системных каталогах, таких как pg_depend, и могут быть переданы другим системным функциям, таким как pg_identify_object или pg_describe_object. classid - OID системного каталога, содержащего объект; objid - это OID самого объекта, а objsubid

это идентификатор подобъекта, или ноль, если его нет. Эта функция является обратной к pg_identify_object_as_address.

Функции, показанные в таблице 73, извлекают комментарии, ранее сохраненные с помощью команды COMMENT. Возвращается нулевое значение, если для указанных параметров не найдено ни одного комментария.

Таблица 73. Comment Information Functions

Имя | Тип ответа | Описание
col_description(table_oid, column_number) | text | получить комментарий к столбцу таблицы
obj_description(object_oid, catalog_name) | text | получить комментарий для объекта базы данных
obj_description(object_oid) | text | получить комментарий для объекта базы данных (не рекомендуется)
shobj_description(object_oid, catalog_name) | text | получить комментарий для общего объекта базы данных

col_description возвращает комментарий для столбца таблицы, который определяется OID ее таблицы и номером столбца. (obj_description нельзя использовать для столбцов таблицы, поскольку столбцы не имеют собственных идентификаторов OID).

Двухпараметрическая форма bj_description возвращает комментарий для объекта базы данных, указанного его OID и именем содержащего системный каталог. Например, obj_description (123456, ’pg_class’) извлекает комментарий для таблицы с OID 123456. Форма obj_description с одним параметром требует только OID объекта. Он не рекомендуется, поскольку нет гарантии, что OID уникальны в разных системных каталогах; следовательно, может быть возвращен неверный комментарий.

shobj_description используется точно так же, как obj_description, за исключением того, что он используется для получения комментариев к общим объектам. Некоторые системные каталоги являются глобальными для всех баз данных в каждом кластере, и описания объектов в них также хранятся глобально.

Функции, показанные в таблице 74, предоставляют информацию о транзакциях сервера в экспортируемой форме. Основное использование этих функций - определить, какие транзакции были совершены между двумя моментальными снимками.

Таблица 74. Идентификаторы транзакций и снимки

Имя | Тип ответа | Описание
txid_current() | bigint | получить идентификатор текущей транзакции, назначив новый, если у текущей транзакции его нет
txid_current_if_assigned() | bigint | то же самое, что и txid_current (), но возвращает ноль вместо назначения нового идентификатора транзакции, если ни один уже не назначен
txid_current_snapshot() | txid_snapshot | получить текущий снимок
txid_snapshot_xip(txid_snapshot) | setof bigint | получить текущие идентификаторы транзакций в снимке
txid_snapshot_xmax(txid_snapshot) | bigint | получить xmax снимка
txid_snapshot_xmin(txid_snapshot) | bigint | получить xmin снимка
txid_visible_in_snapshot(bigint, txid_snapshot) | boolean | виден идентификатор транзакции в снимке? (не используйте с идентификаторами субтранзакций)
txid_status(bigint) | text | сообщить о статусе данной транзакции: зафиксировано, прервано, выполняется или пусто, если идентификатор транзакции слишком старый

Внутренний тип идентификатора транзакции (xid) имеет ширину 32 бита и охватывает каждые 4 миллиарда транзакций. Тем не менее, эти функции экспортируют 64-битный формат, который расширен счетчиком «эпох», поэтому он не будет меняться в течение срока службы установки. Тип данных, используемый этими функциями, txid_snapshot, хранит информацию о видимости идентификатора транзакции в определенный момент времени. Его компоненты описаны в таблице 75.

Таблица 75. Компоненты снимка

Имя
Описание

xmin
Самый ранний идентификатор транзакции (txid), который все еще активен. Все более ранние транзакции будут либо зафиксированы и видимы, либо отменены.

xmax
Первый пока не назначенный txid. Все txids, больше или равные этому, еще не запущены на момент моментального снимка и поэтому невидимы.

xip_list
Активные txids во время снимка. Список включает только те активные txids между xmin и xmax; там могут быть активные txids выше, чем xmax. Txid с xmin <= txid <xmax, которого нет в этом списке, уже был завершен во время моментального снимка и, таким образом, либо видим, либо мертв в соответствии с его состоянием фиксации. Список не включает txids субтранзакций.

Текстовое представление txid_snapshot - это xmin: xmax: xip_list. Например, 10: 20: 10,14,15 означает *xmin = 10, xmax = 20, xip_list = 10, 14, 15.

txid_status (bigint) сообщает о состоянии фиксации недавней транзакции. Приложения могут использовать его для определения того, была ли транзакция совершена или прервана, когда сервер приложений и базы данных отключился во время выполнения COMMIT. О статусе транзакции будет сообщено как о том, что она выполняется, зафиксирована или прервана, при условии, что транзакция достаточно недавняя, чтобы система сохранила статус фиксации этой транзакции. Если статус достаточно стар, чтобы в системе не сохранилось ни одной ссылки на эту транзакцию, а информация о состоянии фиксации была отброшена, эта функция вернет NULL. Обратите внимание, что подготовленные транзакции отображаются как выполняемые; приложения должны проверить pg_prepared_xacts, если им нужно определить, является ли txid подготовленной транзакцией.

Функции, показанные в таблице 76, предоставляют информацию о транзакциях, которые уже были совершены. Эти функции в основном предоставляют информацию о том, когда транзакции были совершены. Они предоставляют полезные данные только при включенной опции конфигурации track_commit_timestamp и только для транзакций, которые были зафиксированы после его включения.

Таблица 76. Информация о совершенной транзакции

Имя | Тип ответа | Описание
pg_xact_commit_timestamp(xid) | timestamp with time zone | получить фиксацию времени транзакции
pg_last_committed_xact() | xid xid, timestamp timestamp with time zone | получить идентификатор транзакции и метку времени последней совершенной транзакции

Функции, показанные в таблице 77, печатают информацию, инициализированную во время initdb, такую как версия каталога. Они также показывают информацию о записи в журнал упреждающей записи и обработке контрольных точек. Эта информация распространяется на весь кластер и не относится к какой-либо одной базе данных. Они предоставляют большую часть той же информации из того же источника, что и qhb_controldata, хотя в форме, лучше подходящей для функций SQL.

Таблица 77. Функции управления данными

Имя | Тип ответа | Описание
pg_control_checkpoint() | record | Возвращает информацию о текущем состоянии контрольной точки.
pg_control_system() | record | Возвращает информацию о текущем состоянии контрольного файла.
pg_control_init() | record | Возвращает информацию о состоянии инициализации кластера.
pg_control_recovery() | record | Возвращает информацию о состоянии восстановления.

pg_control_checkpoint возвращает запись, показанную в таблице 78

Таблица 78. pg_control_checkpoint Columns

Имя столбца | Тип данных
checkpoint_lsn | pg_lsn
redo_lsn | pg_lsn
redo_wal_file | text
timeline_id | integer
prev_timeline_id | integer
full_page_writes | boolean
next_xid | text
next_oid | oid
next_multixact_id | xid
next_multi_offset | xid
oldest_xid | xid
oldest_xid_dbid | oid
oldest_active_xid | xid
oldest_multi_xid | xid
oldest_multi_dbid | oid
oldest_commit_ts_xid | xid
newest_commit_ts_xid | xid
checkpoint_time | timestamp with time zone

pg_control_system возвращает запись, показанную в таблице 79

Таблица 79. Столбцы pg_control_system

Имя столбца | Тип данных
pg_control_version | integer
catalog_version_no | integer
system_identifier | bigint
pg_control_last_modified | timestamp with time zone

pg_control_init возвращает запись, показанную в таблице 80

Таблица 80. Столбцы pg_control_init

Имя столбца | Тип данных
max_data_alignment | integer
database_block_size | integer
blocks_per_segment | integer
wal_block_size | integer
bytes_per_wal_segment | integer
max_identifier_length | integer
max_index_columns | integer
max_toast_chunk_size | integer
large_object_chunk_size | integer
float4_pass_by_value | boolean
float8_pass_by_value | boolean
data_page_checksum_version | integer

pg_control_recovery возвращает запись, показанную в таблице 81

Таблица 81. Столбцы pg_control_recovery

Имя столбца | Тип данных
min_recovery_end_lsn | pg_lsn
min_recovery_end_timeline | integer
backup_start_lsn | pg_lsn
backup_end_lsn | pg_lsn
end_of_backup_record_required | boolean

Функции системного администрирования

Функции, описанные в этом разделе, используются для контроля и мониторинга установки QHB.

Функции настройки конфигурации

В таблице 82 показаны функции, доступные для запроса и изменения параметров конфигурации во время выполнения.

Таблица 82. Функции настройки конфигурации

Имя | Тип ответа | Описание
current_setting(setting_name [, missing_ok ]) | text | получить текущее значение настройки
set_config(setting_name, new_value, is_local) | text | установить параметр и вернуть новое значение

Функция current_setting выдает текущее значение параметра setting_name. Это соответствует команде SQL SHOW. Пример:

SELECT current_setting('datestyle');

 current_setting
-----------------
 ISO, MDY
(1 row)

Если параметр с именем setting_name отсутствует, current_setting выдает ошибку, если не указано значение missing_ok и значение true.

set_config устанавливает параметр setting_name в new_value. Если is_local имеет значение true, новое значение будет применяться только к текущей транзакции. Если вы хотите, чтобы новое значение применялось к текущему сеансу, используйте вместо этого false. Функция соответствует SQL-команде SET. Пример:

SELECT set_config('log_statement_stats', 'off', false);

 set_config
------------
 off
(1 row)

Функции сигнализации сервера

Функции, показанные в таблице 83, отправляют управляющие сигналы другим процессам сервера. Использование этих функций ограничено суперпользователями по умолчанию, но доступ может быть предоставлен другим, использующим GRANT, с отмеченными исключениями.

Таблица 83. Функции сигнализации сервера

Имя | Тип ответа | Описание
pg_cancel_backend(pid int) | boolean | Отмените текущий запрос бэкенда. Это также допустимо, если вызывающая роль является членом роли, бэкенд которой отменяется, или вызывающая роль была предоставлена pg_signal_backend, однако только суперпользователи могут отменять pg_signal_backend суперпользователя.
pg_reload_conf() | boolean | Заставить процессы сервера перезагрузить свои файлы конфигурации
pg_rotate_logfile() | boolean | Повернуть файл журнала сервера
pg_terminate_backend(pid int) | boolean | Завершить бэкенд. Это также разрешено, если вызывающая роль является членом роли, бэкенд которой завершается, или вызывающей роли был предоставлен pg_signal_backend, однако только суперпользователи могут завершить pg_signal_backend суперпользователя.

Каждая из этих функций возвращает true случае успеха и false противном случае.

pg_cancel_backend и pg_terminate_backend отправляют сигналы (SIGINT или SIGTERM соответственно) на внутренние процессы, идентифицируемые по идентификатору процесса. Идентификатор процесса активного бэкенда можно найти в столбце pid представления pg_stat_activity или в списке процессов qhb на сервере (используя ps в Unix). Роль активного бэкенда можно найти в столбце usename представления pg_stat_activity.

pg_reload_conf отправляет сигнал SIGHUP на сервер, вызывая перезагрузку файлов конфигурации всеми процессами сервера.

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

Функции управления резервным копированием

Функции, показанные в таблице 84, помогают создавать резервные копии в режиме онлайн. Эти функции не могут быть выполнены во время восстановления (кроме неисключительных pg_start_backup, неисключительных pg_stop_backup, pg_is_in_backup, pg_backup_start_time и pg_wal_lsn_diff).

Таблица 84. Функции управления резервным копированием

Имя | Тип ответа | Описание
pg_create_restore_point(name text) | pg_lsn | Создать именованную точку для выполнения восстановления (по умолчанию ограничено суперпользователями, но другим пользователям может быть предоставлено разрешение EXECUTE для запуска функции).
pg_current_wal_flush_lsn() | pg_lsn | Получить текущее местоположение для записи журнала впереди
pg_current_wal_insert_lsn() | pg_lsn | Получить текущее местоположение вставки журнала записи
pg_current_wal_lsn() | pg_lsn | Получить текущее местоположение записи в журнал записи
pg_start_backup(label text [, fast boolean [, exclusive boolean ] ]) | pg_lsn | Подготовьтесь к выполнению резервного копирования в онлайн-хранилище (по умолчанию ограничено суперпользователями, но другим пользователям может быть предоставлено разрешение EXECUTE для запуска функции)
pg_stop_backup() | pg_lsn | Завершите выполнение эксклюзивного резервного копирования в онлайн-хранилище (по умолчанию ограничено суперпользователями, но другим пользователям может быть предоставлено разрешение EXECUTE для запуска функции)
pg_stop_backup(exclusive boolean [, wait_for_archive boolean ]) | setof record | Завершите выполнение эксклюзивного или неисключительного резервного копирования в режиме онлайн (по умолчанию ограничено суперпользователями, но другим пользователям может быть предоставлено разрешение EXECUTE для запуска функции)
pg_is_in_backup() | bool | Истинно, если онлайновое эксклюзивное резервное копирование все еще выполняется.
pg_backup_start_time() | timestamp with time zone | Получите время запуска эксклюзивного резервного копирования онлайн.
pg_switch_wal() | pg_lsn | Принудительное переключение на новый файл журнала с опережением записи (по умолчанию ограничено суперпользователями, но другим пользователям может быть предоставлено EXECUTE для запуска функции).
pg_walfile_name(lsn pg_lsn) | text | Преобразовать местоположение журнала записи вперед в имя файла
pg_walfile_name_offset(lsn pg_lsn) | text, integer | Преобразовать местоположение журнала упреждающей записи в имя файла и десятичное смещение байта в файле
pg_wal_lsn_diff(lsn pg_lsn, lsn pg_lsn) | numeric | Рассчитайте разницу между двумя местоположениями журнала записи

pg_start_backup принимает произвольную пользовательскую метку для резервной копии. (Обычно это имя, под которым будет сохранен файл резервного дампа). При использовании в монопольном режиме функция записывает файл метки резервной копии (backup_label) и, если есть какие-либо ссылки в каталоге pg_tblspc/, карты табличного пространства (tablespace_map) в каталог данных кластера базы данных, выполняет контрольную точку, а затем возвращает начальное местоположение резервной копии журнала упреждающей записи в виде текста. Пользователь может игнорировать это значение результата, но оно предоставляется в случае, если оно полезно. При использовании в неисключительном режиме содержимое этих файлов вместо этого возвращается функцией pg_stop_backup и должно быть записано вызывающей стороной в резервную копию.

qhb=# select pg_start_backup('label_goes_here');
 pg_start_backup
-----------------
 0/D4445B8
(1 row)

Существует необязательный второй параметр типа boolean. Если true, это указывает на выполнение pg_start_backup как можно быстрее. Это форсирует немедленную контрольную точку, которая вызовет всплеск операций ввода-вывода, замедляя любые параллельно выполняющиеся запросы.

В исключительной резервной копии pg_stop_backup удаляет файл метки и, если он существует, файл tablespace_map созданный pg_start_backup. В неисключительной резервной копии содержимое backup_label и ablespace_map возвращается в результате работы функции и должно быть записано в файлы резервной копии (а не в каталог данных). Существует необязательный второй параметр типа boolean. Если false, pg_stop_backup вернется сразу после завершения резервного копирования, не дожидаясь архивирования WAL. Такое поведение полезно только для программного обеспечения резервного копирования, которое независимо контролирует архивирование WAL. В противном случае WAL, необходимый для обеспечения согласованности резервного копирования, может отсутствовать и сделать резервную копию бесполезной. Если для этого параметра установлено значение true, pg_stop_backup будет ожидать архивирования WAL, когда включено архивирование; в режиме ожидания это означает, что он будет ждать только, когда archive_mode = always. Если активность записи на основной стороне низкая, может быть полезно запустить pg_switch_wal на основной стороне, чтобы инициировать немедленное переключение сегмента.

При выполнении на первичном сервере функция также создает файл истории резервного копирования в области архива журнала упреждающей записи. Файл истории содержит метку, присвоенную pg_start_backup, начальный и конечный местоположения журналов упреждающей записи для резервной копии, а также время начала и окончания резервного копирования. Возвращаемое значение — это конечное местоположение журнала записи с опережением записи (которое снова можно игнорировать). После записи конечного местоположения текущая точка вставки журнала с опережением записи автоматически перемещается к следующему файлу журнала с опережением записи, так что конечный файл журнала с опережением записи может быть немедленно заархивирован для завершения резервного копирования.

pg_switch_wal переходит к следующему файлу журнала с опережением записи, что позволяет архивировать текущий файл (при условии, что вы используете непрерывное архивирование). Возвращаемое значение - конечное местоположение журнала записи с опережением + 1 в только что завершенном файле журнала записи с опережением. Если с момента последнего переключения журнала с опережением записи не было никаких действий с опережением записи, pg_switch_wal ничего не делает и возвращает начальное местоположение файла журнала с опережением записи, который используется в данный момент.

pg_create_restore_point создает именованную запись журнала предварительной записи, которую можно использовать в качестве цели восстановления, и возвращает соответствующее местоположение журнала предварительной записи. Затем указанное имя можно использовать с recovery_target_name, чтобы указать точку, до которой будет продолжаться восстановление. Избегайте создания нескольких точек восстановления с одним и тем же именем, поскольку восстановление остановится на первой точке, имя которой соответствует цели восстановления.

pg_current_wal_lsn отображает текущее местоположение записи в журнал записи вперед в том же формате, что и вышеуказанные функции. Аналогично, pg_current_wal_insert_lsn отображает текущее местоположение вставки журнала с опережением записи, а pg_current_wal_flush_lsn отображает текущее местоположение pg_current_wal_flush_lsn журнала с опережением записи. Место вставки является «логическим» концом журнала упреждающей записи в любой момент, тогда как место записи — это конец того, что было фактически записано из внутренних буферов сервера, а место сброса — это место, которое гарантированно будет записано в долговременный место хранения. Расположение записи — это конец того, что можно проверить извне сервера, и обычно это то, что вам нужно, если вы заинтересованы в архивировании частично полных файлов журнала с опережением записи. Места для вставки и сброса доступны главным образом для отладки сервера. Обе эти операции предназначены только для чтения и не требуют прав суперпользователя.

Вы можете использовать pg_walfile_name_offset для извлечения соответствующего имени файла журнала записи с опережением записи и смещения байтов из результатов любой из вышеперечисленных функций. Например:

qhb=# SELECT * FROM pg_walfile_name_offset(pg_stop_backup());
        file_name         | file_offset
--------------------------+-------------
 00000001000000000000000D |     4039624
(1 row)

Аналогично, pg_walfile_name извлекает только имя файла журнала записи с опережением. Когда указанное местоположение журнала предварительной записи находится точно на границе файла журнала предварительной записи, обе эти функции возвращают имя предыдущего файла журнала предварительной записи. Обычно это желаемое поведение для управления поведением архивирования журнала с опережением записи, поскольку предыдущий файл является последним, который в настоящее время необходимо архивировать.

pg_wal_lsn_diff вычисляет разницу в байтах между двумя местоположениями журнала записи с опережением. Его можно использовать с pg_stat_replication или некоторыми функциями, показанными в таблице 4.84, для получения задержки репликации.

Подробнее о правильном использовании этих функций см. в разделе Непрерывное архивирование и восстановление на момент времени (PITR).

Функции управления восстановлением

Функции, показанные в таблице 85, предоставляют информацию о текущем состоянии режима ожидания. Эти функции могут выполняться как во время восстановления, так и в нормальном режиме.

Таблица 85. Функции восстановления информации

Имя
Тип ответа
Описание

pg_is_in_recovery()
bool
Верно, если восстановление все еще продолжается.

pg_last_wal_receive_lsn()
pg_lsn
Получите последнее местоположение журнала предварительной записи, полученное и синхронизированное на диск потоковой репликацией. Пока идет потоковая репликация, она будет монотонно возрастать. Если восстановление завершено, оно останется неизменным со значением последней записи WAL, полученной и синхронизированной с диском во время восстановления. Если потоковая репликация выключена или она еще не запущена, функция возвращает NULL.

pg_last_wal_replay_lsn()
pg_lsn
Получите последнюю запись журнала перед воспроизведением во время восстановления. Если восстановление еще продолжается, оно будет монотонно возрастать. Если восстановление завершено, то это значение останется неизменным на уровне последней записи WAL, примененной во время этого восстановления. Когда сервер был запущен нормально без восстановления, функция возвращает NULL.

pg_last_xact_replay_timestamp()
timestamp with time zone
Получить метку времени последней транзакции, воспроизведенной во время восстановления. Это время, когда на первичном сервере была сгенерирована запись WAL для этой транзакции. Если ни одна транзакция не была воспроизведена во время восстановления, эта функция возвращает NULL. В противном случае, если восстановление еще продолжается, оно будет увеличиваться монотонно. Если восстановление завершено, то это значение останется неизменным на уровне последней транзакции, примененной во время этого восстановления. Когда сервер был запущен нормально без восстановления, функция возвращает NULL.

Функции, показанные в таблице 86, контролируют ход восстановления. Эти функции могут быть выполнены только во время восстановления.

Таблица 86. Функции управления восстановлением

Имя
Тип ответа
Описание

pg_is_wal_replay_paused()
bool
Истинно, если восстановление приостановлено.

pg_promote(wait boolean DEFAULT true, wait_seconds integer DEFAULT 60)
boolean
Продвигает физический резервный сервер. Если для параметра wait установлено значение true (по умолчанию), функция ожидает, пока продвижение будет завершено или wait_seconds секунды wait_seconds, и возвращает true если продвижение прошло успешно, и false противном случае. Если для wait установлено значение false, функция возвращает значение true сразу после отправки сообщения SIGUSR1 администратору почты, чтобы запустить повышение. По умолчанию эта функция доступна только суперпользователям, но другим пользователям может быть предоставлено разрешение EXECUTE для ее запуска.

pg_wal_replay_pause()
void
Немедленно приостанавливает восстановление (по умолчанию ограничено суперпользователями, но другим пользователям может быть предоставлено разрешение EXECUTE для запуска функции).

pg_wal_replay_resume()
void
Перезапускает восстановление, если оно было приостановлено (по умолчанию ограничено суперпользователями, но другим пользователям может быть предоставлено разрешение EXECUTE для запуска функции).

Пока восстановление приостановлено, дальнейшие изменения в базе данных не применяются. В режиме горячего резервирования все новые запросы будут видеть одинаковый непротиворечивый снимок базы данных, и никакие дальнейшие конфликты запросов не будут создаваться до возобновления восстановления.

Если потоковая репликация выключена, состояние паузы может продолжаться бесконечно без проблем. Во время потоковой репликации записи WAL будут по-прежнему приниматься, что в конечном итоге заполнит доступное дисковое пространство в зависимости от длительности паузы, скорости генерации WAL и доступного дискового пространства.

Функции синхронизации снимков

QHB позволяет сеансам базы данных синхронизировать свои снимки. Снимок определяет, какие данные видны для транзакции, которая использует снимок. Синхронизированные снимки необходимы, когда два или более сеанса должны видеть идентичный контент в базе данных. Если две сессии просто начинают свои транзакции независимо, всегда существует вероятность того, что какая-то третья транзакция совершит транзакцию между выполнением двух команд START TRANSACTION, так что одна сессия видит результаты этой транзакции, а другая - нет.

Чтобы решить эту проблему, QHB позволяет транзакции экспортировать используемый снимок. Пока экспортируемая транзакция остается открытой, другие транзакции могут импортировать ее моментальный снимок, и тем самым гарантируется, что они видят точно такое же представление базы данных, что и первая транзакция. Но обратите внимание, что любые изменения в базе данных, сделанные любой из этих транзакций, остаются невидимыми для других транзакций, как это обычно бывает для изменений, сделанных незафиксированными транзакциями. Таким образом, транзакции синхронизируются по отношению к ранее существующим данным, но действуют нормально для изменений, которые они вносят сами.

Снимки экспортируются с pg_export_snapshot функции pg_export_snapshot, показанной в таблице 87, и импортируются с помощью команды SET TRANSACTION.

Таблица 87. Функции синхронизации снимков

Имя | Тип ответа | Описание
pg_export_snapshot() | text | Сохранить текущий снимок и вернуть его идентификатор

Функция pg_export_snapshot сохраняет текущий снимок и возвращает text строку, идентифицирующую снимок. Эта строка должна быть передана (вне базы данных) клиентам, которые хотят импортировать снимок. Снимок доступен для импорта только до конца транзакции, которая его экспортировала. Транзакция может экспортировать более одного снимка, если это необходимо. Обратите внимание, что это полезно только в транзакциях READ COMMITTED, поскольку в REPEATABLE READ и на более высоких уровнях изоляции транзакции используют один и тот же моментальный снимок в течение всего срока службы. После того, как транзакция экспортировала какие-либо моментальные снимки, ее нельзя подготовить с помощью PREPARE TRANSACTION.

См. SET TRANSACTION для деталей о том, как использовать экспортированный снимок.

Функции репликации

Функции, показанные в таблице 88, предназначены для управления функциями репликации и взаимодействия с ними.

Использование функций для начала репликации разрешено только суперпользователям. Использование функций для слота репликации ограничено суперпользователями и пользователями, имеющими привилегию REPLICATION.

Функции, описанные в разделе Функции управления резервным копированием, разделе Функции управления восстановлением и разделе Функции синхронизации снимков, также имеют отношение к репликации.

Таблица 88. Функции SQL репликации

Функция
Тип ответа
Описание

pg_create_physical_replication_slot(slot_name name [, immediately_reserve boolean, temporary boolean ])
(name slot_name, lsn pg_lsn)
Создает новый физический слот репликации с именем slot_name. Необязательный второй параметр, если он равен true, указывает, что LSN для этого слота репликации будет зарезервирован немедленно; в противном случае номер LSN зарезервирован при первом подключении от клиента потоковой репликации. Потоковые изменения из физического слота возможны только с протоколом потоковой репликации. Необязательный третий параметр temporary, если он установлен в значение true, указывает, что слот не должен постоянно храниться на диске и предназначен только для использования в текущем сеансе. Временные слоты также освобождаются при любой ошибке. Эта функция соответствует команде протокола репликации CREATE_REPLICATION_SLOT ... PHYSICAL.

pg_drop_replication_slot(slot_name name)
void
Удаляет физический или логический слот репликации с именем slot_name. То же, что команда протокола репликации DROP_REPLICATION_SLOT. Для логических слотов это должно вызываться при подключении к той же базе данных, в которой был создан слот.

pg_create_logical_replication_slot(slot_name name, plugin name [, temporary boolean ])
(name slot_name, lsn pg_lsn)
Создает новый логический (декодирующий) слот репликации с именем slot_name используя плагин вывода plugin. Необязательный третий параметр temporary, если он установлен в значение true, указывает, что слот не должен постоянно храниться на диске и предназначен только для использования в текущем сеансе. Временные слоты также освобождаются при любой ошибке. Вызов этой функции имеет тот же эффект, что и команда протокола репликации CREATE_REPLICATION_SLOT ... LOGICAL.

pg_copy_physical_replication_slot(src_slot_name name, dst_slot_name name [, temporary boolean ])
(name slot_name, lsn pg_lsn)
Копирует существующий физический слот репликации с именем src_slot_name в физический слот репликации с именем dst_slot_name. Скопированный физический слот начинает резервировать WAL из того же LSN, что и исходный слот. temporary является обязательным. Если temporary опущено, используется то же значение, что и для исходного слота.

pg_copy_logical_replication_slot(src_slot_name name, dst_slot_name name [, temporary boolean [, plugin name ] ])
(name slot_name, lsn pg_lsn)
Копирует существующее имя слота логической репликации src_slot_name в слот логической репликации с именем dst_slot_name, изменяя выходной плагин и постоянство. Скопированный логический слот начинается с того же номера LSN, что и исходный логический слот. И temporary и plugin являются обязательными. Если temporary или plugin не указан, используются те же значения, что и для исходного логического слота.

pg_logical_slot_get_changes(slot_name name, upto_lsn pg_lsn, upto_nchanges int, VARIADIC options text[])
(lsn pg_lsn, xid xid, text data)
Возвращает изменения в слоте slot_name, начиная с точки, с которой изменения использовались последними. Если upto_lsn и upto_nchanges равны NULL, логическое декодирование будет продолжаться до конца WAL. Если upto_lsn не NULL, декодирование будет включать только те транзакции, которые совершаются до указанного LSN. Если upto_nchanges равен NULL, декодирование остановится, когда число строк, полученных в результате декодирования, превысит указанное значение. Однако обратите внимание, что фактическое количество возвращаемых строк может быть больше, поскольку этот предел проверяется только после добавления строк, полученных при декодировании каждой новой транзакции.

pg_logical_slot_peek_changes(slot_name name, upto_lsn pg_lsn, upto_nchanges int, VARIADIC options text[])
(lsn pg_lsn, xid xid, text data)
Ведет себя так же, как pg_logical_slot_get_changes(), за исключением того, что изменения не используются; то есть они будут возвращены снова при будущих вызовах.

pg_logical_slot_get_binary_changes(slot_name name, upto_lsn pg_lsn, upto_nchanges int, VARIADIC options text[])
(lsn pg_lsn, xid xid, data bytea)
Работает так же, как pg_logical_slot_get_changes(), за исключением того, что изменения возвращаются как bytea.

pg_logical_slot_peek_binary_changes(slot_name name, upto_lsn pg_lsn, upto_nchanges int, VARIADIC options text[])
(lsn pg_lsn, xid xid, data bytea)
Ведет себя так же, как pg_logical_slot_get_changes(), за исключением того, что изменения возвращаются как bytea и что изменения не используются; то есть они будут возвращены снова при будущих вызовах.

pg_replication_slot_advance(slot_name name, upto_lsn pg_lsn)
(name slot_name, end_lsn pg_lsn) bool
Продвигает текущую подтвержденную позицию слота репликации с именем slot_name. Слот не будет перемещен назад, и он не будет перемещен за пределы текущей позиции вставки. Возвращает название слота и реальную позицию, до которой он был продвинут.

pg_replication_origin_create(node_name text)
oid
Создайте источник репликации с заданным внешним именем и верните присвоенный ему внутренний идентификатор.

pg_replication_origin_drop(node_name text)
void
Удалите ранее созданный источник репликации, включая любой связанный процесс воспроизведения.

pg_replication_origin_oid(node_name text)
oid
Найдите источник репликации по имени и верните внутренний идентификатор. Если соответствующий источник репликации не найден, выдается ошибка.

pg_replication_origin_session_setup(node_name text)
void
Пометить текущий сеанс как воспроизведение с заданным источником, позволяя отслеживать ход воспроизведения. Используйте pg_replication_origin_session_reset чтобы вернуться. Может использоваться, только если не настроено предыдущее происхождение.

pg_replication_origin_session_reset()
void
Отмените эффекты pg_replication_origin_session_setup().

pg_replication_origin_session_is_setup()
bool
Был ли настроен источник репликации в текущем сеансе?

pg_replication_origin_session_progress(flush bool)
pg_lsn
Верните местоположение воспроизведения для источника репликации, настроенного в текущем сеансе. Параметр flush определяет, будет ли соответствующая локальная транзакция записана на диск или нет.

pg_replication_origin_xact_setup(origin_lsn pg_lsn, origin_timestamp timestamptz)
void
Пометить текущую транзакцию как воспроизведение транзакции, которая зафиксирована в данном LSN и отметке времени. Может вызываться только в том случае, если источник репликации был предварительно настроен с помощью pg_replication_origin_session_setup().

pg_replication_origin_xact_reset()
void
Отмените эффекты pg_replication_origin_xact_setup().

pg_replication_origin_advance (node_name text, lsn pg_lsn)
void
Установить процесс репликации для данного узла в заданном месте. Это в первую очередь полезно для настройки исходного местоположения или нового местоположения после изменений конфигурации и т.п. Помните, что неосторожное использование этой функции может привести к непоследовательному копированию данных.

pg_replication_origin_progress(node_name text, flush bool)
pg_lsn
Вернуть местоположение воспроизведения для данного источника репликации. Параметр flush определяет, будет ли соответствующая локальная транзакция записана на диск или нет.

pg_logical_emit_message(transactional bool, prefix text, content text)
pg_lsn
Выдать текстовое логическое сообщение декодирования. Это можно использовать для передачи общих сообщений в плагины логического декодирования через WAL. Параметр transactional указывает, должно ли сообщение быть частью текущей транзакции или оно должно быть записано немедленно и декодировано, как только логическое декодирование прочитает запись. prefix - это текстовый префикс, используемый плагинами логического декодирования для простого распознавания для них интересных сообщений. content - это текст сообщения.

pg_logical_emit_message(transactional bool, prefix text, content bytea)
pg_lsn
Издайте двоичное сообщение логического декодирования. Это можно использовать для передачи общих сообщений в плагины логического декодирования через WAL. Параметр transactional указывает, должно ли сообщение быть частью текущей транзакции или оно должно быть записано немедленно и декодировано, как только логическое декодирование прочитает запись. prefix - это текстовый префикс, используемый плагинами логического декодирования для простого распознавания для них интересных сообщений. content - это двоичное содержимое сообщения.

Функции управления объектами базы данных

Функции, показанные в таблице 89, рассчитывают использование дискового пространства объектов базы данных.

Таблица 89. Функции размера объекта базы данных

Имя | Тип ответа | Описание
pg_column_size(any) | int | Количество байтов, используемых для хранения определенного значения (возможно сжатого)
pg_database_size(oid) | bigint | Дисковое пространство, используемое базой данных с указанным OID
pg_database_size(name) | bigint | Дисковое пространство, используемое базой данных с указанным именем
pg_indexes_size(regclass) | bigint | Общее дисковое пространство, используемое индексами, прикрепленными к указанной таблице
pg_relation_size(relation regclass, fork text) | bigint | Дисковое пространство, используемое указанным ответвлением (’main’, ’fsm’, ’vm’ или ’init’) указанной таблицы или индекса
pg_relation_size(relation regclass) | bigint | pg_relation_size(..., ’main’) для pg_relation_size(..., ’main’)
pg_size_bytes(text) | bigint | Преобразует размер в удобочитаемом формате с единицами измерения размера в байтах
pg_size_pretty(bigint) | text | Преобразует размер в байтах, выраженный как 64-разрядное целое число, в читаемый человеком формат с единицами размера
pg_size_pretty(numeric) | text | Преобразует размер в байтах, выраженный в виде числового значения, в удобочитаемый формат с единицами измерения размера
pg_table_size(regclass) | bigint | Дисковое пространство, используемое указанной таблицей, за исключением индексов (но включая TOAST, карту свободного пространства и карту видимости)
pg_tablespace_size(oid) | bigint | Дисковое пространство, используемое табличным пространством с указанным OID
pg_tablespace_size(name) | bigint | Дисковое пространство, используемое табличным пространством с указанным именем
pg_total_relation_size(regclass) | bigint | Общее дисковое пространство, используемое указанной таблицей, включая все индексы и данные TOAST

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

pg_total_relation_size принимает OID или имя таблицы или таблицы тостов и возвращает общее дисковое пространство, используемое для этой таблицы, включая все связанные индексы. Эта функция эквивалентна pg_table_size + pg_indexes_size.

pg_table_size принимает OID или имя таблицы и возвращает дисковое пространство, необходимое для этой таблицы, исключая индексы. (TOAST space, карта свободного пространства и карта видимости включены).

pg_indexes_size принимает OID или имя таблицы и возвращает общее дисковое пространство, используемое всеми индексами, прикрепленными к этой таблице.

pg_database_size и pg_tablespace_size принимают OID или имя базы данных или табличного пространства и возвращают общее дисковое пространство, используемое в них. Чтобы использовать pg_database_size, вы должны иметь разрешение CONNECT для указанной базы данных (которая предоставляется по умолчанию) или быть членом роли pg_read_all_stats. Чтобы использовать pg_tablespace_size, вы должны иметь разрешение CREATE для указанного табличного пространства или быть членом роли pg_read_all_stats если только это не табличное пространство по умолчанию для текущей базы данных.

pg_relation_size принимает OID или имя таблицы, индекса или таблицы тостов и возвращает размер на диске в байтах одного разветвления этого отношения. (Обратите внимание, что для большинства целей удобнее использовать функции более высокого уровня pg_total_relation_size или pg_table_size, которые суммируют размеры всех вилок). С одним аргументом он возвращает размер основного форка данных отношения. Второй аргумент может быть предоставлен, чтобы указать, какой форк нужно исследовать:

’main’ возвращает размер основного ответвления данных отношения.

’fsm’ возвращает размер карты свободного пространства (см. раздел Карта свободного пространства ), связанной с отношением.

’vm’ возвращает размер карты видимости (см. раздел Карта видимости), связанной с отношением.

’init’ возвращает размер форка инициализации, если он есть, связанный с отношением.

pg_size_pretty может использоваться для форматирования результата одной из других функций в удобочитаемом виде с использованием байтов, КБ, МБ, ГБ или ТБ в зависимости от ситуации.

pg_size_bytes может использоваться для получения размера в байтах из строки в удобочитаемом формате. Входные данные могут иметь единицы байтов, кБ, МБ, ГБ или ТБ и анализируются без учета регистра. Если единицы не указаны, подразумеваются байты.

Приведенные выше функции, работающие с таблицами или индексами, принимают аргумент regclass, который является просто OID таблицы или индекса в системном каталоге pg_class. Однако вам не нужно искать OID вручную, так как преобразователь ввода типа данных regclass сделает всю работу за вас. Просто напишите имя таблицы, заключенное в одинарные кавычки, чтобы оно выглядело как буквальная константа. Для совместимости с обработкой обычных имен SQL строка будет преобразована в нижний регистр, если она не содержит двойных кавычек вокруг имени таблицы.

Если OID, который не представляет существующий объект, передается в качестве аргумента одной из вышеуказанных функций, возвращается NULL.

Функции, показанные в таблице 90, помогают идентифицировать конкретные файлы на диске, связанные с объектами базы данных.

Таблица 90. Функции расположения объектов базы данных

Имя | Тип ответа | Описание
pg_relation_filenode(relation regclass) | oid | Номер файлового узла указанного отношения
pg_relation_filepath(relation regclass) | text | Путь к файлу с указанным отношением
pg_filenode_relation(tablespace oid, filenode oid) | regclass | Найти отношение, связанное с данным табличным пространством и файловым узлом

pg_relation_filenode принимает OID или имя таблицы, индекса, последовательности или таблицы тостов и возвращает номер «файлового узла», назначенный в данный момент. FileNode — это базовый компонент имен файлов, используемых для отношения (см. раздел Структура файлов базы данных для получения дополнительной информации). Для большинства таблиц результат такой же, как у pg_class. relfilenode, но для некоторых системных каталогов relfilenode равен нулю, и эту функцию необходимо использовать для получения правильного значения. Функция возвращает NULL, если передано отношение, которое не имеет хранилища, такое как представление.

pg_relation_filepath похож на pg_relation_filenode, но возвращает полное имя пути файла (относительно каталога данных кластера базы данных PGDATA) отношения.

pg_filenode_relation является противоположностью pg_relation_filenode. При наличии OID «табличного пространства» и «filenode» он возвращает OID связанного отношения. Для таблицы в табличном пространстве базы данных по умолчанию табличное пространство может быть указано как 0.

В таблице 91 перечислены функции, используемые для управления сопоставлениями.

Таблица 91. Функции управления сопоставлением

Имя | Тип ответа | Описание
pg_collation_actual_version(oid) | text | Вернуть актуальную версию сличения из операционной системы
pg_import_system_collations(schema regnamespace) | integer | Импорт сопоставлений операционной системы

pg_collation_actual_version возвращает актуальную версию объекта сортировки, так как он в настоящее время установлен в операционной системе. Если это отличается от значения в pg_collation.collversion, то объекты, зависящие от параметров сортировки, возможно, потребуется перестроить. Смотрите также ALTER COLLATION.

pg_import_system_collations добавляет параметры сортировки в системный каталог pg_collation на основе всех локалей, найденных в операционной системе. Это то, что использует initdb; см. раздел Управление правилами сортировки для более подробной информации. Если впоследствии в операционную систему будут установлены дополнительные локали, эту функцию можно будет запустить снова, чтобы добавить параметры сортировки для новых локалей. Локали, соответствующие существующим записям в pg_collation будут пропущены. (Но объекты сопоставления, основанные на локалях, которых больше нет в операционной системе, не удаляются этой функцией). Параметром schema обычно будет pg_catalog, но это не является обязательным требованием; параметры сортировки могут быть установлены и в другую схему. Функция возвращает количество новых созданных объектов сопоставления.

Таблица 92. Функции разделения информации

Имя
Тип ответа
Описание

pg_partition_tree(regclass)
setof record
Вывести информацию о таблицах или индексах в дереве разделов для данной разделенной таблицы или разделенного индекса, с одной строкой для каждого раздела. Предоставленная информация включает имя раздела, имя его непосредственного родителя, логическое значение, указывающее, является ли раздел листом, и целое число, указывающее его уровень в иерархии. Значение уровня начинается с 0 для входной таблицы или индекса в его роли в качестве корня дерева разделов, 1 для его разделов, 2 для их разделов и т. Д.

pg_partition_ancestors(regclass)
setof regclass
Перечислите отношения предков данного раздела, включая сам раздел.

pg_partition_root(regclass)
regclass
Вернуть самого верхнего родителя дерева разделов, которому принадлежит данное отношение.

Чтобы проверить общий размер данных, содержащихся в таблице измерений, описанной в разделе Пример декларативного партиционирования, можно использовать следующий запрос:

=# SELECT pg_size_pretty(sum(pg_relation_size(relid))) AS total_size
     FROM pg_partition_tree('measurement');
 total_size
------------
 24 kB
(1 row)

Функции обслуживания индекса

В таблице 93 показаны функции, доступные для задач обслуживания индекса. Эти функции не могут быть выполнены во время восстановления. Использование этих функций ограничено суперпользователями и владельцем данного индекса.

Таблица 93. Функции обслуживания индекса

Имя | Тип ответа | Описание
brin_summarize_new_values(index regclass) | integer | суммировать диапазоны страниц, еще не суммированные
brin_summarize_range(index regclass, blockNumber bigint) | integer | суммировать диапазон страниц, охватывающий данный блок, если он еще не суммирован
brin_desummarize_range(index regclass, blockNumber bigint) | integer | разбиение диапазона страниц, охватывающих данный блок, если он суммирован
gin_clean_pending_list(index regclass) | bigint | переместить записи из списка ожидания GIN в основную структуру индекса

brin_summarize_new_values принимает OID или имя индекса BRIN и проверяет индекс, чтобы найти диапазоны страниц в базовой таблице, которые в данный момент не суммируются индексом; для любого такого диапазона он создает новый кортеж сводного индекса путем сканирования страниц таблицы. Возвращает количество новых сводок диапазона страниц, которые были вставлены в индекс. brin_summarize_range делает то же самое, за исключением того, что он только суммирует диапазон, который охватывает данный номер блока.

gin_clean_pending_list принимает OID или имя индекса GIN и очищает ожидающий список указанного индекса, перемещая записи в нем в основную структуру данных GIN. Возвращает количество страниц, удаленных из списка ожидания. Обратите внимание, если аргумент является индексом GIN, созданным с выключенной опцией fastupdate, очистка не происходит, и возвращаемое значение равно 0, поскольку у индекса нет списка ожидания. Пожалуйста, смотрите раздел Быстрое обновление GIN и раздел Советы и рекомендации по применению GIN для получения подробной информации об ожидающем списке и опции fastupdate.

Общие функции доступа к файлам

Функции, показанные в таблице 94, предоставляют собственный доступ к файлам на компьютере, на котором размещен сервер. Только файлы в каталоге кластера базы данных и log_directory могут быть доступны, если пользователю не назначена роль pg_read_server_files. Используйте относительный путь для файлов в каталоге кластера и путь, соответствующий параметру конфигурации log_directory для файлов журнала.

Обратите внимание, что предоставление пользователям права EXECUTE для pg_read_file () или связанных функций дает им возможность читать любой файл на сервере, который может прочитать база данных, и что эти чтения обходят все проверки прав в базе данных. Это означает, что, помимо прочего, пользователь с таким доступом может читать содержимое таблицы pg_authid, в которой содержится информация для аутентификации, а также читать любой файл в базе данных. Поэтому предоставление доступа к этим функциям должно быть тщательно продумано.

Таблица 94. Общие функции доступа к файлам

Имя
Тип ответа
Описание

pg_ls_dir(dirname text [, missing_ok boolean, include_dot_dirs boolean])
setof text
Список содержимого каталога. По умолчанию ограничен суперпользователями, но другим пользователям может быть предоставлено EXECUTE для запуска функции.

pg_ls_logdir()
setof record
Укажите имя, размер и время последнего изменения файлов в каталоге журнала. Доступ предоставляется членам роли pg_monitor и может предоставляться другим ролям, не являющимся суперпользователем.

pg_ls_waldir()
setof record
Укажите имя, размер и время последнего изменения файлов в каталоге WAL. Доступ предоставляется членам роли pg_monitor и может предоставляться другим ролям, не являющимся суперпользователем.

pg_ls_archive_statusdir()
setof record
Укажите имя, размер и время последнего изменения файлов в каталоге состояния архива WAL. Доступ предоставляется членам роли pg_monitor и может предоставляться другим ролям, не являющимся суперпользователем.

pg_ls_tmpdir([tablespace oid])
setof record
Укажите имя, размер и время последнего изменения файлов во временном каталоге табличного пространства. Если табличное пространство не предусмотрено, используется табличное пространство pg_default. Доступ предоставляется членам роли pg_monitor и может предоставляться другим ролям, не являющимся суперпользователем.

pg_read_file(filename text [, offset bigint, length bigint [, missing_ok boolean] ])
text
Вернуть содержимое текстового файла. По умолчанию ограничен суперпользователями, но другим пользователям может быть предоставлено EXECUTE для запуска функции.

pg_read_binary_file(filename text [, offset bigint, length bigint [, missing_ok boolean] ])
bytea
Вернуть содержимое файла. По умолчанию ограничен суперпользователями, но другим пользователям может быть предоставлено EXECUTE для запуска функции.

pg_stat_file(filename text[, missing_ok boolean])
record
Вернуть информацию о файле. По умолчанию ограничен суперпользователями, но другим пользователям может быть предоставлено EXECUTE для запуска функции.

Некоторые из этих функций принимают необязательный параметр missing_ok, который определяет поведение, когда файл или каталог не существует. Если true, функция возвращает NULL (кроме pg_ls_dir, который возвращает пустой набор результатов). Если false, возникает ошибка. По умолчанию установлено значение false.

pg_ls_dir возвращает имена всех файлов (и каталогов и других специальных файлов) в указанном каталоге. Include_dot_dirs указывает, включены ли «.» И «..» в набор результатов. По умолчанию они исключаются (ложь), но их включение может быть полезно, если для параметра missing_ok задано значение true, чтобы отличать пустой каталог от несуществующего каталога.

pg_ls_logdir возвращает имя, размер и время последнего изменения (mtime) каждого файла в каталоге журнала. По умолчанию только суперпользователи и члены роли pg_monitor могут использовать эту функцию. Доступ может быть предоставлен другим лицам, использующим GRANT.

pg_ls_waldir возвращает имя, размер и время последнего изменения (mtime) каждого файла в каталоге WAL. По умолчанию только суперпользователи и члены роли pg_monitor могут использовать эту функцию. Доступ может быть предоставлен другим лицам, использующим GRANT.

pg_ls_archive_statusdir возвращает имя, размер и время последнего изменения (mtime) каждого файла в каталоге состояния архива WAL pg_wal / archive_status. По умолчанию только суперпользователи и члены роли pg_monitor могут использовать эту функцию. Доступ может быть предоставлен другим лицам, использующим GRANT.

pg_ls_tmpdir возвращает имя, размер и время последнего изменения (mtime) каждого файла во временном каталоге файлов для указанного табличного пространства. Если табличное пространство не предусмотрено, используется табличное пространство pg_default. По умолчанию только суперпользователи и члены роли pg_monitor могут использовать эту функцию. Доступ может быть предоставлен другим лицам, использующим GRANT.

pg_read_file возвращает часть текстового файла, начиная с заданного смещения, возвращая не более байтов длины (меньше, если конец файла достигнут первым). Если смещение отрицательно, это относительно конца файла. Если смещение и длина не указаны, возвращается весь файл. Считанные из файла байты интерпретируются как строка в кодировке сервера; выдается ошибка, если они недопустимы в этой кодировке.

pg_read_binary_file похож на pg_read_file, за исключением того, что результатом является значение bytea; соответственно проверки кодирования не выполняются. В сочетании с функцией convert_from эту функцию можно использовать для чтения файла в указанной кодировке:

SELECT convert_from(pg_read_binary_file('file_in_utf8.txt'), 'UTF8');

pg_stat_file возвращает запись, содержащую размер файла, отметку времени последнего доступа, отметку времени последнего изменения, отметку времени последнего изменения статуса файла (только для платформ Unix), отметку времени создания файла (только для Windows) и boolean указание, является ли он каталогом. Типичные использования включают в себя:

SELECT * FROM pg_stat_file ('имя файла');
SELECT (pg_stat_file ('filename')). Модификация;

Функции рекомендательных блокировок

Функции, показанные в таблице 95, управляют рекомендательными блокировками. Подробнее о правильном использовании этих функций см. в разделе Рекомендательные блокировки.

Таблица 95. Функции рекомендательных блокировок

Имя | Тип ответа | Описание
pg_advisory_lock(key bigint) | void | Получить эксклюзивную блокировку на уровне сеанса
pg_advisory_lock(key1 int, key2 int) | void | Получить эксклюзивную блокировку на уровне сеанса
pg_advisory_lock_shared(key bigint) | void | Получить общую блокировку на уровне сеанса
pg_advisory_lock_shared(key1 int, key2 int) | void | Получить общую блокировку на уровне сеанса
pg_advisory_unlock(key bigint) | boolean | Освободить эксклюзивную блокировку на уровне сеанса
pg_advisory_unlock(key1 int, key2 int) | boolean | Освободить эксклюзивную блокировку на уровне сеанса
pg_advisory_unlock_all() | void | Освободить все рекомендательные блокировки уровня сеанса, удерживаемые текущим сеансом
pg_advisory_unlock_shared(key bigint) | boolean | Освободить общую блокировку на уровне сеанса
pg_advisory_unlock_shared(key1 int, key2 int) | boolean | Освободить общую блокировку на уровне сеанса
pg_advisory_xact_lock(key bigint) | void | Получить эксклюзивную блокировку на уровне транзакции
pg_advisory_xact_lock(key1 int, key2 int) | void | Получить эксклюзивную блокировку на уровне транзакции
pg_advisory_xact_lock_shared(key bigint) | void | Получить общую блокировку на уровне транзакций
pg_advisory_xact_lock_shared(key1 int, key2 int) | void | Получить общую блокировку на уровне транзакций
pg_try_advisory_lock(key bigint) | boolean | Получить эксклюзивную блокировку на уровне сеанса, если она доступна
pg_try_advisory_lock(key1 int, key2 int) | boolean | Получить эксклюзивную блокировку на уровне сеанса, если она доступна
pg_try_advisory_lock_shared(key bigint) | boolean | Получить общую блокировку на уровне сеанса, если она доступна
pg_try_advisory_lock_shared(key1 int, key2 int) | boolean | Получить общую блокировку на уровне сеанса, если она доступна
pg_try_advisory_xact_lock(key bigint) | boolean | Получить эксклюзивную рекомендательную блокировку уровня транзакции, если она доступна
pg_try_advisory_xact_lock(key1 int, key2 int) | boolean | Получить эксклюзивную рекомендательную блокировку уровня транзакции, если она доступна
pg_try_advisory_xact_lock_shared(key bigint) | boolean | Получить разделяемую блокировку уровня транзакции, если она доступна
pg_try_advisory_xact_lock_shared(key1 int, key2 int) | boolean | Получить разделяемую блокировку уровня транзакции, если она доступна

pg_advisory_lock блокирует определяемый приложением ресурс, который может быть идентифицирован либо одним 64-битным значением ключа, либо двумя 32-битными значениями ключа (обратите внимание, что эти два пространства ключей не перекрываются). Если другой сеанс уже удерживает блокировку того же идентификатора ресурса, эта функция будет ожидать, пока ресурс не станет доступным. Замок эксклюзивный. Несколько запросов на блокировку стека, так что если один и тот же ресурс заблокирован три раза, его нужно разблокировать три раза, чтобы освободить для использования другими сеансами.

pg_advisory_lock_shared работает так же, как и pg_advisory_lock, за исключением того, что блокировка может использоваться совместно с другими сеансами, запрашивающими общие блокировки. Только потенциальные эксклюзивные шкафчики заблокированы.

pg_try_advisory_lock аналогична pg_advisory_lock, за исключением того, что функция не будет ожидать, когда блокировка станет доступной. Он либо получит блокировку немедленно и вернет true, либо вернет false, если блокировка не может быть получена немедленно.

pg_try_advisory_lock_shared работает так же, как и pg_try_advisory_lock, за исключением того, что он пытается получить разделяемую, а не эксклюзивную блокировку.

pg_advisory_unlock снимет ранее приобретенную эксклюзивную рекомендательную блокировку уровня сеанса. Возвращает true, если блокировка успешно снята. Если блокировка не была удержана, она вернет false и, кроме того, сервер сообщит о предупреждении SQL.

pg_advisory_unlock_shared работает так же, как и pg_advisory_unlock, за исключением того, что освобождает разделяемую рекомендательную блокировку на уровне сеанса.

pg_advisory_unlock_all освободит все рекомендательные блокировки уровня сеанса, удерживаемые текущим сеансом. (Эта функция неявно вызывается в конце сеанса, даже если клиент отключается некорректно).

pg_advisory_xact_lock работает так же, как и pg_advisory_lock, за исключением того, что блокировка автоматически снимается в конце текущей транзакции.

pg_advisory_xact_lock_shared работает так же, как pg_advisory_lock_shared, за исключением того, что блокировка автоматически снимается в конце текущей транзакции и не может быть снята явно.

pg_try_advisory_xact_lock работает так же, как и pg_try_advisory_lock, за исключением того, что блокировка, если она получена, автоматически снимается в конце текущей транзакции и не может быть снята явно.

pg_try_advisory_xact_lock_shared работает так же, как и pg_try_advisory_lock_shared, за исключением того, что блокировка, если она получена, автоматически снимается в конце текущей транзакции и не может быть разблокирована явно.

Отправка метрик и аннотаций

Отправка метрики таймера

qhb_timer_report <name> <value>, где <name> — имя метрики (text), <value> — значение таймера в наносекундах (int8).

Увеличение значения "счётчика"

qhb_counter_increase_by <name> <value>, где <name> — имя метрики (text), <value> — значение (int8), на которое надо увеличить значение счётчика.

Увеличение значения типа "gauge"

qhb_gauge_add <name> <value>, где <name> — имя метрики (text), <value> — значение (int8), на которое надо увеличить значение "gauge".

Уменьшение значения типа "gauge"

qhb_gauge_sub <name> <value>, где <name> — имя метрики (text), <value> — значение (int8), на которое надо уменьшить значение "gauge".

Установка значение типа "gauge"

qhb_gauge_update <name> <value>, где <name> — имя метрики (text), <value> — значение (int8), в которое надо установить значение "gauge".

Отправка аннотации в графану

qhb_annotation <text> [<tag> ...], где <text> — текст аннотации для графаны, <tag> — опциональное значение тега (или тегов) для аннотации; можно указать произвольное количество тегов.

Триггерные функции

В настоящее время QHB предоставляет одну встроенную триггерную функцию, suppress_redundant_updates_trigger, которая предотвращает любое обновление, которое фактически не изменяет данные в строке, в отличие от обычного поведения, которое всегда выполняет обновление независимо от того, изменились ли данные, (Это нормальное поведение ускоряет запуск обновлений, поскольку проверка не требуется, а также полезно в некоторых случаях).

В идеале вам следует избегать запуска обновлений, которые фактически не изменяют данные в записи. Избыточные обновления могут повлечь значительные ненужные расходы времени, особенно если нужно изменить множество индексов, а также пространство в мертвых строках, которое в конечном итоге придется очистить. Однако обнаружение таких ситуаций в клиентском коде не всегда легко или даже невозможно, и написание выражений для их обнаружения может быть подвержено ошибкам. Альтернативой является использование suppress_redundant_updates_trigger, который будет пропускать обновления, которые не изменяют данные. Вы должны использовать это с осторожностью, как бы то ни было. Триггер занимает небольшое, но нетривиальное время для каждой записи, поэтому, если большинство записей, затронутых обновлением, фактически изменено, использование этого триггера фактически замедлит запуск обновления.

Функция suppress_redundant_updates_trigger может быть добавлена в таблицу следующим образом:

CREATE TRIGGER z_min_update
BEFORE UPDATE ON tablename
FOR EACH ROW EXECUTE FUNCTION suppress_redundant_updates_trigger();

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

Для получения дополнительной информации о создании триггеров см. CREATE TRIGGER.

Функции запуска событий

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

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

Захват изменений в конце команды

pg_event_trigger_ddl_commands возвращает список команд DDL, выполняемых каждым действием пользователя при вызове в функции, связанной с триггером события ddl_command_end. Если вызывается в любом другом контексте, возникает ошибка. pg_event_trigger_ddl_commands возвращает одну строку для каждой выполненной базовой команды; некоторые команды, представляющие собой одно предложение SQL, могут возвращать более одной строки. Эта функция возвращает следующие столбцы:

Имя | Тип | Описание
classid | oid | OID каталога, в который входит объект
objid | oid | OID самого объекта
objsubid | integer | Идентификатор подобъекта (например, номер атрибута для столбца)
command_tag | text | Командный тег
object_type | text | Тип объекта
schema_name | text | Имя схемы, к которой принадлежит объект, если есть; в противном случае NULL. Цитирование не применяется.
object_identity | text | Текстовое отображение идентификатора объекта, дополненное схемой. Каждый идентификатор, включенный в личность, указывается в случае необходимости.
in_extension | bool | true, если команда является частью сценария расширения
command | pg_ddl_command | Полное представление команды, во внутреннем формате. Это не может быть выведено напрямую, но может быть передано другим функциям для получения различной информации о команде.

Обработка объектов, отброшенных командой DDL

pg_event_trigger_dropped_objects возвращает список всех объектов, удаленных командой, для которой вызывается событие sql_drop. Если вызвано в любом другом контексте, pg_event_trigger_dropped_objects вызывает ошибку. pg_event_trigger_dropped_objects возвращает следующие столбцы:

Имя | Тип | Описание
classid | oid | OID каталога, в котором находился объект
objid | oid | OID самого объекта
objsubid | integer | Идентификатор подобъекта (например, номер атрибута для столбца)
original | bool | Истинно, если это был один из корневых объектов удаления
normal | bool | Истинно, если в графе зависимостей было нормальное отношение зависимости, ведущее к этому объекту
is_temporary | bool | true, если это был временный объект
object_type | text | Тип объекта
schema_name | text | Имя схемы, к которой принадлежит объект, если есть; в противном случае NULL. Цитирование не применяется.
object_name | text | Имя объекта, если комбинация схемы и имени может использоваться в качестве уникального идентификатора для объекта; в противном случае NULL. Кавычки не применяются, а имя никогда не указывается в схеме.
object_identity | text | Текстовое отображение идентификатора объекта, дополненное схемой. Каждый идентификатор, включенный в личность, указывается в случае необходимости.
address_names | text[] | Массив, который вместе с object_type и address_args может использоваться pg_get_object_address() для воссоздания адреса объекта на удаленном сервере, содержащем объект с таким же именем того же вида
address_args | text[] | Дополнение для address_names

pg_event_trigger_dropped_objects можно использовать в триггере событий, например так:

CREATE FUNCTION test_event_trigger_for_drops()
        RETURNS event_trigger LANGUAGE plpgsql AS $$
DECLARE
    obj record;
BEGIN
    FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()
    LOOP
        RAISE NOTICE '% dropped object: % %.% %',
                     tg_tag,
                     obj.object_type,
                     obj.schema_name,
                     obj.object_name,
                     obj.object_identity;
    END LOOP;
END
$$;
CREATE EVENT TRIGGER test_event_trigger_for_drops
   ON sql_drop
   EXECUTE FUNCTION test_event_trigger_for_drops();

Обработка события перезаписи таблицы

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

Таблица 96. Информация перезаписи таблицы

Имя | Тип ответа | Описание
pg_event_trigger_table_rewrite_oid() | Oid | OID таблицы, которая должна быть переписана.
pg_event_trigger_table_rewrite_reason() | int | Код (или коды) причины, объясняющий причину перезаписи. Точное значение кодов зависит от версии.

pg_event_trigger_table_rewrite_oid можно использовать в триггере событий, например так:

CREATE FUNCTION test_event_trigger_table_rewrite_oid()
 RETURNS event_trigger
 LANGUAGE plpgsql AS
$$
BEGIN
  RAISE NOTICE 'rewriting table % for reason %',
                pg_event_trigger_table_rewrite_oid()::regclass,
                pg_event_trigger_table_rewrite_reason();
END;
$$;

CREATE EVENT TRIGGER test_table_rewrite_oid
                  ON table_rewrite
   EXECUTE FUNCTION test_event_trigger_table_rewrite_oid();

Функции статистической информации

QHB предоставляет функцию для проверки сложной статистики, определенной с помощью команды CREATE STATISTICS.

Проверка списков MCV

pg_mcv_list_items возвращает список всех элементов, хранящихся в многостолбцовом списке MCV, и возвращает следующие столбцы:

Имя | Тип | Описание
index | int | индекс элемента в списке MCV
values | text[] | значения, хранящиеся в элементе MCV
nulls | boolean[] | флаги, идентифицирующие значения NULL
frequency | double precision | частота этого элемента MCV
base_frequency | double precision | базовая частота этого элемента MCV

pg_mcv_list_items можно использовать так:

SELECT m.* FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid),
                pg_mcv_list_items(stxdmcv) m WHERE stxname = 'stts';

Значения pg_mcv_list могут быть получены только из столбца pg_statistic_ext_data.stxdmcv.

PL/pgSQL — процедурный язык SQL

Обзор

PL/pgSQL - это загружаемый процедурный язык для системы баз данных QHB. Цели разработки PL/pgSQL заключались в создании загружаемого процедурного языка, который

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

добавляет структуры управления к языку SQL,

может выполнять сложные вычисления,

наследует все пользовательские типы, функции и операторы,

может быть определен как доверенный сервером,

прост в использовании.

Функции, созданные с помощью PL/pgSQL, можно использовать везде, где могут использоваться встроенные функции. Например, можно создавать сложные функции условного вычисления, а затем использовать их для определения операторов или использовать их в выражениях индекса.

В QHB PL/pgSQL устанавливается по умолчанию. Однако это все еще загружаемый модуль, поэтому администраторы, особенно заботящиеся о безопасности, могут удалить его.

Преимущества использования PL/pgSQL

SQL - это язык QHB, и большинство других реляционных баз данных используют его в качестве языка запросов. Это портативный и легкий в освоении. Но каждый оператор SQL должен выполняться сервером базы данных индивидуально.

Это означает, что ваше клиентское приложение должно отправлять каждый запрос на сервер базы данных, ждать его обработки, получать и обрабатывать результаты, выполнять некоторые вычисления, а затем отправлять дополнительные запросы на сервер. Все это требует межпроцессного взаимодействия, а также повлечет за собой нагрузку на сеть, если ваш клиент находится на другом компьютере, а не на сервере базы данных.

С PL/pgSQL вы можете сгруппировать блок вычислений и серию запросов на сервере базы данных, таким образом обладая мощью процедурного языка и простотой использования SQL, но при этом значительно экономя накладные расходы на связь клиент-сервер.

Дополнительные обходы между клиентом и сервером исключены

Промежуточные результаты, которые не нужны клиенту, не нужно передавать между сервером и клиентом

Можно избежать нескольких раундов разбора запроса

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

Кроме того, с PL/pgSQL вы можете использовать все типы данных, операторы и функции SQL.

Поддерживаемые типы данных аргумента и результата

Функции, написанные на PL/pgSQL, могут принимать в качестве аргументов любой скалярный или массив данных, поддерживаемый сервером, и они могут возвращать результат любого из этих типов. Они также могут принимать или возвращать любой составной тип (тип строки), указанный именем. Также возможно объявить функцию PL/pgSQL как принимающую record, что означает, что любой составной тип будет делать как ввод, или как возвращающую record, что означает, что результатом является тип строки, столбцы которой определяются спецификацией в вызывающем запросе, как описано в разделе Табличные функции.

Функции PL/pgSQL могут быть объявлены для приема переменного числа аргументов с помощью маркера VARIADIC. Это работает точно так же, как и для функций SQL, как описано в разделе Функции SQL с переменным числом аргументов.

Функции PL/pgSQL также могут быть объявлены для приема и возврата полиморфных типов anyelement, anyarray, anynonarray, anyenum и anyrange. Фактические типы данных, обрабатываемые полиморфной функцией, могут варьироваться от вызова к вызову, как описано в разделе Полиморфные типы. Пример показан в разделе Объявление параметров функции.

Функции PL/pgSQL также могут быть объявлены так, чтобы они возвращали «набор» (или таблицу) любого типа данных, который может быть возвращен как один экземпляр. Такая функция генерирует свои выходные данные, выполняя RETURN NEXT для каждого требуемого элемента набора результатов или используя RETURN QUERY для вывода результата оценки запроса.

Наконец, можно объявить функцию PL/pgSQL, которая возвращает void если она не имеет полезного возвращаемого значения. (В качестве альтернативы, это может быть написано как процедура в этом случае).

Функции PL/pgSQL также могут быть объявлены с выходными параметрами вместо явной спецификации возвращаемого типа. Это не добавляет фундаментальной возможности к языку, но это часто удобно, особенно для возврата нескольких значений. Обозначение RETURNS TABLE также может использоваться вместо RETURNS SETOF.

Конкретные примеры приведены в разделе Объявление параметров функции и разделе Возврат из функции.

Структура PL/pgSQL

Функции, написанные на PL/pgSQL, определяются сервером путем выполнения команд CREATE FUNCTION. Такая команда обычно выглядит, скажем,

CREATE FUNCTION somefunc(integer, text) RETURNS integer
AS 'function body text'
LANGUAGE plpgsql;

Тело функции - это просто строковый литерал в отношении CREATE FUNCTION. Часто для написания тела функции полезно использовать знаки доллара (см. раздел Строковые константы с экранированием знаками доллара), а не обычный синтаксис с одинарными кавычками. Без использования знаков доллара любые одиночные кавычки или обратные черты в теле функции должны быть экранированы путем их удвоения. Почти все примеры в этой главе используют литералы в кавычках для своих функций.

PL/pgSQL - это блочно-структурированный язык. Полный текст тела функции должен быть блоком. Блок определяется как:

[ <<label>> ]
[ DECLARE
    declarations ]
BEGIN
    statements
END [ label ];

Каждое объявление и каждый оператор в блоке заканчиваются точкой с запятой. Блок, который появляется внутри другого блока, должен иметь точку с запятой после END, как показано выше; однако последний END который завершает тело функции, не требует точки с запятой.

Заметка
Распространенная ошибка - писать точку с запятой сразу после BEGIN. Это неверно и приведет к синтаксической ошибке.

label нужна только в том случае, если вы хотите идентифицировать блок для использования в операторе EXIT или указать имена переменных, объявленных в блоке. Если метка дается после END, она должна совпадать с меткой в начале блока.

Все ключевые слова не чувствительны к регистру. Идентификаторы неявно преобразуются в нижний регистр, если они не заключены в двойные кавычки, как в обычных командах SQL.

Комментарии работают в коде PL/pgSQL так же, как и в обычном SQL. Двойная черта ( -- ) начинает комментарий, который простирается до конца строки. /* запускает комментарий блока, который распространяется до появления символов */.

Любой оператор в разделе операторов блока может быть субблоком. Подблоки могут использоваться для логической группировки или для локализации переменных в небольшой группе операторов. Переменные, объявленные в подблоке, маскируют любые переменные с одинаковыми именами внешних блоков на время действия субблока; но вы все равно можете получить доступ к внешним переменным, если укажете их имена с меткой их блока. Например:

CREATE FUNCTION somefunc() RETURNS integer AS $$
<< outerblock >>
DECLARE
    quantity integer := 30;
BEGIN
    RAISE NOTICE 'Quantity here is %', quantity;  -- Prints 30
    quantity := 50;
    --
    -- Create a subblock
    --
    DECLARE
        quantity integer := 80;
    BEGIN
        RAISE NOTICE 'Quantity here is %', quantity;  -- Prints 80
        RAISE NOTICE 'Outer quantity here is %', outerblock.quantity;  -- Prints 50
    END;

    RAISE NOTICE 'Quantity here is %', quantity;  -- Prints 50

    RETURN quantity;
END;
$$ LANGUAGE plpgsql;

Заметка
На самом деле существует скрытый «внешний блок», окружающий тело любой функции PL/pgSQL. Этот блок предоставляет объявления параметров функции (если есть), а также некоторые специальные переменные, такие как FOUND (см. раздел Получение статуса результата). Внешний блок помечен именем функции, что означает, что параметры и специальные переменные могут быть дополнены именем функции.

Важно не путать использование BEGIN/END для группировки операторов в PL/pgSQL с одноименными командами SQL для управления транзакциями. PL/pgSQL BEGIN/END предназначены только для группировки; они не начинают и не заканчивают транзакцию. См. раздел Управление транзакциями для получения информации об управлении транзакциями в PL/pgSQL. Кроме того, блок, содержащий предложение EXCEPTION, эффективно формирует субтранзакцию, которую можно откатить без влияния на внешнюю транзакцию. Подробнее об этом см. раздел Ошибки захвата.

Объявления

Все переменные, используемые в блоке, должны быть объявлены в разделе объявлений блока. (Единственное исключение состоит в том, что переменная цикла FOR повторяющегося по диапазону целочисленных значений, автоматически объявляется как целочисленная переменная, и аналогично переменная цикла FOR повторяющегося по результату курсора, автоматически объявляется как переменная записи).

Переменные PL/pgSQL могут иметь любой тип данных SQL, например, integer, varchar и char.

Вот несколько примеров объявлений переменных:

user_id integer;
quantity numeric(5);
url varchar;
myrow tablename%ROWTYPE;
myfield tablename.columnname%TYPE;
arow RECORD;

Общий синтаксис объявления переменной:

name [ CONSTANT ] type [ COLLATE collation_name ] [ NOT NULL ] [ { DEFAULT | := | = } expression ];

Предложение DEFAULT, если оно задано, задает начальное значение, назначенное переменной при вводе блока. Если предложение DEFAULT не задано, переменная инициализируется нулевым значением SQL. Опция CONSTANT предотвращает присвоение переменной после инициализации, так что ее значение будет оставаться постоянным на протяжении всего блока. Опция COLLATE указывает параметры сортировки, которые следует использовать для переменной (см. раздел Сортировка переменных PL/pgSQL). Если указано NOT NULL, присвоение нулевого значения приводит к ошибке во время выполнения. Все переменные, объявленные как NOT NULL должны иметь ненулевое значение по умолчанию. Равно ( = ) может использоваться вместо PL/SQL-совместимого :=.

Значение переменной по умолчанию оценивается и присваивается переменной каждый раз, когда вводится блок (не только один раз за вызов функции). Так, например, присвоение now() переменной типа timestamp приводит к тому, что переменная будет иметь время текущего вызова функции, а не время, когда функция была предварительно скомпилирована.

Примеры:

quantity integer DEFAULT 32;
url varchar := 'http://mysite.com';
user_id CONSTANT integer := 10;

Объявление параметров функции

Параметры, передаваемые в функции, именуются с помощью идентификаторов $1, $2 и т.д. При желании псевдонимы могут быть объявлены для $n имен параметров с целью повышения читабельности. Затем можно использовать псевдоним или числовой идентификатор для ссылки на значение параметра.

Есть два способа создать псевдоним. Предпочтительным способом является присвоение имени параметру в команде CREATE FUNCTION, например:

CREATE FUNCTION sales_tax(subtotal real) RETURNS real AS $$
BEGIN
    RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

Другой способ - явно объявить псевдоним, используя синтаксис объявления.

name ALIAS FOR $n;

Тот же пример в этом стиле выглядит так:

CREATE FUNCTION sales_tax(real) RETURNS real AS $$
DECLARE
    subtotal ALIAS FOR $1;
BEGIN
    RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

Заметка
Эти два примера не являются полностью эквивалентными. В первом случае на промежуточный итог можно ссылаться как на sales_tax.subtotal, но во втором случае это невозможно. (Если бы мы прикрепили метку к внутреннему блоку, subtotal можно было бы квалифицировать с этой меткой).

Еще несколько примеров:

CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$
DECLARE
    v_string ALIAS FOR $1;
    index ALIAS FOR $2;
BEGIN
    -- some computations using v_string and index here
END;
$$ LANGUAGE plpgsql;

CREATE FUNCTION concat_selected_fields(in_t sometablename) RETURNS text AS $$
BEGIN
    RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7;
END;
$$ LANGUAGE plpgsql;

Когда функция PL/pgSQL объявляется с выходными параметрами, выходным параметрам присваиваются имена $n и необязательные псевдонимы точно так же, как обычные входные параметры. Выходной параметр фактически является переменной, которая начинается с NULL; это должно быть назначено во время выполнения функции. Конечное значение параметра - это то, что возвращается. Например, расчет налога с продаж также может быть сделан следующим образом:

CREATE FUNCTION sales_tax(subtotal real, OUT tax real) AS $$
BEGIN
    tax := subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;

Обратите внимание, что мы пропустили RETURNS real - мы могли бы включить его, но это было бы излишним.

Выходные параметры наиболее полезны при возврате нескольких значений. Тривиальный пример:

CREATE FUNCTION sum_n_product(x int, y int, OUT sum int, OUT prod int) AS $$
BEGIN
    sum := x + y;
    prod := x * y;
END;
$$ LANGUAGE plpgsql;

Как обсуждалось в разделе Функции SQL с выходными параметрами, это эффективно создает анонимный тип записи для результатов функции. Если задано предложение RETURNS, оно должно типа record.

Другой способ объявить функцию PL/pgSQL - использовать RETURNS TABLE, например:

CREATE FUNCTION extended_sales(p_itemno int)
RETURNS TABLE(quantity int, total numeric) AS $$
BEGIN
    RETURN QUERY SELECT s.quantity, s.quantity * s.price FROM sales AS s
                 WHERE s.itemno = p_itemno;
END;
$$ LANGUAGE plpgsql;

Это в точности эквивалентно объявлению одного или нескольких параметров OUT и указанию RETURNS SETOF sometype.

Когда возвращаемый тип функции PL/pgSQL объявлен как полиморфный тип ( anyelement, anyarray, anynonarray, anyenum или anyrange), создается специальный параметр $0. Тип данных - это фактический тип возвращаемого значения функции, выведенный из фактических типов ввода (см. раздел Полиморфные типы). Это позволяет функции получить доступ к ее фактическому типу возврата, как показано в разделе Типы копирования. $0 инициализируется нулевым значением и может быть изменено функцией, поэтому его можно использовать для хранения возвращаемого значения, если это необходимо, хотя это не требуется. $0 также может быть присвоен псевдоним. Например, эта функция работает с любым типом данных, который имеет оператор + :

CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement)
RETURNS anyelement AS $$
DECLARE
    result ALIAS FOR $0;
BEGIN
    result := v1 + v2 + v3;
    RETURN result;
END;
$$ LANGUAGE plpgsql;

Тот же эффект можно получить, объявив один или несколько выходных параметров как полиморфные типы. В этом случае специальный параметр $0 не используется; Сами выходные параметры служат той же цели. Например:

CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement,
                                 OUT sum anyelement)
AS $$
BEGIN
    sum := v1 + v2 + v3;
END;
$$ LANGUAGE plpgsql;

ALIAS

newname ALIAS FOR oldname;

Синтаксис ALIAS является более общим, чем предлагается в предыдущем разделе: вы можете объявить псевдоним для любой переменной, а не только для параметров функции. Основное практическое использование для этого состоит в назначении другого имени для переменных с предопределенными именами, таких как NEW или OLD в функции триггера.

Примеры:

DECLARE
  prior ALIAS FOR old;
  updated ALIAS FOR new;

Поскольку ALIAS создает два разных способа именования одного и того же объекта, неограниченное использование может привести к путанице. Лучше всего использовать его только с целью переопределения заранее определенных имен.

Типы копирования

variable%TYPE

%TYPE предоставляет тип данных переменной или столбца таблицы. Вы можете использовать это для объявления переменных, которые будут содержать значения базы данных. Например, предположим, у вас есть столбец с именем user_id в вашей таблице users. Чтобы объявить переменную с тем же типом данных, что и users.user_id вы пишете:

user_id users.user_id%TYPE;

Используя %TYPE вам не нужно знать тип данных структуры, на которую вы ссылаетесь, и, самое главное, если тип данных ссылочного элемента изменится в будущем (например: вы меняете тип user_id с integer на real), вам может не потребоваться изменить определение функции.

%TYPE особенно полезен в полиморфных функциях, поскольку типы данных, необходимые для внутренних переменных, могут меняться от одного вызова к другому. Подходящие переменные могут быть созданы путем применения %TYPE к аргументам функции или местозаполнителям результата.

Типы строк

name table_name%ROWTYPE;
name composite_type_name;

Переменная составного типа называется переменной строки (или переменной типа строки). Такая переменная может содержать целую строку результата запроса SELECT или FOR, если набор столбцов этого запроса соответствует объявленному типу переменной. Доступ к отдельным полям значения строки осуществляется с использованием обычной точечной нотации, например rowvar.field.

Переменная строки может быть объявлена для того же типа, что и строки существующей таблицы или представления, используя нотацию table_name%ROWTYPE; или это может быть объявлено путем указания имени составного типа. (Поскольку каждая таблица имеет связанный составной тип с одним и тем же именем, в QHB не имеет значения, пишете ли вы %ROWTYPE или нет. Но форма с %ROWTYPE более переносима).

Параметрами функции могут быть составные типы (полные строки таблицы). В этом случае соответствующий идентификатор $n будет переменной строки, и из него можно выбрать поля, например, $1.user_id.

Вот пример использования составных типов. table1 и table2 являются существующими таблицами, имеющими по крайней мере упомянутые поля:

CREATE FUNCTION merge_fields(t_row table1) RETURNS text AS $$
DECLARE
    t2_row table2%ROWTYPE;
BEGIN
    SELECT * INTO t2_row FROM table2 WHERE ... ;
    RETURN t_row.f1 || t2_row.f3 || t_row.f5 || t2_row.f7;
END;
$$ LANGUAGE plpgsql;

SELECT merge_fields(t.*) FROM table1 t WHERE ... ;

Типы записей

name RECORD;

Переменные записи аналогичны переменным типа строки, но не имеют предопределенной структуры. Они принимают фактическую структуру строк, назначенных им при выполнении команды SELECT или FOR. Подструктура переменной записи может меняться каждый раз, когда ей назначается. Следствием этого является то, что до тех пор, пока переменная записи не будет впервые назначена, она не имеет подструктуры, и любая попытка доступа к полю в ней вызовет ошибку во время выполнения.

Обратите внимание, что RECORD не является истинным типом данных, а только заполнителем. Следует также понимать, что когда объявляется функция PL/pgSQL, возвращающая record типа, это не совсем то же самое, что и переменная записи, даже если такая функция может использовать переменную записи для хранения своего результата. В обоих случаях фактическая структура строки неизвестна при написании функции, но для функции, возвращающей record фактическая структура определяется при разборе вызывающего запроса, тогда как переменная записи может изменить свою структуру строки на лету.

Сортировка переменных PL/pgSQL

Когда функция PL/pgSQL имеет один или несколько параметров типов данных для сопоставления, для каждого вызова функции определяется сопоставление в зависимости от сопоставлений, назначенных фактическим аргументам, как описано в разделе Поддержка правил сортировки. Если сопоставление успешно идентифицировано (т.е. между аргументами нет конфликтов неявных сопоставлений), то все параметры сопоставления обрабатываются как имеющие такое сопоставление неявно. Это повлияет на поведение чувствительных к сбору операций внутри функции. Например, рассмотрим

CREATE FUNCTION less_than(a text, b text) RETURNS boolean AS $$
BEGIN
    RETURN a < b;
END;
$$ LANGUAGE plpgsql;

SELECT less_than(text_field_1, text_field_2) FROM table1;
SELECT less_than(text_field_1, text_field_2 COLLATE "C") FROM table1;

Первое использование less_than будет использовать общее сравнение text_field_1 и text_field_2 для сравнения, в то время как второе использование будет использовать C сортировку.

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

CREATE FUNCTION less_than(a text, b text) RETURNS boolean AS $$
DECLARE
    local_a text := a;
    local_b text := b;
BEGIN
    RETURN local_a < local_b;
END;
$$ LANGUAGE plpgsql;

Если параметры типов данных для сопоставления отсутствуют или для них нельзя определить общие параметры сортировки, то параметры и локальные переменные используют параметры сортировки по умолчанию для своего типа данных (обычно это параметры сортировки базы данных по умолчанию, но могут отличаться для переменных типа домена).

Локальная переменная типа данных с возможностью сопоставления может иметь другой сопоставление, связанное с включением опции COLLATE в ее объявление, например

DECLARE
    local_a text COLLATE "en_US";

Этот параметр переопределяет параметры сортировки, которые в противном случае были бы переданы переменной в соответствии с приведенными выше правилами.

Также, конечно, явные предложения COLLATE могут быть записаны внутри функции, если требуется принудительно использовать конкретное сопоставление в конкретной операции. Например,

CREATE FUNCTION less_than_c(a text, b text) RETURNS boolean AS $$
BEGIN
    RETURN a < b COLLATE "C";
END;
$$ LANGUAGE plpgsql;

Это переопределяет параметры сортировки, связанные со столбцами таблицы, параметрами или локальными переменными, используемыми в выражении, так же, как это происходит в простой команде SQL.

Выражения

Все выражения, используемые в инструкциях PL/pgSQL, обрабатываются с использованием основного SQL- исполнителя сервера. Например, когда вы пишете оператор PL/pgSQL, как

IF expression THEN ...

PL/pgSQL оценит выражение, подав запрос

SELECT expression

на основной движок SQL. При формировании команды SELECT любые вхождения имен переменных PL/pgSQL заменяются параметрами, как подробно описано в разделе Подстановка переменных. Это позволяет составить план запроса для SELECT только один раз, а затем повторно использовать для последующих оценок с различными значениями переменных. Таким образом, то, что действительно происходит при первом использовании выражения, по сути, является командой PREPARE. Например, если мы объявили две целочисленные переменные x и y, и мы напишем

IF x < y THEN ...

то, что происходит за кулисами, эквивалентно

PREPARE statement_name(integer, integer) AS SELECT $1 < $2;

и затем этот подготовленный оператор будет EXECUTE d для каждого выполнения оператора IF, с текущими значениями переменных PL/pgSQL, предоставленными в качестве значений параметров. Обычно эти детали не важны для пользователя PL/pgSQL, но их полезно знать при попытке диагностировать проблему. Более подробная информация представлена в разделе Планирование кэширования.

Основные положения

В этом и следующих разделах мы опишем все типы операторов, которые явно понятны PL/pgSQL. Все, что не распознается как один из этих типов операторов, считается командой SQL и отправляется на выполнение главному ядру базы данных, как описано в разделе Выполнение команды без результата и разделе Выполнение запроса с результатом в одну строку.

Присваивание

Присвоение значения переменной PL/pgSQL записывается так:

variable { := | = } expression;

Как объяснено ранее, выражение в таком выражении оценивается с помощью команды SQL SELECT отправляемой основному ядру базы данных. Выражение должно давать одно значение (возможно, значение строки, если переменная является строкой или переменной записи). Целевая переменная может быть простой переменной (необязательно квалифицируемой именем блока), полем строки или переменной записи или элементом массива, который является простой переменной или полем. Равное ( = ) может использоваться вместо PL/SQL-совместимого :=.

Если тип данных результата выражения не совпадает с типом данных переменной, значение будет приведено, как если бы было приведено присваивание . Если для пары задействованных типов данных неизвестно приведение присваивания, интерпретатор PL/pgSQL попытается преобразовать значение результата в текстовом виде, то есть применяя функцию вывода типа результата, за которой следует функция ввода типа переменной. Обратите внимание, что это может привести к ошибкам во время выполнения, сгенерированным функцией ввода, если строковая форма значения результата не приемлема для функции ввода.

Примеры:

tax := subtotal * 0.06;
my_record.user_id := 20;

Выполнение команды без результата

Для любой команды SQL, которая не возвращает строки, например, INSERT без предложения RETURNING, вы можете выполнить команду в функции PL / pgSQL, просто написав команду.

Любое имя переменной PL/pgSQL, отображаемое в тексте команды, рассматривается как параметр, а затем текущее значение переменной предоставляется в качестве значения параметра во время выполнения. Это точно так же, как обработка, описанная ранее для выражений; подробности см. в разделе Подстановка переменных.

При выполнении команды SQL таким способом PL/pgSQL может кэшировать и повторно использовать план выполнения для команды, как обсуждалось в разделе Планирование кэширования.

Иногда полезно оценить выражение или запрос SELECT но отбросить результат, например, при вызове функции, которая имеет побочные эффекты, но не имеет полезного значения результата. Чтобы сделать это в PL / pgSQL, используйте оператор PERFORM:

PERFORM query;

Это выполняет query и отбрасывает результат. Напишите query, как и команду SQL SELECT, но замените исходное ключевое слово SELECT на PERFORM. Для запросов WITH используйте PERFORM а затем поместите запрос в скобки. (В этом случае запрос может вернуть только одну строку). Переменные PL/pgSQL будут подставлены в запрос так же, как для команд, которые не возвращают результата, и план кэшируется таким же образом. Кроме того, специальная переменная FOUND устанавливается в значение true, если запрос выдал хотя бы одну строку, или в значение false, если не было строк (см. раздел Получение статуса результата).

Заметка
Можно было бы ожидать, что написание SELECT напрямую приведет к этому результату, но в настоящее время единственный приемлемый способ сделать это - PERFORM. Команда SQL, которая может возвращать строки, например, SELECT, будет отклонена как ошибка, если в ней нет предложения INTO, как описано в следующем разделе.

Пример:

PERFORM create_mv('cs_session_page_requests_mv', my_query);

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

Результат команды SQL, дающей одну строку (возможно, из нескольких столбцов), может быть присвоен переменной записи, переменной типа строки или списку скалярных переменных. Это делается путем написания базовой команды SQL и добавления предложения INTO. Например,

SELECT select_expressions INTO [STRICT] target FROM ...;
INSERT ... RETURNING expressions INTO [STRICT] target;
UPDATE ... RETURNING expressions INTO [STRICT] target;
DELETE ... RETURNING expressions INTO [STRICT] target;

где target может быть переменной записи, переменной строки или разделенным запятыми списком простых переменных и полей записи / строки. Переменные PL/pgSQL будут подставлены в остальную часть запроса, и план будет кэширован, как описано выше для команд, которые не возвращают строки. Это работает для SELECT, INSERT / UPDATE / DELETE с RETURNING и служебных команд, которые возвращают результаты набора строк (например, EXPLAIN). За исключением предложения INTO, команда SQL такая же, как она написана вне PL/pgSQL.

Заметка
Обратите внимание, что эта интерпретация SELECT с INTO сильно отличается от обычной команды SELECT INTO QHB, в которой целью INTO является вновь созданная таблица. Если вы хотите создать таблицу из результата SELECT внутри функции PL/pgSQL, используйте синтаксис CREATE TABLE... AS SELECT.

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

Предложение INTO может появляться практически в любом месте команды SQL. Обычно он записывается либо непосредственно перед, либо сразу после списка select_expressions в команде SELECT, либо в конце команды для других типов команд. Рекомендуется следовать этому соглашению на случай, если парсер PL/pgSQL станет более строгим в будущих версиях.

Если STRICT не указан в предложении INTO, тогда target будет установлен на первую строку, возвращаемую запросом, или на нули, если запрос не вернул ни одной строки. (Обратите внимание, что « первая строка » не является четко определенной, если вы не использовали ORDER BY). Любые строки результатов после первой строки отбрасываются. Вы можете проверить специальную переменную FOUND (см. раздел Получение статуса результата), чтобы определить, была ли возвращена строка:

SELECT * INTO myrec FROM emp WHERE empname = myname;
IF NOT FOUND THEN
    RAISE EXCEPTION 'employee % not found', myname;
END IF;

Если указан параметр STRICT, запрос должен возвращать ровно одну строку, иначе будет сообщено об ошибке во время выполнения, NO_DATA_FOUND (без строк) или TOO_MANY_ROWS (более одной строки). Вы можете использовать блок исключения, если хотите перехватить ошибку, например:

BEGIN
    SELECT * INTO STRICT myrec FROM emp WHERE empname = myname;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            RAISE EXCEPTION 'employee % not found', myname;
        WHEN TOO_MANY_ROWS THEN
            RAISE EXCEPTION 'employee % not unique', myname;
END;

Успешное выполнение команды с помощью STRICT всегда устанавливает FOUND в значение true.

Для INSERT / UPDATE / DELETE с RETURNING PL/pgSQL сообщает об ошибке для более чем одной возвращенной строки, даже если STRICT не указан. Это связано с тем, что нет такой опции, как ORDER BY с помощью которой можно определить, какая затронутая строка должна быть возвращена.

Если для функции включен print_strict_params, то при возникновении ошибки из-за несоблюдения требований STRICT часть DETAIL в сообщении об ошибке будет содержать информацию о параметрах, переданных в запрос. Вы можете изменить параметр print_strict_params для всех функций, установив plpgsql.print_strict_params, хотя это plpgsql.print_strict_params только на последующие компиляции функций. Вы также можете включить его для каждой функции, используя опцию компилятора, например:

CREATE FUNCTION get_userid(username text) RETURNS int
AS $$
#print_strict_params on
DECLARE
userid int;
BEGIN
    SELECT users.userid INTO STRICT userid
        FROM users WHERE users.username = get_userid.username;
    RETURN userid;
END
$$ LANGUAGE plpgsql;

При сбое эта функция может выдавать сообщение об ошибке, например

ERROR:  query returned no rows
DETAIL:  parameters: $1 = 'nosuchuser'
CONTEXT:  PL/pgSQL function get_userid(text) line 6 at SQL statement

Заметка
Опция STRICT соответствует поведению SELECT INTO и связанных операторов Oracle PL/SQL.

Для обработки случаев, когда вам нужно обработать несколько строк результатов из запроса SQL, см. раздел Цикл по результатам запроса.

Выполнение динамических команд

Часто вы захотите генерировать динамические команды внутри ваших функций PL/pgSQL, то есть команды, которые будут включать разные таблицы или разные типы данных при каждом их выполнении. Обычные попытки PL/pgSQL кэшировать планы для команд (как описано в разделе Планирование кэширования) не будут работать в таких сценариях. Для решения такого рода проблем предусмотрен оператор EXECUTE :

EXECUTE command-string [ INTO [STRICT] target ] [ USING expression [, ... ] ];

где command-string - это выражение, дающее строку (типа text ), содержащую команду, которая должна быть выполнена. Необязательной target является переменная записи, переменная строки или разделенный запятыми список простых переменных и полей записи / строки, в которые будут сохраняться результаты команды. Необязательные выражения USING предоставляют значения для вставки в команду.

Подстановка переменных PL/pgSQL в вычисляемой командной строке не производится. Любые обязательные значения переменных должны быть вставлены в командную строку по мере ее создания; или вы можете использовать параметры, как описано ниже.

Также нет кэширования плана для команд, выполняемых через EXECUTE. Вместо этого команда всегда планируется каждый раз, когда выполняется оператор. Таким образом, командная строка может быть динамически создана внутри функции для выполнения действий над различными таблицами и столбцами.

Предложение INTO указывает, где должны быть назначены результаты команды SQL, возвращающей строки. Если указан список строк или переменных, он должен точно соответствовать структуре результатов запроса (когда используется переменная записи, он автоматически настраивается на соответствие структуре результатов). Если возвращается несколько строк, только первая будет назначена переменной INTO. Если строки не возвращаются, NULL присваивается переменной (или переменным) INTO. Если предложение INTO не указано, результаты запроса отбрасываются.

Если задана опция STRICT выдается сообщение об ошибке, если запрос не создает ровно одну строку.

В командной строке могут использоваться значения параметров, на которые в команде ссылаются как $1, $2 и т. Д. Эти символы относятся к значениям, указанным в предложении USING. Этот метод часто предпочтительнее вставки значений данных в командную строку в виде текста: он позволяет избежать накладных расходов во время выполнения преобразования значений в текст и обратно, и он гораздо менее подвержен атакам с использованием SQL-инъекций, поскольку нет необходимости заключать в кавычки или побега. Примером является:

EXECUTE 'SELECT count(*) FROM mytable WHERE inserted_by = $1 AND inserted <= $2'
   INTO c
   USING checked_user, checked_date;

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

EXECUTE 'SELECT count(*) FROM '
    || quote_ident(tabname)
    || ' WHERE inserted_by = $1 AND inserted <= $2'
   INTO c
   USING checked_user, checked_date;

Более чистый подход заключается в использовании %I спецификации format() для имен таблиц или столбцов (строки, разделенные новой строкой, объединяются):

EXECUTE format('SELECT count(*) FROM %I '
   'WHERE inserted_by = $1 AND inserted <= $2', tabname)
   INTO c
   USING checked_user, checked_date;

Другое ограничение на символы параметров заключается в том, что они работают только в командах SELECT, INSERT, UPDATE и DELETE. В других типах операторов (обычно называемых служебными операторами) вы должны вставлять значения текстуально, даже если они являются просто значениями данных.

EXECUTE с простой константой командной строки и некоторыми параметрами USING, как в первом примере выше, функционально эквивалентен простому написанию команды непосредственно в PL/pgSQL и позволяет автоматической замене переменных PL/pgSQL. Важным отличием является то, что EXECUTE будет перепланировать команду при каждом выполнении, генерируя план, специфичный для текущих значений параметров; в то время как PL/pgSQL может создать общий план и кэшировать его для повторного использования. В ситуациях, когда лучший план сильно зависит от значений параметров, может быть полезно использовать EXECUTE чтобы убедиться, что общий план не выбран.

SELECT INTO в настоящее время не поддерживается в EXECUTE; вместо этого выполните простую команду SELECT и укажите INTO как часть самого EXECUTE.

Заметка
Оператор PL/pgSQL EXECUTE не связан с оператором EXECUTE SQL, поддерживаемым сервером QHB. Оператор EXECUTE сервера нельзя использовать непосредственно в функциях PL/pgSQL (и не нужен).

Пример 1. Заключение в кавычки значений в динамических запросах

При работе с динамическими командами вам часто приходится иметь дело с экранированием апострофов. Рекомендованный метод заключения в кавычки фиксированного текста в теле функции — это экранирование знаками доллара. (Если у вас есть устаревший код, который не использует долларовые кавычки, обратитесь к обзору в разделе Обработка кавычек, который может сэкономить некоторые усилия при переводе указанного кода в более разумную схему.)

Динамические значения требуют тщательной обработки, поскольку они могут содержать символы кавычек. Пример использования format() (предполагается, что вы заключили в кавычки тело функции, поэтому кавычки не нужно удваивать):

EXECUTE format('UPDATE tbl SET %I = $1 '
   'WHERE key = $2', colname) USING newvalue, keyvalue;

Также можно напрямую вызывать функции цитирования:

EXECUTE 'UPDATE tbl SET '
        || quote_ident(colname)
        || ' = '
        || quote_literal(newvalue)
        || ' WHERE key = '
        || quote_literal(keyvalue);

Этот пример демонстрирует использование функций quote_ident и quote_literal (см. раздел Строковые функции и операторы). В целях безопасности выражения, содержащие идентификаторы столбцов или таблиц, должны передаваться через quote_ident перед вставкой в динамический запрос. Выражения, содержащие значения, которые должны быть литеральными строками в построенной команде, должны передаваться через quote_literal. Эти функции предпринимают соответствующие шаги для возврата входного текста, заключенного в двойные или одинарные кавычки соответственно, с любыми встроенными специальными символами, должным образом экранированными.

Поскольку quote_literal помечен как STRICT, он всегда будет возвращать ноль при вызове с нулевым аргументом. В приведенном выше примере, если newvalue или keyvalue были нулевыми, вся строка динамического запроса станет нулевой, что приведет к ошибке EXECUTE. Вы можете избежать этой проблемы, используя функцию quote_nullable, которая работает так же, как и quote_literal за исключением того, что при вызове с нулевым аргументом она возвращает строку NULL. Например,

EXECUTE 'UPDATE tbl SET '
        || quote_ident(colname)
        || ' = '
        || quote_nullable(newvalue)
        || ' WHERE key = '
        || quote_nullable(keyvalue);

Если вы имеете дело со значениями, которые могут быть нулевыми, вы должны обычно использовать quote_nullable вместо quote_literal.

Как всегда, нужно позаботиться о том, чтобы нулевые значения в запросе не давали непредвиденных результатов. Например, WHERE

'WHERE key = ' || quote_nullable(keyvalue)

никогда не будет успешным, если значение keyvalue равно нулю, потому что результат использования оператора равенства = с нулевым операндом всегда равен нулю. Если вы хотите, чтобы значение null работало как обычное значение ключа, вам необходимо переписать приведенное выше как

'WHERE key IS NOT DISTINCT FROM ' || quote_nullable(keyvalue)

(В настоящее время IS NOT DISTINCT FROM обрабатывается гораздо менее эффективно, чем =, поэтому не делайте этого, если не нужно. См. раздел Функции и операторы сравнения для получения дополнительной информации о пустых значениях и IS DISTINCT).

Обратите внимание, что цитирование в знаках доллара полезно только для цитирования фиксированного текста. Было бы очень плохой идеей написать этот пример так:

EXECUTE 'UPDATE tbl SET '
        || quote_ident(colname)
        || ' = $$'
        || newvalue
        || '$$ WHERE key = '
        || quote_literal(keyvalue);

потому что он сломался бы, если бы содержимое newvalue содержало $$. То же самое возражение будет относиться к любому другому ограничителю в знаках доллара, который вы можете выбрать. Таким образом, для безопасного цитирования текста, который не известен заранее, вы должны использовать quote_literal, quote_nullable или quote_ident, в зависимости от ситуации.

Динамические операторы SQL также можно безопасно создавать с помощью функции format (см. раздел FORMAT). Например:

EXECUTE format('UPDATE tbl SET %I = %L '
   'WHERE key = %L', colname, newvalue, keyvalue);

%I эквивалентен quote_ident, а %L эквивалентен quote_nullable. Функция format может использоваться вместе с предложением USING :

EXECUTE format('UPDATE tbl SET %I = $1 WHERE key = $2', colname)
   USING newvalue, keyvalue;

Эта форма лучше, потому что переменные обрабатываются в их собственном формате типа данных, а не безоговорочно преобразуют их в текст и заключают их в кавычки через %L Это также более эффективно.

Гораздо больший пример динамической команды и EXECUTE можно увидеть в Примере 10, который создает и выполняет команду CREATE FUNCTION для определения новой функции.

Получение статуса результата

Есть несколько способов определить эффект команды. Первый метод заключается в использовании команды GET DIAGNOSTICS, которая имеет вид:

GET [ CURRENT ] DIAGNOSTICS variable { = | := } item [, ... ];

Эта команда позволяет получить индикаторы состояния системы. CURRENT - это шумовое слово (но см. Также GET STACKED DIAGNOSTICS в разделе Получение информации об ошибке). Каждый item является ключевым словом, идентифицирующим значение состояния, которое должно быть назначено указанной variable (которое должно иметь правильный тип данных для его получения). Доступные в настоящее время элементы состояния показаны в таблице 1. Вместо токена SQL-standard = можно использовать двоеточие ( := ). Пример:

GET DIAGNOSTICS integer_var = ROW_COUNT;

Таблица 1. Доступные элементы диагностики

Имя | Тип | Описание
ROW_COUNT | bigint | количество строк, обработанных самой последней командой SQL
PG_CONTEXT | text | строка (и) текста, описывающего текущий стек вызовов (см. раздел Получение информации о месте выполнения)

Второй метод определения эффектов команды - это проверка специальной переменной с именем FOUND, которая имеет тип boolean. FOUND начинает false в каждом вызове функции PL/pgSQL. Он устанавливается каждым из следующих типов утверждений:

Оператор SELECT INTO устанавливает FOUND true, если назначена строка, и false, если строка не возвращается.

Оператор PERFORM устанавливает FOUND true, если он создает (и отбрасывает) одну или несколько строк, и false, если строка не создается.

Операторы UPDATE, INSERT и DELETE устанавливают FOUND true, если затронута хотя бы одна строка, и false, если строка не затрагивается.

FETCH устанавливает FOUND true, если возвращает строку, и false, если строка не возвращается.

Оператор MOVE устанавливает FOUND true, если он успешно перемещает курсор, в противном случае - false.

FOREACH FOR или FOREACH устанавливает FOUND true, если он повторяется один или несколько раз, в противном случае - false. FOUND устанавливается таким образом при выходе из цикла; внутри выполнения цикла FOUND не изменяется оператором цикла, хотя он может быть изменен выполнением других операторов в теле цикла.

Операторы RETURN QUERY и RETURN QUERY EXECUTE устанавливают FOUND true, если запрос возвращает хотя бы одну строку, и false, если строка не возвращается.

Другие операторы PL/pgSQL не изменяют состояние FOUND. В частности, обратите внимание, что EXECUTE изменяет вывод GET DIAGNOSTICS, но не меняет FOUND.

FOUND - локальная переменная в каждой функции PL/pgSQL ; любые изменения в ней влияют только на текущую функцию.

Пустой оператор

Иногда полезен пустой оператор. Например, когда одно плечо условия if / then / else намеренно пусто. Для этого используйте оператор NULL :

NULL;

Например, следующие два фрагмента кода эквивалентны:

BEGIN
    y := x / 0;
EXCEPTION
    WHEN division_by_zero THEN
        NULL;  -- ignore the error
END;

BEGIN
    y := x / 0;
EXCEPTION
    WHEN division_by_zero THEN  -- ignore the error
END;

Что предпочтительнее, это вопрос вкуса.

Заметка
В Oracle PL/SQL пустые списки операторов недопустимы, поэтому для таких ситуаций требуются операторы NULL. PL/pgSQL позволяет просто ничего не писать.

Управляющие структуры

Управляющие структуры, вероятно, самая полезная (и важная) часть PL/pgSQL. С управляющими структурами PL/pgSQL вы можете манипулировать данными QHB очень гибким и мощным способом.

Возврат из функции

Доступны две команды, которые позволяют вам возвращать данные из функции: RETURN и RETURN NEXT.

RETURN

RETURN expression;

RETURN с выражением завершает функцию и возвращает значение expression вызывающей стороне. Эта форма используется для функций PL/pgSQL, которые не возвращают набор.

В функции, которая возвращает скалярный тип, результат выражения будет автоматически преобразован в тип возврата функции, как описано для назначений. Но чтобы вернуть составное (строковое) значение, вы должны написать выражение, доставляющее точно запрошенный набор столбцов. Это может потребовать использования явного приведения.

Если вы объявили функцию с выходными параметрами, напишите просто RETURN без выражения. Текущие значения переменных выходного параметра будут возвращены.

Если вы объявили функцию, возвращающую void, оператор RETURN может быть использован для досрочного выхода из функции; но не пишите выражение после RETURN.

Возвращаемое значение функции нельзя оставлять неопределенным. Если управление достигает конца блока верхнего уровня функции, не нажимая оператор RETURN, произойдет ошибка времени выполнения. Однако это ограничение не распространяется на функции с выходными параметрами и на функции, возвращающие void. В этих случаях оператор RETURN автоматически выполняется, если заканчивается блок верхнего уровня.

Несколько примеров:

-- functions returning a scalar type
RETURN 1 + 2;
RETURN scalar_var;

-- functions returning a composite type
RETURN composite_type_var;
RETURN (1, 2, 'three'::text);  -- must cast columns to correct types

RETURN NEXT и RETURN QUERY

RETURN NEXT expression;
RETURN QUERY query;
RETURN QUERY EXECUTE command-string [ USING expression [, ... ] ];

Когда объявляется, что функция PL/pgSQL возвращает SETOF sometype, процедура, которой нужно следовать, немного отличается. В этом случае отдельные возвращаемые элементы указываются с помощью последовательности команд RETURN NEXT или RETURN QUERY, а затем используется последняя команда RETURN без аргумента, указывающая, что функция завершилась. RETURN NEXT может использоваться как со скалярными, так и с составными типами данных; с составным типом результата будет возвращена вся «таблица» результатов. RETURN QUERY добавляет результаты выполнения запроса к набору результатов функции. RETURN NEXT и RETURN QUERY можно свободно смешивать в одной функции, возвращающей множество, и в этом случае их результаты будут объединены.

RETURN NEXT и RETURN QUERY фактически не возвращаются из функции - они просто добавляют ноль или более строк в набор результатов функции. Затем выполнение продолжается со следующего оператора в функции PL/pgSQL. По мере выполнения последовательных команд RETURN NEXT или RETURN QUERY набор результатов формируется. Окончательный RETURN, который не должен иметь аргументов, заставляет элемент управления выйти из функции (или вы можете просто позволить элементу управления достигнуть конца функции).

RETURN QUERY имеет вариант RETURN QUERY EXECUTE, который указывает, что запрос должен выполняться динамически. Выражения параметров могут быть вставлены в вычисляемую строку запроса через USING, точно так же, как в команде EXECUTE.

Если вы объявили функцию с выходными параметрами, напишите просто RETURN NEXT без выражения. При каждом выполнении текущие значения переменной (или переменных) выходных параметров будут сохраняться для последующего возврата в виде строки результата. Обратите внимание, что вы должны объявить функцию как возвращающую SETOF record когда есть несколько выходных параметров, или SETOF sometype когда есть только один выходной параметр типа sometype, чтобы создать функцию, возвращающую множество с выходными параметрами.

Вот пример функции, использующей RETURN NEXT :

CREATE TABLE foo (fooid INT, foosubid INT, fooname TEXT);
INSERT INTO foo VALUES (1, 2, 'three');
INSERT INTO foo VALUES (4, 5, 'six');

CREATE OR REPLACE FUNCTION get_all_foo() RETURNS SETOF foo AS
$BODY$
DECLARE
    r foo%rowtype;
BEGIN
    FOR r IN
        SELECT * FROM foo WHERE fooid > 0
    LOOP
        -- can do some processing here
        RETURN NEXT r; -- return current row of SELECT
    END LOOP;
    RETURN;
END
$BODY$
LANGUAGE plpgsql;

SELECT * FROM get_all_foo();

Вот пример функции, использующей RETURN QUERY :

CREATE FUNCTION get_available_flightid(date) RETURNS SETOF integer AS
$BODY$
BEGIN
    RETURN QUERY SELECT flightid
                   FROM flight
                  WHERE flightdate >= $1
                    AND flightdate < ($1 + 1);

    -- Since execution is not finished, we can check whether rows were returned
    -- and raise exception if not.
    IF NOT FOUND THEN
        RAISE EXCEPTION 'No flight at %.', $1;
    END IF;

    RETURN;
 END
$BODY$
LANGUAGE plpgsql;

-- Returns available flights or raises exception if there are no
-- available flights.
SELECT * FROM get_available_flightid(CURRENT_DATE);

Заметка
Текущая реализация RETURN NEXT и RETURN QUERY сохраняет весь набор результатов перед возвратом из функции, как обсуждалось выше. Это означает, что если функция PL/pgSQL выдает очень большой набор результатов, производительность может быть низкой: данные будут записаны на диск во избежание исчерпания памяти, но сама функция не вернется, пока не будет сгенерирован весь набор результатов. Будущая версия PL/pgSQL может позволить пользователям определять функции, возвращающие множество, которые не имеют этого ограничения. В настоящее время точка, с которой данные начинают записываться на диск, контролируется переменной конфигурации work_mem. Администраторам, которые имеют достаточно памяти для хранения больших наборов результатов в памяти, следует рассмотреть возможность увеличения этого параметра.

Возврат из процедуры

Процедура не имеет возвращаемого значения. Поэтому процедура может завершиться без оператора RETURN. Если вы хотите использовать инструкцию RETURN для досрочного выхода из кода, напишите просто RETURN без выражения.

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

Вызов процедуры

Функция, процедура или блок PL/pgSQL могут вызывать процедуру, используя CALL. Выходные параметры обрабатываются иначе, чем CALL работает в простом SQL. Каждый параметр INOUT процедуры должен соответствовать переменной в операторе CALL, и все, что возвращает процедура, присваивается этой переменной после ее возврата. Например:

CREATE PROCEDURE triple(INOUT x int)
LANGUAGE plpgsql
AS $$
BEGIN
    x := x * 3;
END;
$$;

DO $$
DECLARE myvar int := 5;
BEGIN
  CALL triple(myvar);
  RAISE NOTICE 'myvar = %', myvar;  -- prints 15
END
$$;

Условные операторы

Операторы IF и CASE позволяют выполнять альтернативные команды в зависимости от определенных условий. PL/pgSQL имеет три формы IF:

IF ... THEN ... END IF

IF ... THEN ... ELSE ... END IF

IF ... THEN ... ELSIF ... THEN ... ELSE ... END IF

и две формы CASE :

CASE ... WHEN ... THEN ... ELSE ... END CASE

CASE WHEN ... THEN ... ELSE ... END CASE

IF-THEN

IF boolean-expression THEN
    statements
END IF;

IF-THEN оператор являются простейшей формой IF. Операторы между THEN и END IF будут выполнены, если условие истинно. В противном случае они пропускаются.

Пример:

IF v_user_id <> 0 THEN
    UPDATE users SET email = v_email WHERE user_id = v_user_id;
END IF;

IF-THEN-ELSE

IF boolean-expression THEN
    statements
ELSE
    statements
END IF;

Операторы IF-THEN-ELSE добавляют к IF-THEN, позволяя вам указать альтернативный набор операторов, которые должны выполняться, если условие не выполняется. (Обратите внимание, что это включает случай, когда условие оценивается как NULL).

Примеры:

IF parentid IS NULL OR parentid = ''
THEN
    RETURN fullname;
ELSE
    RETURN hp_true_filename(parentid) || '/' || fullname;
END IF;

IF v_count > 0 THEN
    INSERT INTO users_count (count) VALUES (v_count);
    RETURN 't';
ELSE
    RETURN 'f';
END IF;

IF-THEN-ELSIF

IF boolean-expression THEN
    statements
[ ELSIF boolean-expression THEN
    statements
[ ELSIF boolean-expression THEN
    statements
    ...
]
]
[ ELSE
    statements ]
END IF;

Иногда есть больше, чем просто две альтернативы. IF-THEN-ELSIF предоставляет удобный метод проверки нескольких альтернатив по очереди. Условия IF проверяются последовательно, пока не будет найдено первое истинное условие. Затем выполняются соответствующие операторы, после чего управление переходит к следующему оператору после END IF. (Любые последующие условия IF не проверяются). Если ни одно из условий IF выполняется, выполняется блок ELSE (если есть).

Вот пример:

IF number = 0 THEN
    result := 'zero';
ELSIF number > 0 THEN
    result := 'positive';
ELSIF number < 0 THEN
    result := 'negative';
ELSE
    -- hmm, the only other possibility is that number is null
    result := 'NULL';
END IF;

Ключевое слово ELSIF также может быть написано как ELSEIF.

Альтернативный способ выполнения той же задачи - вложение операторов IF-THEN-ELSE, как в следующем примере:

IF demo_row.sex = 'm' THEN
    pretty_sex := 'man';
ELSE
    IF demo_row.sex = 'f' THEN
        pretty_sex := 'woman';
    END IF;
END IF;

Однако этот метод требует написания соответствующего END IF для каждого IF, поэтому он намного более громоздкий, чем использование ELSIF когда есть много альтернатив.

Простой CASE

CASE search-expression
    WHEN expression [, expression [ ... ]] THEN
      statements
  [ WHEN expression [, expression [ ... ]] THEN
      statements
    ... ]
  [ ELSE
      statements ]
END CASE;

Простая форма CASE обеспечивает условное выполнение, основанное на равенстве операндов. search-expression оценивается (один раз) и последовательно сравнивается с каждым expression в предложениях WHEN. Если совпадение найдено, то соответствующие statements выполняются, а затем управление переходит к следующему оператору после END CASE. (Последующие выражения WHEN не оцениваются). Если совпадений не найдено, выполняются операторы ELSE; но если ELSE нет, то возникает исключение CASE_NOT_FOUND.

Простой пример:

CASE x
    WHEN 1, 2 THEN
        msg := 'one or two';
    ELSE
        msg := 'other value than one or two';
END CASE;

Поисковое выражение CASE

CASE
    WHEN boolean-expression THEN
      statements
  [ WHEN boolean-expression THEN
      statements
    ... ]
  [ ELSE
      statements ]
END CASE;

Поисковое выражение CASE обеспечивает условное выполнение, основанное на истинности логических выражений. boolean-expression каждого предложения WHEN вычисляется по очереди, пока не будет найдено одно, которое выдает true. Затем выполняются соответствующие statements, а затем управление переходит к следующему оператору после END CASE. (Последующие выражения WHEN не оцениваются). Если истинный результат не найден, выполняются statements в блоке ELSE ; но если ELSE нет, то возникает исключение CASE_NOT_FOUND.

Пример:

CASE
    WHEN x BETWEEN 0 AND 10 THEN
        msg := 'value is between zero and ten';
    WHEN x BETWEEN 11 AND 20 THEN
        msg := 'value is between eleven and twenty';
END CASE;

Эта форма CASE полностью эквивалентна IF-THEN-ELSIF, за исключением правила, согласно которому достижение пропущенного предложения ELSE приводит к ошибке, а не к бездействию.

Простые циклы

С помощью операторов LOOP, EXIT, CONTINUE, WHILE, FOR и FOREACH вы можете настроить функцию PL/pgSQL на повторение ряда команд.

LOOP

[ <<label>> ]
LOOP
    statements
END LOOP [ label ];

LOOP определяет безусловный цикл, который повторяется бесконечно, пока не будет завершен с помощью оператора EXIT или RETURN. Необязательная label может использоваться операторами EXIT и CONTINUE во вложенных циклах, чтобы указать, к какому циклу относятся эти операторы.

EXIT

EXIT [ label ] [ WHEN boolean-expression ];

Если label не указана, самый внутренний цикл завершается, а затем выполняется оператор, следующий за END LOOP. Если указана label, это должна быть метка текущего или некоторого внешнего уровня вложенного цикла или блока. Затем именованный цикл или блок завершается, и управление продолжается оператором после соответствующего END цикла / блока.

Если указано WHEN, выход из цикла происходит только в том случае, если boolean-expression имеет значение true. В противном случае управление передается оператору после EXIT.

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

При использовании с блоком BEGIN, EXIT передает управление следующему оператору после конца блока. Обратите внимание, что для этой цели должна использоваться метка; немаркированный EXIT никогда не считается совпадающим с блоком BEGIN.

Примеры:

LOOP
    -- some computations
    IF count > 0 THEN
        EXIT;  -- exit loop
    END IF;
END LOOP;

LOOP
    -- some computations
    EXIT WHEN count > 0;  -- same result as previous example
END LOOP;

<<ablock>>
BEGIN
    -- some computations
    IF stocks > 100000 THEN
        EXIT ablock;  -- causes exit from the BEGIN block
    END IF;
    -- computations here will be skipped when stocks > 100000
END;

CONTINUE

CONTINUE [ label ] [ WHEN boolean-expression ];

Если label не указана, начинается следующая итерация самого внутреннего цикла. То есть все операторы, оставшиеся в теле цикла, пропускаются, и управление возвращается к выражению управления цикла (если оно есть), чтобы определить, нужна ли еще одна итерация цикла. Если присутствует label, она указывает метку цикла, выполнение которого будет продолжено.

Если указано WHEN, следующая итерация цикла начинается только в том случае, если boolean-expression имеет значение true. В противном случае управление переходит к оператору после CONTINUE.

CONTINUE можно использовать со всеми типами циклов; он не ограничен использованием с безусловными циклами.

Примеры:

LOOP
    -- some computations
    EXIT WHEN count > 100;
    CONTINUE WHEN count < 50;
    -- some computations for count IN [50 .. 100]
END LOOP;

WHILE

[ <<label>> ]
WHILE boolean-expression LOOP
    statements
END LOOP [ label ];

Оператор WHILE повторяет последовательность операторов до тех пор, пока boolean-expression оценивается как true. Выражение проверяется перед каждой записью в теле цикла.

Например:

WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP
    -- some computations here
END LOOP;

WHILE NOT done LOOP
    -- some computations here
END LOOP;

FOR (целочисленный вариант)

[ <<label>> ]
FOR name IN [ REVERSE ] expression .. expression [ BY expression ] LOOP
    statements
END LOOP [ label ];

Эта форма FOR создает цикл, который перебирает диапазон целых значений. Имя переменной автоматически определяется как тип integer и существует только внутри цикла (любое существующее определение имени переменной игнорируется в цикле). Два выражения, дающие нижнюю и верхнюю границы диапазона, оцениваются один раз при входе в цикл. Если предложение BY не указано, шаг итерации равен 1, в противном случае это значение, указанное в предложении BY, которое снова оценивается один раз при входе в цикл. Если указано REVERSE то значение шага вычитается, а не добавляется после каждой итерации.

Некоторые примеры целочисленных циклов FOR :

FOR i IN 1..10 LOOP
    -- i will take on the values 1,2,3,4,5,6,7,8,9,10 within the loop
END LOOP;

FOR i IN REVERSE 10..1 LOOP
    -- i will take on the values 10,9,8,7,6,5,4,3,2,1 within the loop
END LOOP;

FOR i IN REVERSE 10..1 BY 2 LOOP
    -- i will take on the values 10,8,6,4,2 within the loop
END LOOP;

Если нижняя граница больше, чем верхняя граница (или меньше, в случае REVERSE), тело цикла вообще не выполняется. Ошибка не возникает.

Если к циклу FOR прикреплена label то на целочисленную переменную цикла можно ссылаться с помощью квалифицированного имени, используя эту label.

Цикл по результатам запроса

Используя цикл FOR другого типа, вы можете перебирать результаты запроса и соответствующим образом манипулировать этими данными. Синтаксис:

[ <<label>> ]
FOR target IN query LOOP
    statements
END LOOP [ label ];

target - это переменная записи, переменная строки или разделенный запятыми список скалярных переменных. target назначается последовательно каждой строке, полученной в результате query и тело цикла выполняется для каждой строки.

Пример:

CREATE FUNCTION refresh_mviews() RETURNS integer AS $$
DECLARE
    mviews RECORD;
BEGIN
    RAISE NOTICE 'Refreshing all materialized views...';

    FOR mviews IN
       SELECT n.nspname AS mv_schema,
              c.relname AS mv_name,
              pg_catalog.pg_get_userbyid(c.relowner) AS owner
         FROM pg_catalog.pg_class c
    LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
        WHERE c.relkind = 'm'
     ORDER BY 1
    LOOP

        -- Now "mviews" has one record with information about the materialized view

        RAISE NOTICE 'Refreshing materialized view %.% (owner: %)...',
                     quote_ident(mviews.mv_schema),
                     quote_ident(mviews.mv_name),
                     quote_ident(mviews.owner);
        EXECUTE format('REFRESH MATERIALIZED VIEW %I.%I', mviews.mv_schema, mviews.mv_name);
    END LOOP;

    RAISE NOTICE 'Done refreshing materialized views.';
    RETURN 1;
END;
$$ LANGUAGE plpgsql;

Если цикл завершается оператором EXIT, последнее назначенное значение строки все еще доступно после цикла.

Запрос, используемый в операторе FOR этого типа, может быть любой командой SQL, которая возвращает строки вызывающей стороне: SELECT является наиболее распространенным случаем, но вы также можете использовать INSERT, UPDATE или DELETE с предложением RETURNING. Некоторые служебные команды, такие как EXPLAIN, тоже будут работать.

Переменные PL/pgSQL подставляются в текст запроса, и план запроса кэшируется для возможного повторного использования, как подробно описано в разделе Подстановка переменных и разделе Планирование кэширования.

FOR-IN-EXECUTE - это еще один способ перебора строк:

[ <<label>> ]
FOR target IN EXECUTE text_expression [ USING expression [, ... ] ] LOOP
    statements
END LOOP [ label ];

Это похоже на предыдущую форму, за исключением того, что исходный запрос указан как строковое выражение, которое оценивается и перепланируется для каждой записи в цикле FOR. Это позволяет программисту выбирать скорость заранее спланированного запроса или гибкость динамического запроса, как с простым оператором EXECUTE. Как и в случае EXECUTE, значения параметров могут быть вставлены в динамическую команду через USING.

Другой способ указать запрос, результаты которого должны быть повторены, объявить его как курсор. Это описано в разделе Цикл по результату курсора.

Цикл по массивам

Цикл FOREACH во многом похож на цикл FOR, но вместо итерации по строкам, возвращаемым запросом SQL, он выполняет итерацию по элементам значения массива. (Как правило, FOREACH предназначен для циклического прохождения по компонентам составного выражения; в будущем могут быть добавлены варианты циклического перемещения по композитам, кроме массивов). Оператор FOREACH для циклического перемещения по массиву:

[ <<label>> ]
FOREACH target [ SLICE number ] IN ARRAY expression LOOP
    statements
END LOOP [ label ];

Без SLICE или, если SLICE 0, цикл перебирает отдельные элементы массива, созданного путем вычисления expression. target переменная назначается каждому значению элемента в последовательности, и тело цикла выполняется для каждого элемента. Вот пример зацикливания элементов целочисленного массива:

CREATE FUNCTION sum(int[]) RETURNS int8 AS $$
DECLARE
  s int8 := 0;
  x int;
BEGIN
  FOREACH x IN ARRAY $1
  LOOP
    s := s + x;
  END LOOP;
  RETURN s;
END;
$$ LANGUAGE plpgsql;

Элементы посещаются в порядке хранения, независимо от количества измерений массива. Хотя target обычно является только одна переменная, она может быть списком переменных при циклическом просмотре массива составных значений (записей). В этом случае для каждого элемента массива переменные назначаются из последовательных столбцов составного значения.

При положительном значении FOREACH выполняет итерации по фрагментам массива, а не по отдельным элементам. Значение SLICE должно быть целочисленной константой, не превышающей количество измерений массива. target переменная должна быть массивом, и она получает последовательные срезы значения массива, где каждый срез имеет количество измерений, указанных в SLICE. Вот пример итерации по одномерным слайсам:

CREATE FUNCTION scan_rows(int[]) RETURNS void AS $$
DECLARE
  x int[];
BEGIN
  FOREACH x SLICE 1 IN ARRAY $1
  LOOP
    RAISE NOTICE 'row = %', x;
  END LOOP;
END;
$$ LANGUAGE plpgsql;

SELECT scan_rows(ARRAY[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]);

NOTICE:  row = {1,2,3}
NOTICE:  row = {4,5,6}
NOTICE:  row = {7,8,9}
NOTICE:  row = {10,11,12}

Ошибки захвата

По умолчанию любая ошибка, возникающая в функции PL/pgSQL, прерывает выполнение функции, а также окружающей транзакции. Вы можете перехватывать ошибки и восстанавливать их, используя блок BEGIN с предложением EXCEPTION. Синтаксис является расширением нормального синтаксиса для блока BEGIN :

[ <<label>> ]
[ DECLARE
    declarations ]
BEGIN
    statements
EXCEPTION
    WHEN condition [ OR condition ... ] THEN
        handler_statements
    [ WHEN condition [ OR condition ... ] THEN
          handler_statements
      ... ]
END;

Если ошибки не возникает, эта форма блока просто выполняет все операторы statements, а затем управление переходит к следующему оператору после END. Но если в операторах возникает ошибка, дальнейшая обработка statements прекращается, и управление переходит в список EXCEPTION. В списке ищется первое условие condition соответствующее возникшей ошибке. Если совпадение найдено, выполняются соответствующие handler_statements, а затем управление переходит к следующему оператору после END. Если совпадение не найдено, ошибка распространяется так, как если бы предложение EXCEPTION вообще не было: ошибка может быть перехвачена включающим блоком с помощью EXCEPTION, или, если его нет, он прерывает обработку функции.

Имена condition могут быть любыми из указанных в Коды ошибок QHB. Название категории соответствует любой ошибке в ее категории. Имя специального условия OTHERS соответствует каждому типу ошибки, кроме QUERY_CANCELED и ASSERT_FAILURE. (Можно, но часто неразумно, отлавливать эти два типа ошибок по имени). Имена условий не чувствительны к регистру. Кроме того, условие ошибки может быть указано кодом SQLSTATE; например это эквивалентно:

WHEN division_by_zero THEN ...
WHEN SQLSTATE '22012' THEN ...

Если в выбранном handler_statements возникает новая ошибка, она не может быть перехвачена этим предложением EXCEPTION, но может передаваться через среду. Окружающее предложение EXCEPTION может его поймать.

Когда в предложении EXCEPTION обнаружена ошибка, локальные переменные функции PL/pgSQL остаются такими же, какими они были при возникновении ошибки, но все изменения в постоянном состоянии базы данных в блоке откатываются. В качестве примера рассмотрим этот фрагмент:

INSERT INTO mytab(firstname, lastname) VALUES('Tom', 'Jones');
BEGIN
    UPDATE mytab SET firstname = 'Joe' WHERE lastname = 'Jones';
    x := x + 1;
    y := x / 0;
EXCEPTION
    WHEN division_by_zero THEN
        RAISE NOTICE 'caught division_by_zero';
        RETURN x;
END;

Когда элемент управления достигает назначения для y, он потерпит неудачу с ошибкой Division_by_zero. Это будет поймано предложением EXCEPTION. Значение, возвращаемое в операторе RETURN будет увеличенным значением x, но влияние команды UPDATE будет отменено. Однако команда INSERT предшествующая блоку, не откатывается, поэтому в результате база данных содержит Tom Jones, а не Joe Jones.

Заметка
Блок, содержащий предложение EXCEPTION, значительно дороже для входа и выхода, чем для блока без такового. Поэтому не используйте EXCEPTION без необходимости.

Пример 2. Исключения с UPDATE/INSERT

В этом примере используется обработка исключений для выполнения UPDATE или INSERT, в зависимости от ситуации. Рекомендуется, чтобы приложения использовали INSERT с ON CONFLICT DO UPDATE а не использовали этот шаблон. Этот пример служит главным образом для иллюстрации использования структур потока управления PL/pgSQL :

CREATE TABLE db (a INT PRIMARY KEY, b TEXT);

CREATE FUNCTION merge_db(key INT, data TEXT) RETURNS VOID AS
$$
BEGIN
    LOOP
        -- first try to update the key
        UPDATE db SET b = data WHERE a = key;
        IF found THEN
            RETURN;
        END IF;
        -- not there, so try to insert the key
        -- if someone else inserts the same key concurrently,
        -- we could get a unique-key failure
        BEGIN
            INSERT INTO db(a,b) VALUES (key, data);
            RETURN;
        EXCEPTION WHEN unique_violation THEN
            -- Do nothing, and loop to try the UPDATE again.
        END;
    END LOOP;
END;
$$
LANGUAGE plpgsql;

SELECT merge_db(1, 'david');
SELECT merge_db(1, 'dennis');

Это кодирование предполагает, что ошибка unique_violation вызвана INSERT, а не, скажем, INSERT в триггерной функции таблицы. Это может также привести к неправильной работе, если в таблице имеется более одного уникального индекса, поскольку он будет повторять операцию независимо от того, какой индекс вызвал ошибку. Больше безопасности можно было бы получить, используя функции, обсуждаемые далее, чтобы проверить, была ли ожидаемая ошибка в ловушке.

Получение информации об ошибке

Обработчикам исключений часто требуется идентифицировать конкретную возникшую ошибку. Есть два способа получить информацию о текущем исключении в PL/pgSQL: специальные переменные и команда GET STACKED DIAGNOSTICS.

В обработчике исключений специальная переменная SQLSTATE содержит код ошибки, соответствующий SQLSTATE исключению . Специальная переменная SQLERRM содержит сообщение об ошибке, связанной с исключением. Эти переменные не определены вне обработчиков исключений.

Внутри обработчика исключений можно также получить информацию о текущем исключении с помощью команды GET STACKED DIAGNOSTICS, которая имеет вид:

GET STACKED DIAGNOSTICS variable { = | := } item [, ... ];

Каждый item является ключевым словом, идентифицирующим значение состояния, которое должно быть назначено указанной variable (которое должно иметь правильный тип данных для его получения). Доступные в настоящее время элементы состояния показаны в таблице 2.

Таблица 2. Элементы диагностики ошибок

Имя | Тип | Описание
RETURNED_SQLSTATE | text | код ошибки SQLSTATE исключения
COLUMN_NAME | text | имя столбца, связанного с исключением
CONSTRAINT_NAME | text | имя ограничения, связанного с исключением
PG_DATATYPE_NAME | text | имя типа данных, связанного с исключением
MESSAGE_TEXT | text | текст основного сообщения об исключении
TABLE_NAME | text | имя таблицы, связанной с исключением
SCHEMA_NAME | text | имя схемы, связанной с исключением
PG_EXCEPTION_DETAIL | text | текст подробного сообщения об исключении, если оно есть
PG_EXCEPTION_HINT | text | текст сообщения с подсказкой исключения, если таковое имеется
PG_EXCEPTION_CONTEXT | text | строка (и) текста, описывающего стек вызовов во время исключения (см. раздел Получение информации о месте выполнения)

Если исключение не установило значение для элемента, будет возвращена пустая строка.

Вот пример:

DECLARE
  text_var1 text;
  text_var2 text;
  text_var3 text;
BEGIN
  -- some processing which might cause an exception
  ...
EXCEPTION WHEN OTHERS THEN
  GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT,
                          text_var2 = PG_EXCEPTION_DETAIL,
                          text_var3 = PG_EXCEPTION_HINT;
END;

Получение информации о месте выполнения

Команда GET DIAGNOSTICS, ранее описанная в разделе Получение статуса результата, извлекает информацию о текущем состоянии выполнения (в то время как команда GET STACKED DIAGNOSTICS рассмотренная выше, сообщает информацию о состоянии выполнения на момент предыдущей ошибки). Его элемент состояния PG_CONTEXT полезен для определения текущего местоположения выполнения. PG_CONTEXT возвращает текстовую строку со строками (строками), описывающими стек вызовов. Первая строка ссылается на текущую функцию и текущую команду GET DIAGNOSTICS. Вторая и любые последующие строки относятся к вызывающим функциям дальше по стеку вызовов. Например:

CREATE OR REPLACE FUNCTION outer_func() RETURNS integer AS $$
BEGIN
  RETURN inner_func();
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION inner_func() RETURNS integer AS $$
DECLARE
  stack text;
BEGIN
  GET DIAGNOSTICS stack = PG_CONTEXT;
  RAISE NOTICE E'--- Call Stack ---\n%', stack;
  RETURN 1;
END;
$$ LANGUAGE plpgsql;

SELECT outer_func();

NOTICE:  --- Call Stack ---
PL/pgSQL function inner_func() line 5 at GET DIAGNOSTICS
PL/pgSQL function outer_func() line 3 at RETURN
CONTEXT:  PL/pgSQL function outer_func() line 3 at RETURN
 outer_func
 ------------
           1
(1 row)

GET STACKED DIAGNOSTICS ... PG_EXCEPTION_CONTEXT возвращает тот же вид трассировки стека, но описывает местоположение, в котором была обнаружена ошибка, а не текущее местоположение.

Курсоры

Вместо одновременного выполнения всего запроса можно установить курсор, который инкапсулирует запрос, и затем прочитать результат запроса по несколько строк за раз. Одна из причин этого состоит в том, чтобы избежать переполнения памяти, когда результат содержит большое количество строк. (Однако пользователям PL/pgSQL обычно не нужно беспокоиться об этом, поскольку циклы FOR автоматически используют курсор внутри, чтобы избежать проблем с памятью). Более интересным является возвращение ссылки на курсор, созданный функцией, что позволяет звонящий, чтобы прочитать строки. Это обеспечивает эффективный способ возврата больших наборов строк из функций.

Объявление переменных курсора

Весь доступ к курсорам в PL/pgSQL осуществляется через переменные курсора, которые всегда имеют специальный тип данных refcursor. Один из способов создать курсорную переменную - просто объявить ее как переменную типа refcursor. Другой способ - использовать синтаксис объявления курсора, который в общем случае:

name [ [ NO ] SCROLL ] CURSOR [ ( arguments ) ] FOR query;

(FOR может быть заменен на IS для совместимости с Oracle). Если указан SCROLL, курсор будет способен прокручиваться назад; если не указан NO SCROLL, обратные выборки будут отклонены; если ни одна из спецификаций не появляется, это зависит от запроса, будут ли разрешены обратные выборки. arguments, если указан, представляет собой разделенный запятыми список типов name datatype пар, которые определяют имена, которые должны быть заменены значениями параметров в данном запросе. Фактические значения для замены этих имен будут указаны позже, когда курсор будет открыт.

Несколько примеров:

DECLARE
    curs1 refcursor;
    curs2 CURSOR FOR SELECT * FROM tenk1;
    curs3 CURSOR (key integer) FOR SELECT * FROM tenk1 WHERE unique1 = key;

Все три из этих переменных имеют тип данных refcursor, но первая может использоваться с любым запросом, в то время как вторая имеет полностью определенный запрос, уже связанный с ним, а последняя имеет параметризованный запрос, связанный с ним. ( key будет заменен целочисленным значением параметра при открытии курсора). Переменная curs1 называется несвязанной, поскольку она не связана ни с каким конкретным запросом.

Открытие курсоров

Прежде чем курсор можно использовать для извлечения строк, его необходимо открыть. (Это действие эквивалентно команде SQL DECLARE CURSOR). PL/pgSQL имеет три формы оператора OPEN, две из которых используют несвязанные переменные курсора, а третья использует связанную переменную курсора.

Заметка
Переменные связанного курсора также можно использовать без явного открытия курсора с помощью оператора FOR описанного в разделе Цикл по результату курсора.

OPEN FOR query

OPEN unbound_cursorvar [ [ NO ] SCROLL ] FOR query;

Переменная курсора открывается и получает заданный запрос для выполнения. Курсор уже нельзя открыть, и он должен быть объявлен как несвязанная переменная курсора (то есть как простая переменная refcursor ). Запрос должен быть SELECT или чем-то еще, что возвращает строки (например, EXPLAIN ). Запрос обрабатывается так же, как и другие команды SQL в PL/pgSQL : имена переменных PL/pgSQL заменяются, и план запроса кэшируется для возможного повторного использования. Когда переменная PL/pgSQL подставляется в запрос курсора, подставляется значение, которое она имеет во время OPEN ; Последующие изменения в переменной не влияют на поведение курсора. Параметры SCROLL и NO SCROLL имеют те же значения, что и для привязанного курсора.

Пример:

OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;

OPEN FOR EXECUTE

OPEN unbound_cursorvar [ [ NO ] SCROLL ] FOR EXECUTE query_string
                                     [ USING expression [, ... ] ];

Переменная курсора открывается и получает заданный запрос для выполнения. Курсор уже нельзя открыть, и он должен быть объявлен как несвязанная переменная курсора (то есть как простая переменная refcursor ). Запрос указывается как строковое выражение, так же, как в команде EXECUTE. Как обычно, это обеспечивает гибкость, поэтому план запроса может варьироваться от одного прогона к следующему (см. раздел Планирование кэширования ), а также означает, что в командной строке не производится подстановка переменных. Как и в случае EXECUTE, значения параметров могут быть вставлены в динамическую команду через format() и USING. Параметры SCROLL и NO SCROLL имеют те же значения, что и для привязанного курсора.

Пример:

OPEN curs1 FOR EXECUTE format('SELECT * FROM %I WHERE col1 = $1',tabname) USING keyvalue;

В этом примере имя таблицы вставляется в запрос через format(). Значение сравнения для col1 вставляется через параметр USING, поэтому не нужно col1.

Открытие связанного курсора

OPEN bound_cursorvar [ ( [ argument_name := ] argument_value [, ...] ) ];

Эта форма OPEN используется для открытия переменной курсора, запрос которой был связан с ней, когда она была объявлена. Курсор уже не может быть открыт. Список фактических выражений значений аргументов должен появляться тогда и только тогда, когда указывалось, что курсор принимает аргументы. Эти значения будут подставлены в запрос.

План запроса для привязанного курсора всегда считается кэшируемым; в этом случае нет эквивалента EXECUTE. Обратите внимание, что SCROLL и NO SCROLL не могут быть указаны в OPEN, поскольку поведение прокрутки курсора уже было определено.

Значения аргумента могут быть переданы с использованием позиционной или именованной нотации. В позиционной нотации все аргументы указываются по порядку. В именованной нотации имя каждого аргумента указывается с помощью := чтобы отделить его от выражения аргумента. Как и в случае вызова функций, описанных в разделе Вызов функции, также допускается смешивание позиционной и именованной нотации.

Примеры (они используют примеры объявления курсора выше):

OPEN curs2;
OPEN curs3(42);
OPEN curs3(key := 42);

Поскольку подстановка переменных выполняется в запросе связанного курсора, на самом деле существует два способа передачи значений в курсор: либо с явным аргументом OPEN, либо неявным образом путем ссылки на переменную PL/pgSQL в запросе. Однако в него будут подставлены только переменные, объявленные до объявления ограниченного курсора. В любом случае передаваемое значение определяется во время OPEN. Например, другой способ получить тот же эффект, что и в приведенном выше примере с curs3 :

DECLARE
    key integer;
    curs4 CURSOR FOR SELECT * FROM tenk1 WHERE unique1 = key;
BEGIN
    key := 42;
    OPEN curs4;

Использование курсоров

Когда курсор открыт, им можно манипулировать с помощью описанных здесь операторов.

Эти манипуляции не обязательно должны выполняться в той же функции, которая открывала курсор для начала. Вы можете вернуть значение refcursor из функции и позволить вызывающей стороне воздействовать на курсор. (Внутренне значение refcursor - это просто строковое имя так называемого портала, содержащего активный запрос для курсора. Это имя можно передавать, присваивать другим переменным refcursor и т. д., Не нарушая портал).

Все порталы неявно закрываются в конце транзакции. Поэтому значение refcursor можно использовать для ссылки на открытый курсор только до конца транзакции.

FETCH

FETCH [ direction { FROM | IN } ] cursor INTO target;

FETCH извлекает следующую строку из курсора в цель, которая может быть переменной строки, переменной записи или списком простых переменных, разделенных запятыми, подобно SELECT INTO. Если следующей строки нет, для цели устанавливается значение NULL. Как и в случае SELECT INTO, специальная переменная FOUND может быть проверена, чтобы увидеть, была ли получена строка или нет.

Предложение direction может быть любым из вариантов, разрешенных в команде SQL FETCH, кроме тех, которые могут извлекать более одной строки; а именно, это может быть NEXT, PRIOR, FIRST, LAST, ABSOLUTE count, RELATIVE count, FORWARD или BACKWARD. direction пропуска совпадает с указанием NEXT. В формах, использующих count, count может быть любым целочисленным выражением (в отличие от команды SQL FETCH, которая допускает только целочисленную константу). Значения direction которые требуют перемещения назад, могут потерпеть неудачу, если курсор не был объявлен или открыт с помощью опции SCROLL.

cursor должен быть именем переменной refcursor которая ссылается на портал открытого курсора.

Примеры:

FETCH curs1 INTO rowvar;
FETCH curs2 INTO foo, bar, baz;
FETCH LAST FROM curs3 INTO x, y;
FETCH RELATIVE -2 FROM curs4 INTO x;

MOVE

MOVE [ direction { FROM | IN } ] cursor;

MOVE перемещает курсор без получения каких-либо данных. MOVE работает точно так же, как команда FETCH, за исключением того, что он только перемещает курсор и не возвращает перемещенную строку. Как и в случае SELECT INTO, специальная переменная FOUND может быть проверена, чтобы увидеть, была ли следующая строка для перемещения.

Примеры:

MOVE curs1;
MOVE LAST FROM curs3;
MOVE RELATIVE -2 FROM curs4;
MOVE FORWARD 2 FROM curs4;

UPDATE/DELETE WHERE CURRENT OF

UPDATE table SET ... WHERE CURRENT OF cursor;
DELETE FROM table WHERE CURRENT OF cursor;

Когда курсор расположен в строке таблицы, эту строку можно обновить или удалить, используя курсор для идентификации строки. Существуют ограничения на то, каким может быть запрос курсора (в частности, нет группировки), и лучше всего использовать FOR UPDATE в курсоре. Для получения дополнительной информации см. Справочную страницу DECLARE.

Пример:

UPDATE foo SET dataval = myval WHERE CURRENT OF curs1;

CLOSE

CLOSE cursor;

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

Пример:

CLOSE curs1;

Возврат курсора

Функции PL/pgSQL могут возвращать курсоры вызывающей стороне. Это полезно для возврата нескольких строк или столбцов, особенно с очень большими результирующими наборами. Для этого функция открывает курсор и возвращает имя курсора вызывающей стороне (или просто открывает курсор, используя имя портала, указанное или иным образом известное вызывающей стороне). Затем вызывающая сторона может извлечь строки из курсора. Курсор может быть закрыт вызывающим абонентом, или он будет закрыт автоматически при закрытии транзакции.

Имя портала, используемое для курсора, может быть указано программистом или сгенерировано автоматически. Чтобы указать имя портала, просто присвойте строку переменной refcursor перед ее открытием. Строковое значение переменной refcursor будет использоваться OPEN в качестве имени базового портала. Однако, если переменная refcursor имеет значение null, OPEN автоматически генерирует имя, которое не конфликтует с каким-либо существующим порталом, и присваивает его переменной refcursor.

Заметка
Связанная переменная курсора инициализируется строковым значением, представляющим его имя, так что имя портала совпадает с именем переменной курсора, если только программист не переопределит его присваиванием перед открытием курсора. Но несвязанная переменная курсора по умолчанию изначально имеет нулевое значение, поэтому она получит автоматически сгенерированное уникальное имя, если оно не будет переопределено.

В следующем примере показан один из способов, которым вызывающее лицо может предоставить имя курсора:

CREATE TABLE test (col text);
INSERT INTO test VALUES ('123');

CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS '
BEGIN
    OPEN $1 FOR SELECT col FROM test;
    RETURN $1;
END;
' LANGUAGE plpgsql;

BEGIN;
SELECT reffunc('funccursor');
FETCH ALL IN funccursor;
COMMIT;

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

CREATE FUNCTION reffunc2() RETURNS refcursor AS '
DECLARE
    ref refcursor;
BEGIN
    OPEN ref FOR SELECT col FROM test;
    RETURN ref;
END;
' LANGUAGE plpgsql;

-- need to be in a transaction to use cursors.
BEGIN;
SELECT reffunc2();

      reffunc2
--------------------
 <unnamed cursor 1>
(1 row)

FETCH ALL IN "<unnamed cursor 1>";
COMMIT;

В следующем примере показан один способ вернуть несколько курсоров из одной функции:

CREATE FUNCTION myfunc(refcursor, refcursor) RETURNS SETOF refcursor AS $$
BEGIN
    OPEN $1 FOR SELECT * FROM table_1;
    RETURN NEXT $1;
    OPEN $2 FOR SELECT * FROM table_2;
    RETURN NEXT $2;
END;
$$ LANGUAGE plpgsql;

-- need to be in a transaction to use cursors.
BEGIN;

SELECT * FROM myfunc('a', 'b');

FETCH ALL FROM a;
FETCH ALL FROM b;
COMMIT;

Цикл по результату курсора

Существует вариант оператора FOR который позволяет перебирать строки, возвращаемые курсором. Синтаксис:

[ <<label>> ]
FOR recordvar IN bound_cursorvar [ ( [ argument_name := ] argument_value [, ...] ) ] LOOP
    statements
END LOOP [ label ];

Переменная курсора должна быть привязана к какому-либо запросу, когда она была объявлена, и ее уже нельзя открыть. Оператор FOR автоматически открывает курсор и снова закрывает курсор при выходе из цикла. Список фактических выражений значений аргументов должен появляться тогда и только тогда, когда указывалось, что курсор принимает аргументы. Эти значения будут подставлены в запросе точно так же, как во время OPEN (см. раздел Открытие связанного курсора).

Переменная recordvar автоматически определяется как тип record и существует только внутри цикла (любое существующее определение имени переменной игнорируется в цикле). Каждая строка, возвращаемая курсором, последовательно присваивается этой переменной записи, и выполняется тело цикла.

Управление транзакциями

В процедурах, вызываемых командой CALL, а также в блоках анонимного кода (команда DO ), можно завершать транзакции с помощью команд COMMIT и ROLLBACK. Новая транзакция запускается автоматически после завершения транзакции с использованием этих команд, поэтому отдельной команды START TRANSACTION нет. (Обратите внимание, что BEGIN и END имеют разные значения в PL/pgSQL).

Простой пример:

CREATE PROCEDURE transaction_test1()
LANGUAGE plpgsql
AS $$
BEGIN
    FOR i IN 0..9 LOOP
        INSERT INTO test1 (a) VALUES (i);
        IF i % 2 = 0 THEN
            COMMIT;
        ELSE
            ROLLBACK;
        END IF;
    END LOOP;
END
$$;

CALL transaction_test1();

Новая транзакция начинается с характеристик транзакции по умолчанию, таких как уровень изоляции транзакции. В случаях, когда транзакции фиксируются в цикле, может быть желательно автоматически начинать новые транзакции с теми же характеристиками, что и предыдущая. Команды COMMIT AND CHAIN и ROLLBACK AND CHAIN выполняют это.

Управление транзакциями возможно только в CALL или DO из верхнего уровня или во вложенных CALL или DO без какой-либо другой промежуточной команды. Например, если стеком вызовов является CALL proc1() → CALL proc2() → CALL proc3(), то вторая и третья процедуры могут выполнять действия по управлению транзакциями. Но если стеком вызовов является CALL proc1() → SELECT func2() → CALL proc3(), то последняя процедура не может осуществлять управление транзакциями из-за промежуточного SELECT.

Особые соображения применимы к циклам курсора. Рассмотрим этот пример:

CREATE PROCEDURE transaction_test2()
LANGUAGE plpgsql
AS $$
DECLARE
    r RECORD;
BEGIN
    FOR r IN SELECT * FROM test2 ORDER BY x LOOP
        INSERT INTO test1 (a) VALUES (r.x);
        COMMIT;
    END LOOP;
END;
$$;

CALL transaction_test2();

Обычно курсоры автоматически закрываются при фиксации транзакции. Однако курсор, созданный как часть цикла, подобного этому, автоматически преобразуется в удерживаемый курсор с помощью первого COMMIT или ROLLBACK. Это означает, что курсор полностью вычисляется в первом COMMIT или ROLLBACK а не по строкам. Курсор по-прежнему автоматически удаляется после цикла, поэтому он в большинстве случаев невидим для пользователя.

Команды транзакции не допускаются в циклах курсора, управляемых командами, которые не предназначены только для чтения (например, UPDATE ... RETURNING).

Транзакция не может быть завершена внутри блока с обработчиками исключений.

Ошибки и сообщения

Вывод ошибок и сообщений

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

RAISE [ level ] 'format' [, expression [, ... ]] [ USING option = expression [, ... ] ];
RAISE [ level ] condition_name [ USING option = expression [, ... ] ];
RAISE [ level ] SQLSTATE 'sqlstate' [ USING option = expression [, ... ] ];
RAISE [ level ] USING option = expression [, ... ];
RAISE ;

Параметр level указывает серьезность ошибки. Допустимые уровни: DEBUG, LOG, INFO, NOTICE, WARNING и EXCEPTION, по умолчанию используется EXCEPTION. EXCEPTION вызывает ошибку (которая обычно прерывает текущую транзакцию); другие уровни генерируют только сообщения с различными уровнями приоритета. Независимо от того, передаются ли сообщения определенного приоритета клиенту, записываются ли они в журнал сервера, или и то, и другое контролируются переменными конфигурации log_min_messages и client_min_messages. См. главу Конфигурация сервера для получения дополнительной информации.

После level если он есть, вы можете написать format (который должен быть простым строковым литералом, а не выражением). Строка формата определяет текст сообщения об ошибке, о которой будет сообщено. За форматной строкой могут следовать необязательные выражения аргумента, которые будут вставлены в сообщение. Внутри строки формата % заменяется строковым представлением значения следующего необязательного аргумента. Напишите %% чтобы получить буквальный %. Количество аргументов должно соответствовать количеству % заполнителей в строке формата, иначе при компиляции функции возникает ошибка.

В этом примере значение v_job_id заменит % в строке:

RAISE NOTICE 'Calling cs_create_job(%)', v_job_id;

Вы можете прикрепить дополнительную информацию к отчету об ошибке, написав USING а затем option = expression items. Каждое expression может быть любым строковым выражением. Допустимые ключевые слова option:

Option

MESSAGE
Устанавливает текст сообщения об ошибке. Эта опция не может использоваться в форме RAISE которая включает строку формата перед USING.

DETAIL
Предоставляет подробное сообщение об ошибке.

HINT
Предоставляет подсказку.

ERRCODE
Указывает код ошибки (SQLSTATE) для отчета либо по имени условия, как показано в Коды ошибок QHB, либо непосредственно в виде пятизначного кода SQLSTATE.

COLUMN
CONSTRAINT
DATATYPE
TABLE SCHEMA
Предоставляют имя связанного объекта.

Этот пример прервет транзакцию с данным сообщением об ошибке и подсказкой:

RAISE EXCEPTION 'Nonexistent ID --> %', user_id
      USING HINT = 'Please check your user ID';

Эти два примера показывают эквивалентные способы установки SQLSTATE:

RAISE 'Duplicate user ID: %', user_id USING ERRCODE = 'unique_violation';
RAISE 'Duplicate user ID: %', user_id USING ERRCODE = '23505';

Существует второй синтаксис RAISE в котором основным аргументом является имя условия или SQLSTATE, о котором необходимо сообщить, например:

RAISE division_by_zero;
RAISE SQLSTATE '22012';

В этом синтаксисе USING может использоваться для предоставления пользовательского сообщения об ошибке, детализации или подсказки. Другой способ сделать предыдущий пример

RAISE unique_violation USING MESSAGE = 'Duplicate user ID: ' || user_id;

Еще один вариант - написать RAISE USING или RAISE level USING и поместить все остальное в список USING.

Последний вариант RAISE не имеет параметров вообще. Эта форма может использоваться только внутри предложения EXCEPTION блока BEGIN; это приводит к тому, что ошибка, обрабатываемая в данный момент, будет переброшена.

Заметка
До PostgreSQL 9.1 RAISE без параметров интерпретировалось как повторное генерирование ошибки из блока, содержащего активный обработчик исключений. Таким образом, предложение EXCEPTION, вложенное в этот обработчик, не может его перехватить, даже если RAISE находится в блоке вложенного предложения EXCEPTION. Это было сочтено удивительным, а также несовместимым с Oracle PL/SQL.

Если ни имя условия, ни SQLSTATE не указаны в команде RAISE EXCEPTION, по умолчанию используется ERRCODE_RAISE_EXCEPTION ( P0001 ). Если текст сообщения не указан, по умолчанию используется имя условия или SQLSTATE в качестве текста сообщения.

Заметка
При указании кода ошибки с помощью кода SQLSTATE вы не ограничены предварительно определенными кодами ошибок, но можете выбрать любой код ошибки, состоящий из пяти цифр и / или букв ASCII в верхнем регистре, кроме 00000. Рекомендуется избегать выдачи кодов ошибок, которые заканчиваются тремя нулями, потому что это коды категорий, которые могут быть захвачены только путем захвата всей категории.

Проверка утверждений

Оператор ASSERT является удобным сокращением для вставки проверок отладки в функции PL/pgSQL.

ASSERT condition [, message ];

condition является логическим выражением, которое, как ожидается, всегда будет иметь значение true; если это так, оператор ASSERT больше ничего не делает. Если результатом является ложь или ASSERT_FAILURE, то возникает исключение ASSERT_FAILURE. (Если ошибка возникает при оценке condition, она сообщается как нормальная ошибка).

Если предоставляется необязательное message, это выражение, результат которого (если не нулевой) заменяет текст сообщения об ошибке по умолчанию «утверждение не выполнено», если condition не выполнено. Выражение message не оценивается в обычном случае, когда утверждение успешно выполняется.

Тестирование утверждений можно включить или выключить с помощью параметра конфигурации plpgsql.check_asserts, который принимает логическое значение; по умолчанию включено. Если этот параметр off то операторы ASSERT ничего не делают.

Обратите внимание, что ASSERT предназначен для обнаружения программных ошибок, а не для сообщения об обычных ошибках. Для этого используйте оператор RAISE, описанный выше.

Триггерные функции

PL/pgSQL может использоваться для определения триггерных функций при изменении данных или событий базы данных. Триггерная функция создается с помощью команды CREATE FUNCTION, объявляющей ее как функцию без аргументов и возвращаемого типа trigger (для триггеров изменения данных) или event_trigger (для триггеров событий базы данных). Специальные локальные переменные с именем *TG_

автоматически определяются для описания условия, которое вызвало вызов.

Триггеры при изменении данных

Триггер изменения данных объявляется как функция без аргументов и возвращаемого типа trigger. Обратите внимание, что функция должна быть объявлена без аргументов, даже если она ожидает получить некоторые аргументы, указанные в CREATE TRIGGER - такие аргументы передаются через TG_ARGV, как описано ниже.

Когда в качестве триггера вызывается функция PL/pgSQL, в блоке верхнего уровня автоматически создаются несколько специальных переменных. Вот они:

Переменная |
NEW | Тип данных RECORD; переменная, содержащая новую строку базы данных для операций INSERT/UPDATE в триггерах уровня строки. Эта переменная равна нулю в триггерах уровня оператора и для операций DELETE.
OLD | Тип данных RECORD; переменная, содержащая старую строку базы данных для операций UPDATE / DELETE в триггерах уровня строки. Эта переменная равна нулю в триггерах уровня оператора и для операций INSERT.
NEW | Тип данных RECORD; переменная, содержащая новую строку базы данных для операций INSERT/UPDATE в триггерах уровня строки. Эта переменная равна нулю в триггерах уровня оператора и для операций DELETE.
OLD | Тип данных RECORD; переменная, содержащая старую строку базы данных для операций UPDATE / DELETE в триггерах уровня строки. Эта переменная равна нулю в триггерах уровня оператора и для операций INSERT.
TG_NAME | Тип данных name; переменная, которая содержит имя фактически сработавшего триггера.
TG_WHEN | Тип данных text; строка BEFORE, AFTER или INSTEAD OF, в зависимости от определения триггера.
TG_LEVEL | Тип данных text; строка ROW или STATEMENT зависимости от определения триггера.
TG_OP | Тип данных text; строка INSERT, UPDATE, DELETE или TRUNCATE указывающая, для какой операции сработал триггер.
TG_RELID | Тип данных oid; идентификатор объекта таблицы, вызвавшей вызов триггера.
TG_RELNAME | Тип данных name; имя таблицы, вызвавшей вызов триггера. Теперь это устарело и может исчезнуть в будущем выпуске. TG_TABLE_NAME этого используйте TG_TABLE_NAME.
TG_TABLE_NAME | Тип данных name; имя таблицы, вызвавшей вызов триггера.
TG_TABLE_SCHEMA | Тип данных name; имя схемы таблицы, вызвавшей вызов триггера./dd>
TG_NARGS | Тип данных integer; количество аргументов, переданных функции триггера в операторе CREATE TRIGGER.
TG_ARGV[] | Тип данных массив text; аргументы из оператора CREATE TRIGGER. Индекс отсчитывает от 0. Недопустимые индексы (меньше 0 или больше или равны tg_nargs) приводят к нулевому значению.

Функция триггера должна возвращать либо NULL либо значение записи/строки, имеющее точно структуру таблицы, для которой был запущен триггер.

Триггеры уровня строки срабатывают до того, как они могут вернуть ноль, чтобы сигнализировать диспетчеру триггеров пропустить оставшуюся часть операции для этой строки (т.е. последующие триггеры не запускаются, и INSERT/UPDATE/DELETE для этой строки не происходит). Если возвращается ненулевое значение, то операция продолжается с этим значением строки. Возвращение значения строки, отличного от исходного значения NEW изменяет строку, которая будет вставлена или обновлена. Таким образом, если триггерная функция хочет, чтобы инициирующее действие успешно выполнялось без изменения значения строки, необходимо вернуть NEW (или равное ему значение). Чтобы изменить строку, подлежащую сохранению, можно заменить отдельные значения непосредственно в NEW и вернуть измененный NEW, или создать полную новую запись / строку для возврата. В случае до-триггера на DELETE, возвращаемое значение не имеет прямого эффекта, но оно должно быть ненулевым, чтобы позволить действию триггера продолжаться. Обратите внимание, что NEW является нулем в триггерах DELETE, поэтому возвращать его обычно не имеет смысла. Обычная идиома в триггерах DELETE - возвращать OLD.

INSTEAD OF (которые всегда являются триггерами уровня строки и могут использоваться только в представлениях) могут возвращать ноль, чтобы указать, что они не выполняли никаких обновлений и что оставшаяся часть операции для этой строки должна быть пропущена (т.е. последующие триггеры не срабатывают, и строка не учитывается в состоянии, затронутом строками для окружающего INSERT/UPDATE/DELETE). В противном случае должно быть возвращено ненулевое значение, чтобы сигнализировать, что триггер выполнил запрошенную операцию. Для операций INSERT и UPDATE возвращаемое значение должно быть NEW, которое триггерная функция может изменить для поддержки INSERT RETURNING и UPDATE RETURNING (это также повлияет на значение строки, передаваемое любым последующим триггерам или передаваемое в специальную ссылку EXCLUDED alias в пределах Оператор INSERT с предложением ON CONFLICT DO UPDATE). Для операций DELETE возвращаемое значение должно быть OLD.

Возвращаемое значение срабатывания триггера уровня строки AFTER или триггера уровня оператора BEFORE или AFTER всегда игнорируется; это также может быть нулевым. Однако любой из этих типов триггеров может все же прервать всю операцию, вызвав ошибку.

Пример 3 показывает вариант функции триггера в PL/pgSQL.

Пример 3. Функция запуска PL/pgSQL

Этот пример триггера гарантирует, что каждый раз, когда строка вставляется или обновляется в таблице, текущее имя пользователя и время указываются в строке. И это проверяет, что имя сотрудника дано и что заработная плата является положительным значением.

CREATE TABLE emp (
    empname text,
    salary integer,
    last_date timestamp,
    last_user text
);

CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
    BEGIN
        -- Check that empname and salary are given
        IF NEW.empname IS NULL THEN
            RAISE EXCEPTION 'empname cannot be null';
        END IF;
        IF NEW.salary IS NULL THEN
            RAISE EXCEPTION '% cannot have null salary', NEW.empname;
        END IF;

        -- Who works for us when they must pay for it?
        IF NEW.salary < 0 THEN
            RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
        END IF;

        -- Remember who changed the payroll when
        NEW.last_date := current_timestamp;
        NEW.last_user := current_user;
        RETURN NEW;
    END;
$emp_stamp$ LANGUAGE plpgsql;

CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
    FOR EACH ROW EXECUTE FUNCTION emp_stamp();

Другой способ регистрации изменений в таблице заключается в создании новой таблицы, содержащей строку для каждой происходящей вставки, обновления или удаления. Этот подход можно рассматривать как аудит изменений в таблице. Пример 4 показывает вариант функции запуска аудита в PL/pgSQL.

Пример 4. Функция запуска PL/pgSQL для аудита

Этот пример триггера гарантирует, что любая вставка, обновление или удаление строки в таблице emp записано (т.е. проверено) в таблице emp_audit. Текущее время и имя пользователя указываются в строке вместе с типом выполняемой над ней операции.

CREATE TABLE emp (
    empname           text NOT NULL,
    salary            integer
);

CREATE TABLE emp_audit(
    operation         char(1)   NOT NULL,
    stamp             timestamp NOT NULL,
    userid            text      NOT NULL,
    empname           text      NOT NULL,
    salary integer
);

CREATE OR REPLACE FUNCTION process_emp_audit() RETURNS TRIGGER AS $emp_audit$
    BEGIN
        --
        -- Create a row in emp_audit to reflect the operation performed on emp,
        -- making use of the special variable TG_OP to work out the operation.
        --
        IF (TG_OP = 'DELETE') THEN
            INSERT INTO emp_audit SELECT 'D', now(), user, OLD.*;
        ELSIF (TG_OP = 'UPDATE') THEN
            INSERT INTO emp_audit SELECT 'U', now(), user, NEW.*;
        ELSIF (TG_OP = 'INSERT') THEN
            INSERT INTO emp_audit SELECT 'I', now(), user, NEW.*;
        END IF;
        RETURN NULL; -- result is ignored since this is an AFTER trigger
    END;
$emp_audit$ LANGUAGE plpgsql;

CREATE TRIGGER emp_audit
AFTER INSERT OR UPDATE OR DELETE ON emp
    FOR EACH ROW EXECUTE FUNCTION process_emp_audit();

Вариант предыдущего примера использует представление, соединяющее основную таблицу с таблицей аудита, чтобы показать, когда каждая запись последний раз изменялась. Этот подход по-прежнему регистрирует полный контрольный журнал изменений таблицы, но также представляет упрощенное представление контрольного журнала, показывая только последнюю измененную временную метку, полученную из контрольного журнала для каждой записи. Пример 5 показывает вариант триггера аудита для представления в PL/pgSQL.

Пример 5. Функция запуска PL/pgSQL View для аудита

В этом примере используется триггер в представлении, чтобы сделать его обновляемым и убедиться, что любая вставка, обновление или удаление строки в представлении записано (т.е. проверено) в таблице emp_audit. Текущее время и имя пользователя записываются вместе с типом выполненной операции, и представление отображает время последнего изменения каждой строки.

CREATE TABLE emp (
    empname           text PRIMARY KEY,
    salary            integer
);

CREATE TABLE emp_audit(
    operation         char(1)   NOT NULL,
    userid            text      NOT NULL,
    empname           text      NOT NULL,
    salary            integer,
    stamp             timestamp NOT NULL
);

CREATE VIEW emp_view AS
    SELECT e.empname,
           e.salary,
           max(ea.stamp) AS last_updated
      FROM emp e
      LEFT JOIN emp_audit ea ON ea.empname = e.empname
     GROUP BY 1, 2;

CREATE OR REPLACE FUNCTION update_emp_view() RETURNS TRIGGER AS $$
    BEGIN
        --
        -- Perform the required operation on emp, and create a row in emp_audit
        -- to reflect the change made to emp.
        --
        IF (TG_OP = 'DELETE') THEN
            DELETE FROM emp WHERE empname = OLD.empname;
            IF NOT FOUND THEN RETURN NULL; END IF;

            OLD.last_updated = now();
            INSERT INTO emp_audit VALUES('D', user, OLD.*);
            RETURN OLD;
        ELSIF (TG_OP = 'UPDATE') THEN
            UPDATE emp SET salary = NEW.salary WHERE empname = OLD.empname;
            IF NOT FOUND THEN RETURN NULL; END IF;

            NEW.last_updated = now();
            INSERT INTO emp_audit VALUES('U', user, NEW.*);
            RETURN NEW;
        ELSIF (TG_OP = 'INSERT') THEN
            INSERT INTO emp VALUES(NEW.empname, NEW.salary);

            NEW.last_updated = now();
            INSERT INTO emp_audit VALUES('I', user, NEW.*);
            RETURN NEW;
        END IF;
    END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER emp_audit
INSTEAD OF INSERT OR UPDATE OR DELETE ON emp_view
    FOR EACH ROW EXECUTE FUNCTION update_emp_view();

Одним из применений триггеров является ведение сводной таблицы другой таблицы. Результирующая сводка может использоваться вместо исходной таблицы для определенных запросов - часто с значительно сокращенным временем выполнения. Этот метод обычно используется в хранилищах данных, где таблицы измеренных или наблюдаемых данных (так называемые таблицы фактов) могут быть чрезвычайно большими. В примере 6 показан вариант функции триггера в PL/pgSQL, которая поддерживает сводную таблицу для таблицы фактов в хранилище данных.

Пример 6. Функция запуска PL/pgSQL для ведения сводной таблицы

Схема, подробно описанная здесь, частично основана на примере магазина продуктов из набора инструментов хранилища данных Ральфа Кимбалла.

--
-- Main tables - time dimension and sales fact.
--
CREATE TABLE time_dimension (
    time_key                    integer NOT NULL,
    day_of_week                 integer NOT NULL,
    day_of_month                integer NOT NULL,
    month                       integer NOT NULL,
    quarter                     integer NOT NULL,
    year                        integer NOT NULL
);
CREATE UNIQUE INDEX time_dimension_key ON time_dimension(time_key);

CREATE TABLE sales_fact (
    time_key                    integer NOT NULL,
    product_key                 integer NOT NULL,
    store_key                   integer NOT NULL,
    amount_sold                 numeric(12,2) NOT NULL,
    units_sold                  integer NOT NULL,
    amount_cost                 numeric(12,2) NOT NULL
);
CREATE INDEX sales_fact_time ON sales_fact(time_key);

--
-- Summary table - sales by time.
--
CREATE TABLE sales_summary_bytime (
    time_key                    integer NOT NULL,
    amount_sold                 numeric(15,2) NOT NULL,
    units_sold                  numeric(12) NOT NULL,
    amount_cost                 numeric(15,2) NOT NULL
);
CREATE UNIQUE INDEX sales_summary_bytime_key ON sales_summary_bytime(time_key);

--
-- Function and trigger to amend summarized column(s) on UPDATE, INSERT, DELETE.
--
CREATE OR REPLACE FUNCTION maint_sales_summary_bytime() RETURNS TRIGGER
AS $maint_sales_summary_bytime$
    DECLARE
        delta_time_key          integer;
        delta_amount_sold       numeric(15,2);
        delta_units_sold        numeric(12);
        delta_amount_cost       numeric(15,2);
    BEGIN

        -- Work out the increment/decrement amount(s).
        IF (TG_OP = 'DELETE') THEN

            delta_time_key = OLD.time_key;
            delta_amount_sold = -1 * OLD.amount_sold;
            delta_units_sold = -1 * OLD.units_sold;
            delta_amount_cost = -1 * OLD.amount_cost;

        ELSIF (TG_OP = 'UPDATE') THEN

            -- forbid updates that change the time_key -
            -- (probably not too onerous, as DELETE + INSERT is how most
            -- changes will be made).
            IF ( OLD.time_key != NEW.time_key) THEN
                RAISE EXCEPTION 'Update of time_key : % -> % not allowed',
                                                      OLD.time_key, NEW.time_key;
            END IF;

            delta_time_key = OLD.time_key;
            delta_amount_sold = NEW.amount_sold - OLD.amount_sold;
            delta_units_sold = NEW.units_sold - OLD.units_sold;
            delta_amount_cost = NEW.amount_cost - OLD.amount_cost;

        ELSIF (TG_OP = 'INSERT') THEN

            delta_time_key = NEW.time_key;
            delta_amount_sold = NEW.amount_sold;
            delta_units_sold = NEW.units_sold;
            delta_amount_cost = NEW.amount_cost;

        END IF;

        -- Insert or update the summary row with the new values.
        <<insert_update>>
        LOOP
            UPDATE sales_summary_bytime
                SET amount_sold = amount_sold + delta_amount_sold,
                    units_sold = units_sold + delta_units_sold,
                    amount_cost = amount_cost + delta_amount_cost
                WHERE time_key = delta_time_key;

            EXIT insert_update WHEN found;

            BEGIN
                INSERT INTO sales_summary_bytime (
                            time_key,
                            amount_sold,
                            units_sold,
                            amount_cost)
                    VALUES (
                            delta_time_key,
                            delta_amount_sold,
                            delta_units_sold,
                            delta_amount_cost
                           );

                EXIT insert_update;

            EXCEPTION
                WHEN UNIQUE_VIOLATION THEN
                    -- do nothing
            END;
        END LOOP insert_update;

        RETURN NULL;

    END;
$maint_sales_summary_bytime$ LANGUAGE plpgsql;

CREATE TRIGGER maint_sales_summary_bytime
AFTER INSERT OR UPDATE OR DELETE ON sales_fact
    FOR EACH ROW EXECUTE FUNCTION maint_sales_summary_bytime();

INSERT INTO sales_fact VALUES(1,1,1,10,3,15);
INSERT INTO sales_fact VALUES(1,2,1,20,5,35);
INSERT INTO sales_fact VALUES(2,2,1,40,15,135);
INSERT INTO sales_fact VALUES(2,3,1,10,1,13);
SELECT * FROM sales_summary_bytime;
DELETE FROM sales_fact WHERE product_key = 1;
SELECT * FROM sales_summary_bytime;
UPDATE sales_fact SET units_sold = units_sold * 2;
SELECT * FROM sales_summary_bytime;

Триггеры AFTER также могут использовать таблицы переходов для проверки всего набора строк, измененных оператором запуска. Команда CREATE TRIGGER назначает имена одной или обеим таблицам переходов, а затем функция может ссылаться на эти имена, как если бы они были временными таблицами только для чтения (см. Пример 7).

Пример 7. Аудит с таблицами переходов

Этот пример дает те же результаты, что и Пример 4, но вместо использования триггера, который срабатывает для каждой строки, он использует триггер, который срабатывает один раз для каждого оператора, после сбора соответствующей информации в таблице переходов. Это может быть значительно быстрее, чем подход с триггером строки, когда оператор вызова изменил много строк. Обратите внимание, что мы должны сделать отдельное объявление триггера для каждого типа события, так как предложения REFERENCING должны быть разными для каждого случая. Но это не мешает нам использовать одну функцию триггера, если мы выберем. (На практике может быть лучше использовать три отдельные функции и избегать тестов во время выполнения на TG_OP).

CREATE TABLE emp (
    empname           text NOT NULL,
    salary            integer
);

CREATE TABLE emp_audit(
    operation         char(1)   NOT NULL,
    stamp             timestamp NOT NULL,
    userid            text      NOT NULL,
    empname           text      NOT NULL,
    salary integer
);

CREATE OR REPLACE FUNCTION process_emp_audit() RETURNS TRIGGER AS $emp_audit$
    BEGIN
        --
        -- Create rows in emp_audit to reflect the operations performed on emp,
        -- making use of the special variable TG_OP to work out the operation.
        --
        IF (TG_OP = 'DELETE') THEN
            INSERT INTO emp_audit
                SELECT 'D', now(), user, o.* FROM old_table o;
        ELSIF (TG_OP = 'UPDATE') THEN
            INSERT INTO emp_audit
                SELECT 'U', now(), user, n.* FROM new_table n;
        ELSIF (TG_OP = 'INSERT') THEN
            INSERT INTO emp_audit
                SELECT 'I', now(), user, n.* FROM new_table n;
        END IF;
        RETURN NULL; -- result is ignored since this is an AFTER trigger
    END;
$emp_audit$ LANGUAGE plpgsql;

CREATE TRIGGER emp_audit_ins
    AFTER INSERT ON emp
    REFERENCING NEW TABLE AS new_table
    FOR EACH STATEMENT EXECUTE FUNCTION process_emp_audit();
CREATE TRIGGER emp_audit_upd
    AFTER UPDATE ON emp
    REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
    FOR EACH STATEMENT EXECUTE FUNCTION process_emp_audit();
CREATE TRIGGER emp_audit_del
    AFTER DELETE ON emp
    REFERENCING OLD TABLE AS old_table
    FOR EACH STATEMENT EXECUTE FUNCTION process_emp_audit();

Триггеры на события

PL/pgSQL может использоваться для определения триггеров событий. QHB требует, чтобы функция, вызываемая как триггер события, была объявлена как функция без аргументов и возвращаемого типа event_trigger.

Когда функция PL/pgSQL вызывается как триггер события, в блоке верхнего уровня автоматически создаются несколько специальных переменных. Они есть:

TG_EVENT

Тип данных text; строка, представляющая событие, для которого запускается триггер.

TG_TAG

Тип данных text; переменная, содержащая тег команды, для которого срабатывает триггер.

Пример 8 показывает пример функции триггера событий в PL/pgSQL.

Пример 8. Функция запуска событий PL/pgSQL

Этот пример триггера просто вызывает сообщение NOTICE каждый раз, когда выполняется поддерживаемая команда.

CREATE OR REPLACE FUNCTION snitch() RETURNS event_trigger AS $$
BEGIN
    RAISE NOTICE 'snitch: % %', tg_event, tg_tag;
END;
$$ LANGUAGE plpgsql;

CREATE EVENT TRIGGER snitch ON ddl_command_start EXECUTE FUNCTION snitch();

PL/pgSQL под капотом

В этом разделе обсуждаются некоторые детали реализации, которые часто важно знать пользователям PL/pgSQL.

Подстановка переменных

Операторы SQL и выражения в функции PL/pgSQL могут ссылаться на переменные и параметры функции. За кулисами PL/pgSQL заменяет параметры запроса для таких ссылок. Параметры будут заменены только в тех местах, где синтаксически разрешена ссылка на параметр или столбец. В качестве крайнего случая рассмотрим пример плохого стиля программирования:

INSERT INTO foo (foo) VALUES (foo);

Первое вхождение foo должно быть синтаксически именем таблицы, поэтому оно не будет подставлено, даже если функция имеет переменную с именем foo. Второе вхождение должно быть именем столбца таблицы, поэтому оно также не будет подставлено. Только третье вхождение является кандидатом на ссылку на переменную функции.

Поскольку имена переменных синтаксически ничем не отличаются от имен столбцов таблицы, в выражениях, которые также относятся к таблицам, может быть неоднозначность: подразумевается ли данное имя для ссылки на столбец таблицы или переменную? Давайте изменим предыдущий пример на

INSERT INTO dest (col) SELECT foo + bar FROM src;

Здесь dest и src должны быть именами таблиц, а col должен быть столбцом dest, но foo и bar могут быть либо переменными функции, либо столбцами src.

По умолчанию PL/pgSQL сообщит об ошибке, если имя в выражении SQL может ссылаться либо на переменную, либо на столбец таблицы. Вы можете решить эту проблему, переименовав переменную или столбец, или квалифицируя неоднозначную ссылку, или указав PL/pgSQL, какую интерпретацию выбрать.

Самое простое решение - переименовать переменную или столбец. Общим правилом кодирования является использование другого соглашения об именах для переменных PL/pgSQL, чем для имен столбцов. Например, если вы последовательно называете переменные функции v_something, в то время как ни одно из имен столбцов не начинается с v_, никаких конфликтов не будет.

В качестве альтернативы вы можете квалифицировать неоднозначные ссылки, чтобы прояснить их. В приведенном выше примере src.foo будет однозначной ссылкой на столбец таблицы. Чтобы создать однозначную ссылку на переменную, объявите ее в помеченном блоке и используйте метку блока (см. раздел Структура PL/pgSQL). Например,

<<block>>
DECLARE
    foo int;
BEGIN
    foo := ...;
    INSERT INTO dest (col) SELECT block.foo + bar FROM src;

Здесь block.foo означает переменную, даже если в src есть столбец foo. Параметры функции, а также специальные переменные, такие как FOUND, могут быть квалифицированы по имени функции, поскольку они неявно объявляются во внешнем блоке, помеченном именем функции.

Иногда нецелесообразно исправлять все неоднозначные ссылки в большом объеме кода PL/pgSQL. В таких случаях вы можете указать, что PL/pgSQL должен разрешать неоднозначные ссылки как переменную или как столбец таблицы (который совместим с некоторыми другими системами, такими как Oracle),

Чтобы изменить это поведение в масштабе всей системы, установите для параметра конфигурации plpgsql.variable_conflict один из параметров error, use_variable или use_column (где error - заводская настройка по умолчанию). Этот параметр влияет на последующие компиляции операторов в функциях PL/pgSQL, но не на операторы, уже скомпилированные в текущем сеансе. Поскольку изменение этого параметра может привести к неожиданным изменениям в поведении функций PL/pgSQL, его может изменить только суперпользователь.

Вы также можете установить поведение для каждой функции, вставив одну из этих специальных команд в начале текста функции:

#variable_conflict error
#variable_conflict use_variable
#variable_conflict use_column

Эти команды влияют только на функцию, в которой они написаны, и переопределяют настройку plpgsql.variable_conflict. Примером является

CREATE FUNCTION stamp_user(id int, comment text) RETURNS void AS $$
    #variable_conflict use_variable
    DECLARE
        curtime timestamp := now();
    BEGIN
        UPDATE users SET last_modified = curtime, comment = comment
          WHERE users.id = id;
    END;
$$ LANGUAGE plpgsql;

В команде UPDATE curtime, comment и id будут ссылаться на переменную функции и параметры независимо от того, есть ли у users столбцы с этими именами. Обратите внимание, что нам нужно было квалифицировать ссылку на users.id в WHERE, чтобы она ссылалась на столбец таблицы. Но нам не нужно было указывать ссылку на comment как цель в списке UPDATE, потому что синтаксически это должен быть столбец users. Мы можем написать одну и ту же функцию, не завися от настройки variable_conflict следующим образом:

CREATE FUNCTION stamp_user(id int, comment text) RETURNS void AS $$
    <<fn>>
    DECLARE
        curtime timestamp := now();
    BEGIN
        UPDATE users SET last_modified = fn.curtime, comment = stamp_user.comment
          WHERE users.id = stamp_user.id;
    END;
$$ LANGUAGE plpgsql;

Подстановка переменных не происходит в командной строке, данной EXECUTE или в одном из ее вариантов. Если вам нужно вставить переменное значение в такую команду, сделайте это как часть построения строкового значения или используйте USING, как показано в разделе Выполнение динамических команд.

Подстановка переменных в настоящее время работает только в командах SELECT, INSERT, UPDATE и DELETE, поскольку основной механизм SQL допускает параметры запроса только в этих командах. Чтобы использовать непостоянное имя или значение в других типах операторов (обычно называемых служебными операторами), необходимо создать служебный оператор в виде строки и EXECUTE.

Планирование кэширования

Интерпретатор PL/pgSQL анализирует исходный текст функции и создает внутреннее двоичное дерево инструкций при первом вызове функции (в каждом сеансе). Дерево команд полностью переводит структуру операторов PL/pgSQL, но отдельные выражения SQL и команды SQL, используемые в функции, не переводятся немедленно.

Поскольку каждое выражение и команда SQL сначала выполняются в функции, интерпретатор PL/pgSQL анализирует и анализирует команду, чтобы создать подготовленный оператор, используя функцию SPI_prepare менеджера SPI. Последующие посещения этого выражения или команды повторно используют подготовленное утверждение. Таким образом, функция с условными путями кода, которые редко посещаются, никогда не будет нести затраты на анализ тех команд, которые никогда не выполняются в текущем сеансе. Недостатком является то, что ошибки в определенном выражении или команде не могут быть обнаружены, пока эта часть функции не будет достигнута во время выполнения. (Тривиальные синтаксические ошибки будут обнаружены во время начального этапа анализа, но что-либо более глубокое не будет обнаружено до выполнения).

PL/pgSQL (или, точнее, менеджер SPI) может, кроме того, попытаться кэшировать план выполнения, связанный с любым конкретным подготовленным оператором. Если кэшированный план не используется, то новый план выполнения генерируется при каждом посещении оператора, и текущие значения параметров (то есть значения переменных PL/pgSQL ) можно использовать для оптимизации выбранного плана. Если инструкция не имеет параметров или выполняется много раз, менеджер SPI рассмотрит возможность создания общего плана, который не зависит от конкретных значений параметров, и кэширования его для повторного использования. Обычно это происходит, только если план выполнения не очень чувствителен к значениям переменных PL/pgSQL, на которые есть ссылки. Если это так, генерация плана каждый раз является чистой победой. См. PREPARE для получения дополнительной информации о поведении подготовленных операторов.

Поскольку PL/pgSQL сохраняет подготовленные операторы и иногда планы выполнения таким образом, команды SQL, которые появляются непосредственно в функции PL/pgSQL, должны ссылаться на одни и те же таблицы и столбцы при каждом выполнении; то есть вы не можете использовать параметр в качестве имени таблицы или столбца в команде SQL. Чтобы обойти это ограничение, вы можете создавать динамические команды с помощью оператора PL/pgSQL EXECUTE - ценой выполнения нового анализа синтаксического анализа и создания нового плана выполнения при каждом выполнении.

Изменчивая природа переменных записи представляет другую проблему в этой связи. Когда поля переменной записи используются в выражениях или операторах, типы данных полей не должны изменяться от одного вызова функции к следующему, поскольку каждое выражение будет анализироваться с использованием типа данных, который присутствует, когда выражение является первым достиг. EXECUTE может использоваться, чтобы обойти эту проблему, когда это необходимо.

Если одна и та же функция используется в качестве триггера для нескольких таблиц, PL/pgSQL подготавливает и кэширует операторы независимо для каждой такой таблицы, то есть существует кэш для каждой функции триггера и комбинации таблиц, а не только для каждой функции. Это облегчает некоторые проблемы с различными типами данных; например, триггерная функция сможет успешно работать со столбцом с именем key даже если она имеет разные типы в разных таблицах.

Аналогично, функции, имеющие полиморфные типы аргументов, имеют отдельный кэш операторов для каждой комбинации фактических типов аргументов, для которых они были вызваны, так что различия типов данных не вызывают неожиданных сбоев.

Кэширование операторов иногда может оказать неожиданное влияние на интерпретацию чувствительных ко времени значений. Например, есть разница между тем, что делают эти две функции:

CREATE FUNCTION logfunc1(logtxt text) RETURNS void AS $$
    BEGIN
        INSERT INTO logtable VALUES (logtxt, 'now');
    END;
$$ LANGUAGE plpgsql;

и:

CREATE FUNCTION logfunc2(logtxt text) RETURNS void AS $$
    DECLARE
        curtime timestamp;
    BEGIN
        curtime := 'now';
        INSERT INTO logtable VALUES (logtxt, curtime);
    END;
$$ LANGUAGE plpgsql;

В случае logfunc1 главный анализатор QHB знает, анализируя INSERT что строка ’now’ должна интерпретироваться как timestamp, потому что целевой столбец logtable относится к этому типу. Таким образом, ’now’ будет преобразовано в постоянную timestamp при анализе INSERT, а затем будет использоваться во всех logfunc1 в течение всего времени сеанса. Излишне говорить, что это не то, что хотел программист. Лучшая идея - использовать функцию now() или current_timestamp.

В случае logfunc2 основной анализатор QHB не знает, каким должен быть тип ’now’ и поэтому возвращает значение данных типа text содержащее строку now. Во время последующего присвоения локальной переменной curtime интерпретатор PL/pgSQL преобразует эту строку в тип timestamp , вызывая функции text_out и timestamp_in для преобразования. Таким образом, вычисленная метка времени обновляется при каждом выполнении, как того ожидает программист. Несмотря на то, что это работает должным образом, это не очень эффективно, поэтому использование функции now() все равно было бы лучшей идеей.

Советы по разработке на PL/pgSQL

Хороший способ разработки в PL/pgSQL - использовать выбранный вами текстовый редактор для создания своих функций, а в другом окне использовать psql для загрузки и тестирования этих функций. Если вы делаете это таким образом, хорошей идеей будет написать функцию, используя CREATE OR REPLACE FUNCTION. Таким образом, вы можете просто перезагрузить файл, чтобы обновить определение функции. Например:

CREATE OR REPLACE FUNCTION testfunc(integer) RETURNS integer AS $$
          ....
$$ LANGUAGE plpgsql;

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

\i filename.sql

а затем немедленно выполните команды SQL для проверки функции.

Еще один хороший способ разработки на PL/pgSQL - это инструмент для доступа к базе данных с графическим интерфейсом, который облегчает разработку на процедурном языке. Одним из примеров такого инструмента является pgAdmin, хотя существуют и другие. Эти инструменты часто предоставляют удобные функции, такие как экранирование одинарных кавычек и упрощение воссоздания и отладки функций.

Обработка кавычек

Код функции PL/pgSQL указывается в CREATE FUNCTION как строковый литерал. Если вы пишете строковый литерал обычным способом с окружающими одинарными кавычками, то любые одинарные кавычки внутри тела функции должны быть удвоены; аналогично, любые обратные слеши должны быть удвоены (при условии использования синтаксиса escape-строки). Удвоение кавычек в лучшем случае утомительно, а в более сложных случаях код может стать совершенно непонятным, потому что вы можете легко найти себе полдюжины или более соседних кавычек. Вместо этого рекомендуется написать тело функции в виде строкового литерала, заключенного в «кавычки» (см. раздел Строковые константы с экранированием знаками доллара). При способе с использованием знаков доллара вы никогда не удваиваете любые кавычки, а вместо этого стараетесь выбирать разные разделители долларовых кавычек для каждого необходимого уровня вложенности. Например, можно написать команду CREATE FUNCTION как:

CREATE OR REPLACE FUNCTION testfunc(integer) RETURNS integer AS $PROC$
          ....
$PROC$ LANGUAGE plpgsql;

В рамках этого вы можете использовать кавычки для простых литеральных строк в командах SQL и $$ для разделения фрагментов команд SQL, которые вы собираете в виде строк. Если вам нужно заключить в кавычки текст, $$, вы можете использовать $Q$ и так далее.

Следующая схема показывает, что вы должны делать, когда пишете кавычки без экранирования знаками доллара. Это может быть полезно при переводе обычных кавычек в нечто более понятное.

одинарные кавычки

Чтобы начать и закончить тело функции, например:

CREATE FUNCTION foo() RETURNS integer AS '
          ....
' LANGUAGE plpgsql;

Везде в теле функции, заключенном в одинарные кавычки, кавычки должны появляться парами.

парные кавычки

Для строковых литералов внутри тела функции, например:

a_output := ''Blah'';
SELECT * FROM users WHERE f_name=''foobar'';

При подходе «экранирование знаками доллара» вы просто пишете:

a_output := 'Blah';
SELECT * FROM users WHERE f_name='foobar';

это именно то, что анализатор PL/pgSQL будет видеть в любом случае.

4 кавычки

Когда вам нужна одиночная кавычка в строковой константе внутри тела функции, например:

a_output := a_output || '' AND name LIKE ''''foobar'''' AND xyz''

Значение, добавленное к a_output, будет: AND name LIKE ’foobar’ AND xyz.

В подходе «экранирование знаками доллара» вы написали бы:

a_output := a_output || $$ AND name LIKE 'foobar' AND xyz$$

будьте осторожны, чтобы любые разделители долларовых кавычек вокруг этого не были просто $$.

6 кавычек

Когда одна кавычка в строке внутри тела функции находится рядом с концом этой строковой константы, например:

a_output := a_output || '' AND name LIKE ''''foobar''''''

Значение, добавленное к a_output будет: AND name LIKE ’foobar’.

В подходе «экранирование знаками доллара» это становится:

a_output := a_output || $$ AND name LIKE 'foobar'$$

10 кавычек

Когда вам нужно две одинарные кавычки в строковой константе (на которую приходится 8 кавычек), и это рядом с концом этой строковой константы (еще 2). Это, вероятно, понадобится вам, только если вы пишете функцию, которая генерирует другие функции, как в примере 10. Например:

a_output := a_output || '' if v_'' ||
    referrer_keys.kind || '' like ''''''''''
    || referrer_keys.key_string || ''''''''''
    then return ''''''  || referrer_keys.referrer_type
    || ''''''; end if;'';

Значение a_output тогда будет:

if v_... like ''...'' then return ''...''; end if;

В подходе «экранирование знаками доллара» это становится:

a_output := a_output || $$ if v_$$ || referrer_keys.kind || $$ like '$$
    || referrer_keys.key_string || $$'
    then return '$$  || referrer_keys.referrer_type
    || $$'; end if;$$;

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

Дополнительные проверки во время компиляции и во время выполнения

Чтобы помочь пользователю найти примеры простых, но распространенных проблем до того, как они причинят вред, PL/pgSQL предоставляет дополнительные checks. Когда они включены, в зависимости от конфигурации они могут использоваться для выдачи WARNING или ERROR во время компиляции функции. Функция, получившая WARNING может быть выполнена без выдачи дополнительных сообщений, поэтому рекомендуется выполнять тестирование в отдельной среде разработки.

При необходимости установка plpgsql.extra_warnings или plpgsql.extra_errors в значение "all" рекомендуется в средах разработки и/или тестирования.

Эти дополнительные проверки включены через переменные конфигурации plpgsql.extra_warnings для предупреждений и plpgsql.extra_errors для ошибок. Оба могут быть установлены в список проверок, разделенных запятыми, "none" или "all". По умолчанию установлено значение "none". В настоящее время список доступных проверок включает в себя:

shadowed_variables

Проверяет, скрывает ли объявление ранее определенную переменную.

strict_multi_assignment

Некоторые команды PL/pgSQL позволяют присваивать значения более чем одной переменной одновременно, например, SELECT INTO. Как правило, число целевых переменных и количество исходных переменных должны совпадать, хотя PL/pgSQL будет использовать NULL для пропущенных значений, а дополнительные переменные игнорируются. Включение этой проверки приведет к тому, что PL/pgSQL будет выдавать WARNING или ERROR всякий раз, когда число целевых переменных и количество исходных переменных различаются.

too_many_rows

Включение этой проверки заставит PL/pgSQL проверять, возвращает ли данный запрос более одной строки, когда используется предложение INTO. Поскольку оператор INTO будет когда-либо использовать только одну строку, запрос, возвращающий несколько строк, как правило, либо неэффективен, либо недетерминирован, и, следовательно, вероятно, является ошибкой.

В следующем примере показано влияние plpgsql.extra_warnings установленного на shadowed_variables:

SET plpgsql.extra_warnings TO 'shadowed_variables';

CREATE FUNCTION foo(f1 int) RETURNS int AS $$
DECLARE
f1 int;
BEGIN
RETURN f1;
END
$$ LANGUAGE plpgsql;
WARNING:  variable "f1" shadows a previously defined variable
LINE 3: f1 int;
        ^
CREATE FUNCTION

В приведенном ниже примере показаны эффекты установки plpgsql.extra_warnings в значение strict_multi_assignment:

SET plpgsql.extra_warnings TO 'strict_multi_assignment';

CREATE OR REPLACE FUNCTION public.foo()
 RETURNS void
 LANGUAGE plpgsql
AS $$
DECLARE
  x int;
  y int;
BEGIN
  SELECT 1 INTO x, y;
  SELECT 1, 2 INTO x, y;
  SELECT 1, 2, 3 INTO x, y;
END;
$$;

SELECT foo();
WARNING:  number of source and target fields in assignment does not match
DETAIL:  strict_multi_assignment check of extra_warnings is active.
HINT:  Make sure the query returns the exact list of columns.
WARNING:  number of source and target fields in assignment does not match
DETAIL:  strict_multi_assignment check of extra_warnings is active.
HINT:  Make sure the query returns the exact list of columns.

 foo
-----

(1 row)

Портирование из Oracle PL/SQL

В этом разделе объясняются различия между языком PL/pgSQL QHB и языком PL/SQL Oracle, чтобы помочь разработчикам, которые переносят приложения из Oracle® в QHB.

PL/pgSQL во многом похож на PL/SQL. Это блочно-структурированный императивный язык, и все переменные должны быть объявлены. Присвоения, циклы и условия аналогичны. Основные различия, которые следует учитывать при переносе с PL/SQL на PL/pgSQL:

Если имя, используемое в команде SQL, может быть либо именем столбца таблицы, либо ссылкой на переменную функции, PL/SQL обрабатывает его как имя столбца. Это соответствует поведению PL/pgSQL plpgsql.variable_conflict = use_column, которое не является значением по умолчанию, как описано в разделе Подстановка переменных. Во-первых, во-первых, часто лучше избегать таких неоднозначностей, но если вам нужно портировать большой объем кода, который зависит от этого поведения, настройка variable_conflict может быть лучшим решением.

В QHB тело функции должно быть записано как строковый литерал. Поэтому вам нужно использовать знаки доллара или экранировать одинарные кавычки в теле функции. (См. раздел Обработка кавычек).

Имена типов данных часто нуждаются в переводе. Например, в Oracle строковые значения обычно объявляются как имеющие тип varchar2, который является нестандартным типом SQL. В QHB вместо этого используйте тип varchar или text. Аналогично, замените тип number на numeric или используйте другой тип числовых данных, если есть более подходящий.

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

Поскольку пакетов нет, переменных уровня пакета тоже нет. Это несколько раздражает. Вместо этого вы можете сохранить состояние сеанса во временных таблицах.

Целочисленные циклы FOR с REVERSE работают по-разному: PL/SQL ведет обратный отсчет от второго числа к первому, а PL/pgSQL ведет обратный отсчет от первого числа ко второму, что требует замены границ цикла при переносе. Эта несовместимость вызывает сожаление, но вряд ли изменится. (См. раздел FOR (целочисленный вариант)).

Циклы FOR запросов (кроме курсоров) также работают по-другому: целевая переменная (и) должна быть объявлена, тогда как PL/SQL всегда объявляет их неявно. Преимущество этого состоит в том, что значения переменных все еще доступны после выхода из цикла.

Существуют различные нотационные различия для использования переменных курсора.

Примеры портирования

В примере 9 показано, как перенести простую функцию из PL/SQL в PL/pgSQL.

Пример 9. Портирование простой функции из PL/SQL в PL/pgSQL

Вот функция Oracle PL/SQL :

CREATE OR REPLACE FUNCTION cs_fmt_browser_version(v_name varchar2,
                                                  v_version varchar2)
RETURN varchar2 IS
BEGIN
    IF v_version IS NULL THEN
        RETURN v_name;
    END IF;
    RETURN v_name || '/' || v_version;
END;
/
show errors;

Давайте рассмотрим эту функцию и посмотрим на различия по сравнению с PL/pgSQL :

Имя типа varchar2 должно быть изменено на varchar или text. В примерах в этом разделе мы будем использовать varchar, но text часто является лучшим выбором, если вам не нужны определенные ограничения длины строки.

Ключевое слово RETURN в прототипе функции (а не в теле функции) становится RETURNS в QHB. Кроме того, IS становится AS, и вам нужно добавить предложение LANGUAGE, поскольку PL/pgSQL не является единственным возможным языком функций.

В QHB тело функции считается строковым литералом, поэтому вокруг него нужно использовать кавычки или знаки доллара. Это заменяет прекращение / в подходе Oracle.

Команда show errors не существует в QHB и не нужна, поскольку ошибки сообщаются автоматически.

Вот как будет выглядеть эта функция при портировании на QHB:

CREATE OR REPLACE FUNCTION cs_fmt_browser_version(v_name varchar,
                                                  v_version varchar)
RETURNS varchar AS $$
BEGIN
    IF v_version IS NULL THEN
        RETURN v_name;
    END IF;
    RETURN v_name || '/' || v_version;
END;
$$ LANGUAGE plpgsql;

В примере 10 показано, как портировать функцию, которая создает другую функцию, и как справиться с возникающими проблемами цитирования.

Пример 10. Портирование функции, которая создает другую функцию из PL/SQL в PL/pgSQL

Следующая процедура извлекает строки из SELECT и строит большую функцию с результатами в операторах IF для эффективности.

Это версия Oracle:

CREATE OR REPLACE PROCEDURE cs_update_referrer_type_proc IS
    CURSOR referrer_keys IS
        SELECT * FROM cs_referrer_keys
        ORDER BY try_order;
    func_cmd VARCHAR(4000);
BEGIN
    func_cmd := 'CREATE OR REPLACE FUNCTION cs_find_referrer_type(v_host IN VARCHAR2,
                 v_domain IN VARCHAR2, v_url IN VARCHAR2) RETURN VARCHAR2 IS BEGIN';

    FOR referrer_key IN referrer_keys LOOP
        func_cmd := func_cmd ||
          ' IF v_' || referrer_key.kind
          || ' LIKE ''' || referrer_key.key_string
          || ''' THEN RETURN ''' || referrer_key.referrer_type
          || '''; END IF;';
    END LOOP;

    func_cmd := func_cmd || ' RETURN NULL; END;';

    EXECUTE IMMEDIATE func_cmd;
END;
/
show errors;

Вот как эта функция может оказаться в QHB:

CREATE OR REPLACE PROCEDURE cs_update_referrer_type_proc() AS $func$
DECLARE
    referrer_keys CURSOR IS
        SELECT * FROM cs_referrer_keys
        ORDER BY try_order;
    func_body text;
    func_cmd text;
BEGIN
    func_body := 'BEGIN';

    FOR referrer_key IN referrer_keys LOOP
        func_body := func_body ||
          ' IF v_' || referrer_key.kind
          || ' LIKE ' || quote_literal(referrer_key.key_string)
          || ' THEN RETURN ' || quote_literal(referrer_key.referrer_type)
          || '; END IF;' ;
    END LOOP;

    func_body := func_body || ' RETURN NULL; END;';

    func_cmd :=
      'CREATE OR REPLACE FUNCTION cs_find_referrer_type(v_host varchar,
                                                        v_domain varchar,
                                                        v_url varchar)
        RETURNS varchar AS '
      || quote_literal(func_body)
      || ' LANGUAGE plpgsql;' ;

    EXECUTE func_cmd;
END;
$func$ LANGUAGE plpgsql;

Обратите внимание, что тело функции quote_literal отдельно и передается через quote_literal чтобы удвоить любые кавычки в нем. Этот метод необходим, потому что мы не можем безопасно использовать долларовые кавычки для определения новой функции: мы не знаем наверняка, какие строки будут интерполированы из поля referrer_key.key_string. (Здесь мы предполагаем, что referrer_key.kind всегда можно доверять, чтобы он всегда был host, domain или url, но referrer_key.key_string может быть чем угодно, в частности он может содержать знаки доллара). Эта функция на самом деле является улучшением оригинала Oracle, потому что он не будет генерировать неработающий код, когда referrer_key.key_string или referrer_key.referrer_type содержат кавычки.

В примере 11 показано, как перенести функцию с параметрами OUT и обработкой строк. QHB не имеет встроенной функции instr, но вы можете создать ее, используя комбинацию других функций. В разделе Приложение описана реализация instr в PL/pgSQL, которую вы можете использовать для упрощения переноса.

Пример 11. Перенос процедуры с параметрами String Manipulation и OUT из PL/SQL в PL/pgSQL

Следующая процедура Oracle PL/SQL используется для анализа URL-адреса и возврата нескольких элементов (хост, путь и запрос).

Это версия Oracle:

CREATE OR REPLACE PROCEDURE cs_parse_url(
    v_url IN VARCHAR2,
    v_host OUT VARCHAR2,  -- This will be passed back
    v_path OUT VARCHAR2,  -- This one too
    v_query OUT VARCHAR2) -- And this one
IS
    a_pos1 INTEGER;
    a_pos2 INTEGER;
BEGIN
    v_host := NULL;
    v_path := NULL;
    v_query := NULL;
    a_pos1 := instr(v_url, '//');

    IF a_pos1 = 0 THEN
        RETURN;
    END IF;
    a_pos2 := instr(v_url, '/', a_pos1 + 2);
    IF a_pos2 = 0 THEN
        v_host := substr(v_url, a_pos1 + 2);
        v_path := '/';
        RETURN;
    END IF;

    v_host := substr(v_url, a_pos1 + 2, a_pos2 - a_pos1 - 2);
    a_pos1 := instr(v_url, '?', a_pos2 + 1);

    IF a_pos1 = 0 THEN
        v_path := substr(v_url, a_pos2);
        RETURN;
    END IF;

    v_path := substr(v_url, a_pos2, a_pos1 - a_pos2);
    v_query := substr(v_url, a_pos1 + 1);
END;
/
show errors;

Вот возможный перевод на PL/pgSQL :

CREATE OR REPLACE FUNCTION cs_parse_url(
    v_url IN VARCHAR,
    v_host OUT VARCHAR,  -- This will be passed back
    v_path OUT VARCHAR,  -- This one too
    v_query OUT VARCHAR) -- And this one
AS $$
DECLARE
    a_pos1 INTEGER;
    a_pos2 INTEGER;
BEGIN
    v_host := NULL;
    v_path := NULL;
    v_query := NULL;
    a_pos1 := instr(v_url, '//');

    IF a_pos1 = 0 THEN
        RETURN;
    END IF;
    a_pos2 := instr(v_url, '/', a_pos1 + 2);
    IF a_pos2 = 0 THEN
        v_host := substr(v_url, a_pos1 + 2);
        v_path := '/';
        RETURN;
    END IF;

    v_host := substr(v_url, a_pos1 + 2, a_pos2 - a_pos1 - 2);
    a_pos1 := instr(v_url, '?', a_pos2 + 1);

    IF a_pos1 = 0 THEN
        v_path := substr(v_url, a_pos2);
        RETURN;
    END IF;

    v_path := substr(v_url, a_pos2, a_pos1 - a_pos2);
    v_query := substr(v_url, a_pos1 + 1);
END;
$$ LANGUAGE plpgsql;

Эту функцию можно использовать так:

SELECT * FROM cs_parse_url('http://foobar.com/query.cgi?baz');

В примере 12 показано, как портировать процедуру, которая использует многочисленные функции, характерные для Oracle.

Пример 12. Портирование процедуры из PL/SQL в PL/pgSQL

Версия Oracle:

CREATE OR REPLACE PROCEDURE cs_create_job(v_job_id IN INTEGER) IS
    a_running_job_count INTEGER;
BEGIN
    LOCK TABLE cs_jobs IN EXCLUSIVE MODE;

    SELECT count(*) INTO a_running_job_count FROM cs_jobs WHERE end_stamp IS NULL;

    IF a_running_job_count > 0 THEN
        COMMIT; -- free lock
        raise_application_error(-20000,
                 'Unable to create a new job: a job is currently running.');
    END IF;

    DELETE FROM cs_active_job;
    INSERT INTO cs_active_job(job_id) VALUES (v_job_id);

    BEGIN
        INSERT INTO cs_jobs (job_id, start_stamp) VALUES (v_job_id, now());
    EXCEPTION
        WHEN dup_val_on_index THEN NULL; -- don't worry if it already exists
    END;
    COMMIT;
END;
/
show errors

Вот как мы могли бы перенести эту процедуру в PL/pgSQL :

CREATE OR REPLACE PROCEDURE cs_create_job(v_job_id integer) AS $$
DECLARE
    a_running_job_count integer;
BEGIN
    LOCK TABLE cs_jobs IN EXCLUSIVE MODE;

    SELECT count(*) INTO a_running_job_count FROM cs_jobs WHERE end_stamp IS NULL;

    IF a_running_job_count > 0 THEN
        COMMIT; -- free lock
        RAISE EXCEPTION 'Unable to create a new job: a job is currently running'; -- [^1]
    END IF;

    DELETE FROM cs_active_job;
    INSERT INTO cs_active_job(job_id) VALUES (v_job_id);

    BEGIN
        INSERT INTO cs_jobs (job_id, start_stamp) VALUES (v_job_id, now());
    EXCEPTION
        WHEN unique_violation THEN -- [^2]
            -- don't worry if it already exists
    END;
    COMMIT;
END;
$$ LANGUAGE plpgsql;

¹

Синтаксис RAISE значительно отличается от оператора Oracle, хотя основной случай RAISE exception_name работает аналогично.

²

Имена исключений, поддерживаемые PL/pgSQL, отличаются от имен Oracle. Набор встроенных имен исключений намного больше (см. Коды ошибок QHB). В настоящее время нет способа объявить пользовательские имена исключений, хотя вы можете вместо этого выбрасывать выбранные пользователем значения SQLSTATE.

Другие вещи для наблюдения

В этом разделе объясняются некоторые другие вещи, на которые нужно обратить внимание при портировании функций Oracle PL/SQL на QHB.

Неявный откат после исключений

В PL/pgSQL, когда исключение отлавливается предложением EXCEPTION, все изменения в базе данных, так как блок BEGIN автоматически откатывается. То есть поведение эквивалентно тому, что вы получите в Oracle:

BEGIN
    SAVEPOINT s1;
    ... code here ...
EXCEPTION
    WHEN ... THEN
        ROLLBACK TO s1;
        ... code here ...
    WHEN ... THEN
        ROLLBACK TO s1;
        ... code here ...
END;

Если вы переводите процедуру Oracle, которая использует SAVEPOINT и ROLLBACK TO в этом стиле, ваша задача проста: просто опустите SAVEPOINT и ROLLBACK TO. Если у вас есть процедура, в которой SAVEPOINT и ROLLBACK TO используются по-разному, вам понадобится определенная мысль.

EXECUTE

Версия EXECUTE для PL/pgSQL работает аналогично версии PL/SQL, но вы должны помнить, что используйте quote_literal и quote_ident как описано в разделе Выполнение динамических команд. Конструкции типа EXECUTE 'SELECT * FROM $1'; не будет надежно работать, если вы не используете эти функции.

Оптимизация функций PL/pgSQL

QHB предоставляет вам два модификатора создания функции для оптимизации выполнения: « изменчивость » (всегда ли функция возвращает один и тот же результат, если даны одни и те же аргументы) и «строгость» (возвращает ли функция ноль, если какой-либо аргумент равен нулю). Обратитесь к справочной странице CREATE FUNCTION за подробностями.

При использовании этих атрибутов оптимизации ваш оператор CREATE FUNCTION может выглядеть примерно так:

CREATE FUNCTION foo(...) RETURNS integer AS $$
...
$$ LANGUAGE plpgsql STRICT IMMUTABLE;

Приложение

Этот раздел содержит код для набора Oracle-совместимых функций instr которые вы можете использовать для упрощения процесса переноса.

-
-- instr functions that mimic Oracle's counterpart
-- Syntax: instr(string1, string2 [, n [, m]])
-- where [] denotes optional parameters.
--
-- Search string1, beginning at the nth character, for the mth occurrence
-- of string2.  If n is negative, search backwards, starting at the abs(n)'th
-- character from the end of string1.
-- If n is not passed, assume 1 (search starts at first character).
-- If m is not passed, assume 1 (find first occurrence).
-- Returns starting index of string2 in string1, or 0 if string2 is not found.
--

CREATE FUNCTION instr(varchar, varchar) RETURNS integer AS $$
BEGIN
    RETURN instr($1, $2, 1);
END;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;

CREATE FUNCTION instr(string varchar, string_to_search_for varchar,
                      beg_index integer)
RETURNS integer AS $$
DECLARE
    pos integer NOT NULL DEFAULT 0;
    temp_str varchar;
    beg integer;
    length integer;
    ss_length integer;
BEGIN
    IF beg_index > 0 THEN
        temp_str := substring(string FROM beg_index);
        pos := position(string_to_search_for IN temp_str);

        IF pos = 0 THEN
            RETURN 0;
        ELSE
            RETURN pos + beg_index - 1;
        END IF;
    ELSIF beg_index < 0 THEN
        ss_length := char_length(string_to_search_for);
        length := char_length(string);
        beg := length + 1 + beg_index;

        WHILE beg > 0 LOOP
            temp_str := substring(string FROM beg FOR ss_length);
            IF string_to_search_for = temp_str THEN
                RETURN beg;
            END IF;

            beg := beg - 1;
        END LOOP;

        RETURN 0;
    ELSE
        RETURN 0;
    END IF;
END;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;

CREATE FUNCTION instr(string varchar, string_to_search_for varchar,
                      beg_index integer, occur_index integer)
RETURNS integer AS $$
DECLARE
    pos integer NOT NULL DEFAULT 0;
    occur_number integer NOT NULL DEFAULT 0;
    temp_str varchar;
    beg integer;
    i integer;
    length integer;
    ss_length integer;
BEGIN
    IF occur_index <= 0 THEN
        RAISE 'argument ''%'' is out of range', occur_index
          USING ERRCODE = '22003';
    END IF;

    IF beg_index > 0 THEN
        beg := beg_index - 1;
        FOR i IN 1..occur_index LOOP
            temp_str := substring(string FROM beg + 1);
            pos := position(string_to_search_for IN temp_str);
            IF pos = 0 THEN
                RETURN 0;
            END IF;
            beg := beg + pos;
        END LOOP;

        RETURN beg;
    ELSIF beg_index < 0 THEN
        ss_length := char_length(string_to_search_for);
        length := char_length(string);
        beg := length + 1 + beg_index;

        WHILE beg > 0 LOOP
            temp_str := substring(string FROM beg FOR ss_length);
            IF string_to_search_for = temp_str THEN
                occur_number := occur_number + 1;
                IF occur_number = occur_index THEN
                    RETURN beg;
                END IF;
            END IF;

            beg := beg - 1;
        END LOOP;

        RETURN 0;
    ELSE
        RETURN 0;
    END IF;
END;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;

Команды SQL

Эта часть содержит справочную информацию для команд SQL, поддерживаемых QHB. Под «SQL» подразумевается язык в целом; информацию о соответствии стандартам и совместимости каждой команды можно найти на соответствующей справочной странице.

ABORT — прервать текущую транзакцию

ALTER AGGREGATE — изменить определение агрегатной функции

ALTER COLLATION — изменить определение правила сортировки

ALTER CONVERSION — изменить определение перекодировки

ALTER DATABASE — изменить базу данных

ALTER DEFAULT PRIVILEGES — определить права доступа по умолчанию

ALTER DOMAIN — изменить определение домена

ALTER EVENT TRIGGER — изменить определение событийного триггера

ALTER EXTENSION — изменить определение расширения

ALTER FOREIGN DATA WRAPPER — изменить определение обертки сторонних данных

ALTER FOREIGN TABLE — изменить определение сторонней таблицы

ALTER FUNCTION — изменить определение функции

ALTER GROUP — изменить имя роли или членство

ALTER INDEX — изменить определение индекса

ALTER LANGUAGE — изменить определение процедурного языка

ALTER LARGE OBJECT — изменить определение большого объекта

ALTER MATERIALIZED VIEW — изменить определение материализованного представления

ALTER OPERATOR — изменить определение оператора

ALTER OPERATOR CLASS — изменить определение класса операторов

ALTER OPERATOR FAMILY — изменить определение семейства операторов

ALTER POLICY — изменить определение политики защиты на уровне строк

ALTER PROCEDURE — изменить определение процедуры

ALTER PUBLICATION — изменить определение публикации

ALTER ROLE — изменить роль в базе данных

ALTER ROUTINE — изменить определение подпрограммы

ALTER RULE — изменить определение правила

ALTER SCHEMA — изменить определение схемы

ALTER SEQUENCE — изменить определение генератора последовательности

ALTER SERVER — изменить определение стороннего сервера

ALTER STATISTICS — изменить определение объекта расширенной статистики

ALTER SUBSCRIPTION — изменить определение подписки

ALTER SYSTEM — изменить параметр конфигурации сервера

ALTER TABLE — изменить определение таблицы

ALTER TABLESPACE — изменить определение табличного пространства

ALTER TEXT SEARCH CONFIGURATION — изменить определение конфигурации текстового поиска

ALTER TEXT SEARCH DICTIONARY — изменить определение словаря текстового поиска

ALTER TEXT SEARCH PARSER — изменить определение анализатора текстового поиска

ALTER TEXT SEARCH TEMPLATE — изменить определение шаблона текстового поиска

ALTER TRIGGER — изменить определение триггера

ALTER TYPE — изменить определение типа

ALTER USER — изменить роль в базе данных

ALTER USER MAPPING — изменить определение сопоставления пользователей

ALTER VIEW — изменить определение представления

ANALYZE — собрать статистику по базе данных

BEGIN — начать блок транзакции

CALL — вызвать процедуру

CHECKPOINT — принудительно выполнить контрольную точку в журнале с упреждающей записью

CLOSE — закрыть курсор

CLUSTER — кластеризовать таблицу согласно индексу

COMMENT — определить или изменить комментарий объекта

COMMIT — зафиксировать текущую транзакцию

COMMIT PREPARED — зафиксировать транзакцию, которая ранее была подготовлена для двухфазной фиксации

COPY — копировать данные между файлом и таблицей

CREATE ACCESS METHOD — определить новый метод доступа

CREATE AGGREGATE — определить новую агрегатную функцию

CREATE CAST — определить новое приведение

CREATE COLLATION — определить новое правило сортировки

CREATE CONVERSION — определить новую перекодировку

CREATE DATABASE — создать новую базу данных

CREATE DOMAIN — определить новый домен

CREATE EVENT TRIGGER — определить новый событийный триггер

CREATE EXTENSION — определить новое расширение

CREATE FOREIGN DATA WRAPPER — определить новую обертку сторонних данных

CREATE FOREIGN TABLE — определить новую стороннюю таблицу

CREATE FUNCTION — определить новую функцию

CREATE GROUP — определить новую роль в базе данных

CREATE INDEX — определить новый индекс

CREATE LANGUAGE — определить новый процедурный язык

CREATE MATERIALIZED VIEW — определить новое материализованное представление

CREATE OPERATOR — определить новый оператор

CREATE OPERATOR CLASS — определить новый класс операторов

CREATE OPERATOR FAMILY — определить новое семейство операторов

CREATE POLICY — определить для таблицы новую политику защиты на уровне строк

CREATE PROCEDURE — определить новую процедуру

CREATE PUBLICATION — определить новую публикацию

CREATE ROLE — определить новую роль в базе данных

CREATE RULE — определить новое правило перезаписи

CREATE SCHEMA — определить новую схему

CREATE SEQUENCE — определить новый генератор последовательности

CREATE SERVER — определить новый сторонний сервер

CREATE STATISTICS — определить новую расширенную статистику

CREATE SUBSCRIPTION — определить новую подписку

CREATE TABLE — определить новую таблицу

CREATE TABLE AS — определить новую таблицу из результатов запроса

CREATE TABLESPACE — определить новое табличное пространство

CREATE TEXT SEARCH CONFIGURATION — определить новую конфигурацию текстового поиска

CREATE TEXT SEARCH DICTIONARY — определить новый словарь текстового поиска

CREATE TEXT SEARCH PARSER — определить новый анализатор текстового поиска

CREATE TEXT SEARCH TEMPLATE — определить новый шаблон текстового поиска

CREATE TRANSFORM — определить новую трансформацию

CREATE TRIGGER — определить новый триггер

CREATE TYPE — определить новый тип данных

CREATE USER — определить новую роль в базе данных

CREATE USER MAPPING — определить новое сопоставление пользователей для стороннего сервера

CREATE VIEW — определить новое представление

DEALLOCATE — освободить подготовленный оператор

DECLARE — определить курсор

DELETE — удалить строки в таблице

DISCARD — сбросить состояние сеанса

DO — выполнить анонимный блок кода

DROP ACCESS METHOD — удалить метод доступа

DROP AGGREGATE — удалить агрегатную функцию

DROP CAST — удалить приведение

DROP COLLATION — удалить правило сортировки

DROP CONVERSION — удалить перекодировку

DROP DATABASE — удалить базу данных

DROP DOMAIN — удалить домен

DROP EVENT TRIGGER — удалить событийный триггер

DROP EXTENSION — удалить расширение

DROP FOREIGN DATA WRAPPER — удалить обертку сторонних данных

DROP FOREIGN TABLE — удалить стороннюю таблицу

DROP FUNCTION — удалить функцию

DROP GROUP — удалить роль в базе данных

DROP INDEX — удалить индекс

DROP LANGUAGE — удалить процедурный язык

DROP MATERIALIZED VIEW — удалить материализованное представление

DROP OPERATOR — удалить оператор

DROP OPERATOR CLASS — удалить класс операторов

DROP OPERATOR FAMILY — удалить семейство операторов

DROP OWNED — удалить объекты базы данных, принадлежащие роли в базе данных

DROP POLICY — удалить из таблицы политику защиты на уровне строк

DROP PROCEDURE — удалить процедуру

DROP PUBLICATION — удалить публикацию

DROP ROLE — удалить роль в базе данных

DROP ROUTINE — удалить подпрограмму

DROP RULE — удалить правило перезаписи

DROP SCHEMA — удалить схему

DROP SEQUENCE — удалить последовательность

DROP SERVER — удалить дескриптор стороннего сервера

DROP STATISTICS — удалить расширенную статистику

DROP SUBSCRIPTION — удалить подписку

DROP TABLE — удалить таблицу

DROP TABLESPACE — удалить табличное пространство

DROP TEXT SEARCH CONFIGURATION — удалить конфигурацию текстового поиска

DROP TEXT SEARCH DICTIONARY — удалить словарь текстового поиска

DROP TEXT SEARCH PARSER — удалить анализатор текстового поиска

DROP TEXT SEARCH TEMPLATE — удалить шаблон текстового поиска

DROP TRANSFORM — удалить трансформацию

DROP TRIGGER — удалить триггер

DROP TYPE — удалить тип данных

DROP USER — удалить роль в базе данных

DROP USER MAPPING — удалить сопоставление пользователей для стороннего сервера

DROP VIEW — удалить представление

END — зафиксировать текущую транзакцию

EXECUTE — выполнить подготовленный оператор

EXPLAIN — показать план выполнения оператора

FETCH — получить строки из запроса при помощи курсора

GRANT — определить права доступа

IMPORT FOREIGN SCHEMA — импортировать определения таблицы со стороннего сервера

INSERT — добавить в таблицу новые строки

LISTEN — ожидать уведомления

LOAD — загрузить файл разделяемой библиотеки

LOCK — заблокировать таблицу

MOVE — переместить курсор

NOTIFY — создать уведомление

PREPARE — подготовить оператор к выполнению

PREPARE TRANSACTION — подготовить текущую транзакцию для двухфазной фиксации

REASSIGN OWNED — сменить владельца объектов базы данных, принадлежащих роли в базе данных

REFRESH MATERIALIZED VIEW — заменить содержимое материализованного представления

REINDEX — перестроить индексы

RELEASE SAVEPOINT — уничтожить определенную ранее точку сохранения

RESET — восстановить значение по умолчанию параметра периода выполнения

REVOKE — аннулировать права доступа

ROLLBACK — прервать текущую транзакцию

ROLLBACK PREPARED — отменить транзакцию, которая ранее была подготовлена для двухфазной фиксации

ROLLBACK TO SAVEPOINT — откатиться к точке сохранения

SAVEPOINT — определить новую точку сохранения в текущей транзакции

SECURITY LABEL — определить или изменить примененную к объекту метку безопасности

SELECT — получить строки из таблицы или представления

SELECT INTO — определить новую таблицу из результатов запроса

SET — изменить параметр периода выполнения

SET CONSTRAINTS — установить время проверки ограничений для текущей транзакции

SET ROLE — установить идентификатор текущего пользователя для текущей транзакции

SET SESSION AUTHORIZATION — установить идентификатор пользователя сеанса и идентификатор текущего пользователя текущего сеанса

SET TRANSACTION — установить характеристики текущей транзакции

SHOW — показать значение параметра периода выполнения

START TRANSACTION — начать блок транзакции

TRUNCATE — опустошить таблицу или набор таблиц

UNLISTEN — прекратить ожидание уведомления

UPDATE — изменить строки таблицы

VACUUM — почистить память и при необходимости проанализировать базу данных

VALUES — вычислить набор строк

ABORT

ABORT — прервать текущую транзакцию

Синтаксис

ABORT [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]

Описание

Команда ABORT откатывает текущую транзакцию и отменяет все внесенные ей изменения. По своему поведению ABORT идентична стандартной SQL-команде ROLLBACK и присутствует только по историческим причинам.

Параметры

WORK TRANSACTION

Необязательные ключевые слова, не влияют на результат.

AND CHAIN

Если указывается AND CHAIN, то сразу после окончания текущей транзакции начинается новая с теми же характеристиками, что и только что завершенная (см. раздел SET TRANSACTION). В противном случае новая транзакция не начинается.

Примечания

Для успешного завершения транзакции используйте команду COMMIT.

При выполнении ABORT вне блока транзакций будет выдано предупреждение и больше ничего не произойдет.

Примеры

Отмена всех изменений:

ABORT;

Совместимость

Команда ABORT является расширением QHB и присутствует по историческим причинам. Стандартная SQL-команда ROLLBACK идентична ей по своему поведению.

См. также

BEGIN, COMMIT, ROLLBACK

ALTER AGGREGATE

ALTER AGGREGATE — изменить определение агрегатной функции

Синтаксис

ALTER AGGREGATE имя ( сигнатура_агрегатной_функции ) RENAME TO новое_имя
ALTER AGGREGATE имя ( сигнатура_агрегатной_функции )
                OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER AGGREGATE имя ( сигнатура_агрегатной_функции ) SET SCHEMA новая_схема

Где сигнатура_агрегатной_функции:

* |
[ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ] |
[ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ] ]
	ORDER BY [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ]

Описание

Команда ALTER AGGREGATE изменяет определение агрегатной функции.

Чтобы использовать команду ALTER AGGREGATE, нужно быть владельцем соответствующей агрегатной функции. Чтобы изменить схему агрегатной функции, помимо этого необходимо иметь право CREATE в новой схеме. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме агрегатной функции. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания агрегатной функции. Однако суперпользователь всё равно может сменить владельца любой агрегатной функции.)

Параметры

имя

Имя существующей агрегатной функции (может быть дополнено схемой).

режим_аргумента

Режим аргумента: IN или VARIADIC. Если этот параметр не указан, значением по умолчанию является IN.

имя_аргумента

Название аргумента. Обратите внимание, что ALTER AGGREGATE фактически не обращает никакого внимания на имена аргументов, поскольку для определения идентичности агрегатной функции требуются только типы данных аргументов.

тип_аргумента

Тип входных данных, с которыми работает агрегатная функция. Чтобы сослаться на агрегатную функцию без аргументов, вместо списка спецификаций аргументов напишите *. Чтобы сослаться на сортирующую агрегатную функцию, напишите ORDER BY между спецификациями непосредственных и агрегируемых аргументов.

новое_имя

Новое имя агрегатной функции.

новый_владелец

Новый владелец агрегатной функции.

новая_схема

Новая схема для агрегатной функции.

Примечания

Рекомендуемый синтаксис для ссылки на сортирующую агрегатную функцию — написать ORDER BY между спецификациями непосредственных и агрегируемых аргументов, как в CREATE AGGREGATE. Однако команда будет выполнена и без ORDER BY, если просто перечислить спецификации непосредственных и агрегирующих аргументов в одном списке. В такой сокращенной форме, если VARIADIC "any" использовалась как в списке непосредственных, так и в списке агрегирующих аргументов, достаточно указать VARIADIC "any" только один раз.

Примеры

Переименование агрегатной функции myavg с типом integer в my_average:

ALTER AGGREGATE myavg(integer) RENAME TO my_average;

Смена владельца агрегатной функции myavg с типом integer на joe:

ALTER AGGREGATE myavg(integer) OWNER TO joe;

Перемещение сортирующей агрегатной функции mypercentile с непосредственным аргументом типа float8 и агрегируемым аргументом типа integer в схему myschema:

ALTER AGGREGATE mypercentile(float8 ORDER BY integer) SET SCHEMA myschema;

Так тоже будет работать:

ALTER AGGREGATE mypercentile(float8, integer) SET SCHEMA myschema;

Совместимость

В стандарте SQL нет команды ALTER AGGREGATE.

См. также

CREATE AGGREGATE, DROP AGGREGATE

ALTER COLLATION

ALTER COLLATION — изменить определение правила сортировки

Синтаксис

ALTER COLLATION имя RENAME TO новое_имя
ALTER COLLATION имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER COLLATION имя SET SCHEMA новая_схема

Описание

Команда ALTER COLLATION изменяет определение правила сортировки.

Чтобы использовать команду ALTER COLLATION, нужно быть владельцем соответствующего правила сортировки. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме правила сортировки. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания правила сортировки. Однако суперпользователь всё равно может изменить владельца любого правила сортировки.)

Параметры

имя

Имя существующего правила сортировки (может быть дополнено схемой).

новое_имя

Новое имя правила сортировки.

новый_владелец

Новый владелец правила сортировки.

новая_схема

Новая схема для правила сортировки.

REFRESH VERSION

Обновить версию правила сортировки. См. Примечания ниже.

Примечания

Во время применения правил сортировки, которые предоставляются библиотекой ICU, при создании объекта для данного правила в системный каталог будет записана внутренняя версия сортировщика ICU. При использовании правила сортировки текущая версия будет сверяться с записанной, и в случае несоответствия будет выдано предупреждение, например:

WARNING:  collation "xx-x-icu" has version mismatch
DETAIL:  The collation in the database was created using version 1.2.3.4, but the operating system provides version 2.3.4.5.
HINT:  Rebuild all objects affected by this collation and run ALTER COLLATION pg_catalog."xx-x-icu" REFRESH VERSION, or build PostgreSQL with the right library version.

Изменение определений правил сортировки может привести к разрушению индексов и другим проблемам, поскольку СУБД рассчитывает, что хранимые объекты имеют определенный порядок сортировки. В целом, подобного следует избегать, но это может произойти в легитимных обстоятельствах, например при обновлении с помощью команды qhb\_upgrade двоичных файлов сервера, скомпилированных с более новой версией ICU. Когда это происходит, все объекты, зависящие от правил сортировки, должны быть перестроены, например, с помощью REINDEX. После этого можно обновить версию правил сортировки, выполнив команду ALTER COLLATION ... REFRESH VERSION. Это обновит системный каталог, и в него будет записана текущая версия сортировщика, а предупреждение исчезнет. Обратите внимание, что при выполнении команды фактически не проверяется, все ли затронутые объекты были перестроены правильно.

Когда применяются правила сортировки, предоставляемые библиотекой libc, а QHB был собран с библиотекой GNU C, в качестве версии правила сортировки используется версия библиотеки C. Поскольку определения правила сортировки обычно изменяются только с новыми версиями библиотеки GNU C, это обеспечивает некоторую (хотя и не в полной мере надежную) защиту от повреждений данных.

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

Для определения всех правил сортировки в текущей базе данных, которые необходимо обновить, и объектов, которые зависят от них, можно использовать следующий запрос:

SELECT pg_describe_object(refclassid, refobjid, refobjsubid) AS "Collation",
       pg_describe_object(classid, objid, objsubid) AS "Object"
  FROM pg_depend d JOIN pg_collation c
       ON refclassid = 'pg_collation'::regclass AND refobjid = c.oid
  WHERE c.collversion <> pg_collation_actual_version(c.oid)
  ORDER BY 1, 2;

Примеры

Переименование правила сортировки de_DE в german:

ALTER COLLATION "de_DE" RENAME TO german;

Изменение владельца правила сортировки en_US на joe:

ALTER COLLATION "en_US" OWNER TO joe;

Совместимость

В стандарте SQL нет команды ALTER COLLATION.

См. также

CREATE COLLATION, DROP COLLATION

ALTER CONVERSION

ALTER CONVERSION — изменить определение перекодировки

Синтаксис

ALTER CONVERSION имя RENAME TO новое_имя
ALTER CONVERSION имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER CONVERSION имя SET SCHEMA новая_схема

Описание

Команда ALTER CONVERSION изменяет определение перекодировки.

Чтобы использовать команду ALTER CONVERSION, нужно быть владельцем соответствующей перекодировки. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме перекодировки. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания перекодировки. Однако суперпользователь всё равно может изменить владельца любой перекодировки.)

Параметры

имя

Имя существующей перекодировки (может быть дополнено схемой).

новое_имя

Новое имя перекодировки.

новый_владелец

Новый владелец перекодировки.

новая_схема

Новая схема для перекодировки.

Примеры

Переименование перекодировки iso_8859_1_to_utf8 в latin1_to_unicode:

ALTER CONVERSION iso_8859_1_to_utf8 RENAME TO latin1_to_unicode;

Смена владельца перекодировки iso_8859_1_to_utf8 на joe:

ALTER CONVERSION iso_8859_1_to_utf8 OWNER TO joe;

Совместимость

В стандарте SQL нет команды ALTER CONVERSION.

См. также

CREATE CONVERSION, DROP CONVERSION

ALTER DATABASE

ALTER DATABASE — изменить базу данных

Синтаксис

ALTER DATABASE имя [ [ WITH ] параметр [ ... ] ]

Где параметр может быть:

    ALLOW_CONNECTIONS разрешение_подключения
    CONNECTION LIMIT предел_подключений
    IS_TEMPLATE это_шаблон

ALTER DATABASE имя RENAME TO новое_имя

ALTER DATABASE имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }

ALTER DATABASE имя SET TABLESPACE новое_табличное_пространство

ALTER DATABASE имя SET параметр_конфигурации { TO | = } { значение | DEFAULT }
ALTER DATABASE имя SET параметр_конфигурации FROM CURRENT
ALTER DATABASE имя RESET параметр_конфигурации
ALTER DATABASE имя RESET ALL

Описание

Команда ALTER DATABASE изменяет атрибуты базы данных.

Первая форма команды изменяет определенные параметры на уровне базы данных. (Подробное описание см. ниже). Изменять эти параметры может только владелец соответствующей базы данных или суперпользователь.

Вторая форма команды изменяет имя базы данных. Переименовать базу данных может только владелец базы данных или суперпользователь (не суперпользователи должны обладать правом CREATEDB). Переименовать текущую базу данных нельзя. (Если это нужно сделать, подключитесь к другой базе данных.)

Третья форма команды изменяет владельца базы данных. Для смены владельца текущий пользователь должен быть ее владельцем, а также непосредственным или опосредованным членом новой роли-владельца и иметь право CREATEDB. (Обратите внимание, что суперпользователи получают все эти права автоматически.)

Четвертая форма команды изменяет у базы данных табличное пространство по умолчанию. Это может сделать только владелец базы данных или суперпользователь; кроме того, необходимо иметь право на создание нового табличного пространства. Эта команда физически перемещает все таблицы или индексы из старого табличного пространства по умолчанию в новое. Новое табличное пространство по умолчанию должно быть пустым, и никто не должен быть подключен к базе данных. Таблицы и индексы из нестандартных табличных пространств не затрагиваются.

Остальные формы данной команды изменяют сеансовое значение по умолчанию у конфигурационной переменной периода выполнения для базы данных QHB. При последующем запуске нового сеанса в этой базе данных указанное значение становится значением по умолчанию. Заданные для базы данных значения по умолчанию переопределяют любые значения из qhb.conf или полученные из командной строки QHB. Только владелец базы данных и суперпользователь могут изменить для базы данных сеансовые значения по умолчанию. Некоторые переменные таким образом установить нельзя, либо их может установить только суперпользователь.

Параметры

имя

Имя базы данных, атрибуты которой подлежат изменению.

разрешение_подключения

При значении false к этой базе данных никто не сможет подключиться.

предел_подключений

Устанавливает, сколько одновременных подключений может быть сделано к этой базе данных. -1 означает отсутствие ограничений.

это_шаблон

При значении true эта база данных может быть клонирована любым пользователем с правами CREATEDB; при значении false клонировать базу данных могут только суперпользователи или ее владелец.

новое_имя

Новое имя базы данных.

новый_владелец

Новый владелец базы данных.

новое_табличное_пространство

Новое табличное пространство по умолчанию для базы данных.

Эту форму команды нельзя выполнить внутри блока транзакций.

параметр_конфигурации
значение

Устанавливает сеансовое значение по умолчанию для указанного параметра конфигурации. Если указано значение DEFAULT (или его равнозначный вариант RESET), то предыдущее значение параметра удаляется и в новых сеансах будет действовать значение, взятое из системных настроек по умолчанию. Для сброса всех значений параметров базы данных выполните RESET ALL. SET FROM CURRENT устанавливает значение параметра базы данных из его значения в текущем сеансе.

Дополнительную информацию о допустимых именах и значениях параметров см. в разделе SET и главе Конфигурация сервера.

Примечания

Также существует возможность привязать параметры сеанса по умолчанию к определенной роли, а не к базе данных; см. раздел ALTER ROLE. При возникновении конфликта ролевые параметры переопределяют параметры для конкретной базы данных.

Примеры

Выключение сканирования индекса по умолчанию в базе данных test:

ALTER DATABASE test SET enable_indexscan TO off;

Совместимость

Команда ALTER DATABASE является расширением QHB.

См. также

CREATE DATABASE, DROP DATABASE, SET, CREATE TABLESPACE

ALTER DEFAULT PRIVILEGES

ALTER DEFAULT PRIVILEGES — определить права доступа по умолчанию

Синтаксис

ALTER DEFAULT PRIVILEGES
    [ FOR { ROLE | USER } целевая_роль [, ...] ]
    [ IN SCHEMA имя_схемы [, ...] ]
    предложение_GRANT_или_REVOKE

Где предложение_GRANT_или_REVOKE может быть следующим:

GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
    [, ...] | ALL [ PRIVILEGES ] }
    ON TABLES
    TO { [ GROUP ] имя_роли | PUBLIC } [, ...] [ WITH GRANT OPTION ]

GRANT { { USAGE | SELECT | UPDATE }
    [, ...] | ALL [ PRIVILEGES ] }
    ON SEQUENCES
    TO { [ GROUP ] имя_роли | PUBLIC } [, ...] [ WITH GRANT OPTION ]

GRANT { EXECUTE | ALL [ PRIVILEGES ] }
    ON FUNCTIONS
    TO { [ GROUP ] имя_роли | PUBLIC } [, ...] [ WITH GRANT OPTION ]

GRANT { USAGE | ALL [ PRIVILEGES ] }
    ON TYPES
    TO { [ GROUP ] имя_роли | PUBLIC } [, ...] [ WITH GRANT OPTION ]

REVOKE [ GRANT OPTION FOR ]
    { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
    [, ...] | ALL [ PRIVILEGES ] }
    ON TABLES
    FROM { [ GROUP ] имя_роли | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { { USAGE | SELECT | UPDATE }
    [, ...] | ALL [ PRIVILEGES ] }
    ON SEQUENCES
    FROM { [ GROUP ] имя_роли | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { EXECUTE | ALL [ PRIVILEGES ] }
    ON FUNCTIONS
    FROM { [ GROUP ] имя_роли | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { USAGE | ALL [ PRIVILEGES ] }
    ON TYPES
    FROM { [ GROUP ] имя_роли | PUBLIC } [, ...]
    [ CASCADE | RESTRICT ]

Описание

Команда ALTER DEFAULT PRIVILEGES позволяет задавать права, которые будут применяться к объектам, создаваемым в будущем. (Это не влияет на права, назначенные уже существующим объектам.) В настоящее время права можно задавать только для схем, таблиц (включая представления и сторонние таблицы), последовательностей, функций и типов (включая домены). Применительно к данной команде функциями считаются также агрегатные функции и процедуры. Слова FUNCTIONS и ROUTINES в этой команде равнозначны. (ROUTINES предпочтительнее в перспективе как стандартный термин, охватывающий и функции, и процедуры. Задать права по умолчанию для функций и процедур по отдельности нельзя.)

Изменить права по умолчанию можно только для объектов, которые будут созданы текущим пользователем или ролями, членом которых он является. Права доступа могут быть установлены глобально (т. е. для всех объектов, создаваемых в текущей базе данных) или только для объектов, создаваемых в определенных схемах.

Как объяснено в разделе Права, права по умолчанию для любого типа объекта обычно предоставляют все назначаемые разрешения владельцу объекта и могут также предоставлять некоторые права роли PUBLIC. Однако это поведение можно изменить путем смены глобальных прав по умолчанию командой ALTER DEFAULT PRIVILEGES.

Права по умолчанию на уровне схемы добавляются к любым глобальным правам по умолчанию, применимым к определенному типу объекта. Иными словами, нельзя отозвать права на уровне схемы, если они назначены глобально (либо по умолчанию, либо в соответствии с предыдущей командой ALTER DEFAULT PRIVILEGES, где схема не указана). На уровне схемы команда REVOKE может быть полезна только для того, чтобы отменять действия предыдущей команды GRANT для этой же схемы.

Параметры

целевая_роль

Имя существующей роли, членом которой является текущая роль. Если FOR ROLE не указано, предполагается текущая роль.

имя_схемы

Имя существующей схемы. Если этот параметр указан, то права доступа по умолчанию изменяются для объектов, которые будут создаваться в этой схеме позднее. Если IN SCHEMA не указано, меняются глобальные права по умолчанию. Нельзя указывать IN SCHEMA при установлении прав для схем, так как схемы не могут быть вложенными.

имя_роли

Имя существующей роли, для которой необходимо предоставить или отозвать права. Этот параметр, а также все остальные параметры в предложении_grant_или_revoke действуют как описано в GRANT или REVOKE, за исключением того, что они распространяются не на один конкретный объект, а на целый класс объектов.

Примечания

Для получения информации о текущих назначениях прав по умолчанию, воспользуйтесь командой \ddp в psql. Интерпретация отображения данных о правах такая же, как приведенная в описании команды \dp в разделе Права.

Если вы хотите удалить роль, права по умолчанию для которой были изменены, необходимо отменить изменения прав по умолчанию или воспользоваться командой DROP OWNED BY для упразднения назначенных для этой роли прав по умолчанию.

Примеры

Добавление права SELECT всем пользователям на все таблицы (и представления), которые будут созданы в будущем в схеме myschema, и добавление роли webuser права INSERT для этих же таблиц:

ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT SELECT ON TABLES TO PUBLIC;
ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT INSERT ON TABLES TO webuser;

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

ALTER DEFAULT PRIVILEGES IN SCHEMA myschema REVOKE SELECT ON TABLES FROM PUBLIC;
ALTER DEFAULT PRIVILEGES IN SCHEMA myschema REVOKE INSERT ON TABLES FROM webuser;

Отзыв из общего доступа права EXECUTE (которое обычно выдается для функций) для всех функций, которые будут созданы ролью admin:

ALTER DEFAULT PRIVILEGES FOR ROLE admin REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;

Однако обратите внимание, что тот же эффект нельзя получить с помощью команды, ограниченной одной схемой. Эта команда будет действовать, только если она отменяет соответствующую команду GRANT:

ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC;

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

Совместимость

В стандарте SQL нет команды ALTER DEFAULT PRIVILEGES.

См. также

GRANT, REVOKE

ALTER DOMAIN

ALTER DOMAIN — изменить определение домена

Синтаксис

ALTER DOMAIN имя
    { SET DEFAULT выражение | DROP DEFAULT }
ALTER DOMAIN имя
    { SET | DROP } NOT NULL
ALTER DOMAIN имя
    ADD ограничение_домена [ NOT VALID ]
ALTER DOMAIN имя
    DROP CONSTRAINT [ IF EXISTS ] имя_ограничения [ RESTRICT | CASCADE ]
ALTER DOMAIN имя
     RENAME CONSTRAINT имя_ограничения TO имя_нового_ограничения
ALTER DOMAIN имя
    VALIDATE CONSTRAINT имя_ограничения
ALTER DOMAIN имя
    OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER DOMAIN имя
    RENAME TO новое_имя
ALTER DOMAIN имя
    SET SCHEMA новая_схема

Описание

Команда ALTER DOMAIN изменяет определение существующего домена. Она имеет несколько форм:

SET/DROP DEFAULT

Эти формы устанавливают или удаляют значение по умолчанию для домена. Обратите внимание, что значения по умолчанию применяются только к последующим командам INSERT; они не влияют на строки c данным доменом, уже находящиеся в таблице.

SET/DROP NOT NULL

Эти формы определяют, будут ли разрешены значения NULL. Установить SET NOT NULL можно, только если столбцы, использующие домен, не содержат нулевых значений.

ADD ограничение_домена [ NOT VALID ]

Эта форма добавляет новое ограничение к домену, используя тот же синтаксис, который описан в CREATE DOMAIN. При добавлении нового ограничения домена, все столбцы, использующие этот домен, будут проверены на соответствие вновь добавленному ограничению. Эти проверки можно пропустить, если добавить указание NOT VALID; а потом активировать ограничение с помощью ALTER DOMAIN ... VALIDATE CONSTRAINT. При добавлении и изменении строк они всегда проверяются по всем ограничениям, даже тем, которые отмечены NOT VALID. NOT VALID принимается только для ограничений CHECK.

DROP CONSTRAINT [ IF EXISTS ]

Эта форма удаляет ограничения домена. Если указывается IF EXISTS, а ограничение не существует, это не считается ошибкой. В этом случае выдается предупреждение.

RENAME CONSTRAINT

Эта форма изменяет имя ограничения домена.

VALIDATE CONSTRAINT

Эта форма включает проверку ограничения, ранее добавленного как NOT VALID, то есть проверяет, что все значения в табличных столбцах типа домена удовлетворяют указанному ограничению.

OWNER

Эта форма меняет владельца домена на указанного пользователя.

RENAME

Эта форма изменяет имя домена.

SET SCHEMA

Эта форма изменяет схему домена. Все связанные с доменом ограничения также перемещаются в новую схему.

Чтобы использовать команду ALTER DOMAIN, нужно быть владельцем соответствующего домена. Чтобы изменить схему домена, необходимо помимо этого иметь право CREATE в новой схеме. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме домена. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания домена. Однако суперпользователь всё равно может сменить владельца любого домена.)

Параметры

имя

Имя существующего домена, подлежащего изменению (может быть дополнено схемой).

ограничение_домена

Новое ограничение домена.

имя_ограничения

Имя существующего ограничения, которое будет удалено или переименовано.

NOT VALID

Не проверять существующие сохраненные данные на соответствие ограничению.

CASCADE

Автоматически удалять объекты, зависящие от ограничения, и в свою очередь все объекты, зависящие от этих объектов (см. Отслеживание зависимостей).

RESTRICT

Запретить удаление ограничения, если есть какие-либо зависимые от него объекты. Это поведение по умолчанию.

новое_имя

Новое имя домена.

имя_нового_ограничения

Новое имя ограничения.

новый_владелец

Имя пользователя, который станет новым владельцем домена.

новая_схема

Новая схема домена.

Примечания

Несмотря на то, что команда ALTER DOMAIN ADD CONSTRAINT пытается проверить, что существующие данные удовлетворяют новому ограничению, эта проверка не очень надежна, поскольку команда не может «видеть» строки таблицы, которые были только что добавлены или изменены и еще не зафиксированы. Если существует опасность того, что параллельные операции могут добавлять неверные данные, можно применить следующий подход: создайте ограничения с указанием NOT VALID, зафиксируйте эту команду, подождите, пока завершатся все начатые до фиксирования транзакции, а затем выполните ALTER DOMAIN VALIDATE CONSTRAINT для поиска данных, нарушающих ограничение. Данный метод надежен, так как после фиксирования ограничения он будет гарантированно действовать на все новые значения типа домена, вносимые последующими транзакциями.

В настоящее время ALTER DOMAIN ADD CONSTRAINT, ALTER DOMAIN VALIDATE CONSTRAINT и ALTER DOMAIN SET NOT NULL завершаются ошибкой, если в какой-либо таблице базы данных указанный домен или любой производный от него домен используется в столбце контейнерного типа (составном, диапазонном или массиве). В дальнейшем эти команды будут доработаны, с тем чтобы проверять новое ограничение и для таких вложенных значений.

Примеры

Добавление ограничения NOT NULL к домену:

ALTER DOMAIN zipcode SET NOT NULL;

Удаление ограничения NOT NULL из домена:

ALTER DOMAIN zipcode DROP NOT NULL;

Добавление ограничения-проверки к домену:

ALTER DOMAIN zipcode ADD CONSTRAINT zipchk CHECK (char_length(VALUE) = 5);

Удаление ограничения-проверки из домена:

ALTER DOMAIN zipcode DROP CONSTRAINT zipchk;

Переименование ограничения-проверки в домене:

ALTER DOMAIN zipcode RENAME CONSTRAINT zipchk TO zip_check;

Перемещение домена в другую схему:

ALTER DOMAIN zipcode SET SCHEMA customers;

Совместимость

Команда ALTER DOMAIN соответствует стандарту SQL, за исключением форм OWNER, RENAME, SET SCHEMA и VALIDATE CONSTRAINT, которые являются расширениями QHB. Указание NOT VALID формы ADD CONSTRAINT также является расширением QHB.

См. также

CREATE DOMAIN, DROP DOMAIN

ALTER EVENT TRIGGER

ALTER EVENT TRIGGER — изменить определение событийного триггера

Синтаксис

ALTER EVENT TRIGGER имя DISABLE
ALTER EVENT TRIGGER имя ENABLE [ REPLICA | ALWAYS ]
ALTER EVENT TRIGGER имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER EVENT TRIGGER имя RENAME TO новое_имя

Описание

Команда ALTER EVENT TRIGGER изменяет свойства существующего событийного триггера.

Чтобы изменить событийный триггер, нужно быть суперпользователем.

Параметры

имя

Имя существующего триггера, подлежащего изменению.

новый_владелец

Имя пользователя, который станет новым владельцем событийного триггера.

новое_имя

Новое имя событийного триггера.

DISABLE/ENABLE [ REPLICA | ALWAYS ] TRIGGER

Эти формы настраивают запуск событийного триггера. Выключенный триггер сохраняется в системе, но не выполняется, когда происходит событие, которое его запускает. См. также session_replication_role.

Совместимость

В стандарте SQL нет команды ALTER EVENT TRIGGER.

См. также

CREATE EVENT TRIGGER, DROP EVENT TRIGGER

ALTER EXTENSION

ALTER EXTENSION — изменить определение расширения

Синтаксис

ALTER EXTENSION имя UPDATE [ TO новая_версия ]
ALTER EXTENSION имя SET SCHEMA новая_схема
ALTER EXTENSION имя ADD элемент_объект
ALTER EXTENSION имя DROP элемент_объект

Где элемент_объект может быть:

  ACCESS METHOD имя_объекта |
  AGGREGATE имя_агрегатной_функции ( сигнатура_агрегатной_функции ) |
  CAST (исходный_тип AS целевой_тип) |
  COLLATION имя_объекта |
  CONVERSION имя_объекта |
  DOMAIN имя_объекта |
  EVENT TRIGGER имя_объекта |
  FOREIGN DATA WRAPPER имя_объекта |
  FOREIGN TABLE имя_объекта |
  FUNCTION имя_функции [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] |
  MATERIALIZED VIEW имя_объекта |
  OPERATOR имя_оператора (тип_слева, тип_справа) |
  OPERATOR CLASS имя_объекта USING индексный_метод |
  OPERATOR FAMILY имя_объекта USING индексный_метод |
  [ PROCEDURAL ] LANGUAGE имя_объекта |
  PROCEDURE имя_процедуры [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] |
  ROUTINE имя_подпрограммы [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] |
  SCHEMA имя_объекта |
  SEQUENCE имя_объекта |
  SERVER имя_объекта |
  TABLE имя_объекта |
  TEXT SEARCH CONFIGURATION имя_объекта |
  TEXT SEARCH DICTIONARY имя_объекта |
  TEXT SEARCH PARSER имя_объекта |
  TEXT SEARCH TEMPLATE имя_объекта |
  TRANSFORM FOR имя_типа LANGUAGE имя_языка |
  TYPE имя_объекта |
  VIEW имя_объекта

Где сигнатура_агрегатной_функции:

* |
[ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ] |
[ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ] ] ORDER BY [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ]

Описание

Команда ALTER EXTENSION изменяет определение установленного расширения. Она имеет несколько форм:

UPDATE

Эта форма обновляет расширение до более новой версии. Расширение должно предоставить подходящий скрипт (или набор скриптов) обновления, который сможет изменить текущую установленную версию на требуемую.

SET SCHEMA

Эта форма перемещает объекты расширения в другую схему. Для успешного выполнения этой команды расширение должно быть перемещаемым.

ADD элемент_объект

Эта форма добавляет к расширению существующий объект. Указание в основном используется в скриптах обновления расширений. Объект впоследствии будет рассматриваться как часть расширения; в частности, он может быть удален только вместе с расширением.

DROP элемент_объект

Эта форма удаляет объект из расширения. Указание в основном используется в скриптах обновления расширений. Объект при этом не удаляется, а только отделяется от расширения.

Дополнительную информацию об этих операциях см. в разделе Упаковка связанных объектов в расширение.

Чтобы использовать команду ALTER EXTENSION, нужно быть владельцем соответствующего расширения. Для форм ADD/DROP требуется также быть владельцем добавляемого/удаляемого объекта.

Параметры

имя

Имя установленного расширения.

новая_версия

Запрашиваемая новая версия расширения. Версию можно указать в виде идентификатора или строки. Если версия не указана, ALTER EXTENSION UPDATE попытается обновить расширение до версии по умолчанию в управляющем файле расширения.

новая_схема

Новая схема расширения.

имя_объекта
имя_агрегатной_функции
имя_функции
имя_оператора
имя_процедуры
имя_подпрограммы

Имя объекта, который будет добавлен или удален из расширения. Имена агрегатных функций, доменов, сторонних таблиц, функций, операторов, классов операторов, семейств операторов, процедур, подпрограмм, последовательностей, объектов текстового поиска, типов и представлений могут дополняться именем схемы.

исходный_тип

Имя исходного типа данных для приведения.

целевой_тип

Имя целевого типа данных для приведения.

режим_аргумента

Режим аргумента функции, процедуры или агрегатной функции: IN, OUT, INOUT или VARIADIC. Если этот параметр опущен, значение по умолчанию равно IN. Обратите внимание, что фактически ALTER EXTENSION не обращает никакого внимания на аргументы OUT, так как для определения идентичности функции требуются только входные аргументы. Поэтому достаточно перечислить аргументы IN, INOUT и VARIADIC.

имя_аргумента

Имя аргумента функции, процедуры или агрегатной функции. Обратите внимание, что фактически ALTER EXTENSION не обращает никакого внимания на имена аргументов, поскольку для определения идентичности функции необходимы только типы данных аргументов.

тип_аргумента

Тип данных аргумента функции, процедуры или агрегатной функции.

тип_слева
тип_справа

Типы данных аргументов оператора (могут быть дополнены схемой). При отсутствии аргумента префиксного или постфиксного оператора укажите вместо типа NONE.

PROCEDURAL

Не несет смысловой нагрузки.

имя_типа

Имя типа данных, для которого делается трансформация.

имя_языка

Название языка, для которого делается трансформация.

Примеры

Обновление расширения hstore до версии 2.0:

ALTER EXTENSION hstore UPDATE TO '2.0';

Смена схемы расширения hstore на utils:

ALTER EXTENSION hstore SET SCHEMA utils;

Добавление существующей функции в расширение hstore:

ALTER EXTENSION hstore ADD FUNCTION populate_record(anyelement, hstore);

Совместимость

Команда ALTER EXTENSION является расширением QHB.

См. также

CREATE EXTENSION, DROP EXTENSION

ALTER FOREIGN DATA WRAPPER

ALTER FOREIGN DATA WRAPPER — изменить определение обертки сторонних данных

Синтаксис

ALTER FOREIGN DATA WRAPPER имя
    [ HANDLER функция_обработчик | NO HANDLER ]
    [ VALIDATOR функция_проверки | NO VALIDATOR ]
    [ OPTIONS ( [ ADD | SET | DROP ] параметр ['значение'] [, ... ]) ]
ALTER FOREIGN DATA WRAPPER имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER FOREIGN DATA WRAPPER имя RENAME TO новое_имя

Описание

Команда ALTER FOREIGN DATA WRAPPER изменяет определение обертки сторонних данных. Первая форма команды изменяет вспомогательные функции или общие параметры обертки сторонних данных (требуется по крайней мере одно предложение). Вторая форма меняет владельца обертки сторонних данных.

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

Параметры

имя

Имя существующей обертки сторонних данных.

HANDLER функция_обработчик

Задает новую функцию обработчика для обертки сторонних данных.

NO HANDLER

Используется, чтобы указать что обертка сторонних данных больше не имеет функции обработчика. Обратите внимание, что сторонние таблицы, использующие обертки сторонних данных без функции обработчика, не будут доступны при обращении.

VALIDATOR функция_проверки

Задает новую функцию проверки для обертки сторонних данных. Обратите внимание, что возможна ситуация, когда предыдущие параметры обертки сторонних данных, зависимых серверов, сопоставлений пользователей или сторонних таблиц, окажутся недопустимыми для новой функции проверки. QHB это не проверяет. Пользователь должен самостоятельно убедиться, что эти параметры являются правильными, прежде чем использовать измененную обертку сторонних данных. Однако все параметры, изменяемые в данной команде ALTER FOREIGN DATA WRAPPER, будут проверены с помощью новой функции проверки.

NO VALIDATOR

Указывает, что обертка сторонних данных больше не имеет функции проверки.

OPTIONS ( [ ADD | SET | DROP ] параметр ['значение'] [, ... ] )

Эта форма настраивает параметры обертки сторонних данных. ADD, SE и DROP указывают, какое действие будет выполнено (добавление, установка и удаление соответственно). Если явно не указана никакая операция, по умолчанию подразумевается ADD. Имена параметров должны быть уникальными: они вместе со значениями проверяются функцией проверки, если та установлена.

новый_владелец

Имя пользователя, который станет новым владельцем обертки сторонних данных.

новое_имя

Новое имя обертки сторонних данных.

Примеры

Изменение параметров обертки сторонних данных dbi: добавление параметра foo, удаление bar:

ALTER FOREIGN DATA WRAPPER dbi OPTIONS (ADD foo '1', DROP 'bar');

Установление для обертки сторонних данных dbi новой функции проверки bob.myvalidator:

ALTER FOREIGN DATA WRAPPER dbi VALIDATOR bob.myvalidator;

Совместимость

Команда ALTER FOREIGN DATA WRAPPER соответствует стандарту ISO/IEC 9075-9 (SQL/MED), за исключением указаний HANDLER, VALIDATOR, OWNER TO и RENAME, являющихся расширениями.

См. также

CREATE FOREIGN DATA WRAPPER, DROP FOREIGN DATA WRAPPER

ALTER FOREIGN TABLE

ALTER FOREIGN TABLE — изменить определение сторонней таблицы

Синтаксис

ALTER FOREIGN TABLE [ IF EXISTS ] [ ONLY ] имя [ * ]
    действие [, ... ]
ALTER FOREIGN TABLE [ IF EXISTS ] [ ONLY ] имя [ * ]
    RENAME [ COLUMN ] имя_столбца TO новое_имя_столбца
ALTER FOREIGN TABLE [ IF EXISTS ] имя
    RENAME TO новое_имя
ALTER FOREIGN TABLE [ IF EXISTS ] имя
    SET SCHEMA новая_схема

Где действие может быть следующим:

    ADD [ COLUMN ] имя_столбца тип_данных [ COLLATE правило_сортировки ] [ ограничение_столбца [ ... ] ]
    DROP [ COLUMN ] [ IF EXISTS ] имя_столбца [ RESTRICT | CASCADE ]
    ALTER [ COLUMN ] имя_столбца [ SET DATA ] TYPE тип_данных [ COLLATE правило_сортировки ]
    ALTER [ COLUMN ] имя_столбца SET DEFAULT выражение
    ALTER [ COLUMN ] имя_столбца DROP DEFAULT
    ALTER [ COLUMN ] имя_столбца { SET | DROP } NOT NULL
    ALTER [ COLUMN ] имя_столбца SET STATISTICS integer
    ALTER [ COLUMN ] имя_столбца SET ( атрибут = значение [, ... ] )
    ALTER [ COLUMN ] имя_столбца RESET ( атрибут [, ... ] )
    ALTER [ COLUMN ] имя_столбца SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
    ALTER [ COLUMN ] имя_столбца OPTIONS ( [ ADD | SET | DROP ] параметр ['значение'] [, ... ])
    ADD ограничение_таблицы [ NOT VALID ]
    VALIDATE CONSTRAINT имя_ограничения
    DROP CONSTRAINT [ IF EXISTS ]  имя_ограничения [ RESTRICT | CASCADE ]
    DISABLE TRIGGER [ имя_триггера | ALL | USER ]
    ENABLE TRIGGER [ имя_триггера | ALL | USER ]
    ENABLE REPLICA TRIGGER имя_триггера
    ENABLE ALWAYS TRIGGER имя_триггера
    SET WITHOUT OIDS
    INHERIT таблица_родитель
    NO INHERIT таблица_родитель
    OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
    OPTIONS ( [ ADD | SET | DROP ] параметр ['значение'] [, ... ])

Описание

Команда ALTER FOREIGN TABLE изменяет определение существующей сторонней таблицы. Существует несколько форм этой команды:

ADD COLUMN

Эта форма добавляет новый столбец в стороннюю таблицу, используя тот же синтаксис, что и CREATE FOREIGN TABLE. В отличие от случая, когда столбец добавляется в обычную таблицу, при данной операции в базовом хранилище ничего не происходит: это действие просто объявляет, что некоторый новый столбец теперь доступен через стороннюю таблицу.

DROP COLUMN [ IF EXISTS ]

Эта форма удаляет столбец из сторонней таблицы. Нужно написать указание CASCADE, если что-то вне таблицы зависит от столбца (например, представление). Если указано IF EXISTS и столбец не существует, ошибка не происходит, вместо этого будет выдано замечание.

SET DATA TYPE

Эта форма изменяет тип столбца сторонней таблицы. Опять же, это не оказывает никакого влияния на базовое хранилище: это действие просто изменяет тип, который, по мнению QHB, имеет этот столбец.

SET/DROP DEFAULT

Эти формы устанавливают или удаляют значение по умолчанию для столбца. Значения по умолчанию применяются только в последующих командах INSERT или UPDATE; они не вызывают изменения уже существующих строк в таблице.

SET/DROP NOT NULL

Устанавливает, будет ли столбец принимать значения NULL или нет.

SET STATISTICS

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

SET ( атрибут = значение [, ... ] )
RESET ( атрибут [, ... ] )

Эта форма устанавливает или сбрасывает значения для атрибута. Дополнительную информацию см. в описании аналогичной формы команды ALTER TABLE.

SET STORAGE

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

ADD ограничение_таблицы [ NOT VALID ]

Эта форма добавляет новое ограничение в стороннюю таблицу, используя тот же синтаксис, что и CREATE FOREIGN TABLE. В настоящее время поддерживаются только ограничения CHECK.

В отличие от случая, когда ограничение добавляется в обычную таблицу, ограничение сторонней таблицы никак не проверяется; скорее, это действие просто объявляет, что некоторое новое условие должно выполняться для всех строк в сторонней таблице. (См. описание в разделе CREATE FOREIGN TABLE) Если ограничение помечено как NOT VALID, то предполагается, что оно не поддерживается, а записывается для возможного использования в будущем.

VALIDATE CONSTRAINT

Эта форма помечает ограничение, которое ранее было помечено NOT VALID, как проверенное. Никаких действий для проверки ограничения не предпринимается, но будущие запросы будут предполагать, что оно поддерживается.

DROP CONSTRAINT [ IF EXISTS ]

Эта форма удаляет указанное ограничение для сторонней таблицы. Если указано IF EXISTS и ограничение не существует, это не считается ошибкой. В этом случае выдается только замечание.

DISABLE/ENABLE [ REPLICA | ALWAYS ] TRIGGER

Эти формы настраивают срабатывание триггера (или триггеров), принадлежащего сторонней таблице. Дополнительную информацию см. в описании аналогичной формы команды ALTER TABLE.

SET WITHOUT OIDS

Синтаксис обратной совместимости для удаления системного столбца oid. Так как добавить системные столбцы oid теперь невозможно, это указание не действует.

INHERIT таблица_родитель

Эта форма добавляет целевую стороннюю таблицу в качестве нового дочернего элемента указанной родительской таблицы. Дополнительную информацию см. в описании аналогичной формы команды ALTER TABLE.

NO INHERIT таблица_родитель

Эта форма удаляет целевую стороннюю таблицу из списка дочерних элементов указанной родительской таблицы.

OWNER

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

OPTIONS ( [ ADD | SET | DROP ] параметр ['значение'] [, ... ] )

Изменяет параметры для сторонней таблицы или одного из ее столбцов. ADD, SET и DROP указывают на действие, которое необходимо выполнить. Если явно не указана никакая операция, по умолчанию подразумевается ADD. Повторяющиеся имена параметров не допускаются (хотя иметь одно и то же имя для параметра таблицы и параметра столбца нормально). Имена и значения параметров также проверяются с помощью библиотеки обертки сторонних данных.

RENAME

Формы RENAME изменяют имя сторонней таблицы или имя ее столбца.

SET SCHEMA

Эта форма перемещает стороннюю таблицу в другую схему.

Все действия, кроме RENAME и SET SCHEMA, могут быть объединены в один список и выполнены одновременно. Например, в одной команде можно добавить несколько столбцов и/или изменить тип нескольких столбцов.

Если команда написана в виде ALTER FOREIGN TABLE IF EXISTS ... и сторонняя таблица не существует, это не считается ошибкой. В этом случае будет выдано замечание.

Чтобы использовать команду ALTER FOREIGN TABLE, нужно быть владельцем соответствующей сторонней таблицы. Чтобы изменить схему сторонней таблицы, необходимо помимо этого иметь право CREATE в новой схеме. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме сторонней таблицы. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания сторонней таблицы. Однако суперпользователь всё равно может сменить владельца любой сторонней таблицы.) Чтобы добавить столбец или изменить тип столбца, помимо этого необходимо иметь право USAGE для его типа данных.

Параметры

имя

Имя существующей сторонней таблицы (может быть дополнено схемой), подлежащей изменению. Если перед именем таблицы указывается ONLY, изменяется только эта таблица. Если ONLY не указано, будут изменены таблица и все ее потомки (если таковые имеются). После имени таблицы можно также добавить необязательное указание *, чтобы явно обозначить, что изменению подлежат все дочерние таблицы.

имя_столбца

Имя нового или существующего столбца.

новое_имя_столбца

Новое имя существующего столбца.

новое_имя

Новое имя таблицы.

тип_данных

Тип данных нового столбца или новый тип данных для существующего столбца.

ограничение_таблицы

Новое ограничение уровня таблицы для сторонней таблицы.

имя_ограничения

Имя существующего ограничения, подлежащего удалению.

CASCADE

Автоматически удалять объекты, зависящие от удаляемого столбца или ограничения (например, ссылающиеся на столбец представления), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

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

имя_триггера

Имя одного триггера, который нужно выключить или включить.

ALL

Выключает или включает все триггеры, принадлежащие сторонней таблице. (Если какие-либо из триггеров являются внутрисистемными, то потребуются права суперпользователя. Сама система не добавляет такие триггеры в сторонние таблицы, но дополнительный код может это сделать.)

USER

Выключает или включает все триггеры, принадлежащие сторонней таблице, кроме внутрисистемных

таблица_родитель

Родительская таблица, с которой будет установлена или разорвана связь у этой сторонней таблицы.

новый_владелец

Имя пользователя, который станет новым владельцем таблицы.

новая_схема

Имя схемы, в которую будет перемещена таблица.

Примечания

Ключевое слово COLUMN не несет смысловой нагрузки, и его можно опустить.

Согласованность со сторонним сервером при добавлении или удалении столбца с помощью функции ADD COLUMN/DROP COLUMN, или добавлении ограничений NOT NULL или CHECK, или изменении типа данных SET DATA TYPE не проверяется. Ответственность за то, чтобы определение таблицы соответствовало удаленной стороне, несет пользователь.

Более полное описание допустимых параметров см. в разделе CREATE FOREIGN TABLE.

Примеры

Установление ограничения NOT NULL для столбца:

ALTER FOREIGN TABLE distributors ALTER COLUMN street SET NOT NULL;

Изменение параметров сторонней таблицы:

ALTER FOREIGN TABLE myschema.distributors
	OPTIONS (ADD opt1 'value', SET opt2 'value2', DROP opt3 'value3');

Совместимость

Формы ADD, DROP и SET DATA TYPE соответствуют стандарту SQL. Другие формы являются расширениями QHB. Кроме того, возможность указать в одной команде ALTER FOREIGN TABLE несколько операций также является расширением.

Команда ALTER FOREIGN TABLE DROP COLUMN позволяет удалить единственный столбец сторонней таблицы и оставить таблицу без столбцов. Это является расширением стандарта SQL, который не допускает существование сторонних таблиц без столбцов.

См. также

CREATE FOREIGN TABLE, DROP FOREIGN TABLE

ALTER FUNCTION

ALTER FUNCTION — изменить определение функции

Синтаксис

ALTER FUNCTION имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    действие [ ... ] [ RESTRICT ]
ALTER FUNCTION имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    RENAME TO новое_имя
ALTER FUNCTION имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER FUNCTION имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    SET SCHEMA новая_схема
ALTER FUNCTION имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    DEPENDS ON EXTENSION имя_расширения

Где действие может быть следующим:

    CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
    IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF
    [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
    PARALLEL { UNSAFE | RESTRICTED | SAFE }
    COST стоимость_выполнения
    ROWS строк_в_результате
    SUPPORT вспомогательная_функция
    SET параметр_конфигурации { TO | = } { значение | DEFAULT }
    SET параметр_конфигурации FROM CURRENT
    RESET параметр_конфигурации
    RESET ALL

Описание

Команда ALTER FUNCTION изменяет определение функции.

Чтобы использовать команду ALTER FUNCTION, нужно быть владельцем соответствующей функции. Чтобы изменить схему функции, необходимо помимо этого иметь право CREATE в новой схеме. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме функции. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания функции. Однако суперпользователь всё равно может сменить владельца любой функции.)

Параметры

имя

Имя существующей функции (может быть дополнено схемой). Если список аргументов не указан, имя должно быть уникальным в своей схеме.

режим_аргумента

Режим аргумента: IN, OUT, INOUT или VARIADIC. По умолчанию равно IN. Обратите внимание, что ALTER FUNCTION фактически не обращает никакого внимания на аргументы OUT, так как для определения идентичности функции нужны только входные аргументы. Поэтому достаточно перечислить аргументы IN, INOUT и VARIADIC.

имя_аргумента

Название аргумента. Обратите внимание, что ALTER FUNCTION фактически не обращает никакого внимания на имена аргументов, так как для определения идентичности функции необходимы только типы данных аргументов.

тип_аргумента

Типы данных аргументов функции (могут быть дополнены схемой), если таковые имеются.

новое_имя

Новое имя функции.

новый_владелец

Новый владелец функции. Обратите внимание, что если функция помечена как SECURITY DEFINER, в дальнейшем она будет выполняться от имени нового владельца.

новая_схема

Новая схема функции.

имя_расширения

Имя расширения, от которого должна зависеть функция.

CALLED ON NULL INPUT
RETURNS NULL ON NULL INPUT
STRICT

CALLED ON NULL INPUT изменяет функцию таким образом, чтобы она вызывалась, когда некоторые или все ее аргументы равны NULL. RETURNS NULL ON NULL INPUT или STRICT изменяет функцию таким образом, чтобы она не вызывалась, если какой-либо из ее аргументов равен NULL, а вместо этого автоматически предполагался результат NULL. Дополнительную информацию см. в разделе CREATE FUNCTION.

IMMUTABLE
STABLE
VOLATILE

Устанавливает заданный вариант изменчивости функции. Дополнительную информацию см. в разделе CREATE FUNCTION.

[EXTERNAL] SECURITY INVOKER
[EXTERNAL] SECURITY DEFINER

Устанавливает, является ли функция определяющей контекст безопасности. Ключевое слово EXTERNAL игнорируется для соответствия стандарту SQL. Дополнительную информацию об этой возможности см. в разделе CREATE FUNCTION.

PARALLEL

Устанавливает, будет ли функция считаться безопасной для распараллеливания. Дополнительную информацию см. в разделе CREATE FUNCTION.

LEAKPROOF

Устанавливает, будет ли функция рассматриваться как герметичная. Дополнительную информацию об этой возможности см. в разделе CREATE FUNCTION.

COST стоимость_выполнения

Изменяет ориентировочную стоимость выполнения функции. Дополнительную информацию см. в разделе CREATE FUNCTION.

ROWS строк_в_результате

Изменяет ориентировочное число строк в результате функции, возвращающей множество. Дополнительную информацию см. в разделе CREATE FUNCTION.

SUPPORT вспомогательная_функция

Устанавливает или меняет вспомогательную функцию для планировщика, которая будет использоваться с этой функцией. Дополнительную информацию см. в разделе Информация по оптимизации функций. Чтобы использовать эту функцию, нужно быть суперпользователем.

Имя новой вспомогательной функции является обязательным, поэтому данное указание нельзя использовать для полного удаления вспомогательной функции. Если понадобится удаление, используйте CREATE OR REPLACE FUNCTION.

параметр_конфигурации
значение

Добавляет или изменяет установку параметра конфигурации, выполняемую при вызове функции. Если задано значение DEFAULT или равнозначное указание RESET, локальное переопределение для функции удаляется, вследствие чего функция выполняется со значением, установленным в окружении. Для удаления всех установок параметров функции укажите RESET ALL. SET FROM CURRENT устанавливает для последующих вызовов функции значение параметра, действующее в момент выполнения ALTER FUNCTION.

Дополнительную информацию о допустимых именах и значениях параметров см. в разделе SET и главе Конфигурация сервера.

RESTRICT

Игнорируется для соответствия стандарту SQL.

Примеры

Переименование функции sqrt для типа integer в square_root:

ALTER FUNCTION sqrt(integer) RENAME TO square_root;

Смена владельца функции sqrt для типа integer на joe:

ALTER FUNCTION sqrt(integer) OWNER TO joe;

Смена схемы функции sqrt для типа integer на maths:

ALTER FUNCTION sqrt(integer) SET SCHEMA maths;

Обозначение функции sqrt для типа integer как зависимой от расширения mathlib:

ALTER FUNCTION sqrt(integer) DEPENDS ON EXTENSION mathlib;

Изменение пути поиска, который устанавливается автоматически для функции:

ALTER FUNCTION check_password(text) SET search_path = admin, pg_temp;

Отмена автоматического определения search_path для функции:

ALTER FUNCTION check_password(text) RESET search_path;

Теперь функция будет выполняться с тем путем, который задан в момент вызова.

Совместимость

Эта команда частично совместима с командой ALTER FUNCTION в стандарте SQL. Стандарт позволяет менять больше свойств функции, но не предоставляет возможности переименовывать функции, переключать контекст безопасности, связывать с функциями значения параметров конфигурации, а также менять владельца, схему и тип изменчивости функции. Стандарт SQL также требует обязательного наличия слова RESTRICT, тогда как в QHB оно необязательно.

См. также

CREATE FUNCTION, DROP FUNCTION, ALTER PROCEDURE, ALTER ROUTINE

ALTER GROUP

ALTER GROUP — изменить имя роли или членство

Синтаксис

ALTER GROUP указание_роли ADD USER имя_пользователя [, ... ]
ALTER GROUP указание_роли DROP USER имя_пользователя [, ... ]

Где указание_роли может быть:

    имя_роли
  | CURRENT_USER
  | SESSION_USER

ALTER GROUP имя_группы RENAME TO новое_имя

Описание

Команда ALTER GROUP изменяет атрибуты группы пользователей. Это устаревшая команда, хотя и всё еще поддерживаемая для обратной совместимости, так как группы (и пользователи тоже) были заменены более общей концепцией ролей.

Первые два варианта команды добавляют пользователей в группу или удаляют их из группы. (При этом любая роль может фигурировать либо как «пользователь», либо как «группа».) Эти варианты практически эквивалентны командам разрешающим/запрещающим членство в роли «группа», поэтому вместо них рекомендуется использовать GRANT и REVOKE.

Третий вариант команды изменяет названия группы. Он полностью аналогичен команде ALTER ROLE, выполняющей переименование роли.

Параметры

имя_группы

Имя группы (роли), подлежащей изменению.

имя_пользователя

Пользователи (роли), которые должны быть добавлены в группу или удалены из нее. Пользователи уже должны существовать: ALTER GROUP не создает и не удаляет пользователей.

новое_имя

Новое имя группы.

Примеры

Добавление пользователей в группу:

ALTER GROUP staff ADD USER karl, john;

Удаление пользователей из группы:

ALTER GROUP workers DROP USER beth;

Совместимость

В стандарте SQL нет команды ALTER GROUP.

См. также

GRANT, REVOKE, ALTER ROLE

ALTER INDEX

ALTER INDEX — изменить определение индекса

Синтаксис

ALTER INDEX [ IF EXISTS ] имя RENAME TO новое_имя
ALTER INDEX [ IF EXISTS ] имя SET TABLESPACE имя_табличного_пространства
ALTER INDEX имя ATTACH PARTITION имя_индекса
ALTER INDEX имя DEPENDS ON EXTENSION имя_расширения
ALTER INDEX [ IF EXISTS ] имя SET ( параметр_хранения = значение [, ... ] )
ALTER INDEX [ IF EXISTS ] имя RESET ( параметр_хранения [, ... ] )
ALTER INDEX [ IF EXISTS ] имя ALTER [ COLUMN ] номер_столбца
    SET STATISTICS целое
ALTER INDEX ALL IN TABLESPACE имя [ OWNED BY имя_роли [, ... ] ]
    SET TABLESPACE новое_табличное_пространство [ NOWAIT ]

Описание

Команда ALTER INDEX изменяет определение существующего индекса. Она имеет несколько форм, которые описаны ниже. Обратите внимание, что требуемый уровень блокировки может отличаться для каждой формы. Если явно не указано, то по умолчанию запрашивается блокировка ACCESS EXCLUSIVE. Если перечислено несколько подкоманд, то запрашивается самая строгая блокировка из требуемых подкомандами.

RENAME

Форма RENAME изменяет имя индекса. Если индекс связан с ограничением таблицы (UNIQUE, PRIMARY KEY или EXCLUDE), то ограничение также переименовывается. Это не оказывает никакого влияния на сохраненные данные.

Переименование индекса требует блокировки SHARE UPDATE EXCLUSIVE.

SET TABLESPACE

Эта форма изменяет табличное пространство индекса на указанное и перемещает файл (или файлы) данных, связанный с индексом, в новое табличное пространство. Чтобы изменить табличное пространство индекса, текущий пользователь должен быть владельцем индекса и иметь право CREATE в новом табличном пространстве. Все индексы в текущей базе данных в табличном пространстве можно переместить с помощью формы ALL IN TABLESPACE. При этом индексы блокируются для перемещения, а затем перемещается каждый индекс. Эта форма также поддерживает OWNED BY, с которым будут перемещены только индексы, принадлежащие заданным ролям. Если указана опция NOWAIT, то команда завершится ошибкой, если не сможет сразу получить все необходимые блокировки. Обратите внимание, что системные каталоги этой командой перемещены не будут; при необходимости используйте вызов ALTER DATABASE или явный вызов ALTER INDEX. См. также CREATE TABLESPACE.

ATTACH PARTITION

Эта форма присоединяет указанный индекс к изменяемому. Указанный индекс должен находиться в партиции таблицы, содержащей изменяемый индекс, и иметь эквивалентное определение. Присоединенный индекс не может быть удален сам по себе и будет автоматически удален при удалении родительского индекса.

DEPENDS ON EXTENSION имя_расширения NO DEPENDS ON EXTENSION имя_расширения

Эта форма помечает индекс как зависящий или более не зависящий от данного расширения, если указано NO. Индекс, помеченный как зависящий от расширения, при его удалении также будет автоматически удален.

SET ( параметр_хранения = значение [, ... ] )

Эта форма изменяет один или несколько специфичных для индексного метода параметров хранения. Дополнительную информацию о доступных параметрах см. в разделе CREATE INDEX. Обратите внимание, что содержимое индекса не будет изменено немедленно; в зависимости от параметров, для получения желаемого эффекта может потребоваться перестроить индекс командой REINDEX.

RESET ( параметр_хранения [, ... ] )

Эта форма сбрасывает один или несколько специфичных для индексного метода параметров хранения к значениям по умолчанию. Как и в случае с SET, для полного обновления индекса может потребоваться выполнить REINDEX.

ALTER [ COLUMN ] номер_столбца SET STATISTICS целое

Эта форма задает ориентир сбора статистики по столбцу для последующих операций ANALYZE, хотя ее можно использовать только для индексируемых столбцов, определенных как выражение. Поскольку выражения не имеют уникального имени, на них ссылаются, используя порядковый номер столбца в индексе. Ориентир можно задать в диапазоне от 0 до 10000; кроме того, при установке -1 применяется системное значение по умолчанию (default_statistics_target). Дополнительную информацию об использовании статистики планировщиком запросов QHB см. в разделе Статистика, используемая планировщиком.

Параметры

IF EXISTS

Не считать ошибкой, если индекс не существует. В этом случае должно быть выдано замечание.

номер_столбца

Число, указывающее на порядковый номер столбца в индексе (слева направо).

имя

Имя существующего индекса, подлежащего изменению (может быть дополнено схемой).

новое_имя

Новое имя индекса.

имя_табличного_пространства

Табличное пространство, в которое будет перемещен индекс.

имя_расширения

Имя расширения, от которого будет зависеть индекс.

параметр_хранения

Имя специфичного для индексного метода параметра хранения.

значение

Новое значение специфичного для индексного метода параметра хранения. Это может быть число или строка, в зависимости от параметра.

Примечания

Эти операции также можно выполнить с помощью команды ALTER TABLE. ALTER INDEX фактически является просто синонимом форм команды ALTER TABLE, которые применяются к индексам.

Ранее существовала форма ALTER INDEX OWNER, но теперь она игнорируется (с предупреждением). Владелец индекса должен быть и владельцем таблицы. Смена владельца таблицы автоматически меняет владельца индекса.

Изменение любой части индекса системного каталога не допускается.

Примеры

Переименование существующего индекса:

ALTER INDEX distributors RENAME TO suppliers;

Перемещение индекса в другое табличное пространство:

ALTER INDEX distributors SET TABLESPACE fasttablespace;

Изменение коэффициента заполнения индекса (предполагается, что это поддерживает индексный метод):

ALTER INDEX distributors SET (fillfactor = 75);
REINDEX INDEX distributors;

Установка ориентира сбора статистики для индекса по выражению:

CREATE INDEX coord_idx ON measured (x, y, (z + t));
ALTER INDEX coord_idx ALTER COLUMN 3 SET STATISTICS 1000;

Совместимость

Команда ALTER INDEX является расширением QHB.

См. также

CREATE INDEX, REINDEX

ALTER LANGUAGE

ALTER LANGUAGE — изменить определение процедурного языка

Синтаксис

ALTER [ PROCEDURAL ] LANGUAGE имя RENAME TO новое_имя
ALTER [ PROCEDURAL ] LANGUAGE имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }

Описание

Команда ALTER LANGUAGE изменяет определение процедурного языка. Единственная ее функция — это переименовать язык или назначить нового владельца. Чтобы использовать команду ALTER LANGUAGE, нужно быть суперпользователем или владельцем соответствующего языка.

Параметры

имя

Название языка

новое_имя

Новое название языка

новый_владелец

Новый владелец языка

Совместимость

В стандарте SQL нет команды ALTER LANGUAGE.

См. также

CREATE LANGUAGE, DROP LANGUAGE

ALTER LARGE OBJECT

ALTER LARGE OBJECT — изменить определение большого объекта

Синтаксис

ALTER LARGE OBJECT oid_большого_объекта OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }

Описание

Команда ALTER LARGE OBJECT изменяет определение большого объекта.

Чтобы использовать команду ALTER LARGE OBJECT, нужно быть владельцем соответствующего большого объекта. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца. (Однако суперпользователь всё равно может сменить владельца любого большого объекта.) В настоящее время единственная функция данной команды — это назначение нового владельца, поэтому всегда действуют оба ограничения.

Параметры

oid_большого_объекта

OID большого объекта, подлежащего изменению

новый_владелец

Новый владелец большого объекта

Совместимость

В стандарте SQL нет команды ALTER LARGE OBJECT.

См. также

Большие объекты

ALTER MATERIALIZED VIEW

ALTER MATERIALIZED VIEW — изменить определение материализованного представления

Синтаксис

ALTER MATERIALIZED VIEW [ IF EXISTS ] имя
    действие [, ... ]
ALTER MATERIALIZED VIEW имя
    DEPENDS ON EXTENSION имя_расширения
ALTER MATERIALIZED VIEW [ IF EXISTS ] имя
    RENAME [ COLUMN ] имя_столбца TO новое_имя_столбца
ALTER MATERIALIZED VIEW [ IF EXISTS ] имя
    RENAME TO новое_имя
ALTER MATERIALIZED VIEW [ IF EXISTS ] имя
    SET SCHEMA новая_схема
ALTER MATERIALIZED VIEW ALL IN TABLESPACE имя [ OWNED BY имя_роли [, ... ] ]
    SET TABLESPACE новое_табличное_пространство [ NOWAIT ]

Где действие может быть следующим:

    ALTER [ COLUMN ] имя_столбца SET STATISTICS integer
    ALTER [ COLUMN ] имя_столбца SET ( атрибут = значение [, ... ] )
    ALTER [ COLUMN ] имя_столбца RESET ( атрибут [, ... ] )
    ALTER [ COLUMN ] имя_столбца SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
    CLUSTER ON имя_индекса
    SET WITHOUT CLUSTER
    SET ( параметр_хранения = значение [, ... ] )
    RESET ( параметр_хранения [, ... ] )
    OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }

Описание

Команда ALTER MATERIALIZED VIEW изменяет различные расширенные свойства существующего материализованного представления.

Чтобы использовать команду ALTER MATERIALIZED VIEW, нужно быть владельцем соответствующего материализованного представления. Чтобы изменить схему материализованного представления, необходимо помимо этого иметь право CREATE в новой схеме. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли, и эта роль должна иметь право CREATE в схеме материализованного представления. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания материализованного представления. Однако суперпользователь всё равно может сменить владельца любого материализованного представления.)

Форма DEPENDS ON EXTENSION помечает материализованное представление как зависящее от расширения, поэтому материализованное представление будет автоматически удалено при удалении расширения.

Формы и указания, доступные для команды ALTER MATERIALIZED VIEW, являются подмножеством тех, что доступны для команды ALTER TABLE, и при использовании для материализованных представлений имеют то же значение. Дополнительную информацию см. в описании ALTER TABLE.

Параметры

имя

Имя существующего материализованного представления (может быть дополнено схемой).

имя_столбца

Имя нового или существующего столбца.

имя_расширения

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

новое_имя_столбца

Новое имя существующего столбца.

новый_владелец

Имя пользователя, который станет новым владельцем материализованного представления.

новое_имя

Новое имя материализованного представления.

новая_схема

Новая схема материализованного представления.

Примеры

Переименование материализованного представления foo в bar:

ALTER MATERIALIZED VIEW foo RENAME TO bar;

Совместимость

Команда ALTER MATERIALIZED VIEW является расширением QHB.

См. также

CREATE MATERIALIZED VIEW, DROP MATERIALIZED VIEW, REFRESH MATERIALIZED VIEW

ALTER OPERATOR CLASS

ALTER OPERATOR CLASS — изменить определение класса операторов

Синтаксис

ALTER OPERATOR CLASS имя USING индексный_метод
    RENAME TO новое_имя

ALTER OPERATOR CLASS имя USING индексный_метод
    OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }

ALTER OPERATOR CLASS имя USING индексный_метод
    SET SCHEMA новая_схема

Описание

Команда ALTER OPERATOR CLASS изменяет определение класса операторов.

Чтобы использовать команду ALTER OPERATOR CLASS, нужно быть владельцем соответствующего класса оператора. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме класса оператора. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания класса оператора. Однако суперпользователь всё равно может сменить владельца любого класса оператора.)

Параметры

имя

Имя существующего класса операторов (может быть дополнено схемой).

индексный_метод

Имя индексного метода, для которого предназначен этот класс операторов.

новое_имя

Новое имя класса операторов.

новый_владелец

Новый владелец класса операторов.

новая_схема

Новая схема класса операторов.

Совместимость

В стандарте SQL нет команды ALTER OPERATOR CLASS.

См. также

CREATE OPERATOR CLASS, DROP OPERATOR CLASS, ALTER OPERATOR FAMILY

ALTER OPERATOR FAMILY

ALTER OPERATOR FAMILY — изменить определение семейства операторов

Синтаксис

ALTER OPERATOR FAMILY имя USING индексный_метод ADD
  {  OPERATOR номер_стратегии имя_оператора ( тип_операнда, тип_операнда )
              [ FOR SEARCH | FOR ORDER BY семейство_сортировки ]
   | FUNCTION номер_вспомогательной_функции [ ( тип_операнда [ , тип_операнда ] ) ]
              имя_функции [ ( тип_аргумента [, ...] ) ]
  } [, ... ]

ALTER OPERATOR FAMILY имя USING индексный_метод DROP
  {  OPERATOR номер_стратегии ( тип_операнда [ , тип_операнда ] )
   | FUNCTION номер_вспомогательной_функции ( тип_операнда [ , тип_операнда ] )
  } [, ... ]

ALTER OPERATOR FAMILY имя USING индексный_метод
    RENAME TO новое_имя

ALTER OPERATOR FAMILY имя USING индексный_метод
    OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }

ALTER OPERATOR FAMILY имя USING индексный_метод
    SET SCHEMA новая_схема

Описание

Команда ALTER OPERATOR FAMILY изменяет определение семейства операторов. Можно добавлять в семейство операторы и вспомогательные функции, удалять их оттуда, изменять имя или владельца семейства операторов.

Когда операторы и вспомогательные функции добавляются в семейство операторов командой ALTER OPERATOR FAMILY, они не становятся частью какого-либо конкретного класса операторов в семействе, а просто считаются «слабосвязанными» с семейством. Это свидетельствует о том, что данные операторы и функции семантически совместимы с семейством, но не требуются для правильного функционирования какого-либо конкретного индекса. (Операторы и функции, которые действительно требуются для этого, должны быть объявлены как часть класса операторов, см. CREATE OPERATOR CLASS.) QHB позволяет в любое время удалить из семейства слабосвязанные члены, но нельзя удалить члены класса операторов без удаления всего класса и всех зависящих от него индексов. Как правило, в классы операторов включаются операторы и функции одного типа данных, поскольку они необходимы для поддержки индекса для этого конкретного типа данных, тогда как операторы и функции перекрестных типов данных становятся слабосвязанными членами семейства.

Чтобы использовать команду ALTER OPERATOR FAMILY, нужно быть суперпользователем. (Эти ограничения направлены на то, чтобы ошибочное определение семейства операторов не вызвало нарушение или даже сбой в работе сервера.)

В настоящее время ALTER OPERATOR FAMILY не проверяет, включает ли определение семейства операторов все операторы и функции, требуемые для индексного метода, и образуют ли они целостный набор. Ответственность за определение допустимого семейства операторов несет пользователь.

Дополнительную информацию см. в разделе Интерфейсные расширения для индексов.

Параметры

имя

Имя существующего семейства операторов (может быть дополнено схемой).

индексный_метод

Имя индексного метода, для которого предназначено это семейство операторов.

номер_стратегии

Номер стратегии индексного метода для оператора, связанного с семейством операторов.

имя_оператора

Имя оператора, связанного с семейством операторов (может быть дополнено схемой).

тип_операнда

В предложении OPERATOR указываются типы данных операнда оператора или NONE, если это левый или правый унарный оператор. В отличие от сопоставимого синтаксиса в CREATE OPERATOR CLASS, типы данных операнда всегда должны быть указаны.

В предложении ADD FUNCTION указываются типы данных операнда оператора, который должна поддерживать функция, если они отличаются от входных типов данных функции. Для функций сравнения B-деревьев и хеш-функций указывать тип_операнда необязательно, так как их входные типы данных всегда будут верными. Однако для вспомогательных функций сортировки B-деревьев и всех функций в классах операторов GiST, SP-GiST и GIN необходимо указать типы операндов, с которыми будут использоваться эти функции.

В предложении DROP FUNCTION должны быть указаны типы операндов, для поддержки которых предназначена функция.

семейство_сортировки

Имя существующего семейства операторов btree, описывающего порядок сортировки, связанный с оператором сортировки (может быть дополнено схемой).

Если не указано ни FOR SEARCH (для поиска), ни FOR ORDER BY (для сортировки), то значение по умолчанию равно FOR SEARCH.

номер_вспомогательной_функции

Номер вспомогательной функции индексного метода для функции, связанной с данным семейством операторов

имя_функции

Имя функции, являющейся вспомогательной функцией индексного метода для данного семейства операторов (может быть дополнено схемой). Если список аргументов отсутствует, имя функции должно быть уникальным в ее схеме.

тип_аргумента

Тип(ы) данных параметра функции.

новое_имя

Новое имя семейства операторов.

новый_владелец

Новый владелец семейства операторов.

новая_схема

Новая схема для семейства операторов.

Предложения OPERATOR и FUNCTION могут указываться в любом порядке.

Примечания

Обратите внимание, что в синтаксисе DROP указывается только «слот» в семействе операторов (по номеру стратегии или вспомогательной функции) и входные типы данных. Имя оператора или функции, занимающей слот, не упоминается. Кроме того, для DROP FUNCTION необходимо указать тип (или типы) входных данных, который должна поддерживать функция; для индексов GiST, SP-GiST и Gin это может не иметь ничего общего с фактическими типами входных аргументов функции.

Поскольку механизм индексирования не проверяет права на доступ к функциям перед их использованием, включение функции или оператора в семейство операторов равносильно предоставлению права на их выполнение. Это обычно не является проблемой для тех видов функций, которые полезны в семействе операторов.

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

Примеры

Следующий пример добавляет вспомогательные функции и операторы смешанных типов данных в семейство операторов, уже содержащее классы операторов B-дерева для типов данных int4 и int2.

ALTER OPERATOR FAMILY integer_ops USING btree ADD

  -- int4 и int2
  OPERATOR 1 < (int4, int2) ,
  OPERATOR 2 <= (int4, int2) ,
  OPERATOR 3 = (int4, int2) ,
  OPERATOR 4 >= (int4, int2) ,
  OPERATOR 5 > (int4, int2) ,
  FUNCTION 1 btint42cmp(int4, int2) ,

  -- int2 и int4
  OPERATOR 1 < (int2, int4) ,
  OPERATOR 2 <= (int2, int4) ,
  OPERATOR 3 = (int2, int4) ,
  OPERATOR 4 >= (int2, int4) ,
  OPERATOR 5 > (int2, int4) ,
  FUNCTION 1 btint24cmp(int2, int4) ;

Удаление этих же элементов:

ALTER OPERATOR FAMILY integer_ops USING btree DROP

  -- int4 vs int2
  OPERATOR 1 (int4, int2) ,
  OPERATOR 2 (int4, int2) ,
  OPERATOR 3 (int4, int2) ,
  OPERATOR 4 (int4, int2) ,
  OPERATOR 5 (int4, int2) ,
  FUNCTION 1 (int4, int2) ,

  -- int2 vs int4
  OPERATOR 1 (int2, int4) ,
  OPERATOR 2 (int2, int4) ,
  OPERATOR 3 (int2, int4) ,
  OPERATOR 4 (int2, int4) ,
  OPERATOR 5 (int2, int4) ,
  FUNCTION 1 (int2, int4) ;

Совместимость

В стандарте SQL нет команды ALTER OPERATOR FAMILY.

См. также

CREATE OPERATOR FAMILY, DROP OPERATOR FAMILY, CREATE OPERATOR CLASS, ALTER OPERATOR CLASS, DROP OPERATOR CLASS

ALTER OPERATOR

ALTER OPERATOR — изменить определение оператора

Синтаксис

ALTER OPERATOR имя ( { тип_слева | NONE } , { тип_справа | NONE } )
    OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }

ALTER OPERATOR имя ( { тип_слева | NONE } , { тип_справа | NONE } )
    SET SCHEMA новая_схема

ALTER OPERATOR имя ( { тип_слева | NONE } , { тип_справа | NONE } )
    SET ( {  RESTRICT = { процедура_ограничения | NONE }
           | JOIN = { процедура_соединения | NONE }
         } [, ... ] )

Описание

Команда ALTER OPERATOR изменяет определение оператора.

Чтобы использовать команду ALTER OPERATOR, нужно быть владельцем соответствующего оператора. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме оператора. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания оператора. Однако суперпользователь всё равно может сменить владельца любого оператора.)

Параметры

имя

Имя существующего оператора (может быть дополнено схемой).

тип_слева

Тип данных левого операнда оператора; укажите NONE, если у оператора нет левого операнда.

тип_справа

Тип данных правого операнда оператора; укажите NONE, если у оператора нет правого операнда.

новый_владелец

Новый владелец оператора.

новая_схема

Новая схема оператора.

процедура_ограничения

Функция оценки селективности ограничения для этого оператора; чтобы удалить существующую функцию оценки, укажите NONE.

процедура_соединения

Функция оценки селективности соединения для этого оператора; чтобы удалить существующую функцию оценки, укажите NONE.

Примеры

Смена владельца нестандартного оператора a @@ b для типа text:

ALTER OPERATOR @@ (text, text) OWNER TO joe;

Смена функций оценки избирательности ограничения и соединения для нестандартного оператора a && b для типа int[]:

ALTER OPERATOR && (_int4, _int4) SET (RESTRICT = _int_contsel, JOIN = _int_contjoinsel);

Совместимость

В стандарте SQL нет команды ALTER OPERATOR.

См. также

CREATE OPERATOR, DROP OPERATOR

ALTER POLICY

ALTER POLICY — изменить определение политики защиты на уровне строк

Синтаксис

ALTER POLICY имя ON имя_таблицы RENAME TO новое_имя

ALTER POLICY имя ON имя_таблицы
    [ TO { имя_роли | PUBLIC | CURRENT_USER | SESSION_USER } [, ...] ]
    [ USING ( выражение_использования ) ]
    [ WITH CHECK ( выражение_проверки ) ]

Описание

Команда ALTER POLICY изменяет определение существующей политики защиты на уровне строк. Обратите внимание, что ALTER POLICY разрешает изменить только набор ролей, к которым применяется политика, а также выражения USING и WITH CHECK. Чтобы изменить другие свойства политики, такие как команда, к которой она применяется, либо параметр разрешительная/ограничительная, политика должна быть удалена и создана заново.

Чтобы использовать команду ALTER POLICY, нужно быть владельцем таблицы, к которой применяется политика.

Во второй форме ALTER POLICY список ролей, выражение_использования и выражение_проверки заменяются независимо, если указаны. Если одно из этих условий опущено, соответствующая часть политики остается неизменной.

Параметры

имя

Имя существующей политики, подлежащей изменению.

имя_таблицы

Имя таблицы, к которой применяется политика (может быть дополнено схемой).

новое_имя

Новое имя для политики.

имя_роли

Роль(и), на которую(ые) действует политика. Одновременно можно указать несколько ролей. Чтобы применить политику ко всем ролям, используйте PUBLIC.

выражение_использования

Выражение USING для политики. Дополнительную информацию см. в разделе CREATE POLICY.

выражение_проверки

Выражение WITH CHECK для политики. Дополнительную информацию см. в разделе CREATE POLICY.

Совместимость

Команда ALTER POLICY является расширением QHB.

См. также

CREATE POLICY, DROP POLICY

ALTER PROCEDURE

ALTER PROCEDURE — изменить определение процедуры

Синтаксис

ALTER PROCEDURE имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    действие [ ... ] [ RESTRICT ]
ALTER PROCEDURE имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    RENAME TO новое_имя
ALTER PROCEDURE имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER PROCEDURE имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    SET SCHEMA новая_схема
ALTER PROCEDURE имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    DEPENDS ON EXTENSION имя_расширения

Где действие может быть следующим:

    [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
    SET параметр_конфигурации { TO | = } { значение | DEFAULT }
    SET параметр_конфигурации FROM CURRENT
    RESET параметр_конфигурации
    RESET ALL

Описание

Команда ALTER PROCEDURE изменяет определение процедуры.

Чтобы использовать команду ALTER PROCEDURE, нужно быть владельцем соответствующей процедуры. Чтобы изменить схему процедуры, необходимо помимо этого иметь право CREATE в новой схеме. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме процедуры. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания процедуры. Однако суперпользователь всё равно может сменить владельца любой процедуры.)

Параметры

имя

Имя существующей процедуры (может быть дополнено схемой). Если не указан список аргументов, имя должно быть уникальным в ее схеме.

режим_аргумента

Режим аргумента: IN или VARIADIC. По умолчанию равно IN.

имя_аргумента

Название аргумента. Обратите внимание, что ALTER PROCEDURE фактически не обращает никакого внимания на имена аргументов, поскольку для определения идентичности процедуры необходимы только типы данных аргументов.

тип_аргумента

Тип (или типы) данных аргументов процедуры (возможно, дополненный схемой), если таковой имеются.

новое_имя

Новое имя процедуры.

новый_владелец

Новый владелец функции. Обратите внимание, что если процедура помечена как SECURITY DEFINER, в дальнейшем она будет выполняться от имени нового владельца.

новая_схема

Новая схема для процедуры.

имя_расширения

Имя расширения, от которого будет зависеть процедура.

[EXTERNAL] SECURITY INVOKER
[EXTERNAL] SECURITY DEFINER

Устанавливает, является ли процедура определяющей контекст безопасности. Ключевое слово EXTERNAL игнорируется для соответствия стандарту SQL. Дополнительную информацию об этой возможности см. в разделе CREATE PROCEDURE.

параметр_конфигурации
значение

Добавляет или изменяет установку параметра конфигурации, выполняемую при вызове процедуры. Если задано значение DEFAULT или равнозначное указание RESET, локальное переопределение для процедуры удаляется и функция выполняется со значением, установленным в окружении. Для удаления всех установок параметров процедуры укажите RESET ALL. SET FROM CURRENT устанавливает для последующих вызовов процедуры значение параметра, действующее в момент выполнения команды ALTER FUNCTION.

Дополнительную информацию о допустимых именах и значениях параметров см. в разделе SET и главе Конфигурация сервера.

RESTRICT

Игнорируется для соответствия стандарту SQL.

Примеры

Переименование процедуры insert_data с двумя аргументами типа integer в insert_record:

ALTER PROCEDURE insert_data(integer, integer) RENAME TO insert_record;

Смена владельца процедуры insert_data с двумя аргументами типа integer на joe:

ALTER PROCEDURE insert_data(integer, integer) OWNER TO joe;

Изменение схемы процедуры insert_data с двумя аргументами типа integer на accounting:

ALTER PROCEDURE insert_data(integer, integer) SET SCHEMA accounting;

Обозначение процедуры insert_data(integer, integer) как зависимой от расширения myext:

ALTER PROCEDURE insert_data(integer, integer) DEPENDS ON EXTENSION myext;

Изменение пути поиска, который устанавливается автоматически для процедуры:

ALTER PROCEDURE check_password(text) SET search_path = admin, pg_temp;

Отмена автоматического определения search_path для процедуры:

ALTER PROCEDURE check_password(text) RESET search_path;

Теперь процедура будет выполняться с тем путем, который задан в момент вызова.

Совместимость

Эта команда частично совместима с командой ALTER PROCEDURE в стандарте SQL. Стандарт позволяет изменять больше свойств процедуры, но не предоставляет возможности переименовать процедуру, сделать процедуру определяющей контекст безопасности, связать с процедурой значения параметров конфигурации или изменить владельца, схему или характеристику изменчивости процедуры. Стандарт также требует обязательного наличия слова RESTRICT, которое является необязательным в QHB.

См. также

CREATE PROCEDURE, DROP PROCEDURE, ALTER FUNCTION, ALTER ROUTINE

ALTER PUBLICATION

ALTER PUBLICATION — изменить определение публикации

Синтаксис

ALTER PUBLICATION имя ADD TABLE [ ONLY ] имя_таблицы [ * ] [, ...]
ALTER PUBLICATION имя SET TABLE [ ONLY ] имя_таблицы [ * ] [, ...]
ALTER PUBLICATION имя DROP TABLE [ ONLY ] имя_таблицы [ * ] [, ...]
ALTER PUBLICATION имя SET ( параметр_публикации [= значение] [, ... ] )
ALTER PUBLICATION имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER PUBLICATION имя RENAME TO новое_имя

Описание

Команда ALTER PUBLICATION может изменять атрибуты публикации.

Первые три варианта команды управляют вхождением таблиц в публикации. Предложение SET TABLE заменяет список таблиц в публикации заданным. Предложения ADD TABLE и DROP TABLE добавляют и удаляют таблицы в публикации. Обратите внимание, что для добавления таблиц в публикацию, на которую уже оформлена подписка, потребуется выполнить действие ALTER SUBSCRIPTION ... REFRESH PUBLICATION на стороне подписчика, чтобы это изменение вступило в силу.

Четвертый указанный в синтаксисе вариант этой команды может изменять все свойства публикации, указанные в разделе CREATE PUBLICATION. Свойства, не упомянутые в команде, сохраняют свои предыдущие значения.

Остальные варианты меняют владельца и название публикации.

Чтобы использовать команду ALTER PUBLICATION, нужно быть владельцем соответствующей публикации. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в базе данных. Кроме того, новым владельцем публикации FOR ALL TABLES должен быть суперпользователь. Однако суперпользователь может сменить владельца публикации, обходя эти ограничения.

Параметры

имя

Название существующей публикации, определение которой подлежит изменению.

имя_таблицы

Имя существующей таблицы. Если перед именем таблицы указывается ONLY, затрагивается только эта таблица. Если ONLY не указано, затрагиваются и эта таблица, и все ее потомки (если таковые имеются). После имени таблицы можно добавить необязательное указание *, чтобы явно обозначить, что должны затрагиваться и все дочерние таблицы.

SET ( параметр_публикации [= значение] [, ... ] )

Это предложение изменяет параметры публикации, первоначально заданные командой CREATE PUBLICATION (более подробную информацию см. в описании этой команды).

новый_владелец

Имя пользователя, который станет новым владельцем публикации.

новое_имя

Новое имя для публикации.

Примеры

Изменение публикации, чтобы публиковались только удаления и изменения:

ALTER PUBLICATION noinsert SET (publish = 'update, delete');

Добавление таблиц в публикацию:

ALTER PUBLICATION mypublication ADD TABLE users, departments;

Совместимость

Команда ALTER PUBLICATION является расширением QHB.

См. также

CREATE PUBLICATION, DROP PUBLICATION, CREATE SUBSCRIPTION, ALTER SUBSCRIPTION

ALTER ROLE

ALTER ROLE — изменить роль в базе данных

Синтаксис

ALTER ROLE указание_роли [ WITH ] параметр [ ... ]

Где параметр может быть:

      SUPERUSER | NOSUPERUSER
    | CREATEDB | NOCREATEDB
    | CREATEROLE | NOCREATEROLE
    | INHERIT | NOINHERIT
    | LOGIN | NOLOGIN
    | REPLICATION | NOREPLICATION
    | BYPASSRLS | NOBYPASSRLS
    | CONNECTION LIMIT предел_подключений
    | [ ENCRYPTED ] PASSWORD 'пароль' | PASSWORD NULL
    | VALID UNTIL 'дата_время'

ALTER ROLE имя RENAME TO новое_имя

ALTER ROLE { указание_роли | ALL } [ IN DATABASE имя\_базы\_данных ] SET параметр_конфигурации { TO | = } { значение | DEFAULT }
ALTER ROLE { указание_роли | ALL } [ IN DATABASE имя\_базы\_данных ] SET параметр_конфигурации FROM CURRENT
ALTER ROLE { указание_роли | ALL } [ IN DATABASE имя\_базы\_данных ] RESET параметр_конфигурации
ALTER ROLE { указание_роли | ALL } [ IN DATABASE имя\_базы\_данных ] RESET ALL

Где указание_роли может быть:

    имя_роли
  | CURRENT_USER
  | SESSION_USER

Описание

Команда ALTER ROLE изменяет атрибуты роли QHB.

Первый указанный в синтаксисе вариант этой команды может изменить большинство атрибутов роли, которые можно задать в CREATE ROLE. (Покрываются все возможные атрибуты, за исключением возможности добавления/удаления членов роли; для этого нужно использовать команды GRANT и REVOKE.) Атрибуты, не упомянутые в команде, сохраняют свои предыдущие параметры. Суперпользователи базы данных могут изменять любые из этих параметров для любой роли. Роли с правом CREATEROLE могут изменять любые из этих параметров, за исключением SUPERUSER, REPLICATION и BYPASSRLS, но только не для ролей суперпользователя и репликации. Обычные пользователи (роли) могут изменить только свой пароль.

Второй вариант меняет название роли. Суперпользователи базы данных могут переименовать любую роль. Роли с правом CREATEROLE могут переименовывать роли, не являющиеся суперпользователями. Текущий пользователь сеанса не может быть переименован. (Если нужно это сделать, подключитесь как другой пользователь.) Так как в паролях с MD5-шифрованием имя роли используется в качестве криптосоли, то при переименовании роли ее пароль очищается, если он был зашифрован MD5.

Остальные варианты изменяют значение по умолчанию конфигурационной переменной, которое будет распространятся на сеансы роли либо для всех баз данных, либо, если указано IN DATABASE, только для сеансов в указанной базе данных. Если вместо имени роли указывается ALL, то значение переменной распространяется на все роли. Использование ALL с IN DATABASE по сути равносильно использованию команды ALTER DATABASE ... SET ....

Когда роль впоследствии запустит новый сеанс, указанное значение станет значением по умолчанию, переопределяя все настройки, присутствующие в qhb.conf или полученные из командной строки QHB. Это происходит только во время входа в систему; выполнение SET ROLE или SET SESSION AUTHORIZATION не вызывает установки новых значений конфигурации. Параметры, заданные для всех баз данных, переопределяются установленными для роли параметрами уровня базы данных. Параметры для конкретных баз данных или ролей могут переопределять только суперпользователи.

Суперпользователи могут менять сеансовые значения по умолчанию для любых ролей. Роли с правом CREATEROLE могут изменять значения по умолчанию для ролей, не являющихся суперпользователями. Обычные пользователи (роли) могут устанавливать значения по умолчанию только для себя. Некоторые переменные конфигурации таким способом установить нельзя или можно установить только в том случае, если команду выполняет суперпользователь. Только суперпользователи могут изменять настройки для всех ролей во всех базах данных.

Параметры

имя

Имя роли, атрибуты которой подлежат изменению.

CURRENT_USER

Изменяет текущего пользователя вместо явно определенной роли.

SESSION_USER

Изменяет текущего пользователя сеанса вместо явно определенной роли.

SUPERUSER
NOSUPERUSER
CREATEDB
NOCREATEDB
CREATEROLE
NOCREATEROLE
INHERIT
NOINHERIT
LOGIN
NOLOGIN
REPLICATION
NOREPLICATION
BYPASSRLS
NOBYPASSRLS
CONNECTION LIMIT предел_подключений
[ ENCRYPTED ] PASSWORD 'пароль'
PASSWORD NULL
VALID UNTIL 'дата_время'

Эти предложения изменяют атрибуты, изначально заданные с помощью команды CREATE ROLE. Дополнительную информацию см. в разделе CREATE ROLE.

новое_имя

Новое имя роли.

имя_базы_данных

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

параметр_конфигурации
значение

Устанавливает заданное значение по умолчанию в сеансах этой роли для указанного параметра конфигурации. Если значение задано как DEFAULT или, что равнозначно, используется указание RESET, настройка переменной на уровне роли удаляется, то есть в новых сеансах роль будет наследовать системную настройку по умолчанию. Чтобы очистить все настройки роли, используйте указание RESET ALL. SET FROM CURRENT сохраняет текущее значение параметра сеанса в качестве значения для роли. Если указано IN DATABASE, параметр конфигурации устанавливается или удаляется только для заданной роли и базы данных.

Настройки переменных для роли применяются только при входе в систему; команды SET ROLE и SET SESSION AUTHORIZATION эти определения не обрабатывают.

Дополнительную информацию о допустимых именах и значениях параметров см. в разделе SET и главе Конфигурация сервера.

Примечания

Для добавления новых ролей используйте команду CREATE ROLE, а для удаления — DROP ROLE.

ALTER ROLE не управляет членством роли, для этого применяются команды GRANT и REVOKE.

При указании с помощью этой команды незашифрованного пароля необходимо соблюдать осторожность. Пароль будет передан на сервер в виде открытого текста, а также может быть зарегистрирован в журнале команд клиента или журнале сервера. psql содержит команду \\password, которая может быть использована для изменения пароля роли без предоставления открытого текста пароля.

Кроме того, можно привязать сеанс по умолчанию к определенной базе данных, а не к роли; см. раздел ALTER DATABASE. В случае возникновения конфликта параметры для базы данных и роли переопределяют параметры только для роли, которые в свою очередь переопределяют параметры только для базы данных.

Примеры

Изменение пароля роли:

ALTER ROLE davide WITH PASSWORD 'hu8jmn3';

Удаление пароля роли:

ALTER ROLE davide WITH PASSWORD NULL;

Изменение срока действия пароля указанием, что пароль должен перестать действовать в полдень 4 мая 2015 г. в часовом поясе UTC+1):

ALTER ROLE chris VALID UNTIL 'May 4 12:00:00 2015 +1';

Установка бесконечного срока действия пароля:

ALTER ROLE fred VALID UNTIL 'infinity';

Добавление роли прав на создание других ролей и новых баз данных:

ALTER ROLE miriam CREATEROLE CREATEDB;

Определение нестандартного значения параметра maintenance_work_mem для роли:

ALTER ROLE worker_bee SET maintenance_work_mem = 100000;

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

ALTER ROLE fred IN DATABASE devel SET client_min_messages = DEBUG;

Совместимость

Команда ALTER ROLE является расширением QHB.

См. также

CREATE ROLE, DROP ROLE, ALTER DATABASE, SET

ALTER ROUTINE

ALTER ROUTINE — изменить определение подпрограммы

Синтаксис

ALTER ROUTINE имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    действие [ ... ] [ RESTRICT ]
ALTER ROUTINE имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    RENAME TO новое_имя
ALTER ROUTINE имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER ROUTINE имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    SET SCHEMA новая_схема
ALTER ROUTINE имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ]
    DEPENDS ON EXTENSION имя_расширения

Где действие может быть следующим:

    IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF
    [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
    PARALLEL { UNSAFE | RESTRICTED | SAFE }
    COST стоимость_выполнения
    ROWS строк_в_результате
    SET параметр_конфигурации { TO | = } { значение | DEFAULT }
    SET параметр_конфигурации FROM CURRENT
    RESET параметр_конфигурации
    RESET ALL

Описание

Команда ALTER ROUTINE изменяет определение подпрограммы, которая может быть агрегатной функцией, нормальной функцией или процедурой. Описание параметров, дополнительные примеры и сведения см. в разделах ALTER AGGREGATE, ALTER FUNCTION и ALTER PROCEDURE.

Примеры

Переименование подпрограммы foo для типа integer в foobar:

ALTER ROUTINE foo(integer) RENAME TO foobar;

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

Совместимость

Эта команда частично совместима с командой ALTER ROUTINE в стандарте SQL. Дополнительную информацию см. в разделах ALTER FUNCTION и ALTER PROCEDURE. Возможность сослаться по имени подпрограммы на агрегатную функцию является расширением QHB.

См. также

ALTER AGGREGATE, ALTER FUNCTION, ALTER PROCEDURE, DROP ROUTINE

Обратите внимание, что команды CREATE ROUTINE нет.

ALTER RULE

ALTER RULE — изменить определение правила

Синтаксис

ALTER RULE имя ON имя_таблицы RENAME TO новое_имя

Описание

Команда ALTER RULE изменяет свойства существующего правила. В настоящее время единственным доступным действием является изменение имени правила.

Чтобы использовать команду ALTER RULE, нужно быть владельцем таблицы или представления, к которым применяется правило.

Параметры

имя

Имя существующего правила, подлежащего изменению.

имя_таблицы

Имя таблицы или представления, к которым применяется правило (может быть дополнено схемой).

новое_имя

Новое имя правила.

Примеры

Переименование существующего правила:

ALTER RULE notify_all ON emp RENAME TO notify_me;

Совместимость

Команда ALTER RULE является расширением QHB, как и вся система перезаписи запросов.

См. также

CREATE RULE, DROP RULE

ALTER SCHEMA

ALTER SCHEMA — изменить определение схемы

Синтаксис

ALTER SCHEMA имя RENAME TO новое_имя
ALTER SCHEMA имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }

Описание

Команда ALTER SCHEMA изменяет определение схемы.

Чтобы использовать команду ALTER SCHEMA, нужно быть владельцем соответствующей схемы. Чтобы переименовать схему, необходимо помимо этого иметь право CREATE в базе данных. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в базы данных. (Обратите внимание, что суперпользователи имеют все эти права автоматически.)

Параметры

имя

Имя существующей схемы.

новое_имя

Новое имя схемы. Новое имя не может начинаться с pg_, поскольку такие имена зарезервированы для системных схем.

новый_владелец

Новый владелец схемы.

Совместимость

В стандарте SQL нет команды ALTER SCHEMA.

См. также

CREATE SCHEMA, DROP SCHEMA

ALTER SEQUENCE

ALTER SEQUENCE — изменить определение генератора последовательности

Синтаксис

ALTER SEQUENCE [ IF EXISTS ] имя
    [ AS тип_данных ]
    [ INCREMENT [ BY ] шаг ]
    [ MINVALUE мин_значение | NO MINVALUE ] [ MAXVALUE макс_значение | NO MAXVALUE ]
    [ START [ WITH ] начало ]
    [ RESTART [ [ WITH ] перезапуск ] ]
    [ CACHE кэш ] [ [ NO ] CYCLE ]
    [ OWNED BY { имя_таблицы.имя_столбца | NONE } ]
ALTER SEQUENCE [ IF EXISTS ] имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER SEQUENCE [ IF EXISTS ] имя RENAME TO новое_имя
ALTER SEQUENCE [ IF EXISTS ] имя SET SCHEMA новая_схема

Описание

Команда ALTER SEQUENCE изменяет параметры существующего генератора последовательности. Все параметры, не заданные в ALTER SEQUENCE, сохраняют свои предыдущие настройки.

Чтобы использовать команду ALTER SEQUENCE, нужно быть владельцем соответствующей последовательности. Чтобы изменить схему последовательности, необходимо помимо этого иметь право CREATE в новой схеме. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме последовательности. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания последовательности. Однако суперпользователь всё равно может сменить владельца любой последовательности.)

Параметры

имя

Имя последовательности, подлежащей изменению (может быть дополнено схемой).

IF EXISTS

Не считать ошибкой, если последовательности не существует. В этом случае будет выдано замечание.

тип_данных

Необязательное предложение AS тип_данных меняет тип данных последовательности. Допустимые типы: smallint, integer и bigint.

Изменение типа данных автоматически изменяет минимальное и максимальное значения последовательности тогда и только тогда, когда предыдущие минимальное и максимальное значения были минимальным или максимальным значением старого типа данных (другими словами, если последовательность была создана с использованием NO MINVALUE или NO MAXVALUE, явно или неявно). В противном случае минимальное и максимальное значения сохраняются, если в рамках этой же команды не заданы новые значения. Если минимальное и максимальное значения не укладываются в новый тип данных, будет выдана ошибка.

шаг

Предложение INCREMENT BY шаг является необязательным. Положительное значение шага генерирует возрастающую последовательность, отрицательное — убывающую. Если не указано, будет сохранено старое значение шага.

мин_значение NO MINVALUE

Необязательное предложение MINVALUE мин_значение определяет минимальное значение, которое может генерировать последовательность. Если указано NO MINVALUE, то для возрастающей последовательности по умолчанию будет использоваться 1, а для убывающей — минимальное число для ее типа данных. Если ничего не указано, будет сохранено текущее минимальное значение.

макс_значение
NO MAXVALUE

Необязательное предложение MAXVALUE макс_значение определяет максимальное значение для последовательности. Если указано NO MAXVALUE, то для возрастающей последовательности по умолчанию будет использоваться максимальное число для ее типа данных, а для убывающей — -1. Если ничего не указано, будет сохранено текущее максимальное значение.

начало

Необязательное предложение *START WITH _начало_8 меняет записанное начальное значение последовательности. Это не влияет на текущее значение последовательности; оно просто задает значение, которое будут использовать будущие команды ALTER SEQUENCE RESTART.

перезапуск

Необязательное предложение RESTART [ WITH перезапуск ] меняет текущее значение последовательности. Это похоже на вызов функции setval параметром is_called = false: указанное значение будет возвращено при следующем вызове функции nextval. Если в предложении RESTART отсутствует значение перезапуск, будет выставлено начальное значение, которое было указано при выполнении CREATE SEQUENCE или установлено последним при выполнении ALTER SEQUENCE START WITH.

В отличие от вызова функции setval, операция RESTART над последовательностью является транзакционной и блокирует параллельным транзакциям возможность получать номера из той же последовательности. Если этот режим работы не устраивает, следует воспользоваться функцией setval.

Кэш

Предложение CACHE кэш позволяет заранее распределять и хранить в памяти последовательные номера для быстрого доступа. Минимальное значение равно 1 (за один раз может быть сгенерировано только одно значение, т. е. нет кэша). Если не указано, будет сохранено старое значение кэша.

CYCLE

Необязательный параметр CYCLE можно использовать для того, чтобы зациклить последовательность при достижении макс_значения или мин_значения для возрастающей и убывающей последовательности соответственно. Если предел достигнут, то следующим сгенерированным номером будет мин_значение или макс_значение соответственно.

NO CYCLE

Если указано NO CYCLE, то после того, как последовательность достигнет своего максимального значения, при каждом вызове функции nextval будет возвращена ошибка. Если указания CYCLE и NO CYCLE отсутствуют, сохраняется предыдущее поведение зацикливания.

OWNED BY имя_таблицы.имя_столбца
OWNED BY NONE

Указание OWNED BY связывает последовательность с определенным столбцом таблицы, чтобы при удалении этого столбца (или всей таблицы) последовательность также была автоматически удалена. Это указание заменяет любую ранее установленную связь данной последовательности. Указанная таблица должна иметь того же владельца и находиться в той же схеме, что и последовательность. Указание OWNED BY NONE удаляет все существующие связи, делая последовательность «независимой».

новый_владелец

Имя пользователя нового владельца последовательности.

новое_имя

Новое имя последовательности.

новая_схема

Новая схема последовательности.

Примечания

Команда ALTER SEQUENCE не сразу повлияет на результаты, возвращаемые nextval в серверных процессах, отличных от текущего, которые могли предварительно сгенерировать (кэшировать) значения последовательности. Эти процессы заметят изменение параметров генерации последовательности только после того, как израсходуют все кэшированные значения. Текущий сервер отреагирует на изменения немедленно.

ALTER SEQUENCE не влияет на статус currval для последовательности.

ALTER SEQUENCE блокирует параллельные вызовы nextval, currval, lastval и setval.

По историческим причинам ALTER TABLE также может работать с последовательностями, но все разновидности ALTER TABLE, допустимые для управления последовательностями, равнозначны вышеперечисленным формам.

Примеры

Перезапуск последовательности serial с числа 105:

ALTER SEQUENCE serial RESTART WITH 105;

Совместимость

Команда ALTER SEQUENCE соответствует стандарту SQL, за исключением указаний AS, START WITH, OWNED BY, OWNER TO, RENAME TO и SET SCHEMA, которые являются расширениями QHB.

См. также

CREATE SEQUENCE, DROP SEQUENCE

ALTER SERVER

ALTER SERVER — изменить определение стороннего сервера

Синтаксис

ALTER SERVER имя [ VERSION 'новая_версия' ]
    [ OPTIONS ( [ ADD | SET | DROP ] параметр ['значение'] [, ... ] ) ]
ALTER SERVER имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER SERVER имя RENAME TO новое_имя

Описание

Команда ALTER SERVER изменяет определение стороннего сервера. Первая форма изменяет строку версии сервера или его общие параметры (требуется по крайней мере одно предложение). Вторая форма меняет владельца сервера.

Чтобы изменить сервер, нужно быть его владельцем. Кроме того, для смены владельца нужно владеть сервером, а также быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право USAGE для обертки сторонних данных сервера. (Обратите внимание, что суперпользователи удовлетворяют всем этим критериям автоматически.)

Параметры

имя

Имя существующего сервера.

новая версия

Новая версия сервера.

OPTIONS ( [ ADD | SET | DROP ] параметр ['значение'] [, ... ] )

Формы, изменяющие параметры сервера. ADD, SET и DROP указывают действие, которое необходимо выполнить. Если явно не указана ни одна операция, то по умолчанию предполагается AD. Имена параметров должны быть уникальными; имена и значения проверяются с помощью библиотеки обертки сторонних данных сервера.

новый_владелец

Имя пользователя, который станет новым владельцем стороннего сервера.

новое_имя

Новое имя для стороннего сервера.

Примеры

Изменение свойств сервера foo, добавление параметров подключения:

ALTER SERVER foo OPTIONS (host 'foo', dbname 'foodb');

Изменение свойств сервера foo, смена версии, изменение параметра host:

ALTER SERVER foo VERSION '8.4' OPTIONS (SET host 'baz');

Совместимость

Команда ALTER SERVER соответствует стандарту ISO/IEC 9075-9 (SQL/MED). Формы OWNER TO и RENAME являются расширениями QHB.

См. также

CREATE SERVER, DROP SERVER

ALTER STATISTICS

ALTER STATISTICS — изменить определение объекта расширенной статистики

Синтаксис

ALTER STATISTICS имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER STATISTICS имя RENAME TO новое_имя
ALTER STATISTICS имя SET SCHEMA новая_схема
ALTER STATISTICS имя SET STATISTICS новый_ориентир

Описание

Команда ALTER STATISTICS изменяет параметры существующего объекта расширенной статистики. Все параметры, не заданные в ALTER STATISTICS, сохраняют свои предыдущие значения.

Чтобы использовать команду ALTER STATISTICS, нужно быть владельцем соответствующего объекта статистики. Чтобы изменить схему объекта статистики, необходимо помимо этого иметь право CREATE в новой схеме. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме объекта статистики. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания объекта статистики. Однако суперпользователь всё равно может сменить владельца любого объекта статистики.)

Параметры

имя

Имя объекта статистики, подлежащего изменению (может быть дополнено схемой).

новый_владелец

Имя пользователя, который станет новым владельцем объекта статистики.

новое_имя

Новое имя объекта статистики.

новая_схема

Новая схема объекта статистики.

новый_ориентир Ориентир сбора статистики для данного объекта статистики для последующих операций ANALYZE. Значение ориентира можно задать в диапазоне от 0 до 10 000; также его можно установить равным -1 для возврата к использованию максимального из ориентиров статистики целевых столбцов (если таковые заданы) или к системному ориентиру статистики по умолчанию (default_statistics_target). Более подробную информацию об использовании статистики планировщиком запросов QHB см. в разделе Статистика, используемая планировщиком.

Совместимость

В стандарте SQL нет команды ALTER STATISTICS.

См. также

CREATE STATISTICS, DROP STATISTICS

ALTER SUBSCRIPTION

ALTER SUBSCRIPTION — изменить определение подписки

Синтаксис

ALTER SUBSCRIPTION имя CONNECTION 'строка_подключения'
ALTER SUBSCRIPTION имя SET PUBLICATION имя_публикации [, ...] [ WITH ( параметр_set_publication [= значение] [, ... ] ) ]
ALTER SUBSCRIPTION имя REFRESH PUBLICATION [ WITH ( параметр_обновления [= значение] [, ... ] ) ]
ALTER SUBSCRIPTION имя ENABLE
ALTER SUBSCRIPTION имя DISABLE
ALTER SUBSCRIPTION имя SET ( параметр_подписки [= значение] [, ... ] )
ALTER SUBSCRIPTION имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER SUBSCRIPTION имя RENAME TO новое_имя

Описание

Команда ALTER SUBSCRIPTION может менять большинство свойств подписки, которые можно указать в команде CREATE SUBSCRIPTION.

Чтобы использовать команду ALTER SUBSCRIPTION, нужно быть владельцем соответствующей подписки. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца. Новый владелец должен быть суперпользователем. (В настоящее время все владельцы подписок должны быть суперпользователями, поэтому на практике проверка владельца будет пропущена. Но в будущем это может измениться.)

Параметры

имя

Имя подписки, свойства которой подлежат изменению.

CONNECTION 'строка_подключения'

Это предложение изменяет свойство соединения, первоначально заданное командой CREATE SUBSCRIPTION. Дополнительную информацию см. в описании этой команды.

SET PUBLICATION имя_публикации

Изменяет список публикаций, на которые оформлена подписка. Дополнительную информацию см. в разделе CREATE SUBSCRIPTION. По умолчанию эта команда также будет действовать как REFRESH PUBLICATION.

В указании параметр_set_publication задаются дополнительные свойства этой операции. Поддерживаемые параметры:

refresh (boolean)

При значении false команда не будет пытаться обновить информацию о таблицах. После этого REFRESH PUBLICATION следует выполнять отдельно. Значение по умолчанию равно true.

Кроме того, здесь могут задаваться параметры обновления, перечисленные в описании REFRESH PUBLICATION.

REFRESH PUBLICATION

Считывает недостающую информацию о таблицах с публикующего сервера. Это приводит к запуску репликации таблиц, добавленных в публикации, на которые оформлена подписка, после последнего вызова REFRESH PUBLICATION или CREATE SUBSCRIPTION.

В указании параметр_обновления задаются дополнительные свойства операции обновления. Поддерживаемые параметры:

copy_data (boolean)

Указывает, следует ли копировать существующие данные в публикациях, на которые оформляется подписка, после запуска репликации. Значение по умолчанию равно true. (Таблицы, на которые уже оформлена подписка, не копируются.)

ENABLE

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

DISABLE

Выключает запущенную подписку, останавливая процесс логической репликации в конце транзакции.

SET ( параметр_подписки [= значение] [, ... ] )

Это предложение изменяет параметры, первоначально заданные с помощью команды CREATE SUBSCRIPTION. Допустимые параметры: slot_name и synchronous_commit

новый_владелец

Имя пользователя, который станет новым владельцем подписки.

новое_имя

Новое имя подписки.

Примеры

Изменение подписки путем подписывания на публикацию insert_only:

ALTER SUBSCRIPTION mysub SET PUBLICATION insert_only;

Выключение (остановка) подписки:

ALTER SUBSCRIPTION mysub DISABLE;

Совместимость

Команда ALTER SUBSCRIPTION является расширением QHB.

См. также

CREATE SUBSCRIPTION, DROP SUBSCRIPTION, CREATE PUBLICATION, ALTER PUBLICATION

ALTER SYSTEM

ALTER SYSTEM — изменить параметр конфигурации сервера

Синтаксис

ALTER SYSTEM SET параметр_конфигурации { TO | = } { значение | 'значение' | DEFAULT }

ALTER SYSTEM RESET параметр_конфигурации
ALTER SYSTEM RESET ALL

Описание

Команда ALTER SYSTEM используется для изменения параметров конфигурации сервера во всем кластере баз данных. Это может быть более удобным, чем традиционный метод ручного редактирования файла qhb.conf. ALTER SYSTEM записывает заданную настройку параметра в файл qhb.auto.conf, который считывается в дополнение к qhb.conf. При указании в качестве значения параметра DEFAULT или применении варианта RESET соответствующий элемент конфигурации удаляется из файла qhb.auto.conf. Указание RESET ALL удаляет все настроенные таким способом параметры.

Значения, установленные с помощью ALTER SYSTEM, будут действовать после следующей перезагрузки конфигурации сервера или после перезагрузки сервера (в случае, если параметры можно изменить только при запуске сервера). Дать команду на перезагрузку конфигурации сервера можно путем вызова SQL-функции pg\_reload\_conf(), выполнения qhb_ctl reload или отправки главному серверному процессу сигнала SIGHUP.

Использовать команду ALTER SYSTEM могут только суперпользователи. Кроме того, поскольку эта команда действует непосредственно на файловую систему и не может быть отменена, ее нельзя помещать в блок транзакций или в функцию.

Параметры

параметр_конфигурации

Имя настраиваемого параметра конфигурации. Доступные параметры описаны в главе Конфигурация сервера.

значение

Новое значение параметра. Значения могут быть указаны в виде строковых констант, идентификаторов, чисел или разделенных запятыми списков из них, в зависимости от конкретного параметра. Если в качестве значения указать DEFAULT, параметр и его значение удаляется из qhb.auto.conf.

Примечания

Эту команду нельзя использовать для установки data_directory или параметров, которые не допустимы в qhb.conf (например, Предустановленные параметры).

Другие способы задания параметров см. в разделе Настройка параметров.

Примеры

Установка уровня ведения журнала транзакций (wal_level):

ALTER SYSTEM SET wal_level = replica;

Отмена этого изменения, восстановление значения, заданного в qhb.conf:

ALTER SYSTEM RESET wal_level;

Совместимость

Команда ALTER SYSTEM является расширением QHB.

См. также

SET, SHOW

ALTER TABLESPACE

ALTER TABLESPACE — изменить определение табличного пространства

Синтаксис

ALTER TABLESPACE имя RENAME TO новое_имя
ALTER TABLESPACE имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER TABLESPACE имя SET ( параметр_табличного_пространства = значение [, ... ] )
ALTER TABLESPACE имя RESET ( параметр_табличного_пространства [, ... ] )

Описание

Команда ALTER TABLESPACE изменяет определение табличного пространства.

Чтобы изменить определение табличного пространства, нужно быть владельцем соответствующего табличного пространства. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца. (Обратите внимание, что суперпользователи имеют эти права автоматически.)

Параметры

имя

Имя существующего табличного пространства.

новое_имя

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

новый_владелец

Новый владелец табличного пространства.

параметр_табличного_пространства

Параметр табличного пространства, который необходимо установить или сбросить. В настоящее время доступны только следующие параметры: seq_page_cost, random_page_cost, effective_io_concurrency и maintenance_io_concurrency. Установка любого значения для табличного пространства переопределит обычную оценку планировщиком стоимости чтения страниц из таблиц в этом пространстве, установленную параметрами конфигурации с тем же именем (см. seq_page_cost, random_page_cost, effective_io_concurrency, maintenance_io_concurrency). Это может быть полезно, если одно из табличных пространств расположено на диске, который быстрее или медленнее, чем остальная дисковая система.

Примеры

Переименование табличного пространства index_space в fast_raid:

ALTER TABLESPACE index_space RENAME TO fast_raid;

Смена владельца табличного пространства index_space:

ALTER TABLESPACE index_space OWNER TO mary;

Совместимость

В стандарте SQL нет команды ALTER TABLESPACE.

См. также

CREATE TABLESPACE, DROP TABLESPACE

ALTER TABLE

ALTER TABLE — изменить определение таблицы

Синтаксис

ALTER TABLE [ IF EXISTS ] [ ONLY ] имя [ * ]
    действие [, ... ]
ALTER TABLE [ IF EXISTS ] [ ONLY ] имя [ * ]
    RENAME [ COLUMN ] имя_столбца TO новое_имя_столбца
ALTER TABLE [ IF EXISTS ] [ ONLY ] имя [ * ]
    RENAME CONSTRAINT имя_ограничения TO имя_нового_ограничения
ALTER TABLE [ IF EXISTS ] имя
    RENAME TO новое_имя
ALTER TABLE [ IF EXISTS ] имя
    SET SCHEMA новая_схема
ALTER TABLE ALL IN TABLESPACE имя [ OWNED BY имя_роли [, ... ] ]
    SET TABLESPACE новое_табличное_пространство [ NOWAIT ]
ALTER TABLE [ IF EXISTS ] имя
    ATTACH PARTITION имя_партиции { FOR VALUES указание_границ_партиции | DEFAULT }
ALTER TABLE [ IF EXISTS ] имя
    DETACH PARTITION имя_партиции

Где действие может быть следующим:

    ADD [ COLUMN ] [ IF NOT EXISTS ] имя_столбца тип_данных [ COLLATE правило_сортировки ] [ ограничение_столбца [ ... ] ]
    DROP [ COLUMN ] [ IF EXISTS ] имя_столбца [ RESTRICT | CASCADE ]
    ALTER [ COLUMN ] имя_столбца [ SET DATA ] TYPE тип_данных [ COLLATE правило_сортировки ] [ USING выражение ]
    ALTER [ COLUMN ] имя_столбца SET DEFAULT выражение
    ALTER [ COLUMN ] имя_столбца DROP DEFAULT
    ALTER [ COLUMN ] имя_столбца { SET | DROP } NOT NULL
    ALTER [ COLUMN ] имя_столбца DROP EXPRESSION [ IF EXISTS ]
    ALTER [ COLUMN ] имя_столбца ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( параметры_последовательности ) ]
    ALTER [ COLUMN ] имя_столбца { SET GENERATED { ALWAYS | BY DEFAULT } | SET параметр_последовательности | RESTART [ [ WITH ] перезапуск ] } [...]
    ALTER [ COLUMN ] имя_столбца DROP IDENTITY [ IF EXISTS ]
    ALTER [ COLUMN ] имя_столбца SET STATISTICS integer
    ALTER [ COLUMN ] имя_столбца SET ( атрибут = значение [, ... ] )
    ALTER [ COLUMN ] имя_столбца RESET ( атрибут [, ... ] )
    ALTER [ COLUMN ] имя_столбца SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
    ADD ограничение_таблицы [ NOT VALID ]
    ADD ограничение_таблицы_по_индексу
    ALTER CONSTRAINT имя_ограничения [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
    VALIDATE CONSTRAINT имя_ограничения
    DROP CONSTRAINT [ IF EXISTS ]  имя_ограничения [ RESTRICT | CASCADE ]
    DISABLE TRIGGER [ имя_триггера | ALL | USER ]
    ENABLE TRIGGER [ имя_триггера | ALL | USER ]
    ENABLE REPLICA TRIGGER имя_триггера
    ENABLE ALWAYS TRIGGER имя_триггера
    DISABLE RULE имя_правила_перезаписи
    ENABLE RULE имя_правила_перезаписи
    ENABLE REPLICA RULE имя_правила_перезаписи
    ENABLE ALWAYS RULE имя_правила_перезаписи
    DISABLE ROW LEVEL SECURITY
    ENABLE ROW LEVEL SECURITY
    FORCE ROW LEVEL SECURITY
    NO FORCE ROW LEVEL SECURITY
    CLUSTER ON имя_индекса
    SET WITHOUT CLUSTER
    SET WITHOUT OIDS
    SET TABLESPACE новое_табличное_пространство
    SET { LOGGED | UNLOGGED }
    SET ( параметр_хранения = значение [, ... ] )
    RESET ( параметр_хранения [, ... ] )
    INHERIT таблица_родитель
    NO INHERIT таблица_родитель
    OF имя_типа
    NOT OF
    OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
    REPLICA IDENTITY { DEFAULT | USING INDEX имя_индекса | FULL | NOTHING }

Где указание_границ_партиции может быть:

IN ( выражение_границ_партиции [, ...] ) |
FROM ( { выражение_границ_партиции | MINVALUE | MAXVALUE } [, ...] )
  TO ( { выражение_границ_партиции | MINVALUE | MAXVALUE } [, ...] ) |
WITH ( MODULUS числовая_константа, REMAINDER числовая_константа )

Где ограничение_столбца может быть:

[ CONSTRAINT имя_ограничения ]
{ NOT NULL |
  NULL |
  CHECK ( выражение ) [ NO INHERIT ] |
  DEFAULT выражение_по_умолчанию |
  GENERATED ALWAYS AS ( генерирующее_выражение ) STORED |
  GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( параметры_последовательности ) ] |
  UNIQUE параметры_индекса |
  PRIMARY KEY параметры_индекса |
  REFERENCES целевая_таблица [ ( целевой_столбец ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
    [ ON DELETE ссылочное_действие ] [ ON UPDATE ссылочное_действие ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]

Где ограничение_таблицы может быть:

[ CONSTRAINT имя_ограничения ]
{ CHECK ( выражение ) [ NO INHERIT ] |
  UNIQUE ( имя_столбца [, ... ] ) параметры_индекса |
  PRIMARY KEY ( имя_столбца [, ... ] ) параметры_индекса |
  EXCLUDE [ USING индексный_метод ] ( элемент_исключения WITH оператор [, ... ] ) параметры_индекса [ WHERE ( предикат ) ] |
  FOREIGN KEY ( имя_столбца [, ... ] ) REFERENCES целевая_таблица [ ( целевой_столбец [, ... ] ) ]
    [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE ссылочное_действие ] [ ON UPDATE ссылочное_действие ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]

Где ограничение_таблицы_по_индексу может быть:

    [ CONSTRAINT имя_ограничения ]
    { UNIQUE | PRIMARY KEY } USING INDEX имя_индекса
    [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]

Где параметры_индекса в ограничениях UNIQUE, PRIMARY KEY и EXCLUDE могут быть:

[ INCLUDE ( имя_столбца [, ... ] ) ]
[ WITH ( параметр_хранения [= значение] [, ... ] ) ]
[ USING INDEX TABLESPACE имя_табличного_пространства ]

Где элемент_исключения в ограничении EXCLUDE может быть:

{ имя_столбца | ( выражение ) } [ класс_операторов ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ]

Описание

Команда ALTER TABLE изменяет определение существующей таблицы. Ниже описано несколько форм команды. Обратите внимание, что требуемый уровень блокировки у каждой формы может быть свой. Если явно не отмечено другое, требуется блокировка ACCESS EXCLUSIVE. При перечислении нескольких подкоманд будет запрашиваться самая сильная блокировка из требуемых ими.

ADD COLUMN [ IF NOT EXISTS ]

Эта форма добавляет в таблицу новый столбец, используя тот же синтаксис, что и команда CREATE TABLE. Если указано IF NOT EXISTS и столбец с таким именем уже существует, это не будет ошибкой.

DROP COLUMN [ IF EXISTS ]

Эта форма удаляет столбец из таблицы. Связанные со столбцом индексы и ограничения таблицы также будут автоматически удалены. Кроме того, будет удалена охватывающая удаляемый столбец многовариантная статистика, если после его удаления в статистике останутся данные только одного столбца. Чтобы удалить какие-либо зависящие от этого столбца объекты вне таблицы, например ссылки на внешние ключи или представления, необходимо добавить в команду указание CASCADE. Если в команде указано IF EXISTS и удаляемый столбец не существует, это не считается ошибкой, вместо этого просто выдается замечание.

SET DATA TYPE

Эта форма изменяет тип столбца таблицы. Включающие данный столбец индексы и простые табличные ограничения будут автоматически преобразованы для использования нового типа столбца путем повторного разбора изначально определяющего их выражения. Необязательное предложение OLLATE задает правило сортировки для нового столбца; если оно опущено, для нового типа столбца устанавливается правило сортировки по умолчанию. Необязательное предложение USING определяет, как вычислить новое значение столбца из старого; если оно опущено, преобразование по умолчанию выполняется как присваивание значения старого типа данных новому. Предложение USING становится обязательным, если отсутствует неявное приведение или присвоение от старого к новому типу.

SET/DROP DEFAULT

Эти формы устанавливают или удаляют для столбца значение по умолчанию. Значения по умолчанию применяются только в последующих командах INSERT или UPDATE, не вызывая изменений уже существующих в таблице строк.

SET/DROP NOT NULL

Эти формы определяют, будет ли столбец принимать значения NULL или нет.

Указание SET NOT NULL можно применять к столбцу, только если ни одна из записей в таблице не содержит значение NULL. Как правило, это проверяется во время выполнения команды ALTER TABLE и требует сканирования всей таблицы, однако в случае наличия у столбца ограничения CHECK, которое гарантирует, что значения NULL отсутствуют, сканирование таблицы пропускается.

Если таблица является партицией, нельзя выполнить DROP NOT NULL, если столбец помечен NOT NULL в родительской таблице. Для снятия ограничения NOT NULL со всех партиций нужно выполнить DROP NOT NULL у родительской таблицы. При этом, если у родительской таблицы отсутствует ограничение NOT NULL, при желании его всё же можно добавить к отдельным партициям; то есть потомки могут запретить значения NULL, даже если родитель разрешает их, но не наоборот.

DROP EXPRESSION [ IF EXISTS ] Эта форма превращает хранимый генерируемый столбец в обычный. Имеющиеся в столбцах данные сохраняются, но будущие изменения больше не будут вноситься генерирующим выражением.

Если указано DROP EXPRESSION IF EXISTS и столбец не является столбцом идентификации, ошибка не возникает. В этом случае выдается только замечание.

ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY
SET GENERATED { ALWAYS | BY DEFAULT }
DROP IDENTITY [ IF EXISTS ]

Эти формы определяют, является ли столбец столбцом идентификации, и меняют свойства генерирования значений уже существующего столбца идентификации. Дополнительную информацию см. в разделе CREATE TABLE. Как и SET DEFAULT, эти формы меняют поведение только последующих команд INSERT или UPDATE и не вызывают изменений уже имеющихся в таблице строк.

Если указывается DROP IDENTITY IF EXISTS и столбец не является столбцом идентификации, ошибка не возникает. В этом случае выдается только замечание.

SET параметр_последовательности
RESTART

Эти формы меняют нижележащую последовательность уже существующего столбца идентификации. Свойство параметр_последовательности поддерживается командой ALTER SEQUENCE, например INCREMENT BY.

SET STATISTICS

Эта форма задает ориентир сбора статистики по столбцу для последующих операций ANALYZE. Значение ориентира можно задать в диапазоне от 0 до 10 000; также его можно установить равным -1 для возврата к использованию системного ориентира статистики по умолчанию (default_statistics_target). Более подробную информацию об использовании статистики планировщиком запросов QHB см. в разделе Статистика, используемая планировщиком.

SET STATISTICS запрашивает блокировку SHARE UPDATE EXCLUSIVE.

SET ( атрибут = значение [, ... ] )
RESET ( атрибут [, ... ] )

Эта форма устанавливает или сбрасывает параметры для каждого атрибута. В настоящее время единственными параметрами атрибутов являются n_distinct и n_distinct_inherited, которые переопределяют оценки числа различных значений, производимые последующими операциями ANALYZE. n_distinct влияет на статистику для самой таблицы, тогда как n_distinct_inherited влияет на статистику, собранную и для таблицы, и для ее потомков. Если установлено положительное значение, ANALYZE будет считать, что столбец содержит точно указанное число различных значений не NULL. Если установлено отрицательное значение, которое должно быть больше или равно -1, ANALYZE будет считать, что число различных значений не NULL в столбце линейно зависит от размера таблицы; точное число будет вычислено путем умножения расчетного размера таблицы на абсолютное значение данного числа. Например, значение -1 подразумевает, что все значения в столбце различны, а значение -0,5 — что каждое значение повторяется в среднем дважды. Это может быть полезно, когда размер таблицы изменяется с течением времени, так как умножение на количество строк в таблице выполняется только во время планирования запроса. При указании значения 0 количество различных значений оценивается как обычно. Дополнительную информацию об использовании статистики планировщиком запросов QHB см. в разделе Статистика, используемая планировщиком.

Для изменения параметров атрибутов запрашивается блокировка SHARE UPDATE EXCLUSIVE.

SET STORAGE

Эта форма задает режим хранения для столбца. Этот параметр определяет, будет ли этот столбец храниться внутри таблицы или в отдельной таблице TOAST, а также следует ли сжимать данные. Режим PLAIN необходимо использовать для значений фиксированной длины, таких как integer; это вариант хранения внутри, без сжатия. Режим MAIN предназначен для хранения внутри, но сжимаемых данных. Режим EXTERNAL предназначен для внешних несжатых данных, а EXTENDED — для внешних сжатых данных. EXTENDED используется по умолчанию для большинства типов данных, которые поддерживают хранение non-PLAIN. Применение EXTERNAL позволяет ускорить операции с подстроками на очень больших значениях text и bytea за счет проигрыша в объеме хранилища. Обратите внимание, что сам по себе SET STORAGE ничего не меняет в таблице, а лишь задает стратегию, которая будет реализована при последующих обновлениях таблицы. Дополнительную информацию см. в разделе TOAST.

ADD ограничение_таблицы [ NOT VALID ]

Эта форма добавляет новое ограничение к таблице, используя тот же синтаксис, что и CREATE TABLE, плюс параметр NOT VALID, который в настоящее время разрешен только для ограничений внешнего ключа и проверки.

Обычно эта форма вызывает сканирование таблицы с целью проверки, что все существующие строки в таблице удовлетворяют новому ограничению. Но если используется параметр NOT VALID, то это потенциально-длительное сканирование пропускается. Ограничение по-прежнему будет применяться к последующим добавлениям или обновлениям (то есть они завершатся ошибкой, если при наличии внешнего ключа в ссылочной таблице нет соответствующей строки или если новая строка не соответствует указанному условию проверки). Но база данных не будет считать, что ограничение выполняется для всех строк в таблице, пока оно не будет проверено с помощью VALIDATE CONSTRAIN. Дополнительную информацию об использовании указания NOT VALID см. ниже, в Примечаниях.

Хотя большинству форм ADD ограничение\_таблицы нужна блокировка ACCESS EXCLUSIVE, вариант ADD FOREIGN KEY требует только блокировку SHARE ROW EXCLUSIVE. Обратите внимание, что ADD FOREIGN KEY запрашивает эту блокировку как в целевой таблице, так и в таблице, на которую данное ограничение ссылается.

С ограничениями уникальности и первичного ключа, которые добавляются в партиционированные таблицы, связаны дополнительные требования; см. CREATE TABLE. Кроме того, в настоящее время ограничения внешнего ключа для партиционированных таблиц не могут объявляться как непроверенные (NOT VALID).

ADD ограничение_таблицы_по_индексу

Эта форма добавляет в таблицу новое ограничение PRIMARY KEY или UNIQUE, основанное на существующем уникальном индексе. Все столбцы индекса будут включены в ограничение.

Индекс не может быть частичным и включать столбцы-выражения. Кроме того, это должен быть индекс В-дерева с порядком сортировки по умолчанию. Эти ограничения гарантируют, что данный индекс эквивалентен тому, который был бы построен обычными командами ADD PRIMARY KEY или ADD UNIQUE.

Если указывается PRIMARY KEY и столбцы индекса еще не помечены NOT NULL, то эта команда попытается выполнить ALTER COLUMN SET NOT NULL для каждого такого столбца. Это потребует полного сканирования таблицы с целью проверки, что столбцы не содержат нулей. Во всех остальных случаях это быстрая операция.

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

После выполнения этой команды индекс становится «принадлежащим» ограничению, аналогично тому, как если бы этот индекс был построен командой ADD PRIMARY KEY или ADD UNIQUE. В частности, удаление ограничения приведет к удалению индекса.

В настоящее время эта форма не поддерживается для партиционированных таблиц.

Примечание
Добавление ограничения с помощью существующего индекса может быть полезно в ситуациях, когда необходимо добавить новое ограничение, не блокируя обновления таблицы в течение длительного времени. Для этого можно создать индекс, используя CREATE INDEX CONCURRENTLY, а затем задействовать его как полноценное ограничение, используя эту запись. См. пример ниже.

ALTER CONSTRAINT

Эта форма меняет атрибуты созданного ранее ограничения. В настоящее время изменять можно только ограничения внешнего ключа.

VALIDATE CONSTRAINT

Эта форма проверяет ограничение внешнего ключа или проверочное ограничение, которое ранее было создано как NOT VALID, путем сканирования всей таблицы, чтобы убедиться в отсутствии строк, для которых ограничение не выполняется. Если ограничение уже помечено как проверенное, ничего не происходит. (См. Примечания ниже, где объясняется полезность этой команды.)

DROP CONSTRAINT [ IF EXISTS ]

Эта форма удаляет указанное ограничение на таблицу вместе с лежащим в его основе индексом. Если указано IF EXISTS и ограничение не существует, это не считается ошибкой. В таком случае просто выдается уведомление.

DISABLE/ENABLE [ REPLICA | ALWAYS ] TRIGGER

Эти формы настраивают срабатывание принадлежащего таблице триггера (или триггеров). Выключенный триггер сохраняется в системе, но не выполняется, когда происходит его запускающее событие. Для отложенного триггера состояние enable проверяется при наступлении события, а не при фактическом выполнении функции триггера. Можно выключить или включить один триггер, указанный по имени, или все триггеры в таблице, или только пользовательские триггеры (этот параметр исключает внутрисистемные триггеры ограничений, например используемые для реализации ограничений внешнего ключа или отложенных ограничений уникальности и исключения). Выключение или включение сгенерированных внутрисистемных триггеров ограничений требует прав суперпользователя; выключение следует делать с осторожностью, так как, разумеется, при невыполнении триггеров нельзя гарантировать целостность ограничения.

На механизм срабатывания триггера также влияет переменная конфигурации session_replication_role. Включенные без дополнительных указаний триггеры (по умолчанию) будут срабатывать, когда роль репликации является «origin» (по умолчанию) или «local». Триггеры, настроенные с указанием ENABLE REPLICA, будут срабатывать только в том случае, если сеанс находится в режиме «replica», а триггеры, настроенные с указанием ENABLE ALWAYS, будут запущены независимо от текущего режима репликации.

Действие этого механизма заключается в том, что в конфигурации по умолчанию триггеры не срабатывают на репликах. Это полезно, так как если триггер используется в исходной базе для распространения данных между таблицами, то система репликации также будет реплицировать и распределенные данные, и триггер не должен срабатывать во второй раз на реплике, потому что это приведет к дублированию. Однако если триггер используется для другой цели, например для создания внешних оповещений, то его можно задать равным ENABLE ALWAYS, чтобы он срабатывал и в репликах.

Эта команда запрашивает блокировку SHARE ROW EXCLUSIVE.

DISABLE/ENABLE [ REPLICA | ALWAYS ] RULE

Эти формы настраивают запуск принадлежащих таблице правил перезаписи. Выключенное правило сохраняется в системе, но не применяется во время перезаписи запроса. Семантика является такой же, как для выключенных/включенных триггеров. Эта конфигурация игнорируется для правил ON SELECT, которые всегда применяются для поддержания работы представлений, даже в сеансах, исполняющих не основную роль репликации.

Механизм запуска правила также зависит от переменной конфигурации session_replication_role аналогичной вышеописанным триггерам.

DISABLE/ENABLE ROW LEVEL SECURITY

Эти формы управляют применением принадлежащих таблице политик защиты строк. Если защита включена и для таблицы не определены политики, то применяется политика по умолчанию — запретить доступ. Обратите внимание, что политики могут существовать для таблицы, даже если защита на уровне строк выключена — в этом случае политики НЕ применяются и их ограничения игнорируются. См. также CREATE POLICY.

NO FORCE/FORCE ROW LEVEL SECURITY

Эти формы управляют применением принадлежащих таблице политик защиты строк, когда пользователь является владельцем таблицы. Если этот параметр включен, политики защиты на уровне строк будут применяться, когда пользователь является владельцем таблицы. Если этот параметр выключен (по умолчанию), то защита на уровне строк не будет применяться, если пользователь является владельцем таблицы. См. также CREATE POLICY.

CLUSTER ON

Эта форма выбирает индекс по умолчанию для будущих операций кластера CLUSTER. На самом деле это не перекластеризует таблицу.

Для изменения параметров кластеризации запрашивается блокировка SHARE UPDATE EXCLUSIVE.

SET WITHOUT CLUSTER

Эта форма удаляет из таблицы самую последнюю используемую спецификацию индекса кластера CLUSTER. Это влияет на будущие операции кластера, для которых не будет задан индекс.

Для изменения параметров кластеризации запрашивается блокировка SHARE UPDATE EXCLUSIVE.

SET WITHOUT OIDS

Обеспечивающий обратную совместимость синтаксис удаления системного столбца oid. Так как добавить системные столбцы oid теперь невозможно, это указание не действует.

SET TABLESPACE

Эта форма изменяет табличное пространство таблицы на указанное табличное пространство и перемещает связанные с таблицей файлы данных в новое табличное пространство. Индексы таблицы, если таковые имеются, не перемещаются, но их можно перемещать отдельно дополнительными командами SET TABLESPACE. При применении к партиционированной таблице ничего не перемещается, но все партиции, созданные позднее с помощью CREATE TABLE PARTITION OF, будут использовать это табличное пространство, если только оно не будет переопределено предложением TABLESPACE.

Все таблицы в текущей базе данных в табличном пространстве можно переместить с помощью формы ALL IN TABLESPACE, которая заблокирует все таблицы, которые будут перемещены в первую очередь, а затем переместит каждую из них. Эта форма также поддерживает указание OWNED BY: с которым перемещаются только таблицы указанных владельцев. Если указана опция NOWAIT, то команда завершится ошибкой, если не сможет сразу получить все необходимые блокировки. Обратите внимание, что системные каталоги этой командой не перемещаются; если требуется переместить их, следует использовать ALTER DATABASE или явные вызовы ALTER TABLE. Отношения information_schema не считаются частью системных каталогов и подлежат перемещению. См. также CREATE TABLESPACE.

SET { LOGGED / UNLOGGED }

Эта форма меняет характеристику журналирования таблицы, делает таблицу журналируемой/нежурналируемой соответственно (см. UNLOGGED). Не может быть применена к временной таблице.

SET ( параметр_хранения = значение [, ... ] )

Эта форма изменяет один или несколько параметров хранения таблицы. Подробную информацию о доступных параметрах см. в разделе Параметры хранения описания команды CREATE TABLE. Обратите внимание, что содержимое таблицы этой командой не будет изменено немедленно; в зависимости от параметра может потребоваться переписать таблицы, чтобы получить желаемый эффект. Это можно сделать с помощью команд VACUUM FULL, CLUSTER или одной из форм ALTER TABLE, которая принудительно перезаписывает таблицу. Изменения параметров, связанных с планировщиком, вступят в силу при следующей блокировке таблицы, поэтому текущие выполняемые запросы затронуты не будут.

Данная форма запрашивает блокировку SHARE UPDATE EXCLUSIVE для изменения параметров хранения, связанных с коэффициентом заполнения, TOAST и автоочисткой, а также параметра планировщика parallel_workers.

RESET ( параметр_хранения [, ... ] )

Эта форма сбрасывает один или несколько параметров хранения до значений по умолчанию. Как и в случае с SET, для полного обновления таблицы может потребоваться ее перезапись.

INHERIT таблица_родитель

Эта форма добавляет целевую таблицу в качестве нового потомка (дочерней таблицы) указанной родительской таблицы. Впоследствии запросы к родительской таблице будут включать записи целевой таблицы. Чтобы стать потомком, целевая таблица уже должна содержать те же столбцы, что и родительская (она также может содержать и дополнительные столбцы). Столбцы должны иметь соответствующие типы данных, и если они имеют ограничения NOT NULL в родителе, то должны иметь их и в потомке.

Кроме того, в дочерней таблице должны присутствовать все ограничения CHECK родительской таблицы, за исключением помеченных как ненаследуемые (т. е. созданных с помощью команды ALTER TABLE ... ADD CONSTRAINT ... NO INHERIT), которые игнорируются; все соответствующие ограничения дочерней таблицы не должны быть помечены как ненаследуемые. В настоящий момент ограничения UNIQUE, PRIMARY KEY и FOREIGN KE не учитываются, но в будущем это может измениться.

NO INHERIT таблица_родитель

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

OF имя_типа

Эта форма связывает таблицу с составным типом, как если бы таблица была сформирована командой CREATE TABLE OF. Список имен и типов столбцов таблицы должен точно соответствовать списку составного типа. Таблица не должна быть потомком другой таблицы. Эти ограничения гарантируют, что CREATE TABLE OF позволит создать таблицу с таким же определением.

NOT OF

Эта форма отделяет типизированную таблицу от ее типа.

OWNER TO

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

REPLICA IDENTITY

Эта форма изменяет информацию, которая записывается в журнал предзаписи для идентификации строк, которые обновляются или удаляются. Этот параметр не действует, за исключением случаев, когда используется логическая репликация. В режиме DEFAULT (по умолчанию для несистемных таблиц) записываются старые значения столбцов первичного ключа, если таковые имеются. В режиме USING INDEX записываются старые значения столбцов, составляющих индекс, который должен быть уникальным, не частичным, не отложенным и включать только столбцы, помеченные NOT NULL. В режиме FULL записываются старые значения всех столбцов в строке. В режиме NOTHING не записывается никакой информации о старой строке. (Это значение по умолчанию для системных таблиц.) Во всех случаях старые значения регистрируются, только если хотя бы в одном из столбцов, которые должны быть записаны, имеются различия между старой и новой версиями строки.

RENAME

Форма RENAME изменяет имя таблицы (или индекса, последовательности, представления, материализованного представления или сторонней таблицы), имя отдельного столбца в таблице или имя ограничения таблицы. При переименовании ограничения, имеющего базовый индекс, этот индекс также переименовывается. При этом влияние на сохраненные данные отсутствует.

SET SCHEMA

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

ATTACH PARTITION имя_партиции { FOR VALUES указание_границ_партиции | DEFAULT }

Эта форма присоединяет существующую таблицу (которая сама может быть партиционирована) в качестве партиции к целевой таблице. Таблица может быть присоединена в качестве партиции для конкретных значений с помощью FOR VALUES или в качестве партиции по умолчанию с помощью DEFAULT. Для каждого индекса в целевой таблице будет создан соответствующий индекс в присоединенной таблице, или, если эквивалентный индекс уже существует, он будет присоединен к индексу целевой таблицы, как при выполнении команды ALTER INDEX ATTACH PARTITION. Обратите внимание, что если существующая таблица является сторонней, то в настоящее время ее нельзя присоединить в качестве партиции целевой таблицы, если в целевой таблице есть индексы UNIQUE (уникальные). (См. также CREATE FOREIGN TABLE.) Для каждого пользовательского триггера уровня строк, существующего в целевой таблице, будет создан соответствующий триггер в присоединенной таблице.

Для партиции, добавляемой с FOR VALUES, используется то же указание_границ_партиции, что и в CREATE TABLE. Спецификация привязки к партициям должна соответствовать стратегии и ключу разбиения целевой таблицы. Присоединяемая таблица должна содержать те же столбцы, что и целевая таблица, и никаких других; более того, типы столбцов также должны совпадать. Кроме того, присоединяемая таблица должна иметь все ограничения NOT NULL и CHECK целевой таблицы. В настоящий момент ограничения FOREIGN KEY не рассматриваются. Если ограничения UNIQUE и PRIMARY KEY еще не существуют в партиции, они будут созданы в ней из родительской таблицы. Если какое-либо из ограничений CHECK присоединяемой таблицы помечено как NO INHERIT, команда выдаст ошибку; такие ограничения нужно будет пересоздать без предложения NO INHERIT.

Если новая партиция является обычной таблицей, выполняется ее полное сканирование, чтобы проверить, что существующие строки в таблице не нарушают ограничение партиции. Можно избежать этого сканирования, перед выполнением этой команды добавив в таблицу допустимое ограничение CHECK, позволяющее использовать только строки, удовлетворяющие требуемому ограничению партиции. Ограничение CHECK будет использоваться для определения того, что таблицу не нужно сканировать с целью проверки ограничения партиции. Однако это не работает, если какой-либо из ключей разбиения является выражением и раздел не принимает значения NULL. При присоединении партиции по списку, не принимающей значения NULL, также добавьте ограничение NOT NULL в столбец ключа разбиения, если это не выражение.

Если новая партиция является сторонней таблицей, то проверка того, подчиняются ли все строки в сторонней таблице ограничению партиции, не выполняется. (См. рассмотрение ограничений сторонней таблицы в разделе CREATE FOREIGN TABLE.)

Если таблица имеет партицию по умолчанию, добавление новой партиции изменяет ограничение партиции по умолчанию. Партиция по умолчанию не может содержать строк, которые может понадобиться переместить в основную партицию, поэтому она будет просканирована и проверена на предмет их отсутствия. Этого сканирования, как и сканирования новой партиции, можно избежать, если определить подходящее ограничение CHECK. Кроме того, как и сканирование новой партиции, оно всегда пропускается, когда партиция по умолчанию является сторонней таблицей.

Для присоединения партиции наряду с блокировками ACCESS EXCLUSIVE в присоединяемых таблицах и партиции по умолчанию (при наличии) запрашивается блокировка SHARE UPDATE EXCLUSIVE в родительской таблице.

DETACH PARTITION имя_партиции

Эта форма отсоединяет указанную партицию целевой таблицы. Отсоединенная партиция продолжает существовать как отдельная таблица, но больше не имеет никаких связей с таблицей, от которой была отсоединена. Все индексы, присоединенные к индексам целевой таблицы, отсоединяются. Все триггеры, созданные как клоны триггеров целевой таблицы, удаляются.

Все формы ALTER TABLE, которые действуют на одну таблицу, за исключением RENAME, SET SCHEMA, ATTACH PARTITION и DETACH PARTITION, могут быть объединены в список из нескольких изменений, которые будут применяться вместе. Например, можно добавить несколько столбцов и/или изменить тип нескольких столбцов в одной команде. Это особенно полезно с большими таблицами, поскольку вся таблица обрабатывается за один проход.

Чтобы использовать команду ALTER TABLE, нужно быть владельцем соответствующей таблицы. Чтобы изменить схему или табличное пространство таблицы, необходимо помимо этого иметь право CREATE в новой схеме или табличном пространстве. Чтобы добавить таблицу в качестве нового потомка родительской таблицы, нужно также быть владельцем родительской таблицы. Кроме того, чтобы присоединить таблицу в качестве новой партиции таблицы, нужно быть владельцем присоединяемой таблицы. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме таблицы. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания таблицы. Однако суперпользователь всё равно может сменить владельца любой таблицы.) Чтобы добавить столбец, сменить тип столбца или применить предложение OF, необходимо помимо этого иметь право USAGE для соответствующего типа данных.

Параметры

IF EXISTS

Не считать ошибкой, если таблица не существует. В этом случае будет выдано замечание.

имя

Имя существующей таблицы, подлежащей изменению (может быть дополнено схемой). Если перед именем таблицы указывается ONLY, изменяется только эта таблица. Если ONLY не указано, будут изменены таблица и все ее потомки (если таковые имеются). После имени таблицы можно также добавить необязательное указание *, чтобы явно обозначить, что изменению подлежат все дочерние таблицы.

имя_столбца

Имя нового или существующего столбца.

новое_имя_столбца

Новое имя существующего столбца.

новое_имя

Новое имя таблицы.

тип_данных

Тип данных нового столбца или новый тип данных для существующего столбца.

ограничение_таблицы

Новое ограничение таблицы.

имя_ограничения

Имя нового или существующего ограничения.

CASCADE

Автоматически удаляются объекты, зависящие от удаляемого столбца или ограничения (например, представления, ссылающиеся на столбец), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

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

имя_триггера

Имя триггера, который нужно выключить или включить.

ALL

Выключить или включить все принадлежащие таблице триггеры. (Если в числе этих триггеров оказываются сгенерированные внутрисистемные триггеры исключений, например используемые для реализации ограничений внешнего ключа или отложенных ограничений уникальности и исключения, то для этого потребуются права суперпользователя.)

USER

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

имя_индекса

Имя существующего индекса.

параметр_хранения

Имя параметра хранения таблицы.

значение

Новое значение для параметра хранения таблицы. В зависимости от параметра, это может быть число или строка.

таблица_родитель

Родительская таблица для связывания или отмены связи с этой таблицей.

новый_владелец

Имя пользователя, который станет новым владельцем таблицы.

новое_табличное_пространство

Имя табличного пространства, в которое будет перемещена таблица.

новая_схема

Имя схемы, в которую будет перемещена таблица.

имя_партиции

Имя таблицы, присоединяемой в качестве новой партиции или, наоборот, отсоединяемой от данной таблицы.

указание_границ_партиции

Указание границ для новой партиции. Более подробную информацию о синтаксисе этого указания см. в разделе CREATE TABLE.

Примечания

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

Когда столбец добавляется с помощью ADD COLUMN и задано неизменчивое выражение DEFAULT, значение по умолчанию вычисляется во время выполнения команды и результат сохраняется в метаданных таблицы. Это значение будет использоваться для столбца во всех существующих строках. Если указание DEFAULT отсутствует, используется значение NULL. Ни в том, ни в другом случае переписывать таблицу не требуется.

Добавление столбца с изменчивым выражением DEFAULT или изменение типа существующего столбца потребует перезаписи всей таблицы и ее индексов. Исключение: если указание USING не изменяет содержимое столбца и старый тип двоично приводится к новому или является неограниченным доменом поверх нового типа, то при изменении типа существующего столбца перезапись таблицы не требуется, но все индексы на затронутых столбцах всё равно должны быть перестроены. Перестройка большой таблицы и/или ее индекса может занять длительное время; при этом временно потребуется вдвое больше места на диске.

Добавление ограничений CHECK или NOT NULL требует сканирования таблицы, чтобы убедиться, что существующие строки соответствуют ограничению, но не требует перезаписи таблицы.

Аналогично при присоединении новой партиции та может быть проверена, чтобы убедиться, что существующие строки соответствуют ограничению партиции.

Основная причина предоставления возможности указать несколько изменений в одной команде ALTER TABLE заключается в том, что это позволяет совместить требуемые этим операциям сканирования и перезаписи таблицы и выполнить их за один проход.

Сканирование большой таблицы для проверки нового внешнего ключа или проверочного ограничения может занять много времени, и другие обновления таблицы блокируются до тех пор, пока не будет зафиксирована команда ALTER TABLE ADD CONSTRAINT. Основное предназначение указания NOT VALID при добавлении ограничения состоит в уменьшении влияния этой операции на параллельные изменения данных. С указанием NOT VALID команда ADD CONSTRAINT не сканирует таблицу и может быть немедленно зафиксирована. После этого можно выполнить команду VALIDATE CONSTRAINT, чтобы проверить, что существующие строки удовлетворяют ограничению. Эта команда не будет препятствовать параллельным изменениям, так как ей известно, что в других транзакциях уже будет действовать ограничение для добавляемых или изменяемых строк; проверить нужно только уже существующие строки. Следовательно, проверка запрашивает на изменяемую таблицу только блокировку SHARE UPDATE EXCLUSIVE. (Если ограничение является внешним ключом, то в целевой таблице этого ключа также запрашивается блокировка ROW SHARE.) Помимо оптимизации параллельной работы, может быть полезно использовать указания NOT VALID и VALIDATE CONSTRAINT в тех случаях, когда заведомо известно, что в таблице есть строки, нарушающие ограничения. После создания ограничения добавить новые недопустимые строки будет невозможно, а все существующие проблемы могут разрешаться в свободное время, пока VALIDATE CONSTRAINT наконец не будет успешно завершена.

Форма DROP COLUMN не удаляет столбец физически, а просто делает его невидимым для операций SQL. Последующие операции добавления и обновления в таблице будут сохранять нулевое значение для столбца. Как следствие, удаление столбца выполняется быстро, но это не приводит к немедленному уменьшению размера таблицы на диске, поскольку пространство, занимаемое удаляемым столбцом, не восстанавливается. Пространство будет освобождаться с течением времени по мере обновления существующих строк.

Чтобы принудительно освободить место, занимаемое удаляемым столбцом, можно выполнить одну из форм ALTER TABLE, которая выполняет перезапись всей таблицы. Это приведет к воссозданию всех строк с заменой удаленного столбца значением NULL.

Перезаписывающие формы ALTER TABLE небезопасны с точки зрения MVCC. После перезаписи таблицы та будет казаться пустой для параллельных транзакций, если они используют снимок, сделанный до этого события. Более подробную информацию см. в разделе Ограничения.

В указании USING предложения SET DATA TYPE действительно можно указать любое выражение, включающее старые значения строки; то есть оно может ссылаться на другие столбцы, в том числе и на преобразуемый. Это позволяет с помощью SET DATA TYPE выполнять очень общие преобразования. Из-за этой гибкости выражение USING не применяется к значению столбца по умолчанию (если таковое имеется); результат может не быть константным выражением, как это требуется для значения по умолчанию. Это означает, что при отсутствии неявного приведения или присвоения от старого к новому типу SET DATA TYPE, возможно, не удастся преобразовать значение по умолчанию, даже если указано USING. В таких случаях нужно удалить значение по умолчанию с помощью DROP DEFAULT, выполнить ALTER TYPE, а затем использовать SET DEFAULT, чтобы добавить подходящее новое значение по умолчанию. Аналогичные рекомендации применимы к связанным со столбцом индексам и ограничениям.

Если таблица имеет дочерние таблицы, то не разрешается добавлять, переименовывать или изменять тип столбца в родительской таблице, не делая то же самое для потомков. Это гарантирует, что у потомков столбцы всегда соответствуют родительским. Точно так же нельзя переименовать ограничение CHECK в родительской таблице, не переименовывая его во всех дочерних таблицах, что тоже гарантирует соответствие всех ограничений CHECK между родителем и потомком. (Однако этот запрет не распространяется на ограничения, основанные на индексах.) Кроме того, поскольку выборка из родительской таблицы также отбирает из ее потомков, ограничение на родителе не может быть помечено как действующее, если оно не помечено как действующее для ее потомков. Во всех этих случаях команда ALTER TABLE ONLY выполнена не будет.

Рекурсивная операция DROP COLUMN удаляет столбец дочерней таблицы только в том случае, если та не унаследовала данный столбец от каких-то других родителей и никогда не имела независимое определение столбца. Нерекурсивная операция DROP COLUMN (т. е. ALTER TABLE ONLY ... DROP COLUMN) никогда не удаляет унаследованные столбцы, а вместо этого помечает их как независимо определенные, а не унаследованные. С партиционированной таблицей нерекурсивная команда DROP COLUMN выдаст ошибку, так как все партиции таблицы должны содержать те же столбцы, что и главная таблица.

Действия для столбцов идентификации (ADD GENERATED, SET и т. д., DROP IDENTITY), а также действия TRIGGER, CLUSTER, OWNER и TABLESPACE никогда не распространяются рекурсивно на дочерние таблицы; иначе говоря, они всегда действуют так, будто указано ONLY. Добавление ограничения выполняется рекурсивно только для ограничений CHECK, не помеченных как NO INHERIT.

Изменение любой части таблицы системного каталога не допускается.

Более подробное описание допустимых параметров см. в разделе CREATE TABLE. В главе Определение данных содержится дополнительная информация о наследовании.

Примеры

Добавление в таблицу столбца типа varchar:

ALTER TABLE distributors ADD COLUMN address varchar(30);

Удаление столбца из таблицы:

ALTER TABLE distributors DROP COLUMN address RESTRICT;

Изменение типов двух существующих столбцов в одной операции:

ALTER TABLE distributors
    ALTER COLUMN address TYPE varchar(80),
    ALTER COLUMN name TYPE varchar(100);

Смена типа целочисленного столбца, содержащего время в стиле Unix, на тип timestamp with time zone с применением предложения USING:

ALTER TABLE foo
    ALTER COLUMN foo_timestamp SET DATA TYPE timestamp with time zone
    USING
        timestamp with time zone 'epoch' + foo_timestamp * interval '1 second';

То же самое, но в случае, когда у столбца есть значение по умолчанию, которое не может быть автоматически приведено к новому типу данных:

ALTER TABLE foo
    ALTER COLUMN foo_timestamp DROP DEFAULT,
    ALTER COLUMN foo_timestamp TYPE timestamp with time zone
    USING
        timestamp with time zone 'epoch' + foo_timestamp * interval '1 second',
    ALTER COLUMN foo_timestamp SET DEFAULT now();

Переименование существующего столбца:

ALTER TABLE distributors RENAME COLUMN address TO city;

Переименование существующей таблицы:

ALTER TABLE distributors RENAME TO suppliers;

Переименование существующего ограничения:

ALTER TABLE distributors RENAME CONSTRAINT zipchk TO zip_check;

Добавление в столбец ограничения NOT NULL:

ALTER TABLE distributors ALTER COLUMN street SET NOT NULL;

Удаление ограничения NOT NULL из столбца:

ALTER TABLE distributors ALTER COLUMN street DROP NOT NULL;

Добавление проверочного ограничения в таблицу и во всех ее потомков:

ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);

Добавление проверочного ограничения только в таблицу, но не в ее потомков:

ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5)
	NO INHERIT;

(Данное проверочное ограничение не будет наследоваться и будущими потомками тоже.)

Удаление проверочного ограничения из таблицы и из всех ее потомков:

ALTER TABLE distributors DROP CONSTRAINT zipchk;

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

ALTER TABLE ONLY distributors DROP CONSTRAINT zipchk;

(Проверочное ограничение остается во всех дочерних таблицах.)

Добавление в таблицу ограничения внешнего ключа:

ALTER TABLE distributors ADD CONSTRAINT distfk FOREIGN KEY (address)
	REFERENCES addresses (address);

Добавление в таблицу ограничения внешнего ключа с наименьшим влиянием на работу других:

ALTER TABLE distributors ADD CONSTRAINT distfk FOREIGN KEY (address)
	REFERENCES addresses (address) NOT VALID;
ALTER TABLE distributors VALIDATE CONSTRAINT distfk;

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

ALTER TABLE distributors ADD CONSTRAINT dist_id_zipcode_key UNIQUE (dist_id, zipcode);

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

ALTER TABLE distributors ADD PRIMARY KEY (dist_id);

Перемещение таблицы в другое табличное пространство:

ALTER TABLE distributors SET TABLESPACE fasttablespace;

Перемещение таблицы в другую схему:

ALTER TABLE myschema.distributors SET SCHEMA yourschema;

Пересоздание ограничения первичного ключа без блокировки изменений в процессе перестроения индекса:

CREATE UNIQUE INDEX CONCURRENTLY dist_id_temp_idx ON distributors (dist_id);
ALTER TABLE distributors DROP CONSTRAINT distributors_pkey,
    ADD CONSTRAINT distributors_pkey PRIMARY KEY USING INDEX dist_id_temp_idx;

Присоединение партиции к таблице, партиционированной по диапазонам:

ALTER TABLE measurement
    ATTACH PARTITION measurement_y2016m07 FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');

Присоединение партиции к таблице, партиционированной по списку:

ALTER TABLE cities
    ATTACH PARTITION cities_ab FOR VALUES IN ('a', 'b');

Присоединение партиции к таблице, партиционированной по хешу:

ALTER TABLE orders
    ATTACH PARTITION orders_p4 FOR VALUES WITH (MODULUS 4, REMAINDER 3);

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

ALTER TABLE cities
    ATTACH PARTITION cities_partdef DEFAULT;

Удаление партиции из партиционированной таблицы:

ALTER TABLE measurement
    DETACH PARTITION measurement_y2015m12;

Совместимость

Формы ADD (без USING INDEX), DROP [COLUMN], DROP IDENTITY, RESTART, SET DEFAULT, SET DATA TYPE (без USING), SET GENERATED и SET параметр_последовательности соответствуют стандарту SQL. Другие формы являются расширениями стандарта SQL, которые реализованы в QHB. Кроме того, возможность указать несколько операций изменения в одной команде ALTER TABLE тоже является расширением.

Команда ALTER TABLE DROP COLUMN позволяет удалить единственный столбец таблицы, оставляя ее без столбцов. Это расширение стандарта SQL, который не допускает таблицы без столбцов.

См. также

CREATE TABLE

ALTER TEXT SEARCH CONFIGURATION

ALTER TEXT SEARCH CONFIGURATION — изменить определение конфигурации текстового поиска

Синтаксис

ALTER TEXT SEARCH CONFIGURATION имя
    ADD MAPPING FOR тип_фрагмента [, ... ] WITH имя_словаря [, ... ]
ALTER TEXT SEARCH CONFIGURATION имя
    ALTER MAPPING FOR тип_фрагмента [, ... ] WITH имя_словаря [, ... ]
ALTER TEXT SEARCH CONFIGURATION имя
    ALTER MAPPING REPLACE старый_словарь WITH новый_словарь
ALTER TEXT SEARCH CONFIGURATION имя
    ALTER MAPPING FOR тип_фрагмента [, ... ] REPLACE старый_словарь WITH новый_словарь
ALTER TEXT SEARCH CONFIGURATION имя
    DROP MAPPING [ IF EXISTS ] FOR тип_фрагмента [, ... ]
ALTER TEXT SEARCH CONFIGURATION имя RENAME TO новое_имя
ALTER TEXT SEARCH CONFIGURATION имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER TEXT SEARCH CONFIGURATION имя SET SCHEMA новая_схема

Описание

Команда ALTER TEXT SEARCH CONFIGURATION изменяет определение конфигурации текстового поиска. Можно модифицировать его сопоставления типов фрагментов со словарями или изменить имя или владельца конфигурации.

Чтобы использовать команду ALTER TEXT SEARCH CONFIGURATION, нужно быть владельцем соответствующей конфигурации.

Параметры

имя

Имя существующей конфигурации текстового поиска (может быть дополнено схемой).

тип_фрагмента

Имя типа фрагмента, которое выдается при разборе конфигурации.

имя_словаря

Имя словаря текстового поиска, в котором будет проводиться поиск указанного типа (или типов) фрагмента. Если перечислено несколько словарей, к ним будут обращаться в указанном порядке.

старый_словарь

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

новый_словарь

Имя словаря текстового поиска, которое будет подставлено там, где был старый_словарь.

новое_имя

Новое имя конфигурации текстового поиска.

новый_владелец

Новый владелец конфигурации текстового поиска.

новая_схема

Новая схема конфигурации текстового поиска.

Форма ADD MAPPING FOR устанавливает список словарей, которые будут просматриваться в поиске указанных типов фрагментов; если уже существует сопоставление для любого из типов фрагментов, выдается ошибка. Форма ALTER MAPPING FOR делает то же самое, но сначала удаляет все существующие сопоставления для типов фрагментов. Формы ALTER MAPPING REPLACE подставляют новый_словарь вместо старый_словарь везде, где имеется последний. При добавлении FOR это делается только для указанных типов фрагментов, в противном случае — для всех сопоставлений конфигурации. Форма DROP MAPPING удаляет все словари для указанных типов фрагментов, в результате чего фрагменты этих типов игнорируются конфигурацией текстового поиска. Если сопоставлений для заданных типов фрагментов нет, возникает ошибка, если только не добавлено указание IF EXISTS.

Примеры

В следующем примере словарь english заменяется на swedish везде, где использовался english в конфигурации my_config.

ALTER TEXT SEARCH CONFIGURATION my_config
  ALTER MAPPING REPLACE english WITH swedish;

Совместимость

В стандарте SQL нет команды ALTER TEXT SEARCH CONFIGURATION.

См. также

CREATE TEXT SEARCH CONFIGURATION, DROP TEXT SEARCH CONFIGURATION

ALTER TEXT SEARCH DICTIONARY

Синтаксис

Описание

Параметры

Примеры

Совместимость

См. также

TEXT SEARCH DICTIONARY* — изменить определение словаря текстового поиска

Синтаксис

ALTER TEXT SEARCH DICTIONARY имя (
    параметр [ = значение ] [, ... ]
)
ALTER TEXT SEARCH DICTIONARY имя RENAME TO новое_имя
ALTER TEXT SEARCH DICTIONARY имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER TEXT SEARCH DICTIONARY имя SET SCHEMA новая_схема

Описание

Команда ALTER TEXT SEARCH DICTIONARY изменяет определение словаря текстового поиска. Можно изменить параметры справочника, относящиеся к определенному шаблону, или изменить имя или владельца справочника.

Чтобы использовать команду ALTER TEXT SEARCH DICTIONARY, нужно быть владельцем соответствующего словаря.

Параметры

имя

Имя существующего словаря текстового поиска (может быть дополнено схемой).

параметр

Имя параметра шаблона, который будет установлен для этого словаря.

значение

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

новое_имя

Новое имя словаря текстового поиска.

новый_владелец

Новый владелец словаря текстового поиска.

новая_схема

Новая схема словаря текстового поиска.

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

Примеры

В следующем примере команда меняет список стоп-слов словаря на базе Snowball. Другие параметры остаются неизменными.

ALTER TEXT SEARCH DICTIONARY my_dict ( StopWords = newrussian );

В следующем примере команда меняет параметр, определяющий язык, на dutch и полностью удаляет параметр, задающий список стоп-слов.

ALTER TEXT SEARCH DICTIONARY my_dict ( language = dutch, StopWords );

Следующая команда «изменяет» определение словаря, на самом деле не меняя ничего.

ALTER TEXT SEARCH DICTIONARY my_dict ( dummy );

(Это работает потому, что код удаления параметра не считает ошибкой отсутствие такого параметра.) Этот способ полезен при изменении файлов конфигурации словаря; ALTER принудит все существующие сеансы перечитать файлы конфигурации, что в противном случае они не сделают никогда, если прочитали конфигурацию ранее.

Совместимость

В стандарте SQL нет команды ALTER TEXT SEARCH DICTIONARY.

См. также

CREATE TEXT SEARCH DICTIONARY, DROP TEXT SEARCH DICTIONARY

ALTER TEXT SEARCH PARSER

ALTER TEXT SEARCH PARSER — изменить определение синтаксического анализатора текстового поиска

Синтаксис

ALTER TEXT SEARCH PARSER имя RENAME TO новое_имя
ALTER TEXT SEARCH PARSER имя SET SCHEMA новая_схема

Описание

Команда ALTER TEXT SEARCH PARSER изменяет определение синтаксического анализатора текстового поиска. В настоящее время ее единственной поддерживаемой функциональностью является изменение имени синтаксического анализатора текстового поиска.

Чтобы использовать команду ALTER TEXT SEARCH PARSER, нужно быть суперпользователем.

Параметры

имя

Имя существующего синтаксического анализатора текстового поиска (может быть дополнено схемой).

новое_имя

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

новая_схема

Новая схема синтаксического анализатора текстового поиска.

Совместимость

В стандарте SQL нет команды ALTER TEXT SEARCH PARSER.

См. также

CREATE TEXT SEARCH PARSER, DROP TEXT SEARCH PARSER

ALTER TEXT SEARCH TEMPLATE

ALTER TEXT SEARCH TEMPLATE — изменить определение шаблона текстового поиска

Синтаксис

ALTER TEXT SEARCH TEMPLATE имя RENAME TO новое_имя
ALTER TEXT SEARCH TEMPLATE имя SET SCHEMA новая_схема

Описание

Команда ALTER TEXT SEARCH TEMPLATE изменяет определение шаблона текстового поиска. В настоящее время ее единственной поддерживаемой функциональностью является изменение имени шаблона.

Чтобы использовать команду ALTER TEXT SEARCH TEMPLATE, нужно быть суперпользователем.

Параметры

имя

Имя существующего шаблона текстового поиска (может быть дополнено схемой).

новое_имя

Новое имя шаблона текстового поиска.

новая_схема

Новая схема для шаблона текстового поиска.

Совместимость

В стандарте SQL нет команды ALTER TEXT SEARCH TEMPLATE.

См. также

CREATE TEXT SEARCH TEMPLATE, DROP TEXT SEARCH TEMPLATE

ALTER TRIGGER

ALTER TRIGGER — изменить определение триггера

Синтаксис

ALTER TRIGGER имя ON имя_таблицы RENAME TO новое_имя
ALTER TRIGGER имя ON имя_таблицы DEPENDS ON EXTENSION имя_расширения

Описание

Команда ALTER TRIGGER изменяет свойства существующего триггера. Предложение RENAME изменяет имя данного триггера без изменения определения триггера. Предложение DEPENDS ON EXTENSION помечает триггер как зависящий от расширения, благодаря чему если расширение удаляется, триггер также будет автоматически удален.

Только владелец таблицы, на которой работает триггер, имеет возможность изменять его свойства.

Параметры

имя

Имя существующего триггера, свойства которого подлежат изменению.

имя_таблицы

Имя таблицы, на которую действует этот триггер.

новое_имя

Новое имя для триггера.

имя_расширения

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

Примечания

Возможность временного включения или выключения триггера предоставляется командой ALTER TABLE, а не ALTER TRIGGER, поскольку в ALTER TRIGGER нет удобного способа выразить возможность включения или выключения всех триггеров таблицы одновременно.

Примеры

Переименование существующего триггера:

ALTER TRIGGER emp_stamp ON emp RENAME TO emp_track_chgs;

Обозначение триггера как зависимого от расширения:

ALTER TRIGGER emp_stamp ON emp DEPENDS ON EXTENSION emplib;

Совместимость

Команда ALTER TRIGGER, реализованная в QHB, является расширением стандарта SQL.

См. также

ALTER TABLE

ALTER TYPE

ALTER TYPE — изменить определение типа

Синтаксис

ALTER TYPE имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER TYPE имя RENAME TO новое_имя
ALTER TYPE имя SET SCHEMA новая_схема
ALTER TYPE имя RENAME ATTRIBUTE имя_атрибута TO новое_имя_атрибута [ CASCADE | RESTRICT ]
ALTER TYPE имя действие [, ... ]
ALTER TYPE имя ADD VALUE [ IF NOT EXISTS ] новое_значение_перечисления [ { BEFORE | AFTER } соседнее_значение_перечисления ]
ALTER TYPE имя RENAME VALUE существующее_значение_перечисления TO новое_значение_перечисления
ALTER TYPE имя SET ( свойство = значение [, ... ] )

Где действие может быть следующим:

    ADD ATTRIBUTE имя_атрибута тип_данных [ COLLATE правило_сортировки ] [ CASCADE | RESTRICT ]
    DROP ATTRIBUTE [ IF EXISTS ] имя_атрибута [ CASCADE | RESTRICT ]
    ALTER ATTRIBUTE имя_атрибута [ SET DATA ] TYPE тип_данных [ COLLATE правило_сортировки ] [ CASCADE | RESTRICT ]

Описание

Команда ALTER TYPE изменяет определение существующего типа. Существует несколько форм этой команды:

OWNER

Эта форма меняет владельца типа.

RENAME

Эта форма изменяет имя типа.

SET SCHEMA

Эта форма перемещает тип в другую схему.

RENAME ATTRIBUTE

Эта форма применима только к составным типам. Она изменяет имя отдельного атрибута составного типа.

ADD ATTRIBUTE

Эта форма добавляет новый атрибут к составному типу, используя тот же синтаксис, что и CREATE TYPE.

DROP ATTRIBUTE [ IF EXISTS ]

Эта форма удаляет атрибут из составного типа. Если указано IF EXISTS и атрибут не существует, это не считается ошибкой. В этом случае выдается только замечание.

ALTER ATTRIBUTE ... SET DATA TYPE

Эта форма изменяет тип атрибута составного типа.

ADD VALUE [ IF NOT EXISTS ] [ BEFORE | AFTER ]

Эта форма добавляет новое значение в перечислимый тип. Позицию нового значения в перечислении можно указать как BEFORE или AFTER с одним из существующих значений. Без этого указания новый элемент добавляется в конец списка значений.

С указанием IF NOT EXISTS, если тип уже содержит новое значение, ошибки не возникнет: будет выдано замечание, и ничего больше не произойдет. В противном случае, если новое значение уже представлено, возникнет ошибка.

RENAME VALUE

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

SET ( свойство = значение [, ... ] )

Эта форма применима только к базовым типам. Она позволяет настраивать подгруппу свойств типа, которую можно задать в команде CREATE TYPE. В частности, можно изменить следующие свойства:

RECEIVE позволяет задать имя функции двоичного ввода, а NONE удаляет ссылку на такую функцию. Для изменения этого свойства требуются права суперпользователя.

SEND позволяет задать имя функции двоичного вывода, а NONE удаляет ссылку на такую функцию. Для изменения этого свойства требуются права суперпользователя.

TYPMOD_IN позволяет задать имя функции ввода модификатора типа, а NONE удаляет ссылку на такую функцию. Для изменения этого свойства требуются права суперпользователя.

TYPMOD_OUT позволяет задать имя функции вывода модификатора типа, а NONE удаляет ссылку на такую функцию. Для изменения этого свойства требуются права суперпользователя.

ANALYZE позволяет задать имя функции сбора статистики типа, а NONE удаляет ссылку на такую функцию. Для изменения этого свойства требуются права суперпользователя.

STORAGE может принимать значения plain, extended, external или main (более подробную информацию о том, что они означают, см. в разделе TOAST). Однако для изменения значения с plain на все остальные требуются права суперпользователя (т. к. для этого изменения нужно, чтобы все функции, реализующие типа на С, поддерживали TOAST), а изменение других значений на plain не разрешено вовсе (поскольку значения этого типа в базе данных могут уже храниться в виде TOAST). Обратите внимание, что изменение этого свойства само по себе не меняет никакие сохраненные данные; оно просто задает стратегию TOAST по умолчанию, которая будет использоваться для столбцов, создаваемых в будущем. Изменение стратегии TOAST для существующих столбцов таблицы описано в разделе ALTER TABLE.

Более подробную информацию об этих свойствах типа см. в разделе CREATE TYPE. Обратите внимание, что везде, где применимо, изменение этих свойств у базового типа будет автоматически распространяться на основанные на этом типе домены.

Указания ADD ATTRIBUTE, DROP ATTRIBUTE и ALTER ATTRIBUTE можно объединить в список множественных изменений для одновременного выполнения. Например, можно добавить несколько атрибутов и/или изменить тип нескольких атрибутов в одной команде.

Чтобы использовать команду ALTER TYPE, нужно быть владельцем соответствующего типа. Чтобы изменить схему типа, необходимо помимо этого иметь право CREATE в новой схеме. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме типа. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания типа. Однако суперпользователь всё равно может сменить владельца любого типа.) Чтобы добавить атрибут или изменить его тип, необходимо помимо этого иметь право USAGE для соответствующего типа данных.

Параметры

имя

Имя существующего типа, подлежащего изменению (может быть дополнено схемой).

новое_имя

Новое имя типа.

новый_владелец

Имя пользователя нового владельца типа.

новая_схема

Новая схема типа.

имя атрибута

Имя атрибута, который нужно добавить, изменить или удалить.

новое_имя_атрибута

Новое имя переименовываемого атрибута.

тип_данных

Тип данных добавляемого атрибута либо новый тип данных изменяемого атрибута.

новое_значение_перечисления

Новое значение, которое будет добавлено в список значений перечислимого типа, или новое имя, которое будет присвоено существующему значению. Как и все элементы перечисления, оно должно быть заключено в кавычки.

соседнее_значение_перечисления

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

существующее_значение_перечисления

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

свойство

Имя изменяемого свойства базового типа; возможные значения см. выше.

CASCADE

Автоматически распространять операцию на типизированные таблицы изменяемого типа и их потомков.

RESTRICT

Запретить выполнение операции, если изменяемый тип является типом типизированной таблицы. Это поведение по умолчанию.

Примечания

Если ALTER TYPE ... ADD VALUE (форма, которая добавляет новое значение к типу-перечислению) выполняется внутри блока транзакций, новое значение не может быть использовано до тех пор, пока транзакция не будет зафиксирована.

Сравнение, включающее добавленное значение перечисления, иногда происходит медленнее, чем сравнение, включающее только исходные члены перечислимого типа. Это, как правило, происходит только в том случае, если BEFORE или AFTER устанавливает порядковую позицию нового элемента не в конце списка. Однако иногда это наблюдается даже тогда, когда новое значение добавляется в конец списка (это происходит, если счетчик OID «прокручивается» с момента изначального создания перечислимого типа). Замедление обычно незначительно, но если это имеет значение, оптимальная производительность может быть восстановлена путем удаления и повторного создания перечислимого типа или путем выгрузки копии и повторной загрузки базы данных.

Примеры

Переименование типа данных:

ALTER TYPE electronic_mail RENAME TO email;

Смена владельца типа email на joe:

ALTER TYPE email OWNER TO joe;

Смена схемы типа email на customers:

ALTER TYPE email SET SCHEMA customers;

Добавление в тип нового атрибута:

ALTER TYPE compfoo ADD ATTRIBUTE f3 int;

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

ALTER TYPE colors ADD VALUE 'orange' AFTER 'red';

Переименование значения в перечислении:

ALTER TYPE colors RENAME VALUE 'purple' TO 'mauve';

Создание функций двоичного ввода/вывода для существующего базового типа:

CREATE FUNCTION mytypesend(mytype) RETURNS bytea ...;
CREATE FUNCTION mytyperecv(internal, oid, integer) RETURNS mytype ...;
ALTER TYPE mytype SET (
    SEND = mytypesend,
    RECEIVE = mytyperecv
);

Совместимость

Варианты добавления и удаления атрибутов являются частью стандарта SQL; другие варианты являются расширениями QHB.

См. также

CREATE TYPE, DROP TYPE

ALTER USER MAPPING

ALTER USER MAPPING — изменить определение сопоставления пользователей

Синтаксис

ALTER USER MAPPING FOR { имя_пользователя | USER | CURRENT_USER | SESSION_USER | PUBLIC }
    SERVER имя_сервера
    OPTIONS ( [ ADD | SET | DROP ] параметр ['значение'] [, ... ] )

Описание

Команда ALTER USER MAPPING изменяет определение сопоставления пользователей.

Владелец стороннего сервера может изменять сопоставления любых пользователей для этого сервера. Кроме того, пользователь может изменить сопоставление для своего собственного имени пользователя, если у него есть право USAGE на сервере.

Параметры

имя_пользователя

Имя пользователя для сопоставления. Значения CURRENT_USER и USER соответствует имени текущего пользователя. PUBLIC используется для сопоставления всех настоящих и будущих имен пользователей в системе.

имя_сервера

Имя сервера, для которого меняется сопоставление пользователей.

OPTIONS ( [ ADD | SET | DROP ] параметр ['значение'] [, ... ] )

Эти формы меняют параметры сопоставления пользователей. Новые параметры переопределяют все ранее указанные параметры. ADD, SET и DROP указывают действие, которое необходимо выполнить. Если операция не указана явно, предполагается ADD. Имена параметров должны быть уникальными; параметры также проверяются оберткой сторонних данных сервера.

Примеры

Изменение пароля в сопоставлении пользователя bob на сервере foo:

ALTER USER MAPPING FOR bob SERVER foo OPTIONS (SET password 'public');

Совместимость

Команда ALTER USER MAPPING соответствует стандарту ISO/IEC 9075-9 (SQL/MED), но существует небольшое синтаксическое различие: в стандарте ключевое слово FOR опускается. Так как и в CREATE USER MAPPING, и в DROP USER MAPPING слово FOR находится в аналогичных позициях, а IBM DB2 (еще одна популярная реализация SQL/MED) требует его наличия и для команды ALTER USER MAPPING, QHB в этом аспекте отклоняется от стандарта в целях согласованности и совместимости.

См. также

CREATE USER MAPPING, DROP USER MAPPING

ALTER USER

ALTER USER — изменить роль в базе данных

Синтаксис

ALTER USER указание_роли [ WITH ] параметр [ ... ]

Где параметр может быть:

      SUPERUSER | NOSUPERUSER
    | CREATEDB | NOCREATEDB
    | CREATEROLE | NOCREATEROLE
    | INHERIT | NOINHERIT
    | LOGIN | NOLOGIN
    | REPLICATION | NOREPLICATION
    | BYPASSRLS | NOBYPASSRLS
    | CONNECTION LIMIT предел_подключений
    | [ ENCRYPTED ] PASSWORD 'пароль' | PASSWORD NULL
    | VALID UNTIL 'дата_время'

ALTER USER имя RENAME TO новое_имя

ALTER USER { указание_роли | ALL } [ IN DATABASE имя\_базы\_данных ] SET параметр_конфигурации { TO | = } { значение | DEFAULT }
ALTER USER { указание_роли | ALL } [ IN DATABASE имя\_базы\_данных ] SET параметр_конфигурации FROM CURRENT
ALTER USER { указание_роли | ALL } [ IN DATABASE имя\_базы\_данных ] RESET параметр_конфигурации
ALTER USER { указание_роли | ALL } [ IN DATABASE имя\_базы\_данных ] RESET ALL

Где указание_роли может быть:

    имя_роли
  | CURRENT_USER
  | SESSION_USER

Описание

Команда ALTER USER теперь стала синонимом команды ALTER ROLE.

Совместимость

Команда ALTER USER является расширением QHB. В стандарте SQL определение пользователей считается зависимым от реализации.

См. также

ALTER ROLE

ALTER VIEW

ALTER VIEW — изменить определение представления

Синтаксис

ALTER VIEW [ IF EXISTS ] имя ALTER [ COLUMN ] имя_столбца SET DEFAULT выражение
ALTER VIEW [ IF EXISTS ] имя ALTER [ COLUMN ] имя_столбца DROP DEFAULT
ALTER VIEW [ IF EXISTS ] имя OWNER TO { новый_владелец | CURRENT_USER | SESSION_USER }
ALTER VIEW [ IF EXISTS ] имя RENAME TO новое_имя
ALTER VIEW [ IF EXISTS ] имя SET SCHEMA новая_схема
ALTER VIEW [ IF EXISTS ] имя SET ( имя_параметра_представления [= значение_параметра_представления] [, ... ] )
ALTER VIEW [ IF EXISTS ] имя RESET ( имя_параметра_представления [, ... ] )

Описание

Команда ALTER VIEW изменяет различные дополнительные свойства представления. (Для изменения запроса, определяющего представление, используйте команду CREATE OR REPLACE VIEW.)

Чтобы использовать команду ALTER VIEW, нужно быть владельцем соответствующего представления. Чтобы изменить схему представления, необходимо помимо этого иметь право CREATE в новой схеме. Для смены владельца текущий пользователь также должен быть непосредственным или опосредованным членом новой роли-владельца, и эта роль должна иметь право CREATE в схеме представления. (Эти ограничения направлены на то, чтобы при смене владельца не происходило ничего, что нельзя было бы сделать путем удаления и повторного создания представления. Однако суперпользователь всё равно может сменить владельца любого представления.)

Параметры

имя

Имя существующего представления (может быть дополнено схемой).

имя_столбца

Имя существующего столбца.

новое_имя_столбца

Новое имя существующего столбца.

IF EXISTS

Не считать ошибкой, если представление не существует. В этом случае будет выдано замечание.

SET/DROP DEFAULT

Эти формы устанавливают или удаляют значение по умолчанию для столбца. Значение по умолчанию столбца представления подставляется в команды INSERT или UPDATE, чьей целью является это представление, перед применением к последнему любых правил или триггеров. Таким образом, значение по умолчанию представления будет иметь приоритет над любыми значениями по умолчанию в нижележащих отношениях.

новый_владелец

Имя пользователя нового владельца представления.

новое_имя

Новое имя представления.

новая_схема

Новая схема представления.

SET ( имя_параметра_представления [= значение_параметра_представления] [, ... ] ) RESET ( имя_параметра_представления [, ... ] )

Устанавливает или сбрасывает параметр представления. В настоящее время поддерживаются следующие параметры:

check_option (string)

Изменяет параметр проверки представления. Допустимые значения: local (локальная) или cascaded (каскадная)

security_barrier (boolean)

Изменяет свойство представления, включающее барьер безопасности. Значение должно быть логическим: true или false.

Примечания

По историческим причинам команду ALTER TABLE тоже можно использовать с представлениями; но формы команды ALTER TABLE, которые разрешены для работы с представлениями, эквивалентны показанным выше.

Примеры

Переименование представления foo в bar:

ALTER VIEW foo RENAME TO bar;

Добавление значения столбца по умолчанию в изменяемое представление:

CREATE TABLE base_table (id int, ts timestamptz);
CREATE VIEW a_view AS SELECT * FROM base_table;
ALTER VIEW a_view ALTER COLUMN ts SET DEFAULT now();
INSERT INTO base_table(id) VALUES(1);  -- в ts окажется значение NULL
INSERT INTO a_view(id) VALUES(2);  -- в ts окажется текущее время

Совместимость

Команда ALTER VIEW, реализованная в QHB, является расширением стандарта SQL.

См. также

CREATE VIEW, DROP VIEW

ANALYZE

ANALYZE — собрать статистику о базе данных

Синтаксис

ANALYZE [ ( параметр [, ...] ) ] [ таблица_и_столбцы [, ...] ]
ANALYZE [ VERBOSE ] [ таблица_и_столбцы [, ...] ]

Где параметр может быть:

    VERBOSE [ логическое_значение ]
    SKIP_LOCKED [ логическое_значение ]

Где таблица_и_столбцы это:

    имя_таблицы [ ( имя_столбца [, ...] ) ]

Описание

Команда ANALYZE собирает статистическую информацию о содержании таблиц в базе данных и сохраняет результаты в системном каталоге pg_statistic. Впоследствии планировщик запросов использует эти статистические данные для определения наиболее эффективных планов выполнения запросов.

Без списка таблица_и_столбцы команда ANALYZE обрабатывает все таблицы и материализованные представления в текущей базе данных, которые текущий пользователь имеет право анализировать. Со списком ANALYZE обрабатывает только те таблицы, которые в нем указаны. Кроме того, можно указать список имен столбцов таблицы; в этом случае собираются статистические данные только для этих столбцов.

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

Параметры

VERBOSE

Включает отображение сообщений о ходе выполнения.

SKIP_LOCKED

Указывает, что команда ANALYZE не должна ждать освобождения конфликтующих блокировок при начале работы над отношением: если отношение не может быть немедленно заблокировано без ожидания, оно пропускается. Обратите внимание, что даже при использовании этого параметра ANALYZE по-прежнему может блокироваться при открытии индексов отношения или при получении выборки строк из партиций, потомков таблиц и некоторых типов сторонних таблиц. Кроме того, при обычном анализе обрабатываются все партиции указанных партиционированных таблиц, но эта опция заставит ANALYZE пропустить все партиции при наличии на партиционированной таблице конфликтующей блокировки.

логическое_значение

Указывает, должна ли выбранная функция быть включена или выключена. Для включения функции можно написать TRUE, ON или 1, а для выключения — FALSE, OFF или 0. Если этот параметр опущен, предполагается значение TRUE.

имя_таблицы

Имя конкретной таблицы для анализа (может быть дополнено схемой). Если этот параметр опущен, то анализируются все обычные таблицы, партиционированные таблицы и материализованные представления в текущей базе данных (но не сторонние таблицы). Если указанная таблица является партиционированной таблицей, то обновляется как статистика наследования партиционированной таблицы в целом, так и статистика отдельных партиций.

имя_столбца

Имя конкретного столбца для анализа. По умолчанию анализ проводится для всех столбцов.

Выводимая информация

При указании VERBOSE команда ANALYZE выдает сообщения о ходе выполнения, показывая, какая таблица обрабатывается в настоящее время. Также печатаются различные статистические данные о таблицах.

Примечания

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

Сторонние таблицы анализируются только при явном указании. Не все обертки сторонних данных поддерживают ANALYZE. Если обертка таблицы не поддерживает ANALYZE, команда выводит предупреждение и ничего не делает.

В конфигурации QHB по умолчанию процесс «Автовакуум» (см. раздел Процесс «Автовакуум») запускает автоматический анализ таблиц, когда они впервые заполняются данными, а также по мере их изменения в ходе штатных операций. Если функция автоочистки выключена, рекомендуется запускать ANALYZE периодически или сразу после внесения существенных изменений в содержимое таблицы. Точная статистика поможет планировщику выбрать наиболее подходящий план запроса и тем самым повысить скорость обработки запроса. Общая стратегия для баз данных, где данные преимущественно читаются, заключается в запуске VACUUM и ANALYZE раз в день во время наименьшей активности. (Этого будет недостаточно, если данные обновляются активно.)

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

Статистические данные, собранные методом ANALYZE, обычно включают список некоторых наиболее распространенных значений в каждом столбце и гистограмму, показывающую примерное распределение данных в каждом столбце. Один или оба этих элемента статистики могут быть опущены, если ANALYZE сочтет их неинтересными (например, в столбце с уникальным ключом нет общих значений) или если тип данных столбца не поддерживает соответствующие операторы. Более подробная информация о статистике приведена в главе Регулярные задачи обслуживания базы данных.

Для больших таблиц ANALYZE берет случайную выборку содержимого таблицы, а не рассматривает каждую строку. Это позволяет проанализировать за короткое время даже очень большие таблицы. Обратите внимание, однако, что статистические данные окажутся приблизительными и будут незначительно меняться при каждом запуске ANALYZE, даже если фактическое содержимое таблицы не изменилось. Это может привести к небольшим изменениям в оценках стоимости запросов, показанных командой EXPLAIN. В редких случаях этот недетерминизм приведет к смене выбора планов запросов у планировщика после выполнения ANALYZE. Чтобы избежать этого, нужно увеличить объем статистики, собранной методом ANALYZE, как описано ниже.

Количеством статистики можно управлять, настраивая конфигурационную переменную default_statistics_target или на уровне столбцов устанавливая целевой показатель статистики для каждого столбца командой ALTER TABLE ... ALTER COLUMN ... SET STATISTICS (см. ALTER TABLE). Целевое значение задает максимальное количество записей в списке наиболее распространенных значений и максимальное количество интервалов в гистограмме. Целевое значение по умолчанию равно 100, но оно может быть скорректировано вверх или вниз, чтобы компенсировать точность оценок планировщика относительно времени, затраченного на выполнение ANALYZE, и объема занимаемого пространства в pg_statistic. В частности, установка значения целевого показателя статистики равным нулю выключает сбор статистических данных для этого столбца. Скорее всего, будет полезно сделать это для столбцов, которые никогда не используются в предложениях WHERE, GROUP BY или ORDER BY, так как планировщик не будет использовать статистику по таким столбцам.

Число строк таблицы, выбираемых для подготовки статистики, определяется наибольшим целевым показателем статистики из всех анализируемых столбцов этой таблицы. Увеличение целевого показателя вызывает пропорциональное увеличение времени и пространства, необходимого для выполнения ANALYZE.

Один из показателей, оцениваемых командой ANALYZE, — это число различных значений, встречающихся в каждом столбце. Поскольку рассматривается только подмножество строк, эта оценка иногда может быть довольно неточной, даже с максимально возможным целевым показателем статистики. Если эта неточность приводит к плохим планам запросов, можно вручную определить более точное значение, а затем задать его командой ALTER TABLE ... ALTER COLUMN ... SET (n_distinct = ...) (см. ALTER TABLE).

Если анализируемая таблица имеет одну или нескольких дочерних таблиц, ANALYZE будет собирать статистику дважды: один раз только по строкам родительской таблицы и второй раз по строкам родительской таблицы со всеми ее потомками. Этот второй набор статистических данных необходим при планировании запросов, проходящих через всё дерево наследования. Процесс «Автовакуум», однако, при принятии решения о запуске автоматического анализа для этой таблицы будет рассматривать операции добавления или обновления данных только в самой родительской таблице. Если эта таблица редко обновляется, наследуемая статистика не будет обновляться до тех пор, пока не будет вручную запущена команда ANALYZE.

Если какая-либо из дочерних таблиц является сторонней таблицей, чьи обертки сторонних данных не поддерживают ANALYZE, эти дочерние таблицы игнорируются при сборе статистики наследования.

Если анализируемая таблица пуста, ANALYZE не будет обновлять статистику для этой таблицы. Будет сохранена вся существующая статистика.

Совместимость

В стандарте SQL нет команды ANALYZE.

См. также

VACUUM, vacuumdb, Определение предела стоимости работы процесса очистки, Процесс «Автовакуум»

BEGIN

BEGIN — начать блок транзакций

Синтаксис

BEGIN [ AUTONOMOUS ] [ WORK | TRANSACTION ] [ режим_транзакции [, ...] ]

Где режим_транзакции может быть следующим:

    ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
    READ WRITE | READ ONLY
    [ NOT ] DEFERRABLE

Описание

Команда BEGIN инициирует блок транзакций, то есть после ее выполнения все операторы будут выполняться в одной транзакции до тех пор, пока не будет дано явной команды COMMIT или ROLLBACK. По умолчанию (без BEGIN) QHB выполняет транзакции в режиме «autocommit» (автофиксация), то есть каждый оператор выполняется в своей собственной транзакции, а фиксация неявно выполняется в конце оператора (если выполнение было успешным; в противном случае выполняется откат).

В блоке транзакций операторы выполняются быстрее, потому что запуск/фиксация транзакций требует значительной активности процессора и диска. Также выполнение нескольких операторов внутри транзакции позволяет обеспечить согласованность при внесении нескольких связанных изменений: другие сеансы не смогут увидеть промежуточные состояния, в которых были сделаны не все связанные обновления.

Если указан уровень изоляции, режим чтения/записи или отложенный режим, новая транзакция имеет те же характеристики, что и после выполнения SET TRANSACTION.

Параметры

WORK
TRANSACTION

Необязательные ключевые слова. Они не имеют никакого эффекта.

Информацию о значении других параметров для данного оператора см. в разделе SET TRANSACTION.

Примечания

START TRANSACTION имеет ту же функциональность, что и BEGIN.

Для завершения блока транзакций используйте COMMIT или ROLLBACK.

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

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

Примеры

Начало блока транзакции:

BEGIN;

Совместимость

Команда BEGIN является языковым расширением QHB. BEGIN равнозначна стандартной SQL команде START TRANSACTION, справочная страница которой содержит дополнительную информацию о совместимости.

Значение DEFERRABLE параметра режим_транзакции является языковым расширением QHB.

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

См. также

COMMIT, ROLLBACK, START TRANSACTION, SAVEPOINT

CALL

CALL — вызвать процедуру

Синтаксис

CALL имя ( [ аргумент ] [, ...] )

Описание

Команда CALL вызывает процедуру.

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

Параметры

имя

Имя процедуры (может быть дополнено схемой).

аргумент

Входной аргумент для вызова процедуры. Полную информацию о синтаксисе вызова функций и процедур, включая использование именованных параметров, см. в разделе Запуск сервера базы данных.

Примечания

Чтобы вызывать процедуру, необходимо иметь для нее право EXECUTE.

Чтобы вызвать функцию (а не процедуру), используйте команду SELECT.

Если CALL выполняется в блоке транзакций, то вызываемая процедура не может выполнять команды управления транзакциями. Эти команды допускаются только в том случае, если CALL выполняется в собственной транзакции.

PL/pgSQL обрабатывает выходные параметры в командах вызова по-другому; см. раздел Вызов процедуры.

Примеры

CALL do_db_maintenance();

Совместимость

Команда CALL соответствует стандарту SQL.

См. также

CREATE PROCEDURE

CHECKPOINT

CHECKPOINT — произвести контрольную точку в журнале предзаписи

Синтаксис

CHECKPOINT

Описание

Контрольная точка — это момент в последовательности событий в журнале предзаписи, когда все файлы данных приводятся в актуальное состояние, соответствующее информации в журнале. Все файлы данных сохраняются на диск. Более подробную информацию о том, что происходит во время контрольной точки, см. в разделе Конфигурация WAL.

Команда CHECKPOINT приводит к принудительному выполнению контрольной точки в момент вызова, не дожидаясь периодической контрольной точки, запланированной системой (управляется настройками в разделе Контрольные точки). Команда CHECKPOINT не предназначена для использования в обычном режиме работы.

Если команда CHECKPOINT будет выполнена во время восстановления, то вместо записи новой контрольной точки будет принудительно введена точка перезапуска (см. раздел Конфигурация WAL).

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

Совместимость

Команда CHECKPOINT является языковым расширением QHB.

CLOSE

CLOSE — закрыть курсор

Синтаксис

CLOSE { имя | ALL }

Описание

Команда CLOSE освобождает ресурсы, связанные с открытым курсором. После закрытия курсора никакие последующие операции на нем не допускаются. Курсор нужно закрывать, когда он больше не нужен.

Каждый неудерживаемый открытый курсор неявно закрывается, когда транзакция завершается командами COMMIT или ROLLBACK. Удерживаемый курсор неявно закрывается, если транзакция, которая его создала, прерывается командой ROLLBACK. Если создаваемая транзакция успешно фиксируется, удерживаемый курсор остается открытым до вызова команды CLOSE или отключения клиента.

Параметры

имя

Имя открытого курсора, подлежащего закрытию.

ALL

Закрыть все открытые курсоры.

Примечания

QHB не имеет явной команды OPEN для курсора; курсор считается открытым, когда он объявлен. Для объявления курсора используйте команду DECLARE.

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

Если курсор закрывается после точки сохранения, которая позже откатывается назад, CLOSE не откатывается, то есть курсор остается закрытым.

Примеры

Закрытие курсора liahona:

CLOSE liahona;

Совместимость

Команда CLOSE полностью соответствует стандарту SQL. Команда CLOSE ALL является расширением QHB.

См. также

DECLARE, FETCH, MOVE

CLUSTER

CLUSTER — кластеризовать таблицу согласно индексу

Синтаксис

CLUSTER [VERBOSE] имя_таблицы [ USING имя_индекса ]
CLUSTER [VERBOSE]

Описание

Команда CLUSTER дает указание QHB кластеризировать таблицу, заданную в параметре имя_таблицы, основываясь на индексе, заданном в имя_индекса. Индекс уже должен быть определен в имя_таблицы.

Когда таблица кластеризуется, она физически переупорядочивается на основе данных индекса. Кластеризация является одноразовой операцией: при последующем обновлении таблицы изменения не кластеризуются. То есть не предпринимается никаких попыток сохранить новые или обновленные строки в соответствии с их порядком индексирования. (При желании можно периодически повторять кластеризацию, выполняя команду снова. Кроме того, установка у таблицы параметра FILLFACTOR меньше 100% может помочь сохранить порядок кластеризации во время обновлений, так как обновленные строки останутся на той же странице, если там доступно достаточно места.)

Когда таблица кластеризуется, QHB запоминает, по какому индексу она была кластеризована. Форма CLUSTER имя\_таблицы повторно кластеризует таблицу, используя тот же индекс, что и раньше. Для установки индекса, который будет использоваться в будущих операциях кластеризации, или очистки предыдущего значения можно также применить команду CLUSTER или формы SET WITHOUT CLUSTER команды ALTER TABLE.

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

Когда таблица кластеризуется, запрашивается блокировка ACCESS EXCLUSIVE. Это препятствует выполнению любых других операций базы данных (как чтению, так и записи) над таблицей, пока CLUSTER не будет завершена.

Параметры

имя_таблицы

Имя таблицы (может быть дополнено схемой).

имя_индекса

Имя индекса.

VERBOSE

Выводит отчет о процессе кластеризации по мере обработки таблиц.

Примечания

В случаях, когда происходит обращение к случайным единичным строкам таблицы, фактический порядок данных в таблице не имеет значения. Однако если имеется тенденция обращения к одним данным чаще, чем к другим, и есть индекс, который группирует их вместе, использование команды CLUSTER дает преимущества. Если запросить из таблицы диапазон индексированных значений или одно индексированное значение, имеющее несколько соответствующих строк, CLUSTER поможет, так как страница таблицы, найденная по индексу для первой искомой строки, скорее всего, будет содержать и все остальные искомые строки. Таким образом, кластеризация помогает оптимизировать обращения к диску и ускорить запросы.

CLUSTER может повторно отсортировать таблицу, используя либо сканирование по указанному индексу, либо (если индекс является B-деревом) последовательное сканирование с последующей сортировкой. Она будет пытаться выбрать более быстрый метод на основе плановых параметров затрат и доступной статистической информации.

При использовании сканирования индекса создается временная копия таблицы, содержащая данные таблицы в порядке следования индексов. Также создаются временные копии каждого индекса в таблице. Поэтому для данной операции потребуется свободное пространство на диске, по крайней мере равное сумме размера таблицы и размеров индекса.

Когда используется последовательное сканирование и сортировка, также создается временный файл сортировки, так что пиковое требование к временному пространству равно удвоенному размеру таблицы плюс размеры индекса. Этот метод часто работает быстрее, чем метод сканирования индекса, но если требование к дисковому пространству неприемлемо, этот выбор можно выключить, временно установив enable_sort в OFF.

Перед кластеризацией рекомендуется установить значение maintenance_work_mem достаточно большим (но не больше, чем объем ОЗУ, который вы можете выделить для операции CLUSTER).

Поскольку планировщик записывает статистику о порядке таблиц, для новой кластеризованной таблицы рекомендуется выполнить ANALYZE. В противном случае планировщик может сделать неверный выбор планов запросов.

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

Примеры

Кластеризация таблицы employees согласно ее индексу employees_ind:

CLUSTER employees USING employees_ind;

Кластеризация таблицы employees согласно уже использовавшемуся индексу:

CLUSTER employees;

Кластеризация всех кластеризованных ранее таблиц в базе данных:

CLUSTER;

Совместимость

В стандарте SQL нет команды CLUSTER.

См. также

clusterdb

COMMENT

COMMENT — задать или изменить комментарий объекта

Синтаксис

COMMENT ON
{
  ACCESS METHOD имя_объекта |
  AGGREGATE имя_агрегатной_функции ( сигнатура_агрегатной_функции ) |
  CAST (исходный_тип AS целевой_тип) |
  COLLATION имя_объекта |
  COLUMN имя_отношения.имя_столбца |
  CONSTRAINT имя_ограничения ON имя_таблицы |
  CONSTRAINT имя_ограничения ON DOMAIN имя_домена |
  CONVERSION имя_объекта |
  DATABASE имя_объекта |
  DOMAIN имя_объекта |
  EXTENSION имя_объекта |
  EVENT TRIGGER имя_объекта |
  FOREIGN DATA WRAPPER имя_объекта |
  FOREIGN TABLE имя_объекта |
  FUNCTION имя_функции [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] |
  INDEX имя_объекта |
  LARGE OBJECT oid_большого_объекта |
  MATERIALIZED VIEW имя_объекта |
  OPERATOR имя_оператора (тип_слева, тип_справа) |
  OPERATOR CLASS имя_объекта USING индексный_метод |
  OPERATOR FAMILY имя_объекта USING индексный_метод |
  POLICY имя_политики ON имя_таблицы |
  [ PROCEDURAL ] LANGUAGE имя_объекта |
  PROCEDURE имя_процедуры [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] |
  PUBLICATION имя_объекта |
  ROLE имя_объекта |
  ROUTINE имя_подпрограммы [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] |
  RULE имя_правила ON имя_таблицы |
  SCHEMA имя_объекта |
  SEQUENCE имя_объекта |
  SERVER имя_объекта |
  STATISTICS имя_объекта |
  SUBSCRIPTION имя_объекта |
  TABLE имя_объекта |
  TABLESPACE имя_объекта |
  TEXT SEARCH CONFIGURATION имя_объекта |
  TEXT SEARCH DICTIONARY имя_объекта |
  TEXT SEARCH PARSER имя_объекта |
  TEXT SEARCH TEMPLATE имя_объекта |
  TRANSFORM FOR имя_типа LANGUAGE имя_языка |
  TRIGGER имя_триггера ON имя_таблицы |
  TYPE имя_объекта |
  VIEW имя_объекта
} IS 'текст'

Где сигнатура_агрегатной_функции:

* |
[ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ] |
[ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ] ] ORDER BY [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ]

Описание

Команда COMMENT хранит комментарий об объекте базы данных.

Для каждого объекта сохраняется только одна строка комментария, поэтому для изменения комментария следует ввести новую команду комментария для того же объекта. Чтобы удалить комментарий, вместо текстовой строки нужно написать NULL. Комментарии автоматически удаляются вместе с объектом.

Для большинства видов объектов установить комментарий может только владелец объекта. У ролей нет владельцев, поэтому команду COMMENT ON ROLE для ролей суперпользователей могут выполнять только суперпользователи, а для обычных ролей — те, кто имеет право CREATEROLE. Кроме того, методы доступа также не имеют владельцев; чтобы добавлять комментарий для метода доступа, нужно быть суперпользователем. Разумеется, суперпользователь может задавать комментарии любым объектам.

Комментарии можно просматривать с помощью семейства команд psql \D. Другие пользовательские интерфейсы для получения комментариев могут быть построены поверх тех же встроенных функций, которые использует psql, а именно obj_description, col_description и shobj_description (см. таблицу Comment Information Functions).

Параметры

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

Имя объекта, для которого задается комментарий. Имена таблиц, агрегатных функций, правил сортировки, перекодировок, доменов, сторонних таблиц, функций, индексов, операторов, классов операторов, семейств операторов, процедур, подпрограмм, последовательностей, статистики, объектов текстового поиска, типов и представлений могут быть дополнены именем схемы. При комментировании колонки имя_отношения должно ссылаться на таблицу, представление, составной тип или стороннюю таблицу.

имя_таблицы
имя_домена

При создании комментария к ограничению, триггеру, правилу или политике эти параметры задают имя таблицы или домена, в котором этот объект определен.

исходный_тип

Имя исходного типа данных для приведения.

целевой_тип

Имя целевого типа данных для приведения.

режим_аргумента

Режим аргумента функции, процедуры или агрегатной функции: IN, OUT, INOUT или VARIADIC. Если этот параметр опущен, значение по умолчанию равно IN. Обратите внимание, что COMMENT на самом деле не обращает никакого внимания на аргументы OUT, так как для определения идентичности функции необходимы только входные аргументы. Поэтому достаточно перечислить аргументы IN, INOUT и VARIADIC.

имя_аргумента

Имя аргумента функции, процедуры или агрегатной функции. Обратите внимание, что COMMENT фактически не обращает никакого внимания на имена аргументов, так как для определения идентичности функции необходимы только типы данных аргумента.

тип_аргумента

Тип данных аргумента функции, процедуры или агрегатной функции.

oid_большого_объекта

OID большого объекта.

тип_слева
тип_справа

Типы данных аргументов оператора (могут быть дополнены именем схемы). В случае отсутствия аргумента оператора укажите вместо типа NONE.

PROCEDURAL

Это слово не несет смысловой нагрузки.

имя_типа

Имя типа данных, для которого предназначена трансформация.

имя_языка

Название языка, для которого предназначена трансформация.

текст

Новый комментарий, написанный в виде строкового литерала, или NULL для удаления комментария.

Примечания

В настоящее время механизм безопасности для просмотра комментариев отсутствует: любой пользователь, подключенный к базе данных, может просматривать все комментарии для объектов в этой базе данных. Для общих объектов, таких как базы данных, роли и табличные пространства, комментарии хранятся глобально, поэтому любой пользователь, подключенный к любой базе данных в кластере, может просматривать все комментарии для общих объектов. Поэтому не следует помещать в комментарии критически важную для безопасности информацию.

Примеры

Добавление комментария для таблицы mytable:

COMMENT ON TABLE mytable IS 'Это моя таблица.';

Его удаление:

COMMENT ON TABLE mytable IS NULL;

Еще несколько примеров:

COMMENT ON ACCESS METHOD rtree IS 'Метод доступа R-Tree';
COMMENT ON AGGREGATE my_aggregate (double precision) IS 'Вычисляет дисперсию выборки';
COMMENT ON CAST (text AS int4) IS 'Выполняет приведение строк к int4';
COMMENT ON COLLATION "fr_CA" IS 'Канадский французский';
COMMENT ON COLUMN my_table.my_column IS 'Порядковый номер сотрудника';
COMMENT ON CONVERSION my_conv IS 'Перекодировка в UTF8';
COMMENT ON CONSTRAINT bar_col_cons ON bar IS 'Ограничение столбца col';
COMMENT ON CONSTRAINT dom_col_constr ON DOMAIN dom IS 'Ограничение col для домена';
COMMENT ON DATABASE my_database IS 'База данных разработчиков';
COMMENT ON DOMAIN my_domain IS 'Домен почтового адреса';
COMMENT ON EVENT TRIGGER abort_ddl IS 'Прерывает все команды DDL';
COMMENT ON EXTENSION hstore IS 'Реализует тип данных hstore';
COMMENT ON FOREIGN DATA WRAPPER mywrapper IS 'Моя обертка сторонних данных';
COMMENT ON FOREIGN TABLE my_foreign_table IS 'Информация о сотрудниках в другой БД';
COMMENT ON FUNCTION my_function (timestamp) IS 'Возвращает число римскими цифрами';
COMMENT ON INDEX my_index IS 'Обеспечивает уникальность по коду сотрудника';
COMMENT ON LANGUAGE plpython IS 'Поддержка Python для хранимых процедур';
COMMENT ON LARGE OBJECT 346344 IS 'Документ планирования';
COMMENT ON MATERIALIZED VIEW my_matview IS 'Сводка истории заказов';
COMMENT ON OPERATOR ^ (text, text) IS 'Вычисляет пересечение двух текстов';
COMMENT ON OPERATOR - (NONE, integer) IS 'Унарный минус';
COMMENT ON OPERATOR CLASS int4ops USING btree IS 'Операторы для четырехбайтовых целых (для B-деревьев)';
COMMENT ON OPERATOR FAMILY integer_ops USING btree IS 'Все целочисленные операторы (для B-деревьев)';
COMMENT ON POLICY my_policy ON mytable IS 'Фильтр строк по пользователям';
COMMENT ON PROCEDURE my_proc (integer, integer) IS 'Строит отчет';
COMMENT ON PUBLICATION alltables IS 'Публикует все операции во всех таблицах';
COMMENT ON ROLE my_role IS 'Административная группа для таблиц бухгалтерии';
COMMENT ON ROUTINE my_routine (integer, integer) IS 'Выполняет подпрограмму (функцию или процедуру)';
COMMENT ON RULE my_rule ON my_table IS 'Протоколирует изменения в записях сотрудников';
COMMENT ON SCHEMA my_schema IS 'Данные отдела';
COMMENT ON SEQUENCE my_sequence IS 'Предназначена для генерации первичных ключей';
COMMENT ON SERVER myserver IS 'Мой сторонний сервер';
COMMENT ON STATISTICS my_statistics IS 'Улучшает оценку числа строк для планировщика';
COMMENT ON TABLE my_schema.my_table IS 'Данные сотрудников';
COMMENT ON TABLESPACE my_tablespace IS 'Табличное пространство для индексов';
COMMENT ON TEXT SEARCH CONFIGURATION my_config IS 'Фильтрация специальных слов';
COMMENT ON TEXT SEARCH DICTIONARY swedish IS 'Стеммер Snowball для шведского языка';
COMMENT ON TEXT SEARCH PARSER my_parser IS 'Разделяет текст на слова';
COMMENT ON TEXT SEARCH TEMPLATE snowball IS 'Стеммер Snowball';
COMMENT ON TRANSFORM FOR hstore LANGUAGE plpythonu IS 'Трансформирует данные из hstore в словарь языка Python';
COMMENT ON TRIGGER my_trigger ON my_table IS 'Обеспечивает ссылочную целостность';
COMMENT ON TYPE complex IS 'Тип данных комплексных чисел';
COMMENT ON VIEW my_view IS 'Представление расходов по отделам';

Совместимость

В стандарте SQL нет команды COMMENT.

COMMIT PREPARED

COMMIT PREPARED — зафиксировать транзакцию, которая ранее была подготовлена для двухфазной фиксации

Синтаксис

COMMIT PREPARED id_транзакции

Описание

Команда COMMIT PREPARED фиксирует транзакцию, находящуюся в подготовленном состоянии.

Параметры

id_транзакции

Идентификатор транзакции, подлежащей фиксации.

Примечания

Чтобы зафиксировать подготовленную транзакцию, нужно быть либо пользователем, выполнявшим транзакцию изначально, либо суперпользователем. При этом не обязательно работать в том же сеансе, где выполнялась транзакция.

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

Все доступные в настоящее время подготовленные транзакции перечислены в системном представлении pg_prepared_xacts.

Примеры

Фиксация транзакции, имеющей идентификатор foobar:

COMMIT PREPARED 'foobar';

Совместимость

Команда COMMIT PREPARED является расширением QHB. Она предназначена для использования внешними системами управления транзакциями, на некоторые из которых распространяются стандарты (например, X/Open XA), но SQL-сторона этих систем не стандартизирована.

См. также

PREPARE TRANSACTION, ROLLBACK PREPARED

COMMIT

COMMIT — зафиксировать текущую транзакцию

Синтаксис

COMMIT [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]

Описание

Команда COMMIT фиксирует текущую транзакцию. Все внесенные транзакцией изменения становятся видимыми для других и гарантированно сохранятся, если произойдет сбой.

Параметры

WORK
TRANSACTION

Необязательные ключевые слова. Они не оказывают никакого влияния.

AND CHAIN

Если указывается AND CHAIN, то немедленно запускается новая транзакция с теми же характеристиками (см. раздел SET TRANSACTION), что и только что завершенная. В противном случае новая транзакция не запускается.

Примечания

Для прерывания транзакции используйте команду ROLLBACK.

Выполнение COMMIT вне транзакции не вызовет ошибку, будет выдано только предупреждающее сообщение. Однако COMMIT AND CHAIN вне транзакции вызовет ошибку.

Примеры

Фиксация текущей транзакции и сохранение всех изменений:

COMMIT;

Совместимость

Команда COMMIT соответствует стандарту SQL. Форма COMMIT TRANSACTION является расширением QHB.

См. также

BEGIN, ROLLBACK

COPY

COPY — копировать данные между файлом и таблицей

Синтаксис

COPY имя_таблицы [ ( имя_столбца [, ...] ) ]
    FROM { 'имя_файла' | PROGRAM 'команда' | STDIN }
    [ [ WITH ] ( параметр [, ...] ) ]
    [ WHERE условие ]

COPY { имя_таблицы [ ( имя_столбца [, ...] ) ] | ( запрос ) }
    TO { 'имя_файла' | PROGRAM 'команда' | STDOUT }
    [ [ WITH ] ( параметр [, ...] ) ]

Где параметр может быть:

    FORMAT имя_формата
    FREEZE [ boolean ]
    DELIMITER 'символ_разделитель'
    NULL 'маркер_NULL'
    HEADER [ boolean ]
    QUOTE 'символ_кавычек'
    ESCAPE 'управляющий_символ'
    FORCE_QUOTE { ( имя_столбца [, ...] ) | * }
    FORCE_NOT_NULL ( имя_столбца [, ...] )
    FORCE_NULL ( имя_столбца [, ...] )
    ENCODING 'имя_кодировки'

Описание

Команда COPY перемещает данные между таблицами QHB и стандартными файлами файловой системы. COPY TO копирует содержимое таблицы в файл, а COPY FROM копирует данные из файла в таблицу (добавляя данные к тому, что уже находится в таблице). Команда COPY TO может также копировать результаты запроса SELECT.

Если указан список столбцов, COPY TO будет копировать в файл только данные из указанных столбцов, а COPY FROM будет по порядку вставлять каждое поле из файла в указанные столбцы. В столбцы, которых нет в списке, функция COPY FROM добавит их значения по умолчанию.

COPY с именем файла указывает серверу QHB напрямую читать или записывать в конкретный файл. Файл должен быть доступен пользователю QHB (идентификатору пользователя, под которым работает сервер), а путь к нему указан с точки зрения сервера. Когда указывается PROGRAM, сервер выполняет заданную команду и считывает данные со стандартного вывода программы либо записывает их на стандартный вход программы. Команда должна быть определена с точки зрения сервера и быть выполнимой пользователем QHB. Когда указывается STDIN или STDOUT, данные передаются через соединение между клиентом и сервером.

Параметры

имя_таблицы

Имя существующей таблицы (может быть дополнено схемой).

имя_столбца

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

запрос

Команда SELECT, VALUES, INSERT, UPDATE или DELETE, результаты которой должны быть скопированы. Обратите внимание, что запрос заключается в круглые скобки.

Для запросов INSERT, UPDATE и DELETE должно задаваться предложение RETURNING, а целевое отношение не должно иметь ни условного правила, ни правила ALSO, ни правила INSTEAD, разворачивающегося в несколько операторов.

имя_файла

Путь к входному или выходному файлу. Путь входного файла может быть абсолютным или относительным путем, но путь выходного файла должен быть абсолютным путем. Пользователям Windows может потребоваться использование строки E'' и дублирование каждой обратной черты в пути файла.

PROGRAM

Выполняемая команда. COPY FROM читает стандартный вывод команды, а COPY TO записывает в ее стандартный ввод

Обратите внимание, что команда вызывается оболочкой, поэтому если вам нужно передать команде оболочки какие-либо аргументы, которые поступают из ненадежного источника, обязательно следует удалить или экранировать любые специальные символы, которые могут иметь особое значение для оболочки. По соображениям безопасности лучше всего использовать фиксированную командную строку или, по крайней мере, избегать передачи в нее любого пользовательского ввода.

STDIN

Указывает, что входные данные поступают из клиентского приложения.

STDOUT

Указывает, что выходные данные передаются в клиентское приложение.

boolean

Указывает, должен ли выбранный параметр быть включен или выключен. Для включения параметра можно написать TRUE, ON или 1, а для выключения — FALSE, OFF или 0. Значение boolean можно опустить; в этом случае подразумевается TRUE.

FORMAT

Выбор формата данных для чтения или записи: text (текстовый), csv (значения, разделенные запятыми (Comma Separated Values)) или binary (двоичный). По умолчанию стоит формат text.

FREEZE

Запрашивает копирование данных с уже замороженными строками, как это было бы после выполнения команды VACUUM FREEZE. Это позволяет увеличить производительность для начальной загрузки данных. Строки будут заморожены только в том случае, если загружаемая таблица была создана или очищена в текущей подтранзакции, нет открытых курсоров и нет удерживаемых этой транзакцией старых снимков. В настоящее время невозможно выполнить COPY FREEZE для партиционированной таблицы.

Обратите внимание, что все остальные сеансы сразу же смогут увидеть данные, как только те будут успешно загружены. Это нарушает обычные правила видимости MVCC, и пользователи, включающие данный режим, должны понимать, какие проблемы это может вызвать.

DELIMITER

Задает символ, который разделяет столбцы в каждой строке файла. По умолчанию используется символ табуляции в текстовом формате, запятая внутри формата CSV. Это должен быть один однобайтовый символ. Для формата binary данный параметр не допускается.

NULL

Задает строку, представляющую собой нулевое значение. Значение по умолчанию: \N (обратная косая черта и N) в текстовом формате и пустая строка без кавычек в формате CSV. Если нет желания различать значения NULL и пустые строки, можно использовать пустую строку даже в текстовом формате. Для формата binary данный параметр не допускается.

Примечание
При использовании COPY FROM любой элемент данных, соответствующий этой строке, будет сохранен как значение NULL, поэтому следует убедиться, что вы используете ту же строку, что и в COPY TO.

HEADER

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

QUOTE

Задает символ кавычки, который будет использоваться при вводе значения данных в кавычках. По умолчанию используется двойная кавычка. Это должен быть один однобайтовый символ. Данный параметр разрешен только при использовании формата CSV.

ESCAPE

Указывает символ, который должен отображаться перед символом данных, соответствующим значению QUOTE. Значение по умолчанию совпадает с QUOTE (так что если символ кавычки появляется в данных, он удваивается). Это должен быть один однобайтовый символ. Данный параметр разрешен только при использовании формата CSV.

FORCE_QUOTE

Принудительно заключает в кавычки все значения не NULL в указанных столбцах. Выводимое значение NULL никогда не заключается в кавычки. Если указана *, значения не NULL будут заключены в кавычки во всех столбцах. Данный параметр разрешен только в COPY TO и только при использовании формата CSV.

FORCE_NOT_NULL

Не сопоставлять значения указанных столбцов с маркером NULL. По умолчанию, когда маркер пуст, это означает, что пустые значения будут считываться как строки нулевой длины, а не значения NULL, даже если они не заключены в кавычки. Данный параметр разрешен только в COPY FROM и только при использовании формата CSV.

FORCE_NULL

Сопоставлять значения указанных столбцов с маркером NULL, даже если те были заключены в кавычки, и в случае совпадения устанавливать значение NULL. По умолчанию, когда маркер пуст, пустая строка в кавычках будет преобразовываться в NULL. Данный параметр разрешен только в COPY FROM и только при использовании формата CSV.

ENCODING

Указывает, что файл имеет кодировку имя_кодировки. Если этот параметр опущен, используется текущая клиентская кодировка. Более подробную информацию см. ниже в примечаниях.

WHERE

Необязательное предложение WHERE имеет общую форму

WHERE условие

где условие — это любое выражение, возвращающее результат типа boolean. Любая строка, которая не удовлетворяет этому условию, не будет добавлена в таблицу. Строка удовлетворяет условию, если она возвращает true при подстановке вместо ссылок на переменные фактических значений из этой строки.

В настоящее время выражения WHERE не могут включать подзапросы, а при вычислении выражений не видны изменения, которые вносит сама команда COPY (это имеет значение, когда в них вызываются функции с характеристикой VOLATILE).

Выводимая информация

После успешного завершения команда COPY возвращает метку команды в виде

COPY число

Где число — количество скопированных записей.

Примечание
psql будет печатать эту метку команды только в том случае, если выполнялась не команда COPY ... TO STDOUT или ее аналог в psql, метакоманда \copy ... to stdout. Это сделано для того, чтобы не перепутать метку команды с выведенными перед ней данными.

Примечания

COPY TO можно использовать только с простыми таблицами, но не с представлениями; также эта команда не копирует строки из дочерних таблиц или партиций. Например, COPY table TO копирует те же строки, что и SELECT * FROM ONLY table. Чтобы выгрузить все строки представления, или таблицы с учетом иерархии наследования, или партиционированной таблицы, можно написать COPY (SELECT * FROM table) TO ...

COPY FROM можно использовать с простыми, сторонними или партиционированными таблицами или с представлениями, в которых установлены триггеры INSTEAD OF INSERT.

Нужно иметь право на выборку заданных для таблицы, значения которой считываются COPY TO, и право на их добавление для таблицы, в которую значения добавляются COPY FROM. Однако если в команде перечисляются выбранные столбцы, достаточно иметь права только для них.

Если для таблицы включена защита на уровне строк, то соответствующие политики SELECT будут применяться и к операторам COPY таблица TO. В настоящее время COPY FROM не поддерживается для таблиц с защитой на уровне строк. Вместо этого используйте эквивалентные инструкции INSERT.

Файлы, названные в команде COPY, считываются или записываются непосредственно сервером, а не клиентским приложением. Поэтому они должны располагаться на сервере или быть доступными серверу, а не клиенту. Они должны быть доступны на чтение или запись пользователю QHB (идентификатору пользователя, под которым работает сервер), а не клиенту. Аналогично команда, указанная параметром PROGRAM, выполняется непосредственно сервером, а не клиентским приложением и должна быть доступна на выполнение пользователю QHB. Выполнять COPY с указанием файла или внешней команды разрешено только суперпользователям базы данных или членам встроенных ролей pg_read_server_files, pg_write_server_files или pg_execute_server_program, так как это позволяет читать/записывать любые файлы и запускать любые программы, к которым имеет доступ сервер.

Не путайте COPY с инструкцией psql \copy. Метакоманда \\copy вызывает функцию COPY FROM STDIN или COPY TO STDOUT, а затем извлекает/сохраняет данные в файле, доступном клиенту psql. Таким образом, при использовании \\copy доступность файла и права доступа зависят от клиента, а не от сервера.

Рекомендуется, чтобы имя файла, используемое в COPY, всегда указывалось в качестве абсолютного пути. Это обязательное условие для команды COPY TO, но для COPY FROM есть возможность чтения из файла, указанного относительным путем. Такой путь будет интерпретирован относительно рабочего каталога серверного процесса (обычно это каталог данных кластера), а не рабочего каталога клиента.

Выполнение команды с помощью PROGRAM может быть ограничено механизмами контроля доступа операционной системы, такими как SELinux.

COPY FROM вызовет все триггеры и проверочные ограничения для целевой таблицы. Однако правила при загрузке данных не вызываются.

Для столбцов идентификации команда COPY FROM всегда будет записывать значения столбцов, указанные во входных данных, подобно команде INSERT с указанием OVERRIDING SYSTEM VALUE.

При вводе и выводе данных COPY учитывается DateStyle. Чтобы обеспечить переносимость на другие установки QHB, которые могут использовать нестандартные настройки DateStyle, перед использованием COPY TO значение DateStyle следует установить равным ISO. Также рекомендуется не выгружать данные с IntervalStyle равным sql_standard, так как сервер с другими настройками IntervalStyle может неправильно воспринимать значения отрицательных интервалов в таких данных.

Входные данные интерпретируются в соответствии с кодировкой, заданной параметром ENCODING, или текущей клиентской кодировкой, а выходные данные кодируются в кодировке ENCODING или текущую клиентской кодировке, даже если эти данные не проходят через клиент, а считываются или записываются в файл непосредственно сервером.

COPY останавливает работу при первой ошибке. Это не должно приводить к проблемам в случае COPY TO, но после COPY FROM в целевой таблице остаются ранее полученные строки. Эти строки не будут видны или доступны, но по-прежнему будут занимать место на диске. Если сбой происходит при копировании большого объема данных, это может привести к значительным потерям дискового пространства. Восстановить потерянное пространство можно с помощью команды VACUUM.

FORCE_NULL и FORCE_NOT_NULL могут применяться одновременно к одному и тому же столбцу. В результате NULL-значения в кавычках будут преобразованы в NULL, а NULL-значения без кавычек — в пустые строки.

Форматы файлов

Текстовый формат

Когда используется текстовый формат, данные читаются или записываются в виде текстового файла, строка в котором соответствует строке таблицы. Столбцы в строке разделяются символом-разделителем. Сами значения столбцов представляют собой текстовые строки, выдаваемые функцией вывода либо воспринимаемые функцией ввода, соответствующей типу данных столбца. Заданный маркер NULL используется вместо столбцов со значением NULL. COPY FROM выдает ошибку, если какая-либо строка входного файла содержит больше или меньше столбцов, чем ожидалось.

Конец данных может быть обозначен одной строкой, содержащей только обратную косую черту и точку (\.). Маркер конца данных не является обязательным при чтении из файла, так как его роль вполне выполняет конец файла; он необходим только при копировании данных в клиентские приложения или из клиентских приложений, использующих протокол pre-3.0 client.

Символы обратной косой черты (\) в данных COPY позволяют экранировать символы данных, которые в противном случае могут быть приняты за разделители строк или столбцов. В частности, при появлении в качестве части значения столбца обратной косой чертой должны предваряться следующие символы: сама обратная косая черта, новая строка, возврат каретки и текущий символ разделителя.

Маркер NULL передается командой COPY TO без добавления обратной косой черты; в то же время COPY FROM ищет во вводимых данных маркеры NULL до удаления обратных косых черт. Как следствие, маркер NULL, например \N, невозможно спутать с настоящим значением \N в данных (которое должно представляться в виде \\N).

COPY FROM распознает следующие специальные последовательности с обратной косой чертой:

Последовательность | Представляет
\b | Backspace (ASCII 8)
\f | Подача формы (ASCII 12))
\n | Новая строка (ASCII 10)
\r | Возврат каретки (ASCII 13)
\t | Табуляция (ASCII 9)
\v | Вертикальная табуляция (ASCII 11)
\цифры | Обратная косая черта с последующими 1-3 восьмеричными цифрами представляет символ с заданным числовым кодом
\xцифры | Обратная косая черта с последующим x и 1-2 шестнадцатеричными цифрами представляет символ с заданным числовым кодом

В настоящее время COPY TO никогда не будет выдавать специальные последовательности с восьмеричными или шестнадцатеричными кодами, однако эта команда использует для данных управляющих символов другие вышеперечисленные последовательности.

Любой другой символ после обратной косой черты, который не упоминается в приведенной выше таблице, будет принят для представления самого себя. Однако остерегайтесь добавлять обратные косые черты без необходимости, так как это может случайно привести к появлению строки, соответствующей маркеру конца данных (\.) или маркеру NULL (\N по умолчанию). Эти строки будут восприняты раньше, чем обработаются специальные последовательности с обратной косой чертой.

Настоятельно рекомендуется, чтобы приложения, генерирующие данные для COPY, преобразовывали символы новой строки и возврата каретки в в последовательности \n и \r соответственно. В настоящее время возврат каретки в данных можно представить символами обратная косая черта + возврат каретки, а новую строку — символами обратная косая черта + новая строка. Однако эти представления могут быть не приняты в будущих выпусках. Также они крайне уязвимы к искажениям, если файл с выводом COPY переносится между разными системами (например, с Unix в Windows и наоборот).

COPY TO завершает каждую строку символом новой строки в стиле Unix («\n»). Серверы, работающие на Microsoft Windows, вместо этого выводят символы возврат каретки/новая строка («\r\n»), но только при выводе COPY в файл на сервере; для обеспечения согласованности между платформами COPY TO STDOUT всегда передает «\n» независимо от платформы сервера. COPY FROM может обрабатывать строки, заканчивающиеся символами новая строка, возврат каретки или возврат каретки/новая строка. Чтобы уменьшить риск ошибки из-за отсутствия обратной косой черты перед символами новой строки и возврата каретки, которые должны были быть данными, COPY FROM сигнализирует о проблеме, если концы строк во входных данных различаются.

Формат CSV

Этот формат применяется для импорта и экспорта данных, разделенных запятой (CSV), и используется многими другими программами, такими как электронные таблицы. Вместо правил экранирования, используемых стандартным текстовым форматом QHB, этот формат создает и распознает общий механизм экранирования CSV.

Значения в каждой записи разделяются символом DELIMITER. Если значение содержит символ разделителя, символ QUOTE, маркер NULL, символ возврата каретки или перевода строки, то всё значение будет дополнено спереди и сзади символами QUOTE, а любое вхождение символа QUOTE или спецсимвола (ESCAPE) в данных предваряется спецсимволом. Также можно использовать FORCE_QUOTE, чтобы принудительно заключать в кавычки любые значения не NULL в указанных столбцах.

В формате CSV отсутствует стандартный способ отличить значение NULL от пустой строки. В QHB команда COPY решает это с помощью кавычек. Значение NULL выводится в виде строки, задаваемой параметром NULL, и не заключается в кавычки, тогда как значение не NULL, совпадающее со строкой, задаваемой параметром NULL, заключается. Например, с настройками по умолчанию NULL записывается в виде пустой строки без кавычек, в то время как пустое строковое значение данных записывается с двойными кавычками (""). Чтение значений следует аналогичным правилам. Указание FORCE_NOT_NULL позволяет избежать сравнений с NULL во входных данных в заданных столбцах, а FORCE_NULL — преобразовывать в NULL даже заключенные в кавычки маркеры NULL.

Так как обратная косая черта не является специальным символом в формате CSV, маркер конца данных \. также может быть значением данных. Чтобы избежать любого неправильного толкования, данные \., отображаемые как одиночная запись в строке, автоматически заключаются в кавычки при выводе, а при вводе этот маркер, заключенный в кавычки, не интерпретируется как маркер конца данных. Если вы загружаете файл, созданный другим приложением, который имеет один столбец без кавычек и может иметь значение \., потребуется дополнительно заключить это значение в кавычки.

Примечание
В формате CSV все символы являются значимыми. Заключенное в кавычки значение, дополненное пробелами или любыми другими символами, кроме DELIMITER, будет включать в себя эти символы. Это может привести к ошибкам при импорте данных из системы, дополняющей строки CSV пробельными символами до некоторой фиксированной ширины. В случае возникновения такой ситуации перед импортом данных в QHB может потребоваться предварительная обработка файла CSV для удаления конечного пробела.

Примечание
Обработчик формата CSV воспринимает и генерирует файлы CSV со значениями в кавычках, которые могут содержать символы возврата каретки и перевода строки. Таким образом, число строк в этих файлах не строго равно числу строк в таблице, как в файлах текстового формата.

Примечание
Многие программы создают странные и иногда неприемлемые файлы CSV, поэтому формат файла является скорее условным, чем стандартным. Как следствие, можно столкнуться с некоторыми файлами, которые нельзя импортировать с помощью этого механизма, и COPY также может создавать файлы, которые другие программы не могут обрабатывать.

Двоичный формат

При выборе формата binary все данные сохраняются/считываются в двоичном, а не текстовом виде. Этот формат может обрабатываться несколько быстрее, чем текстовый и CSV форматы, но файл двоичного формата менее переносим в разных машинных архитектурах и версиях QHB. Кроме того, двоичный формат очень зависит для типа данных; например, он не позволит вывести двоичные данные из столбца smallint а затем прочитать их в столбец integer, хотя с текстовым форматом это вполне возможно.

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

Заголовок файла

Заголовок файла состоит из 15 байт фиксированных полей, за которыми следует область расширения заголовка переменной длины. Фиксированные поля:

Сигнатура

11-байтовая последовательность PGCOPY\n \ 377\r\n\0 — обратите внимание, что нулевой байт является обязательной частью сигнатуры. (Сигнатура предназначена для облегчения идентификации файлов, которые были испорчены при передаче, не сохраняющей все 8 бит данных. Она изменится при прохождении через фильтры, меняющие концы строк, удалении нулевых байтов или старших битов либо при изменениях четности.)

Поле флагов

32-разрядная целочисленная битовая маска для обозначения важных аспектов формата файла. Биты нумеруются от 0 (LSB) до 31 (MSB). Обратите внимание, что это поле хранится в сетевом порядке байтов (наиболее значащий байт первый), как и все целочисленные поля, используемые в данном формате. Биты 16-31 зарезервированы, чтобы обозначить критичные проблемы формата; обработчик должен прервать чтение, встретив неожиданный набор битов в этом диапазоне. Биты 0-15 зарезервированы, чтобы сигнализировать о проблемах формата обратной совместимости; обработчик может просто игнорировать любые неожиданные наборы битов в этом диапазоне. В настоящее время определен только один битовый флаг, остальные должны быть равны 0:

Бит 16

При 1 данные включаются в OID; при 0 — нет. В QHB больше не поддерживаются системные столбцы Oid, но формат всё еще содержит этот индикатор.

Длина области расширения заголовка

32-разрядное целое число, длина в байтах остатка заголовка, не включая само это значение. В настоящее время содержит 0, и сразу за ним следует первая запись. Будущие изменения формата могут привести к появлению дополнительных данных в заголовке. Обработчик должен просто пропустить все данные расширения заголовка, с которыми он не знает, что делать.

Предполагается, что область расширения заголовка будет содержать последовательность самоидентифицирующихся фрагментов. Поле флагов не должно содержать указаний для обработчика о том, что содержится в области расширения. Точное содержимое области расширения может быть определено в будущих версиях.

Эта конструкция позволяет выполнять как обратно-совместимые добавления заголовков (добавлять блоки расширения заголовков или устанавливать младшие биты флагов), так и не обратно-совместимые изменения (устанавливать старшие биты флагов, чтобы сигнализировать о таких изменениях, и при необходимости добавлять вспомогательные данные в область расширения).

Записи

Каждая запись начинается с 16-разрядного целого числа количества полей в записи. (В настоящее время все записи в таблице будут иметь одинаковое количество полей, но это может быть не всегда верно.) Затем для каждого поля в записи указывается 32-битное машинное слово, за которым следует это же количество байт с данными поля. (Машинное слово не включает свой размер и может быть равно нулю.) В качестве особого варианта -1 обозначает, что в поле содержится NULL. В случае с NULL за длиной не следуют байты данных.

Между полями нет выравнивания или каких-либо других дополнительных данных.

В настоящее время все значения данных в файле двоичного формата содержаться в двоичном формате (формате под кодом 1). Предполагается, что в будущем расширении в заголовок может быть добавлено поле, позволяющее задавать другие коды форматов для разных столбцов.

Чтобы определить подходящий двоичный формат для фактических данных, обратитесь к исходному коду QHB, в частности, к функциям send и recv для типов данных каждого столбца (обычно эти функции находятся в каталоге src/backend/utils/adt/ в дереве исходного кода).

Если в файл включается OID, поле OID следует немедленно за числом, определяющим количество полей. Это обычное поле, за исключением того, что оно не учитывается в количестве полей. Обратите внимание, что системные столбцы oid не поддерживаются в текущих версиях QHB.

Окончание файла

Трейлер файла состоит из 16-разрядного целого слова, содержащего -1. Это позволяет легко отличить его от счетчика полей в записи.

Обработчик, читающий файл, должен выдать ошибку, если число полей в записи не равно -1 или ожидаемому числу столбцов. Это обеспечивает дополнительную проверку синхронизации данных.

Примеры

В следующем примере таблица передается клиенту с разделителем полей «вертикальная черта» (|):

COPY country TO STDOUT (DELIMITER '|');

Копирование данных из файла в таблицу country:

COPY country FROM '/usr1/proj/bray/sql/country_data';

Копирование в файл только данных стран, название которых начинается с «A»:

COPY (SELECT * FROM country WHERE country_name LIKE 'A%') TO '/usr1/proj/bray/sql/a_list_countries.copy';

Для копирования данных в сжатый файл можно направить вывод через внешнюю программу сжатия:

COPY country TO PROGRAM 'gzip > /usr1/proj/bray/sql/country_data.gz';

Пример данных, подходящих для копирования в таблицу из STDIN:

AF      AFGHANISTAN
AL      ALBANIA
DZ      ALGERIA
ZM      ZAMBIA
ZW      ZIMBABWE

Обратите внимание, что пробел в каждой строке фактически является символом табуляции.

Ниже приведены те же данные, но выведенные в двоичном формате. Данные показаны после обработки Unix-утилитой od -c. Таблица содержит три столбца; первый имеет тип char(2), второй — text, а третий — integer. Последний столбец во всех строках содержит NULL.

0000000   P   G   C   O   P   Y  \n 377  \r  \n  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0 003  \0  \0  \0 002   A   F  \0  \0  \0 013   A
0000040   F   G   H   A   N   I   S   T   A   N 377 377 377 377  \0 003
0000060  \0  \0  \0 002   A   L  \0  \0  \0 007   A   L   B   A   N   I
0000100   A 377 377 377 377  \0 003  \0  \0  \0 002   D   Z  \0  \0  \0
0000120 007   A   L   G   E   R   I   A 377 377 377 377  \0 003  \0  \0
0000140  \0 002   Z   M  \0  \0  \0 006   Z   A   M   B   I   A 377 377
0000160 377 377  \0 003  \0  \0  \0 002   Z   W  \0  \0  \0  \b   Z   I
0000200   M   B   A   B   W   E 377 377 377 377 377 377

Совместимость

В стандарте SQL нет команды COPY.

CREATE ACCESS METHOD

CREATE ACCESS METHOD — определить новый метод доступа

Синтаксис

CREATE ACCESS METHOD имя
    TYPE тип_метода_доступа
    HANDLER функция_обработчик

Описание

Команда CREATE ACCESS METHOD создает новый метод доступа.

Имя метода доступа должно быть уникальным в базе данных.

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

Параметры

имя

Имя создаваемого метода доступа.

тип_метода_доступа

В этом предложении указывается тип определяемого метода доступа. В настоящее время поддерживаются только TABLE и INDEX.

функция_обработчик

В аргументе функция_обработчик указывается имя ранее зарегистрированной функции, представляющей метод доступа (может быть дополнено схемой). Функция-обработчик должна объявляться как принимающая один аргумент типа internal, а тип результата зависит от типа метода доступа; для методов доступа типа TABLE это должен быть table_am_handler, а для INDEX — index_am_handler. API уровня C, который должна реализовывать эта функция-обработчик, меняется в зависимости от типа метода доступа. API табличных методов доступа описаны в главе Определение интерфейса метода доступа к таблице, а индексных — в главе Определение интерфейса метода доступа к индексу

Примеры

Создание метода доступа к индексу heptree с функцией-обработчиком heptree_handler:

CREATE ACCESS METHOD heptree TYPE INDEX HANDLER heptree_handler;

Совместимость

Команда CREATE ACCESS METHOD является расширением QHB.

См. также

DROP ACCESS METHOD, CREATE OPERATOR CLASS, CREATE OPERATOR FAMILY

CREATE AGGREGATE

CREATE AGGREGATE — определить новую агрегатную функцию

Синтаксис

CREATE [ OR REPLACE ] AGGREGATE имя ( [ режим_аргумента ] [ имя_аргумента ] тип_данных_аргумента [ , ... ] ) (
    SFUNC = функция_состояния,
    STYPE = тип_данных_состояния
    [ , SSPACE = размер_данных_состояния ]
    [ , FINALFUNC = функция_завершения ]
    [ , FINALFUNC_EXTRA ]
    [ , FINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ]
    [ , COMBINEFUNC = комбинирующая_функция ]
    [ , SERIALFUNC = функция_сериализации ]
    [ , DESERIALFUNC = функция_десериализации ]
    [ , INITCOND = начальное_условие ]
    [ , MSFUNC = функция_состояния_движ ]
    [ , MINVFUNC = обратная_функция_движ ]
    [ , MSTYPE = тип_данных_состояния_движ ]
    [ , MSSPACE = размер_данных_состояния_движ ]
    [ , MFINALFUNC = функция_завершения_движ ]
    [ , MFINALFUNC_EXTRA ]
    [ , MFINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ]
    [ , MINITCOND = начальное_условие_движ ]
    [ , SORTOP = оператор_сортировки ]
    [ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ]
)

CREATE [ OR REPLACE ] AGGREGATE имя ( [ [ режим_аргумента ] [ имя_аргумента ] тип_данных_аргумента [ , ... ] ]
                        ORDER BY [ режим_аргумента ] [ имя_аргумента ] тип_данных_аргумента [ , ... ] ) (
    SFUNC = функция_состояния,
    STYPE = тип_данных_состояния
    [ , SSPACE = размер_данных_состояния ]
    [ , FINALFUNC = функция_завершения ]
    [ , FINALFUNC_EXTRA ]
    [ , FINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ]
    [ , INITCOND = начальное_условие ]
    [ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ]
    [ , HYPOTHETICAL ]
)

или старый синтаксис:

CREATE [ OR REPLACE ] AGGREGATE имя (
    BASETYPE = базовый_тип,
    SFUNC = функция_состояния,
    STYPE = тип_данных_состояния
    [ , SSPACE = размер_данных_состояния ]
    [ , FINALFUNC = функция_завершения ]
    [ , FINALFUNC_EXTRA ]
    [ , FINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ]
    [ , COMBINEFUNC = комбинирующая_функция ]
    [ , SERIALFUNC = функция_сериализации ]
    [ , DESERIALFUNC = функция_десериализации ]
    [ , INITCOND = начальное_условие ]
    [ , MSFUNC = функция_состояния_движ ]
    [ , MINVFUNC = обратная_функция_движ ]
    [ , MSTYPE = тип_данных_состояния_движ ]
    [ , MSSPACE = размер_данных_состояния_движ ]
    [ , MFINALFUNC = функция_завершения_движ ]
    [ , MFINALFUNC_EXTRA ]
    [ , MFINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ]
    [ , MINITCOND = начальное_условие_движ ]
    [ , SORTOP = оператор_сортировки ]
)

Описание

Команда CREATE AGGREGATE определяет новую агрегатную функцию. CREATE OR REPLACE AGGREGATE либо определяет новую агрегатную функцию, либо заменяет определение уже существующей. Некоторые основные и часто используемые агрегатные функции включены в дистрибутив; они описаны в разделе Агрегатные функции. При определении новых типов или возникновении потребности в агрегатной функции, которая еще не предоставлена, можно получить требуемые объекты, используя CREATE AGGREGATE.

При замене существующего определения типы аргументов, тип результата и количество аргументов изменить нельзя. Кроме того, новое определение должно быть того же вида (обычный, сортирующий или гипотезирующий агрегат), что и старое.

Если задано имя схемы (например, CREATE AGGREGATE myschema.myagg ...), то агрегатная функция создается в указанной схеме. В противном случае — в текущей.

Агрегатная функция идентифицируется по имени, а также по типу (или типам) входных данных. Два агрегата в одной схеме могут иметь одно и то же имя, если они работают с разными типами входных данных. Имя и тип (или типы) входных данных агрегата также должны отличаться от имени и типа (или типов) входных данных простых функций в той же схеме. Это поведение идентично перегрузке обычных имен функций (см. CREATE FUNCTION).

Простая агрегатная функция состоит из одной или двух обычных функций: функции перехода состояния функция_состояния, и необязательная функция окончательного вычисления функция_завершения. Они используются следующим образом:

функция_состояния( внутреннее-состояние, следующие-значения-данных ) ---> следующее-внутреннее-состояние

функция_завершения( внутреннее-состояние ) ---> агрегатное-значение

QHB создает временную переменную типа данных тип_данных_состояния, чтобы сохранить текущее внутреннее состояние агрегата. В каждой входной строке вычисляется значение (или значения) агрегированных аргументов и вызывается функция перехода состояния с текущим значением состояния и новым значением (или значениями) аргумента для вычисления нового значения внутреннего состояния. После обработки всех строк однократно вызывается завершающая функция для вычисления возвращаемого агрегатом значения. Если нет завершающей функции, то просто возвращается значение конечного состояния.

Агрегатная функция может определить начальное условие, то есть начальное значение для значения внутреннего состояния. Это значение задается и сохраняется в базе данных в виде строки типа text, но оно должно быть допустимым внешним представлением константы типа данных переменной состояния. По умолчанию начальным значением состояния считается NULL.

Если функция перехода состояния объявлена «strict» (строгой), то она не может быть вызвана с входными данными NULL. С такой функцией перехода агрегатное выполнение ведет себя следующим образом. Строки с входными значениями NULL игнорируются (функция не вызывается, а предыдущее значение состояния сохраняется), и если начальное значение состояния равно NULL, то в первой строке со всеми входными значениями не NULL значение первого аргумента заменяется значением состояния, а функция перехода вызывается в каждой последующей строке со всеми входными значениями не NULL. Это удобно для реализации агрегатов, таких как max. Обратите внимание, что это поведение возможно только тогда, когда тип_данных_состояния совпадает с первым типом_данных_аргумента. Если эти типы отличаются, необходимо указать начальное условие не NULL или использовать нестрогую функцию перехода.

Если функция перехода состояния не является строгой, то она будет вызываться безусловно в каждой входной строке и должна сама обрабатывать вводимые значения и переменную состояния, равные NULL. Это позволяет разработчику агрегатной функции полностью управлять тем, как она воспринимает значения NULL.

Если функция завершения объявлена «strict» (строгой), то она не будет вызвана, если значение конечного состояния равно NULL; вместо этого будет автоматически возвращен результат NULL. (Разумеется, это всего лишь обычное поведение строгих функций.) В любом случае функция завершения имеет возможность возвращать результат NULL. Например, функция завершения для avg возвращает NULL, если определяет, что было обработано ноль строк

Иногда бывает полезно объявить функцию завершения как принимающую не только значение состояния, но и дополнительные параметры, соответствующие входным значениям агрегата. В основном это имеет смысл, если функция завершения является полиморфной и типа данных значения состояния было бы недостаточно, чтобы вывести тип результата. Эти дополнительные параметры всегда передаются как NULL (поэтому функция завершения не должна быть строгой, когда применяется опция FINALFUNC_EXTRA), но в остальном это обычные параметры. Функция завершения может, например, использовать get\_fn\_expr\_argtype, чтобы определить фактический тип аргумента в текущем вызове.

Агрегатная функция может дополнительно поддерживать режим движущегося агрегата, как описано в разделе Режим движущегося агрегата. Для этого необходимо указать следующие параметры: MSFUNC, MINVFUNC и MSTYPE, а также выборочно параметры MSSPACE, MFINALFUNC, MFINALFUNC_EXTRA, MFINALFUNC_MODIFY и MINITCOND. За исключением MINVFUNC, эти параметры работают как соответствующие параметры простого агрегата без начальной буквы M; они определяют отдельную реализацию агрегата, включающую функцию обратного перехода.

Если в список параметров добавлено указание ORDER BY, создается особый типа агрегата, называемый сортирующим агрегатом; с указанием HYPOTHETICAL создается гипотезирующий агрегат. Эти агрегаты работают над группами отсортированных значений в зависимости от порядка сортировки, так что спецификация порядка сортировки входных данных является неотъемлемой частью вызова. Кроме того, они могут иметь непосредственные аргументы, которые вычисляются единожды для всей процедуры агрегирования, а не для каждой входной строки. Гипотезирующие агрегаты — это подкласс сортирующих агрегатов, в котором часть непосредственных аргументов должна соответствовать столбцам агрегированных аргументов по количеству и типам данных. Это позволяет добавить значения этих непосредственных аргументов в набор агрегируемых строк в качестве дополнительной «гипотетической» строки.

Агрегатная функция может дополнительно поддерживать частичное агрегирование, как описано в разделе Частичная агрегация. Для этого необходимо задать параметр COMBINEFUNC. Если в качестве типа_данных_состояния выбран internal, обычно также указывают параметры SERIALFUNC и DESERIALFUN, чтобы было возможно параллельное агрегирование. Обратите внимание, что для возможности параллельной агрегации агрегат также должен быть помечен как PARALLEL SAFE .

Агрегаты, работающие подобно MIN и MAX, иногда можно оптимизировать, заменив сканирование каждой входной строки обращением по индексу. Если агрегат подлежит такой оптимизации, обозначьте это, указав оператор сортировки. Основное требование состоит в том, что агрегат должен выдавать первый элемент по порядку сортировки, задаваемому этим оператором, другими словами:

SELECT agg(col) FROM tab;

должно быть эквивалентно:

SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;

Дополнительно предполагается, что агрегат игнорирует значения NULL и возвращает NULL тогда и только тогда, когда строки со значениями не NULL отсутствуют. Обычно оператор < является подходящим оператором сортировки для MIN, а > — для MAX. Обратите внимание, что оптимизация ни на что не повлияет, если указанный оператор не является участником стратегии «меньше, чем» или «больше, чем» в классе операторов индекса типа B-дерево.

Чтобы иметь возможность создать агрегатную функцию, необходимо иметь право USAGE для типов аргументов, типа (или типов) состояния и типа результата, а также право EXECUTE для вспомогательных функций.

Параметры

имя

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

режим_аргумента

Режим аргумента: IN или VARIADIC (Агрегатные функции не поддерживают выходные аргументы (OUT).) Если этот параметр не указан, значение по умолчанию равно IN. Только последний аргумент может быть помечен как VARIADIC.

имя_аргумента

Название аргумента. В настоящее время используется только для целей документации. Если опущено, соответствующий аргумент будет безымянным.

тип_данных_аргумента

Тип входных данных, с которым работает эта агрегатная функция. Чтобы создать агрегатную функцию без аргументов, вместо списка определений аргументов напишите *. (Примером такого агрегата является count(*).)

базовый_тип

В старом синтаксисе для CREATE AGGREGATE тип входных данных задается параметром базовый_тип, а не записывается после имени агрегата. Обратите внимание, что этот синтаксис допускает только один входной параметр. Чтобы определить агрегатную функцию без аргументов с помощью этого синтаксиса, в качестве значения базовый_тип нужно указать ANY (не *). Создать сортирующий агрегат старый синтаксис не позволяет.

функция_состояния

Имя функции перехода состояния, вызываемой для каждой входной строки. Для обычных агрегатных функций с N аргументами функция_состояния должна принимать N+1 аргумент, первый должен иметь тип тип_данных_состояния, а остальные — соответствовать заявленному типу входных данных агрегата. Функция должна возвращать значение типа тип_данных_состояния. Эта функция принимает текущее значение состояния и текущее значение (или значения) входных данных, а возвращает следующее значение состояния.

В сортирующих (в том числе гипотезирующих) агрегатах функция перехода состояния получает только текущее значение состояния и агрегируемые аргументы, а не непосредственные аргументы. В остальном это одно и то же.

тип_данных_состояния

Тип данных значения состояния агрегатной функции.

размер_данных_состояния

Приблизительный средний размер (в байтах) значения состояния агрегата. Если этот параметр опущен или равен нулю, используется оценка по умолчанию, определяемая по типу тип_данных_состояния. Планировщик использует это значение для оценки памяти, необходимой для агрегатного запроса с группировкой. Планировщик может применить агрегирование по хешу для такого запроса, только если хеш-таблица, судя по оценке, поместится в work_mem; таким образом, при больших значениях этого параметра агрегирование по хешу будет менее желательным.

функция_завершения

Имя функции завершения, вызываемой для вычисления результата агрегатной функции после обработки всех входных строк. Для нормального агрегата эта функция должна принимать один аргумент типа тип_данных_состояния. Возвращаемый тип данных агрегата определяется как возвращаемый тип этой функции. Если функция_завершения не указана, то значение конечного состояния используется в качестве результата агрегирования, а типом возвращаемого значения является тип_данных_состояния.

В сортирующих (в том числе гипотезирующих) агрегатах функция завершения получает не только значение конечного состояния, но и значения всех непосредственных аргументов.

Если команда содержит указание FINALFUNC_EXTRA, то в дополнение к конечному значению состояния и всем непосредственным аргументам конечная функция получает дополнительные значения NULL, соответствующие обычным (агрегированным) аргументам агрегата. Это в основном полезно для обеспечения правильного определения типа результата агрегата при определении полиморфной агрегатной функции.

FINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE }

Этот параметр указывает, является ли функция завершения чистой функцией, которая не изменяет свои аргументы. Это свойство функции передает значение READ_ONLY; другие два значения указывают, что она может изменить значение состояния перехода. Подробнее это рассматривается в Примечаниях. По умолчанию значение равно READ_ONLY, за исключением сортирующих агрегатов (для них значение по умолчанию — READ_WRITE).

комбинирующая_функция

Функция комбинирующая_функция может быть дополнительно задана, чтобы разрешить агрегатной функции поддерживать частичную агрегацию. Если задается, комбинирующая_функция должна комбинировать два значения типа_данных_состояния, каждое из которых содержит результат агрегации по некоторому подмножеству входных значений, чтобы получить новое значение типа_данных_состояния, представляющее результат агрегирования по обоим наборам данных. Эту функцию можно рассматривать как функцию_состояния, которая вместо обработки отдельной входной строки и добавления ее данных в текущее агрегируемое состояние включает еще одно агрегированное состояние в текущее.

Указанная комбинирующая_функция должна быть объявлена как принимающая два аргумента с типом тип_данных_состояния и возвращающая значение с типом тип_данных_состояния. Эта функция дополнительно может быть объявлена «строгой». В этом случае функция не будет вызываться, когда одно из входных состояний равно NULL; в качестве корректного результата будет выдано другое состояние.

Для агрегатных функций, у которых тип_данных_состояния — internal, комбинирующая_функция не должна быть «строгой». В этом случае комбинирующая_функция должна проверять, что состояния NULL обрабатываются правильно и что возвращаемое состояние правильно хранится в контексте памяти агрегирования.

функция_сериализации

Агрегатная функция, у которой тип_данных_состояния — internal, может участвовать в параллельном агрегировании только в том случае, если для нее задана функция_сериализации, которая должна сериализовать агрегатное состояние в значение bytea для передачи другому процессу. Эта функция должна принимать один аргумент типа internal и возвращать тип bytea. Также при этом нужно задать соответствующую функцию_десериализации.

функция_десериализации

Десериализует ранее сериализованное агрегатное состояние обратно в тип_данных_состояния. Эта функция должна принимать два аргумента типа bytea и internal и возвращать результат типа internal. (Примечание: второй аргумент internal не используется, но требуется из соображений безопасности для типа)

начальное_условие

Начальное значение переменной состояния. Это должна быть строковая константа в форме, принятой для данного типа данных тип_данных_состояния. Если не указано, то начальным значением состояния будет NULL.

функция_состояния_движ

Имя функции прямого перехода состояния, которая будет вызываться для каждой входной строки в режиме движущегося агрегата. Это такая же функция, как и обычная функция перехода, за исключением того, что ее первый аргумент и результат имеют тип тип_данных_состояния_движ, который может отличаться от типа тип_данных_состояния.

обратная_функция_движ

Имя функции обратного перехода состояния, которая будет использоваться в режиме движущегося агрегата. Эта функция имеет те же типы аргументов и результатов, что и функция_состояния_движ, но используется для удаления значения из текущего состояния агрегата, а не для добавления к нему значения. Функция обратного перехода должна иметь ту же характеристику строгости, что и функция прямого перехода состояния.

тип_данных_состояния_движ

Тип данных для значения состояния агрегата при использовании режима движущегося агрегата.

размер_данных_состояния_движ

Приблизительный средний размер (в байтах) значения состояния агрегата при использовании режима движущегося агрегата. Он имеет то же значение, что и размер_данных_состояния.

функция_завершения_движ

Имя функции завершения, вызываемой для вычисления результата агрегата после обработки всех входных строк при использовании режима движущегося агрегата. Она работает так же, как функция_завершения, за исключением того, что тип ее первого аргумента тип_данных_состояния_движ, а дополнительными пустыми аргументами управляет параметр MFINALFUNC_EXTRA. Тип результата, который определяет функция_завершения_движ или тип_данных_состояния_движ, должен совпадать с типом результата обычной реализации агрегата.

MFINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE }

Этот параметр подобен FINALFUNC_MODIFY, но описывает поведение функции завершения для движущегося агрегата.

начальное_условие_движ

Начальное значение переменной состояния в режиме движущегося агрегата. Оно применяется так же, как начальное_условие.

оператор_сортировки

Связанный оператор сортировки для реализации агрегатов, подобных MIN или MAX. Это просто имя оператора (может быть дополнено схемой). Предполагается, что оператор имеет те же типы входных данных, что и агрегат (который должен быть обычным и иметь один аргумент).

PARALLEL = { SAFE | RESTRICTED | UNSAFE }

Указания PARALLEL SAFE, PARALLEL RESTRICTED и PARALLEL UNSAFE имеют те же значения, что и в CREATE FUNCTION. Агрегатная функция не будет считаться распараллеливаемой, если она помечена как PARALLEL UNSAFE (что является значением по умолчанию!) или PARALLEL RESTRICTED. Обратите внимание, что планировщик не обращает внимание на допустимость распараллеливания вспомогательных функций агрегата, а учитывает только характеристику самой агрегатной функции.

HYPOTHETICAL

Только для сортирующих агрегатов. Этот флаг указывает, что статистические аргументы должны обрабатываться в соответствии с требованиями для агрегатов с гипотетическими наборами: то есть последние несколько непосредственных аргументов должны соответствовать типам данных агрегатных (WITHIN GROUP) аргументов. Флаг HYPOTHETICAL не влияет на поведение во время выполнения, а учитывается только при разрешении типов данных и правил сортировки для аргументов агрегата во время синтаксического анализа.

Параметры CREATE AGGREGATE могут быть записаны в любом порядке, а не только в показанном выше.

Примечания

В параметрах, определяющих имена вспомогательных функций, при необходимости можно записать имя схемы, например SFUNC = public.sum. Однако не записывайте там типы аргументов — типы аргументов вспомогательных функций определяются из других параметров.

Обычно предполагается, что функции QHB являются настоящими функциями, которые не изменяют свои входные значения. Тем не менее, функциям агрегатного перехода, используемым в контексте агрегатной функции, разрешено действовать хитрее и изменять аргумент переходного состояния на лету. Это может обеспечить существенные преимущества производительности по сравнению с созданием каждый раз новой копии переходного состояния.

Подобным образом, хотя функция завершения агрегата обычно не должна изменять свои входные значения, иногда может быть полезно допустить изменение аргумента со значением переходного состояния. Такое поведение должно быть объявлено с помощью параметра FINALFUNC_MODIFY. Его значение READ_WRITE указывает, что функция завершения модифицирует переходное состояние неопределенным образом. Это значение предотвращает использование агрегата в качестве оконной функции, а также предотвращает объединение переходных состояний при вызове агрегатов с одинаковыми входными данными и функциями перехода. Значение SHAREABLE указывает, что функция перехода не может быть применена после функции завершения, но над значением конечного переходного состояния могут быть выполнены несколько вызовов функции завершения. Это значение предотвращает использование агрегата в качестве оконной функции, но позволяет объединять переходные состояния. (То есть оптимизация в данном случае заключается не в многократном применении одной и той же функции завершения, а в применении различных функции завершения к одному и тому же конечному переходному состоянию. Это разрешено до тех пор, пока ни одна из функций завершения не будет отмечена READ_WRITE.)

Если агрегатная функция поддерживает режим движущегося агрегата, это повысит эффективность вычислений, когда агрегат будет использоваться в качестве оконной функции для окна с движущимся началом рамки (то есть когда начало определяется не как UNBOUNDED PRECEDING). По существу, функция прямого перехода добавляет входные значения к состоянию агрегата, когда они появляются в оконной рамке, а функция обратного перехода снова вычитает их, когда они покидают рамку. Поэтому, вычитаются значения в том же порядке, в каком добавлялись. Всякий раз, когда вызывается обратная функция перехода, она, таким образом, получит самое раннее добавленное, но еще не удаленное значение (или значения) аргумента. Функция обратного перехода может предполагать, что по крайней мере одна строка останется в текущем состоянии после удаления самой старой строки. (В противном случае механизм функции окна просто запускает новую агрегацию, а не использует обратную функцию перехода.)

Функция прямого перехода движущегося агрегата не может возвращать NULL в качестве нового значения состояния. Если функция обратного перехода возвращает значение NULL, это показывает, что обратная функция не может произвести обратное вычисление для этих конкретных данных, и поэтому вычисление агрегата следует выполнить заново от начальной позиции текущей рамки. Это соглашение позволяет использовать режим движущегося агрегата в ситуациях, в которых обратный расчет состояния производить непрактично.

Агрегатную функцию можно использовать с движущимися рамками и без реализации движущегося агрегата, но QHB будет заново агрегировать все данные при каждом перемещении начала рамки. Обратите внимание, что независимо от того, поддерживает ли агрегатная функция режим движущегося агрегата, QHB может обойтись без повторных вычислений при сдвиге конца рамки; это делается путем продолжения добавления новых значений в состояние агрегата. Именно поэтому использование агрегата в качестве оконной функции требует, чтобы функция завершения была только читающей: она не должна повредить значение состояния агрегата, чтобы агрегация могла быть продолжена даже после получения значения результата агрегирования для одного набора строк в определенной рамке.

Синтаксис сортирующих агрегатных функций позволяет указать VARIADIC и в последнем непосредственном параметре, и в последнем агрегатном (WITHIN GROUP). Тем не менее, текущая реализация ограничивает двоякое использование VARIADIC. Во-первых, в сортирующих агрегатных функциях можно использовать только VARIADIC "any", но не другие типы переменных массивов. Во-вторых, если последним непосредственным аргументом является VARIADIC "any", тогда может быть только один агрегированный параметр, и он также должен быть VARIADIC "any". (В представлении, используемом в системных каталогах, эти два параметра объединяются в один пункт VARIADIC "any", так как в pg_proc нельзя представить функцию с несколькими параметрами VARIADIC.) Если агрегат является гипотезирующим, то непосредственные аргументы, соответствующие параметру VARIADIC "any", будут гипотетическими; любые предыдущие параметры представляют дополнительные непосредственные аргументы, которые могут не соответствовать агрегатным.

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

Частичная (в том числе параллельная) агрегация в настоящее время не поддерживается для сортирующих агрегатных функций. Также она никогда не будет применяться для агрегатных вызовов с предложениями DISTINCT или ORDER BY, так как они по природе своей не могут быть реализованы с частичным агрегированием.

Примеры

См. раздел Пользовательские агрегаты.

Совместимость

Команда CREATE AGGREGATE является расширением QHB. Стандарт SQL не предусматривает создание пользовательских агрегатных функций.

См. также

ALTER AGGREGATE, DROP AGGREGATE

CREATE CAST

CREATE CAST — определить новое приведение

Синтаксис

CREATE CAST (исходный_тип AS целевой_тип)
    WITH FUNCTION имя_функции [ (тип_аргумента [, ...]) ]
    [ AS ASSIGNMENT | AS IMPLICIT ]

CREATE CAST (исходный_тип AS целевой_тип)
    WITHOUT FUNCTION
    [ AS ASSIGNMENT | AS IMPLICIT ]

CREATE CAST (исходный_тип AS целевой_тип)
    WITH INOUT
    [ AS ASSIGNMENT | AS IMPLICIT ]

Описание

Команда CREATE CAST определяет новое приведение. Приведение указывает, как выполнить преобразование из одного типа в другой. Например,

SELECT CAST(42 AS float8);

преобразует целочисленную константу 42 в тип float8 путем вызова ранее указанной функции, в данном случае float8(int4). (Если подходящее приведение не было определено, преобразование завершается ошибкой.)

Два типа могут быть двоично-сводимыми, что означает, что преобразование может быть выполнено «бесплатно» без вызова какой-либо функции. Для этого требуется, чтобы соответствующие значения имели одинаковое внутреннее представление. Например, типы text и varchar являются двоично-сводимыми в обе стороны. Отношение двоичной сводимости не обязательно симметрично. Например, приведение типа xml к типу text в настоящей реализации может быть выполнено бесплатно, но обратное направление требует функции, которая выполняет, по крайней мере, проверку синтаксиса. (Два типа, двоично-сводимые в обе стороны, также называются двоично-совместимыми.)

Приведение можно определить как преобразование ввода/вывода, используя указание WITH INOUT. Приведение преобразования ввода-вывода выполняется путем вызова выходной функции исходного типа данных и передачи результирующей строки во входную функцию целевого типа данных. Во многих распространенных случаях эта функция позволяет избежать необходимости писать отдельную функцию приведения для преобразования. Приведение преобразования ввода-вывода действует так же, как и обычное приведение на основе функций; отличается только реализация.

По умолчанию приведение может быть вызвано только явным запросом приведения, то есть применением конструкции CAST(x AS имя\_типа) или x::имя\_типа.

Если приведение помечено AS ASSIGNMENT, его можно вызывать неявно, присваивая значение столбцу с целевым типом данных. Например, если foo.f1 — столбец типа text, то команда:

INSERT INTO foo (f1) VALUES (42);

будет допустимой, если приведение типа integer к text помечено AS ASSIGNMENT, и не будет в противном случае. (Для описания такого типа приведений обычно используют термин приведение присваивания.)

Если приведение помечено AS IMPLICIT, то оно может быть вызван неявно в любом контексте, будь то присваивание или внутреннее преобразование в выражении. (Для описания такого типа приведений обычно используют термин неявное приведение.) Например, рассмотрим этот запрос:

SELECT 2 + 4.0;

Сначала анализатор помечает константы как относящиеся к типам integer и numeric соответственно. Но в системных каталогах нет оператора integer + numeric, но есть оператор numeric + numeric Поэтому запрос будет выполнен успешно, если доступно преобразование типа integer к numeric с пометкой AS IMPLICIT — что на самом деле так и есть. Анализатор запроса применит неявное приведение и обработает запрос, как если бы тот был записан в виде

SELECT CAST ( 2 AS numeric ) + 4.0;

Надо сказать, что системные каталоги также содержат приведение типа numeric к integer. Если бы это приведение тоже было помечено AS IMPLICIT (на самом деле это не так), то анализатор столкнулся бы с выбором между приведенным выше вариантом и приведением константы numeric к типу integer с последующим применением оператора integer + integer. Не имея никаких знаний о том, какой выбор предпочесть, анализатор объявил бы запрос неоднозначным. Именно потому, что только одно из двух приведений неявно, анализатор приходит к пониманию, что предпочтительным является преобразование выражения numeric-и-integer в numeric; отдельных встроенных сведений об этом нет.

Разумно проявлять консерватизм в отношении того, объявлять ли приведения неявными. Переизбыток неявных способов приведения может привести к тому, что QHB выберет неожиданные интерпретации команд или даже не сможет их выполнить из-за наличия нескольких возможных интерпретаций. Хорошее эмпирическое правило состоит в том, чтобы сделать приведение неявно вызываемым только для сохраняющих информацию преобразований между типами в одной и той же категории типов. Например, приведение int2 к int4 разумно сделать неявным, но приведение float8 к int4, возможно, лучше сделать только приведением присваивания. Приведения типов разных категорий, например text к int4, лучше делать только явными.

Примечание
Иногда по соображениям удобства использования или соответствия стандартам необходимо обеспечить несколько неявных приведений для набора типов, что приводит к неоднозначности, которую нельзя избежать, как указано выше. Чтобы анализатор запроса мог обеспечить желаемое поведение в таких случаях, он дополнительно принимает во внимание категории типов и предпочитаемые типы. Более подробную информацию см. в разделе CREATE TYPE.

Чтобы создать приведение, нужно быть владельцем одного (исходного или целевого) типа данных и иметь право USAGE для другого типа. Чтобы создать двоично-сводимое приведение, нужно быть суперпользователем. (Это ограничение введено потому, что преобразование данных с ошибочным двоичным сведением легко может вызвать сбой сервера.)

Параметры

исходный_тип

Имя исходного типа данных для приведения.

целевой_тип

Имя целевого типа данных для приведения.

имя_функции[(тип_аргумента [, ...])]

Функция, используемая для выполнения приведения. Имя функции может быть дополнено схемой. В ином случае для поиска функции просматривается путь поиска. Тип данных результата функции должен соответствовать целевому типу приведения. Аргументы функции рассматриваются ниже. Если список аргументов не указан, имя функции должно быть уникальным в своей схеме.

WITHOUT FUNCTION

Указывает, что исходный тип является приводимым к целевому на двоичном уровне, так что функция для приведения не требуется.

WITH INOUT

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

AS ASSIGNMENT

Указывает, что приведение может быть вызвано неявно в контекстах присвоения.

AS IMPLICIT

Указывает, что приведение может быть вызвано неявно в любом контексте.

Функции, реализующие приведение, могут иметь от одного до трех аргументов. Тип первого аргумента должен быть идентичным или двоично-сводимым к исходному типу приведения. Второй аргумент, если он присутствует, должен быть типом integer; он получает модификатор типа, связанный с типом назначения, или -1, если их вообще нет. Третий аргумент, если он присутствует, должен иметь тип boolean; если приведение является явным приведением, в нем передается true; иначе false. (Как ни странно, стандарт SQL требует различного поведения для явных и неявных приведений в некоторых случаях. Этот аргумент предназначен для функций, которые должны реализовывать такие приведения. Однако создавать собственные типы данных, для которых это имело бы значение, не рекомендуется.)

Возвращаемый тип функции приведения должен быть идентичным или двоично-сводимым к целевому типу приведения.

Обычно приведение должно иметь различные типы исходных и целевых данных. Однако разрешается объявлять приведение с одинаковыми исходными и целевыми типами, если функция, реализующая преобразование, имеет более одного аргумента. Это используется для представления в системных каталогах функций, сводящих разные длины типов. Реализующая такое приведение функция используется для сведения значения типа к значению с определенным модификатором, заданному его вторым аргументом.

Если приведение имеет различные исходные и целевые типы, а также функцию, принимающую более одного аргумента, то оно поддерживает преобразование из одного типа в другой и сведение к нужной длине может выполняться за один шаг. Если же соответствующей записи не находится, приведение к типу с определенным модификатором включает два этапа приведения: один для преобразования между типами данных и второй для применения модификатора.

Приведение типа домена или к типу домена в настоящее время не осуществляется. При попытке выполнить такое приведение, вместо этого выполняется приведение, связанное с базовым типом домена.

Примечания

Для удаления приведений, созданных пользователем, применяется DROP CAST.

Помните, что если вы хотите иметь возможность конвертировать типы в обе стороны, вам нужно явно объявить приведение в обе стороны.

Обычно нет необходимости создавать приведения между пользовательскими типами и стандартными строковыми типами (text, varchar и char(n), а также пользовательскими типами, относящимися к категории строковых). QHB предоставляет для этого автоматические преобразования ввода-вывода. Автоматические приведения к строковым типам обрабатываются как приведения присваивания, тогда как автоматические приведения от строковых типов могут быть только явными. Это поведение можно переопределить, объявив свое собственное приведение, чтобы заменить автоматическое, но обычно это нужно, только чтобы сделать вызов более удобным, чем стандартное присваивание или явное указание. Другая возможная причина заключается в желании, чтобы преобразование вело себя иначе, чем функция ввода-вывода; но это выглядит достаточно странным, поэтому стоит дважды подумать, является ли это хорошей идеей. (На самом деле у небольшого количества встроенных типов имеются подобные специфические приведения, в основном, из-за требований стандарта SQL.)

Несмотря на то, что это не обязательно, рекомендуется продолжать следовать старому соглашению именования функций, реализующих приведение, по целевому типу данных. Многие пользователи привыкли иметь возможность приводить типы данных, записывая его в стиле функций, т. е. имя_типа(x). Эта запись на самом деле ни что иное, как вызов функции, реализующей приведение; такой вызов не воспринимается как именно приведение. Если не называть функции преобразования, следуя этому соглашению, это может удивить пользователей. Поскольку QHB позволяет перегружать одно и то же имя функции с различными типами аргументов, ничто не мешает тому, чтобы иметь несколько функций преобразования из разных типов, названных по имени этого целевого типа.

Примечание
На самом деле в предыдущем абзаце содержится некоторое упрощение: есть два случая, в которых конструкция вызова функции будет рассматриваться как запрос приведения без сопоставления его с фактической функцией. Если вызову функции имя(x) в точности не соответствует существующая функция, но имеется тип данных имя и в pg_cast есть двоично-сводимое приведение типа x к этому типу, такой вызов будет воспринят как приведение. Это исключение введено, чтобы двоично-сводимое приведение можно было вызвать, используя синтаксис функций, несмотря на то, что никакой функции преобразования у него нет. Аналогично, если запись приведения в pg_cast отсутствует, но приведение было бы к или от строкового типа, вызов будет истолкован как приведение преобразования ввода-вывода. Это исключение позволяет вызывать приведения преобразования ввода-вывода, используя синтаксис вызова функции.

Примечание
Существует также исключение и из этого исключения: преобразование ввода/вывода из составных типов в строковые нельзя вызвать, используя синтаксис вызова функции; его необходимо записать как явное приведение (используя CAST или запись ::) Это исключение было добавлено, так как после введения автоматически предоставляемых приведений преобразования ввода-вывода было обнаружено, что такое приведение слишком легко вызвать случайно, намереваясь на самом деле дать ссылку на столбец или функцию.

Примеры

Создание приведения присваивания типа bigint к типу int4 с помощью функции int4(bigint):

CREATE CAST (bigint AS int4) WITH FUNCTION int4(bigint) AS ASSIGNMENT;

(Это приведение уже предопределено в системе.)

Совместимость

Команда CREATE CAST соответствует стандарту SQL, за исключением того, что в стандарте ничего не говорится о двоично-сводимых типах и дополнительных аргументах реализующих функций. Указание AS IMPLICIT тоже является расширением QHB.

См. также

CREATE FUNCTION, CREATE TYPE, DROP CAST

CREATE COLLATION

CREATE COLLATION — определить новое правило сортировки

Синтаксис

CREATE COLLATION [ IF NOT EXISTS ] имя (
    [ LOCALE = локаль, ]
    [ LC_COLLATE = категория_сортировки, ]
    [ LC_CTYPE = категория_типов_символов, ]
    [ PROVIDER = провайдер, ]
    [ DETERMINISTIC = логическое_значение, ]
    [ VERSION = версия ]
)
CREATE COLLATION [ IF NOT EXISTS ] имя FROM существующее_правило

Описание

Команда CREATE COLLATION определяет новое правило сортировки, используя указанные параметры локали операционной системы или копируя существующие правила сортировки.

Чтобы создать правило сортировки, необходимо иметь право CREATE в целевой схеме.

Параметры

IF NOT EXISTS

Не выдавать ошибку, если правило сортировки с тем же именем уже существует. В этом случае будет выдано соответствующее уведомление. Обратите внимание, что нет никакой гарантии, что существующее правило сортировки как-то соотносится с тем, которое было бы создано.

имя

Имя правила сортировки. Может быть дополнено схемой (если это не так, правило сортировки определяется в текущей схеме). Имя правила сортировки должно быть уникальным в этой схеме. (Системные каталоги могут содержать правила сортировки с тем же именем для других кодировок, но они игнорируются, если кодировка базы данных не соответствует.)

локаль

Это ярлык для одновременной настройки LC_COLLATE и LC_CTYPE. Если задать этот параметр, то нельзя будет указать ни один из этих параметров отдельно.

категория_сортировки

Использовать указанную локаль операционной системы в качестве категории локали LC_COLLATE.

категория_типов_символов

Использовать указанную локаль операционной системы в качестве категории локали LC_CTYPE.

провайдер

Указывает провайдера, который будет использоваться для служб локали, связанных с данным правилом сортировки. Возможные значения: icu, libc. По умолчанию значение равно libc. Доступные варианты зависят от параметров операционной системы и сборки.

DETERMINISTIC

Указывает, должны ли правила сортировки использовать детерминированные сравнения. Значение по умолчанию равно true(использовать детерминированные сравнения). При детерминированном сравнении строки, которые не равны по байтам, считаются неравными, даже если они считаются логически равными при сравнении. QHB решает вопрос их равенства, используя байтовое сравнение. Недетерминированное сравнение может сделать правило, к примеру, независимым от ударения или регистра символов. Для этого нужно выбрать подходящий вариант LC_COLLATE и сделать это правило сортировки недетерминированным.

Недетерминированные правила сортировки поддерживаются только с провайдером ICU.

версия

Указывает строку версии, сохраняемую с правилом сортировки. Обычно ее не следует задавать, что приведет к вычислению версии из фактической версии правила сортировки, предоставленного операционной системой. Эта опция предназначена к использованию qhb\_upgrade для копирования версии из существующей установки.

Что делать при несовпадении версий правил сортировки, описано в разделе ALTER COLLATION.

существующее_правило

Имя копируемого существующего правила сортировки. Новое правило сортировки будет иметь те же свойства, что и существующее, но будет независимым объектом.

Примечания

CREATE COLLATION устанавливает блокировку SHARE ROW EXCLUSIVE в системном каталоге pg_collation. Эта блокировка конфликтует с такой же, поэтому в один момент времени может выполняться только одна команда CREATE COLLATION.

Используйте DROP COLLATION для удаления пользовательских правил сортировки.

Дополнительную информацию о создании правил сортировки см. в разделе Создание новых правил сортировки.

При использовании провайдера libc для правил сортировки локаль должна быть применима к текущей кодировке базы данных. См. CREATE DATABASE.

Примеры

Создание правила сортировки из локали операционной системы fr_FR.utf8 (предполагается, что кодировка текущей базы данных — UTF8):

CREATE COLLATION french (locale = 'fr_FR.utf8');

Создание правила сортировки с порядком, принятым в Германии для телефонных книг, с использованием провайдера ICU:

CREATE COLLATION german_phonebook (provider = icu, locale = 'de-u-co-phonebk');

Создание правила сортировки из уже существующего:

CREATE COLLATION german FROM "de_DE";

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

Совместимость

В стандарте SQL есть команда CREATE COLLATION, но она ограничена копированием существующих правил сортировки. Синтаксис для создания новых правил сортировки является расширением QHB.

См. также

ALTER COLLATION, DROP COLLATION

CREATE CONVERSION

CREATE CONVERSION — определить новую перекодировку

Синтаксис

CREATE [ DEFAULT ] CONVERSION имя
    FOR исходная_кодировка TO целевая_кодировка FROM имя_функции

Описание

Команда CREATE CONVERSION определяет новую перекодировку набора символов. Кроме того, перекодировки, отмеченные как DEFAULT, могут использоваться для автоматической перекодировки между клиентом и сервером. Для этого необходимо определить две перекодировки — из кодировки A в B и из кодировки B в A.

Чтобы создать перекодировку, необходимо иметь право EXECUTE для реализующей функции и право CREATE в целевой схеме.

Параметры

DEFAULT

Предложение DEFAULT указывает, что эта перекодировка является перекодировкой по умолчанию для заданной исходной кодировки в кодировку назначения. В схеме для пары перекодировок должна быть только одна перекодировка по умолчанию.

имя

Имя перекодировки (может быть дополнено схемой). Если схема не указана, перекодировка создается в текущей схеме. Имя перекодировки должно быть уникальным в пределах схемы.

исходная_кодировка

Имя исходной кодировки.

целевая_кодировка

Имя целевой кодировки.

имя_функции

Функция, используемая для выполнения перекодировки. Имя функции может быть дополнено схемой. Если это не так, для поиска функции просматривается путь поиска.

Функция должна иметь следующую сигнатуру:

conv_proc(
    integer,  -- идентификатор исходной кодировки
    integer,  -- идентификатор целевой кодировки
    cstring,  -- исходная строка (строка, завершающаяся 0, как в C)
    internal, -- целевая строка (заполняется строкой, завершающейся 0, как в C)
    integer   -- длина исходной строки
) RETURNS void;

Примечания

Как исходная, так и конечная кодировки должны отличаться от SQL_ASCII, поскольку поведение сервера в случаях использования «кодировки» SQL_ASCII является аппаратным.

Для удаления пользовательских перекодировок используйте команду DROP CONVERSION.

Права доступа, необходимые для создания перекодировки, могут быть изменены в будущих версиях.

Примеры

Создание перекодировки из кодировки UTF8 в LATIN1 с использованием функции myfunc:

CREATE CONVERSION myconv FOR 'UTF8' TO 'LATIN1' FROM myfunc;

Совместимость

Команда CREATE CONVERSION является расширением QHB. В стандарте SQL нет команды CREATE CONVERSION, но есть команда CREATE TRANSLATION, которая очень похожа по назначению и синтаксису.

См. также

ALTER CONVERSION, CREATE FUNCTION, DROP CONVERSION

CREATE DATABASE

CREATE DATABASE — создать новую базу данных

Синтаксис

CREATE DATABASE имя
    [ [ WITH ] [ OWNER [=] имя_пользователя ]
           [ TEMPLATE [=] шаблон ]
           [ ENCODING [=] кодировка ]
           [ LOCALE [=] локаль ]
           [ LC_COLLATE [=] категория_сортировки ]
           [ LC_CTYPE [=] категория_типов_символов ]
           [ TABLESPACE [=] имя_табличного_пространства ]
           [ ALLOW_CONNECTIONS [=] разр_подключения ]
           [ CONNECTION LIMIT [=] предел_подключений ]
           [ IS_TEMPLATE [=] это_шаблон ] ]

Описание

Команда CREATE DATABASE создает новую базу данных QHB.

Чтобы создать базу данных, нужно быть суперпользователем или иметь специальное право CREATEDB. См. раздел CREATE ROLE.

По умолчанию новая база данных будет создана путем клонирования стандартной системной базы данных template1. Другой шаблон можно задать указанием TEMPLATE имя. В частности, путем указания TEMPLATE template0 вы можете создать чистую базу данных, содержащую только стандартные объекты, предопределенные вашей версией QHB. Это полезно, если вы хотите избежать копирования любых локальных объектов установки, которые могли быть добавлены к template1.

Параметры

имя

Имя создаваемой базы данных.

имя_пользователя

Имя пользователя (роли), которому будет принадлежать новая база данных, или DEFAULT, чтобы использовать значение по умолчанию (а именно, пользователя, выполняющего команду). Чтобы создать базу данных с другой ролью-владельцем, необходимо быть прямым или косвенным членом этой роли или суперпользователем.

шаблон

Имя шаблона, из которого будет создана новая база данных, или DEFAULT, чтобы использовать шаблон по умолчанию (template1).

кодировка

Кодировка набора символов для использования в новой базе данных. Укажите строковую константу (например, ’SQL_ASCII’), или целочисленный номер кодировки, или DEFAULT, чтобы использовать кодировку по умолчанию (а именно, кодировку шаблона базы данных). Наборы символов, поддерживаемые сервером QHB, описаны в разделе Управление правилами сортировки. Дополнительные ограничения см. ниже.

локаль

Это краткая форма задания двух параметров, LC_COLLATE и LC_TYPE, сразу. При указании этой формы нельзя задать данные параметры по отдельности.

Совет
Другие параметры локали lc_messages, lc_monetary, lc_numeric и lc_time задаются не на уровне базы данных и не устанавливаются этой командой. Если хотите изменить их значения по умолчанию для конкретной базы данных, можно использовать ALTER DATABASE ... SET.

категория_сортировки

Порядок сортировки (LC_COLLATE), который будет использоваться в новой базе данных. Это влияет на порядок сортировки, применяемый к строкам, например, в запросах с ORDER BY, а также на порядок, используемый в индексах текстовых столбцов. По умолчанию используется порядок сортировки шаблона базы данных. Дополнительные ограничения см. ниже.

категория_типов_символов

Классификация символов (LC_CTYPE), которая будет использоваться в новой базе данных. Это влияет на классификацию символов, например: строчные, заглавные, цифры и т. п. По умолчанию используется классификация символов шаблона базы данных. Дополнительные ограничения см. ниже.

имя_табличного_пространства

Имя табличного пространства, которое будет связано с новой базой данных, или DEFAULT, чтобы использовать табличное пространство шаблона базы данных. Это табличное пространство будет табличным пространством по умолчанию, используемым для объектов, созданных в этой базе данных. Дополнительную информацию см. в разделе CREATE TABLESPACE.

разрешение_подключения

Если установлено значение false, то никто не может подключиться к этой базе данных. По умолчанию установлено значение true, разрешающее подключения (за исключением случаев, ограниченных другими механизмами, такими как GRANT/REVOKE CONNECT).

предел_подключений

Предельное число одновременных подключений, которое может быть сделано к этой базе данных. -1 (по умолчанию) означает отсутствие ограничений.

это_шаблон

Если установлено значение true, то эта база данных может быть клонирована любым пользователем с правами CREATEDB; если стоит значение false (по умолчанию), то клонировать базу данных могут только суперпользователи или владелец базы данных.

Дополнительные параметры могут быть записаны в любом порядке, а не только в порядке, показанном выше.

Примечания

Команда CREATE DATABASE не может быть выполнена внутри блока транзакций.

Ошибки вида «не удалось инициализировать каталог базы данных», скорее всего, связаны с недостаточными разрешениями на каталог данных, переполненным диском или другими проблемами в файловой системе.

Для удаления базы данных используйте команду DROP DATABASE.

Программа createdb является программой-оболочкой этой команды, предусмотренной для удобства.

Параметры конфигурации уровня базы данных (заданные через ALTER DATABASE), а также разрешения уровня базы данных (заданные через GRANT) не копируются из шаблона базы данных.

Хотя можно скопировать не только template1, но и другую базу данных, указав ее имя в качестве шаблона, данная команда не предназначена (пока) для использования в качестве универсального средства вроде «COPY DATABASE». Основное ограничение заключается в том, что во время копирования никто не должен быть подключен к базе данных, указанной в качестве шаблона. CREATE DATABASE завершится ошибкой, если при запуске команды есть другие подключения к этой базе; в противном случае новые соединения с базой данных шаблона будут заблокированы до завершения процесса CREATE DATABASE. Дополнительную информацию см. в разделе Базы данных шаблонов.

Кодировка набора символов, заданная для новой базы данных, должна быть совместима с выбранными параметрами локали (LC_COLLATE и LC_CTYPE). Если выбрана локаль С (или POSIX), то разрешены все кодировки, но для других настроек локали есть только одна кодировка, которая будет работать должным образом. (В Windows, однако, кодировка UTF-8 может использоваться с любой локалью.) CREATE DATABASE позволяет суперпользователям указывать кодировку SQL_ASCII независимо от настроек локали, но этот вариант устарел и может привести к неправильному использованию функций символьной строки, если в базе хранятся данные, не совместимые с кодировкой локали.

Параметры кодировки и локали должны соответствовать параметрам шаблона базы данных, за исключением случаев, когда в качестве шаблона используется template0. Это связано с тем, что другие базы данных могут содержать данные, не соответствующие указанной кодировке, или индексы, порядок сортировки которых зависит от LC_COLLATE и LC_CTYPE. Копирование таких данных может привести к повреждению базы данных в соответствии с новыми настройками. Однако template0, как известно, не содержит никаких данных или индексов, которые могло бы затронуть.

Ограничение CONNECTION LIMIT применяется только приблизительно; если два новых сеанса начинаются примерно в то же время, когда для базы данных остается только один «слот» на подключение, не исключено, что оба сеанса завершатся неудачей. Кроме того, ограничение не применяется к суперпользователям или фоновым рабочим процессам.

Примеры

Создание базы данных:

CREATE DATABASE lusiadas;

Создание базы данных sales, принадлежащей пользователю salesapp, с табличным пространством по умолчанию salesspace:

CREATE DATABASE sales OWNER salesapp TABLESPACE salesspace;

Создание базы данных music с другой локалью:

CREATE DATABASE music
    LC_COLLATE 'sv_SE.utf8' LC_CTYPE 'sv_SE.utf8'
    TEMPLATE template0;

В этом примере предложение TEMPLATE template0 необходимо, только если указанная локаль отличается от локали в template1. (В противном случае явное указание локали является избыточным.)

Создание базы данных music2 с другой локалью и другой кодировкой символов:

CREATE DATABASE music2
    LC_COLLATE 'sv_SE.iso885915' LC_CTYPE 'sv_SE.iso885915'
    ENCODING LATIN9
    TEMPLATE template0;

Свойства кодировки должны соответствовать свойствам локали, иначе возникнет ошибка.

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

Совместимость

В стандарте SQL нет команды CREATE DATABASE. Базы данных эквивалентны каталогам, создание которых определяется реализацией.

См. также

ALTER DATABASE, DROP DATABASE

CREATE DOMAIN

CREATE DOMAIN — определить новый домен

Синтаксис

CREATE DOMAIN имя [ AS ] тип_данных
    [ COLLATE правило_сортировки ]
    [ DEFAULT выражение ]
    [ ограничение [ ... ] ]

Где ограничение может быть:

[ CONSTRAINT имя_ограничения ]
{ NOT NULL | NULL | CHECK (выражение) }

Описание

Команда CREATE DOMAIN создает новый домен. Домен по существу является типом данных с дополнительными ограничениями (ограничениями на допустимый набор значений). Пользователь, определяющий домен, становится его владельцем.

Если задано имя схемы (например, CREATE DOMAIN myschema.mydomain ...), то домен создается в указанной схеме, в противном случае — в текущей. Имя домена должно быть уникальным среди имен типов и доменов, существующих в его схеме.

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

Чтобы иметь возможность создать домен, необходимо иметь право USAGE для нижележащего типа.

Параметры

имя

Имя создаваемого домена (может быть дополнено схемой).

тип_данных

Нижележащий тип данных домена. Может включать определение массива с этим типом.

правило_сортировки

Необязательные правила сортировки для домена. Если параметры сортировки не указаны, используются правила сортировки по умолчанию для нижележащего типа данных. Указать COLLATE можно, только если нижележащий тип данных является сортируемым.

DEFAULT выражение

Предложение DEFAULT задает значение по умолчанию для столбцов, типом данных которых является этот домен. Значением является любое выражение без переменной (но подзапросы не разрешены). Тип данных выражения по умолчанию должен соответствовать типу данных домена. Если значение по умолчанию не указано, оно равно NULL.

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

CONSTRAINT имя_ограничения

Необязательное имя ограничения. Если не указано, система создает имя.

NOT NULL

Значения этого домена не могут быть равны NULL (но см. Примечания ниже).

NULL

Значения этого домена могут быть равны NULL. Это значение по умолчанию.

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

CHECK (выражение)

Предложения CHECK определяют ограничения целостности или проверки, которым должны удовлетворять значения домена. Каждое ограничение должно быть выражением, дающим логический результат. Проверяемое значение в этом выражении должно обозначаться ключевым словом VALUE. Выражения, выдающие значение TRUE или UNKNOWN, успешно выполняются. Если выражение выдает FALSE, появляется сообщение об ошибке и значение не может быть преобразовано в тип домена.

В настоящий момент выражения CHECK не могут содержать вложенные запросы и ссылаться на другие переменные, кроме VALUE.

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

Примечания

Ограничения домена, в частности, NOT NULL, проверяются при преобразовании значения в тип домена. Бывает, что даже несмотря на наличие такого ограничения, столбец, который номинально относится к типу домена, всё же можно прочитать как NULL. Например, это может произойти в запросе внешнего соединения, если столбец домена находится на стороне nullable внешнего соединения. Более тонкий пример:

INSERT INTO tab (domcol) VALUES ((SELECT domcol FROM tab WHERE false));

Пустой скалярный SELECT создаст значение NULL, которое считается доменным типом, поэтому к нему не применяется дополнительная проверка ограничений, и добавление будет выполнена успешно.

Очень трудно избежать подобных проблем из-за общего предположения SQL, что значение NULL является допустимым значением каждого типа данных. Поэтому наилучшим решением будет разработать ограничения домена таким образом, чтобы разрешить значение NULL, а затем применить ограничения NOT NULL для столбцов типа домена по мере необходимости, а не непосредственно для типа домена.

QHB предполагает, что условия ограничений CHECK неизменны, то есть они всегда будут давать один и тот же результат для одного и того же входного значения. Именно это предположение объясняет проверку ограничений CHECK только при первом преобразовании значения в тип домена, а не при каждом обращении к нему. (По сути, таким же образом обрабатываются ограничения CHECK для таблиц, как описано в разделе Контрольные ограничения.)

Примером распространенного способа нарушения данного предположения является ссылка на определяемую пользователем функцию в ограничении CHECK, а затем изменение поведения этой функции. QHB не запрещает этого, но и не заметит, если есть сохраненные значения типа домена, которые теперь нарушают ограничение CHECK. Это может привести к сбою последующего дампа и перезагрузки базы данных. Рекомендуемый способ обработки такого изменения состоит в том, чтобы удалить ограничение (используя ALTER DOMAIN), настроить определение функции и повторно добавить ограничение, тем самым перепроверив его относительно сохраненных данных.

Примеры

В этом примере создается тип данных us_postal_code (почтовый индекс США), который затем используется в определении таблицы. Для проверки значения на соответствие формату почтовых индексов США применяется проверка с регулярными выражениями:

CREATE DOMAIN us_postal_code AS TEXT
CHECK(
   VALUE ~ '^\d{5}$'
OR VALUE ~ '^\d{5}-\d{4}$'
);

CREATE TABLE us_snail_addy (
  address_id SERIAL PRIMARY KEY,
  street1 TEXT NOT NULL,
  street2 TEXT,
  street3 TEXT,
  city TEXT NOT NULL,
  postal us_postal_code NOT NULL
);

Совместимость

Команда CREATE DOMAIN соответствует стандарту SQL.

См. также

ALTER DOMAIN, DROP DOMAIN

CREATE EVENT TRIGGER

CREATE EVENT TRIGGER — определить новый событийный триггер

Синтаксис

CREATE EVENT TRIGGER имя
    ON событие
    [ WHEN переменная_фильтра IN (filter_value [, ... ]) [ AND ... ] ]
    EXECUTE { FUNCTION | PROCEDURE } имя_функции()

Описание

Команда CREATE EVENT TRIGGER создает новый событийный триггер. Всякий раз, когда происходит назначенное событие и удовлетворяется связанное с триггером условие WHEN (при наличии), функция триггера будет выполнена. Общие сведения о событийных триггерах см. в главе Триггеры событий. Пользователь, создающий событийный триггер, становится его владельцем.

Параметры

имя

Имя, назначаемое новому триггер. Это имя должно быть уникальным в пределах базы данных.

событие

Имя события, которое вызывает срабатывание триггера и вызов функции. Дополнительную информацию об именах событий см. в разделе Поведение триггера событий.

переменная_фильтра

Имя переменной, используемой для фильтрации событий. Это позволяет ограничить запуск триггера подмножеством случаев, в которых он поддерживается. В настоящее время единственным поддерживаемым значением параметра переменная_фильтра является TAG.

значение фильтра

Список значений для связанного параметра переменная_фильтра, для которых должен срабатывать триггер. Для переменной TAG это подразумевает список меток команд (например, 'DROP FUNCTION').

имя_функции

Заданная пользователем функция, которая заявлена как не принимающая аргументов и возвращающая тип event_trigger.

В синтаксисе CREATE EVENT TRIGGER ключевые слова FUNCTION и PROCEDURE эквивалентны, но указанная функция должна в любом случае быть функцией, а не процедурой. Использование здесь ключевого слова PROCEDURE поддерживается по историческим причинам и считается устаревшим.

Примечания

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

В однопользовательском режиме событийные триггеры выключены (см. раздел Процесс qhb). Если ошибочный событийный триггер заблокировал базу данных настолько, что его даже невозможно удалить, перезагрузите сервер в однопользовательском режиме, и вы сможете это сделать.

Примеры

Запрет на выполнение любой команды DDL:

CREATE OR REPLACE FUNCTION abort_any_command()
  RETURNS event_trigger
 LANGUAGE plpgsql
  AS $$
BEGIN
  RAISE EXCEPTION 'command % is disabled', tg_tag;
END;
$$;

CREATE EVENT TRIGGER abort_ddl ON ddl_command_start
   EXECUTE FUNCTION abort_any_command();

Совместимость

В стандарте SQL нет команды CREATE EVENT TRIGGER.

См. также

ALTER EVENT TRIGGER, DROP EVENT TRIGGER, CREATE FUNCTION

CREATE EXTENSION

CREATE EXTENSION — установить расширение

Синтаксис

CREATE EXTENSION [ IF NOT EXISTS ] имя_расширения
    [ WITH ] [ SCHEMA имя_схемы ]
             [ VERSION версия ]
             [ CASCADE ]

Описание

Команда CREATE EXTENSION загружает в текущую базу данных новое расширение. Уже загруженного расширения с тем же именем в базе быть не должно.

Загрузка расширения по существу сводится к запуску файла скрипта расширения. Скрипт обычно создает новые объекты SQL, например: функции, типы данных, операторы и методы поддержки индексов. CREATE EXTENSION дополнительно записывает идентификаторы всех созданных объектов, так что впоследствии их можно снова удалить, выполнив команду DROP EXTENSION.

Пользователь, запускающий CREATE EXTENSION, становится владельцем расширения для последующих проверок доступа, а также, как правило, владельцем всех объектов, созданных скриптом расширения.

Для загрузки расширения требуются те же права доступа, что и для создания составляющих его объектов. Для большинства расширений это означает, что необходимы права суперпользователя или владельца базы данных. Однако если расширение помечено в своем управляющем файле как trusted (доверенное), его может установить любой пользователь с правом CREATE в текущей базе данных. В этом случае сам объект расширения будет принадлежать вызывающему пользователю, но владельцем содержащихся в нем объектов будет первоначальный суперпользователь (если только скрипт расширения явно не назначит их владельцем вызывающего пользователя). Эта конфигурация дает вызывающему пользователю право удалить расширение, но не модифицировать отдельные объекты внутри него.

Параметры

IF NOT EXISTS

Не выдавать ошибку, если расширение с тем же именем уже существует. В этом случае будет выдано соответствующее уведомление. Обратите внимание, что нет никакой гарантии, что существующее расширение как-то соотносится с тем, которое было бы создано из доступного в данный момент скрипта.

имя_расширения

Имя устанавливаемого расширения. QHB создаст расширение, используя сведения из файла SHAREDIR/extension/имя_расширения.control.

имя схемы

Имя схемы, в которой будут устанавливаться объекты расширения (при условии, что расширение позволяет перемещать его содержимое). Названная схема уже должна существовать. Если этот параметр не указан, а в управляющем файле расширения также не указана схема, то используется текущая схема создания объекта по умолчанию.

Если в управляющем файле расширения задается параметр schema, то эту схему нельзя переопределить предложением SCHEMA. Обычно при указании предложения SCHEMA возникает ошибка, если эта схема конфликтует с параметром schema данного расширения. Однако если также задано предложение CASCADE, то в случае конфликта имя_схемы игнорируется. Заданное имя_схемы будет использоваться для установки любых необходимых расширений, если в их управляющем файле не указана schema.

Помните, что само расширение не считается частью какой-либо схемы: расширения имеют неполные имена, которые должны быть уникальными для всей базы данных. Но объекты, принадлежащие расширению, могут находиться внутри схем.

версия

Версия расширения. Ее можно записать в виде идентификатора или строкового значения. Версия по умолчанию указана в управляющем файле расширения.

CASCADE

Автоматически устанавливать все расширения, от которых зависит это расширение и которые еще не установлены. Их зависимости также устанавливаются автоматически, рекурсивно. Предложение SCHEMA (если указано) применяется ко всем расширениями, которые устанавливаются таким образом. Другие параметры инструкции не применяются к автоматически устанавливаемым расширениями; в частности, всегда выбираются их версии по умолчанию.

Примечания

Прежде чем использовать CREATE EXTENSION для загрузки расширения в базу данных, необходимо установить файлы его поддержки. Информацию об установке расширений, поставляемых с QHB, можно найти в разделе Расширения QHB.

Расширения, доступные для загрузки в настоящее время, можно найти в системных представлениях pg_available_extensions или pg_available_extension_versions.

Внимание
Установка расширение от имени суперпользователя требует уверенности в том, что автор расширения написал его установочный скрипт безопасным образом. Для злоумышленного пользователя не составит особого труда создать объекты типа троянского коня, которые впоследствии скомпрометируют выполнение небрежно написанного скрипта расширения, позволив этому пользователю получить права суперпользователя. Однако такие объекты опасны, только если находятся в пути search_path во время выполнения скрипта, то есть если они находятся в схеме, в которую устанавливается расширение, или в схеме, где располагается другое расширение, от которого зависит первое. Таким образом, имея дело с расширениями, скрипты которых не были тщательно проверены, рекомендуется устанавливать их только в те схемы, для которых недоверенные пользователи не имеют и не будут иметь права CREATE. То же самое касается других расширений, от которых они зависят.

Расширения, поставляемые в составе QHB, можно считать защищенными от подобного рода атак времени выполнения, за исключением некоторых, зависящих от других расширений. Как отмечается в документации таких расширений, их следует устанавливать в безопасные схемы и/или в те же схемы, в которые устанавливаются требующиеся им расширения.

Дополнительную информацию о создании новых расширений см. в разделе Упаковка связанных объектов в расширение.

Примеры

Установка расширения hstore в текущую базу данных с размещением его объектов в схеме addons:

CREATE EXTENSION hstore SCHEMA addons;

Еще один способ сделать вышеописанную операцию:

SET search_path = addons;
CREATE EXTENSION hstore;

Совместимость

Команда CREATE EXTENSION является расширением QHB.

См. также

ALTER EXTENSION, DROP EXTENSION

CREATE FOREIGN DATA WRAPPER

CREATE FOREIGN DATA WRAPPER — определить новую обертку сторонних данных

Синтаксис

CREATE FOREIGN DATA WRAPPER имя
    [ HANDLER функция_обработчик | NO HANDLER ]
    [ VALIDATOR функция_проверки | NO VALIDATOR ]
    [ OPTIONS ( параметр 'значение' [, ... ] ) ]

Описание

Команда CREATE FOREIGN DATA WRAPPER создает новую обертку сторонних данных. Пользователь, определяющий обертку сторонних данных, становится ее владельцем.

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

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

Параметры

имя

Имя создаваемой обертки сторонних данных.

HANDLER функция_обработчик

В аргументе функция_обработчика указывается имя ранее зарегистрированной функции, которая будет вызвана для извлечения функций, реализующих обращения к сторонним таблицам. Функция обработчика должна быть без аргументов, и ее возвращаемый тип должен быть fdw_handler.

Можно создать обертку сторонних данных без функции обработчика, но сторонние таблицы, использующие такую обертку, будут только объявлены, но не доступны.

VALIDATOR функция_проверки

функция_проверки — это имя ранее зарегистрированной функции, которая будет вызвана для проверки общих параметров, передаваемых обертке сторонних данных, а также параметров для сторонних серверов, сопоставлений пользователей и сторонних таблиц, использующих эту обертку сторонних данных. Если нет функции проверки или указано NO VALIDATOR, то во время создания объектов параметры проверяться не будут. (Возможно, обертки сторонних данных будут игнорировать или отклонять недопустимые спецификации параметров во время выполнения, в зависимости от реализации.) Функция проверки должна принимать два аргумента: первый типа text[] (в нем содержится массив параметров, хранящихся в системном каталоге), а второй типа oid (в нем указывается OID системного каталога с этими параметрами). Возвращаемое значение игнорируется; функция должна сообщить о недопустимых параметрах с помощью системной функции ereport(ERROR).

OPTIONS ( параметр 'значение' [, ... ] )

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

Примечания

Функциональность работы QHB со сторонними данными всё еще находится в стадии активного развития. Оптимизация запросов примитивна (и по большей части это тоже делает обертка). Таким образом, имеются широкие возможности для дальнейшего повышения производительности.

Примеры

Создание бесполезной обертки сторонних данных dummy:

CREATE FOREIGN DATA WRAPPER dummy;

Создание обертки сторонних данных file с функцией-обработчиком file_fdw_handler:

CREATE FOREIGN DATA WRAPPER file HANDLER file_fdw_handler;

Создание обертки сторонних данных mywrapper с некими параметрами:

CREATE FOREIGN DATA WRAPPER mywrapper
    OPTIONS (debug 'true');

Совместимость

Команда CREATE FOREIGN DATA WRAPPER соответствует стандарту ISO/IEC 9075-9 (SQL/MED), за исключением того, что в QHB предложения HANDLER и VALIDATOR являются расширениями, а стандартные предложения LIBRARY и LANGUAGE не реализованы.

Обратите внимание, однако, что соответствие функциональности SQL/MED в комплексе пока не достигнуто.

См. также

ALTER FOREIGN DATA WRAPPER, DROP FOREIGN DATA WRAPPER, CREATE SERVER, CREATE USER MAPPING, CREATE FOREIGN TABLE

CREATE FOREIGN TABLE

CREATE FOREIGN TABLE — определить новую стороннюю таблицу

Синтаксис

CREATE FOREIGN TABLE [ IF NOT EXISTS ] имя_таблицы ( [
  { имя_столбца тип_данных [ OPTIONS ( параметр 'значение' [, ... ] ) ] [ COLLATE правило_сортировки ] [ ограничение_столбца [ ... ] ]
    | ограничение_таблицы }
    [, ... ]
] )
[ INHERITS ( таблица_родитель [, ... ] ) ]
  SERVER имя_сервера
[ OPTIONS ( параметр 'значение' [, ... ] ) ]

CREATE FOREIGN TABLE [ IF NOT EXISTS ] имя_таблицы
  PARTITION OF таблица_родитель [ (
  { имя_столбца [ WITH OPTIONS ] [ ограничение_столбца [ ... ] ]
    | ограничение_таблицы }
    [, ... ]
) ] указание_границ_партиции
  SERVER имя_сервера
[ OPTIONS ( параметр 'значение' [, ... ] ) ]

Где ограничение_столбца может быть:

[ CONSTRAINT имя_ограничения ]
{ NOT NULL |
  NULL |
  CHECK ( выражение ) [ NO INHERIT ] |
  DEFAULT выражение_по_умолчанию |
  GENERATED ALWAYS AS ( генерирующее_выражение ) STORED }

Где ограничение_таблицы может быть:

[ CONSTRAINT имя_ограничения ]
CHECK ( выражение ) [ NO INHERIT ]

Описание

Команда CREATE FOREIGN TABLE создает в текущей базе данных новую стороннюю таблицу. Таблица будет принадлежать пользователю, выполнившему эту команду.

Если задано имя схемы (например, CREATE FOREIGN TABLE myschema.mytable ...), то таблица создается в указанной схеме, в противном случае — в текущей. Имя сторонней таблицы должно отличаться от имени любой другой сторонней таблицы, а также таблицы, последовательности, индекса, представления или материализованного представления в той же схеме.

CREATE FOREIGN TABLE также автоматически создает тип данных, который представляет собой составной тип, соответствующий одной строке сторонней таблицы. По этой причине сторонние таблицы не могут иметь то же имя, что и любой существующий тип данных в той же схеме.

Если указано предложение PARTITION OF, таблица создается в виде партиции parent_table с заданными границами.

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

Параметры

IF NOT EXISTS

Не считать ошибкой, если отношение с таким именем уже существует. В этом случае будет выдано соответствующее уведомление. Обратите внимание, что нет никакой гарантии, что существующее отношение как-то соотносится с тем, которое было бы создано.

имя_таблицы

Имя создаваемой таблицы (может быть дополнено схемой).

имя_столбца

Имя столбца, который будет создан в новой таблице.

тип_данных

Тип данных столбца. Может включать спецификаторы массива с этим типом. Более подробную информацию о типах данных, поддерживаемых QHB, см. в главе Типы данных.

COLLATE правило_сортировки

Предложение COLLATE назначает правило сортировки для столбца (который должен иметь сопоставимый тип данных). Если параметр не указан, используются правила сортировки по умолчанию для типа данных столбца.

INHERITS ( таблица_родитель [, ... ] )

Необязательное предложение INHERITS задает список таблиц, из которых новая сторонняя таблица автоматически наследует все столбцы. Родительские таблицы могут быть простыми или сторонними таблицами. Более подробную информацию см. в аналогичной форме команды CREATE TABLE.

PARTITION OF таблица_родитель FOR VALUES указание_границ_партиции

Эта форма может быть использована для создания сторонней таблицы в качестве партиции указанной родительской таблицы с заданными граничными значениями. Более подробную информацию см. в аналогичной форме команды CREATE TABLE. Обратите внимание, что в настоящее время не разрешается создавать стороннюю таблицу в качестве партиции родительской таблицы, если в последней есть индексы UNIQUE (уникальные). (См. также раздел ALTER TABLE ATTACH PARTITION.)

CONSTRAINT имя_ограничения

Необязательное имя столбца или ограничения таблицы. Если ограничение нарушается, имя ограничения присутствует в сообщениях об ошибках, поэтому имена ограничений, например столбец должен быть положительным, могут использоваться для передачи клиентским приложениям полезной информации об ограничениях. (Имена ограничений, содержащие пробелы, необходимо заключать в двойные кавычки.) Если имя ограничения не указано, система генерирует имя автоматически.

NOT NULL

Столбец не может содержать значения NULL.

NULL

Столбец может содержать значения NULL. Это значение по умолчанию.

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

CHECK ( выражение ) [ NO INHERIT ]

Предложение CHECK задает выражение, производящее логический результат, которому должна удовлетворять каждая строка в сторонней таблице; то есть выражение должно выдавать TRUE или UNKNOWN, но не FALSE для всех строк в сторонней таблице. Проверочное ограничение, указанное в качестве ограничения столбца, должно ссылаться только на значение этого столбца, тогда как выражение, указанное в ограничении таблицы, может ссылаться на несколько столбцов.

В настоящий момент выражения CHECK не могут содержать вложенные запросы и ссылаться на переменные, отличные от столбцов текущей строки. Также допустима ссылка на системный столбец tableoid, но не на другие системные столбцы.

Ограничение с пометкой NO INHERIT не будет наследоваться дочерними таблицами.

DEFAULT выражение_по_умолчанию

Предложение DEFAULT задает значение по умолчанию для столбца, в определении которого оно присутствует. Значением является любое выражение без переменных (подзапросы и перекрестные ссылки на другие столбцы текущей таблицы запрещены). Тип данных выражения по умолчанию должен соответствовать типу данных столбца.

Выражение по умолчанию будет использоваться в любой операции добавления, в которой не задано значение для столбца. Если для столбца не задано значение по умолчанию, то значение по умолчанию равно NULL.

GENERATED ALWAYS AS ( генерирующее_выражение ) STORED

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

Ключевое слово STORED требуется для обозначения того, что столбец будет вычисляться при записи. (Вычисленное значение будет передаваться обертке сторонних данных для сохранения и должно быть возвращено оберткой при чтении.)

Генерирующее выражение может ссылаться на другие столбцы в таблице, но не на другие генерируемые столбцы. Все функции и операторы в нем должны быть постоянными. Ссылки на другие таблицы не допускаются.

имя_сервера

Имя существующего стороннего сервера, используемого для сторонней таблицы. Дополнительную информацию об определении сервера см. в разделе CREATE SERVER.

OPTIONS ( параметр 'значение' [, ...] )

Параметры, которые будут связаны с новой сторонней таблицей или одним из ее столбцов. Допустимые имена параметров и их значений являются специфичными для каждой обертки сторонних данных и проверяются с помощью функции проверки. Имена параметров не должны повторяться (хотя параметр таблицы и параметр столбца могут иметь одно и то же имя).

Примечания

Ограничения сторонних таблицы (такие как CHECK или NOT NULL) не контролируются ядром системы QHB, и большинство оберток сторонних данных также не пытаются их контролировать; то есть система просто предполагает, что ограничение выполняется. Подобный контроль был бы нецелесообразен, поскольку применялся бы только к строкам, добавленным или обновленным через стороннюю таблицу, а не к строкам, измененным другими способами, например непосредственно на удаленном сервере. Вместо этого ограничение, связанное со сторонней таблицей, должно представлять собой ограничение, контролируемое удаленным сервером.

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

Хотя QHB не пытается контролировать ограничения для сторонних таблиц, она предполагает, что они выполняются для целей оптимизации запросов. Если есть строки, видимые в сторонней таблице, которые не удовлетворяют объявленному ограничению, запросы к таблице могут выдавать некорректные результаты. Ответственность за то, чтобы выполнялись условия ограничения, несет пользователь.

Подобные соображения распространяются и на генерируемые столбцы. Сохраненные генерируемые столбцы вычисляются при добавлении или обновлении на локальном сервере QHB и передаются обертке сторонних данных для записи в стороннее хранилище данных. При этом сервер не требует, чтобы запросы сторонней таблицы возвращали значения сохраненных генерируемых столбцов, соответствующие генерирующему выражению. Опять же, это может привести к неверным результатам запроса.

Хотя строки можно переместить из локальных партиций в партицию сторонней таблицы (при условии, что обертка сторонних данных поддерживает маршрутизацию кортежей), их нельзя переместить из партиции сторонней таблицы в другую партицию.

Примеры

Создание сторонней таблицы films, которая будет доступна через сервер film_server:

CREATE FOREIGN TABLE films (
    code        char(5) NOT NULL,
    title       varchar(40) NOT NULL,
    did         integer NOT NULL,
    date_prod   date,
    kind        varchar(10),
    len         interval hour to minute
)
SERVER film_server;

Создание сторонней таблицы measurement_y2016m07, которая будет доступна через сервер server_07, в виде партиции таблицы measurement, партиционированной по диапазонам:

CREATE FOREIGN TABLE measurement_y2016m07
    PARTITION OF measurement FOR VALUES FROM ('2016-07-01') TO ('2016-08-01')
    SERVER server_07;

Совместимость

Команда CREATE FOREIGN TABLE в значительной степени соответствует стандарту SQL, однако, так же, как и CREATE TABLE, допускает ограничения NULL и сторонние таблицы без столбцов. Возможность указать значения столбца по умолчанию также является расширением QHB. Наследование таблиц в форме, определенной QHB, не соответствует стандарту.

См. также

ALTER FOREIGN TABLE, DROP FOREIGN TABLE, CREATE TABLE, CREATE SERVER, IMPORT FOREIGN SCHEMA

CREATE FUNCTION

CREATE FUNCTION — определить новую функцию

Синтаксис

CREATE [ OR REPLACE ] FUNCTION
    имя ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ { DEFAULT | = } выражение_по_умолчанию ] [, ...] ] )
    [ RETURNS тип_результата
      | RETURNS TABLE ( имя_столбца тип_столбца [, ...] ) ]
  { LANGUAGE имя_языка
    | TRANSFORM { FOR TYPE имя_типа } [, ... ]
    | WINDOW
    | IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF
    | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT
    | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
    | PARALLEL { UNSAFE | RESTRICTED | SAFE }
    | COST стоимость_выполнения
    | ROWS строк_в_результате
    | SUPPORT вспомогательная_функция
    | SET параметр_конфигурации { TO значение | = значение | FROM CURRENT }
    | AS 'определение'
    | AS 'объектный_файл', 'объектный_символ'
  } ...

Описание

Команда CREATE FUNCTION определяет новую функцию. CREATE OR REPLACE FUNCTION будет либо создавать новую функцию, либо заменять определение существующей. Чтобы определить функцию, необходимо иметь право USAGE для соответствующего языка.

Если указано имя схемы, то функция создается в указанной схеме, в противном случае — в текущей. Имя новой функции не должно совпадать ни с одной существующей функцией или процедурой с теми же типами входных аргументов в этой же схеме. Однако функции и процедуры с различными типами аргументов могут иметь одно имя (это называется перегрузкой ).

Чтобы заменить текущее определение существующей функции, используйте CREATE OR REPLACE FUNCTION. Однако таким способом невозможно изменить имя или типы аргументов функции (если вы попытаетесь, то фактически создадите новую, независимую функцию). Кроме того, CREATE OR REPLACE FUNCTION не позволит изменить тип возвращаемого значения существующей функции. Для этого необходимо удалить и заново создать функцию. (В случае использования параметров OUT (выходных) это означает, что изменить типы параметров OUT можно, только удалив функцию.)

Когда CREATE OR REPLACE FUNCTION используется для замены существующей функции, владелец и права доступа функции не изменяются. Всем остальным свойствам функции присваиваются значения, указанные в команде, или значения по умолчанию. Чтобы заменить функцию, необходимо быть ее владельцем (или быть членом роли-владельца).

Если вы удалите и снова создадите функцию, новая функция будет другой сущностью; вам придется удалить существующие правила, представления, триггеры и т. д. — всё, что относится к старой функции. Поэтому чтобы изменить определение функции, не нарушая ссылающиеся на нее объекты, используйте команду CREATE OR REPLACE FUNCTION. Кроме того, многие дополнительные свойства существующей функции можно изменить с помощью ALTER FUNCTION.

Пользователь, создавший функцию, становится ее владельцем.

Чтобы создать функцию, необходимо иметь право USAGE для типов ее аргументов и возвращаемого типа.

Параметры

имя

Имя создаваемой функции (может быть дополнено схемой).

режим_аргумента

Режим аргумента: IN (входной), OUT (выходной), INOUT (входной и выходной) или VARIADIC (переменный). Если этот параметр опущен, значение по умолчанию равно IN. За аргументом VARIADIC могут следовать только аргументы OUT. Также аргументы OUT и INOUT нельзя использовать с предложением RETURNS TABLE.

имя_аргумента

Имя аргумента. Некоторые языки (включая SQL и PL/pgSQL) позволяют использовать это имя в теле функции. Для других языков имя входного аргумента является просто дополнительным описанием, если говорить о самой функции; но имена входных аргументов при вызове функции можно использовать для улучшения читаемости (см. раздел Вызов функции). В любом случае имя выходного аргумента является значимым, поскольку оно определяет имя столбца в типе результата. (Если вы опустите имя для выходного аргумента, система выберет имя столбца по умолчанию.)

тип_аргумента

Тип (или типы) данных аргументов функции (может быть дополнен схемой), если таковой имеется. Типы аргументов могут быть базовыми, составными или доменными типами, а также могут ссылаться на тип столбца таблицы.

В зависимости от языка реализации также может быть разрешено указывать «псевдотипы», такие как cstring. Псевдотипы свидетельствуют о том, что фактический тип аргумента либо указан не полностью, либо находится вне набора обычных типов данных SQL.

Ссылка на тип столбца записывается в виде имя_таблицы.имя_столбца%TYPE. Использование такой записи иногда может помочь сделать функцию независимой от изменений в определении таблицы.

выражение_по_умолчанию

Выражение, которое будет использоваться в качестве значения по умолчанию, если параметр не указан. Результат выражения должен сводиться к типу соответствующего параметра. Только входные параметры (включая INOUT) могут иметь значение по умолчанию. Все входные параметры, следующие за параметром со значением по умолчанию, также должны иметь значения по умолчанию.

тип_результата

Возвращаемый тип данных (может быть дополнен схемой). Тип возвращаемого значения может быть базовым, составным или доменным, а также может ссылаться на тип столбца таблицы. В зависимости от языка реализации также может быть разрешено указывать «псевдотипы», например cstring. Если функция не должна возвращать значение, в качестве возвращаемого типа укажите void.

В случае наличия параметров OUT или INOUT предложение RETURNS можно опустить. Если оно присутствует, то должно быть согласовано с типом результата, подразумеваемым выходными параметрами: RECORD, если выходных параметров несколько, либо тем же типом, что и у единственного выходного параметра.

Указание SETOF показывает, что функция возвращает множество, а не единственный элемент.

Ссылка на тип столбца записывается в виде имя_таблицы.имя_столбца%TYPE.

имя_столбца

Имя выходного столбца в записи RETURNS TABLE. Это фактически еще один способ объявления именованного выходного параметра (OUT), но RETURNS TABLE также подразумевает и RETURNS SETOF.

тип_столбца

Тип данных выходного столбца в записи RETURNS TABLE.

имя_языка

Имя языка, на котором реализована функция. Это может быть sql, c, internal, или имя определяемого пользователем процедурного языка, например plpgsql. Заключать имя в одинарные кавычки не рекомендуется и требует соответствующего регистра.

TRANSFORM { FOR TYPE имя_типа } [, ... ] }

Устанавливает список трансформаций, которые должны применяться при вызове функции. Трансформации выполняют преобразования между типами SQL и языковыми типами данных; см. раздел CREATE TRANSFORM. Преобразования встроенных типов обычно жестко предопределены в реализациях процедурных языков, так что их здесь указывать не нужно. Если реализация процедурного языка не знает, как обрабатывать тип, и трансформация не предоставляется, будет выполнено преобразование типов данных по умолчанию, но это зависит от реализации.

WINDOW

WINDOW указывает, что функция является не простой, а оконной функцией. В настоящее время имеет смысл только для функций, написанных на языке C. Указание WINDOW не может быть изменено при изменении существующего определения функции.

IMMUTABLE
STABLE
VOLATILE

Эти атрибуты информируют оптимизатор запросов о поведении функции. Одновременно можно указать не более одного атрибута. Если ни один из них не указан, по умолчанию предполагается VOLATILE.

IMMUTABLE (постоянная) указывает, что функция не может изменять базу данных и всегда возвращает один и тот же результат при одинаковых заданных значениях аргумента; т. е. она не выполняет поиск в базе данных и не использует иным образом информацию, не представленную непосредственно в ее списке аргументов. Если задана эта опция, то любой вызов функции со всеми постоянными аргументами может быть немедленно заменен значением функции.

STABLE (стабильная) указывает, что функция не может изменять базу данных и что в рамках одного сканирования таблицы она будет последовательно возвращать один и тот же результат для одних и тех же значений аргумента, но при этом его результат может изменяться в разных инструкциях SQL. Это подходящий выбор для функций, результаты которых зависят от содержимого базы данных и настраиваемых параметров (таких как текущий часовой пояс) и т. д. (Но этот вариант не подходит для триггеров AFTER, желающих сделать запрос на строки, измененные текущей командой.) Также обратите внимание, что функции семейства current_timestamp считаются стабильными, так как их результаты не изменяются в рамках транзакции.

VOLATILE (изменчивая) указывает, что значение функции может изменяться даже в пределах одного сканирования таблицы, поэтому ее вызовы нельзя оптимизировать. Относительно немногие функции базы данных являются изменчивыми в этом смысле, например: random(), currval() и timeofday(). Но обратите внимание, что любая функция, которая имеет побочные эффекты, должна быть классифицирована как VOLATILE, даже если ее результат вполне предсказуем, чтобы предотвратить оптимизацию вызовов; примером является setval().

Дополнительную информацию см. в разделе Категории изменчивости функций.

LEAKPROOF

LEAKPROOF (герметичная) указывает, что функция не имеет никаких побочных эффектов. Она не раскрывает никакой информации о своих аргументах, кроме своего возвращаемого значения. Например, функция, которая выдает сообщение об ошибке только для некоторых значений аргументов или которая включает значения аргументов в каждое сообщение об ошибке, не является герметичной. Это влияет на то, как система выполняет запросы к представлениям, созданным с барьером безопасности security_barrier, или таблицам с включенной защитой на уровне строк. Система будет принудительно применять условия из политик защиты и представлений с барьерами безопасности перед любыми условиями, которые пользователь задает в запросе и в которых задействуются негерметичные функции, чтобы предотвратить непреднамеренное раскрытие данных. Функции и операторы, помеченные как герметичные, считаются надежными и могут выполняться до выполнения условий из политик защиты и представлений с барьерами безопасности. Кроме того, функции, которые не имеют аргументов или которым не передаются никакие аргументы из представления с барьером безопасности или таблицы, не требуется помечать как герметичные, чтобы они выполнялись до условий, связанных с безопасностью. См. разделы CREATE VIEW и Система правил QHB. Этот параметр может быть установлен только суперпользователем.

CALLED ON NULL INPUT
RETURNS NULL ON NULL INPUT
STRICT

CALLED ON NULL INPUT (по умолчанию) указывает, что функция будет вызываться как обычно, когда некоторые из ее аргументов равны NULL. В этом случае ответственность за проверку (при необходимости) значений NULL и соответствующую их обработку несет автор функции.

RETURNS NULL ON NULL INPUT или STRICT указывает, что функция всегда возвращает значение NULL, когда любой из ее аргументов равен NULL. Если этот параметр указан, функция не выполняется при наличии NULL аргументов; вместо этого автоматически принимается результат NULL.

[EXTERNAL] SECURITY INVOKER
[EXTERNAL] SECURITY DEFINER

Характеристика SECURITY INVOKER (безопасность вызывающего) указывает, что функция должна выполняться с правами вызывающего ее пользователя. Это значение по умолчанию. Вариант SECURITY DEFINER (безопасность определившего) указывает, что функция должна выполняться с правами пользователя, которому она принадлежит (владельца).

Ключевое слово EXTERNAL (внешняя) допускается для соответствия стандарту SQL, но является необязательным, так как, в отличие от SQL, эта характеристика распространяется на все функции, а не только внешние.

PARALLEL

Указание PARALLEL UNSAFE означает, что функция не может быть выполнена в параллельном режиме и наличие такой функции в инструкции SQL приводит к последовательному плану выполнения. Это значение по умолчанию. Указание PARALLEL RESTRICTED означает, что функция может выполняться в параллельном режиме, но только в ведущем процессе параллельной группы. PARALLEL SAFE указывает, что функция безопасна для работы в параллельном режиме без ограничений.

Функции должны быть помечены как небезопасные для параллельного выполнения (PARALLEL UNSAFE), если они изменяют состояние базы данных, или вносят изменения в транзакцию, например используя подтранзакции, или обращаются к последовательностям или пытаются сохранять настройки параметров (например, используя setval). Функции должны быть помечены как ограниченно параллельные (PARALLEL RESTRICTED), если они обращаются к временным таблицам, состоянию клиентского подключения, курсорам, подготовленным операторам или разнообразному состоянию обслуживающего процесса, которые система не может синхронизировать в параллельном режиме (например, setseed может выполнять только ведущий процесс группы, так как изменения, внесенные другим процессом, не передаются ведущему). В целом, если функция помечена как безопасная (PARALLEL SAFE), являясь на самом деле ограниченной или небезопасной, либо помечена как ограниченно безопасная, не являясь безопасной, она может выдавать ошибки или давать неправильные результаты при использовании в параллельном запросе. Функции на языке C теоретически могут демонстрировать совершенно неопределенное поведение при неправильной маркировке, поскольку система не может защитить себя от произвольного кода на C, но в большинстве случаев результат будет не хуже, чем для любой другой функции. При возникновении сомнений функции следует помечать как UNSAFE, что является значением по умолчанию.

COST стоимость_выполнения

Положительное число, задающее примерную стоимость выполнения функции в единицах cpu_operator_cost. Если функция возвращает множество, то это стоимость за возвращенную строку. Если стоимость не указана, то для функций языка C и внутренних функций она считается равной 1 единице, а для функций на всех других языках — 100 единицам. Более высокие значения заставляют планировщик пытаться избегать оценки функции чаще необходимого.

ROWS строк_в_результате

Положительное число, задающее примерное количество строк, которое планировщик должен ожидать от функции на выходе. Это указание допустимо только в том случае, если функция объявлена возвращающей множество. Предполагаемое по умолчанию значение — 1000 строк.

SUPPORT вспомогательная_функция

Имя вспомогательной функции для планировщика, используемое для этой функции (может быть дополнено схемой). Дополнительную информацию см. в разделе Информация по оптимизации функций. Чтобы использовать эту опцию, нужно быть суперпользователем.

параметр_конфигурации
значение

Предложение SET определяет, что при вызове функции указанный параметр конфигурации должен принять заданное значение, а затем восстановить свое предыдущее значение по завершении функции. Предложение SET FROM CURRENT сохраняет значение параметра, действующее при выполнении CREATE FUNCTION, в качестве значения, которое будет применено при входе в функцию.

Если в определение функции добавлено предложение SET, то действие команды SET LOCAL, выполняемой внутри функции для той же переменной, ограничивается этой функцией: предыдущее значение параметра конфигурации восстанавливается по завершении функции. Однако обычная команда SET (без LOCAL) переопределяет предложение SET, как сделало бы и для предыдущей команды SET LOCAL: эффекты такой команды сохранятся после завершения функции, если не случится откат текущей транзакции.

Дополнительную информацию о допустимых именах и значениях параметров см. в разделе SET и главе Конфигурация сервера.

определение

Строковая константа, определяющая функцию; значение зависит от языка. Это может быть имя внутренней функции, путь к объектному файлу, команда SQL или текст на процедурном языке.

Часто бывает полезно заключать определение функции в знаки доллара (см. раздел Строковые константы с экранированием знаками доллара), а не в традиционные апострофы. Если не использовать знаки доллара, то любые одиночные кавычки или обратные косые черты в определении функции должны быть экранированы путем удвоения.

объектный_файл, объектный_символ

Эта форма предложения AS используется для динамически загружаемых функций языка C, когда имя функции в исходном коде языка C не совпадает с именем функции SQL. Строка объектный_файл является именем файла разделяемой библиотеки, содержащего скомпилированную функцию C, и интерпретируется как параметр команды LOAD. Строка объектный_символ задает скомпонованный символ функции, то есть имя функции в исходном коде языка C. Если объектный символ опущен, предполагается, что он совпадает с именем определяемой функции SQL. В C имена всех функций должны быть разными, поэтому следует давать перегруженным функциям C разные имена (например, включать в имена C обозначения типов аргументов).

При повторных вызовах CREATE FUNCTION ссылается на один и тот же объектный файл, который загружается только один раз за сеанс. Чтобы выгрузить и перезагрузить файл (скажем, во время разработки), запустите новый сеанс.

Дополнительную информацию о функциях записи см. в разделе Пользовательские функции.

Перегрузка

QHB допускает перегрузку функций; то есть одно и то же имя может использоваться для нескольких различных функций, если они имеют различные типы входных аргументов. Независимо от того, используете вы эту возможность или нет, она требует предосторожности при вызове функций в базах данных, где одни пользователи не доверяют другим пользователям (см. раздел Перегрузка функций).

Две функции считаются одинаковыми, если они имеют одинаковые имена и типы входных аргументов, игнорируя параметры OUT. Так, например, эти декларации конфликтуют:

CREATE FUNCTION foo(int) ...
CREATE FUNCTION foo(int, out text) ...

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

CREATE FUNCTION foo(int) ...
CREATE FUNCTION foo(int, int default 42) ...

Вызов foo(10) завершится ошибкой из-за неоднозначности в выборе вызываемой функции.

Примечания

В объявлении аргументов функции и возвращаемого значения допускается полный синтаксис описания типа SQL. Однако заключенные в скобки модификаторы типа (например, поле точности для типа numeric) команда CREATE FUNCTION не учитывает. Так, например, CREATE FUNCTION foo (varchar(10)) ... — это ровно то же самое, что и CREATE FUNCTION foo (varchar) ....

При замене существующей функции с помощью CREATE OR REPLACE FUNCTION существуют ограничения на изменение имен параметров. Нельзя изменить имя, уже присвоенное какому-либо входному параметру (хотя можно добавить имена к параметрам, которые ранее не имели имени). Если существует несколько выходных параметров, нельзя изменить их имена, поскольку это приведет к изменению имен столбцов анонимного составного типа, описывающего результат функции. Эти ограничения применяются для того, чтобы существующие вызовы функции гарантированно не прекращали работу после ее замены.

Если функция объявлена как STRICT с аргументом VARIADIC, то при оценке строгости проверяется, чтобы весь переменный массив в целом не был NULL. Если же массив содержит элементы NULL, функция будет по-прежнему вызываться.

Примеры

Ниже приведено несколько простых вводных примеров. Дополнительную информацию и примеры см. в разделе Написание триггерных функций на C.

CREATE FUNCTION add(integer, integer) RETURNS integer
    AS 'select $1 + $2;'
    LANGUAGE SQL
    IMMUTABLE
    RETURNS NULL ON NULL INPUT;

Функция увеличения целого числа, использующая именованный аргумент, на языке PL/pgSQL:

CREATE OR REPLACE FUNCTION increment(i integer) RETURNS integer AS $$
        BEGIN
                RETURN i + 1;
        END;
$$ LANGUAGE plpgsql;

Функция, возвращающая запись с несколькими выходными параметрами:

CREATE FUNCTION dup(in int, out f1 int, out f2 text)
    AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
    LANGUAGE SQL;

SELECT * FROM dup(42);

То же самое можно сделать более развернуто, явно объявив составной тип:

CREATE TYPE dup_result AS (f1 int, f2 text);

CREATE FUNCTION dup(int) RETURNS dup_result
    AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
    LANGUAGE SQL;

SELECT * FROM dup(42);

Еще один способ вернуть несколько столбцов — применить функцию TABLE:

CREATE FUNCTION dup(int) RETURNS TABLE(f1 int, f2 text)
    AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
    LANGUAGE SQL;

SELECT * FROM dup(42);

Однако пример с TABLE отличается от предыдущих, так как в нем функция на самом деле возвращает не одну, а набор записей.

Разработка защищенных функций SECURITY DEFINER

Так как функция SECURITY DEFINER выполняется с правами владеющего ей пользователя, необходимо позаботиться о том, чтобы функцию невозможно было использовать не по назначению. В целях обеспечения безопасности следует установить search_path, чтобы исключить любые схемы, доступные для записи ненадежными пользователями. Это предотвращает создание злоумышленниками объектов (например, таблиц, функций и операторов), маскирующих объекты, предназначенные для использования функцией. Особенно важной в этом отношении является схема временной таблицы, которая по умолчанию ищется первой и обычно доступна для записи любым пользователям. Соответствующую защиту можно организовать, поместив временную схему в конец списка поиска. Для этого нужно сделать pg_temp последней записью в search_path. Эта функция иллюстрирует безопасное использование:

CREATE FUNCTION check_password(uname TEXT, pass TEXT)
RETURNS BOOLEAN AS $$
DECLARE passed BOOLEAN;
BEGIN
        SELECT  (pwd = $2) INTO passed
        FROM    pwds
        WHERE   username = $1;

        RETURN passed;
END;
$$  LANGUAGE plpgsql
    SECURITY DEFINER
    -- Установить безопасный путь поиска: сначала доверенная схема (или схемы), затем 'pg_temp'.
    SET search_path = admin, pg_temp;

Эта функция должна обращаться к таблице admin.pwds, но без предложения SET или с предложением SET, включающим только admin; ее можно «обмануть», создав временную таблицу с именем pwds.

Еще один момент, который следует иметь в виду, — это то, что по умолчанию право выполнения для создаваемых функций имеет роль PUBLIC (см. дополнительную информацию в разделе Права). Однако зачастую требуется разрешить доступ к функции, работающей в контексте безопасности определившего, только некоторым пользователям. Для этого необходимо отозвать стандартные права PUBLIC, а затем предоставить права выполнения выборочно. Чтобы избежать появления окна, в котором новая функция доступна для всех, создайте ее и установите права доступа в рамках одной транзакции. Например:

BEGIN;
CREATE FUNCTION check_password(uname TEXT, pass TEXT) ... SECURITY DEFINER;
REVOKE ALL ON FUNCTION check_password(uname TEXT, pass TEXT) FROM PUBLIC;
GRANT EXECUTE ON FUNCTION check_password(uname TEXT, pass TEXT) TO admins;
COMMIT;

Совместимость

Команда CREATE FUNCTION определена в стандарте SQL. Реализация в QHB аналогична, но совместима не полностью. К отличиям относятся непереносимые атрибуты, а также поддержка различных языков.

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

Для определения значений параметров по умолчанию стандарт SQL поддерживает только синтаксис с помощью ключевого слова DEFAULT. Синтаксис со знаком = используется в T-SQL и Firebird.

См. также

ALTER FUNCTION, DROP FUNCTION, GRANT, LOAD, REVOKE

CREATE GROUP

CREATE GROUP — определить новую роль в базе данных

Синтаксис

CREATE GROUP имя [ [ WITH ] параметр [ ... ] ]

Где параметр может быть:

      SUPERUSER | NOSUPERUSER
    | CREATEDB | NOCREATEDB
    | CREATEROLE | NOCREATEROLE
    | INHERIT | NOINHERIT
    | LOGIN | NOLOGIN
    | REPLICATION | NOREPLICATION
    | BYPASSRLS | NOBYPASSRLS
    | CONNECTION LIMIT предел_подключений
    | [ ENCRYPTED ] PASSWORD 'пароль' | PASSWORD NULL
    | VALID UNTIL 'дата_время'
    | IN ROLE имя_роли [, ...]
    | IN GROUP имя_роли [, ...]
    | ROLE имя_роли [, ...]
    | ADMIN имя_роли [, ...]
    | USER имя_роли [, ...]
    | SYSID uid

Описание

Команда CREATE GROUP теперь является синонимом команды CREATE ROLE.

Совместимость

В стандарте SQL нет команды CREATE GROUP.

См. также

CREATE ROLE

CREATE INDEX

CREATE INDEX — определить новый индекс

Синтаксис

CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ] имя ] ON [ ONLY ] имя_таблицы [ USING метод ]
    ( { имя_столбца | ( выражение ) } [ COLLATE правило_сортировки ] [ класс_операторов  [ ( параметр_класса_операторов = значение [, ... ] ) ] ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
    [ INCLUDE ( имя_столбца [, ...] ) ]
    [ WITH ( параметр_хранения [= значение] [, ... ] ) ]
    [ TABLESPACE имя_табличного_пространства ]
    [ WHERE предикат ]

Описание

Команда CREATE INDEX строит индекс по указанному столбцу (или столбцам) заданного отношения, которое может быть таблицей или материализованным представлением. Индексы в основном используются для повышения производительности базы данных (хотя неправильное использование может привести к ее снижению).

Ключевое поле (или поля) для индекса указывается в виде имен столбцов или же в виде выражений, заключенных в круглые скобки. Если индексный метод поддерживает составные индексы, можно указать несколько полей.

Поле индекса может быть выражением, вычисленным из значений одного или нескольких столбцов строки таблицы. Эту функцию можно использовать для быстрого доступа к данным на основе некоторого преобразования исходных данных. Например, индекс, построенный по выражению upper(col), позволит использовать поиск по индексу в предложении WHERE upper(col) = 'JIM'.

QHB предоставляет следующие индексные методы: B-дерево, хеш, GiST, SP-GiST, GIN и BRIN. Пользователи также могут определять свои собственные методы индексирования, но это довольно сложно.

Когда в команде присутствует предложение WHERE, создается частичный индекс. Частичный индекс — это индекс, содержащий записи только для части таблицы, обычно более полезной для индексации, чем остальная часть таблицы. Например, если у вас есть таблица, которая содержит выставленные и неоплаченные счета за заказы, и неоплаченные заказы занимают малую часть общей таблицы, но запрашиваются чаще, можно улучшить производительность, создав индекс только для этой части. Еще одним возможным применением является указание WHERE вместе с UNIQUE для обеспечения уникальности по подмножеству таблицы. Дополнительную информацию см. в разделе Частичные индексы.

Выражение, используемое в предложении WHERE, может ссылаться только на столбцы нижележащей таблицы, но при этом использовать все столбцы, а не только индексируемые. В настоящее время подзапросы и агрегатные выражения в WHERE тоже запрещены. Такие же ограничения применяются к выражениями в полях индексов.

Все функции и операторы, используемые в определении индекса, должны быть «постоянными», то есть их результаты должны зависеть только от их аргументов, а не от какого-либо внешнего влияния (например, содержимого другой таблицы или текущего времени). Это ограничение гарантирует, что поведение индекса четко определено. Чтобы использовать определяемую пользователем функцию в выражении индекса или предложении WHERE, не забудьте при создании отметить функцию как IMMUTABLE (постоянную).

Параметры

UNIQUE

Указывает, что система должна проверять наличие повторяющихся значений в таблице при создании индекса (если данные уже существуют) и при каждом добавлении данных. Попытки добавить или обновить данные, которые вызовут дублирование записей, приведут к ошибке.

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

CONCURRENTLY

Если используется этот параметр, QHB будет строить индекс без установления каких-либо блокировок, предотвращающих параллельные добавления, изменения или удаление записей в таблице, тогда как стандартная операция построения индекса блокирует запись (но не чтение) в таблице до своего завершения. Существует несколько особенностей, которые следует учитывать при использовании этого параметра — см. Неблокирующее построение индексов ниже.

IF NOT EXISTS

Не считать ошибкой, если индекс с таким именем уже существует. В этом случае будет выдано соответствующее уведомление. Обратите внимание, что нет никакой гарантии, что существующий индекс как-то соотносится с тем, который был бы создан. Если указано IF NOT EXISTS, то имя индекса является обязательным.

INCLUDE

Необязательное предложение INCLUDE указывает список столбцов, которые будут включены в индекс в качестве неключевых столбцов. Неключевой столбец не будет использоваться в условии поиска для сканирования по индексу; также он игнорируется при анализе ограничения уникальности или исключения, применяемого индексом. Однако при сканировании только индекса может возвращаться содержимое неключевых столбцов без необходимости обращения к таблице, поскольку они доступны непосредственно из записи индекса. Таким образом, добавление неключевых столбцов позволяет использовать сканирование только индекса в тех запросах, где иначе оно было бы неприменимо.

В отношении добавления неключевых столбцов в индекс разумно проявлять консерватизм, особенно для широких столбцов. Если кортеж индекса превысит максимальный размер, разрешенный для типа индекса, добавление данных завершится ошибкой. В любом случае, неключевые столбцы дублируют данные из таблицы и раздувают размер индекса, что потенциально замедляет поиск. Более того, в индексе B-дерево, содержащем неключевой столбец, не будет работать исключение дубликатов.

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

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

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

имя

Имя создаваемого индекса. Имя схемы здесь не может быть включено: индекс всегда создается в той же схеме, что и его родительская таблица. Если имя опущено, QHB выбирает подходящее имя на основе имени родительской таблицы и индексированных имен столбцов.

ONLY

Указывает, что индексы не должны рекурсивно создаваться на партициях, если таблица партиционирована. По умолчанию используется рекурсивно.

имя_таблицы

Имя индексируемой таблицы (может быть дополнено схемой).

метод

Имя используемого индексного метода. Возможные варианты: btree, hash, gist, spgist, gin и brin. По умолчанию используется метод btree.

имя_столбца

Имя столбца таблицы.

выражение

Выражение, основанное на одном или нескольких столбцах таблицы. Обычно выражение должно быть записано в скобках, как показано в синтаксисе. Однако если выражение записано в виде вызова функции, круглые скобки могут быть опущены.

правило_сортировки

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

класс_операторов

Имя класса оператора. Дополнительную информацию см. ниже.

параметр_класса_операторов

Имя параметра класса операторов. Дополнительную информацию см. ниже.

ASC

Задает порядок сортировки по возрастанию (который является порядком сортировки по умолчанию).

DESC

Задает порядок сортировки по убыванию.

NULLS FIRST

Указывает, что значения NULL после сортировки оказываются перед остальными. Это значение по умолчанию, когда задано DESC.

NULLS LAST

Указывает, что значения NULL после сортировки оказываются после остальных. Это значение по умолчанию, когда задано ASC.

параметр_хранения

Имя параметра хранения, зависящего от индексного метода. Дополнительную информацию см. в разделе Параметры хранения индекса.

имя_табличного_пространства

Табличное пространство, в котором будет создан индекс. Если этот параметр не задан, выполняется обращение к default_tablespace или temp_tablespaces для индексов временных таблиц.

предикат

Выражение ограничения для частичного индекса.

Параметры хранения индекса

Необязательное предложение WITH задает параметры хранения для индекса. Каждый индексный метод имеет свой собственный набор допустимых параметров хранения. Индексные методы B-дерево, хеш, GiST и SP-GiST принимают этот параметр:

fillfactor (integer)

Коэффициент заполнения для индекса выражается в процентах и определяет, насколько плотно индексный метод будет пытаться заполнить страницы индекса. Для B-деревьев страницы уровня листьев заполняются до этого процента во время первоначального построения индекса, а также при расширении индекса вправо (добавление новых наибольших значений ключей). Если впоследствии страницы будут полностью заполнены, они будут разделены, что приведет к постепенному снижению эффективности индекса. По умолчанию B-деревья используют коэффициент заполнения 90, но можно выбрать любое целочисленное значение от 10 до 100. Если таблица статична, то коэффициент заполнения, равный 100, помогает уменьшить физический размер таблицы, но для интенсивно изменяемых таблиц лучше использовать меньшее значение коэффициента заполнения, чтобы минимизировать необходимость разбиения страниц. С другими индексными методами коэффициент заполнения действует по-другому, но примерно в том же ключе; значение коэффициента заполнения по умолчанию для разных методов разное.

Индексы B-деревья также принимают эти параметры:

deduplicate_items (boolean)

Контролирует использование техники исключения дубликатов, описанной в подразделе Дедупликация. Устанавливается на значения ON или OFF для включения или выключения соответственно. (Допустимые варианты написания ON и OFF описаны в разделе Настройка параметров.) Значение по умолчанию равно ON.

Примечание
После выключения deduplicate_items во время выполнения команды ALTER INDEX в будущем при операциях добавления элементов автоматическая дедупликация срабатывать не будет, но представление существующих кортежей со списком идентификаторов на стандартное не поменяется.

vacuum_cleanup_index_scale_factor (floating point)

Значение vacuum_cleanup_index_scale_factor для индекса.

Индексы GiST дополнительно принимают этот параметр:

buffering (enum)

Определяет, используется ли для построения индекса метод буферизации, описанный в разделе Индексы GiST. Реализация. Со значением OFF он выключен, с ON — включен, а с AUTO — вначале выключен, но включается «на лету», как только размер индекса достигает effective_cache_size. Значение по умолчанию равно AUTO.

Индексы GIN принимают другие параметры:

fastupdate (boolean)

Этот параметр управляет использованием механизма быстрого обновления, описанного в разделе Быстрое обновление GIN. Это логический параметр: ON включает быстрое обновление, OFF выключает его. (Допустимые варианты написания ON и OFF описаны в разделе Настройка параметров.) Значение по умолчанию равно ON.

Примечание
Выключение fastupdate в ALTER INDEX предотвращает помещение добавляемых в дальнейшем строк в список записей, ожидающих индексации, но записи, добавленные в этот список ранее, в нем остаются. Чтобы очистить очередь операций, надо затем выполнить для этой таблицы VACUUM или вызвать функцию gin_clean_pending_list.

gin_pending_list_limit (integer)

Пользовательский параметр gin_pending_list_limit. Это значение указывается в килобайтах.

Индексы BRIN принимают другие параметры:

pages_per_range (integer)

Определяет количество блоков таблицы, которые составляют один диапазон блоков для каждой записи индекса BRIN (более подробную информацию см. в разделе Индексы BRIN). Значение по умолчанию равно 128.

autosummarize (boolean)

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

Неблокирующее построение индексов

Создание индекса может помешать обычной работе базы данных. Как правило, QHB блокирует для записи предназначенную к индексированию таблицу и полностью выполняет построение индекса за одно сканирование таблицы. Другие транзакции по-прежнему могут читать из таблицы, но при попытке добавления, изменения или удаления строки в таблице они будут блокироваться до завершения построения индекса. Это может иметь серьезные последствия, если система является производственной базой данных в реальном времени. Индексация очень больших таблиц может занять много часов, и даже у таблиц меньшего размера построение индекса может блокировать записи на время, неприемлемое для производственной системы.

QHB поддерживает построение индексов без блокировки записи. Этот метод вызывается путем указания CONCURRENTLY команды CREATE INDEX. Если используется этот параметр, QHB должен выполнить два сканирования таблицы и помимо этого дождаться завершения всех существующих транзакций, которые потенциально могут изменить или использовать индекс. Таким образом, данный метод требует проделать в сумме больше действий, чем стандартное построение индекса, и занимает значительно больше времени. Однако поскольку он позволяет продолжать обычную работу во время построения индекса, этот метод удобен для добавления новых индексов в производственной среде. Разумеется, дополнительная нагрузка на процессор и подсистему ввода/вывода, вызванная созданием индекса, может замедлить другие операции.

При неблокирующем построении индекса он действительно попадает в системный каталог в одной транзакции, а затем в еще двух транзакциях происходят два сканирования таблиц. Перед каждым сканированием операция построения индекса должна ожидать завершения существующих транзакций, которые модифицируют таблицу. После второго сканирования этой операции также необходимо дождаться завершения всех транзакций, которые получили снимок таблицы (см. главу Параллельный контроль) перед вторым сканированием. Затем индекс наконец может быть помечен как готовый к использованию, и команда CREATE INDEX завершается. Однако даже в этом случае индекс может не сразу стать пригодным для запросов: в худшем случае его нельзя применять до тех пор, пока существуют транзакции, запущенные до начала построения индекса.

Если при сканировании таблицы возникает проблема, например, взаимоблокировка или нарушение уникальности в уникальном индексе, команда CREATE INDEX завершится ошибкой, но оставит «нерабочий» индекс. Этот индекс будет игнорироваться при запросах, поскольку он может быть неполным; однако он все равно будет потреблять ресурсы при обновлениях. Команда psql \d сообщит о таком индексе, пометив его как INVALID:

qhb=# \d tab
       Table "public.tab"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 col    | integer |           |          |
Indexes:
    "idx" btree (col) INVALID

Рекомендуемый метод восстановления в таких случаях заключается в том, чтобы удалить индекс и попытаться снова выполнить CREATE INDEX CONCURRENTLY. (Другая возможность состоит в том, чтобы перестроить индекс с помощью команды REINDEX INDEX CONCURRENTLY.)

Еще одна сложность, с которой можно столкнуться при неблокирующем построении уникального индекса, в том, что когда начинается второе сканирование таблицы, ограничение уникальности уже применяется к другим транзакциям. Это означает, что нарушения ограничений могут быть зарегистрированы в других запросах раньше, чем индекс станет доступным для использования, или даже в тех случаях, когда построение индекса в конечном итоге завершится неудачей. Кроме того, если во время второго сканирования происходит сбой, то «нерабочий» индекс все равно продолжает после этого применять свое ограничение уникальности.

Метод неблокирующего построения поддерживает также индексы выражений и частичные индексы. Ошибки, возникающие при вычислении этих выражений, могут привести к поведению, описанному выше для случаев с нарушением ограничений уникальности.

Обычное построение индекса допускает одновременное построение других индексов для таблицы обычным методом, но неблокирующее построение для конкретной таблицы в один момент времени допускается только одно. В любом случае никакие другие изменения схемы таблицы во время построения индекса не допускаются. Другое отличие состоит в том, что обычная команда CREATE INDEX может быть выполнена в блоке транзакций, а CREATE INDEX CONCURRENTLY — нет.

Метод неблокирующего построения для индексов в партиционированных таблицах в настоящее время не поддерживаются. Однако можно использовать этот метод для каждой партиции отдельно, а затем создать индекс партиции в обычном режиме, чтобы сократить время блокировки записи в партиционированную таблицу. В этом случае построение индекса партиции будет заключаться только в изменении метаданных.

Примечания

Информацию о том, когда можно использовать индексы, когда они не используются и в каких конкретных ситуациях они могут быть полезны, см. в главе Индексы.

В настоящее время только методы B-дерево, GiST, GIN и BRIN поддерживают составные индексы. По умолчанию можно указать до 32 полей. (Это ограничение можно изменить при пересборке QHB.) В настоящее время только B-дерево поддерживает уникальные индексы.

Для каждого столбца индекса можно указать класс операторов. Класс операторов определяет операторов, которые будут использоваться индексом для этого столбца. Например, индекс B-дерева для четырехбайтовых целых чисел будет использовать класс int4_ops; этот класс операторов включает функции сравнения для четырехбайтовых целых чисел. На практике класс операторов по умолчанию для типа данных столбца обычно является достаточным. Основной смысл наличия классов операторов состоит в том, что для некоторых типов данных может существовать больше одного осмысленного порядка сортировки. Например, мы можем захотеть отсортировать комплексные числа как по абсолютному значению, так и по вещественной части. Мы могли бы сделать это, определив два класса операторов для типа данных и затем при создании индекса выбрав подходящий. Более подробную информацию о классах операторов см. в разделах Классы операторов и семейства операторов и Интерфейсные расширения для индексов.

Когда команда CREATE INDEX вызывается для партиционированной таблицы, поведение по умолчанию — это рекурсивно распространить ее действие на все партиции, чтобы убедиться, что все они имеют соответствующие индексы. Сначала каждая партиция проверяется на наличие равнозначного индекса, и если таковой имеется, он будет присоединен в качестве индекса партиции к создаваемому индексу, который станет его родительским индексом. Если соответствующий индекс не существует, он будет создан и автоматически присоединен; имя индекса для каждой партиции выбирается так же, как и при выполнении этой команды без имени индекса. С указанием ONLY рекурсия не производится и индекс помечается как нерабочий. (Команда ALTER INDEX ... ATTACH PARTITION пометит его как рабочий, когда все партиции получат соответствующие индексы.) Обратите внимание, что для любой партиции, которая будет создана в будущем с помощью CREATE TABLE ... PARTITION OF, соответствующий индекс будет создан автоматически, независимо от того, задано ли ONLY.

Для индексных методов, поддерживающих сканирование по порядку (в настоящее время это поддерживает только В-дерево), можно изменить порядок сортировки индекса, указав необязательные предложения AS, DESC, NULLS FIRST или NULLS LAST. Поскольку упорядоченный индекс можно сканировать как вперед, так и назад, обычно не рекомендуется создавать индекс по убыванию (DESC) для одного столбца — этот порядок сортировки уже доступен с обычным индексом. Ценность этих параметров состоит в возможности создать составные индексы, которые будут соответствовать порядку сортировки, указанному в запросе со смешанным порядком сортировки, например SELECT ... ORDER BY x ASC, y DESC. Параметры NULLS полезны, когда в запросах, зависящих от индексов, требуется вместо стандартного «NULL вверху» реализовать поведение «NULL внизу», чтобы избежать этапов сортировки.

Система регулярно собирает статистику со всех столбцов таблицы. Новые индексы, созданные без применения выражений, могут сразу использовать эту статистику, чтобы определить полезность индекса. Для тех же новых индексов, что были построены с применением выражений, нужно выполнить команду ANALYZE или подождать, пока процесс «Автовакуум» не проанализирует таблицу и не сформирует статистику для этих индексов.

Для большинства методов индексов скорость создания индекса зависит от параметра maintenance_work_mem. Более высокие значения этого параметра сократят время, необходимое для создания индекса (если только заданное значение не превышает объем действительно доступной памяти, что влечет за собой использование подкачки).

QHB может создавать индексы, используя несколько процессоров, чтобы быстрее обрабатывать строки таблицы. Эта функция называется параллельным построением индекса. Для индексных методов, поддерживающих параллельное построение индексов (в настоящее время только В-дерево), maintenance_work_mem задает максимальный объем памяти, который может использовать каждая операция построения индекса в целом, независимо от количества запущенных рабочих процессов. Целесообразность использования параллельных процессов и их оптимальное количество обычно автоматически определяется моделью стоимости.

Параллельное построение индексов может выиграть от увеличения maintenance_work_mem там, где для аналогичного последовательного построения индекса выигрыша не будет или он будет минимальным. Обратите внимание, что maintenance_work_mem может влиять на количество запрашиваемых рабочих процессов, так как параллельные рабочие процессы должны иметь не менее 32 Мб из общего бюджета maintenance_work_mem. Кроме того, еще 32 Мб должно остаться для основного процесса. Увеличение max_parallel_maintenance_workers может позволить использовать больше исполнителей, что сократит время, необходимое для создания индекса (если только создание индекса уже не упирается в скорость ввода/вывода). Конечно, для этого должно быть достаточно процессорных ресурсов, которые иначе бы простаивали.

Если в команде ALTER TABLE задается значение для parallel_workers, это напрямую определяет, сколько параллельных рабочих процессов будет запрашивать для таблицы команда CREATE INDEX. При этом полностью игнорируется модель стоимости, и maintenance_work_mem не влияет на определение количества параллельных исполнителей. Если в ALTER TABLE параметр parallel_workers установлен равным 0, это полностью выключает параллельное построение индексов для этой таблицы.

Совет
Имеет смысл сбросить настройки parallel_workers после выполнения построения индекса. Это позволит избежать нежелательных изменений планов запросов, так как parallel_workers влияет на все параллельные сканирования таблицы.

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

Для удаления индекса используйте DROP INDEX.

Как и любая другая длительная транзакция, операция CREATE INDEX с таблицей может повлиять на то, какие кортежи может удалить параллельная операция VACUUM с какой-либо другой таблицей

Предыдущие выпуски системы также поддерживали индексный метод R-дерево. Этот метод был удален, поскольку не имел существенных преимуществ по сравнению с методом GiST. Указание USING rtree в CREATE INDEX будет интерпретироваться как USING GiST, чтобы упростить преобразование старых баз данных в GiST.

Примеры

Создание уникального индекса B-дерева по столбцу title в таблице films:

CREATE UNIQUE INDEX title_idx ON films (title);

Создание уникального индекса B-дерева по столбцу title, а также включенным столбцам director и rating в таблице films:

CREATE UNIQUE INDEX title_idx ON films (title) INCLUDE (director, rating);

Создание индекса B-дерева с выключенным механизмом дедупликации:

CREATE INDEX title_idx ON films (title) WITH (deduplicate_items = off);

Создание индекса по выражению lower(title), позволяющего эффективно выполнять поиск без учета регистра:

CREATE INDEX ON films ((lower(title)));

(В этом примере мы решили опустить имя индекса, чтобы его выбрала система; обычно это films_lower_idx.)

Создание индекса с нестандартным правилом сортировки:

CREATE INDEX title_idx_german ON films (title COLLATE "de_DE");

Создание индекса с нестандартным порядком сортировки значений NULL:

CREATE INDEX title_idx_nulls_low ON films (title NULLS FIRST);

Создание индекса с нестандартным коэффициентом заполнения:

CREATE UNIQUE INDEX title_idx ON films (title) WITH (fillfactor = 70);

Создание индекса GIN с выключенным механизмом быстрого обновления:

CREATE INDEX gin_idx ON documents_table USING GIN (locations) WITH (fastupdate = off);

Создание индекса по столбцу code в таблице films и размещение его в табличном пространстве indexspace:

CREATE INDEX code_idx ON films (code) TABLESPACE indexspace;

Создание индекса GiST по координатам точек, позволяющего эффективно использовать операторы box с результатом функции преобразования:

CREATE INDEX pointloc
    ON points USING gist (box(location,location));
SELECT * FROM points
    WHERE box(location,location) && '(0,0),(1,1)'::box;

Создание индекса без блокировки записи в таблицу:

CREATE INDEX CONCURRENTLY sales_quantity_index ON sales_table (quantity);

Совместимость

Команда CREATE INDEX является расширением QHB. В стандарте SQL нет положений для индексов.

См. также

ALTER INDEX, DROP INDEX, REINDEX

CREATE LANGUAGE

CREATE LANGUAGE — определить новый процедурный язык

Синтаксис

CREATE [ OR REPLACE ] [ PROCEDURAL ] LANGUAGE имя
CREATE [ OR REPLACE ] [ TRUSTED ] [ PROCEDURAL ] LANGUAGE имя
    HANDLER обработчик_вызова [ INLINE обработчик_внедренного_кода ] [ VALIDATOR функция_проверки ]

Описание

Команда CREATE LANGUAGE регистрирует в базе данных QHB новый процедурный язык. Впоследствии на этом новом языке могут быть определены функции и процедуры.

В сущности, CREATE LANGUAGE связывает имя языка с функциями-обработчиками, которые отвечают за выполнение функций, написанных на этом языке. Дополнительную информацию о языковых обработчиках см. в главе Процедурные языки.

Команда CREATE OR REPLACE LANGUAGE приведет либо к созданию нового языка, либо к замене существующего определения. Если язык уже существует, его параметры обновляются в соответствии со значениями, указанными в команде, но при этом владелец языка и права доступа не меняются, а все существующие функции, написанные на этом языке, считаются по-прежнему действительными.

Чтобы зарегистрировать новый язык или изменить параметры существующего языка, нужно иметь права суперпользователя QHB. Однако после создания языка его владельцем можно назначить и обычного пользователя, который затем может его удалить или переименовать, изменить права доступа к нему или назначить нового владельца. (Однако нельзя назначить обычного пользователя владельцем нижележащих функций на С, поскольку это откроет перед ним возможность повысить свои права.)

Форма CREATE LANGUAGE, которая не задает никакую функцию-обработчик, является устаревшей. Для обратной совместимости со старыми файлами дампа она интерпретируется как CREATE EXTENTION. Это сработает, если язык был упакован в расширение с тем же именем — как обычно и устанавливаются процедурные языки.

Параметры

TRUSTED

TRUSTED (доверенный) указывает, что язык не предоставляет доступ к данным, к которым пользователь не имел бы доступа без него. Если при регистрации языка это ключевое слово опущено, только пользователи с правами суперпользователя QHB смогут использовать этот язык для создания новых функций.

PROCEDURAL

Это слово не несет смысловой нагрузки.

имя

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

Для обратной совместимости имя может быть заключено в одинарные кавычки.

HANDLER обработчик_вызова

обработчик_вызова — это имя ранее зарегистрированной функции, которая будет вызвана для выполнения функций на этом процедурном языке. Обработчик вызовов для процедурного языка должен быть написан на компилируемом языке, например C с соглашениями о вызовах версии 1, и зарегистрирован в QHB как функция, не принимающая аргументы и возвращающая фиктивный тип language_handler, который просто используется для идентификации функции в качестве обработчика вызовов.

INLINE обработчик_внедренного_кода

обработчик_внедренного_кода — это имя ранее зарегистрированной функции, которая будет вызвана для выполнения анонимного блока кода (команды DO) на этом языке. Если функция обработчика_внедренного_кода не указана, язык не будет поддерживать анонимные блоки кода. Функция обработчика должна принимать один аргумент типа internal, который будет внутренним представлением команды DO, и он, как правило, возвращает тип void. Возвращаемое значение обработчика игнорируется.

VALIDATOR функция_проверки

функция_проверки — это имя ранее зарегистрированной функции, которая будет вызываться при создании новой функции на языке, чтобы ее проверить. Если функция проверки не указана, то новая функция при создании проверяться не будет. Функция проверки должна принимать один аргумент типа oid, который станет идентификатором создаваемой функции и обычно будет возвращать void.

Функция проверки обычно проверяет синтаксическую корректность тела функции, но также может анализировать и другие свойства функции, например поддержку определенных типов аргументов этим языком. Чтобы сигнализировать об ошибке, функция проверки должна использовать функцию ereport(). Возвращаемое значение функции игнорируется.

Примечания

Для удаления процедурных языков используйте DROP LANGUAGE.

Информация о текущих установленных языках содержится в системном каталоге pg_language. Кроме того, команда psql \dL показывает список установленных языков.

Чтобы создавать функции на процедурном языке, необходимо иметь право USAGE для языка. По умолчанию для доверенных языков право USAGE предоставляется роли PUBLIC (т.е. для всех), но при желании это можно отменить.

Процедурные языки являются локальными для отдельных баз данных. Тем не менее, язык может быть установлен в базу данных template1, вследствие чего язык будет автоматически доступен во всех созданных после этого базах данных.

Примеры

Минимальная запись для создания нового процедурного языка:

CREATE FUNCTION plsample_call_handler() RETURNS language_handler
    AS '$libdir/plsample'
    LANGUAGE C;
CREATE LANGUAGE plsample
    HANDLER plsample_call_handler;

Обычно эти команды записываются в скрипте установки расширения, и пользователи устанавливают его так:

CREATE EXTENSION plsample;

Совместимость

Команда CREATE LANGUAGE является расширением QHB.

См. также

ALTER LANGUAGE, CREATE FUNCTION, DROP LANGUAGE, GRANT, REVOKE

CREATE MATERIALIZED VIEW

CREATE MATERIALIZED VIEW — определить новое материализованное представление

Синтаксис

CREATE MATERIALIZED VIEW [ IF NOT EXISTS ] имя_таблицы
    [ (имя_столбца [, ...] ) ]
    [ USING метод ]
    [ WITH ( параметр_хранения [= значение] [, ... ] ) ]
    [ TABLESPACE имя_табличного_пространства ]
    AS запрос
    [ WITH [ NO ] DATA ]

Описание

Команда CREATE MATERIALIZED VIEW определяет материализованное представление запроса. Запрос выполняется и используется для заполнения представления во время выполнения команды (если только не указано WITH NO DATA). Обновить представление позже можно, выполнив REFRESH MATERIALIZED VIEW.

Команда CREATE MATERIALIZED VIEW подобна CREATE TABLE AS, за исключением того, что она еще и запоминает запрос, используемый для инициализации представления, чтобы его можно было обновить позже по требованию. Материализованное представление по многим свойствам сходно с таблицей, но в нём не поддерживаются временные материализованные представления.

Параметры

IF NOT EXISTS

Не выдавать ошибку, если материализованное представление с таким же именем уже существует. В этом случае выдается соответствующее уведомление. Обратите внимание, что нет никакой гарантии, что существующее материализованное представление как-то соотносится с тем, которое было бы создано.

имя_таблицы

Имя создаваемого материализованного представления (может быть дополнено схемой).

имя_столбца

Имя столбца в новом материализованном представлении. Если имена столбцов не указаны, они берутся из имен столбцов результата запроса.

USING метод

Это необязательное предложение определяет метод доступа к таблице, используемый для хранения содержимого для нового материализованного представления; этот метод должен быть типа TABLE. Дополнительную информацию см. в главе Определение интерфейса метода доступа к таблице. Если этот параметр не указан, то для нового материализованного представления выбирается метод доступа к таблице по умолчанию. Дополнительную информацию см. в разделе default_table_access_method.

WITH ( параметр_хранения [= значение] [, ... ] )

В этом предложении указываются необязательные параметры хранения для нового материализованного представления; дополнительную информацию см. в разделе Параметры хранения описания команды CREATE TABLE. Все параметры, которые поддерживает CREATE TABLE, поддерживает и CREATE MATERIALIZED VIEW. Дополнительную информацию см. в разделе CREATE TABLE.

TABLESPACE имя_табличного_пространства

имя_табличного_пространства — это имя табличного пространства, в котором будет создано новое материализованное представление. Если не указано, то выбирается default_tablespace.

запрос

Команда SELECT, TABLE или VALUES. Этот запрос будет выполняться в рамках операции с ограничениями безопасности; в частности, вызовы функций, которые сами создают временные таблицы, завершатся ошибкой.

WITH [ NO ] DATA

Это предложение указывает, следует ли заполнять материализованное представление во время его создания. Если материализованное представление не наполняется, оно помечается как нечитаемое, так что к нему нельзя будет обращаться до выполнения REFRESH MATERIALIZED VIEW.

Совместимость

Команда CREATE MATERIALIZED VIEW является расширением QHB.

См. также

ALTER MATERIALIZED VIEW, CREATE TABLE AS, CREATE VIEW, DROP MATERIALIZED VIEW, REFRESH MATERIALIZED VIEW

CREATE OPERATOR CLASS

CREATE OPERATOR CLASS — определить новый класс операторов

Синтаксис

CREATE OPERATOR CLASS имя [ DEFAULT ] FOR TYPE тип_данных
  USING индексный_метод [ FAMILY имя_семейства ] AS
  {  OPERATOR номер_стратегии имя_оператора [ ( тип_операнда, тип_операнда ) ] [ FOR SEARCH | FOR ORDER BY имя_семейства_сортировки ]
   | FUNCTION номер_вспомогательной_функции [ ( тип_операнда [ , тип_операнда ] ) ] имя_функции ( тип_аргумента [, ...] )
   | STORAGE тип_хранения
  } [, ... ]

Описание

Команда CREATE OPERATOR CLASS создает новый класс операторов. Класс операторов определяет, как конкретный тип данных может использоваться с индексом. Класс операторов указывает, что некоторые операторы будут заполнять для этого типа данных и этого индексного метода определенные роли или «стратегии». Класс операторов также определяет вспомогательные функции, которые будут использоваться индексным методом при выборе класса операторов для столбца индекса. Все операторы и функции, используемые классом операторов, должны быть определены еще до появления возможности его создать.

Если задано имя схемы, то класс операторов создается в указанной схеме, в противном случае — в текущей схеме. Два класса операторов в одной схеме могут иметь одинаковое имя только в том случае, если они предназначены для разных индексных методов.

Пользователь, определяющий класс операторов, становится его владельцем. В настоящее время создавать классы операторов могут только суперпользователи. (Причина этого ограничения в том, что ошибочное определение класса операторов может вызвать нарушения или даже сбой в работе сервера.)

В настоящее время CREATE OPERATOR CLASS не проверяет, включает ли определение класса оператора все операторы и функции, требуемые индексным методом, и образуют ли операторы и функции целостный набор. Ответственность за определение корректного класса операторов лежит на пользователе.

Связанные классы операторов можно сгруппировать в семейства операторов. Чтобы добавить новый класс оператора в существующее семейство, укажите параметр FAMILY в команде CREATE OPERATOR CLASS. Без этого параметра новый класс помещается в семейство с тем же именем, что и класс (если такое семейство не существует, оно создается).

Дополнительную информацию см. в разделе Интерфейсные расширения для индексов.

Параметры

имя

Имя создаваемого класса операторов (может быть дополнено схемой).

DEFAULT

При наличии этого указания класс операторов становится классом по умолчанию для своего типа данных. У конкретного типа данных и индексного метода не может быть более одного класса операторов по умолчанию.

тип_данных

Тип данных столбца, для которого предназначен этот класс операторов.

индексный метод

Имя индексного метода, для которого предназначен этот класс операторов.

имя_семейства

Имя существующего семейства операторов, в которое будет добавлен этот класс. Если не указано, используется семейство с тем же именем, что и у класса оператора (если такое семейство не существует, оно создается).

номер_стратегии

Номер стратегии индексного метода для оператора, связанного с классом оператора.

имя_оператора

Имя оператора, связанного с данным классом оператора (может быть дополнено схемой).

тип_операнда

В предложении OPERATOR это тип (или типы) данных операнда (либо ключевое слово NONE), характеризующий левый унарный или правый унарный оператор. В нормальных условиях, когда типы данных операнда совпадают с типом данных класса оператора, их можно опустить.

В предложении FUNCTION это тип (или типы) данных операнда, для поддержки которого предназначена эта функция, если тот отличается от типа (или типов) входных данных функции (для функций сравнения B-дерева и хеш-функций) или типа данных класса (для вспомогательных функций сортировки B-дерева и всех функций в классах операторов GiST, SP-GiST, GIN и BRIN). Обычно предполагаемые по умолчанию типы оказываются верными, так что тип_операнда указывать в FUNCTION не нужно (исключение составляют функции сортировки B-дерева, предназначенные для сравнений разных типов данных).

имя_семейства_сортировки

Имя существующего семейства операторов В-деревьев, описывающего порядок сортировки, связанный с оператором сортировки (может быть дополнено схемой).

Если не указано ни FOR SEARCH (для поиска), ни FOR ORDER BY (для сортировки), по умолчанию подразумевается FOR SEARCH.

номер_вспомогательной_функции

Номер вспомогательной функции индексного метода для функции, связанной с классом операторов.

имя_функции

Имя функции, которая является вспомогательной функцией индексного метода для класса операторов (может быть дополнено схемой).

тип_аргумента

Тип (или типы) данных параметра функции.

тип_хранения

Тип данных, фактически сохраненных в индексе. Обычно это то же самое, что и тип данных столбца, но некоторые индексные методы (в настоящее время GiST, GIN и BRIN) могут работать с отличающимся типом. Если тип_данных столбца задан как anyarray, тип_хранения может быть объявлен как anyelement, чтобы показать, что записи в индексе являются членами типа элемента, принадлежащего к фактическому типу массива, для которого создается конкретный индекс.

Предложения OPERATOR, FUNCTION и STORAGE могут появляться в любом порядке.

Примечания

Поскольку механизм индексирования не проверяет разрешения на доступ к функциям перед их использованием, включение функции или оператора в класс операторов равносильно предоставлению общедоступного разрешения на их выполнение. Обычно это не проблема для разновидностей функций, которые бывают полезны в классе операторов.

Операторы не должны определяться функциями SQL. Функция SQL, скорее всего, будет встроена в вызывающий запрос, что помешает оптимизатору распознать, что запрос соответствует индексу.

Примеры

Команда в следующем примере определяет класс операторов индекса GiST для типа данных _int4 (массива из int4). Полный пример приведен в модуле intarray.

CREATE OPERATOR CLASS gist__int_ops
    DEFAULT FOR TYPE _int4 USING gist AS
        OPERATOR        3       &&,
        OPERATOR        6       = (anyarray, anyarray),
        OPERATOR        7       @>,
        OPERATOR        8       <@,
        OPERATOR        20      @@ (_int4, query_int),
        FUNCTION        1       g_int_consistent (internal, _int4, smallint, oid, internal),
        FUNCTION        2       g_int_union (internal, internal),
        FUNCTION        3       g_int_compress (internal),
        FUNCTION        4       g_int_decompress (internal),
        FUNCTION        5       g_int_penalty (internal, internal, internal),
        FUNCTION        6       g_int_picksplit (internal, internal),
        FUNCTION        7       g_int_same (_int4, _int4, internal);

Совместимость

Команда CREATE OPERATOR CLASS является расширением QHB. В стандарте SQL такой команды нет.

См. также

ALTER OPERATOR CLASS, DROP OPERATOR CLASS, CREATE OPERATOR FAMILY, ALTER OPERATOR FAMILY

CREATE OPERATOR FAMILY

CREATE OPERATOR FAMILY — определить новое семейство операторов

Синтаксис

CREATE OPERATOR FAMILY имя USING индексный_метод

Описание

Команда CREATE OPERATOR FAMILY создает новое семейство операторов. Семейство операторов определяет набор связанных классов операторов и, возможно, некоторые дополнительные операторы и вспомогательные функции, совместимые с этими классами операторов, но несущественные для функционирования любого отдельного индекса. (Операторы и функции, которые важны для индексов, следует сгруппировать в пределах соответствующего класса операторов, а не «слабо связывать» в семействе операторов. Как правило, операторы с одним типом данных привязаны к классам операторов, тогда как операторы с перекрестными типами данных могут быть слабо связаны в семействе операторов, содержащем классы операторов для обоих типов данных.)

Новое семейство операторов изначально пустое. Его нужно заполнить командами CREATE OPERATOR CLASS, которые добавят в него классы операторов, и, возможно, командами ALTER OPERATOR FAMILY, которые добавят «слабосвязанные» операторы и соответствующие вспомогательные функции.

Если задано имя схемы, то семейство операторов создается в указанной схеме, в противном случае — в текущей схеме. Два семейства операторов в одной схеме могут иметь одинаковое имя только в том случае, если они предназначены для разных индексных методов.

Пользователь, определяющий семейство операторов, становится его владельцем. В настоящее время для создания семейства операторов нужно быть суперпользователем. (Причина этого ограничения в том, что ошибочное определение семейства операторов может вызвать нарушения или даже сбой в работе сервера.)

Дополнительную информацию см. в разделе Интерфейсные расширения для индексов.

Параметры

имя

Имя создаваемого семейства операторов (может быть дополнено схемой).

индексный метод

Имя индексного метода, для которого предназначено это семейство операторов.

Совместимость

Команда CREATE OPERATOR FAMILY является расширением QHB. В стандарте SQL такой команды нет.

См. также

ALTER OPERATOR FAMILY, DROP OPERATOR FAMILY, CREATE OPERATOR CLASS, ALTER OPERATOR CLASS, DROP OPERATOR CLASS

CREATE OPERATOR

CREATE OPERATOR — определить новый оператор

Синтаксис

CREATE OPERATOR имя (
    {FUNCTION|PROCEDURE} = имя_функции
    [, LEFTARG = тип_слева ] [, RIGHTARG = тип_справа ]
    [, COMMUTATOR = коммут_оператор ] [, NEGATOR = обратный_оператор ]
    [, RESTRICT = процедура_ограничения ] [, JOIN = процедура_соединения ]
    [, HASHES ] [, MERGES ]
)

Описание

Команда CREATE OPERATOR определяет новый оператор, имя. Пользователь, создавший оператор, становится его владельцем. Если задано имя схемы, то оператор создается в указанной схеме, если нет — в текущей.

Имя оператора представляет собой последовательность из нескольких символов (не более чем NAMEDATALEN-1, по умолчанию 63) из следующего списка:

+ - * / < > = ~ ! @ # % ^ & | ` ?

Есть несколько ограничений на выбор имени:

-- и /* в имени оператора присутствовать не могут, так как они приняты в качестве начала комментария.

Многосимвольное имя оператора не может заканчиваться на + или -, если только имя также не содержит хотя бы один из этих символов:

   ~ ! @ \# % ^ &  ‘ ?

Например, @- является допустимым именем оператора, а *- не является. Это ограничение позволяет QHB анализировать SQL-совместимые команды, не требуя пробелов между токенами.

Использование => в качестве имени оператора считается устаревшим и может быть окончательно запрещено в будущих выпусках.

Оператор != при вводе отображается в <>, так что эти два имени всегда равнозначны.

Необходимо обязательно определить LEFTARG либо RIGHTARG, а для бинарных операторов — оба аргумента. Для правых унарных операторов должно быть определено только LEFTARG, тогда как для левых унарных операторов — только RIGHTARG.

Примечание
Правые унарные операторы, также называемые постфиксными, являются устаревшими и будут удалены в следующей версии QHB.

Функция имя_функции должна быть заранее определена с помощью CREATE FUNCTION и таким образом, чтобы принимать правильное числа аргументов (одного или двух) из указанных типов.

В синтаксисе CREATE OPERATOR ключевые слова FUNCTION и PROCEDURE равнозначны, но указанная функция должна в любом случае быть функцией, а не процедурой. Использование ключевого слова PROCEDURE здесь поддерживается по историческим причинам и считается устаревшим.

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

Чтобы создать оператор, необходимо иметь право USAGE для типов аргументов и результата, а также право EXECUTE для нижележащей функции. Если указывается коммутативный или обратный оператор, нужно быть его владельцем.

Параметры

имя

Имя определяемого оператора. Допустимые символы см. выше. Имя может быть дополнено схемой, например, так: CREATE OPERATOR myschema.+ (...). Если схема не указана, то оператор создается в текущей схеме. Если два оператора в одной схеме работают с разными типами данных, они могут иметь одинаковое имя. Это называется перегрузкой.

имя_функции

Функция, используемая для реализации этого оператора.

тип_слева

Тип данных левого операнда оператора, если таковой имеется. Этот параметр будет опущен для левых унарных операторов.

тип_справа

Тип данных правого операнда оператора, если таковой имеется. Этот параметр будет опущен для правых унарных операторов.

коммут_оператор

Оператор, коммутирующий для данного.

обратный_оператор

Оператор, обратный для данного.

процедура_ограничения

Функция оценки селективности ограничения для этого оператора.

процедура_соединения

Функция оценки селективности соединения для этого оператора.

HASHES

Указывает, что этот оператор может поддерживать хеш-соединение.

MERGES

Указывает, что этот оператор может поддерживать соединение слиянием.

Чтобы задать имя оператора с указанием схемы в коммут_оператор или другом дополнительном аргументе, применяется синтаксис OPERATOR(), например:

COMMUTATOR = OPERATOR(myschema.===) ,

Примечания

Указать лексический приоритет оператора в команде CREATE OPERATOR невозможно, поскольку поведение приоритета синтаксического анализатора реализовано аппаратно. См. раздел Приоритеты операторов.

Устаревшие опции SORT1, SORT2, LTCMP и GTCMP ранее использовались для указания имен операторов сортировки, связанных с оператором, применяемым при соединении слиянием. В этом больше нет необходимости, так как сведения о связанных операторах можно найти, обратившись вместо этого к семействам операторов B-деревьев. Если в команде отсутствует явное указание MERGES, все эти параметры игнорируются.

Для удаления из базы данных пользовательских операторов используйте DROP OPERATOR, а для изменения их свойств — ALTER OPERATOR.

Примеры

Следующая команда определяет новый оператор, проверяющий равенство площадей, для типа данных box:

CREATE OPERATOR === (
    LEFTARG = box,
    RIGHTARG = box,
    FUNCTION = area_equal_function,
    COMMUTATOR = ===,
    NEGATOR = !==,
    RESTRICT = area_restriction_function,
    JOIN = area_join_function,
    HASHES, MERGES
);

Совместимость

Команда CREATE OPERATOR является расширением QHB. В стандарте SQL нет определения для пользовательских операторов.

См. также

ALTER OPERATOR, CREATE OPERATOR CLASS, DROP OPERATOR

CREATE POLICY

CREATE POLICY — определить для таблицы новую политику защиты на уровне строк

Синтаксис

CREATE POLICY имя ON имя_таблицы
    [ AS { PERMISSIVE | RESTRICTIVE } ]
    [ FOR { ALL | SELECT | INSERT | UPDATE | DELETE } ]
    [ TO { имя_роли | PUBLIC | CURRENT_USER | SESSION_USER } [, ...] ]
    [ USING ( выражение_USING ) ]
    [ WITH CHECK ( выражение_CHECK ) ]

Описание

Команда CREATE POLICY определяет для таблицы новую политику защиты на уровне строк. Обратите внимание, что для применения созданных политик нужно включить для таблицы защиту на уровне строк (используя ALTER TABLE ... ENABLE ROW LEVEL SECURITY).

Политика предоставляет разрешение на выбор, добавление, обновление или удаление строк, удовлетворяющих соответствующему выражению политики. Существующие строки таблицы сравниваются с выражением, указанным в в USING, в то время как новые строки, которые будут созданы с помощью INSERT или UPDATE, проверяются на соответствие выражению, указанному в WITH CHECK. Когда выражение USING возвращает для данной строки true, эта строка видна пользователю, а если возвращается false или NULL, то не видна. Когда выражение WITH CHECK возвращает для строки значение true, эта строка добавляется или обновляется, а если возвращается значение false или NULL, то возникает ошибка.

Для операторов INSERT и UPDATE выражения WITH CHECK применяются после того, как срабатывают триггеры *BEFORE, и прежде, чем будут сделаны какие-либо фактические изменения данных. Таким образом, триггер BEFORE ROW может изменять добавляемые данные, влияя на результат проверки политики защиты. Выражения WITH CHECK применяются перед любыми другими ограничениями.

Имена политик указаны на уровне таблицы. Поэтому одно имя политики может использоваться для нескольких разных таблиц и в каждой дать отдельное определение политики, подходящее этой конкретной таблице.

Политики могут применяться для определенных команд или для определенных ролей. По умолчанию для вновь созданных политик они применяются для всех команд и ролей, если не указано иное. К одной команде может применяться несколько политик; дополнительную информацию см. ниже. Ниже в таблице 256 показано, как различные типы политик применяются к определенным командам.

Для политик, которые могут иметь и выражения USING, и выражения WITH CHECK (ALL и UPDATE), в случае отсутствия выражения WITH CHECK выражение USING будет использоваться для определения того, какие строки будут видимыми (обычное назначение USING) и какие новые строки будет разрешено добавить (назначение WITH CHECK)

Если для таблицы включена защита на уровне строк, но применимые политики отсутствуют, то предполагается политика «по умолчанию запретить», чтобы никакие строки не были видны или доступны для обновления.

Параметры

имя

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

имя_таблицы

Имя таблицы, к которой применяется политика (может быть дополнено схемой).

PERMISSIVE

Указывает, что политика должна быть создана как разрешительная. Все разрешительные политики, применимые к данному запросу, будут объединены с помощью логического оператора «ИЛИ». Создавая разрешительные политики, администраторы могут расширить множество записей, к которым можно обращаться. По умолчанию политики являются разрешительными.

RESTRICTIVE

Указывает, что политика должна быть создана как ограничительная. Все ограничительные политики, применимые к данному запросу, будут объединены с помощью логического оператора «И». Создавая ограничительные политики, администраторы могут сократить множество записей, к которым можно обращаться, так как для каждой записи должны удовлетворяться все ограничительные политики.

Обратите внимание, что для предоставления доступа к записям необходима хотя бы одна разрешительная политика, и только после этого возможно успешное использование ограничительных политик для сокращения этого доступа. Если существуют только ограничительные политики, то никакие записи не будут доступны. При наличии сочетания разрешительных и ограничительных политик запись доступна только в том случае, если кроме всех ограничительных политик удовлетворяется как минимум одна из разрешительных.

команда

Команда, к которой применяется политика. Допустимые варианты: ALL, SELECT, INSERT, UPDATE и DELETE. По умолчанию подразумевается ALL (все). Особенности их применения описаны ниже.

имя_роли

Роль (или роли), к которой будет применяться политика. Значение по умолчанию равно PUBLIC, то есть политика применяется ко всем ролям.

выражение_USING

Произвольное условное выражение SQL (возвращающее boolean). Условное выражение не может содержать никаких агрегатных или оконных функций. Это выражение будет добавлено в запросы, обращающиеся к данной таблице, если включена защита на уровне строк. Строки, для которых выражение возвращает true, будут видны. Любые строки, для которых выражение возвращает значение false или NULL, не будут видны пользователю (в запросе SELECT) и не будут доступны для изменения (в запросе UPDATE или DELETE). Такая строка просто пропускается, ошибка при этом не выдается.

выражение_CHECK

Произвольное условное выражение SQL (возвращающее boolean). Условное выражение не может содержать никаких агрегатных или оконных функций. Это выражение будет использоваться в запросах INSERT и UPDATE к таблице, если включена защита на уровне строк, так что в них принимаются только те строки, для которых оно выдает true. Если это выражение выдает false или NULL для любой из добавляемых записей или записей, получаемых при изменении, выдается ошибка. Обратите внимание, что выражение_CHECK вычисляется для предлагаемого нового содержимого строки, а не для существующих данных.

Политики по командам

ALL

Указание ALL для политики означает, что она будет применяться ко всем командам, независимо от типа последних. Если существует политика ALL и другие более детализированные политики, то будут применяться и политика ALL, и более детализированные политики. Вдобавок политики ALL будут применяться как к стороне выборки, так и к стороне изменения данных в запросе, используя в обоих случаях выражение USING, если определено только оно.

Например, когда выполняется UPDATE, политика ALL будет фильтровать и строки, которые UPDATE сможет выбрать для изменения (применяя выражение USING), и итоговые измененные строки, проверяя, разрешено ли записать их в таблицу (применяя выражение WITH CHECK, если оно определено, или, в противном случае, USING). Если команда INSERT или UPDATE пытается добавить в таблицу строки, не удовлетворяющие выражению WITH CHECK политики ALL, вся команда будет прервана.

SELECT

Указание SELECT для политики означает, что она будет применяться к запросам SELECT всякий раз, когда при обращении к отношению, для которого определена политика, требуется право SELECT. Результатом является то, что запрос SELECT выдаст только те записи из отношения, которые удовлетворят политике SELECT, и запросы, использующие право SELECT, например запрос UPDATE, увидят только записи, разрешенные политикой SELECT. Для политики SELECT не может задаваться выражение WITH CHECK, так как оно действует, только когда записи читаются из отношения.

INSERT

Указание INSERT для политики означает, что она будет применяться к командам INSERT. Попытка добавления строк, которые не удовлетворят этой политике, приведет к ошибке нарушения политики, и вся команда INSERT будет прервана. Для политики INSERT не может задаваться выражение USING, так как она действует, только когда в отношение добавляются записи.

Обратите внимание, что INSERT с указанием ON CONFLICT DO UPDATE проверяет выражения WITH CHECK политик INSERT только для строк, добавляемых в отношение по пути INSERT.

UPDATE

Указание UPDATE для политики означает, что она будет применяться к командам UPDATE, SELECT FOR UPDATE и SELECT FOR SHARE, а также к дополнительным предложениям ON CONFLICT DO UPDATE команд INSERT. Так как UPDATE подразумевает извлечение существующей записи и замену ее новой измененной записью, политики UPDATE принимают оба выражения: и USING, и WITH CHECK. Выражение USING определяет, какие записи команда UPDATE будет видеть для последующего изменения, в то время как выражение WITH CHECK определяет, какие измененные строки могут быть сохранены в отношении.

Любые строки, обновленные значения которых не будут удовлетворять выражению WITH CHECK, вызовут ошибку, и вся команда будет прервана. Если указывается только предложение USING, оно будет применяться и в качестве собственно USING, и в качестве выражения WITH CHECK.

Как правило, команда UPDATE также должна считывать данные из столбцов подлежащего изменению отношения (например, в предложении WHERE или RETURNING либо в выражении в правой части предложения SET). В этом случае также требуется иметь права SELECT в изменяемом отношении, а в дополнение к политикам UPDATE будут применяться соответствующие политики SELECT или ALL. Таким образом, пользователь должен иметь доступ к изменяемой строке (или строкам) через политику UPDATE или ALL, а также разрешение на изменение этой строки (или строк) от политики *SELECT или ALL.

Если команда INSERT указана со вспомогательным предложением ON CONFLICT DO UPDATE, то при выборе пути UPDATE сначала подлежащая изменению строка проверяется по выражениям USING всех политик UPDATE, а затем новая обновленная строка проверяется по выражениям WITH CHECK. Обратите внимание, однако, что, в отличие от отдельной команды UPDATE, если существующая строка не удовлетворяет выражениям USING, будет выдаваться ошибка (путь UPDATE никогда не пропускается неявно).

DELETE

Указание DELETE для политики означает, что она будет применяться к командам DELETE. Команде DELETE будут видны только те строки, которые разрешит эта команда. При этом среди них могут быть строки, которые видны через SELECT, но если они не удовлетворяют выражению USING политики DELETE, то удалить их будет нельзя.

В большинстве случаев команде DELETE также требуется считывать данные из столбцов в отношении, из которого осуществляется удаление (например, в предложении WHERE или RETURNING). В этом случае также требуются права SELECT на отношения, и в дополнение к политикам DELETE будут применяться соответствующие политики SELECT или ALL. Таким образом, пользователь должен иметь доступ к удаляемой строке (или строкам) через политику SELECT или ALL, а также разрешение на удаление этой строки (или строк) от политики DELETE или ALL.

Для политики DELETE не может задаваться выражение WITH CHECK, так как она применяется только тогда, когда записи удаляются из отношения, а в этом случае новые строки, подлежащие проверке, отсутствуют.

Таблица 256. Политики, применяемые по типу команды

Команда | Политика SELECT/ALL Выражение USING | Политика INSERT/ALL Выражение WITH CHECK | Политика UPDATE/ALL Выражение USING | Политика UPDATE/ALL Выражение WITH CHECK | Политика DELETE/ALL
SELECT | Существующая строка | - | - | - | -
SELECT FOR UPDATE/SHARE | Существующая строка | - | Существующая строка | - | -
INSERT | - | Новая строка | - | - | -
INSERT ... RETURNING | Новая строка ¹ | Новая строка | - | - | -
UPDATE | Существующие и новые строки ¹ | - | Существующая строка | Новая строка | -
DELETE | Существующая строка ¹ | - | - | - | Существующая строка
ON CONFLICT DO UPDATE | Существующие и новые строки | - | Существующая строка | Новая строка | -

¹

Если для существующей или новой строки требуется доступ на чтение (например, предложения WHERE или RETURNING, обращающиеся к столбцам отношения).

Применение нескольких политик

Если к одной команде применяются несколько политик различных типов команд (например, политики SELECT и UPDATE применяются к команде UPDATE), пользователь должен иметь оба типа разрешений (например, разрешение на выбор строк из отношения и разрешение на их обновление). Таким образом, выражения для одного типа политики комбинируются с выражениями для другого типа политики с помощью оператора И.

Если к одной команде применяется несколько политик одного и того же типа команд, то должна быть хотя бы одна политика PERMISSIVE (разрешительная), предоставляющая доступ к отношению, а также должны удовлетворяться все политики RESTRICTIVE (ограничительные). Таким образом, выражения всех политик PERMISSIVE объединяются с помощью ИЛИ, выражения всех политик RESTRICTIVE объединяются при помощи И и полученные результаты также объединяются при помощи И. Если политики PERMISSIVE отсутствуют, доступ запрещается.

Обратите внимание, что в целях объединения нескольких политик политики ALL рассматриваются как политики любого применимого в данном случае типа.

Например, в команде UPDATE, требующей разрешения и для SELECT, и для UPDATE, в случае существования нескольких применимых политик каждого типа они будут объединяться следующим образом:

выражение from RESTRICTIVE SELECT/ALL policy 1
AND
выражение from RESTRICTIVE SELECT/ALL policy 2
AND
...
AND
(
  выражение from PERMISSIVE SELECT/ALL policy 1
  OR
  выражение from PERMISSIVE SELECT/ALL policy 2
  OR
  ...
)
AND
выражение from RESTRICTIVE UPDATE/ALL policy 1
AND
выражение from RESTRICTIVE UPDATE/ALL policy 2
AND
...
AND
(
  выражение from PERMISSIVE UPDATE/ALL policy 1
  OR
  выражение from PERMISSIVE UPDATE/ALL policy 2
  OR
  ...
)

Примечания

Чтобы создавать или изменять политики для таблицы, нужно быть ее владельцем.

Хотя политики будут применяться для явных запросов к таблицам в базе данных, они не применяются, когда система выполняет внутренние проверки целостности ссылок или проверяет ограничения. Это означает, что существуют косвенные способы определить, что данное значение существует. Примером этого является попытка добавить повторяющееся значение в столбец, образующий первичный ключ или имеющий ограничение уникальности. Если добавление завершается неудачно, то пользователь может сделать вывод, что значение уже существует. (В этом примере предполагается, что пользователю разрешено политикой добавлять записи, которые он не может видеть.) Другой пример — это когда пользователю разрешено добавлять записи в таблицу, которая ссылается на другую, иным образом не видимую. Существование значения может быть определено пользователем, который добавляет значения в подчиненную таблицу, где успешный результат операции будет признаком того, что значение существует в главной таблице. Эти проблемы могут быть решены путем тщательной разработки политик, чтобы полностью запретить пользователям добавлять, удалять или обновлять записи, которые могут указывать на не видимое иным образом значение, либо путем использования генерируемых значений (например, суррогатных ключей) вместо ключей с внешними значениями.

Как правило, система будет применять условия фильтрации, введенные с помощью политик безопасности, до условий в запросах пользователя, чтобы предотвратить нежелательную утечку защищаемых данных через пользовательские функции, которые могут не быть доверенными. Однако функции и операторы, помеченные системой (или системным администратором) как LEAKPROOF, могут вычисляться до условий политики, так как они считаются доверенными.

Поскольку выражения политики добавляются непосредственно в запрос пользователя, они будут выполняться с правами пользователя, выполняющего запрос. Таким образом, пользователи, на которых распространяется заданная политика, должны иметь доступ ко всем таблицам или функциям, на которые ссылается выражение, иначе при попытке обращения к таблице, у которой включена защита на уровне строк, они просто получат ошибку «отказано в доступе». Однако это не меняет способ работы представлений. Как и с обычными запросами и представлениями, проверки разрешений и политики для таблиц, на которые ссылается представление, будут использовать права владельца представления и все политики, распространяющиеся на этого владельца.

Дополнительное описание и практические примеры можно найти в разделе Политики безопасности строк.

Совместимость

Команда CREATE POLICY является расширением QHB.

См. также

ALTER POLICY, DROP POLICY, ALTER TABLE

CREATE PROCEDURE

CREATE PROCEDURE — определить новую процедуру

Синтаксис

CREATE [ OR REPLACE ] PROCEDURE
    имя ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ { DEFAULT | = } выражение_по_умолчанию ] [, ...] ] )
  { LANGUAGE имя_языка
    | TRANSFORM { FOR TYPE имя_типа } [, ... ]
    | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
    | SET параметр_конфигурации { TO значение | = значение | FROM CURRENT }
    | AS 'определение'
    | AS 'объектный_файл', 'объектный_символ'
  } ...

Описание

Команда CREATE PROCEDURE определяет новую процедуру. CREATE OR REPLACE PROCEDURE будет либо создавать новую процедуру, либо заменять существующее определение. Чтобы иметь возможность определить процедуру, необходимо иметь право USAGE для соответствующего языка.

Если указано имя схемы, то процедура создается в указанной схеме, в противном случае — в текущей. Имя новой процедуры не должно совпадать ни с одной существующей процедурой или функцией с теми же типами входных аргументов в той же схеме. Однако процедуры и функции с различными типами аргументов могут иметь одинаковое имя (это называется перегрузкой ).

Чтобы заменить текущее определение существующей процедуры, используйте CREATE OR REPLACE PROCEDURE. Таким способом невозможно изменить имя или типы аргументов процедуры (если вы попытаетесь, то фактически создадите новую, отличную процедуру).

Если CREATE OR REPLACE PROCEDURE используется для замены существующей процедуры, владелец и права доступа к этой процедуре не меняются. Всем остальным свойствам процедуры присваиваются значения, указанные в команде явно или подразумеваемые по умолчанию. Чтобы заменить процедуру, нужно быть ее владельцем (или быть членом роли-владельца).

Пользователь, создающий процедуру, становится ее владельцем.

Чтобы создать процедуру, необходимо иметь право USAGE для типов ее аргументов.

Параметры

имя

Имя создаваемой процедуры (может быть дополнено схемой).

режим_аргумента

Режим аргумента: IN, INOUT или VARIADIC. Если этот параметр опущен, значение по умолчанию равно IN. (Аргументы OUT в настоящее время для процедур не поддерживаются. Вместо них можно воспользоваться INOUT.)

имя_аргумента

Имя аргумента.

тип_аргумента

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

В зависимости от языка реализации также может быть разрешено указывать «псевдотипы», такие как cstring. Псевдотипы показывают, что фактический тип аргумента либо указан не полностью, либо находится вне набора обычных типов данных SQL.

Ссылка на тип столбца записывается в виде имя_таблицы.имя_столбца%TYPE. Использование этой функции иногда может помочь сделать процедуру независимой от изменений в определении таблицы.

выражение_по_умолчанию

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

имя_языка

Имя языка, на котором реализована процедура. Это может быть sql, c, internal или имя определяемого пользователем процедурного языка, например plpgsql. Стиль написания этого имени в апострофах считается устаревшим и требует точного совпадения регистра.

TRANSFORM { FOR TYPE имя_типа } [, ... ] }

Устанавливает список трансформаций, которые должны применяться при вызове процедуры. Трансформации выполняют преобразования между типами SQL и языковыми типами данных; см. раздел CREATE TRANSFORM. Преобразования встроенных типов обычно жестко предопределены в реализациях процедурных языков, так что их здесь указывать не нужно. Если реализация процедурного языка не знает, как обрабатывать тип, и трансформация не предоставляется, будет выполнено преобразование типов данных по умолчанию, но это зависит от реализации.

[EXTERNAL] SECURITY INVOKER
[EXTERNAL] SECURITY DEFINER

Характеристика SECURITY INVOKER (безопасность вызывающего) указывает, что процедура должна выполняться с правами вызывающего ее пользователя. Это значение по умолчанию. Вариант SECURITY DEFINER (безопасность определившего) указывает, что процедура должна выполняться с правами пользователя, которому она принадлежит (владельца).

Ключевое слово EXTERNAL (внешняя) допускается для соответствия стандарту SQL, но является необязательным, так как, в отличие от SQL, эта характеристика распространяется на все процедуры, а не только внешние.

В процедуре с характеристикой SECURITY DEFINER не могут выполняться операторы управления транзакциями (например, COMMIT и ROLLBACK в некоторых языках).

параметр_конфигурации
значение

Предложение SET определяет, что при вызове процедуры указанный параметр конфигурации должен принять заданное значение, а затем, по завершении процедуры, восстановить свое предыдущее значение. Предложение SET FROM CURRENT сохраняет значение параметра, действующее при выполнении CREATE PROCEDURE, в качестве значения, которое будет применено при входе в процедуру.

Если в определение процедуры добавлено SET, то действие команды SET LOCAL, выполняемой внутри процедуры для той же переменной, ограничивается процедурой: предыдущее значение параметра конфигурации восстанавливается по завершении процедуры. Однако обычная команда SET (без LOCAL) переопределяет значение SET, как и предыдущую команду SET LOCAL: эффекты команды сохраняются после завершения процедуры, если не случится откат текущей транзакции.

Если к определению процедуры добавлено SET, то в этой процедуре не смогут выполняться операторы управления транзакциями (например, COMMIT и ROLLBACK в некоторых языках).

Дополнительную информацию о допустимых именах и значениях параметров см. в разделе SET и главе Конфигурация сервера.

определение

Строковая константа, определяющая процедуру; значение зависит от языка. Это может быть имя внутренней процедуры, путь к объектному файлу, команда SQL или код на процедурном языке.

Часто бывает полезно заключать определение процедуры в знаки доллара (см. раздел Строковые константы с экранированием знаками доллара), а не в традиционные апострофы. Если не использовать знаки доллара, то любые одиночные кавычки или обратные косые черты в определении процедуры должны быть экранированы путем удвоения.

объектный_файл, объектный_символ

Это форма предложения AS используется для динамически загружаемых процедур на языке C, когда имя процедуры в исходном коде языка C не совпадает с именем процедуры SQL. Строка объектный_файл является именем файла разделяемой библиотеки, содержащего скомпилированную процедуру на C, и интерпретируется как параметр команды LOAD. Строка объектный_символ задает скомпонованный символ процедуры, то есть имя процедуры в исходном коде языка C. Если объектный символ опущен, предполагается, что он совпадает с именем определяемой процедуры SQL.

При повторных вызовах CREATE PROCEDURE ссылается на один и тот же объектный файл, который загружается только один раз за сеанс. Чтобы выгрузить и перезагрузить файл (возможно, во время разработки), запустите новый сеанс.

Примечания

Дополнительную информацию о создании функций, которые также применяются к процедурам, см. в разделе CREATE FUNCTION.

Чтобы выполнить процедуру, используйте команду CALL.

Примеры

CREATE PROCEDURE insert_data(a integer, b integer)
LANGUAGE SQL
AS $$
INSERT INTO tbl VALUES (a);
INSERT INTO tbl VALUES (b);
$$;

CALL insert_data(1, 2);

Совместимость

Команда CREATE PROCEDURE определена в стандарте SQL. Реализация QHB близка к стандартизированной, но совместима с ней не полностью. Дополнительную информацию см. в разделе CREATE FUNCTION.

См. также

ALTER PROCEDURE, DROP PROCEDURE, CALL, CREATE FUNCTION

CREATE PUBLICATION

CREATE PUBLICATION — определить новую публикацию

Синтаксис

CREATE PUBLICATION имя
    [ FOR TABLE [ ONLY ] имя_таблицы [ * ] [, ...]
      | FOR ALL TABLES ]
    [ WITH ( параметр_публикации [= значение] [, ... ] ) ]

Описание

Команда CREATE PUBLICATION добавляет новую публикацию в текущую базу данных. Имя публикации должно отличаться от имени других существующих публикаций в текущей базе данных.

Публикация — это, по существу, группа таблиц, изменения данных которых подлежат логической репликации.

Параметры

имя

Имя новой публикации.

FOR TABLE

Указывает список таблиц для добавления в публикацию. Если перед именем таблицы указано ONLY, в публикацию добавляется только заданная таблица. Без ONLY добавляется и заданная таблица, и все ее потомки (если таковые имеются). После имени таблицы можно добавить необязательное указание *, чтобы явно обозначить, что включены дочерние таблицы.

Только постоянные базовые таблицы могут быть частью публикации. Временные, нежурналируемые, сторонние и партиционированные таблицы, а также материализованные и обычные представления не могут быть частью публикации. Чтобы реплицировать партиционированную таблицу, добавьте в публикацию ее отдельные партиции.

FOR ALL TABLES

Отмечает публикацию как реплицирующую изменения для всех таблиц в базе данных, включая таблицы, созданные в будущем.

WITH ( параметр_публикации [= значение] [, ... ] )

В этом предложении указываются необязательные параметры для публикации. Поддерживаются следующие параметры:

publish (string)

Этот параметр определяет, какие операции DML будут опубликованы новой публикацией для подписчиков. В качестве его значения через запятую задается список операций. Разрешены следующие операции: insert, update, delete и truncate. По умолчанию публикуются все действия, поэтому значение по умолчанию для этого параметра равно 'insert, update, delete, truncate'.

publish_via_partition_root (boolean)

Этот параметр определяет, будут ли изменения во включенной в публикацию партиционированной таблице (или в ее партициях) опубликованы с именем и схемой этой партиционированной таблицы, а не ее отдельных партиций, где эти изменения на самом деле произошли (второе — поведение по умолчанию). Включение этого параметра позволяет реплицировать изменения в непартиционированную таблицу или в партиционированную таблицу, состоящую из другого набора партиций.

Если этот параметр включен, операции TRUNCATE, выполняемые непосредственно с партициями, не реплицируются.

Примечания

Если не задано ни FOR TABLE, ни FOR ALL TABLES, публикация создается с пустым набором таблиц. Это удобно, если таблицы будут добавлены позже.

Создание публикации не запускает репликацию. Оно лишь определяет логику группировки и фильтрации для будущих подписчиков.

Чтобы создать публикацию, необходимо иметь право CREATE в текущей базе данных. (Разумеется, суперпользователи эту проверку проходят.)

Чтобы добавить таблицу в публикацию, нужно быть владельцем таблицы. Предложение FOR ALL TABLES требует, чтобы вызывающий его пользователь был суперпользователем.

Таблицы, добавленные в публикацию, которая публикует операции UPDATE и/или DELETE, должны иметь свойство REPLICA IDENTITY. В противном случае данные операции для этих таблиц будут запрещены.

Для команды INSERT ... ON CONFLICT публикация выдает операцию, которая фактически является результатом выполнения этой команды. Таким образом, в зависимости от результата, она может быть опубликована как INSERT или UPDATE или не быть опубликована вовсе.

Команды COPY ... FROM публикуются в виде операций INSERT.

Операции DDL не публикуются.

Примеры

Создание публикации, охватывающей изменения в двух таблицах:

CREATE PUBLICATION mypublication FOR TABLE users, departments;

Создание публикации, охватывающей все изменения во всех таблицах:

CREATE PUBLICATION alltables FOR ALL TABLES;

Создание публикации, охватывающей только операции INSERT в одной таблице:

CREATE PUBLICATION insert_only FOR TABLE mydata
    WITH (publish = 'insert');

Совместимость

Команда CREATE PUBLICATION является расширением QHB.

См. также

ALTER PUBLICATION, DROP PUBLICATION

CREATE ROLE

CREATE ROLE — определить новую роль в базе данных

Синтаксис

CREATE ROLE имя [ [ WITH ] параметр [ ... ] ]

Где параметр:

      SUPERUSER | NOSUPERUSER
    | CREATEDB | NOCREATEDB
    | CREATEROLE | NOCREATEROLE
    | INHERIT | NOINHERIT
    | LOGIN | NOLOGIN
    | REPLICATION | NOREPLICATION
    | BYPASSRLS | NOBYPASSRLS
    | CONNECTION LIMIT предел_подключений
    | [ ENCRYPTED ] PASSWORD 'пароль' | PASSWORD NULL
    | VALID UNTIL 'дата_время'
    | IN ROLE имя_роли [, ...]
    | IN GROUP имя_роли [, ...]
    | ROLE имя_роли [, ...]
    | ADMIN имя_роли [, ...]
    | USER имя_роли [, ...]
    | SYSID uid

Описание

Команда CREATE ROLE добавляет новую роль в кластер баз данных QHB. Роль — это логический объект, который может владеть объектами базы данных и иметь права доступа к ней; роль может рассматриваться как «пользователь», «группа» или и то, и другое в зависимости от того, как она используется. Дополнительную информацию об управлении пользователями и проверке подлинности см. в главе Роли в базе данных. Чтобы выполнить эту команду, нужно быть суперпользователем базы данных или иметь право CREATEROLE.

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

Параметры

имя

Имя новой роли.

SUPERUSER
NOSUPERUSER

Эти предложения определяют, является ли новая роль «суперпользователем», который может переопределить все ограничения доступа в базе данных. Статус суперпользователя опасен и должен использоваться только тогда, когда это действительно необходимо. Чтобы создать нового суперпользователя нужно самому быть суперпользователем. Если ничего не указано, значение по умолчанию равно NOSUPERUSER.

CREATEDB
NOCREATEDB

Эти предложения определяют способность роли создавать базы данных. Если указывается CREATEDB, то определяемой роли будет разрешено создавать новые базы данных. NOCREATEDB лишит роль возможности создавать базы данных. Если ничего не указано, значение по умолчанию равно NOCREATEDB.

CREATEROLE
NOCREATEROLE

Эти предложения определяют, будет ли разрешено роли создавать новые роли (то есть выполнять CREATE ROLE). Роль с правом CREATEROLE может также изменять и удалять другие роли. Если ничего не указано, значение по умолчанию равно NOCREATEROLE.

INHERIT
NOINHERIT

Эти предложения определяют, «наследует» ли роль права ролей, членом которых является. Роль с атрибутом INHERIT может автоматически использовать любые права в базе данных, предоставленные всем ролям, в которые она непосредственно или опосредованно входит. Без INHERIT членство в другой роли позволяет только выполнить SET ROLE и переключиться на эту роль; права, назначенные другой роли, будут доступны только после этого. Значение по умолчанию равно INHERIT.

LOGIN
NOLOGIN

Эти предложения определяют, разрешен ли роли вход на сервер; то есть может ли роль быть задана в качестве начального авторизованного имени при подключении клиента. Роль с атрибутом LOGIN может рассматриваться как пользователь. Роли без этого атрибута полезны для управления правами доступа к базе данных, но не являются пользователями в обычном смысле этого слова. Если ничего не указано, значение по умолчанию равно NOLOGIN, за исключением случаев, когда CREATE ROLE вызывается через альтернативное написание CREATE USER.

REPLICATION
NOREPLICATION

Эти предложения определяют, является ли роль ролью репликации. Роль должна иметь этот атрибут (или быть суперпользователем), чтобы иметь возможность подключаться к серверу в режиме репликации (физической или логической), а также создавать или удалять слоты репликации. Роль с атрибутом REPLICATION обладает очень большими правами и должна использоваться только в ролях, фактически используемых для репликации. Если ничего не указано, значение по умолчанию равно NOREPLICATION. Чтобы создать новую роль с атрибутом REPLICATION, нужно быть суперпользователем.

BYPASSRLS
NOBYPASSRLS

Эти предложения определяют, будут ли для роли игнорироваться все политики защиты на уровне строк (row-level security, RLS). Значение по умолчанию равно NOBYPASSRLS. Чтобы создать новую роль с атрибутом BYPASSRLS, нужно быть суперпользователем. Обратите внимание, что qhb_dump по умолчанию установит для row_security значение OFF для гарантии, что всё содержимое таблицы выгружается. Если пользователь, выполняющий qhb_dump, не имеет соответствующих прав, будет возвращена ошибка. Суперпользователь и владелец выгружаемой таблицы всегда обходят RLS.

CONNECTION LIMIT предел_подключений

Если роли разрешен вход, параметр указывает, сколько одновременных подключений она может выполнить. Значение -1 (по умолчанию) означает отсутствие ограничений. Обратите внимание, что под это ограничение подпадают только обычные соединения. Ни подготовленные транзакции, ни фоновые рабочие соединения не учитываются.

[ ENCRYPTED ] PASSWORD 'пароль'
PASSWORD NULL

Задает пароль роли. (Пароль используется только для ролей с атрибутом LOGIN, но тем не менее вы можете определить его для ролей без него.) Если вы не планируете использовать аутентификацию паролем, можете опустить эту опцию. Если пароль не указан, он будет установлен в значение NULL, и проверка подлинности пароля для этого пользователя всегда завершится ошибкой. При желании пароль NULL можно установить явно, указав PASSWORD NULL.

Примечание
При указании пустой строки пароль также будет иметь значение NULL. Чтобы избежать двусмысленности, следует избегать указания пустой строки.

Пароль всегда хранится зашифрованным в системных каталогах. Ключевое слово ENCRYPTED не имеет эффекта, но принимается для обратной совместимости. Способ шифрования определяется параметром конфигурации password_encryption. Если представленная строка пароля уже зашифрована в формате MD5 или SCRAM, она сохраняется как есть независимо от password_encryption (поскольку система не может расшифровать указанную зашифрованную строку пароля, чтобы зашифровать ее в другом формате). Это позволяет перезагрузить зашифрованные пароли во время выгрузки/восстановления.

VALID UNTIL ’дата_время’

Предложение VALID UNTIL устанавливает дату и время, после которых пароль роли становится недействительным. Если это предложение пропущено пароль будет действителен в течение всего времени.

IN ROLE имя_роли

В предложении IN ROLE перечислены одна или несколько существующих ролей, к которым новая роль будет немедленно добавлена в качестве нового члена. (Обратите внимание, что нет возможности добавить новую роль в качестве администратора; для этого используйте отдельную команду GRANT).

IN GROUP имя_роли

IN GROUP — это устаревшее написание предложения IN ROLE.

ROLE имя_роли

В предложении ROLE перечислены одна или несколько существующих ролей, которые автоматически добавляются в качестве членов новой роли. (Это фактически делает новую роль «группой».)

ADMIN имя_роли

Предложение ADMIN подобно ROLE, но перечисленные роли добавляются в новую роль с атрибутом WITH ADMIN OPTION, давая им право предоставлять членство в этой роли другим ролям.

USER имя_роли

Предложение USER является устаревшим написанием предложения ROLE.

SYSID uid

Предложение SYSID игнорируется, но принимается для обратной совместимости.

Примечания

Используйте команду ALTER ROLE, чтобы изменить атрибуты роли, и DROP ROLE, чтобы ее удалить. Все атрибуты, указанные в CREATE ROLE, могут быть позднее изменены командами ALTER ROLE.

Предпочтительным способом добавления и удаления членов ролей, используемых в качестве групп, является использование функций GRANT и REVOKE.

Предложение VALID UNTIL определяет срок действия только для пароля, а не для роли как таковой. В частности, при входе в систему с помощью метода проверки подлинности без пароля время истечения срока действия не проверяется.

Атрибут INHERIT управляет наследованием предоставленных прав (т. е. прав доступа для объектов базы данных и членства в ролях). Он не применяется к специальным атрибутам роли, заданным с помощью CREATE ROLE и ALTER ROLE. Например, членство в роли с правом CREATEDB недостаточно для получения права создавать базы данных, даже если установлен атрибут INHERIT; перед тем, как создать базу данных, необходимо будет переключиться на эту роль, выполнив SET ROLE.

Хотя по умолчанию установлено INHERIT, но значение NOINHERIT больше подходит по смыслу к тому, что описано в стандарте SQL.

Будьте осторожны с правом CREATEROLE, т. к. на роли с этим правом не распространяется концепция наследования прав. Это означает, что даже если у роли нет определенных прав, ей всё равно разрешено создавать другие роли, и она может легко создать другую роль с правами, отличными от тех, что есть у нее самой (за исключением создания ролей с правами суперпользователя). Например, если роль «user» имеет право CREATEROLE, но не CREATEDB, она, тем не менее, может создать новую роль с правом CREATEDB. Поэтому роль с правом CREATEROLE следует воспринимать почти как роль суперпользователя.

QHB включает в себя программу createuser, которая имеет ту же функциональность, что и команда CREATE ROLE (на самом деле она вызывает эту команду), но может запускаться в командной оболочке.

Ограничение CONNECTION LIMIT действует приблизительно; если одновременно запускаются два сеанса, в то время как для этой роли остается только один «слот», может так случиться, что будут отклонены оба подключения. Кроме того, это ограничение не распространяется на суперпользователей.

При указании незашифрованного пароля с помощью этой команды следует соблюдать осторожность. Пароль будет передан на сервер в виде открытого текста, и он также может быть зарегистрирован в журнале команд клиента или журнале сервера. Команда createuser, однако, передает зашифрованный пароль. Кроме того, psql содержит команду \password, которую можно использовать для последующего безопасного изменения пароля.

Примеры

Создание роли, для которой разрешен вход, но не задан пароль:

CREATE ROLE jonathan LOGIN;

Создание роли с паролем:

CREATE USER davide WITH PASSWORD 'jw8s0F4';

(CREATE USER действует так же, как CREATE ROLE, но подразумевает еще и атрибут LOGIN.)

Создание роли с паролем, действующим до конца 2004 г., то есть пароль перестает действовать в первую же секунду 2005 г.

CREATE ROLE miriam WITH LOGIN PASSWORD 'jw8s0F4' VALID UNTIL '2005-01-01';

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

CREATE ROLE admin WITH CREATEDB CREATEROLE;

Совместимость

Команда CREATE ROLE описана в стандарте SQL, но стандарт требует поддержки только следующего синтаксиса:

CREATE ROLE имя [ WITH ADMIN имя_роли ]

Возможность создавать множество начальных администраторов и все другие параметры CREATE ROLE относятся к расширениям QHB.

Стандарт SQL определяет понятия пользователей и ролей, но рассматривает их как отдельные понятия и оставляет на усмотрение каждой реализации СУБД самостоятельно указывать все команды, определяющие пользователей. В QHB мы решили объединить пользователей и роли в единый вид логического объекта. Таким образом, роли имеют гораздо больше дополнительных атрибутов, чем в стандарте.

Поведение, заданное стандартом SQL, с наибольшей достоверностью можно получить, если создавать пользователей с атрибутом NOINHERIT, а роли — с атрибутом INHERIT.

См. также

SET ROLE, ALTER ROLE, DROP ROLE, GRANT, REVOKE, createuser

CREATE RULE

CREATE RULE — определение нового правила перезаписи

Синтаксис

CREATE [ OR REPLACE ] RULE имя AS ON событие
    TO имя_таблицы [ WHERE условие ]
    DO [ ALSO | INSTEAD ] { NOTHING | команда | ( команда ; команда ... ) }

Где событие может быть:

    SELECT | INSERT | UPDATE | DELETE

Описание

Команда CREATE RULE определяет новое правило, применяемое к указанной таблице или представлению. CREATE OR REPLACE RULE будет либо создавать новое правило, либо заменять существующее правило с тем же именем для той же таблицы.

Система правил QHB позволяет определить альтернативное действие, которое будет выполняться при добавлении, изменении или удалении в таблицах базы данных. Грубо говоря, когда выполняется данная команда на данной таблице, правило вызывает выполнение дополнительных команд. В качестве альтернативы правило INSTEAD может заменить данную команду другой или привести к тому, что команда не будет выполняться вообще. Правила также используются для реализации представлений SQL. Важно понимать, что правило — это действительно механизм преобразования команд, или командный макрос. Преобразование происходит до начала выполнения команды. Если вам действительно нужна операция, которая срабатывает независимо для каждой физической строки, вы, вероятно, захотите использовать триггер, а не правило.

На данный момент правила ON SELECT должны быть безусловными, с указанием INSTEAD, и их действия должны состоять из единственной команды SELECT. Таким образом, правило ON SELECT эффективно превращает таблицу в представление, видимым содержимым которого являются строки, возвращаемые заданной в правиле командой SELECT, а не данные, хранящиеся в таблице (если они есть). Считается всё же, что для этой цели лучше пользоваться командой CREATE VIEW, а не создавать реальную таблицу и затем определять для нее правило ON SELECT.

Вы можете создать иллюзию изменяемого представления путем определения правил ON INSERT, ON UPDATE и ON DELETE (или любого подмножество правил, достаточного для ваших целей), чтобы заменить операции изменения в представлении соответствующими изменениями в других таблицах. Если хотите поддержать операторы вроде INSERT RETURNING и и пр., то не забудьте поместить в каждое из этих правил подходящее предложение RETURNING.

При попытке использовать условные правила для изменения сложных представлений возникает одна загвоздка: для каждого действия, которое вы хотите разрешить для представления, необходимо определить безусловное правило INSTEAD. Если правило условное или не типа INSTEAD, система отвергнет попытки выполнить изменения, предполагая, что в некоторых случаях они могут свестись к попыткам провести операцию с фиктивной таблицей представления. Если вы хотите обрабатывать все полезные случаи изменений в условных правилах, добавьте безусловное правило DO INSTEAD NOTHING, гарантирующее, что система поймет, что ей никогда не придется изменять фиктивную таблицу. Затем создайте условные правила без свойства INSTEAD; в тех случаях, когда они будут применяться, их действия будут добавлены к действию по умолчанию INSTEAD NOTHING. (Однако в настоящее время этот способ не подходит для реализации запросов RETURNING.)

Примечание
Представление, которое достаточно просто, чтобы быть автоматически изменяемым (см. CREATE VIEW), не требует для изменения созданного пользователем правила. Хотя всё равно можно создать явное правило, но автоматическое преобразование изменения в общем и целом его превзойдет.
Еще одна альтернатива, которую стоит рассмотреть, — это использование вместо правил триггеров INSTEAD OF (см. раздел CREATE TRIGGER).

Параметры

имя

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

событие

Тип события: SELECT, INSERT, UPDATE или DELETE. Обратите внимание, что INSERT с предложением ON CONFLICT не может быть использовано в таблицах, для которых определено правило INSERT или UPDATE. Вместо этого рассмотрите возможность использования изменяемого представления.

имя_таблицы

Имя таблицы или представления, к которым будет применяться правило (может быть дополнено схемой).

условие

Любое выражение условия SQL (возвращающее boolean). Выражение условия не может ссылаться ни на какие таблицы, кроме NEW и OLD, и не может содержать агрегатных функций.

INSTEAD

INSTEAD указывает, что команды должны выполняться вместо исходной команды.

ALSO

ALSO указывает, что команды должны выполняться в дополнение к исходной команде.

Если ни INSTEAD, ни ALSO не заданы, по умолчанию применяется ALSO.

команда

Команда или команды, составляющие действие правила. Допустимыми командами являются SELECT, INSERT , UPDATE , DELETE и NOTIFY.

В параметрах условие и команда для обращения к значениям в соответствующей таблице могут использоваться имена специальных таблиц NEW и OLD. NEW действительна в правилах ON INSERT и ON UPDATE для ссылки на новую добавляемую или изменяемую строку. OLD действительна в правилах ON UPDATE и ON DELETE для ссылки на существующую строку, которая изменяется или удаляется.

Примечания

Чтобы создавать или изменять правила для таблицы, нужно быть ее владельцем.

В правила для INSERT, UPDATE или DELETE для представления можно добавить предложение RETURNING, которое выдает столбцы представления. Это предложение будет использоваться для вычисления выходных данных, если правило инициируется командой INSERT RETURNING, UPDATE RETURNING или DELETE RETURNING. Когда правило запускается командой без RETURNING, его собственное предложение RETURNING будет проигнорировано. Текущая реализация позволяет содержать RETURNING только безусловным правилам INSTEAD; более того, среди всех правил для одного события может быть не более одного предложения RETURNING. (Это гарантирует, что существует только одно подходящее предложение RETURNING, которое и будет использоваться для вычисления результатов.) Запросы с RETURNING к представлению будут отклонены, если в его правилах нет ни одного предложения RETURNING.

Очень важно позаботиться о том, чтобы правила не зацикливались. Например, хотя каждое из следующих двух определений правил принимается QHB, команда SELECT приведет к тому, что QHB сообщит об ошибке из-за рекурсивного расширения правила:

CREATE RULE "_RETURN" AS
    ON SELECT TO t1
    DO INSTEAD
        SELECT * FROM t2;

CREATE RULE "_RETURN" AS
    ON SELECT TO t2
    DO INSTEAD
        SELECT * FROM t1;

SELECT * FROM t1;

В настоящее время если действие правила содержит команду NOTIFY, та будет выполняться безоговорочно, то есть уведомление будет выдано, даже если нет строк, к которым должно применяться правило. Например, в:

CREATE RULE notify_me AS ON UPDATE TO mytable DO ALSO NOTIFY mytable;

UPDATE mytable SET name = 'foo' WHERE id = 42;

событие NOTIFY будет отправлено во время выполнения UPDATE независимо от того, есть ли строки, соответствующие условию id = 42. Это ограничение реализации, которое может быть исправлено в будущих выпусках.

Совместимость

Команда CREATE RULE является языковым расширением QHB, как и вся система перезаписи запросов.

См. также

ALTER RULE, DROP RULE

CREATE SCHEMA

CREATE SCHEMA — определить новую схему

Синтаксис

CREATE SCHEMA имя_схемы [ AUTHORIZATION указание_роли ] [ элемент_схемы [ ... ] ]
CREATE SCHEMA AUTHORIZATION указание_роли [ элемент_схемы [ ... ] ]
CREATE SCHEMA IF NOT EXISTS имя_схемы [ AUTHORIZATION указание_роли ]
CREATE SCHEMA IF NOT EXISTS AUTHORIZATION указание_роли

Где указание_роли может быть:

    имя_пользователя
  | CURRENT_USER
  | SESSION_USER

Описание

Команда CREATE SCHEMA вносит новую схему в текущую базу данных. Имя схемы должно отличаться от имени любой существующей схемы в текущей базе данных.

Схема по существу является пространством имен: она содержит именованные объекты (таблицы, типы данных, функции и операторы), имена которых могут дублировать имена других объектов, существующих в других схемах. Доступ к именованным объектам осуществляется либо путем «дополнения» их имен именем схемы в качестве префикса, либо путем задания пути поиска, включающего требуемую схему (или схемы). Команда CREATE, в которой указывается неполное имя объекта, создает объект в текущей схеме (той, что стоит в пути поиска первой; узнать ее позволяет функция current_schema).

При необходимости CREATE SCHEMA может включать подкоманды для создания объектов в новой схеме. В целом, подкоманды обрабатываются так же, как и отдельные команды, выполняемые после создания схемы, за исключением того, что при использовании предложения AUTHORIZATION все созданные объекты будут принадлежать указанному пользователю.

Параметры

имя_схемы

Имя создаваемой схемы. Если этот параметр опущен, то в качестве имени схемы используется имя_пользователя. Имя не может начинаться с pg_, поскольку такие имена зарезервированы для системных схем.

имя_пользователя

Имя пользователя (роли), которому будет принадлежать новая схема. Если этот параметр опущен, по умолчанию владельцем становится пользователь, выполняющий команду. Чтобы создать схему, принадлежащую другой роли, необходимо быть непосредственным или опосредованным членом этой роли или суперпользователем.

элемент_схемы

Оператор SQL, определяющий объект, который должен быть создан в схеме. В настоящее время CREATE SCHEMA может содержать только подкоманды CREATE TABLE, CREATE VIEW, CREATE INDEX, CREATE SEQUENCE, CREATE TRIGGER и GRANT. Другие виды объектов должны быть созданы в отдельных командах после создания схемы.

IF NOT EXISTS

Не делать ничего (кроме выдачи уведомления), если схема с тем же именем уже существует. Когда используется это указание, такая команда не может содержать подкоманды элемент_схемы.

Примечания

Чтобы создать схему, необходимо иметь право CREATE в текущей базе данных. (Разумеется, на суперпользователей это условие не распространяется.)

Примеры

Создание схемы:

CREATE SCHEMA myschema;

Создание схемы для пользователя joe; схема получит имя joe:

CREATE SCHEMA AUTHORIZATION joe;

Создание схемы с именем test, владельцем которой будет пользователь joe, если только схема test еще не существует. (Является ли пользователь joe владельцем существующей схемы, значения не имеет.)

CREATE SCHEMA IF NOT EXISTS test AUTHORIZATION joe;

Создание схемы, в которой сразу создаются таблица и представление:

CREATE SCHEMA hollywood
    CREATE TABLE films (title text, release date, awards text[])
    CREATE VIEW winners AS
        SELECT title, release FROM films WHERE awards IS NOT NULL;

Обратите внимание, что отдельные подкоманды не завершаются точкой с запятой.

Следующие команды приводят к тому же результату другим способом:

CREATE SCHEMA hollywood;
CREATE TABLE hollywood.films (title text, release date, awards text[]);
CREATE VIEW hollywood.winners AS
    SELECT title, release FROM hollywood.films WHERE awards IS NOT NULL;

Совместимость

Стандарт SQL допускает в команде CREATE SCHEMA предложение DEFAULT CHARACTER SET и дополнительные типы подкоманд, которые QHB в настоящее время не принимает.

Стандарт SQL указывает, что подкоманды CREATE SCHEMA могут быть указаны в любом порядке. Настоящая реализация QHB не обрабатывает все случаи порядка указания подкоманд; иногда может потребоваться изменить порядок подкоманд, чтобы избежать ссылок вперед.

Согласно стандарту SQL, владелец схемы всегда владеет всеми объектами внутри нее. QHB позволяет схемам содержать объекты, принадлежащие пользователям, отличным от владельца схемы. Это может произойти, только если владелец схемы предоставляет право CREATE на своей схеме кому-то другому либо объекты в ней будет создавать суперпользователь.

Указание IF NOT EXISTS является расширением QHB.

См. также

ALTER SCHEMA, DROP SCHEMA

CREATE SEQUENCE

CREATE SEQUENCE — определить новый генератор последовательности

Синтаксис

CREATE [ TEMPORARY | TEMP ] SEQUENCE [ IF NOT EXISTS ] имя
    [ AS тип_данных ]
    [ INCREMENT [ BY ] шаг ]
    [ MINVALUE мин_значение | NO MINVALUE ] [ MAXVALUE макс_значение | NO MAXVALUE ]
    [ START [ WITH ] начало ] [ CACHE кэш ] [ [ NO ] CYCLE ]
    [ OWNED BY { имя_таблицы.имя_столбца | NONE } ]

Описание

Команда CREATE SEQUENCE создает новый генератор порядковых номеров. Это включает в себя создание и инициализацию новой специальной однострочной таблицы с именем имя. Генератор будет принадлежать пользователю, выдавшему команду.

Если задано имя схемы, то последовательность создается в указанной схеме, в противном случае — в текущей. Временные последовательности существуют в специальной схеме, поэтому при создании временной последовательности имя схемы не может быть задано. Имя последовательности должно отличаться от имени любой другой последовательности, таблицы, индекса, представления или сторонней таблицы в той же схеме.

После того как последовательность создана, с ней можно работать, используя функции nextval, currval и setval. Эти функции описаны в разделе Функции управления последовательностями.

Хотя обновить последовательность напрямую нельзя, можно использовать запрос наподобие этого:

SELECT * FROM name;

чтобы изучить ее параметры и текущее состояние. В частности, поле last_value последовательности показывает последнее значение, выделенное для какого-либо сеанса. (Разумеется, если другие сеансы активно вызывают nextval, то к моменту печати это значение может устареть.)

Параметры

TEMPORARY или TEMP

Если этот параметр указан, объект последовательности создается только для этого сеанса и автоматически удаляется при выходе из сеанса. Существующие постоянные последовательности с одинаковым именем не видны (в этом сеансе), пока существует временная последовательность, если только они не связаны с именами, определенными схемой.

IF NOT EXISTS

Не считать ошибкой, если связь с тем же именем уже существует. В этом случае будет выдано соответствующее уведомление. Обратите внимание, что нет никакой гарантии, что существующее отношение как-то соотносится с последовательностью, которая была бы создана — это может даже не быть последовательностью.

имя

Имя создаваемой последовательности (может быть дополнено схемой).

тип_данных

Необязательное предложение AS тип_данных задает тип данных последовательности. Допустимые типы: smallint, integer и bigint. Значение по умолчанию равно bigint. Тип данных определяет минимальные и максимальные значения последовательности по умолчанию.

шаг

Необязательное предложение INCREMENT BY шаг указывает, какое значение добавляется к текущему значению последовательности для создания нового значения. Положительное значение сделает восходящую последовательность, отрицательное — нисходящую. Значение по умолчанию равно 1.

мин_значение NO MINVALUE

Необязательное предложение MINVALUE мин_значение определяет минимальное значение, которое может генерировать последовательность. Если это условие не указано или не указано значение NO MINVALUE, будут использоваться значения по умолчанию. Значение по умолчанию для восходящей последовательности равно 1. Значение по умолчанию для нисходящей последовательности равно минимальному значению типа данных.

макс_значение NO MAXVALUE

Необязательное предложение MAXVALUE макс_значение определяет максимальное значение для последовательности. Если это условие не указано или указано значение NO MAXVALUE, будут использоваться значения по умолчанию. Значение по умолчанию для восходящей последовательности равно максимальному значению типа данных. Значение по умолчанию для нисходящей последовательности равно -1.

начало

Необязательное предложение START WITH начало позволяет последовательности начинаться в любом месте. Начальное значение по умолчанию равно мин_значение для восходящих последовательностей и макс_значение для нисходящих.

кэш

Необязательное предложение CACHE кэш указывает, сколько порядковых номеров должно быть предварительно выделено и сохранено в памяти для быстрого доступа. Минимальное значение равно 1 (за один раз может быть сгенерировано только одно значение, т. е. кэша нет), и оно же значение по умолчанию.

CYCLE NO CYCLE

Параметр CYCLE позволяет последовательности зацикливаться, когда восходящей или нисходящей последовательностью было достигнуто макс_значение или мин_значение соответственно. Если предел достигнут, то следующим сгенерированным номером будет мин_значение или макс_значение соответственно.

Если указано NO CYCLE, любые вызовы nextval после достижения последовательностью максимального значения будут возвращать ошибку. Если не указано ни CYCLE, ни NO CYCLE, значение по умолчанию равно NO CYCLE.

OWNED BY имя_таблицы.имя_столбца
OWNED BY NONE

Параметр OWNED BY заставляет последовательность связываться с определенным столбцом таблицы таким образом, что если этот столбец (или вся его таблица) удаляется, последовательность будет также автоматически удалена. Указанная таблица должна иметь того же владельца и находиться в той же схеме, что и последовательность. Значение по умолчанию OWNED BY NONE указывает, что такой связи не существует.

Примечания

Чтобы удалить последовательность, используйте DROP SEQUENCE.

Последовательности основаны на арифметике bigint, поэтому диапазон не может превышать диапазон восьмибайтного целого числа (от -9223372036854775808 до 9223372036854775807).

Поскольку вызовы nextval и setval никогда не откатываются, объекты последовательности нельзя использовать, если требуется непрерывное присвоение порядковых номеров. Такое присвоение можно устроить с помощью исключительной блокировки таблицы, содержащей счетчик, но это решение гораздо дороже, чем объекты последовательности, особенно если сразу много транзакций одновременно затребует порядковые номера.

Если для объекта последовательности, который будет использоваться одновременно несколькими сеансами, значение кэш установлено больше единицы, это может привести к неожиданным результатам. Каждый сеанс будет выделять и кэшировать следующие друг за другом значения последовательности во время одного обращения к объекту последовательности и, соответственно, увеличивать последнее_значение объекта последовательности. Затем следующие кэш-1 вызовы nextval в этом сеансе будут просто возвращать предварительно выделенные значения, не касаясь объекта последовательности. В результате любые номера, выделенные, но не используемые в сеансе, по его окончании будут потеряны, что приведет к «прорехам» в последовательности.

Более того, хотя несколько сеансов гарантированно выделяют отдельные значения последовательности, последние могут быть сгенерированы из последовательности, когда рассматриваются все сеансы. Например, когда значение кэш равно 10, сеанс A может резервировать значения от 1 до 10 и возвращать nextval=1, затем сеанс B может зарезервировать значения от 11 до 20 и возвращать nextval=11 до того, как сеанс А сгенерирует nextval=2. Таким образом, при значении кэш, равном единице, можно с уверенностью предположить, что значения nextval генерируются последовательно, но при значении кэш выше единицы следует предполагать лишь, что все значения nextval различны, а не исключительную последовательность их генерации. Кроме того, последнее_значение будет отражать самое последнее значение, зарезервированное любым сеансом, независимо от того, было ли оно уже возвращено nextval.

Также следует учесть, что действие функции setval, выполненное в такой последовательности, не будет замечено другими сеансами, пока те не используют все предварительно выделенные значения, которые они кэшировали.

Примеры

Создание восходящей последовательности с именем serial с начальным значением 101:

CREATE SEQUENCE serial START 101;

Выбор следующего номера из этой последовательности:

SELECT nextval('serial');

 nextval
---------
     101

Выбор следующего номера из этой последовательности:

SELECT nextval('serial');

 nextval
---------
     102

Использование этой последовательности в команде INSERT:

INSERT INTO distributors VALUES (nextval('serial'), 'nothing');

Изменение значения последовательности после COPY FROM:

BEGIN;
COPY distributors FROM 'input_file';
SELECT setval('serial', max(id)) FROM distributors;
END;

Совместимость

Команда CREATE SEQUENCE соответствует стандарту SQL, за следующими исключениями:

Получение следующего значения производится с помощью функции nextval() вместо стандартного выражения NEXT VALUE FOR.

Предложение OWNED BY является расширением QHB.

См. также

ALTER SEQUENCE, DROP SEQUENCE

CREATE SERVER

CREATE SERVER — определить новый сторонний сервер

Синтаксис

CREATE SERVER [ IF NOT EXISTS ] имя_сервера [ TYPE 'тип_сервера' ] [ VERSION 'версия_сервера' ]
    FOREIGN DATA WRAPPER имя_обертки_сторонних_данных
    [ OPTIONS ( параметр 'значение' [, ... ] ) ]

Описание

Команда CREATE SERVER определяет новый сторонний сервер. Пользователь, создавший сервер, становится его владельцем.

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

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

Для создания сервера требуется право USAGE на используемую обертку сторонних данных.

Параметры

IF NOT EXISTS

Не выдавать ошибку, если сервер с тем же именем уже существует. В этом случае будет выдано соответствующее уведомление. Обратите внимание, что нет никакой гарантии, что существующий сервер как-то соотносится с тем, который был бы создан.

имя_сервера

Имя создаваемого стороннего сервера.

тип_сервера

Необязательный тип сервера, потенциально полезный для оберток сторонних данных.

версия_сервера

Необязательная версия сервера, потенциально полезная для оберток сторонних данных.

имя_обертки_сторонних_данных

Имя обертки сторонних данных, управляющей сервером.

OPTIONS ( параметр 'значение' [, ... ] )

Это предложение задает параметры для сервера. Параметры обычно определяют характеристики соединения сервера, но фактические имена и значения зависят от обертки сторонних данных сервера.

Примечания

При использовании модуля dblink имя стороннего сервера можно использовать в качестве аргумента функции dblink_connect для указания параметров соединения. Необходимо иметь право USAGE для стороннего сервера, чтобы иметь возможность использовать его таким образом.

Примеры

Создание сервера myserver, который использует обертку сторонних данных qhb_fdw:

CREATE SERVER myserver FOREIGN DATA WRAPPER qhb_fdw OPTIONS (host 'foo', dbname 'foodb', port '5432');

Совместимость

Команда CREATE SERVER соответствует стандарту ISO/IEC 9075-9 (SQL/MED).

См. также

ALTER SERVER, DROP SERVER, CREATE FOREIGN DATA WRAPPER, CREATE FOREIGN TABLE, CREATE USER MAPPING

CREATE STATISTICS

CREATE STATISTICS — определить расширенную статистику

Синтаксис

CREATE STATISTICS [ IF NOT EXISTS ] имя_статистики
    [ ( вид_статистики [, ... ] ) ]
    ON имя_столбца, имя_столбца [, ...]
    FROM имя_таблицы

Описание

Команда CREATE STATISTICS создаст новый расширенный объект статистики, отслеживающий данные о заданной таблице, сторонней таблице или материализованном представлении. Объект статистики будет создан в текущей базе данных и будет принадлежать пользователю, выдавшему команду.

Если задано имя схемы (например, CREATE STATISTICS myschema.mystat ...), то объект статистики создается в указанной схеме, в противном случае — в текущей. Имя объекта статистики должно отличаться от имени любого другого объекта статистики в той же схеме.

Параметры

IF NOT EXISTS

Не выдавать ошибку, если объект статистики с тем же именем уже существует. В этом случае будет выдано соответствующее уведомление. Обратите внимание, что здесь рассматривается только имя объекта статистики, а не детали его определения.

имя_статистики

Имя создаваемого объекта статистики (может быть дополнено схемой).

вид_статистики

Вид статистики, который будет вычисляться в этом объекте статистики. В настоящее время поддерживаются следующие виды: ndistinct, который активирует n-различную статистику, dependencies, который активирует статистику функциональных зависимостей, и mcv, который активирует списки наиболее распространенных значений. Если это предложение опущено, в объект статистики включаются все поддерживаемые виды статистики. Дополнительную информацию см. в разделах Расширенная статистика и Примеры многомерной статистики.

имя_столбца

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

имя_таблицы

Имя таблицы, содержащей столбец (или столбцы), по которому вычисляются статистические данные (может быть дополнено схемой).

Примечания

Чтобы создать считывающий таблицу объект статистики, нужно быть владельцем этой таблицы. Однако после создания объекта статистики его владелец определяется независимо от нижележащих таблиц.

Примеры

Создание таблицы t1 с двумя функционально зависимыми столбцами, т. е. знания значения в первом столбце достаточно для определения значения в другом столбце. Затем на этих столбцах строится статистика функциональной зависимости:

CREATE TABLE t1 (
    a   int,
    b   int
);

INSERT INTO t1 SELECT i/100, i/500
                 FROM generate_series(1,1000000) s(i);

ANALYZE t1;

-- число совпадающих строк будет существенно недооценено:
EXPLAIN ANALYZE SELECT * FROM t1 WHERE (a = 1) AND (b = 0);

CREATE STATISTICS s1 (dependencies) ON a, b FROM t1;

ANALYZE t1;

-- теперь оценка числа строк стала точнее:
EXPLAIN ANALYZE SELECT * FROM t1 WHERE (a = 1) AND (b = 0);

Без статистики функциональной зависимости планировщик предположил бы, что эти два условия WHERE независимы, и перемножил бы их избирательности вместе, получив при этом слишком маленькую оценку количества строк. С такой статистикой планировщик признает, что условия WHERE являются избыточными, и не занижает количество строк.

Создание таблицы t2 с двумя идеально коррелирующими столбцами (содержащими идентичные данные) и списком MCV на этих столбцах:

CREATE TABLE t2 (
    a   int,
    b   int
);

INSERT INTO t2 SELECT mod(i,100), mod(i,100)
                 FROM generate_series(1,1000000) s(i);

CREATE STATISTICS s2 (mcv) ON a, b FROM t2;

ANALYZE t2;

-- подходящая комбинация (входит в MCV)
EXPLAIN ANALYZE SELECT * FROM t2 WHERE (a = 1) AND (b = 1);

-- неподходящая комбинация (не входит в MCV)
EXPLAIN ANALYZE SELECT * FROM t2 WHERE (a = 1) AND (b = 2);

Список MCV предоставляет планировщику более подробную информацию о конкретных значениях, которые обычно отображаются в таблице, а также верхнюю границу избирательности комбинаций значений, которые не отображаются в таблице, что позволяет ему генерировать лучшие оценки в обоих случаях.

Совместимость

В стандарте SQL нет команды CREATE STATISTICS.

См. также

ALTER STATISTICS, DROP STATISTICS

CREATE SUBSCRIPTION

CREATE SUBSCRIPTION — определить новую подписку

Синтаксис

CREATE SUBSCRIPTION имя_подписки
    CONNECTION 'строка_подключения'
    PUBLICATION имя_публикации [, ...]
    [ WITH ( параметр_подписки [= значение] [, ... ] ) ]

Описание

Команда CREATE SUBSCRIPTION добавляет новую подписку для текущей базы данных. Имя подписки должно отличаться от имени любой существующей подписки в базе данных.

Подписка представляет собой подключение репликации к публикующему серверу. Таким образом, эта команда не только добавляет определения в локальные каталоги, но и создает слот репликации на публикующем сервере.

При фиксации транзакции, в которой выполняется эта команда, будет запущен модуль логической репликации, который будет реплицировать данные для новой подписки.

Параметры

имя_подписки

Имя новой подписки.

CONNECTION 'строка_подключения'

Строка подключения к публикующему серверу..

PUBLICATION имя_публикации

Имена публикаций на публикующем сервере, на которые производится подписка.

WITH ( параметр_подписки [= значение] [, ... ] )

В этом предложении указываются необязательные параметры для подписки. Поддерживаются следующие параметры:

copy_data (boolean)

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

create_slot (boolean)

Указывает, должна ли команда создать слот репликации на публикующем сервере. Значение по умолчанию равно true.

enabled (boolean)

Указывает, следует ли активировать репликацию подписки или ее нужно только настроить, но пока не запускать. Значение по умолчанию равно true.

slot_name (string)

Имя используемого слота репликации. Поведение по умолчанию заключается в использовании имени подписки для имени слота.

Когда значение slot_name задается как NONE, с подпиской не будет связан никакой слот репликации. Это можно использовать, если слот репликации будет создан позже вручную. В таких подписках также должны быть установлены значения false для параметров enabled и create_slot.

synchronous_commit (enum)

Значение этого параметра переопределяет параметр synchronous_commit. Значение по умолчанию равно off.

Использование значения off безопасно для логической репликации: если подписчик потеряет транзакции из-за отсутствия синхронизации, данные будут повторно отправлены с публикующего сервера.

При выполнении синхронной логической репликации может потребоваться другой параметр. Модули логической репликации передают на публикующий сервер позиции записанных и сохраненных на диске данных, и при использовании синхронной репликации тот будет ожидать фактического сохранения. Следовательно, значение off параметра synchronous_commit для подписчика при использовании подписки для синхронной репликации может увеличить задержку выполнения COMMIT на публикующем сервере. При таком сценарии может быть выгодно установить для synchronous_commit значение local или выше.

connect (boolean)

Указывает, должна ли CREATE SUBSCRIPTION вообще подключаться к публикующему серверу. При установке для этого параметра значения false значения по умолчанию параметров enabled, create_slot и copy_data также сменятся на false.

Не допускается совмещение значения false для параметра connect и значения true для параметров enabled,create_slot или copy_data.

Так как при значении false для этого параметра соединение не устанавливается, подписка на таблицы не производится, и поэтому после включения подписки ничего реплицироваться не будет. Чтобы запустить подписку на таблицы, нужно выполнить команду ALTER SUBSCRIPTION ... REFRESH PUBLICATION.

Примечания

.

При создании слота репликации (поведение по умолчанию) команду CREATE SUBSCRIPTION нельзя выполнить внутри блока транзакций.

Создание подписки, которая подключается к одному и тому же кластеру баз данных (например, для репликации между базами данных в одном и том же кластере или для репликации в одной и той же базе данных), будет успешным только в том случае, если слот репликации не создается в рамках той же команды. В противном случае вызов CREATE SUBSCRIPTION будет зависать. Чтобы выполнить эту работу, создайте слот репликации отдельно (с помощью функции pg_create_logical_replication_slot с именем плагина pgoutput) и создайте подписку с помощью параметра create_slot = false. Это ограничение реализации, которое может быть снято в будущем выпуске.

Примеры

Создание подписки на удаленный сервер, который реплицирует таблицы в публикациях mypublication и insert_only с немедленным запуском репликации при фиксации транзакции:

CREATE SUBSCRIPTION mysub
         CONNECTION 'host=192.168.1.50 port=5432 user=foo dbname=foodb'
        PUBLICATION mypublication, insert_only;

Создание подписки на удаленный сервер, который реплицирует таблицы в публикацию insert_only с отложенным запуском репликации.

CREATE SUBSCRIPTION mysub
         CONNECTION 'host=192.168.1.50 port=5432 user=foo dbname=foodb'
        PUBLICATION insert_only
               WITH (enabled = false);

Совместимость

Команда CREATE SUBSCRIPTION является расширением QHB.

См. также

ALTER SUBSCRIPTION, DROP SUBSCRIPTION, CREATE PUBLICATION, ALTER PUBLICATION

CREATE TABLE AS

CREATE TABLE AS — определить новую таблицу из результатов запроса

Синтаксис

CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] имя_таблицы
    [ (имя_столбца [, ...] ) ]
    [ USING метод ]
    [ WITH ( параметр_хранения [= значение] [, ... ] ) | WITHOUT OIDS ]
    [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
    [ TABLESPACE имя_табличного_пространства ]
    AS запрос
    [ WITH [ NO ] DATA ]

Описание

Команда CREATE TABLE AS создает таблицу и заполняет ее данными, вычисленными командой SELECT. Столбцы таблицы имеют имена и типы данных, связанные с выходными столбцами SELECT (за исключением того, что можно переопределить имена столбцов, предоставив явный список новых имен столбцов).

Команда CREATE TABLE AS имеет некоторое сходство с созданием представления, но на самом деле это совсем другое: она создает новую таблицу и оценивает запрос только один раз, чтобы заполнить новую таблицу изначально. Новая таблица не будет отслеживать последующие изменения исходных таблиц запроса. Напротив, представление повторно оценивает определяющую его команду SELECT при каждом запросе к нему.

Параметры

GLOBAL или LOCAL

Игнорируется для совместимости. Использование этих ключевых слов не рекомендуется; дополнительную информацию см. в разделе CREATE TABLE.

TEMPORARY или TEMP

Если этот параметр указан, таблица создается как временная таблица. Дополнительную информацию см. в разделе CREATE TABLE.

UNLOGGED

Если этот параметр указан, таблица создается как незарегистрированная таблица. Дополнительную информацию см. в разделе CREATE TABLE.

IF NOT EXISTS

Не считать ошибкой, если связь с тем же именем уже существует. В этом случае будет выдано соответствующее уведомление. Дополнительную информацию см. в разделе CREATE TABLE.

имя_таблицы

Имя создаваемой таблицы (может быть дополнено схемой).

имя_столбца

Имя столбца в новой таблице. Если имена столбцов не предоставлены, они берутся из имен выходных столбцов запроса.

USING метод

Это необязательное предложение определяет метод доступа к таблице, который будет использоваться для хранения содержимого для новой таблицы; этот метод должен быть типа TABLE.
Дополнительную информацию см. в главе Определение интерфейса метода доступа к таблице.

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

В QHB есть встроенный метод доступа к таблице qss, при указании которого данные таблицы, ее индексов и журнала будут записываться на диск в зашифрованном виде.
Дополнительную информацию см. в разделе Модуль безопасного хранения QSS.

Также в QHB реализован встроенный метод доступа, реализующий хранилище append_only, для которого характерны следующие свойства:

Запрещена модификация данных (update и delete).

Осуществляется максимально быстрая вставка данных.

Отсутствует необходимость в автоочистке.

Поддерживаются все типы индексов.

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

Не поддерживается механизм TOAST.

Дополнительную информацию см. в разделе Таблицы APPEND_ONLY.

WITH ( параметр_хранения [= значение] [, ... ] )

В этом предложении указываются необязательные параметры хранения для новой таблицы. Дополнительную информацию см. в подразделе Параметры хранения в описании команды CREATE TABLE. В целях обратной совместимости предложение WITH для таблицы также может включать в себя OIDS=FALSE, чтобы указать, что строки новой таблицы не должны содержать OID (идентификаторы объектов); OIDS=TRUE не поддерживается.

WITHOUT OIDS

Это синтаксис обратной совместимости для объявления таблицы WITHOUT OIDS; создание таблицы WITH OIDS не поддерживается.

ON COMMIT

Поведение временных таблиц в конце блока транзакций можно контролировать с помощью ON COMMIT. Есть три варианта::

PRESERVE ROWS

По завершении транзакции никаких специальных действий не предпринимается. Это поведение по умолчанию.

DELETE ROWS

В конце каждого блока транзакций все строки во временной таблице будут удалены. По существу, при каждой фиксации выполняется автоматическое TRUNCATE.

DROP

В конце текущего блока транзакций временная таблица будет удалена.

TABLESPACE имя_табличного_пространства

имя_табличного_пространства — это имя табличного пространства, в котором будет создана новая таблица. Если этот параметр не указан, выполняется запрос default_tablespace или temp_tablespaces (если таблица временная).

запрос

Команда SELECT, TABLE или VALUES либо команда EXECUTE, которая выполняет подготовленный запрос SELECT, TABLE или VALUES.

WITH [NO ] DATA

Это предложение указывает, следует ли копировать данные, созданные запросом, в новую таблицу. Если нет, то копируется только структура таблицы. По умолчанию выполняется копирование данных.

Примечания

Эта команда функционально похожа на SELECT INTO, но она предпочтительнее, так как ее менее вероятно спутать с другими вариантами использования синтаксиса SELECT INTO. Кроме того, набор функций у CREATE TABLE AS больше, чем у SELECT INTO.

Примеры

Создание новой таблицы films_recent, состоящей только из последних записей из таблицы films:

CREATE TABLE films_recent AS
  SELECT * FROM films WHERE date_prod >= '2002-01-01';

Чтобы скопировать таблицу полностью, также можно использовать короткую форму с помощью команды TABLE:

CREATE TABLE films2 AS
  TABLE films;

Создание новой временной таблицы films_recent, состоящей только из последних записей таблицы films, используя подготовленный оператор. Новая таблица будет удалена при фиксации транзакции:

PREPARE recentfilms(date) AS
  SELECT * FROM films WHERE date_prod > $1;
CREATE TEMP TABLE films_recent ON COMMIT DROP AS
  EXECUTE recentfilms('2002-01-01');

Совместимость

Команда CREATE TABLE AS соответствует стандарту SQL. Ниже приведены нестандартные расширения:

Стандарт требует заключать предложение подзапроса в круглые скобки; в QHB эти скобки необязательны.

В стандарте требуется указывать предложение WITH [ NO ] DATA; в QHB это необязательно.

QHB обрабатывает временные таблицы не так, как указано в стандарте; подробную информацию см. в разделе CREATE TABLE.

Предложение WITH является расширением QHB; параметры хранения не входят в стандарт.

Концепция табличных пространств QHB не является частью стандарта. Следовательно, предложение TABLESPACE является расширением.

См. также

CREATE MATERIALIZED VIEW, CREATE TABLE, EXECUTE, SELECT, SELECT INTO, VALUES

CREATE TABLESPACE

CREATE TABLESPACE — определить новое табличное пространство

Синтаксис

CREATE TABLESPACE имя_табличного_пространства
    [ OWNER { новый_владелец | CURRENT_USER | SESSION_USER } ]
    LOCATION 'каталог'
    [ WITH ( параметр_табличного_пространства = значение [, ... ] ) ]

Описание

Команда CREATE TABLESPACE регистрирует новое табличное пространство в масштабе кластера. Имя табличного пространства должно отличаться от имени любого существующего табличного пространства в кластере баз данных.

Табличное пространство позволяет суперпользователям определять альтернативное расположение в файловой системе, в котором могут находиться файлы данных, содержащие объекты базы данных (такие как таблицы и индексы).

Пользователь с соответствующими правами может передать параметр имя_табличного_пространства команде CREATE DATABASE, CREATE TABLE, CREATE INDEX или ADD CONSTRAINT, чтобы файлы данных для этих объектов хранились в указанном табличном пространстве.

Предупреждение!
Табличное пространство не может использоваться независимо от кластера, в котором оно определено; см. раздел Табличные пространства.

Параметры

имя_табличного_пространства

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

имя_пользователя

Имя пользователя, которому будет принадлежать табличное пространство. Если этот параметр опущен, по умолчанию владельцем станет пользователь, выполняющий команду. Только суперпользователи могут создавать табличные пространства, но они могут назначать владение табличными пространствами не суперпользователям.

каталог

Каталог, который будет использоваться для табличного пространства. Каталог должен существовать (CREATE TABLESPACE не будет его создавать), быть пустым и принадлежать системному пользователю QHB. Каталог должен быть задан абсолютным именем пути.

параметр_табличного_пространства

Параметр табличного пространства, который необходимо установить или сбросить. В настоящее время доступны только следующие параметры: seq_page_cost, random_page_cost, effective_io_concurrency и maintenance_io_concurrency. Установка любого значения для конкретного табличного пространства переопределит обычную оценку планировщиком стоимости чтения страниц из таблиц в этом табличном пространстве, установленную параметрами конфигурации с тем же именем (см. подразделы seq_page_cost, random_page_cost, effective_io_concurrency, maintenance_io_concurrency). Это может быть полезно, если одно табличное пространство расположено на диске, который быстрее или медленнее, чем остальная часть подсистемы ввода-вывода.

Примечания

Табличные пространства поддерживаются только в системах, поддерживающих символьные ссылки.

Команда CREATE TABLESPACE не может быть выполнена внутри блока транзакций.

Примеры

Чтобы создать табличное пространство dbspace в расположении файловой системы /opt/qhb/data/dbs, сначала создайте каталог с помощью средств операционной системы и установите правильное владение:

mkdir /opt/qhb/data/dbs
chown qhb:qhb /opt/qhb/data/dbs

Затем выполните команду создания табличного пространства внутри QHB:

CREATE TABLESPACE dbspace LOCATION '/opt/qhb/data/dbs';

Чтобы создать табличное пространство, принадлежащее другому пользователю базы данных, используйте следующую команду:

CREATE TABLESPACE indexspace OWNER genevieve LOCATION '/opt/qhb/data/indexes';

Совместимость

Команда CREATE TABLESPACE является расширением QHB.

См. также

CREATE DATABASE, CREATE TABLE, CREATE INDEX, DROP TABLESPACE, ALTER TABLESPACE

CREATE TABLE

CREATE TABLE — определить новую таблицу

Синтаксис

CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] имя_таблицы ( [
  { имя_столбца тип_данных [ COLLATE правило_сортировки ] [ ограничение_столбца [ ... ] ]
    | ограничение_таблицы
    | LIKE исходная_таблица [ вариант_копирования ... ] }
    [, ... ]
] )
[ INHERITS ( таблица_родитель [, ... ] ) ]
[ PARTITION BY { RANGE | LIST | HASH } ( { имя_столбца | ( выражение ) } [ COLLATE правило_сортировки ] [ класс_операторов ] [, ... ] ) ]
[ USING метод ]
[ WITH ( параметр_хранения [= значение] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE имя_табличного_пространства ]

CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] имя_таблицы
    OF имя_типа [ (
  { имя_столбца [ WITH OPTIONS ] [ ограничение_столбца [ ... ] ]
    | ограничение_таблицы }
    [, ... ]
) ]
[ PARTITION BY { RANGE | LIST | HASH } ( { имя_столбца | ( выражение ) } [ COLLATE правило_сортировки ] [ класс_операторов ] [, ... ] ) ]
[ USING метод ]
[ WITH ( параметр_хранения [= значение] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE имя_табличного_пространства ]

CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] имя_таблицы
    PARTITION OF таблица_родитель [ (
  { имя_столбца [ WITH OPTIONS ] [ ограничение_столбца [ ... ] ]
    | ограничение_таблицы }
    [, ... ]
) ] { FOR VALUES указание_границ_партиции | DEFAULT }
[ PARTITION BY { RANGE | LIST | HASH } ( { имя_столбца | ( выражение ) } [ COLLATE правило_сортировки ] [ класс_операторов ] [, ... ] ) ]
[ USING метод ]
[ WITH ( параметр_хранения [= значение] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE имя_табличного_пространства ]

Где ограничение_столбца может быть:

[ CONSTRAINT имя_ограничения ]
{ NOT NULL |
  NULL |
  CHECK ( выражение ) [ NO INHERIT ] |
  DEFAULT выражение_по_умолчанию |
  GENERATED ALWAYS AS ( выражение_генерации ) STORED |
  GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( параметры_последовательности ) ] |
  UNIQUE параметры_индекса |
  PRIMARY KEY параметры_индекса |
  REFERENCES ссылочная_таблица [ ( ссылочный_столбец ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
    [ ON DELETE ссылочное_действие ] [ ON UPDATE ссылочное_действие ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]

Где ограничение_таблицы может быть:

[ CONSTRAINT имя_ограничения ]
{ CHECK ( выражение ) [ NO INHERIT ] |
  UNIQUE ( имя_столбца [, ... ] ) параметры_индекса |
  PRIMARY KEY ( имя_столбца [, ... ] ) параметры_индекса |
  EXCLUDE [ USING индексный_метод ] ( элемент_исключения WITH оператор [, ... ] ) параметры_индекса [ WHERE ( предикат ) ] |
  FOREIGN KEY ( имя_столбца [, ... ] ) REFERENCES целевая_таблица [ ( целевой_столбец [, ... ] ) ]
    [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE ссылочное_действие ] [ ON UPDATE ссылочное_действие ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]

Где вариант_копирования может быть::

{ INCLUDING | EXCLUDING } { COMMENTS | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }

Где указание_границ_партиции может быть:

IN ( выражение_границ_партиции [, ...] ) |
FROM ( { выражение_границ_партиции | MINVALUE | MAXVALUE } [, ...] )
  TO ( { выражение_границ_партиции | MINVALUE | MAXVALUE } [, ...] ) |
WITH ( MODULUS числовая_константа, REMAINDER числовая_константа )

Где параметр_индекса в ограничениях UNIQUE, PRIMARY KEY и EXCLUDE может быть:

[ INCLUDE ( имя_столбца [, ... ] ) ]
[ WITH ( параметр_хранения [= значение] [, ... ] ) ]
[ USING INDEX TABLESPACE имя_табличного_пространства ]

Где элемент_исключения в ограничении EXCLUDE может быть:

{ имя_столбца | ( выражение ) } [ класс_операторов ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ]

Описание

Команда CREATE TABLE создаст новую, изначально пустую таблицу в текущей базе данных. Таблица будет принадлежать пользователю, выполнившему эту команду.

Если задано имя схемы (например, CREATE TABLE myschema.mytable ...), то таблица создается в указанной схеме, в противном случае — в текущей. Временные таблицы существуют в специальной схеме, поэтому при их создании не может быть задано имя схемы. Имя таблицы должно отличаться от имени любой другой таблицы, последовательности, индекса, представления или сторонней таблицы в той же схеме.

Также команда CREATE TABLE автоматически создает тип данных, представляющий составной тип, соответствующий одной строке таблицы. Поэтому таблицы не могут иметь то же имя, что и любой существующий тип данных в той же схеме.

Необязательные предложения ограничений определяют ограничения (тесты), которым новые или измененные строки должны удовлетворять для успешного выполнения операции добавления или изменения. Ограничение — это объект SQL, который помогает различными способами определить набор допустимых значений в таблице.

Существует два способа определения ограничений: ограничения таблицы и ограничения столбца. Ограничение столбца определяется как часть определения столбца. Определение ограничения таблицы не привязано к определенному столбцу и может охватывать несколько столбцов. Каждое ограничение столбца также может быть записано как ограничение таблицы; ограничение столбца — это только удобство обозначений для использования, когда ограничение затрагивает только один столбец.

Чтобы иметь возможность создать таблицу, необходимо иметь право USAGE на все типы столбцов или тип в предложении OF соответственно.

Параметры

TEMPORARY или TEMP

Если указан этот параметр, таблица создается как временная таблица. Временные таблицы автоматически удаляются в конце сеанса или, при необходимости, в конце текущей транзакции (см. подраздел ON COMMIT ниже). Существующие постоянные таблицы с одинаковыми именами не видны текущему сеансу, пока существует временная таблица, если только они не связаны с именами, определенными схемой. Все индексы, созданные во временной таблице, также автоматически являются временными.

Процесс «Автовакуум» не может получить доступ и, следовательно, не может очищать или анализировать временные таблицы. По этой причине соответствующие операции очистки и анализа должны выполняться с помощью команд сеанса SQL. Например, если временная таблица будет использоваться в сложных запросах, целесообразно после ее заполнения запустить ANALYZE для временной таблицы.

При желании перед TEMPORARY или *TEMP можно написать GLOBAL или LOCAL. В настоящее время в QHB это не имеет никакого значения и является устаревшей опцией; см. раздел Совместимость.

UNLOGGED

Если указан этот параметр, таблица создается как нерегистрируемая таблица. Данные, записанные в нерегистрируемые таблицы, не записываются в журнал предварительной записи (см. главу Прочность и журнал записи), что делает их значительно быстрее, чем обычные таблицы. Однако они не защищены от сбоев: нерегистрируемая таблица автоматически усекается после сбоя или небезопасного завершения работы. Кроме того, содержимое нерегистрируемой таблицы не реплицируется на резервные серверы. Все индексы, созданные в незарегистрированной таблице, тоже автоматически становятся нерегистрируемыми.

IF NOT EXISTS

Не считать ошибкой, если отношение с тем же именем уже существует. В этом случае будет выдано соответствующее уведомление. Обратите внимание, что нет никакой гарантии, что существующее отношение как-то соотносится с тем, которое было бы создано.

имя_таблицы

Имя создаваемой таблицы (может быть дополнено схемой).

OF имя_типа

Создает типизированную таблицу, которая берет свою структуру из указанного составного типа (имя может быть дополнено схемой). Типизированная таблица привязывается к своему типу: например, таблица будет удалена, если тип будет удален (с DROP TYPE ... CASCADE).

При создании типизированной таблицы типы данных столбцов определяются базовым составным типом и не определяются командой CREATE TABLE. Но CREATE TABLE может добавлять в таблицу значения по умолчанию и ограничения, а также указывать параметры хранения.

имя_столбца

Имя столбца, который будет создан в новой таблице.

тип_данных

Тип данных столбца. Может включать в себя спецификаторы массива. Дополнительную информацию о типах данных, поддерживаемых QHB, см. в главе Типы данных.

COLLATE правило_сортировки

Предложение COLLATE назначает правило сортировки для столбца (который должен иметь сопоставимый тип данных). Если параметр не указан, используется правило сортировки по умолчанию для типа данных столбца.

INHERITS ( таблица_родитель [, ... ] )

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

Использование INHERITS создает постоянную связь между новой дочерней таблицей и ее родительской таблицей (или таблицами). Изменения схемы в родительской таблице (или таблицах) обычно распространяются также и на дочерние, и по умолчанию данные дочерней таблицы включаются в сканы родительской.

Если одно и то же имя столбца существует в более чем одной родительской таблице, то появляется сообщение об ошибке, если только типы данных столбцов не совпадают в каждой из родительских таблиц. В этом случае повторяющиеся столбцы объединяются, чтобы сформировать один столбец в новой таблице. Если список имен столбцов новой таблицы содержит имя столбца, которое тоже наследуется, тип данных аналогичным образом должен соответствовать унаследованному столбцу (или столбцам), и определения столбцов объединяются в одно. Если новая таблица явно указывает значение по умолчанию для столбца, оно переопределяет любые значения по умолчанию из унаследованных объявлений столбца. В противном случае все родительские таблицы, указывающие значения по умолчанию для столбца, должны указывать одно и то же значение по умолчанию, иначе будет сообщено об ошибке.

Ограничения CHECK объединяются, по сути, так же, как и столбцы: если несколько родительских таблиц и/или новое определение таблицы содержат ограничения CHECK с идентичными именами, то все эти ограничения должны иметь одно и то же выражение проверки, иначе появится сообщение об ошибке. Ограничения, имеющие одинаковое имя и выражение, будут объединены в одну копию. Ограничение, помеченное в родительской таблице как NO INHERIT, рассматриваться не будет. Обратите внимание, что безымянное ограничение CHECK в новой таблице никогда не будет объединено, так как для него всегда будет выбираться уникальное имя.

Параметры STORAGE для столбца также копируются из родительских таблиц.

Если столбец в родительской таблице является столбцом идентификаторов, это свойство не наследуется. При желании столбец в дочерней таблице можно объявить столбцом идентификаторов.

PARTITION BY { RANGE | LIST | HASH } ( { имя_столбца | ( выражение ) } [ класс_операторов ] [, ...] )

Необязательное предложение PARTITION BY задает стратегию разбиения таблицы. Созданная таким образом таблица называется партиционированной таблицей. Заключенный в скобки список столбцов или выражений формирует ключ разбиения для таблицы. При использовании партиционирования по диапазонам или по хешу ключ разбиения может включать несколько столбцов или выражений (до 32, но это ограничение может быть изменено при создании QHB), но для партиционирования по списку ключ разбиения должен состоять из одного столбца или выражения.

Для партиционирования по диапазонам и по списку требуется класс операторов В-дерева, а для партиционирования по хешу — класс операторов хеширования. Если класс операторов не указан явно, будет использоваться класс операторов по умолчанию соответствующего типа; если класс операторов по умолчанию отсутствует, будет вызвана ошибка. При использовании хеш-партиционирования применяемый класс операторов должен реализовывать вспомогательную функцию 2 (более подробную информацию см. в разделе Процедуры поддержки индексного метода).

Партиционированная таблица делится на подтаблицы (называемые партициями), которые создаются с помощью отдельных команд CREATE TABLE. Партиционированная таблица сама по себе пуста. Добавленная в таблицу строка данных направляется в партицию на основании значения столбцов или выражений в ключе разбиения. Если ни одна существующая партиция не соответствует значениям в новой строке, будет сообщено об ошибке.

Партиционированные таблицы не поддерживают ограничение EXCLUDE, однако эти ограничения можно определить для отдельных партиций.

Дополнительную информацию о партиционировании таблиц см. в разделе Партиционирование таблиц.

PARTITION OF таблица_родитель { FOR VALUES указание_границ_партиции | DEFAULT }

Создает таблицу как партицию указанной родительской таблицы. Таблица может быть создана либо как партиция для конкретных значений (с помощью FOR VALUES), либо в качестве партиции по умолчанию (с помощью DEFAULT). Все индексы, ограничения и пользовательские триггеры уровня строк, которые имеются в родительской таблице, клонируются в новую партицию.

указание_границ_партиции должно соответствовать методу и ключу разбиения родительской таблицы и не должно перекрываться ни с какой существующей партицией этого родителя. Форма с IN используется для партиционирования по списку, форма с FROM и TO — для партиционирования по диапазонам, а форма с WITH — для партиционирования по хешу.

выражение_границ_партиции — это любое выражение без переменных (подзапросы, оконные функции, агрегатные функции и функции возврата набора не допускаются). Его тип данных должен соответствовать типу данных соответствующего столбца ключа разбиения. Выражение вычисляется один раз во время создания таблицы, поэтому может даже содержать изменчивые выражения, такие как CURRENT_TIMESTAMP.

При создании списочной партиции можно указать в этом списке NULL, чтобы обозначить, что столбец ключа разбиения может содержать NULL. Однако у отдельно взятой родительской таблицы не может быть более одной такой списочной партиции. Нельзя указывать NULL для диапазонных партиций.

При создании диапазонной партиции нижняя граница, заданная с помощью FROM, является включающей границей, тогда как верхняя граница, заданная с помощью TO, является исключающей границей. То есть значения, указанные в списке FROM, — это допустимые значения соответствующих столбцов ключа разбиения для данной партиции, тогда как значения в списке TO таковыми не являются. Обратите внимание, что это утверждение следует понимать в соответствии с правилами сравнения строк (см. раздел Сравнение конструкторов строк). Например, с разделением PARTITION BY RANGE (x,y) партиция с границами FROM (1, 2) TO (3, 4) примет x=1 с любым значением y>=2, x=2 — с любым значением y, отличным от NULL, и x=3 — с любым y<4.

При создании диапазонной партиции можно использовать особые значения MINVALUE и MAXVALUE, чтобы указать, что нет нижней или верхней границы значения столбца. Например, партиция, определенная с помощью FROM (MINVALUE) TO (10) принимает любые значения меньше 10, а партиция, определенная с помощью FROM (10) TO (MAXVALUE), принимает любые значения, превышающие или равные 10.

При создании диапазонной партиции, включающей более одного столбца, также может иметь смысл использовать MAXVALUE как часть нижней границы и MINVALUE как часть верхней границы. Например, партиция, определенная с помощью FROM (0, MAXVALUE) TO (10, MAXVALUE), принимает любые строки, в которых первый столбец ключа разбиения больше 0 и меньше или равен 10. Аналогично, партиция, определенная с помощью FROM (’a’, MINVALUE) TO (’b’, MINVALUE), принимает любые строки, в которых первый столбец ключа разбиения начинается с «a».

Обратите внимание, что если MINVALUE или MAXVALUE используется для одного столбца границы партиции, то это же значение должно использоваться для всех последующих столбцов. Например, (10, MINVALUE, 0) не является допустимой границей; следует написать (10, MINVALUE, MINVALUE).

Также обратите внимание, что у некоторых типов элементов, таких как timestamp (отметка времени), есть понятие «infinity» (бесконечность), которое является просто еще одним сохраняемым значением. Оно отличается от MINVALUE и MAXVALUE, являющимися не реальными значениями, которые могут быть сохранены, а скорее способами сказать, что значение безгранично. MAXVALUE можно рассматривать как значение, превышающее любое другое, включая «бесконечность», а MINVALUE — как значение меньше любого другого, включая «минус бесконечность». Таким образом, диапазон FROM ('infinity') TO (MAXVALUE) — это не пустой диапазон; он позволяет хранить ровно одно значение — «infinity».

Если указывается DEFAULT, то таблица будет создана как партиция по умолчанию родительской таблицы. Этот параметр недоступен для хеш-партиционированных таблиц. Значение ключа разбиения, не вписывающееся в любую другую партицию данного родительского объекта, будет перенаправлено в партицию по умолчанию.

Когда таблица уже имеет партицию по умолчанию (DEFAULT) и к нему добавляется новая партиция, нужно просканировать партицию по умолчанию, чтобы убедиться, что она не содержит строк, которые точно подходят для новой партиции. Если партиция по умолчанию содержит большое количество строк, этот процесс может быть медленным. Проверка будет пропущена, если партиция по умолчанию является сторонней таблицей или имеет ограничение, которое доказывает, что она не может содержать строки, которые должны быть размещены в новой партиции.

При создании хеш-партиции необходимо указать модуль и остаток. Модуль должен быть положительным целым числом, а остаток — неотрицательным целым числом меньше модуля. Как правило, при первоначальной настройке хеш-партиционированной таблицы следует выбрать модуль, равный числу партиций, и назначить каждой таблице этот модуль и разные остатки (см. примеры ниже). Однако партициям необязательно иметь одинаковый модуль, достаточно чтобы каждый модуль, который встречается среди партиций хеш-партиционированной таблицы, был кратным следующего большего модуля. Это позволяет постепенно увеличивать число партиций без необходимости перемещать все данные одновременно. Например, предположим, что у вас есть хеш-партиционированная таблица с 8 партициями, каждая из которых имеет модуль 8, но необходимо увеличить число партиций до 16. Можно отсоединить одну из партиций по модулю 8, создать две новые партиции по модулю 16, охватывающие ту же часть пространства ключей (одну с остатком, равным остатку отсоединенной партиции, а другую с остатком, равным этому значению плюс 8), и повторно заполнить их данными. Затем этот процесс можно повторить — возможно, позднее — для каждой партиции по модулю 8, пока ни одной из них не останется. Хотя такой способ по-прежнему может включать в себя большой объем перемещения данных на каждом этапе, это всё же лучше, чем создавать целую новую таблицу и перемещать все данные сразу.

Партиция должна содержать те же имена и типы столбцов, что и партиционированная таблица, к которой она принадлежит. Изменения имен или типов столбцов партиционированной таблицы будут автоматически распространяться на все партиции. Ограничения CHECK будут наследоваться автоматически каждой партицией, но для отдельных партиций можно указать дополнительные ограничения CHECK; дополнительные ограничения с теми же именами и условиями, что и в родительском элементе, будут объединены с родительским ограничением. Значения по умолчанию могут быть указаны отдельно для каждой партиции. Однако обратите внимание, что значение по умолчанию, заданное для партиции, не будет применяться при добавлении кортежа через партиционированную таблицу.

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

Такие операции, как TRUNCATE, которые обычно влияют на таблицу и все ее дочерние элементы, будут распространяться на все партиции, но также могут выполняться на отдельной партиции. Обратите внимание, что удаление партиции с помощью DROP TABLE требует запрашивать блокировку ACCESS EXCLUSIVE на родительской таблице.

LIKE исходная_таблица [ вариант_копирования ... ]

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

В отличие от INHERITS, новая и исходная таблицы полностью разъединяются после завершения создания. Изменения исходной таблицы не будут применены к новой таблице, и нет возможности включить данные новой таблицы в сканы исходной таблицы.

Также, в отличие от INHERITS, столбцы и ограничения, скопированные при помощи LIKE, не объединяются с одноименными столбцами и ограничениями. Если одно и то же имя указано явно или содержится в другом предложении LIKE, будет выдана ошибка.

Необязательные предложения вариант_копирования указывают, какие дополнительные свойства исходной таблицы следует скопировать. Указание INCLUDING копирует свойство, указание EXCLUDING опускает свойство. EXCLUDING — это значение по умолчанию. Если для одного и того же типа объекта создается несколько спецификаций, то используется последняя из них. Доступные варианты:

INCLUDING COMMENTS

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

INCLUDING CONSTRAINTS

Будут скопированы ограничения CHECK. Между ограничениями столбцов и таблиц никакого различия не делается. Ограничения на NULL всегда копируются в новую таблицу.

INCLUDING DEFAULTS

Будут скопированы выражения по умолчанию для скопированных определений столбцов. В противном случае выражения по умолчанию не копируются, в результате чего скопированные столбцы в новой таблице имеют значения по умолчанию NULL. Обратите внимание, что копирование значений по умолчанию, которые вызывают функции изменения базы данных, такие как nextval, может создать функциональную связь между исходной и новой таблицами.

INCLUDING GENERATED

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

INCLUDING IDENTITY

Будут скопированы все спецификации идентификаторов скопированных определений столбцов. Новая последовательность создается для каждого столбца идентификаторов новой таблицы отдельно от последовательностей, связанных со старой таблицей.

INCLUDING INDEXES

Индексы и ограничения PRIMARY KEY, UNIQUE и EXCLUDE для исходной таблицы будут созданы для новой таблицы. Имена для новых индексов и ограничений выбираются в соответствии с правилами по умолчанию, независимо от того, как были названы оригиналы. (Это поведение позволяет избежать возможных ошибок повторяющихся имен для новых индексов.)

INCLUDING STATISTICS

Расширенная статистика копируется в новую таблицу.

INCLUDING STORAGE

Будут скопированы параметры STORAGE для скопированных определений столбцов. Поведением по умолчанию является исключение параметров STORAGE, в результате чего скопированные столбцы в новой таблице имеют параметры по умолчанию для конкретного типа. Дополнительную информацию о параметрах STORAGE см. в разделе TOAST.

INCLUDING ALL

INCLUDING ALL — это сокращенная форма выбора всех доступных индивидуальных настроек. (Может быть удобно после применения INCLUDING ALL написать отдельные предложения EXCLUDING, чтобы выбрать все, кроме некоторых конкретных параметров.)

Предложение LIKE также можно использовать для копирования определений столбцов из представлений, сторонних таблиц или составных типов. Неприменимые настройки (например, INCLUDING INDEXES из представления) игнорируются.

CONSTRAINT имя_ограничения

Необязательное имя для ограничения столбца или таблицы. Если ограничение нарушается, имя ограничения присутствует в сообщениях об ошибках, поэтому имена ограничений, такие как столбец должен быть положительным, можно использовать для передачи полезной информации об ограничениях клиентским приложениям. (Для указания имен ограничений, содержащих пробелы, необходимы двойные кавычки.) Если имя ограничения не указано, система создает его автоматически.

NOT NULL

Столбец не может содержать значения NULL.

NULL

Столбец может содержать значения NULL. Это значение по умолчанию.

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

CHECK ( выражение ) [ NO INHERIT ]

Предложение CHECK задает выражение, создающее логический результат, которому должны удовлетворять новые или измененные строки для успешного выполнения операции добавления или изменения. Выражения, результат которых равен TRUE или UNKNOWN, успешно выполняются. Если какая-либо строка операции добавления или изменения приводит к результату FALSE, возникает ошибка и эта операция не изменяет базу данных. Проверочное ограничение, указанное как ограничение столбца, должно ссылаться только на значение этого столбца, тогда как выражение, расположенное в ограничении таблицы, может ссылаться на несколько столбцов.

В настоящий момент выражения CHECK не могут содержать вложенные запросы и ссылаться на переменные, отличные от столбцов текущей строки (см. раздел Контрольные ограничения). Допустима ссылка на системный столбец tableoid, но не на другие системные столбцы.

Ограничение с пометкой NO INHERIT не будет распространяться на дочерние таблицы.

Когда таблица имеет несколько ограничений CHECK, они будут проверяться для каждой строки в алфавитном порядке по имени после проверки ограничений NOT NULL.

DEFAULT выражение_по_умолчанию

Предложение DEFAULT присваивает значение данных по умолчанию для столбца, в определении которого оно отображается. Значением является любое выражение без переменной (в частности, перекрестные ссылки на другие столбцы в текущей таблице не допускаются). Подзапросы также не допускаются. Тип данных выражения по умолчанию должен соответствовать типу данных столбца.

Выражение по умолчанию будет использоваться в любой операции добавления, которая не задает значение для столбца. Если для столбца нет значения по умолчанию, то оно будет равно NULL.

GENERATED ALWAYS AS ( выражение_генерации ) STORED

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

Ключевое слово STORED требуется для обозначения того, что столбец будет вычислен при записи и сохранен на диске.

Выражение генерации может ссылаться на другие столбцы в таблице, но не на другие генерируемые столбцы. Любые используемые функции и операторы должны быть неизменяемыми. Ссылки на другие таблицы не допускаются.

GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( параметры_последовательности ) ]

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

Предложения ALWAYS и BY DEFAULT определяют, каким образом значение последовательности имеет приоритет над пользовательским значением в операторе INSERT. Если задано значение ALWAYS, пользовательское значение принимается только в том случае, если в операторе INSERT указано VERRIDING SYSTEM VALUE. Если указано BY DEFAULT, то пользовательское значение имеет приоритет. Дополнительную информацию см. в разделе INSERT. (В команде COPY всегда используются пользовательские значения, независимо от этого параметра.)

Необязательное предложение параметры_последовательности можно использовать для переопределения параметров последовательности. Дополнительную информацию см. в разделе CREATE SEQUENCE.

UNIQUE (ограничение столбца) UNIQUE ( имя_столбца [, ... ] ) [ INCLUDE ( имя_столбца [, ...]) ] (ограничение таблицы)

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

При проверке ограничения уникальности значения NULL не считаются равными.

Каждое ограничение уникальности для таблицы должно содержать имя набора столбцов, отличного от набора столбцов, названных любым другим ограничением уникального или первичного ключа, определенным для таблицы. (В противном случае это будет просто одно и то же ограничение, указанное дважды.)

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

Добавление ограничения уникальности автоматически создаст уникальный индекс типа В-дерево для столбца или группы столбцов, используемых в ограничении. Необязательное предложение INCLUDE добавляет к этому индексу один или несколько столбцов, для которых не применяется уникальность. Обратите внимание, что хотя ограничение не применяется к включенным столбцам, оно всё равно зависит от них. Следовательно, некоторые операции над этими столбцами (например, DROP COLUMN) могут вызвать каскадное ограничение и удаление индекса.

PRIMARY KEY (ограничение столбца) PRIMARY KEY ( имя_столбца [, ... ] ) [ INCLUDE ( имя_столбца [, ...]) ] (ограничение таблицы)

Ограничение PRIMARY KEY указывает, что столбец или столбцы таблицы могут содержать только уникальные (не повторяющиеся) значения, отличные от NULL. Для таблицы можно указать только один первичный ключ, будь то ограничение столбца или ограничение таблицы.

Ограничение первичного ключа должно содержать имя набора столбцов, отличного от набора столбцов, названных любым ограничением уникальности, определенным для той же таблицы. (В противном случае ограничение уникальности является избыточным и будет отброшено.)

PRIMARY KEY принудительно применяет те же ограничения данных, что и комбинация UNIQUE и NOT NULL, но указание набора столбцов в качестве первичного ключа также предоставляет метаданные о конструкции схемы, поскольку первичный ключ подразумевает, что другие таблицы могут полагаться на этот набор столбцов в качестве уникального идентификатора для строк.

Ограничения PRIMARY KEY разделяют условия, которые имеют ограничения UNIQUE при размещении в партиционированных таблицах.

Добавление ограничения PRIMARY KEY автоматически создаст уникальный индекс типа B-дерево для столбца или группы столбцов, используемых в ограничении. Необязательное предложение INCLUDE позволяет указать список столбцов, которые будут включены в неключевую часть индекса. Хотя уникальность не применяется к включенным столбцам, ограничение всё равно зависит от них. Следовательно, некоторые операции над включенными столбцами (например, DROP COLUMN) могут вызвать каскадное ограничение и удаление индекса.

EXCLUDE [ USING индексный_метод ] ( элемент_исключения WITH оператор [, ... ] ) параметры_индекса [ WHERE ( предикат ) ]

Предложение EXCLUDE определяет ограничение исключения, которое гарантирует, что если любые две строки сравниваются на указанном столбце (столбцах) или выражении (выражениях) с использованием указанного оператора (или операторов), не все эти сравнения вернут значение TRUE. Если все указанные операторы проверяются на равенство, это эквивалентно ограничению UNIQUE, хотя обычное ограничение уникальности будет быстрее. Однако ограничения исключения могут указывать на ограничения, которые являются более общими, чем простое равенство. Например, можно указать ограничение, что никакие две строки в таблице не содержат перекрывающихся кругов (см. Геометрические типы), используя оператор &&.

Ограничения исключения реализуются с помощью индекса, поэтому каждый указанный оператор должен быть связан с соответствующим классом оператора (см. раздел Классы операторов и семейства операторов) для метода доступа к индексу индексный_метод. Операторы должны быть коммутативными. В каждом элементе_исключения можно дополнительно указать класс оператора и/или параметры сортировки; они полностью описаны в разделе CREATE INDEX.

Метод доступа должен поддерживать amgettuple (см. главу Определение интерфейса метода доступа к индексу); в настоящее время это означает, что GIN использовать нельзя. Хотя это разрешено, нет особого смысла в использовании с ограничением исключения B-дерева или хеш-индексов, потому что это ничуть не лучше, чем обычное ограничение уникальности. Поэтому на практике метод доступа всегда будет GiST или SP-GiST.

Параметр предикат позволяет задать ограничение исключения для подмножества таблицы; внутри он создает частичный индекс. Обратите внимание, что вокруг предиката требуются круглые скобки.

REFERENCES ссылочная_таблица [ ( ссылочный_столбец ) ] [ MATCH тип_совпадения ] [ ON DELETE ссылочное_действие ] [ ON UPDATE ссылочное_действие ] (ограничение столбца) FOREIGN KEY ( имя_столбца [, ...\ ] ) REFERENCES ссылочная_таблица [ ( ссылочный_столбец [, ... ] ) ] [ MATCH тип_совпадения ] [ ON DELETE ссылочное_действие ] [ ON UPDATE ссылочное_действие ] (ограничение таблицы)

Эти предложения задают ограничение внешнего ключа, которое требует, чтобы группа из одного или нескольких столбцов новой таблицы содержала только значения, соответствующие значениям в ссылочном столбце (или столбцах) некоторых строк ссылочной таблицы. Если список ссылочных_столбцов опущен, в качестве него будет использован первичный ключ ссылочной_таблицы. Ссылочные столбцы должны быть столбцами неоткладываемого ограничения уникальности или первичного ключа в ссылочной таблице. Необходимо иметь разрешение REFERENCES на эту ссылочную таблицу (либо на всю таблицу, либо на конкретные ссылочные столбцы). Добавление ограничения внешнего ключа требует блокировки SHARE ROW EXCLUSIVE соответствующей ссылочной таблицы. Обратите внимание, что ограничения внешнего ключа нельзя определить между временными и постоянными таблицами.

Значение, добавленное в ссылочный столбец (или столбцы), сопоставляется со значениями ссылочной таблицы и ссылочных столбцов с использованием заданного типа соответствия. Существует три типа соответствия: MATCH FULL, MATCH PARTIAL и MATCH SIMPLE (что является значением по умолчанию). MATCH FULL не допускает, чтобы один столбец многоколоночного внешнего ключа имел значение NULL, если только все столбцы внешнего ключа не имеют значения NULL; если все они имеют значение NULL, то строка не обязательно должна совпадать в указанной таблице. MATCH SIMPLE позволяет любому из столбцов внешнего ключа иметь значение NULL; если какой-либо из них имеет значение NULL, строка не обязательно должна совпадать в ссылочной таблице. MATCH PARTIAL пока не реализовано. (Разумеется, для предотвращения возникновения подобных случаев к ссылочному столбцу (или столбцам) могут быть применены ограничения NOT NULL.)

Кроме того, при изменении данных в ссылочных столбцах выполняются определенные действия с данными в столбцах этой таблицы. Предложение ON DELETE указывает действие, которое необходимо выполнить при удалении ссылочной строки в ссылочной таблице. Аналогичным образом предложение ON UPDATE указывает действие, которое необходимо выполнить, когда ссылочный столбец в ссылочной таблице обновляется до нового значения. Если строка обновляется, но ссылочный столбец фактически не изменяется, никакие действия не выполняются. Ссылочные действия, отличные от NO ACTION, нельзя сделать откладываемыми, даже если ограничение объявлено отложенным. Для каждого предложения возможны следующие действия:

NO ACTION

Выдать ошибку, указывающую на то, что удаление или обновление приведет к нарушению ограничения внешнего ключа. Если ограничение отложено, эта ошибка произойдет во время проверки ограничения, если всё еще существуют какие-либо ссылочные строки. Это действие по умолчанию.

RESTRICT

Выдать ошибку, указывающую на то, что удаление или обновление приведет к нарушению ограничения внешнего ключа. Это то же самое, что и NO ACTION, за исключением того, что проверка не откладывается.

CASCADE

Удалить все строки, ссылающиеся на удаленную строку, или обновить значения ссылочного столбца (или столбцов) до новых значений ссылочных столбцов соответственно.

SET NULL

Установить для ссылочных столбцов значение NULL.

SET DEFAULT

Установить для ссылочного столбца (или столбцов) значения по умолчанию. (В ссылочной таблице должна быть строка, соответствующая значениям по умолчанию, если их значение не NULL, иначе операция завершится ошибкой.)

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

DEFERRABLE NOT DEFERRABLE

Это предложение определяет, может ли ограничение быть отложено. Неоткладываемое ограничение будет проверяться сразу же после выполнения каждой команды. Проверка откладываемых ограничений может быть отсрочена до окончания транзакции (с помощью команды SET CONSTRAINTS). NOT DEFERRABLE является значением по умолчанию. В настоящее время только ограничения UNIQU, PRIMARY KEY, EXCLUDE и REFERENCES (внешний ключ) принимают это предложение. Ограничения NOT NULL и CHECK являются неоткладываемыми. Обратите внимание, что откладываемые ограничения нельзя использовать в качестве арбитраторов конфликтов в операторе INSERT, который включает в себя предложение ON CONFLICT DO UPDATE.

INITIALLY IMMEDIATE INITIALLY DEFERRED

Если ограничение является откладываемым, это предложение указывает время по умолчанию для проверки ограничения. Ограничение с характеристикой INITIALLY IMMEDIATE проверяется после каждого оператора. Это значение по умолчанию. Ограничение с характеристикой INITIALLY DEFERRED проверяется только в конце транзакции. Время проверки ограничений можно изменить с помощью команды SET CONSTRAINTS.

USING метод

Это необязательное предложение определяет метод доступа к таблице, который будет использоваться для хранения содержимого для новой таблицы; этот метод должен быть типа TABLE.
Дополнительную информацию см. в главе Определение интерфейса метода доступа к таблице.

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

В QHB есть встроенный метод доступа к таблице qss, при указании которого данные таблицы, ее индексов и журнала будут записываться на диск в зашифрованном виде.
Дополнительную информацию см. в разделе Модуль безопасного хранения QSS.

Также в QHB реализован встроенный метод доступа, реализующий хранилище append_only, для которого характерны следующие свойства:

Запрещена модификация данных (update и delete).

Осуществляется максимально быстрая вставка данных.

Отсутствует необходимость в автоочистке.

Поддерживаются все типы индексов.

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

Не поддерживается механизм TOAST.

Дополнительную информацию см. в разделе Таблицы APPEND_ONLY.

WITH ( параметр_хранения [= значение] [, ... ] )

В этом предложении указываются необязательные параметры хранения для таблицы или индекса; дополнительную информацию см. в подразделе Параметры хранения. Для обратной совместимости предложение WITH для таблицы также может включать в себя условие OIDS=FALSE, чтобы указать, что строки новой таблицы не должны содержать OID (идентификаторы объектов); OIDS=TRUE не поддерживается.

WITHOUT OIDS

Это синтаксис обратной совместимости для объявления таблицы WITHOUT OIDS; создание таблицы WITH OIDS не поддерживается.

ON COMMIT

Поведение временных таблиц в конце блока транзакций можно контролировать с помощью ON COMMIT. Есть три варианта:

PRESERVE ROWS

Никаких специальных действий по завершению транзакций не предпринимается. Это поведение по умолчанию.

DELETE ROWS

Все строки во временной таблице будут удалены в конце каждого блока транзакций. По существу, при каждой фиксации автоматически выполняется TRUNCATE. При использовании в партиционированной таблице это не распространяется на ее партиции.

DROP

Временная таблица будет удалена в конце текущего блока транзакций. При использовании в партиционированной таблице это действие удаляет ее партиции, а при использовании в таблицах с дочерними элементами в иерархии наследования — зависимые дочерние элементы.

TABLESPACE имя_табличного_пространства

имя_табличного_пространства — это имя табличного пространства, в котором будет создана новая таблица. Если этот параметр не указан, выполняется запрос default_tablespace или temp_tablespaces (если таблица временная). Для партиционированных таблиц, поскольку для самой таблицы хранение не требуется, указанное табличное пространство переопределяет default_tablespace как табличное пространство по умолчанию, используемое для любых вновь созданных партиций, когда никакое другое табличное пространство явно не указано.

USING INDEX TABLESPACE имя_табличного_пространства

Это предложение позволяет выбрать табличное пространство, в котором будет создан индекс, связанный с ограничением UNIQUE, PRIMARY KEY или EXCLUDE. Если этот параметр не указан, выполняется запрос default_tablespace или temp_tablespaces (если таблица временная).

Параметры хранения

Предложение WITH может указывать параметры хранения для таблиц, а также для индексов, связанных с ограничениями UNIQUE, PRIMARY KEY или EXCLUDE. Параметры хранения для индексов описаны в разделе CREATE INDEX. Ниже перечислены параметры хранения, доступные в настоящее время для таблиц. Для многих из этих параметров, как показано, существует дополнительный параметр с тем же именем, префиксом которого является toast., который управляет поведением вторичной таблицы TOAST, если таковая имеется (дополнительную информацию о таких таблицах см. в разделе TOAST). Если установлено значение параметра таблицы, а и аналогичный параметр toast. не определен, таблица TOAST будет использовать значение параметра таблицы. Указание этих параметров для партиционированных таблиц не поддерживается, но их можно задать для отдельных конечных партиций.

fillfactor (integer)

Коэффициент заполнения для таблицы, задается в процентах от 10 до 100. Значение по умолчанию равно 100 (полное заполнение). Если указан меньший коэффициент заполнения, то операции добавления упаковывают страницы таблицы только до указанного процента; оставшееся пространство на каждой странице зарезервировано для изменения строк на этой странице. Это дает UPDATE возможность разместить измененную копию строки на той же странице, что и оригинал, что более эффективно, чем размещение его на другой странице. Для таблицы, записи которой никогда не обновляются, лучшим выбором является полное заполнение, но в активно обновляемых таблицах целесообразнее использовать более мелкие коэффициенты заполнения. Этот параметр нельзя задать для таблиц TOAST.

toast_tuple_target (integer)

toast_tuple_target задает минимальную необходимую длину кортежа, после превышения которой мы попытаемся сжать и/или переместить значения длинных столбцов в таблицы TOAST, а также целевое значение, до которого мы попытаемся уменьшить нижележащую длину после перехода к TOAST. Это касается столбцов, помеченных как External (внешние, которые могут переноситься), Main (основные, которые могут сжиматься) или Extended (расширенные, которые могут как сжиматься, так и просто переноситься) и применяется только к новым кортежам. На существующие строки это не влияет. По умолчанию значение этого параметра допускает размещение не менее 4 кортежей на блок, что при стандартном размере блока составляет 2040 байт. Допустимые значения находятся в диапазоне от 128 байт до (размер блока — заголовок), по умолчанию 8160 байт. Изменение этого значения может быть не очень полезно для слишком коротких или слишком длинных строк. Обратите внимание, что настройка по умолчанию часто близка к оптимальной, и вполне возможно, что перенастройка этого параметра в некоторых случаях может иметь негативные последствия. Этот параметр нельзя задать для таблиц TOAST.

parallel_workers (integer)

Этот параметр задает число процессов, которые должны использоваться для поддержки параллельного сканирования данной таблицы. Если этот параметр не задан, система определит значение на основе размера отношения. Фактическое число процессов, выбранных планировщиком или служебными операторами, использующими параллельное сканирование, может быть меньше, например, из-за установки ограничения max_worker_processes.

autovacuum_enabled, toast.autovacuum_enabled (boolean)

Включает или выключает процесс «Автовакуум» для определенной таблицы. Если задано значение true, то процесс «Автовакуум» будет выполнять в этой таблице автоматические операции VACUUM и/или ANALYZE, следуя правилам, описанным в разделе Процесс «Автовакуум». Если значение равно false, эта таблица не будет подвергнута автоочистке, за исключением случаев, когда необходимо предотвратить зацикливание идентификатора транзакции. Дополнительную информацию о предотвращении зацикливания см. в разделе Предотвращение ошибок зацикливания идентификатора транзакции. Обратите внимание, что процесс «Автовакуум» не запускается вообще (кроме как для предотвращения обхода идентификатора транзакции), если параметр autovacuum имеет значение false; установка параметров хранения для отдельных таблиц это не переопределяет. Поэтому явно задавать этому параметру хранения значение true не имеет особого смысла — полезно только значение false.

vacuum_index_cleanup, toast.vacuum_index_cleanup (boolean)

Включает или выключает очистку индекса при выполнении команды VACUUM в этой таблице. Значение по умолчанию равно true. Выключение очистки индекса может значительно ускорить выполнение VACUUM, но также может привести к появлению сильно «раздутых» индексов, если изменения таблицы происходят часто. Параметр INDEX_CLEANUP команды VACUUM, если он указан, переопределяет значение этого параметра.

vacuum_truncate, toast.vacuum_truncate (boolean)

Включает или выключает очистку, чтобы попытаться провести усечение всех пустых страниц в конце этой таблицы. Значение по умолчанию равно true. При значении true VACUUM и автоочистка делают усечение, и дисковое пространство для усеченных страниц возвращается в операционную систему. Обратите внимание, что для усечения требуется блокировка ACCESS EXCLUSIVE для таблицы. Явно указанный параметр TRUNCATE команды VACUUM переопределяет значение этого параметра.

autovacuum_vacuum_threshold, toast.autovacuum_vacuum_threshold (integer)

Значение параметра autovacuum_vacuum_threshold для таблицы.

autovacuum_vacuum_scale_factor, toast.autovacuum_vacuum_scale_factor (floating point)

Значение параметра autovacuum_vacuum_scale_factor для таблицы.

autovacuum_vacuum_insert_threshold, toast.autovacuum_vacuum_insert_threshold (integer)

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

autovacuum_vacuum_insert_scale_factor, toast.autovacuum_vacuum_insert_scale_factor (floating point)

Значение параметра autovacuum_vacuum_insert_scale_factor для таблицы.

autovacuum_analyze_threshold (integer)

Значение параметра autovacuum_analyze_threshold для таблицы.

autovacuum_analyze_scale_factor (floating point)

Значение параметра autovacuum_analyze_scale_factor для таблицы.

autovacuum_vacuum_cost_delay, toast.autovacuum_vacuum_cost_delay (floating point)

Значение параметра autovacuum_vacuum_cost_delay для таблицы.

autovacuum_vacuum_cost_limit, toast.autovacuum_vacuum_cost_limit (integer)

Значение параметра autovacuum_vacuum_cost_limit для таблицы.

autovacuum_freeze_min_age, toast.autovacuum_freeze_min_age (integer)

Значение параметра vacuum_freeze_min_age для таблицы. Обратите внимание, что автоочистка будет игнорировать табличные параметры autovacuum_freeze_min_age, превышающие половину значения общесистемного параметра autovacuum_freeze_max_age.

autovacuum_freeze_max_age, toast.autovacuum_freeze_max_age (integer)

Значение параметра autovacuum_freeze_max_age для таблицы. Обратите внимание, что автоочистка будет игнорировать табличные параметры autovacuum_freeze_max_age, превышающие значение общесистемного параметра (они могут быть установлены только меньше).

autovacuum_freeze_table_age, toast.autovacuum_freeze_table_age (integer)

Значение параметра vacuum_freeze_table_age для таблицы.

autovacuum_multixact_freeze_min_age, toast.autovacuum_multixact_freeze_min_age (integer)

Значение параметра vacuum_multixact_freeze_min_age для таблицы. Обратите внимание, что автоочистка будет игнорировать табличные параметры autovacuum_multixact_freeze_min_age, превышающие половину значения общесистемного параметра autovacuum_multixact_freeze_max_age.

autovacuum_multixact_freeze_max_age, toast.autovacuum_multixact_freeze_max_age (integer)

Значение параметра autovacuum_multixact_freeze_max_age для таблицы. Обратите внимание, что автоочистка будет игнорировать табличные параметры autovacuum_multixact_freeze_max_age, превышающие значение общесистемного параметра (они могут быть установлены только меньше).

autovacuum_multixact_freeze_table_age, toast.autovacuum_multixact_freeze_table_age (integer)

Значение параметра vacuum_multixact_freeze_table_age для таблицы.

log_autovacuum_min_duration, toast.log_autovacuum_min_duration (integer)

Значение параметра log_autovacuum_min_duration для таблицы.

user_catalog_table (boolean)

Объявить таблицу дополнительной таблицей каталога в целях логической репликации. Этот параметр нельзя задать для таблиц TOAST.

holdmem (OFF, POSSIBLY, ONLY)

Параметр указывающий что для таблицы будет использоваться особый кэш дисковых блоков. Подробнее см. Параметр HOLDMEM и дополнительные кэши дисковых блоков

Примечания

QHB автоматически создает индекс, обеспечивающий уникальность для каждого ограничения уникальности и ограничения первичного ключа. Таким образом, нет необходимости явно создавать индекс для столбцов первичного ключа. (Дополнительную информацию см. в разделе CREATE INDEX.)

Ограничения уникальности и первичные ключи в текущей реализации не наследуются. Поэтому сочетание наследования и ограничений уникальности довольно дисфункционально.

Таблица не может иметь более 1600 столбцов. (На практике эффективный предел обычно ниже из-за ограничений длины кортежа.)

Примеры

Создание таблиц films и distributors:

CREATE TABLE films (
    code        char(5) CONSTRAINT firstkey PRIMARY KEY,
    title       varchar(40) NOT NULL,
    did         integer NOT NULL,
    date_prod   date,
    kind        varchar(10),
    len         interval hour to minute
);

CREATE TABLE distributors (
     did    integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
     name   varchar(40) NOT NULL CHECK (name<> '')
);

Создание таблицы с двумерным массивом:

CREATE TABLE array_int (
    vector  int[][]
);

Определение ограничения уникальности таблицы для таблицы films. Ограничения уникальности таблицы могут быть определены для одного или нескольких столбцов таблицы:

CREATE TABLE films (
    code        char(5),
    title       varchar(40),
    did         integer,
    date_prod   date,
    kind        varchar(10),
    len         interval hour to minute,
    CONSTRAINT production UNIQUE(date_prod)
);

Определение ограничения-теста для столбца:

CREATE TABLE distributors (
    did     integer CHECK (did > 100),
    name    varchar(40)
);

Определение ограничения-теста для таблицы:

CREATE TABLE distributors (
    did     integer,
    name    varchar(40),
    CONSTRAINT con1 CHECK (did > 100 AND name<> '')
);

Определение ограничения первичного ключа для таблицы films:

CREATE TABLE films (
    code        char(5),
    title       varchar(40),
    did         integer,
    date_prod   date,
    kind        varchar(10),
    len         interval hour to minute,
    CONSTRAINT code_title PRIMARY KEY(code,title)
);

Определение ограничения первичного ключа для таблицы distributors. Следующие два примера равнозначны: первый использует синтаксис ограничения для таблицы, а второй — для столбца:

CREATE TABLE distributors (
    did     integer,
    name    varchar(40),
    PRIMARY KEY(did)
);

CREATE TABLE distributors (
    did     integer PRIMARY KEY,
    name    varchar(40)
);

Установка значений по умолчанию: для столбца name это литерная константа, для столбца did оно генерируется путем выбора следующего значения объекта последовательности, для modtime это время добавления строки:

CREATE TABLE distributors (
    name      varchar(40) DEFAULT 'Luso Films',
    did       integer DEFAULT nextval('distributors_serial'),
    modtime   timestamp DEFAULT current_timestamp
);

Определение двух ограничений NOT NULL для столбцов в таблице distributors, одному из которых явно дано имя:

CREATE TABLE distributors (
    did     integer CONSTRAINT no_null NOT NULL,
    name    varchar(40) NOT NULL
);

Определение ограничения уникальности для столбца name:

CREATE TABLE distributors (
    did     integer,
    name    varchar(40) UNIQUE
);

То же самое, указанное в качестве ограничения для таблицы:

CREATE TABLE distributors (
    did     integer,
    name    varchar(40),
    UNIQUE(name)
);

Создание той же таблицы с указанием коэффициента заполнения 70% как для самой таблицы, так и для ее уникального индекса:

CREATE TABLE distributors (
    did     integer,
    name    varchar(40),
    UNIQUE(name) WITH (fillfactor=70)
)
WITH (fillfactor=70);

Создание таблицы circles с ограничением исключения, которое предотвращает перекрытие любых двух кругов:

CREATE TABLE circles (
    c circle,
    EXCLUDE USING gist (c WITH &&)
);

Создание таблицы cinemas в табличном пространстве diskvol1:

CREATE TABLE cinemas (
        id serial,
        name text,
        location text
) TABLESPACE diskvol1;

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

CREATE TYPE employee_type AS (name text, salary numeric);

CREATE TABLE employees OF employee_type (
    PRIMARY KEY (name),
    salary WITH OPTIONS DEFAULT 1000
);

Создание таблицы, партиционированной по диапазонам:

CREATE TABLE measurement (
    logdate         date not null,
    peaktemp        int,
    unitsales       int
) PARTITION BY RANGE (logdate);

Создание таблицы, партиционированной по диапазонам, с несколькими столбцами в ключе разбиения:

CREATE TABLE measurement_year_month (
    logdate         date not null,
    peaktemp        int,
    unitsales       int
) PARTITION BY RANGE (EXTRACT(YEAR FROM logdate), EXTRACT(MONTH FROM logdate));

Создание таблицы, партиционированной по списку:

CREATE TABLE cities (
    city_id      bigserial not null,
    name         text not null,
    population   bigint
) PARTITION BY LIST (left(lower(name), 1));

Создание хеш-партиционированной таблицы:

CREATE TABLE orders (
    order_id     bigint not null,
    cust_id      bigint not null,
    status       text
) PARTITION BY HASH (order_id);

Создание партиции таблицы, партиционированной по диапазонам:

CREATE TABLE measurement_y2016m07
    PARTITION OF measurement (
    unitsales DEFAULT 0
) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');

Создание нескольких партиций таблицы, партиционированной по диапазонам, с несколькими столбцами в ключе разбиения:

CREATE TABLE measurement_ym_older
    PARTITION OF measurement_year_month
    FOR VALUES FROM (MINVALUE, MINVALUE) TO (2016, 11);

CREATE TABLE measurement_ym_y2016m11
    PARTITION OF measurement_year_month
    FOR VALUES FROM (2016, 11) TO (2016, 12);

CREATE TABLE measurement_ym_y2016m12
    PARTITION OF measurement_year_month
    FOR VALUES FROM (2016, 12) TO (2017, 01);

CREATE TABLE measurement_ym_y2017m01
    PARTITION OF measurement_year_month
    FOR VALUES FROM (2017, 01) TO (2017, 02);

Создание партиции таблицы со списочным партиционированием:

CREATE TABLE cities_ab
    PARTITION OF cities (
    CONSTRAINT city_id_nonzero CHECK (city_id != 0)
) FOR VALUES IN ('a', 'b');

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

CREATE TABLE cities_ab
    PARTITION OF cities (
    CONSTRAINT city_id_nonzero CHECK (city_id != 0)
) FOR VALUES IN ('a', 'b') PARTITION BY RANGE (population);

CREATE TABLE cities_ab_10000_to_100000
    PARTITION OF cities_ab FOR VALUES FROM (10000) TO (100000);

Создание партиций хеш-партиционированной таблицы:

CREATE TABLE orders_p1 PARTITION OF orders
    FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE orders_p2 PARTITION OF orders
    FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE orders_p3 PARTITION OF orders
    FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE orders_p4 PARTITION OF orders
    FOR VALUES WITH (MODULUS 4, REMAINDER 3);

Создание партиции по умолчанию:

CREATE TABLE cities_partdef
    PARTITION OF cities DEFAULT;

Совместимость

Команда CREATE TABLE соответствует стандарту SQL с исключениями, указанными ниже.

Временная таблица

Хотя синтаксис CREATE TEMPORARY TABLE похож на тот, что имеется в стандарте SQL, их действие неодинаково. В стандарте временные таблицы определяются только один раз и автоматически существуют (начиная с пустого содержимого) в каждом сеансе, который нуждается в них. QHB вместо этого требует, чтобы каждый сеанс выдавал для каждой используемой временной таблицы свою собственную команду CREATE TEMPORARY TABLE. Это позволяет различным сеансам использовать одно и то же имя временной таблицы для различных целей, в то время как подход стандарта ограничивает все экземпляры данного имени временной таблицы одной и той же структурой таблицы.

Стандартное определение поведения временных таблиц по большей части игнорируется. Поведение QHB на этом этапе аналогично поведению нескольких других баз данных SQL.

Стандарт SQL также различает глобальные и локальные временные таблицы, где локальная временная таблица имеет отдельный набор содержимого для каждого модуля SQL в каждом сеансе, хотя его определение всё еще является общим для всех сеансов. Поскольку QHB не поддерживает модули SQL, это различие в QHB отсутствует.

Для обеспечения совместимости QHB принимает в объявлении временной таблицы ключевые слова GLOBAL и LOCAL, но в настоящее время они не имеют никакого эффекта. Использование этих ключевых слов не рекомендуется, так как будущие версии QHB могут принять более стандартную интерпретацию их значения.

Предложение ON COMMIT для временных таблиц также напоминает стандарт SQL, но имеет некоторые отличия. Если предложение ON COMMIT опущено, SQL указывает, что поведением по умолчанию является ON COMMIT DELETE ROWS. Однако в QHB поведением по умолчанию является ON COMMIT PRESERVE ROWS. Параметра ON COMMIT DROP в SQL не существует.

Неотложные ограничения уникальности

Когда ограничение UNIQUE или PRIMARY KEY не является откладываемым, QHB проверяет уникальность непосредственно в момент добавления или изменения строки. Стандарт SQL говорит, что уникальность должна быть применена только в конце оператора; это имеет значение, когда, например, одна команда изменяет множество ключевых значений. Чтобы получить поведение, соответствующее стандарту, объявите ограничение как DEFERRABLE (откладываемое), но не отложенное (т. е. INITIALLY IMMEDIATE). Имейте в виду, что это может быть значительно медленнее, чем немедленная проверка уникальности.

Ограничения-тесты для столбцов

В стандарте SQL говорится, что ограничения CHECK для столбца могут ссылаться только на столбец, к которому они применяются; только ограничения CHECK для таблицы могут ссылаться на множество столбцов. QHB не применяет это условие; он обрабатывает ограничения-тесты столбцов и таблиц одинаково.

Ограничение EXCLUDE

Ограничение EXCLUDE является расширением QHB.

«Ограничение» NULL

«Ограничение» NULL (на самом деле не является ограничением) является расширением QHB для стандарта SQL, которое включено для совместимости с некоторыми другими системами баз данных (и для симметрии с ограничением NOT NULL). Поскольку это поведение по умолчанию для любого столбца, присутствие данного слова не несет смысловой нагрузки.

Ограничение именования

Стандарт SQL говорит, что ограничения таблицы и домена должны иметь имена, которые являются уникальными в схеме, содержащей таблицу или домен. QHB в этом плане либеральнее: он требует только, чтобы имена были уникальными среди ограничений, прикрепленных к определенной таблице или домену. Однако эта дополнительная свобода не существует для ограничений на основе индексов (ограничения UNIQUE, PRIMARY KEY и EXCLUDE), поскольку связанный индекс имеет то же имя, что и ограничение, а имена индексов должны быть уникальными во всех отношениях в пределах одной схемы.

В настоящее время QHB вообще не записывает имена для ограничения NOT NULL, поэтому они не подлежат ограничению уникальности. Это может измениться в будущем выпуске.

Наследование

Множественное наследование через предложение INHERITS является языковым расширением QHB. SQL:1999 и более поздние версии определяют одиночное наследование, используя другой синтаксис и другую семантику. Наследование стиля SQL:1999 в QHB пока еще не поддерживается.

Таблицы с нулевыми столбцами

QHB позволяет создавать таблицу без столбцов (например, CREATE TABLE foo();). Это расширение от стандарта SQL, который не позволяет таблицы с нулевыми столбцами. Таблицы с нулевыми столбцами сами по себе не очень полезны, но их запрещение создает специальные случаи для ALTER TABLE DROP COLUMN, поэтому кажется более правильным игнорировать это ограничение стандарта.

Множество столбцов идентификаторов

QHB позволяет таблице иметь более одного столбца идентификаторов. Стандарт же указывает, что таблица может иметь не более одного столбца идентификаторов. Это ограничение смягчено главным образом для обеспечения большей гибкости при выполнении изменений схемы или миграции. Обратите внимание, что команда INSERT поддерживает только одно предложение переопределения, которое применяется ко всему оператору, поэтому наличие нескольких столбцов идентификаторов с различным поведением поддерживается не очень хорошо.

Генерируемые столбцы

Вариант STORED не является стандартным, но также используется другими реализациями SQL. Стандарт SQL не определяет хранение генерируемых столбцов.

Предложение LIKE

Хотя предложение LIKE и существует в стандарте SQL, многие параметры, которые принимает для него QHB, не входят в стандарт, и наоборот, некоторые параметры стандарта не реализуются в QHB.

Предложение USING qss

Предложение USING с методом qss. является расширением QHB.

Предложение USING append_only

Предложение USING с методом append_only является расширением QHB.

Предложение WITH

Предложение WITH является расширением QHB; параметры хранения не входят в стандарт.

TABLESPACE

Концепция табличных пространств QHB не является частью стандарта. Следовательно, пункты TABLESPACE и USING INDEX TABLESPACE являются расширениями.

Типизированная таблица

Типизированные таблицы реализуют подмножество стандарта SQL. Согласно стандарту, типизированная таблица имеет столбцы, соответствующие базовому составному типу, а также еще один столбец, который является «самоссылающимся столбцом». QHB явно не поддерживает столбцы, ссылающиеся на себя.

Предложение PARTITION BY

Предложение PARTITION BY является расширением QHB.

Предложение PARTITION OF

Предложение PARTITION OF является расширением QHB.

См. также

ALTER TABLE, DROP TABLE, CREATE TABLE AS, CREATE TABLESPACE, CREATE TYPE

CREATE TEXT SEARCH CONFIGURATION

CREATE TEXT SEARCH CONFIGURATION — определить новую конфигурацию текстового поиска

Синтаксис

CREATE TEXT SEARCH CONFIGURATION имя (
    PARSER = имя_анализатора |
    COPY = исходная_конфигурация
)

Описание

Команда CREATE TEXT SEARCH CONFIGURATION создает новую конфигурацию текстового поиска. Конфигурация текстового поиска задает синтаксический анализатор текстового поиска, который может разделить строку на фрагменты, а также словари, которые могут использоваться для определения, какие из фрагментов представляют интерес для поиска.

Если указан только синтаксический анализатор, то новая конфигурация текстового поиска изначально не имеет сопоставлений между типами фрагментов и словарями и поэтому будет игнорировать все слова. Для создания сопоставлений и извлечения пользы из конфигурации следом необходимо использовать команду ALTER TEXT SEARCH CONFIGURATION. Или же можно скопировать существующую конфигурацию текстового поиска.

Если задано имя схемы, то конфигурация текстового поиска создается в указанной схеме, в противном случае — в текущей.

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

Параметры

имя

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

имя_анализатора

Имя синтаксического анализатора текстового поиска, используемого для этой конфигурации.

исходная_конфигурация

Имя существующей конфигурации текстового поиска для копирования.

Примечания

Параметры PARSER и COPY являются взаимоисключающими, поскольку при копировании существующей конфигурации копируется и выбранный в ней синтаксический анализатор.

Совместимость

В стандарте SQL нет команды CREATE TEXT SEARCH CONFIGURATION.

См. также

ALTER TEXT SEARCH CONFIGURATION, DROP TEXT SEARCH CONFIGURATION

CREATE TEXT SEARCH DICTIONARY

CREATE TEXT SEARCH DICTIONARY — определить новый словарь текстового поиска

Синтаксис

CREATE TEXT SEARCH DICTIONARY имя (
    TEMPLATE = шаблон
    [, параметр = значение [, ... ]]
)

Описание

Команда CREATE TEXT SEARCH DICTIONARY создает новый словарь текстового поиска. Словарь текстового поиска задает способ распознавания интересных или неинтересных для поиска слов. Словарь зависит от шаблона текстового поиска, который определяет функции, фактически выполняющие эту работу. Обычно словарь предоставляет некоторые параметры, которые управляют подробным поведением функций шаблона.

Если задано имя схемы, то словарь текстового поиска создается в указанной схеме, в противном случае — в текущей.

Пользователь, определивший словарь текстового поиска, становится его владельцем.

Параметры

имя

Имя создаваемого словаря текстового поиска (может быть дополнено схемой).

шаблон

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

параметр

Имя привязанного к шаблону параметра, устанавливаемого для этого словаря.

значение

Значение, используемое для привязанного к шаблону параметра. Если значение не является простым идентификатором или числом, оно должно быть заключено в кавычки (при желании его можно заключать в кавычки всегда).

Параметры могут отображаться в любом порядке.

Примеры

В следующем примере команда создает словарь на основе Snowball с нестандартным списком стоп-слов.

CREATE TEXT SEARCH DICTIONARY my_russian (
    template = snowball,
    language = russian,
    stopwords = myrussian
);

Совместимость

В стандарте SQL нет команды CREATE TEXT SEARCH DICTIONARY.

См. также

ALTER TEXT SEARCH DICTIONARY, DROP TEXT SEARCH DICTIONARY

CREATE TEXT SEARCH PARSER

CREATE TEXT SEARCH PARSER — определить новый синтаксический анализатор текстового поиска

Синтаксис

CREATE TEXT SEARCH PARSER имя (
    START = функция_начала ,
    GETTOKEN = функция_выдачи_фрагмента ,
    END = функция_окончания ,
    LEXTYPES = функция_лекс_типов
    [, HEADLINE = функция_выдержек ]
)

Описание

Команда CREATE TEXT SEARCH PARSER создает новый синтаксический анализатор текстового поиска. Синтаксический анализатор текстового поиска определяет способ разбиения текстовой строки на фрагменты и назначения фрагментам типов (категорий). Синтаксический анализатор сам по себе не особенно полезен, но должен быть привязан к конфигурации текстового поиска вместе с некоторыми словарями текстового поиска, которые будут использоваться для поиска.

Если задано имя схемы, то синтаксический анализатор текстового поиска создается в указанной схеме, в противном случае — в текущей.

Чтобы использовать команду CREATE TEXT SEARCH PARSER, нужно быть суперпользователем. (Это ограничение сделано потому, что ошибочное определение синтаксического анализатора текстового поиска может нарушить или даже аварийно завершить работу сервера.)

Параметры

имя

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

функция_начала

Имя функции запуска для синтаксического анализатора.

функция_выдачи_фрагмента

Имя функции, выдающей следующий фрагмент для синтаксического анализатора.

функция_окончания

Имя конечной функции для синтаксического анализатора.

функция_лекс_типов

Имя функции перечисления лексических типов для синтаксического анализатора (функция, которая возвращает информацию о производимом ей наборе типов фрагментов).

функция_выдержек

Имя функции извлечения выдержек (функция, которая выделяет краткое содержание для набора фрагментов).

При необходимости имена функций могут быть дополнены схемой. Типы аргументов не задаются, так как список аргументов для каждого типа функции предопределен. Все функции, кроме функции выдержек, являются обязательными.

Аргументы могут появляться в любом порядке, а не только в показанном выше.

Совместимость

В стандарте SQL нет команды CREATE TEXT SEARCH PARSER.

См. также

ALTER TEXT SEARCH PARSER, DROP TEXT SEARCH PARSER

CREATE TEXT SEARCH TEMPLATE

CREATE TEXT SEARCH TEMPLATE — определить новый шаблон текстового поиска

Синтаксис

CREATE TEXT SEARCH TEMPLATE имя (
    [ INIT = функция_инициализации , ]
    LEXIZE = функция_выделения_лексем
)

Описание

Команда CREATE TEXT SEARCH TEMPLATE создает новый шаблон текстового поиска. Шаблоны текстового поиска определяют функции, реализующие словари текстового поиска. Сам по себе шаблон бесполезен, но его нужно установить как словарь для будущего использования. В словаре обычно указываются параметры, которые должны быть заданы функциям шаблона.

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

Чтобы использовать команду CREATE TEXT SEARCH TEMPLATE, нужно быть суперпользователем. Это ограничение сделано из-за того, что ошибочное определение шаблона текстового поиска может нарушить или даже аварийно завершить работу сервера. Причина отделения шаблонов от словарей заключается в том, что шаблон инкапсулирует «небезопасные» аспекты определения словаря. Параметры, которые можно задать при определении словаря, не могут причинить вред, поэтому создание словаря не требует особых прав.

Параметры

имя

Имя создаваемого шаблона текстового поиска (может быть дополнено схемой).

функция_инициализации

Имя функции инициализации для шаблона.

функция_выделения_лексем

Имя функции выделения лексем для шаблона.

При необходимости имена функций могут быть определены схемой. Типы аргументов не задаются, так как список аргументов для каждого типа функции предопределен. Функция выделения лексем является необходимой, но функция инициализации необязательна.

Аргументы могут появляться в любом порядке, а не только в показанном выше.

Совместимость

В стандарте SQL нет команды CREATE TEXT SEARCH TEMPLATE.

См. также

ALTER TEXT SEARCH TEMPLATE, DROP TEXT SEARCH TEMPLATE

CREATE TRANSFORM

CREATE TRANSFORM — определить новую трансформацию

Синтаксис

CREATE [ OR REPLACE ] TRANSFORM FOR имя_типа LANGUAGE имя_языка (
    FROM SQL WITH FUNCTION имя_функции_из_sql [ (тип_аргумента [, ...]) ],
    TO SQL WITH FUNCTION имя_функции_в_sql [ (тип_аргумента [, ...]) ]
);

Описание

Команда CREATE TRANSFORM определяет новую трансформацию. CREATE OR REPLACE TRANSFORM будет либо создавать новую трансформацию, либо заменять существующее определение.

Трансформация указывает, как адаптировать тип данных к процедурному языку. Например, при написании функции на языке PL/Python с использованием типа hstore PL/Python заранее не знает, как представить значения hstore в среде Python. Языковые реализации обычно по умолчанию используют текстовое представление, но это неудобно, когда, например, более подходящими были бы ассоциативный массив или список.

Трансформация определяет две функции:

Функция «из SQL», которая преобразует тип из среды SQL в среду языка. Эта функция будет вызываться для аргументов функции, написанной на этом языке.

Функция «в SQL», которая преобразует тип из среды языка в среду SQL. Эта функция будет вызываться для значения, возвращаемого из функции, написанной на этом языке.

Нет необходимости предоставлять обе эти функции одновременно. Если одна из них не указана, при необходимости будет использоваться поведение по умолчанию для конкретного языка. (Чтобы полностью исключить возможность трансформации в одном направлении, можно написать функцию трансформации, которая будет всегда выдавать ошибку.)

Чтобы иметь возможность создать трансформацию, нужно быть владельцем и иметь право USAGE для типа, иметь право USAGE для языка и быть владельцем и иметь право EXECUTE для функций из-SQL и в-SQL, если те заданы.

Параметры

имя_типа

Имя типа данных, для которого предназначена трансформация.

имя_языка

Имя языка, для которого предназначена трансформация.

имя_функции_из_sql[(тип_аргумента [, ...])]

Имя функции для преобразования типа из среды SQL в среду языка. Она должна принимать один аргумент типа internal и возвращать тип internal. Фактический аргумент будет иметь тип, предназначенный для трансформирования, и функция должна быть закодирована под это. (Но нельзя объявить функцию, возвращающую тип internal, на уровне SQL без как минимум одного входного аргумента типа internal.) Фактическое возвращаемое значение будет определяться реализацией языка. Если не указан список аргументов, имя функции должно быть уникальным в схеме.

имя_функции_в_sql[(тип_аргумента [, ...])]

Имя функции для преобразования типа из среды языка в среду SQL. Она должна принимать один аргумент типа internal и возвращать тип, который и предназначен для трансформации. Фактическое значение аргумента будет определяться реализацией языка. Если не указан список аргументов, имя функции должно быть уникальным в схеме.

Примечания

Для удаления трансформации используйте DROP TRANSFORM.

Примеры

Чтобы создать трансформацию для типа hstore и языка plpythonu, сначала нужно создать тип и язык:

CREATE TYPE hstore ...;

CREATE EXTENSION plpythonu;

Затем создать необходимые функции:

CREATE FUNCTION hstore_to_plpython(val internal) RETURNS internal
LANGUAGE C STRICT IMMUTABLE
AS ...;

CREATE FUNCTION plpython_to_hstore(val internal) RETURNS hstore
LANGUAGE C STRICT IMMUTABLE
AS ...;

И наконец создать трансформацию, чтобы соединить их вместе:

CREATE TRANSFORM FOR hstore LANGUAGE plpythonu (
    FROM SQL WITH FUNCTION hstore_to_plpython(internal),
    TO SQL WITH FUNCTION plpython_to_hstore(internal)
);

На практике эти команды будут помещены в расширение.

Раздел contrib содержит ряд расширений, предоставляющих трансформации, которые могут служить в качестве реальных примеров.

Совместимость

Команда CREATE TRANSFORM является расширением QHB. В стандарте SQL существует команда CREATE TRANSFORM, но она предназначена для адаптации типов данных к языкам на стороне клиента. Такой вариант использования QHB не поддерживается.

См. также

CREATE FUNCTION, CREATE LANGUAGE, CREATE TYPE, DROP TRANSFORM

CREATE TRIGGER

CREATE TRIGGER — определить новый триггер

Синтаксис

CREATE [ CONSTRAINT ] TRIGGER имя { BEFORE | AFTER | INSTEAD OF } { событие [ OR ... ] }
    ON имя_таблицы
    [ FROM имя_ссылочной_таблицы ]
    [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
    [ REFERENCING { { OLD | NEW } TABLE [ AS ] имя_переходного_отношения } [ ... ] ]
    [ FOR [ EACH ] { ROW | STATEMENT } ]
    [ WHEN ( условие ) ]
    EXECUTE { FUNCTION | PROCEDURE } имя_функции ( аргументы )

Где событием может быть:

    INSERT
    UPDATE [ OF имя_столбца [, ... ] ]
    DELETE
    TRUNCATE

Описание

Команда CREATE TRIGGER создает новый триггер. Триггер будет связан с заданной таблицей, представлением или сторонней таблицей и будет выполнять указанную функцию имя_функции при выполнении определенных типов операций с таблицей.

Триггер может быть настроен так, чтобы срабатывать до попытки выполнения операции со строкой (до проверки ограничений и попытки выполнить INSERT, UPDATE или DELETE), или после завершения операции (после проверки ограничений и завершения INSERT, UPDATE или DELETE), или вместо операции (в случае добавления, обновления или удаления строк в представлении). Если триггер срабатывает до или вместо события, он может пропустить операцию с текущей строкой или изменить добавляемую строку (только для операций INSERT и UPDATE). Если триггер срабатывает после события, все изменения, включая результаты действия других триггеров, будут для него «видимыми».

Триггер с указанием FOR EACH ROW вызывается один раз для каждой строки, которая изменяется в результате операции. Например, операция DELETE, которая затрагивает(удаляет) 10 строк, вызовет срабатывание всех триггеров с событием ON DELETE для целевого отношения 10 раз подряд: по одному разу для каждой удаляемой строки. И наоборот, триггер с указанием FOR EACH STATEMENT срабатывает только один раз для любой заданной операции, независимо от того, сколько строк она изменяет (в частности, выполнение операции, которая не изменит ни одной строки, всё равно приведет к вызову всех применимых триггеров FOR EACH STATEMENT).

Триггеры, предназначенные для запуска вместо события, т. е. INSTEAD OF, должны быть помечены как FOR EACH ROW и могут быть определены только для представлений. Триггеры с режимами BEFORE и AFTER для представления должны быть помечены как FOR EACH STATEMENT.

Кроме того, триггеры могут быть определены для операции TRUNCATE, но только с указанием FOR EACH STATEMENT.

В следующей таблице перечислены типы триггеров, которые могут использоваться для таблиц, представлений и сторонних таблиц:

Когда | Событие | На уровне строк | На уровне оператора
BEFORE | INSERT/UPDATE/DELETE | Таблицы и сторонние таблицы | Таблицы, представления и сторонние таблицы
BEFORE | TRUNCATE | — | Таблицы
AFTER | INSERT/UPDATE/DELETE | Таблицы и сторонние таблицы | Таблицы, Представления и сторонние таблицы
AFTER | TRUNCATE | — | Таблицы
INSTEAD OF | INSERT/UPDATE/DELETE | Представления | —
INSTEAD OF | TRUNCATE | — | —

Кроме того, в определении триггера можно указать логическое условие WHEN, после проверки которого определяется, должен ли срабатывать триггер. В триггерах на уровне строк условие WHEN может проверять старые и/или новые значения столбцов строки. Триггеры на уровне оператора также могут содержать условия WHEN, хотя для них эта функциональность не так полезна, так как в условии нельзя сослаться на какие-либо значения в таблице.

Если для одного и того же события определено несколько триггеров одного типа, они будут срабатывать в алфавитном порядке их имен.

Если указан параметр CONSTRAINT, команда создает триггер ограничения. Он такой же, как и обычный триггер, за исключением того, что время его срабатывания можно регулировать с помощью команды SET CONSTRAINTS. Триггеры ограничений должны быть созданы с указанием AFTER ROW для обычных таблиц (не для сторонних). Они могут срабатывать либо в конце оператора, вызывающего целевое событие, либо в конце содержащей оператор транзакции; в последнем случае они считаются отложенными. Срабатывание ожидающего отложенного запуска триггера также может быть принудительно вызвано с помощью команды SET CONSTRAINTS. Ожидается, что триггеры ограничений должны генерировать исключение, когда нарушаются реализуемые ими ограничения.

Если указан параметр REFERENCING, для триггера собираются переходные отношения, которые представляют собой наборы строк, включающие все строки, которые были добавлены, удалены или изменены текущим оператором SQL. Эта функциональность позволяет триггеру иметь глобальное представление о том, что сделал оператор, а не только об одной строке за раз. Это указание разрешено только для триггера с режимом AFTER, который не является триггером ограничения; также, если такой триггер является триггером для операций UPDATE, у него должен отсутствовать список имен_столбцов. Указание OLD TABLE может быть задано только один раз и только для триггера, который срабатывает для операций UPDATE или DELETE; это указание создает переходное отношение, которое содержит образы до изменения всех строк, обновленных или удаленных оператором. Аналогично указание NEW TABLE может быть задано только один раз и только для триггера, который срабатывает для операций UPDATE или INSERT; это указание создает переходное отношение, которое содержит образы после изменения всех строк, обновленных или добавленных оператором.

Операция SELECT не изменяет строки, поэтому нельзя создать триггеры SELECT. Для решения проблем, в которых, видимо, требуются такие триггеры, могут подойти правила и представления

Дополнительную информацию о триггерах см. в главе Триггеры.

Параметры

имя

Имя, задаваемое новому триггеру. Оно должно отличаться от имени любого другого триггера для той же таблицы. Имя триггера не может быть дополнено схемой, так как тот наследует схему своей таблицы. Для триггера ограничения это имя также используется для изменения поведения триггера с помощью команды SET CONSTRAINTS.

BEFORE
AFTER
INSTEAD OF

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

событие

Принимает одно из значений: INSERT, UPDATE, DELETE или TRUNCATE; параметр указывает на событие, которое приведет к срабатыванию триггера. Можно указать несколько событий с помощью слова OR, за исключением случаев, когда запрашиваются переходные отношения.

Для событий UPDATE можно указать список столбцов, используя следующий синтаксис:

UPDATE OF имя_столбца1 [, имя_столбца2 ... ]

Триггер будет срабатывать только в том случае, если хотя бы один из перечисленных столбцов указан как цель команды UPDATE или является генерируемым и при этом зависящим от столбца, который фигурирует в UPDATE.

Для событий INSTEAD OF UPDATE не допускается использование списка столбцов. Список столбцов также не может быть указан при запросе переходных отношений.

имя_таблицы

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

имя_ссылающейся_таблицы

Имя другой таблицы, на которую ссылается ограничение (может быть дополнено схемой). Этот параметр используется для ограничений внешнего ключа и не рекомендуется для обычного использования. Параметр может быть указан только для триггеров ограничений.

DEFERRABLE
NOT DEFERRABLE
INITIALLY IMMEDIATE
INITIALLY DEFERRED

Время срабатывания триггера по умолчанию. Подробную информацию об этих параметрах см. в разделе CREATE TABLE. Параметр может быть указан только для триггеров ограничений.

REFERENCING

Это ключевое слово непосредственно предшествует объявлению одного или двух имен отношений, обеспечивают доступ к переходным отношениям при выполнении операторов, вызванных триггером.

OLD TABLE NEW TABLE

Это предложение указывает, относится ли следующее имя к отношению перехода исходного образа записи или к отношению перехода преобразованного образа записи.

имя_переходного_отношения

Имя (без указания схемы), которое будет использоваться в триггере для этого переходного отношения.

FOR EACH ROW FOR EACH STATEMENT

Этот параметр указывает, запускать ли функцию триггера по одному разу для каждой строки, на которую влияет событие триггера, или только один раз для оператора SQL. Если ничего не указано, по умолчанию подразумевается FOR EACH STATEMENT. Для триггеров ограничений можно указать только FOR EACH ROW.

условие

Логическое выражение, определяющее, будет ли фактически выполняться функция триггера. Если указано WHEN, функция будет вызываться только в том случае, если условие вернет true. В триггерах FOR EACH ROW условие WHEN может ссылаться на столбцы старых и/или новых значений строк в виде записи OLD.имя_столбца или NEW.имя_столбца соответственно. Конечно, триггеры INSERT не могут ссылаться на OLD, а триггеры DELETE не могут ссылаться на NEW.

Триггеры INSTEAD OF не поддерживают условия WHEN.

В настоящий момент выражения WHEN не могут содержать вложенные запросы.

Обратите внимание, что для триггеров ограничений вычисление условия WHEN не откладывается, а выполняется сразу после операции изменения строки. Если результат проверки условия не true, то триггер не помещается в очередь для отложенного выполнения.

имя_функции

Функция, заданная пользователем, которая объявляется как функция без аргументов и возвращающая тип trigger и которая выполняется при срабатывании триггера.

В синтаксисе команды CREATE TRIGGER ключевые слова FUNCTION и PROCEDURE равнозначны, но указанная функция должна в любом случае быть функцией, а не процедурой. Ключевое слово PROCEDURE оставлено по историческим причинам и является устаревшим.

аргументы

Необязательный список аргументов, разделенных запятыми, которые будут переданы функции при срабатывании триггера. Аргументы являются строковыми константами. Также в качестве аргументов можно указать простые имена и числовые константы, но все они будут преобразованы в строки. Следует проверить описание языка реализации триггерной функции, чтобы точно знать порядок обращения к таким аргументам внутри функции, так как он может отличаться от обычных аргументов функции.

Примечания

Чтобы создать триггер, необходимо иметь право TRIGGER для таблицы, а также право EXECUTE для функции триггера.

Для удаления триггера используйте команду DROP TRIGGER.

Триггер для определенных столбцов (созданный с помощью синтаксиса UPDATE OF имя_столбца) будет срабатывать, когда любой из его столбцов перечислен в качестве целевого в списке SET команды UPDATE. Значение столбца можно изменить, даже когда триггер не работает, потому что изменения содержимого строки, внесенные с помощью триггеров BEFORE UPDATE, не рассматриваются. И наоборот, команда вроде UPDATE ... SET x = x ... запустит триггер по колонке x, даже если значение столбца не изменилось.

Существует несколько встроенных триггерных функций, которые можно использовать для решения распространенных задач без необходимости написания собственного триггерного кода; см. раздел Триггерные функции.

В триггере BEFORE условие WHEN вычисляется непосредственно перед фактическим или возможным выполнением функции, поэтому использование WHEN существенно не отличается от проверки того же условия в начале триггерной функции. В частности, обратите внимание, что строка NE, рассматриваемая условием, содержит текущее значение, которое, возможно, было изменено предыдущими триггерами. Кроме того, условие WHEN триггера BEFORE не сможет проверять системные столбцы строк NEW (такие как ctid), потому что они еще не будут установлены.

В триггере AFTER условие WHEN вычисляется сразу после того, как происходит изменение строки, и это определяет, помещается ли событие в очередь для запуска триггера в конце оператора. Поэтому когда условие WHE триггера AFTER не возвращает true, нет необходимости ставить событие в очередь или повторно считывать строку в конце оператора. Это может привести к значительному ускорению операторов, изменяющих много строк, если триггер должен срабатывать только для нескольких из них.

В некоторых случаях одна команда SQL может запустить несколько видов триггеров. Например, команда INSERT с предложением ON CONFLICT DO UPDATE может вызвать как операции добавления, так и операции изменения, поэтому она будет запускать оба типа триггеров по мере необходимости. Отношения перехода, предоставляемые триггерам, являются специфичными для их типа событий; таким образом, триггер INSERT будет видеть только добавленные строки, в то время как триггер UPDATE будет видеть только измененные строки.

Изменения или удаления строк, вызванные принудительными действиями внешнего ключа, такими как ON UPDATE CASCADE или ON DELETE SET NULL, рассматриваются как часть команды SQL, которая вызвала их (обратите внимание, что такие действия никогда не откладываются). В затрагиваемой таблице будут запущены соответствующие триггеры, так что это дает команде SQL еще один способ запускать триггеры, не вполне соответствующие их типу. В простых случаях триггеры, которые запрашивают отношения перехода, будут видеть все изменения, сделанные в их таблице одной исходной командой SQL, в виде одного отношения перехода. Однако существуют случаи, в которых наличие триггера AFTER ROW, который запрашивает отношения перехода, приведет к тому, что действия принудительного применения внешнего ключа, инициированные одной командой SQL, будут разделены на несколько этапов, каждый со своим собственным отношением (или отношениями) перехода. В таких случаях любые имеющиеся триггеры уровня оператора будут вызваны один раз при создании отношения перехода, гарантируя, что триггеры будут видеть каждую затронутую строку в отношения перехода один и только один раз.

Триггеры уровня операторов для представления срабатывают только в том случае, если операция с представлением обрабатывается триггером уровня строк INSTEAD OF. Если операция обрабатывается по правилу INSTEAD, то все генерируемые им операторы выполняются вместо исходного оператора, обращающегося к этому представлению, так что триггеры, которые будут запущены, являются триггерами для таблиц, к которым обращаются заменяющие операторы. Аналогично, если представление автоматически обновляется, то операция обрабатывается путем автоматического переписывания оператора в виде операции с базовой таблицей представления, так что запускаются триггеры уровня операторов базовой таблицы.

Создание триггера уровня строки в партиционированной таблице приведет к созданию идентичных триггеров во всех ее партициях; и все партиции, созданные или присоединенные позже, также будут содержать идентичный триггер. Если партиция отсоединяется от таблицы-родителя, триггер удаляется. Триггеры для партиционированных таблиц могут быть только с режимом AFTER.

Изменение данных в партиционированной таблице или таблице, у которой есть потомки, запускает триггеры уровня оператора, связанные с явно задействованной таблицей, но не триггеры уровня оператора для ее партиций или дочерних таблиц. Напротив, триггеры уровня строк запускаются для строк в затронутых партициях или дочерних таблицах, даже если они явно не присутствуют в запросе. Если триггер уровня оператора был определен с отношениями перехода, указанными в предложении REFERENCING, то в них будут видны исходные и преобразованные образы строк из всех затронутых партиций или дочерних таблиц. В случае дочерних таблиц в иерархии наследования образы строк включают только столбцы, которые присутствуют в таблице, связанной с триггером. В настоящее время триггеры уровня строк с отношениями перехода нельзя определить для партиций или дочерних таблиц в иерархии наследования.

Примеры

Выполнение функции check_account_update всякий раз перед изменением строк таблицы accounts:

CREATE TRIGGER check_update
    BEFORE UPDATE ON accounts
    FOR EACH ROW
    EXECUTE FUNCTION check_account_update();

То же самое, но функция будет выполняться, если столбец balance указан в списке целевых столбцов команды UPDATE:

CREATE TRIGGER check_update
    BEFORE UPDATE OF balance ON accounts
    FOR EACH ROW
    EXECUTE FUNCTION check_account_update();

В этом примере функция будет выполняться, если значение столбца balance действительно изменилось:

CREATE TRIGGER check_update
    BEFORE UPDATE ON accounts
    FOR EACH ROW
    WHEN (OLD.balance IS DISTINCT FROM NEW.balance)
    EXECUTE FUNCTION check_account_update();

Вызов функции, ведущей журнал изменений в accounts, но только если что-то изменилось:

CREATE TRIGGER log_update
    AFTER UPDATE ON accounts
    FOR EACH ROW
    WHEN (OLD.* IS DISTINCT FROM NEW.*)
    EXECUTE FUNCTION log_account_update();

Выполнение для каждой строки функции view_insert_row, которая будет добавлять строки в нижележащие таблицы представления:

CREATE TRIGGER view_insert
    INSTEAD OF INSERT ON my_view
    FOR EACH ROW
    EXECUTE FUNCTION view_insert_row();

Выполнение функции check_transfer_balances_to_zero для каждого оператора, чтобы убедиться, что строки таблицы transfer в совокупности дают нулевой баланс:

CREATE TRIGGER transfer_insert
    AFTER INSERT ON transfer
    REFERENCING NEW TABLE AS inserted
    FOR EACH STATEMENT
    EXECUTE FUNCTION check_transfer_balances_to_zero();

Выполнение функции check_matching_pairs для каждой строки, чтобы убедиться, что соответствующие пары пунктов изменены синхронно (одним оператором):

CREATE TRIGGER paired_items_update
    AFTER UPDATE ON paired_items
    REFERENCING NEW TABLE AS newtab OLD TABLE AS oldtab
    FOR EACH ROW
    EXECUTE FUNCTION check_matching_pairs();

Раздел Полный пример запуска содержит полный пример триггерной функции, написанной на языке C.

Совместимость

Команда CREATE TRIGGER в QHB реализует подмножество возможностей, описанных в стандарте SQL. В настоящее время отсутствуют следующие функциональные возможности:

В то время как имена переходных таблиц для триггеров AFTER задаются с помощью предложения REFERENCING стандартным образом, переменные строк, используемые в триггерах FOR EACH ROW нельзя указывать в предложении REFERENCING. Порядок обращения к таким строкам зависит от языка, на котором написана функция триггера, но для каждого языка он вполне определенный. Некоторые языки действуют так, как будто предложение REFERENCING присутствует в команде, и содержит указание OLD ROW AS OLD NEW ROW AS NEW.

Стандарт позволяет использовать переходные таблицы со специфичными для столбцов триггерами UPDATE, но тогда набор строк, которые должны быть видны в переходных таблицах, должен зависеть от списка целевых столбцов триггера. В настоящее время в QHB это не реализовано.

QHB разрешает задавать в качестве действия триггера только пользовательскую функцию. Стандарт же позволяет выполнять в качестве действия триггера ряд других команд SQL, таких как CREATE TABLE. Это ограничение нетрудно обойти, создав пользовательскую функцию, которая выполняет нужные команды.

В стандарте SQL определено, что несколько триггеров должны срабатывать в том порядке, в каком они были созданы. QHB использует порядок имен, который был сочтен более удобным.

В стандарте SQL указано, что триггеры BEFORE DELETE для каскадного удаления запускаются после завершения каскадного DELETE. В QHB триггеры BEFORE DELETE всегда запускаются перед операцией удаления, даже каскадной. Это поведение выбрано как более логичное. Существует еще одно отклонение от стандарта, если триггеры BEFORE изменяют строки или препятствуют обновлениям во время обновления, вызванного ссылочной операцией. Это может привести к нарушениям ограничений или сохранению данных, которые не соблюдают ссылочную целостность.

Возможность задать несколько действий для одного триггера с помощью слова OR является расширением стандарта SQL, реализованным в QHB.

Возможность вызова триггеров для операции TRUNCATE является расширением стандарта SQL, реализованным в QHB, так же как и возможность определять триггеры уровне оператора для представлений.

Вариант команды CREATE CONSTRAINT TRIGGER является расширением стандарта SQL, реализованным в QHB.

См. также

ALTER TRIGGER, DROP TRIGGER, CREATE FUNCTION, SET CONSTRAINTS

CREATE TYPE

CREATE TYPE — определить новый тип данных

Синтаксис

CREATE TYPE имя AS
    ( [ имя_атрибута тип_данных [ COLLATE правило_сортировки ] [, ... ] ] )

CREATE TYPE имя AS ENUM
    ( [ 'метка' [, ... ] ] )

CREATE TYPE имя AS RANGE (
    SUBTYPE = подтип
    [ , SUBTYPE_OPCLASS = класс_оператора_подтипа ]
    [ , COLLATION = правило_сортировки ]
    [ , CANONICAL = каноническая_функция ]
    [ , SUBTYPE_DIFF = функция_разницы_подтипа ]
)

CREATE TYPE имя (
    INPUT = функция_ввода,
    OUTPUT = функция_вывода
    [ , RECEIVE = функция_получения ]
    [ , SEND = функция_отправки ]
    [ , TYPMOD_IN = функция_ввода_модификатора_типа ]
    [ , TYPMOD_OUT = функция_вывода_модификатора_типа ]
    [ , ANALYZE = функция_анализа ]
    [ , INTERNALLENGTH = { внутренняя_длина | VARIABLE } ]
    [ , PASSEDBYVALUE ]
    [ , ALIGNMENT = выравнивание ]
    [ , STORAGE = хранение ]
    [ , LIKE = тип_образец ]
    [ , CATEGORY = категория ]
    [ , PREFERRED = предпочитаемый ]
    [ , DEFAULT = по_умолчанию ]
    [ , ELEMENT = элемент ]
    [ , DELIMITER = разделитель ]
    [ , COLLATABLE = сортируемый ]
)

CREATE TYPE имя

Описание

Команда CREATE TYPE регистрирует новый тип данных для использования в текущей базе данных. Пользователь, определивший тип, становится его владельцем.

Если задано имя схемы, то тип будет создан в указанной схеме, в противном случае — в текущей. Имя типа должно отличаться от имени любого существующего типа или домена в той же схеме. (Поскольку таблицы связаны с типами данных, имя типа также должно отличаться от имени любой существующей таблицы в той же схеме.)

Существует пять форм команды CREATE TYPE, как показано выше в блоке синтаксис. Они создают соответственно: составной тип, перечисляемый тип, диапазонный тип, базовый тип или тип-оболочку. Первые четыре из них рассматриваются ниже. Тип-оболочка представляет собой просто заготовку для типа, который будет определен позже; он создается командой CREATE TYPE без параметров, только с указанием имени типа. Типы-оболочки необходимы в качестве прямых ссылок при создании диапазонных и базовых типов, как это описано в соответствующих разделах.

Составные типы

Первая форма CREATE TYPE создает составной тип. Составной тип определяется списком имен атрибутов и типов данных. Для атрибута может быть также указано правило сортировки, если тип данных является сортируемым. Составной тип по существу совпадает с типом строки таблицы, но использование CREATE TYPE позволяет избежать необходимости создавать настоящую таблицу, когда требуется всего лишь определить тип. Отдельный составной тип может быть полезен, например, в качестве аргумента или возвращаемого типа функции.

Чтобы создать составной тип, необходимо иметь право USAGE для всех типов его атрибутов.

Перечислимые типы

Вторая форма CREATE TYPE создает перечисляемый тип, как описано в разделе Перечислимые типы. Перечислимые типы принимают список заключенных в кавычки меток, каждая из которых может иметь длину не больше NAMEDATALEN байт (64 байта в стандартной сборке QHB). (Можно создать перечислимый тип без меток, но такой тип нельзя использовать для хранения значений, пока хотя бы одна метка не будет добавлена с помощью команды ALTER TYPE.)

Диапазонные типы

Третья форма CREATE TYPE создает новый диапазонный тип, как описано в разделе Диапазонные типы.

Подтип диапазонного типа может быть любым типом со связанным классом операторов B-дерева (для определения порядка значений в диапазоне). Обычно для определения порядка используется класс операторов B-дерева по умолчанию; чтобы изменить класс оператора, укажите его имя в параметре класс_операторов_подтипа. Если подтип поддерживает сортировку и вы хотите использовать правила сортировки, отличные от заданных по умолчанию, укажите нужные правила сортировки в параметре правило_сортировки.

Необязательная каноническая_функция должна принимать один аргумент определяемого диапазонного типа и возвращать значение того же типа. Эта настройка используется для преобразования значений диапазона в каноническую форму, когда это применимо. Дополнительную информацию см. в разделе Определение новых диапазонных типов. Создание канонической_функции сложнее, так как она должна быть определена до того, как диапазонный тип может быть объявлен. Для этого необходимо сначала создать тип-оболочку, который является заглушкой, не имеющей никаких свойств, кроме имени и владельца. Это делается путем выполнения команды CREATE TYPE имя без дополнительных параметров. Затем может быть объявлена функция с использованием типа-оболочки в качестве аргумента и результата, и наконец диапазонный тип может быть объявлен с использованием того же имени. Последнее действие автоматически заменяет тип-оболочку допустимым диапазонным типом.

Необязательная функция_разницы_подтипа должна принимать в качестве аргументов два значения типа подтип и возвращать значение double precision, представляющее собой разницу между двумя первыми значениями. Хотя эта функция необязательная, с помощью нее можно кардинально увеличить эффективность индексов GiST для столбцов диапазонного типа. Дополнительную информацию см. в разделе Определение новых диапазонных типов.

Базовые типы

Четвертая форма CREATE TYPE создает новый базовый тип (скалярный тип). Чтобы создать новый базовый тип, нужно быть суперпользователем. (Это ограничение сделано потому, что ошибочное определение типа может вызвать нарушения или даже сбой в работе сервера.)

Параметры могут перечисляться в любом порядке, а не только в том, который показан выше, и большинство из них являются необязательными. Перед определением типа необходимо зарегистрировать две или более функций (с помощью CREATE FUNCTION). Обязательными функциями являются функция_ввода и функция_вывода, а функции функция_получения, функция_отправки, функция_модификатора_типа, функция_вывода_модификатора_типа и функция_анализа можно не указывать. Как правило, эти функции должны быть написаны на C/RUST или другом языке низкого уровня.

Функция_ввода преобразует внешнее текстовое представление типа во внутреннее, используемое операторами и функциями, определенными для данного типа. Функция_вывода выполняет обратное преобразование. Функция ввода может быть объявлена как принимающая один аргумент типа cstring или как принимающая три аргумента с типами cstring, oid, integer. Первый аргумент — это вводимый текст в виде строки в стиле C, второй аргумент — собственный OID типа (за исключением типов массивов, для которых передается OID типа элемента), а третий — это модификатор_типа для целевого столбца, если он известен (и -1, если нет). Функция ввода должна возвращать значение нового типа данных. Обычно функция ввода должна быть объявлена строгой (STRICT); если это не так, при получении на вход значения NULL она будет вызываться с первым параметром NULL. В этом случае функция всё равно должна возвращать значение NULL, иначе будет вызвана ошибка. (Этот случай в основном предназначен для поддержки функций ввода доменных типов, которые не должны принимать значения NULL). Функция вывода должна быть объявлена как принимающая один аргумент нового типа данных, а возвращать должна тип cstring. Функции вывода не вызываются для значений NULL.

Необязательная функция_получения преобразует внешнее двоичное представление типа во внутреннее представление. Если эта функция не указана, тип не может участвовать в двоичном вводе. Двоичное представление должно быть выбрано так, чтобы оно легко преобразовывалось во внутреннюю форму, будучи при этом достаточно переносимым. (Например, для стандартных целочисленных типов данных в качестве внешнего двоичного представления используют сетевой порядок байтов, в то время как внутреннее представление определяется порядком байтов в процессоре.) Функция получения должна выполнять адекватную проверку, чтобы убедиться, что значение является допустимым. Функция получения может быть объявлена как принимающая один аргумент типа internal либо как принимающая три аргумента с типами internal, oid, integer. Первый аргумент — это указатель на буфер StringInfo, содержащий полученную байтовую строку; а остальные аргументы такие же, как и для функции ввода текста. Функция получения должна возвращать значение нового типа данных. Обычно функция получения должна быть объявлена строгой (STRICT); если это не так, при прочтении входного значения NULL, она будет вызываться с первым параметром NULL. В этом случае функция всё равно должна возвращать значение NULL, иначе будет вызвана ошибка. (Этот случай в основном предназначен для поддержки функций ввода доменных типов, которые не должны принимать значения NULL.) Аналогично, необязательная функция_отправки преобразует данные из внутреннего представления во внешнее двоичное представление. Если эта функция не указана, тип не может участвовать в двоичном выводе. Функция отправки должна быть объявлена как принимающая один аргумент нового типа данных, а возвращать тип bytea. Функции отправки не вызываются для значений NULL.

На этом этапе может возникнуть вопрос, как функции ввода и вывода могут быть объявлены принимающими или возвращающими значения нового типа, если они должны быть созданы до того, как новый тип объявлен. Ответ прост: тип сначала должен быть определен как тип-оболочка, который является заготовкой и не имеет никаких свойств, кроме имени и владельца. Он объявляется командой CREATE TYPE имя без дополнительных параметров. Тогда функции ввода/вывода на C/RUST могут быть определены, ссылаясь на этот пустой тип. Затем команда CREATE TYPE с полным определением заменит тип-оболочку на полное, допустимое определение типа, после чего новый тип можно будет использовать как обычно.

Необязательные функции функция_ввода_модификатора_типа и функция_вывода_модификатора_типа необходимы, если тип поддерживает модификаторы, то есть дополнительные ограничения, связанные с объявлением типа, такие как char(5) или numeric(30,2). QHB позволяет создавать типы, которые могут принимать одну или несколько простых констант или идентификаторов в качестве модификаторов. Однако эта информация должна быть упакована в одно неотрицательное целочисленное значение для хранения в системных каталогах. Функция_ввода_модификатора_типа получает объявленный модификатор (или модификаторы) в виде строки cstring. Функция должна проверить значения на допустимость (и вызвать ошибку, если они неверны) и, если они верны, выдать одно неотрицательное значение integer, которое будет сохранено в столбце «typmod». Модификаторы типа не будут приниматься, если функция_ввода_модификатора_типа для типа не указана. Функция_вывода_модификатора_типа преобразует внутреннее целочисленное значение typmod обратно в понятную форму для отображения пользователю. Она должна вернуть значение cstring, которое является строкой для добавления к имени типа; например, функция numeric должна вернуть (30,2). Функция_вывода_модификатора_типа, может быть не указана; в этом случае отображением по умолчанию будет сохраненное целочисленное значение typmod, заключенное в круглые скобки.

Необязательная функция_анализа выполняет сбор специфичной для типа статистики по столбцам с этим типом данных. По умолчанию команда ANALYZE попытается собрать статистику, используя операторы «равно» и «меньше, чем», если для типа определен класс операторов B-дерева по умолчанию. Для нескалярных типов такое поведение в большинстве случаев не подходит, поэтому его можно переопределить, указав собственную функцию анализа. Функция анализа должна быть объявлена как принимающая один аргумент типа internal, и возвращающая результат boolean. Подробно API функций анализа описан в разделе src/include/commands/vacuum.h.

Хотя сведения о внутреннем представлении нового типа известны только функциям ввода-вывода и другим функциям, созданным для работы с типом, существует еще несколько свойств внутреннего представления, которые должны быть объявлены в QHB. Прежде всего, это внутренняя_длина. Если базовый тип данных фиксированной длины, то внутренняя_длина указывается в виде положительного числа, а если тип переменной длины, во внутренней_длине задается значение VARIABLE. (Внутри же typlen принимает значение -1.) Внутреннее представление всех типов переменной длины должно начинаться с 4-байтового целого числа, задающего общую длину значения типа. (Обратите внимание, что поле длины часто кодируется, как описано в разделе TOAST; неразумно обращаться к нему напрямую.)

Необязательный флаг PASSEDBYVALUE указывает, что значения создаваемого типа данных передаются по значению, а не по ссылке. Типы, передаваемые по значению, должны быть фиксированной длины, и их внутреннее представление не может быть больше размера типа Datum (4 байта на одних машинах, 8 байт на других).

Параметр выравнивание указывает, как требуется выравнивать данные этого типа. Допустимые значения выравнивания по границам 1, 2, 4 или 8 байт. Обратите внимание, что типы переменной длины должны иметь выравнивание не менее 4 байт, так как они обязательно содержат своим первым компонентом int4.

Параметр хранение позволяет выбирать стратегии хранения для типов данных переменной длины. (Для типов фиксированной длины допускается только вариант plain.) Вариант plain указывает, что данные этого типа всегда будут храниться внутри и не будут сжаты. Вариант extended указывает, что система сначала попытается сжать большое значение данных, а затем, если оно и после сжатия окажется слишком большим, переместит его из строки основной таблицы. Вариант external позволяет переместить значение из основной таблицы, но система не будет пытаться сжать его. Вариант main разрешает сжатие, но препятствует перемещению значения из основной таблицы. (Элементы данных с этим вариантом хранения всё равно могут быть перемещены из основной таблицы, если нет другого способа уместить их в строке, но предпочтение будет отдано сохранению в основной таблице, в отличие от вариантов extended и external.)

Все значения параметра хранение, кроме plain, подразумевают, что функции типа данных могут принимать значения в формате toast, как описано в разделах TOAST и Использование TOAST. Такое значение просто определяет вариант хранения TOAST по умолчанию для столбцов отделяемого в TOAST типа данных; другие варианты для отдельных столбцов можно выбирать с помощью команды ALTER TABLE SET STORAGE.

Параметр тип_образец предоставляет альтернативный метод для указания основных свойств представления типа данных: скопировать их из другого существующего типа. Из указанного типа будут скопированы значения свойств внутренняя_длина, передача_по_значению, выравнивание и хранение. (Можно, хотя обычно это нежелательно, переопределить некоторые из этих значений, указав их вместе с предложением LIKE) Указывать представление типа таким образом особенно удобно при низкоуровневой реализации нового типа, которая некоторым образом «опирается» на существующий тип.

Параметры категория и предпочитаемый могут использоваться для поддержки в управлении тем, какое неявное приведение будет применяться в неоднозначных ситуациях. Каждый тип данных относится к категории, обозначаемой одним символом ASCII, и каждый тип в этой категории является либо «предпочтительным» либо нет. Анализатор запроса постарается выбрать приведение к предпочтительному типу (но только из других типов в той же категории); это правило полезно при разрешении перегруженных функций или операторов. Более подробную информацию см. в главе PL/pgSQL. Для типов, которые не имеют неявных приведений к какому-либо другому типу или из них, достаточно оставить значения по умолчанию. Однако для группы связанных типов, которые имеют неявные приведения, часто бывает полезно пометить их все как принадлежащие к категории и выбрать один или два «наиболее общих» типа как предпочтительные в категории. Параметр категория особенно полезен при добавлении пользовательского типа в существующую встроенную категорию, например, числовые или строковые типы. Однако также можно создавать новые, полностью пользовательские категории типов. В качестве имени такой категории можно выбрать любой ASCII-символ, кроме латинской заглавной буквы.

При желании можно назначить столбцам с этим типом данных значение по умолчанию, отличное от NULL, указав его после ключевого слова DEFAULT в этой команде. (Такое значение по умолчанию может быть переопределено явным предложением DEFAULT, добавленным при создании определенного столбца.)

Чтобы указать, что тип является массивом, задайте тип элементов массива с помощью ключевого слова ELEMENT. Например, чтобы определить массив 4-байтовых целых чисел (int4), задайте ELEMENT = int4. Ниже приведены дополнительные сведения о типах массивов.

Для указания разделителя, который будет использоваться между значениями во внешнем представлении массива с элементами этого типа, можно задать параметру delimiter определенный символ. Разделителем по умолчанию является запятая (,). Обратите внимание, что разделитель связан с типом элементов массива, а не с самим типом массива.

Если у необязательного логического параметра сортируемый установлено значение true, определения столбцов и выражения этого типа могут содержать информацию о правилах сортировки с помощью предложения COLLATE. Получение фактической пользы от этих правил зависит от реализаций функций, работающих с типом; просто от того, что тип помечен как сортируемый, эти правила действовать не будут.

Типы массивов

Каждый раз при создании пользовательского типа QHB автоматически создает связанный тип массива, имя которого состоит из имени типа элемента и знака подчеркивания перед ним. Полученное имя усекается, если это его длина превышает NAMEDATALEN байт. (Если имя, созданное таким образом, конфликтует с существующим именем типа, процесс повторяется до тех пор, пока не будет найдено непротиворечивое имя.) Этот неявно созданный тип массива имеет переменную длину и использует встроенные функции ввода и вывода array_in и array_out. Тип массива отражает любые изменения владельца или схемы его типа элемента и удаляется, если удаляется тип элемента.

Вы можете задать вопрос, зачем существует параметр ELEMENT, если система создает правильный тип массива автоматически. Единственный случай, когда полезно использовать ELEMENT, это когда вы создаете тип фиксированной длины, который оказывается массивом нескольких одинаковых элементов, и вы хотите, чтобы эти элементы были доступны при обращению по индексу, в дополнение к любым операциям, которые вы планируете реализовать для типа в целом. Например, тип point представляется в виде двух чисел с плавающей запятой, доступ к которым можно получить с помощью point[0] и point[1]. Обратите внимание, что эта функция работает только для типов фиксированной длины, внутренняя форма которых представляет собой точную последовательность одинаковых полей фиксированной длины. Тип массива переменной длины должен иметь обобщенное внутреннее представление, с которым умеют работать array_in и array_out. По историческим причинам (т.е. это определенно некорректно, но слишком поздно это менять), индексы в массивах фиксированной длины начинаются с нуля, а не с единицы, как для массивов переменной длины.

Параметры

имя

Имя создаваемого типа (может быть дополнено схемой).

имя_атрибута

Имя атрибута (столбца) для составного типа.

тип_данных

Имя существующего типа данных, который должен стать столбцом составного типа.

правило_сортировки

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

метка

Строковый литерал, представляющий текстовую метку, связанную с одним значением перечисляемого типа.

подтип

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

класс_оператора_подтипа

Имя класса операторов B-дерева для подтипа.

каноническая_функция

Имя функции канонизации для диапазонного типа.

функция_разницы_подтипа

Имя функции разницы для данного подтипа.

функция_ввода

Имя функции, преобразующей данные из внешней текстовой формы (представления) типа в его внутреннюю форму.

функция_вывода

Имя функции, преобразующей данные из внутренней формы (представления) типа во внешнюю текстовую форму.

функция_получения

Имя функции, преобразующей данные из внешней двоичной формы (представления) типа в его внутреннюю форму.

функция_отправки

Имя функции, преобразующей данные из внутренней формы типа во внешнюю двоичную форму типа.

функция_ввода_модификатора_типа

Имя функции, преобразующей массив модификаторов для данного типа во внутреннюю форму.

функция_вывода_модификатора_типа

Имя функции, преобразующей внутреннюю форму модификатора (или модификаторов) типа во внешнюю текстовую форму.

функция_анализа

Имя функции, выполняющей статистический анализ для данного типа данных.

внутренняя_длина

Числовая константа, указывающая длину внутреннего представления нового типа в байтах. По умолчанию предполагается, что тип имеет переменную длину.

выравнивание

Требуемое выравнивание для типа данных. Допустимые значения: char, int2, int4 или double; значение по умолчанию равно int4.

хранение

Стратегия хранения для данного типа данных. Допустимые значения: plain, external, extended или main; значение по умолчанию равно plain.

тип_образец

Имя существующего типа данных, от которого получит свойства представления новый тип. Будут скопированы значения следующих параметров этого типа: внутренняя_длина, передача_по_значению, выравнивание и хранение, если только они не будут переопределены явными указаниями в команде CREATE TYPE.

категория

Код категории (один символ ASCII) для этого типа. Значение по умолчанию: 'U', что означает «пользовательский тип» (user-defined). Другие коды категорий можно найти в разделе Таблица кодов категорий. Вы также можете выбрать другие символы ASCII для создания пользовательских категорий.

предпочитаемый

Если значение равно true, то этот тип является предпочтительным типом в своей категории типов. Значение по умолчанию равно false. Будьте очень осторожны при создании нового предпочтительного типа в рамках существующей категории типов, так как это может вызвать неожиданные изменения в поведении.

по_умолчанию

Значение по умолчанию для данного типа данных. Если этот параметр не задан, будет использовано значение по умолчанию, равное NULL.

элемент

Указывает что создаваемый тип является массивом; это параметр определяет тип элементов массива.

разделитель

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

сортируемый

Если значение равно true, в операциях с этим типом может учитываться информация о параметрах сортировки. Значение по умолчанию равно false.

Примечания

Поскольку нет никаких ограничений на использование типа данных после его создания, создание базового или диапазонного типа равносильно предоставлению права на выполнение функций, упомянутых в определении типа. Это обычно не является проблемой для тех функций, которые используются в определении типа. Но стоит подумать дважды, прежде чем создавать тип, преобразование которого во внешнюю форму и обратно потребует использования «секретной» информации.

Рекомендуется избегать использования имен типов и таблиц, начинающихся с символа подчеркивания(_). Хотя сервер сможет сгенерировать другое имя, чтобы избежать конфликтов с пользовательскими именами, по-прежнему существует риск возникновения путаницы, особенно со старыми клиентскими приложениями, которые могут предположить, что имена типов, начинающиеся с подчеркивания, относится к типу массива.

Примеры

В этом примере создается составной тип, который затем используется в определении функции:

CREATE TYPE compfoo AS (f1 int, f2 text);

CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS $$
    SELECT fooid, fooname FROM foo
$$ LANGUAGE SQL;

В этом примере создается перечисляемый тип, который затем используется в определении таблицы:

CREATE TYPE bug_status AS ENUM ('new', 'open', 'closed');

CREATE TABLE bug (
    id serial,
    description text,
    status bug_status
);

В этом примере создается диапазонный тип:

CREATE TYPE float8_range AS RANGE (subtype = float8, subtype_diff = float8mi);

В этом примере создается базовый тип данных box, который затем используется в определении таблицы:

CREATE TYPE box;

CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ;
CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ;

CREATE TYPE box (
    INTERNALLENGTH = 16,
    INPUT = my_box_in_function,
    OUTPUT = my_box_out_function
);

CREATE TABLE myboxes (
    id integer,
    description box
);

Если бы внутренняя структура box была массивом из четырех элементов float4, можно было бы вместо этого использовать:

CREATE TYPE box (
    INTERNALLENGTH = 16,
    INPUT = my_box_in_function,
    OUTPUT = my_box_out_function,
    ELEMENT = float4
);

В этом случае к числам, составляющим значение этого типа, можно было бы обращаться по индексу. В остальном поведение этого типа будет таким же.

В этом примере создается тип большого объекта и используется в определении таблицы:

CREATE TYPE bigobj (
    INPUT = lo_filein, OUTPUT = lo_fileout,
    INTERNALLENGTH = VARIABLE
);
CREATE TABLE big_objs (
    id integer,
    obj bigobj
);

Дополнительные примеры, включая соответствующие функции ввода и вывода, приведены в разделе Пользовательские типы.

Совместимость

Первая форма команды CREATE TYPE, которая создает составной тип, соответствует стандарту SQL. Другие формы являются расширением QHB. Команда CREATE TYPE в стандарте SQL также имеет другие формы, которые не реализованы в QHB.

Возможность создания составного типа без атрибутов является специфическим для QHB отклонением от стандарта (аналогично CREATE TABLE).

См. также

ALTER TYPE, CREATE DOMAIN, CREATE FUNCTION, DROP TYPE

CREATE USER MAPPING

CREATE USER MAPPING — определить новое сопоставление пользователя для стороннего сервера

Синтаксис

CREATE USER MAPPING [ IF NOT EXISTS ] FOR { имя_пользователя | USER | CURRENT_USER | PUBLIC }
    SERVER имя_сервера
    [ OPTIONS ( параметр 'значение' [ , ... ] ) ]

Описание

Команда CREATE USER MAPPING определяет сопоставление пользователя на стороннем сервере. Пользовательское сопоставление обычно содержит информацию о соединении, которую обертка сторонних данных будет использовать вместе с информацией, инкапсулированной сторонним сервером, для доступа к внешнему ресурсу данных.

Владелец стороннего сервера может создавать сопоставления для любых пользователей на этом сервере. Кроме того, пользователь может создавать сопоставление пользователей для своего собственного имени, если у него есть право USAGE на данном сервере.

Параметры

IF NOT EXISTS

Не выдавать ошибку, если сопоставление данного пользователя с данным сторонним сервером уже существует. В этом случае выдается соответствующее уведомление. Обратите внимание, что нет никакой гарантии, что существующее пользовательское сопоставление как-то соотносится с тем, которое было бы создано.

имя_пользователя

Имя существующего пользователя, для которого создается сопоставление на стороннем сервере. Ключевые слова CURRENT_USER и USER обозначают имя текущего пользователя. Если указывается PUBLIC, то создается так называемое общедоступное сопоставление, которое используется, когда не применяется никакое пользовательское сопоставление.

имя_сервера

Имя существующего сервера, для которого создается сопоставление пользователя.

OPTIONS ( параметр 'значение' [, ... ] )

В этом предложении задаются параметры сопоставления. Эти параметры обычно определяют фактическое имя и пароль пользователя на целевом сервере. Имена параметров должны быть уникальными. Набор допустимых имен и значений параметров определяется оберткой сторонних данных внешнего сервера.

Примеры

Создание пользовательского сопоставления для пользователя bob, сервер foo:

CREATE USER MAPPING FOR bob SERVER foo OPTIONS (user 'bob', password 'secret');

Совместимость

Команда CREATE USER MAPPING соответствует стандарту ISO/IEC 9075-9 (SQL/MED).

См. также

ALTER USER MAPPING, DROP USER MAPPING, CREATE FOREIGN DATA WRAPPER, CREATE SERVER

CREATE USER

CREATE USER — определить новую роль в базе данных

Синтаксис

CREATE USER имя [ [ WITH ] параметр [ ... ] ]

Где параметр может быть:

      SUPERUSER | NOSUPERUSER
    | CREATEDB | NOCREATEDB
    | CREATEROLE | NOCREATEROLE
    | INHERIT | NOINHERIT
    | LOGIN | NOLOGIN
    | REPLICATION | NOREPLICATION
    | BYPASSRLS | NOBYPASSRLS
    | CONNECTION LIMIT предел_подключений
    | [ ENCRYPTED ] PASSWORD 'пароль' | PASSWORD NULL
    | VALID UNTIL 'дата_время'
    | IN ROLE имя_роли [, ...]
    | IN GROUP имя_роли [, ...]
    | ROLE имя_роли [, ...]
    | ADMIN имя_роли [, ...]
    | USER имя_роли [, ...]
    | SYSID uid

Описание

Сейчас команда CREATE USER является псевдонимом для CREATE ROLE. Единственное отличие заключается в том, что в команде CREATE USER по умолчанию подразумевается параметр LOGIN, в то время как в команде CREATE ROLE по умолчанию подразумевается параметр NOLOGIN.

Совместимость

Команда CREATE USER является расширением QHB. Стандарт SQL оставляет определение пользователей на усмотрение реализации.

См. также

CREATE ROLE

CREATE VIEW

CREATE VIEW — определить новое представление

Синтаксис

CREATE [ OR REPLACE ] [ TEMP | TEMPORARY ] [ RECURSIVE ] VIEW имя [ ( имя_столбца [, ...] ) ]
    [ WITH ( имя_параметра_представления [= значение_параметра_представления] [, ... ] ) ]
    AS запрос
    [ WITH [ CASCADED | LOCAL ] CHECK OPTION ]

Описание

Команда CREATE VIEW определяет представление запроса. Так как это представление физически не материализуется, то указанный запрос будет выполняться при каждом обращении к представлению.

CREATE OR REPLACE VIEW действует аналогичным образом, но если представление с тем же именем уже существует, оно будет заменено. Новый запрос должен выдавать те же столбцы, что и существующий запрос представления (то есть те же имена столбцов в том же порядке и с теми же типами данных), но также он может добавлять дополнительные столбцы в конец списка. Вычисления, в результате которых формируются столбцы представления, могут быть совершенно другими.

Если задано имя схемы (см. раздел Схемы) (например, CREATE VIEW myschema.myview ...), то представление создается в указанной схеме, в противном случае — в текущей. Временные представления существуют в специальной схеме, поэтому при создании таких представлений имя схемы задать нельзя. Имя представления должно отличаться от имен других представлений, таблиц, последовательностей, индексов или сторонних таблиц в этой схеме.

Параметры

TEMPORARY или TEMP

Если этот параметр указан, то представление создается как временное представление. Временные представления автоматически удаляются в конце сеанса. Существующее постоянное представление с тем же именем не будет видно в текущем сеансе, пока существует временное, однако к нему можно обратиться, дополнив имя указанием схемы.

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

RECURSIVE

Создает рекурсивное представление. Синтаксис

CREATE RECURSIVE VIEW [ схема . ] имя_представления (имена_столбцов) AS SELECT ...;

равнозначен

CREATE VIEW [ схема . ] имя_представления AS WITH RECURSIVE имя_представления (имена_столбцов) AS (SELECT ...) SELECT имена_столбцов FROM имя_представления;

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

имя

Имя создаваемого представления (может быть дополнено схемой).

имя_столбца

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

WITH ( имя_параметра_представления [= значение_параметра_представления] [, ... ] )

В этом предложении указываются необязательные параметры для представления. Поддерживаются следующие параметры:

check_option (enum)

Этот параметр может принимать значение local (локально) или cascaded (каскадно) и равнозначен указанию WITH [ CASCADED | LOCAL ] CHECK OPTION (см. ниже). На существующих представлениях этот параметр можно изменить с помощью команды ALTER VIEW.

security_barrier (boolean)

Этот параметр следует использовать, если представление должно обеспечивать защиту на уровне строк. Дополнительную информацию см. в разделе Политики безопасности строк.

запрос

Команда SELECT или VALUES, которая предоставляет столбцы и строки представления.

WITH [ CASCADED | LOCAL ] CHECK OPTION

Этот параметр управляет поведением автоматически обновляемых представлений. Если этот параметр указан, то команды INSERT и UPDATE в представлении будут проверяться на предмет того, удовлетворяют ли новые строки условию, определяющему представление (то есть будет выполнена проверка новых строк на видимость через это представление). Если строки не видны, обновление будет отклонено. Если параметр CHECK OPTION не указан, то командам INSERT и UPDATE в представлении разрешается создавать строки, которые не будут видны через это представление. Поддерживаются следующие варианты проверки:

LOCAL

Новые строки проверяются только на соответствие условиям, определенным непосредственно в самом представлении. Любые условия, определенные на нижележащих базовых представлениях, не проверяются (если только в них тоже нет указания CHECK OPTION).

CASCADED

Новые строки проверяются на соответствие условиям представления и всех нижележащих базовых представлений. Если указано CHECK OPTION, а *LOCAL и CASCADED опущены, то предполагается значение CASCADED.

Не допускается использование CHECK OPTION с представлениями RECURSIVE.

Обратите внимание, что CHECK OPTION поддерживается только для автоматически изменяемых представлений, не имеющих триггеров INSTEAD OF или правил INSTEAD. Если автоматически изменяемое представление определено поверх базового представления с триггерами INSTEAD OF, то параметр LOCAL CHECK OPTION можно использовать для проверки ограничений автоматически изменяемого представления, но условия базового представления с триггером INSTEAD OF проверяться не будут (каскадная опция проверки не будет спускаться ниже к представлению, модифицируемому триггером, и любые параметры проверки, определенные напрямую для такого представления, будут игнорироваться). Если для представления или любого из его базовых отношений определено правило INSTEAD (которое приводит к перезаписи команды INSERT или UPDATE), то все параметры проверки в перезаписанном запросе будут проигнорированы, в том числе проверки из автоматически обновляемых представлений, определенных поверх отношения с правилом INSTEAD.

Примечания

Для удаления представлений применяется команда DROP VIEW.

Позаботьтесь о том, чтобы столбцы представления получили желаемые имена и типы. Например, такая команда:

CREATE VIEW vista AS SELECT 'Hello World';

плоха тем, что по умолчанию именем столбца будет ?column?, а типом данных — text, а это может быть совсем не тем, что вы хотели. Лучше записывать строковую константу в результате представления примерно так:

CREATE VIEW vista AS SELECT text 'Hello World' AS hello;

Доступ к таблицам, задействованным в представлении, определяется правами владельца представления. В некоторых случаях это позволяет организовать безопасный, но ограниченный доступ к нижележащим таблицам. Однако учтите, что не все представления могут быть защищенными (подробную информацию см. в разделе Система правил QHB). Функции, вызываемые в представлении, выполняются так, будто они вызываются непосредственно из запроса, обращающегося к представлению. Поэтому пользователь представления должен иметь права, необходимые для вызова всех функций, задействованных в представлении.

При выполнении CREATE OR REPLACE VIEW для существующего представления изменяется только правило SELECT, определяющее представление. Другие свойства представления, включая владельца, права и правила (кроме SELECT), остаются неизменными. Чтобы изменить определение представления, необходимо быть его владельцем (или членом роли-владельца).

Изменяемые представления

Простые представления становятся изменяемыми автоматически: система позволит выполнять команды INSERT, UPDATE и DELETE с таким представлением так же, как и с обычной таблицей. Представление будет автоматически изменяемым, если оно удовлетворяет одновременно всем следующим условиям:

Список *FROM в запросе, определяющем представление, должен содержать ровно один элемент, и это должна быть таблица или другое изменяемое представление.

Определение представления не должно содержать WITH, DISTINCT, GROUP BY, HAVING, LIMIT и OFFSET BY на верхнем уровне запроса.

Определение представления не должно содержать операций с множествами (UNION, INTERSECT или EXCEPT) на верхнем уровне запроса.

Список выборки в запросе не должен содержать агрегатные и оконные функции, а также функции, возвращающие множества.

Автоматически обновляемое представление может содержать как изменяемые, так и неизменяемые столбцы. Столбец является изменяемым, если это простая ссылка на изменяемый столбец нижележащего базового отношения; в противном случае этот столбец будет доступен только для чтения, и если команда INSERT или UPDATE попытается записать в него значение, то возникнет ошибка.

Если представление автоматически изменяемое, то система будет преобразовывать обращающиеся к нему команды INSERT, UPDATE и DELETE в соответствующие операторы, обращающиеся к нижележащему базовому отношению. При этом в полной мере поддерживаются команды INSERT с параметром ON CONFLICT UPDATE.

Если автоматически изменяемое представление содержит условие WHERE, то это условие ограничивает набор строк базового отношения, которые могут быть изменены командой UPDATE и удалены командой DELETE в этом представлении. Однако UPDATE может изменить строку так, что она перестанет соответствовать условию WHERE и, как следствие, больше не будет видна через представление. Команда INSERT подобным образом может добавить в базовое отношение строки, которые не удовлетворят условию WHERE и поэтому не будут видны через представление (ON CONFLICT UPDATE может подобным образом воздействовать на существующую строку, не видимую через представление). Чтобы предотвратить создание подобных строк командами INSERT и UPDATE, можно воспользоваться указанием CHECK OPTION.

Если автоматически изменяемое представление имеет свойство security_barrier (барьер безопасности), то все условия WHERE этого представления (и все условия с герметичными операторами (LEAKPROOF)) будут всегда вычисляться перед условиями, добавленными пользователем представления. Подробную информацию см. в разделе Система правил QHB. Обратите внимание, что по этой причине строки, которые в итоге не были возвращены (потому что не прошли проверку в пользовательском условии WHERE), могут всё же остаться заблокированными. Чтобы определить, какие условия применяются на уровне отношения (и, как следствие, избавляют часть строк от блокировки), можно воспользоваться командой EXPLAIN.

Более сложные представления, не удовлетворяющие этим условиям, по умолчанию доступны только для чтения: система не позволит выполнить операции добавления, изменения или удаления строк в таком представлении. Создать эффект изменяемого представления для них можно, определив триггеры INSTEAD OF, которые будут преобразовывать запросы на изменение данных в соответствующие действия с другими таблицами. Подробную информацию см. в разделе CREATE TRIGGER. Также есть возможность создавать правила (см. CREATE RULE), но на практике триггеры проще для понимания и применения.

Учтите, что пользователь, выполняющий операции добавления, изменения или удаления данных в представлении, должен иметь соответствующие права для этого представления. Кроме того, владелец представления должен иметь сопутствующие права в нижележащих базовых отношениях, хотя пользователь, выполняющий эти операции, может таких прав не иметь (см. Система правил QHB).

Примеры

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

CREATE VIEW comedies AS
    SELECT *
    FROM films
    WHERE kind = 'Comedy';

Эта команда создаст представление со столбцами, которые содержались в таблице film в момент выполнения команды. Хотя при создании представления было указано *, столбцы, добавляемые в таблицу позже, не будут являться частью представления.

Создание представления с указанием LOCAL CHECK OPTION:

CREATE VIEW universal_comedies AS
    SELECT *
    FROM comedies
    WHERE classification = 'U'
    WITH LOCAL CHECK OPTION;

Эта команда создаст представление на базе представления comedies, выдающее только комедии (kind = 'Comedy') универсальной возрастной категории classification = 'U'. Любая попытка выполнить в представлении INSERT или UPDATE со строкой, не удовлетворяющей условию classification = 'U', будет отвергнута, но ограничение по полю kind (тип фильма) проверяться не будет.

Создание представления с указанием CASCADED CHECK OPTION:

CREATE VIEW pg_comedies AS
    SELECT *
    FROM comedies
    WHERE classification = 'PG'
    WITH CASCADED CHECK OPTION;

Это представление будет проверять, удовлетворяют ли новые строки обоим условиям: по столбцу kind и по столбцу classification.

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

CREATE VIEW comedies AS
    SELECT f.*,
           country_code_to_name(f.country_code) AS country,
           (SELECT avg(r.rating)
            FROM user_ratings r
            WHERE r.film_id = f.id) AS avg_rating
    FROM films f
    WHERE f.kind = 'Comedy';

Это представление будет поддерживать операции INSERT, UPDATE и DELETE. Изменяемыми будут все столбцы из таблицы films, тогда как вычисляемые столбцы country и avg_rating будут доступны только для чтения.

Создание рекурсивного представления, состоящего из чисел от 1 до 100:

CREATE RECURSIVE VIEW public.nums_1_100 (n) AS
    VALUES (1)
UNION ALL
    SELECT n+1 FROM nums_1_100 WHERE n < 100;

Обратите внимание, что несмотря на то, что имя рекурсивного представления дополнено схемой в этой команде CREATE, внутренняя ссылка представления на себя же схемой не дополняется. Это связано с тем, что имя неявно создаваемого общего табличного выражения (ОТВ) не может дополняться схемой.

Совместимость

Команда CREATE OR REPLACE VIEW является языковым расширением QHB. Также расширением является предложение WITH ( ... ) и концепция временного представления.

См. также

ALTER VIEW, DROP VIEW, CREATE MATERIALIZED VIEW

DEALLOCATE

DEALLOCATE — освободить подготовленный оператор

Синтаксис

DEALLOCATE [ PREPARE ] { имя | ALL }

Описание

Команда DEALLOCATE используется для освобождения заранее подготовленного оператора SQL. Если не освободить подготовленный оператор явно, он освобождается по окончании сеанса.

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

Параметры

PREPARE

Это ключевое слово игнорируется.

имя

Имя подготовленного оператора для освобождения.

ALL

Освобождает все подготовленные операторы.

Совместимость

Стандарт SQL включает в себя команду DEALLOCATE, но она предназначена только для использования во встраиваемом SQL.

См. также

EXECUTE, PREPARE

DECLARE

DECLARE — определить курсор

Синтаксис

DECLARE имя [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ]
    CURSOR [ { WITH | WITHOUT } HOLD ] FOR запрос

Описание

Команда DECLARE позволяет пользователю создавать курсоры, которые могут использоваться для извлечения небольшого количества строк за один раз из большего запроса. После создания курсора строки извлекаются из него с помощью команды FETCH.

Примечание
Эта страница описывает использование курсоров на уровне команд SQL. Если вы пытаетесь использовать курсоры внутри функции PL/pgSQL, правила будут другими — см. раздел Структура PL/pgSQL.

Параметры

имя

Имя создаваемого курсора.

BINARY

Курсор будет возвращать данные в двоичном, а не в текстовом формате.

INSENSITIVE

Указывает, что данные, извлеченные из курсора, не должны быть затронуты обновлениями таблицы (или таблиц), лежащей в основе курсора, которые происходят после его создания. В QHB это поведение по умолчанию, поэтому данное ключевое слово не имеет никакого эффекта и принимается только для совместимости со стандартом SQL.

SCROLL
NO SCROLL

SCROLL указывает, что курсор можно использовать для получения строк непоследовательно (например, в обратном порядке). В зависимости от сложности плана выполнения запроса, указание SCROLL может привести к снижению производительности во время выполнения запроса. NO SCROLL указывает, что курсор не может быть использован для получения строк неупорядоченным образом. По умолчанию прокрутка в некоторых случаях разрешается, но это не равнозначно эффекту указания SCROLL. Дополнительную информацию см. в Примечаниях ниже.

WITH HOLD
WITHOUT HOLD

WITH HOLD указывает, что курсор можно продолжать использовать после успешной фиксации транзакции, которая его создала. WITHOUT HOLD указывает, что курсор не может использоваться вне транзакции, которая его создала. Если не указано ни WITHOUT HOLD, ни WITH HOLD, значением по умолчанию является WITHOUT HOLD.

запрос

Команды SELECT или VALUES, которые предоставляют строки, возвращаемые курсором.

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

Примечания

Обычные курсоры возвращают данные в текстовом формате, в каком их выдает SELECT. Опция BINARY указывает, что курсор должен возвращать данные в двоичном формате. Это упрощает операции преобразования данных для сервера и клиента за счет дополнительных усилий программиста по работе с зависимыми от платформы двоичными форматами данных. Например, если запрос возвращает значение единица из целочисленного столбца, с курсором по умолчанию вы получите строку, содержащую 1, тогда как с двоичным курсором вы получите 4-байтовое поле, содержащее внутреннее представление значения (с сетевым порядком байтов).

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

Примечание
Когда клиентское приложение использует протокол «расширенный запрос» для выполнения команды FETCH, сообщение протокола Bind указывает, следует ли извлекать данные в текстовом или двоичном формате. Этот выбор переопределяет свойство курсора, заданное в его определении. Таким образом, понятие двоичного курсора как такового устарело при использовании расширенного протокола запросов — любой курсор может рассматриваться как текстовый или двоичный.

Если в команде объявления курсора не указано WITH HOLD, созданный этой командой курсор может использоваться только в рамках текущей транзакции. Таким образом, оператор DECLARE без WITH HOLD бесполезен вне блока транзакций: курсор сохранится только до завершения оператора. Поэтому QHB сообщает об ошибке, если такая команда используется вне блока транзакций. Для определения блока транзакций используйте команды BEGIN и COMMIT (или ROLLBACK).

Если в объявлении курсора WITH HOLD указывается и транзакция, создавшая курсор, успешно фиксируется, курсор может и далее быть доступным для последующих транзакций в том же сеансе. (Но если создание транзакции прерывается, курсор удаляется.) Курсор со свойством WITH HOLD закрывается, когда явно задается команда CLOSE или по завершении сеанса. В текущей реализации строки, представленные удерживаемым курсором, копируются во временный файл или область памяти, чтобы оставаться доступными для последующих транзакций.

Свойство WITH HOLD можно не указывать, если запрос включает в себя указания FOR UPDATE или FOR SHARE.

Опция SCROLL должна быть указана при определении курсора, который будет использоваться для выборки данных в обратном порядке. Это требуется стандартом SQL. Однако для совместимости с более ранними версиями в QHB разрешается выборка в обратном направлении и без указания SCROLL*, если план запроса курсора достаточно прост и для его поддержки не требуются никакие дополнительные накладные расходы. Однако разработчикам приложений рекомендуется не полагаться на использование выборки в обратном порядке из курсора, который не был создан с помощью SCROLL. С указанием NO SCROLL прокрутка назад запрещается в любом случае.

Выборка в обратном направлении также запрещается, если запрос включает в себя указания FOR UPDATE и FOR SHARE; в этом случае указание SCROLL не принимается.

Внимание!!!
Прокручиваемые и удерживаемые (WITH HOLD) курсоры могут давать неожиданные результаты, если они вызывают любые изменчивые функции (см. раздел Категории изменчивости функций). Когда ранее извлеченная строка выбирается повторно, функции также могут выполниться повторно, возможно, приведя к результатам, отличным от полученных в первый раз. Одним из обходных путей для таких случаев является объявление курсора с указанием WITH HOLD и фиксация транзакции перед чтением любых строк из него. В этом случае весь набор данных курсора будет материализован во временном хранилище, так что изменчивые функции будут выполнены для каждой строки лишь единожды.

Если запрос в определении курсора включает указания FOR UPDATE или FOR SHARE, возвращаемые курсором строки блокируются в момент первой выборки, аналогично происходящему при выполнении обычной команды SELECT с этими указаниями. Кроме того, возвращаемые строки будут самыми актуальными версиями; поэтому эти параметры обеспечивают эквивалент того, что стандарт SQL называет «чувствительным курсором». (Указание INSENSITIVE для курсора с запросом FOR UPDATE или FOR SHARE является ошибкой.)

Внимание!!!
Обычно рекомендуется использовать FOR UPDATE, если курсор предназначен для использования в командах UPDATE ... WHERE CURRENT OF и DELETE ... WHERE CURRENT OF. Указание FOR UPDATE запрещает другим сеансам изменять строки между моментом их извлечения и моментом их обновления. Без указания FOR UPDATE последующая команда с WHERE CURRENT OF не сработает, если строка была изменена с момента создания курсора.
Еще одна причина использовать FOR UPDATE в том, что без него при выполнении последующих команд с WHERE CURRENT OF может произойти сбой, если запрос курсора не удовлетворяет оговоренному в стандарте SQL критерию «простой изменяемости» (в частности, курсор должен ссылаться только на одну таблицу и не должен использовать группировку и сортировку (ORDER BY).) Курсоры, не удовлетворяющие этому критерию, могут работать либо не работать, в зависимости от конкретного выбранного плана; так что в худшем случае приложение может работать в тестовой, но сломается в производственной среде. С указанием FOR UPDATE курсор гарантированно будет изменяемым.
Главная причина не использовать FOR UPDATE вместе с WHERE CURRENT OF возникает, если требуется получить прокручиваемый курсор или курсор, не отражающий последующие изменения (то есть продолжающий показывать старые данные). Если это действительно необходимо, обязательно учтите при реализации приведенные выше замечания.

Стандарт SQL содержит только положения для курсоров во встраиваемом SQL. Сервер QHB не реализует инструкцию OPEN для курсоров; курсор считается открытым при его объявлении. Однако ECPG, встроенный препроцессор SQL для QHB, поддерживает стандартные соглашения о курсорах SQL, в том числе включающие инструкции DECLARE и OPEN. Вы можете просмотреть все доступные курсоры, запросив системное представление pg_cursors.

Примеры

Чтобы объявить курсор:

DECLARE liahona CURSOR FOR SELECT * FROM films;

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

Совместимость

Стандарт SQL говорит, что чувствительность курсоров к параллельному обновлению нижележащих данных по умолчанию определяется реализацией. В QHB курсоры по умолчанию нечувствительны и могут быть сделаны чувствительными путем указания FOR UPDATE. Другие СУБД могут работать по-другому.

Стандарт SQL позволяет использовать курсоры только во встраиваемом SQL и в модулях. QHB позволяет использовать курсоры в интерактивном режиме.

Бинарные курсоры являются расширением QHB.

См. также

CLOSE, FETCH, MOVE

DELETE

DELETE — удалить строки таблицы

Синтаксис

[ WITH [ RECURSIVE ] запрос_WITH [, ...] ]
DELETE FROM [ ONLY ] имя_таблицы [ * ] [ [ AS ] псевдоним ]
    [ USING элемент_FROM [, ...] ]
    [ WHERE условие | WHERE CURRENT OF имя_курсора ]
    [ RETURNING * | выражение_результата [ [ AS ] имя_результата ] [, ...] ]

Описание

Команда DELETE удаляет строки из указанной таблицы, удовлетворяющие требованиям предложения WHERE. Если предложение WHERE отсутствует, результат будет заключаться в удалении всех строк таблицы. Итогом станет действительная, но пустая таблица.

Совет
Команда TRUNCATE обеспечивает более быстрый механизм для удаления всех строк из таблицы.

Существует два способа удаления строк в таблице с помощью информации, содержащейся в других таблицах базы данных: с помощью подзапросов или путем указания дополнительных таблиц в предложении USING. Какой метод является более подходящим, зависит от конкретных обстоятельств.

Необязательное предложение RETURNING указывает, что команда DELETE должна вычислить и возвратить значение (или значения) для каждой фактически удаленной строки. Вычислить в нем можно любое выражение со столбцами целевой таблицы и/или столбцами других таблиц, упомянутых в USING. Список RETURNING имеет тот же синтаксис, что и список результатов SELECT.

Чтобы удалять данные из таблицы, необходимо иметь право DELETE для нее, а также право SELECT для всех таблиц, перечисленных в предложении USING, и таблиц, данные которых считываются в условии.

Параметры

запрос_WITH

Предложение WITH позволяет указать один или несколько вложенных запросов, на которые можно ссылаться по имени в запросе на удаление. Дополнительную информацию см. в разделах Запросы WITH и SELECT.

имя_таблицы

Имя таблицы, из которой будут удалены строки (может быть дополнено схемой). Если перед именем таблицы указывается ONLY, совпадающие строки удаляются только из указанной таблицы. Если ONLY не указано, совпадающие строки также удаляются из всех таблиц, наследуемых от указанной таблицы. После имени таблицы можно добавить необязательное указание *, чтобы явно обозначить, что операция затрагивает все дочерние таблицы.

псевдоним

Альтернативное имя целевой таблицы. Когда предоставляется псевдоним, он полностью скрывает фактическое имя таблицы. Например, в запросе DELETE FROM foo AS f остальная часть инструкции DELETE должна ссылаться на эту таблицу по имени f, а не foo.

элемент_FROM

Табличное выражение, позволяющее добавить в условие WHERE столбцы из других таблиц. В этом выражении используется тот же синтаксис, что и в предложении FROM команды SELECT; например, в нем можно определить псевдоним для таблицы. Повторять в нем имя целевой таблицы нужно, только если требуется определить замкнутое соединение (в этом случае для данного имени должен определяться псевдоним).

условие

Выражение, которое возвращает значение типа boolean. Будут удалены только те строки, для которых это выражение возвращает true.

имя_курсора

Имя курсора, который будет использоваться в условии WHERE CURRENT OF. Строка, подлежащая удалению, является самой последней извлеченной из этого курсора. Курсор должен быть образован запросом без группировки по целевой таблице команды DELETE. Обратите внимание, что WHERE CURRENT OF не может быть указано вместе с логическим условием. Дополнительную информацию об использовании курсоров с WHERE CURRENT OF см. в описании команды DECLARE.

выражение_результата

Выражение, вычисляемое и возвращаемое командой DELETE после удаления каждой строки. Выражение может использовать любые имена столбцов таблицы имя_таблицы или таблиц, перечисленных в списке USING. Чтобы получить все столбцы, достаточно написать *.

имя_результата

Имя, используемое для возвращаемого столбца.

Выводимая информация

После успешного завершения команда DELETE возвращает метку команды в виде

DELETE число

Где число — количество удаленных строк. Обратите внимание, что это число может быть меньше, чем число строк, которые соответствуют условию, если удаления были подавлены триггером BEFORE DELETE. Если число равно 0, ни одна строка не была удалена запросом (это не считается ошибкой).

Если команда DELETE содержит предложение RETURNING, результат будет похож на результат команды SELECT (с теми же столбцами и значениями, что содержатся в списке RETURNING), полученный для строк, удаленных этой командой.

Примечания

QHB позволяет ссылаться на столбцы других таблиц в условии WHERE, когда эти таблицы перечисляются в предложении USING. Например, чтобы удалить все фильмы, произведенные определенным производителем, можно сделать:

DELETE FROM films USING producers
  WHERE producer_id = producers.id AND producers.name = 'foo';

По существу происходит соединение таблиц films и producers, причем все успешно включенные в соединение строки в films помечаются для удаления. Этот синтаксис не является стандартным. Более стандартный способ сделать это:

DELETE FROM films
  WHERE producer_id IN (SELECT id FROM producers WHERE name = 'foo');

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

Примеры

Удаление всех фильмов, кроме мюзиклов:

DELETE FROM films WHERE kind <> 'Musical';

Очистка таблицы films:

DELETE FROM films;

Удаление завершенных задач с получением всех данных удаленных строк:

DELETE FROM tasks WHERE status = 'DONE' RETURNING *;

Удаление из tasks строки, на которой в текущий момент располагается курсор c_tasks:

DELETE FROM tasks WHERE CURRENT OF c_tasks;

Совместимость

Команда DELETE соответствует стандарту SQL, за исключением того, что предложения USING и RETURNING являются расширениями QHB, так же, как и возможность использовать WITH с этой командой.

См. также

TRUNCATE

DISCARD

DISCARD — очистить состояние сеанса

Синтаксис

DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }

Описание

Команда DISCARD освобождает внутренние ресурсы, связанные с сеансом использования базы данных. Эта команда полезна для частичного или полного сброса состояния сеанса. Существует несколько подкоманд для освобождения различных типов ресурсов; вариант DISCARD ALL объединяет все остальные, а также сбрасывает дополнительное состояние.

Параметры

PLANS

Освобождает все кэшированные планы запросов, заставляя сервер провести повторное планирование при следующем использовании связанного подготовленного оператора.

SEQUENCES

Сбрасывает все кэшированные состояния, связанные с последовательностями, включая информацию currval()/lastval() и любые предварительно выделенные значения последовательности, которые еще не были возвращены nextval(). (Описание предварительно выделенных значений последовательности см. в разделе CREATE SEQUENCE).

TEMPORARY или TEMP

Удаляет все временные таблицы, созданные в текущем сеансе.

ALL

Освобождает все временные ресурсы, связанные с текущим сеансом, и возвращает сеанс в исходное состояние. В настоящее время это имеет тот же эффект, что и выполнение следующей последовательности операторов:

CLOSE ALL;
SET SESSION AUTHORIZATION DEFAULT;
RESET ALL;
DEALLOCATE ALL;
UNLISTEN *;
SELECT pg_advisory_unlock_all();
DISCARD PLANS;
DISCARD TEMP;
DISCARD SEQUENCES;

Примечания

DISCARD ALL не может быть выполнена внутри блока транзакций.

Совместимость

Команда DISCARD ALL является расширением QHB.

DO

DO — выполнить анонимный блок кода

Синтаксис

DO [ LANGUAGE имя_языка ] код

Описание

Команда DO выполняет анонимный блок кода или, другими словами, временную анонимную функцию на процедурном языке.

Блок кода обрабатывается так, как если бы это было тело функции без параметров, возвращающее значение void. Этот код анализируется и выполняется один раз.

Необязательное предложение LANGUAGE может быть написано как до, так и после блока кода.

Параметры

код

Выполняемый код на процедурном языке. Он должен задаваться в виде текстовой строки (ее рекомендуется заключать в знаки доллара), как и код в CREATE FUNCTION.

имя_языка

Название процедурного языка, на котором написан код. Если этот параметр опущен, значение по умолчанию равно plpgsql.

Примечания

Используемый процедурный язык должен быть уже установлен в текущую базу данных с помощью команды CREATE EXTENSION. plpgsql устанавливается по умолчанию, но другие языки — нет.

Необходимо иметь право USAGE для данного процедурного языка либо быть суперпользователем, если язык недоверенный. Такие же права требуются и при создании функции на этом языке.

Если команда DO выполняется в блоке транзакций, то код процедуры не может вызывать операторы управления транзакциями. Такие операторы допускаются только в том случае, если DO выполняется в собственной транзакции.

Примеры

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

DO $$DECLARE r record;
BEGIN
    FOR r IN SELECT table_schema, table_name FROM information_schema.tables
             WHERE table_type = 'VIEW' AND table_schema = 'public'
    LOOP
        EXECUTE 'GRANT ALL ON ' || quote_ident(r.table_schema) || '.' || quote_ident(r.table_name) || ' TO webuser';
    END LOOP;
END$$;

Совместимость

В стандарте SQL нет команды DO.

См. также

CREATE LANGUAGE

DROP ACCESS METHOD

DROP ACCESS METHOD — удалить метод доступа

Синтаксис

DROP ACCESS METHOD [ IF EXISTS ] имя [ CASCADE | RESTRICT ]

Описание

Команда DROP ACCESS METHOD удаляет существующий метод доступа. Удалять методы доступа могут только суперпользователи.

Параметры

IF EXISTS

Не считать ошибкой, если метод доступа не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующего метода доступа.

CASCADE

Автоматически удалять объекты, зависящие от метода доступа (такие как классы операторов, семейства операторов и индексы), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять метод доступа, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление метода доступа heptree:

DROP ACCESS METHOD heptree;

Совместимость

Команда DROP ACCESS METHOD является расширением QHB.

См. также

CREATE ACCESS METHOD

DROP AGGREGATE

DROP AGGREGATE — удалить агрегатную функции

Синтаксис

DROP AGGREGATE [ IF EXISTS ] имя ( сигнатура_агрегатной_функции ) [ CASCADE | RESTRICT ]

Где сигнатура_агрегатной_функции:

* |
[ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ] |
[ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ] ] ORDER BY [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ]

Описание

Команда DROP AGGREGATE удаляет существующую агрегатную функцию. Для выполнения этой команды нужно быть владельцем соответствующей агрегатной функции.

Параметры

IF EXISTS

Не считать ошибкой, если агрегатная функция не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующей агрегатной функции (может быть дополнено схемой).

режим_аргумента

Режим аргумента: IN или VARIADIC. Значение по умолчанию равно IN.

имя_аргумента

Имя аргумента. Обратите внимание, что DROP AGGREGATE фактически не обращает внимания на имена аргументов, поскольку для идентификации агрегатной функции необходимы только типы данных ее аргументов.

тип_аргумента

Тип входных данных, с которыми работает агрегатная функция. Чтобы сослаться на агрегатную функцию без аргументов, напишите вместо списка аргументов *. Чтобы сослаться на сортирующую агрегатную функцию, напишите ORDER BY между непосредственными и агрегированными аргументами.

CASCADE

Автоматически удалять объекты, зависящие от агрегатной функции (например, использующие ее представления), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять агрегатную функцию, если от нее зависят какие-либо объекты. Это поведение по умолчанию.

Примечания

Альтернативные синтаксисы для указания сортирующих агрегатных функций описаны в разделе ALTER AGGREGATE.

Примеры

Удалить агрегатную функцию myavg для типа integer:

DROP AGGREGATE myavg(integer);

Удалить гипотезирующую агрегатную функцию myrank, которая принимает произвольный список сортируемых столбцов и соответствующий список непосредственных аргументов:

DROP AGGREGATE myrank(VARIADIC "any" ORDER BY VARIADIC "any");

Удалить несколько агрегатных функций в одной команде:

DROP AGGREGATE myavg (integer), myavg (bigint);

Совместимость

В стандарте SQL нет команды DROP AGGREGATE.

См. также

ALTER AGGREGATE, CREATE AGGREGATE

DROP CAST

DROP CAST — удалить приведение типа

Синтаксис

DROP CAST [ IF EXISTS ] (исходный_тип AS целевой_тип) [ CASCADE | RESTRICT ]

Описание

Команда DROP CAST удаляет ранее определенное приведение типа.

Чтобы удалить приведение типа, нужно быть владельцем исходного или целевого типа данных. Эти же требования действуют и при создании приведения.

Параметры

IF EXISTS

Не считать ошибкой, если приведение не существует. В этом случае будет выдано соответствующее уведомление.

исходный_тип

Имя исходного типа данных для приведения.

целевой_тип

Имя целевого типа данных для приведения.

CASCADE RESTRICT

Эти ключевые слова не имеют эффекта, так как нет объектов, зависящих от приведений.

Примеры

Удалить приведение типа text к типу int:

DROP CAST (text AS int);

Совместимость

Команда DROP CAST соответствует стандарту SQL.

См. также

CREATE CAST

DROP COLLATION

DROP COLLATION — удалить правило сортировки

Синтаксис

DROP COLLATION [ IF EXISTS ] имя [ CASCADE | RESTRICT ]

Описание

Команда DROP COLLATION удаляет ранее определенное правило сортировки. Чтобы удалить правило сортировки, нужно быть его владельцем.

Параметры

IF EXISTS

Не считать ошибкой, если правило сортировки не существует. В этом случае будет выдано соответствующее уведомление.

имя

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

CASCADE

Автоматически удалять объекты, зависящие от правила сортировки, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять правила сортировки, если от них зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удалить правило сортировки с именем german:

DROP COLLATION german;

Совместимость

Команда DROP COLLATION соответствует стандарту SQL, за исключением параметра IF EXISTS, который является расширением QHB.

См. также

ALTER COLLATION, CREATE COLLATION

DROP CONVERSION

DROP CONVERSION — удалить преобразование

Синтаксис

DROP CONVERSION [ IF EXISTS ] имя [ CASCADE | RESTRICT ]

Описание

Команда DROP CONVERSION удаляет ранее определенное преобразование. Чтобы иметь возможность удалить преобразование, нужно быть его владельцем.

Параметры

IF EXISTS

Не считать ошибкой, если преобразование не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя преобразования (может быть дополнено схемой).

CASCADE
RESTRICT

Эти ключевые слова не имеют эффекта, так как нет объектов, зависящих от преобразований.

Примеры

Удалить преобразование с именем myname:

DROP CONVERSION myname;

Совместимость

В стандарте SQL нет команды DROP CONVERSION, но есть команда DROP TRANSLATION, сопутствующая команде CREATE TRANSLATION, которая, в свою очередь, подобна команде CREATE CONVERSION в QHB.

См. также

ALTER CONVERSION, CREATE CONVERSION

DROP DATABASE

DROP DATABASE — удалить базу данных

Синтаксис

DROP DATABASE [ IF EXISTS ] имя [ [ WITH ] ( параметр [, ...] ) ]

Где параметр может быть:

  FORCE

Описание

Команда DROP DATABASE удаляет базу данных. Она удаляет записи из каталогов, которые относятся к базе данных, и удаляет каталог, содержащий данные. Выполнить команду может только владелец базы данных. Кроме того, команда не может быть выполнена, пока вы или кто-либо другой подключен к целевой базе данных. (Чтобы выполнить эту команду, подключитесь к QHB или любой другой базе данных.)

Действие команды DROP DATABASE нельзя отменить. Используйте ее с осторожностью!

Параметры

IF EXISTS

Не считать ошибкой, если база данных не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя базы данных, подлежащей удалению.

FORCE

Попытаться завершить все существующие подключения к целевой базе данных. Подключения не завершаются, если в целевой базе данных находятся подготовленные транзакции, активные слоты логической репликации или подписки.

Данная операция завершится ошибкой, если у текущего пользователя нет прав завершать другие подключения (это те же права, что требуются для выполнения pg_terminate_backend; они описаны в разделе Функции сигнализации сервера). Также ошибка произойдет, если завершить подключения не удастся.

Примечания

Команда DROP DATABASE не может быть выполнена внутри блока транзакций.

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

Совместимость

В стандарте SQL нет команды DROP DATABASE.

См. также

CREATE DATABASE

DROP DOMAIN

DROP DOMAIN — удалить домен

Синтаксис

DROP DOMAIN [ IF EXISTS ] имя [, ...] [ CASCADE | RESTRICT ]

Описание

Команда DROP DOMAIN удаляет домен. Только владелец домена может его удалить.

Параметры

IF EXISTS

Не считать ошибкой, если домен не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующего домена (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от домена (например, столбцы таблицы), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять домен, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удалить домен box:

DROP DOMAIN box;

Совместимость

Команда DROP DOMAIN соответствует стандарту SQL, за исключением параметра IF EXISTS, который является расширением QHB.

См. также

CREATE DOMAIN, ALTER DOMAIN

DROP EVENT TRIGGER

DROP EVENT TRIGGER — удалить событийный триггер

Синтаксис

DROP EVENT TRIGGER [ IF EXISTS ] имя [ CASCADE | RESTRICT ]

Описание

Команда DROP EVENT TRIGGER удаляет существующий событийный триггер. Для выполнения этой команды нужно быть владельцем соответствующего событийного триггера.

Параметры

IF EXISTS

Не считать ошибкой, если событийный триггер не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя событийного триггера, подлежащего удалению.

CASCADE

Автоматически удалять объекты, зависящие от триггера, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять триггер, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление триггера snitch:

DROP EVENT TRIGGER snitch;

Совместимость

В стандарте SQL нет команды DROP EVENT TRIGGER.

См. также

CREATE EVENT TRIGGER, ALTER EVENT TRIGGER

DROP EXTENSION

DROP EXTENSION — удалить расширение

Синтаксис

DROP EXTENSION [ IF EXISTS ] имя [, ...] [ CASCADE | RESTRICT ]

Описание

Команда DROP EXTENSION удаляет расширения из базы данных. Удаление расширения также приводит к удалению составляющих его объектов.

Для выполнения команды DROP EXTENSION нужно быть владельцем соответствующего расширения.

Параметры

IF EXISTS

Не считать ошибкой, если расширение не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя установленного расширения.

CASCADE

Автоматически удалять объекты, зависящие от расширения, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять расширение, если от него зависят какие-либо объекты (кроме его собственных объектов и других расширений, перечисленных в той же команде DROP). Это поведение по умолчанию.

Примеры

Удаление расширения hstore из текущей базы данных:

DROP EXTENSION hstore;

Эта команда не будет выполнена, если какой-либо объект из hstore используется в базе данных, например, если в какой-либо таблице есть столбец с типом hstore. Добавив указание CASCADE, можно принудительно удалить и все зависимые объекты.

Совместимость

Команда DROP EXTENSION является расширением QHB.

См. также

CREATE EXTENSION, ALTER EXTENSION

DROP FOREIGN DATA WRAPPER

DROP FOREIGN DATA WRAPPER — удалить обертку сторонних данных

Синтаксис

DROP FOREIGN DATA WRAPPER [ IF EXISTS ] имя [ CASCADE | RESTRICT ]

Описание

Команда DROP FOREIGN DATA WRAPPER удаляет существующую обертку сторонних данных. Для выполнения этой команды нужно быть владельцем соответствующей обертки сторонних данных.

Параметры

IF EXISTS

Не считать ошибкой, если обертка сторонних данных не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующей обертки сторонних данных.

CASCADE

Автоматически удалять объекты, зависящие от обертки сторонних данных (например, сторонние таблицы и серверы), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять обертку сторонних данных, если от нее зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление обертки сторонних данных dbi:

DROP FOREIGN DATA WRAPPER dbi;

Совместимость

Команда DROP FOREIGN DATA WRAPPER соответствует стандарту ISO/IEC 9075-9 (SQL/MED). Предложение IF EXISTS является расширением QHB.

См. также

CREATE FOREIGN DATA WRAPPER, ALTER FOREIGN DATA WRAPPER

DROP FOREIGN TABLE

DROP FOREIGN TABLE — удалить стороннюю таблицу

Синтаксис

DROP FOREIGN TABLE [ IF EXISTS ] имя [, ...] [ CASCADE | RESTRICT ]

Описание

Команда DROP FOREIGN TABLE удаляет стороннюю таблицу. Только владелец сторонней таблицы может ее удалить.

Параметры

IF EXISTS

Не считать ошибкой, если сторонняя таблица не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя сторонней таблицы, подлежащей удалению (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от сторонней таблицы (например, представления), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять стороннюю таблицу, если от нее зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление двух сторонних таблиц, films и distributors:

DROP FOREIGN TABLE films, distributors;

Совместимость

Команда DROP FOREIGN TABLE соответствует стандарту ISO/IEC 9075-9 (SQL/MED), за исключением возможности удалять несколько сторонних таблиц в одной команде, а также параметра IF EXISTS, которые являются расширением QHB.

См. также

ALTER FOREIGN TABLE, CREATE FOREIGN TABLE

DROP FUNCTION

DROP FUNCTION — удалить функцию

Синтаксис

DROP FUNCTION [ IF EXISTS ] имя ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] )
    [ CASCADE | RESTRICT ]

Описание

Команда DROP FUNCTION удаляет определение существующей функции. Для выполнения этой команды нужно быть владельцем соответствующей функции. Кроме имени функции должны быть еще указаны типы ее аргументов, так как могут существовать несколько различных функций с одинаковым именем и разными списками аргументов.

Параметры

IF EXISTS

Не считать ошибкой, если функция не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующей функции (может быть дополнено схемой). Если список аргументов не указан, имя должно быть уникальным в своей схеме.

режим_аргумента

Режим аргумента: IN, OUT, INOUT или VARIADIC. Значением по умолчанию является IN. Обратите внимание, что команда DROP FUNCTION на самом деле не обращает внимания на аргументы с режимом OUT, поскольку для идентификации функции нужны только аргументы с режимом IN. Так что достаточно перечислить аргументы с режимами IN, INOUT и VARIADIC.

имя_аргумента

Имя аргумента. Обратите внимание, что команда DROP FUNCTION фактически не обращает внимания на имена аргументов, поскольку для идентификации функции нужны только типы данных ее аргументов.

тип_аргумента

Тип (или типы) данных аргументов функции (может быть дополнен схемой), если таковой имеется.

CASCADE

Автоматически удалять объекты, зависящие от функции (например, операторы или триггеры), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять функцию, если от нее зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Эта команда удаляет функцию, вычисляющую квадратный корень:

DROP FUNCTION sqrt(integer);

Удаление нескольких функций одной командой:

DROP FUNCTION sqrt(integer), sqrt(bigint);

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

DROP FUNCTION update_employee_salaries;

Обратите внимание, что это отличается от

DROP FUNCTION update_employee_salaries();

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

Совместимость

Команда DROP FUNCTION соответствует стандарту SQL, со следующими расширениями QHB:

Стандарт позволяет удалить командой только одну функцию.

Параметр IF EXISTS

Возможность задавать режимы и имена аргументов

См. также

CREATE FUNCTION, ALTER FUNCTION, DROP PROCEDURE, DROP ROUTINE

DROP GROUP

DROP GROUP — удалить роль в базе данных

Синтаксис

DROP GROUP [ IF EXISTS ] имя [, ...]

Описание

Команда DROP GROUP является синонимом команды DROP ROLE.

Совместимость

В стандарте SQL нет команды DROP GROUP.

См. также

DROP ROLE

DROP INDEX

DROP INDEX — удалить индекс

Синтаксис

DROP INDEX [ CONCURRENTLY ] [ IF EXISTS ] имя [, ...] [ CASCADE | RESTRICT ]

Описание

Команда DROP INDEX удаляет существующий индекс из базы данных. Для выполнения этой команды нужно быть владельцем соответствующего индекса.

Параметры

CONCURRENTLY

Это указание позволяет удалить индекс без блокировки одновременных операций отбора, добавления, изменения и удаления данных в таблице индекса. Обычная команда DROP INDEX запрашивает исключительную блокировку на таблицу, блокируя другие обращения к ней до тех пор, пока удаление индекса не будет завершено. При использовании же данного параметра команда вместо этого будет ожидать завершения конфликтующих транзакций.

Есть несколько особенностей, которые следует учитывать при использовании этого параметра. В команде можно указать только одно имя индекса; также не поддерживается указание CASCADE. (Следовательно, индекс, поддерживающий ограничение UNIQUE или PRIMARY KEY, не может быть удален таким способом.) Кроме того, обычные команды DROP INDEX могут выполняться в блоке транзакций, а DROP INDEX CONCURRENTLY не может. И наконец, с помощью этого параметра нельзя удалить индексы партиционированных таблиц.

На временных таблицах команда DROP INDEX всегда выполняется менее затратным неблокирующим способом, так как они не могут использоваться другими сеансами.

IF EXISTS

Не считать ошибкой, если индекс не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя индекса, подлежащего удалению (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от индекса, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять индекс, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Эта команда удалит индекс title_idx:

DROP INDEX title_idx;

Совместимость

Команда DROP INDEX является расширением QHB. В стандарте SQL нет положений для индексов.

См. также

CREATE INDEX

DROP LANGUAGE

DROP LANGUAGE — удалить процедурный язык

Синтаксис

DROP [ PROCEDURAL ] LANGUAGE [ IF EXISTS ] имя [ CASCADE | RESTRICT ]

Описание

Команда DROP LANGUAGE удаляет определение ранее зарегистрированного процедурного языка. Чтобы использовать команду DROP LANGUAGE, нужно быть суперпользователем или владельцем соответствующего языка.

Примечание
Большинство процедурных языков были преобразованы в «расширения», поэтому теперь их следует удалять командой DROP EXTENSION, а не DROP LANGUAGE.

Параметры

IF EXISTS

Не считать ошибкой, если процедурный язык не существует. В этом случае будет выдано соответствующее уведомление.

имя

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

CASCADE

Автоматически удалять объекты, зависящие от процедурного языка (например, функции в языке), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять процедурный язык, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Эта команда удаляет процедурный язык plsample:

DROP LANGUAGE plsample;

Совместимость

В стандарте SQL нет команды DROP LANGUAGE.

См. также

ALTER LANGUAGE, CREATE LANGUAGE

DROP MATERIALIZED VIEW

DROP MATERIALIZED VIEW — удалить материализованное представление

Синтаксис

DROP MATERIALIZED VIEW [ IF EXISTS ] имя [, ...] [ CASCADE | RESTRICT ]

Описание

Команда DROP MATERIALIZED VIEW удаляет существующее материализованное представление. Для выполнения этой команды нужно быть владельцем соответствующего материализованного представления.

Параметры

IF EXISTS

Не считать ошибкой, если материализованное представление не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя материализованного представления, подлежащего удалению (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от материализованного представления (например, другие материализованные представления или обычные представления), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять материализованное представление, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Эта команда удаляет материализованное представление с именем order_summary:

DROP MATERIALIZED VIEW order_summary;

Совместимость

Команда DROP MATERIALIZED VIEW является расширением QHB.

См. также

CREATE MATERIALIZED VIEW, ALTER MATERIALIZED VIEW, REFRESH MATERIALIZED VIEW

DROP OPERATOR CLASS

DROP OPERATOR CLASS — удалить класс операторов

Синтаксис

DROP OPERATOR CLASS [ IF EXISTS ] имя USING индексный_метод [ CASCADE | RESTRICT ]

Описание

Команда DROP OPERATOR CLASS удаляет существующий класс операторов. Для выполнения этой команды нужно быть владельцем соответствующего класса операторов.

DROP OPERATOR CLASS не удаляет операторы и функции, связанные с этим классом. Если есть какие-либо индексы, зависящие от этого класса оператора, необходимо будет указать CASCADE, чтобы удаление было успешно завершено.

Параметры

IF EXISTS

Не считать ошибкой, если класс операторов не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующего класса операторов (может быть дополнено схемой).

индексный_метод

Имя метода доступа к индексу, для которого предназначен класс операторов.

CASCADE

Автоматически удалять объекты, зависящие от класса операторов (например, индексы), и в свою очередь все объекты, зависящие от этих объектов (см.раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять класс операторов, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примечания

DROP OPERATOR CLASS не удаляет семейство операторов, содержащее класс, даже если в семействе больше ничего не останется (в частности, в том случае, когда семейство было неявно создано с помощью команды CREATE OPERATOR CLASS). Пустое семейство операторов безвредно, но из соображений аккуратности его можно удалить командой DROP OPERATOR FAMILY. Или, возможно, лучше изначально использовать DROP OPERATOR FAMILY.

Примеры

Удаление класса операторов B-дерева с именем widget_ops:

DROP OPERATOR CLASS widget_ops USING btree;

Эта команда не будет выполнена, если существуют какие-либо индексы, использующие этот класс операторов. Чтобы удалить такие индексы вместе с классом оператора, нужно добавить в команду указание CASCADE.

Совместимость

В стандарте SQL нет команды DROP OPERATOR CLASS.

См. также

ALTER OPERATOR CLASS, CREATE OPERATOR CLASS, DROP OPERATOR FAMILY

DROP OPERATOR FAMILY

DROP OPERATOR FAMILY — удалить семейство операторов

Синтаксис

DROP OPERATOR FAMILY [ IF EXISTS ] имя USING индексный_метод [ CASCADE | RESTRICT ]

Описание

Команда DROP OPERATOR FAMILY удаляет существующее семейство операторов. Для выполнения этой команды нужно быть владельцем соответствующего семейства операторов.

Команда DROP OPERATOR FAMILY удаляет также и все классы операторов, содержащиеся в семействе, но не удаляет связанные с семейством операторы и функции. Если внутри семейства существуют какие-либо индексы, зависящие от классов операторов, для успешного завершения команды необходимо будет указать CASCADE.

Параметры

IF EXISTS

Не считать ошибкой, если семейство операторов не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующего семейства операторов (может быть дополнено схемой).

индексный_метод

Имя метода доступа к индексу, для которого предназначено семейство операторов.

CASCADE

Автоматически удалять объекты, зависящие от семейства операторов, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять семейство операторов, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление семейства операторов B-дерева с именем float_ops:

DROP OPERATOR FAMILY float_ops USING btree;

Эта команда не будет выполнена, если внутри данного семейства существуют какие-либо индексы, использующие классы операторов. Чтобы удалить такие индексы вместе с семейством операторов, нужно добавить указание CASCADE.

Совместимость

В стандарте SQL нет команды DROP OPERATOR FAMILY.

См. также

ALTER OPERATOR FAMILY, CREATE OPERATOR FAMILY, ALTER OPERATOR CLASS CREATE OPERATOR CLASS, DROP OPERATOR CLASS

DROP OPERATOR

DROP OPERATOR — удалить оператор

Синтаксис

DROP OPERATOR [ IF EXISTS ] имя ( { тип_слева | NONE } , { тип_справа | NONE } ) [, ... ] [ CASCADE | RESTRICT ]

Описание

Команда DROP OPERATOR удаляет существующий оператор из базы данных. Для выполнения этой команды нужно быть владельцем соответствующего оператора.

Параметры

IF EXISTS

Не считать ошибкой, если оператор не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующего оператора (может быть дополнено схемой).

тип_слева

Тип данных левого операнда оператора; если у оператора нет левого операнда, укажите NONE.

тип_справа

Тип данных правого операнда оператора; если у оператора нет правого операнда, укажите NONE.

CASCADE

Автоматически удалять объекты, зависящие от оператора (например, использующие его представления), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять оператор, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление оператора возведения в степень a^b для типа integer:

DROP OPERATOR ^ (integer, integer);

Удаление левого унарного оператора двоичного дополнения ~b для типа bit:

DROP OPERATOR ~ (none, bit);

Удаление правого унарного оператора вычисления факториала x! для типа bigint:

DROP OPERATOR ! (bigint, none);

Удаление нескольких операторов одной командой:

DROP OPERATOR ~ (none, bit), ! (bigint, none);

Совместимость

В стандарте SQL нет команды DROP OPERATOR.

См. также

CREATE OPERATOR, ALTER OPERATOR

DROP OWNED

DROP OWNED — удалить объекты базы данных, принадлежащие роли

Синтаксис

DROP OWNED BY { имя | CURRENT_USER | SESSION_USER } [, ...] [ CASCADE | RESTRICT ]

Описание

Команда DROP OWNED удаляет все объекты в текущей базе данных, принадлежащие указанным ролям. Все права, предоставленные данным ролям для объектов в текущей базе данных или для общих объектов (баз данных, табличных пространств), также будут отозваны.

Параметры

имя

Имя роли, объекты которой будут удалены и права которой будут отозваны.

CASCADE

Автоматически удалять объекты, зависящие от затронутых объектов, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

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

Примечания

Команда DROP OWNED часто используется для подготовки к удалению одной или нескольких ролей. Поскольку DROP OWNED влияет только на объекты в текущей базе данных, обычно необходимо выполнить эту команду в каждой базе данных, содержащей объекты, принадлежащие удаляемой роли.

С указанием CASCADE эта команда рекурсивно удалит объекты, принадлежащие и другим пользователям.

Команда REASSIGN OWNED является альтернативой DROP OWNED: она переназначает владельца всех объектов базы данных, принадлежащих одной или нескольким ролям. Однако REASSIGN OWNED не затрагивает никакие права для других объектов.

Базы данных и табличные пространства, принадлежащие роли (или ролям), удалены не будут.

Дополнительную информацию см. в разделе Удаление ролей.

Совместимость

Команда DROP OWNED является расширением QHB.

См. также

REASSIGN OWNED, DROP ROLE

DROP POLICY

DROP POLICY — удалить политику защиты на уровне строк для таблицы

Синтаксис

DROP POLICY [ IF EXISTS ] имя ON имя_таблицы [ CASCADE | RESTRICT ]

Описание

Команда DROP POLICY удаляет указанную политику из таблицы. Обратите внимание, что если из таблицы удаляется последняя политика и для таблицы по-прежнему включена защита на уровне строк (с помощью ALTER TABLE), то использоваться будет политика запрета по умолчанию. Для выключения защиты строк для таблицы, независимо от того, существуют ли политики для таблицы или нет, можно использовать команду ALTER TABLE ... DISABLE ROW LEVEL SECURITY.

Параметры

IF EXISTS

Не считать ошибкой, если политика не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя политики, подлежащей удалению.

имя_таблицы

Имя таблицы, для которой включена политика (может быть дополнено схемой).

CASCADE
RESTRICT

Эти ключевые слова не имеют эффекта, так как у политик отсутствуют зависимые объекты.

Примеры

Удаление политики p1 из таблицы my_table:

DROP POLICY p1 ON my_table;

Совместимость

Команда DROP POLICY является расширением QHB.

См. также

CREATE POLICY, ALTER POLICY

DROP PROCEDURE

DROP PROCEDURE — удалить процедуру

Синтаксис

DROP PROCEDURE [ IF EXISTS ] имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] [, ...]
    [ CASCADE | RESTRICT ]

Описание

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

Параметры

IF EXISTS

Не считать ошибкой, если процедура не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующей процедуры (может быть дополнено схемой). Если список аргументов не указан, имя должно быть уникальным в своей схеме.

режим_аргумента

Режим аргумента: IN или VARIADIC. Значение по умолчанию равно IN.

имя_аргумента

Имя аргумента. Обратите внимание, что команда DROP PROCEDURE фактически не обращает внимания на имена аргументов, поскольку для идентификации процедуры необходимы только типы данных ее аргументов.

тип_аргумента

Тип (или типы) данных аргументов процедуры (может быть дополнен схемой), если таковой имеется.

CASCADE

Автоматически удалять объекты, зависящие от процедуры, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять процедуру, если от нее зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

DROP PROCEDURE do_db_maintenance();

Совместимость

Команда DROP PROCEDURE соответствует стандарту SQL и имеет следующие расширения QHB:

Стандарт позволяет удалить командой только одну процедуру.

Параметр IF EXISTS

Возможность задавать режимы и имена аргументов

См. также

CREATE PROCEDURE, ALTER PROCEDURE, DROP FUNCTION, DROP ROUTINE

DROP PUBLICATION

DROP PUBLICATION — удалить публикацию

Синтаксис

DROP PUBLICATION [ IF EXISTS ] имя [, ...] [ CASCADE | RESTRICT ]

Описание

Команда DROP PUBLICATION удаляет существующую публикацию из базы данных.

Публикацию может удалить только ее владелец или суперпользователь.

Параметры

IF EXISTS

Не считать ошибкой, если публикация не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующей публикации.

CASCADE
RESTRICT

Эти ключевые слова не имеют эффекта, так как у публикаций нет зависимых объектов.

Примеры

Удаление публикации:

DROP PUBLICATION mypublication;

Совместимость

Команда DROP PUBLICATION является расширением QHB.

См. также

CREATE PUBLICATION, ALTER PUBLICATION

DROP ROLE

DROP ROLE — удалить роль в базе данных

Синтаксис

DROP ROLE [ IF EXISTS ] имя [, ...]

Описание

Команда DROP ROLE удаляет указанную(ые) роль(и). Чтобы удалить роль суперпользователя, нужно самому быть суперпользователем; чтобы удалять обычные роли, необходимо иметь право CREATEROLE.

Роль нельзя удалить, если на нее всё еще есть ссылки в любой базе данных кластера; в этом случае возникнет ошибка и роль не будет удалена. Перед удалением роли необходимо удалить все принадлежащие ей объекты (или сменить их владельца) и отозвать все права, предоставленные этой роли для других объектов. Для этой цели могут быть применены команды REASSIGN OWNED и DROP OWNED; дополнительную информацию см. в разделе Удаление ролей.

Тем не менее, нет необходимости удалять членство в ролях, включающее роль; DROP ROLE автоматически отменяет любое членство целевой роли в других ролях и каких-либо других ролей в целевой роли. Нецелевые роли при этом не удаляются и не затрагиваются иным образом.

Параметры

IF EXISTS

Не считать ошибкой, если роль не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя роли, подлежащей удалению.

Примечания

QHB включает в себя программу dropuser, которая имеет ту же функциональность, что и эта команда (фактически она вызывает эту команду), но может быть запущена из командной оболочки.

Примеры

Удаление роли:

DROP ROLE jonathan;

Совместимость

В стандарте SQL описана команда DROP ROLE, но он позволяет удалять командой только одну роль и определяет требования к правам, отличные от требований QHB.

См. также

CREATE ROLE, ALTER ROLE, SET ROLE

DROP ROUTINE

DROP ROUTINE — удалить подпрограмму

Синтаксис

DROP ROUTINE [ IF EXISTS ] имя [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] [, ...]
    [ CASCADE | RESTRICT ]

Описание

Команда DROP ROUTINE удаляет определение существующей подпрограммы, которая может быть агрегатной функцией, обычной функцией или процедурой. Описание параметров, а также дополнительные примеры и сведения см. в разделах DROP AGGREGATE, DROP FUNCTION и DROP PROCEDURE.

Примеры

Удаление подпрограммы foo для типа integer:

DROP ROUTINE foo(integer);

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

Совместимость

Команда DROP ROUTINE соответствует стандарту SQL и имеет следующие расширения QHB:

Стандарт позволяет удалить командой только одну подпрограмму.

Параметр IF EXISTS

Возможность задавать режимы и имена аргументов

Агрегатные функции.

См. также

DROP AGGREGATE, DROP FUNCTION, DROP PROCEDURE, ALTER ROUTINE Обратите внимание, что команды CREATE ROUTINE нет.

DROP RULE

DROP RULE — удалить правило перезаписи

Синтаксис

DROP RULE [ IF EXISTS ] имя ON имя_таблицы [ CASCADE | RESTRICT ]

Описание

Команда DROP RULE удаляет правило перезаписи.

Параметры

IF EXISTS

Не считать ошибкой, если правило не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя правила, подлежащего удалению.

имя_таблицы

Имя таблицы или представления, к которым применяется правило (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от правила, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять правило, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление правила перезаписи newrule:

DROP RULE newrule ON mytable;

Совместимость

Команда DROP RULE является языковым расширением QHB, как и вся система перезаписи запросов.

См. также

CREATE RULE, ALTER RULE

DROP SCHEMA

DROP SCHEMA — удалить схему

Синтаксис

DROP SCHEMA [ IF EXISTS ] имя [, ...] [ CASCADE | RESTRICT ]

Описание

Команда DROP SCHEMA удаляет схемы из базы данных.

Схему может удалить только ее владелец или суперпользователь. Обратите внимание, что владелец может удалить схему (и, как следствие, все содержащиеся в ней объекты), даже если он не владеет некоторыми объектами внутри схемы.

Параметры

IF EXISTS

Не считать ошибкой, если схема не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя схемы.

CASCADE

Автоматически удалять объекты (таблицы, функции и т. д.), которые содержатся в схеме, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять схему, если она содержит какие-либо объекты. Это поведение по умолчанию.

Примечания

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

Примеры

Удаление схемы mystuff из базы данных вместе со всем, что в ней содержится:

DROP SCHEMA mystuff CASCADE;

Совместимость

Команда DROP SCHEMA полностью соответствует стандарту SQL, за исключением того, что стандарт позволяет удалять в одной команде только одну схему, а также указания IF EXISTS, которое является расширением QHB.

См. также

ALTER SCHEMA, CREATE SCHEMA

DROP SEQUENCE

DROP SEQUENCE — удалить последовательность

Синтаксис

DROP SEQUENCE [ IF EXISTS ] имя [, ...] [ CASCADE | RESTRICT ]

Описание

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

Параметры

IF EXISTS

Не считать ошибкой, если последовательность не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя последовательности (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от последовательности, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять последовательность, если от нее зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление последовательности serial:

DROP SEQUENCE serial;

Совместимость

Команда DROP SEQUENCE соответствует стандарту SQL, за исключением того, что стандарт позволяет удалять в одной команде только одну последовательность, а также указания IF EXISTS, которое является расширением QHB.

См. также

CREATE SEQUENCE, ALTER SEQUENCE

DROP SERVER

DROP SERVER — удалить описание стороннего сервера

Синтаксис

DROP SERVER [ IF EXISTS ] имя [, ...] [ CASCADE | RESTRICT ]

Описание

Команда DROP SERVER удаляет существующее описание стороннего сервера. Для выполнения этой команды нужно быть владельцем соответствующего стороннего сервера.

Параметры

IF EXISTS

Не считать ошибкой, если сервер не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующего сервера.

CASCADE

Автоматически удалять объекты, зависящие от сервера (например, пользовательские сопоставления), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять сервер, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление определения сервера foo, если оно существует:

DROP SERVER IF EXISTS foo;

Совместимость

Команда DROP SERVER соответствует стандарту ISO/IEC 9075-9 (SQL/MED). Предложение IF EXISTS является расширением QHB.

См. также

CREATE SERVER, ALTER SERVER

DROP STATISTICS

DROP STATISTICS — удалить расширенную статистику

Синтаксис

DROP STATISTICS [ IF EXISTS ] имя [, ...]

Описание

Команда DROP STATISTICS удаляет объект(ы) статистики из базы данных. Только владелец соответствующего объекта статистики, владелец схемы или суперпользователь могут удалить объект статистики.

Параметры

IF EXISTS

Не считать ошибкой, если объект статистики не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя объекта статистики, подлежащего удалению (может быть дополнено схемой).

Примеры

Удаление двух объектов статистики в разных схемах (если эти объекты отсутствуют, ошибки не будет):

DROP STATISTICS IF EXISTS
    accounting.users_uid_creation,
    public.grants_user_role;

Совместимость

В стандарте SQL нет команды DROP STATISTICS.

См. также

ALTER STATISTICS, CREATE STATISTICS

DROP SUBSCRIPTION

DROP SUBSCRIPTION — удалить подписку

Синтаксис

DROP SUBSCRIPTION [ IF EXISTS ] имя [ CASCADE | RESTRICT ]

Описание

Команда DROP SUBSCRIPTION удаляет подписку из кластера базы данных.

Подписку может удалить только суперпользователь.

DROP SUBSCRIPTION не может быть выполнена внутри блока транзакций, если подписка связана с слотом репликации. (Для освобождения слота можно использовать ALTER SUBSCRIPTION.)

Параметры

имя

Имя подписки, подлежащей удалению.

CASCADE
RESTRICT

Эти ключевые слова не имеют эффекта, так как подписки не имеют зависимых объектов.

Примечания

При удалении подписки, связанной со слотом репликации на удаленном узле (типичная ситуация), команда DROP SUBSCRIPTION подключится к удаленному узлу и попытается удалить слот репликации как часть этой операции. Это необходимо для того, чтобы освободить ресурсы, выделенные для подписки на удаленном узле. Если при этом происходит сбой — либо из-за недоступности удаленного узла, либо из-за ошибки при удалении слота репликации, либо вообще из-за его отсутствия, — команда DROP SUBSCRIPTION прерывается. Чтобы продолжить работу в такой ситуации, отключите подписку от слота репликации путем выполнения ALTER SUBSCRIPTION ... SET (slot_name = NONE). После этого DROP SUBSCRIPTION больше не будет пытаться выполнять какие-либо действия на удаленном узле. Примите к сведению, что если удаленный слот репликации всё еще существует, его следует удалить вручную; в противном случае он будет продолжать резервировать WAL, что может в конечном итоге привести к переполнению диска.

Если подписка связана со слотом репликации, то команда DROP SUBSCRIPTION не может быть выполнена внутри блока транзакций.

Примеры

Удаление подписки:

DROP SUBSCRIPTION mysub;

Совместимость

Команда DROP SUBSCRIPTION является расширением QHB.

См. также

CREATE SUBSCRIPTION, ALTER SUBSCRIPTION

DROP TABLESPACE

DROP TABLESPACE — удалить табличное пространство

Синтаксис

DROP TABLESPACE [ IF EXISTS ] имя

Описание

Команда DROP TABLESPACE удаляет табличное пространство из системы.

Табличное пространство может быть удалено только его владельцем или суперпользователем. Перед удалением табличное пространство необходимо очистить от всех объектов базы данных. Необходимо также учесть, что если в текущей базе данных не будет ни одного объекта, находящегося в этом пространстве, в нём вполне могут оставаться объекты других баз данных. Кроме того, если табличное пространство, указано в списке temp_tablespaces любого активного сеанса, в этом пространстве окажутся временные файлы, из-за чего команда DROP может завершиться ошибкой.

Параметры

IF EXISTS

Не считать ошибкой, если табличное пространство не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя табличного пространства.

Примечания

Команда DROP TABLESPACE не может быть выполнена внутри блока транзакций.

Примеры

Удаление табличного пространства mystuff:

DROP TABLESPACE mystuff;

Совместимость

Команда DROP TABLESPACE является расширением QHB.

См. также

CREATE TABLESPACE, ALTER TABLESPACE

DROP TABLE

DROP TABLE — удалить таблицу

Синтаксис

DROP TABLE [ IF EXISTS ] имя [, ...] [ CASCADE | RESTRICT ]

Описание

Команда DROP TABLE удаляет таблицы из базы данных. Только владелец соответствующей таблицы, владелец схемы или суперпользователь могут удалить таблицу. Чтобы удалить содержимое таблицы, не удаляя ее саму, используйте команды DELETE или TRUNCATE.

Команда DROP TABLE также удаляет все индексы, правила, триггеры и ограничения, которые существуют для целевой таблицы. Однако для удаления таблицы, на которую ссылается представление или ограничение внешнего ключа другой таблицы, должно быть дополнительно указано CASCADE. (CASCADE полностью удалит зависимое представление, но в случае внешнего ключа будет удалено только ограничение внешнего ключа, а не вся таблица, с которой он связан.)

Параметры

IF EXISTS

Не считать ошибкой, если таблица не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя удаляемой таблицы (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от таблицы (например, представления), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять таблицу, если от нее зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление таблиц films и distributors:

DROP TABLE films, distributors;

Совместимость

Команда DROP TABLE соответствует стандарту SQL, за исключением того, что стандарт позволяет удалять в одной команде только одну таблицу, а также параметра IF EXISTS, который является расширением QHB.

См. также

ALTER TABLE, CREATE TABLE

DROP TEXT SEARCH CONFIGURATION

DROP TEXT SEARCH CONFIGURATION — удалить конфигурацию текстового поиска

Синтаксис

DROP TEXT SEARCH CONFIGURATION [ IF EXISTS ] имя [ CASCADE | RESTRICT ]

Описание

Команда DROP TEXT SEARCH CONFIGURATION удаляет существующую конфигурацию текстового поиска. Для выполнения этой команды нужно быть владельцем соответствующей конфигурации.

Параметры

IF EXISTS

Не считать ошибкой, если конфигурация текстового поиска не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующей конфигурации текстового поиска (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от конфигурации текстового поиска, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять конфигурацию текстового поиска, если от нее зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление конфигурации текстового поиска my_english:

DROP TEXT SEARCH CONFIGURATION my_english;

Эта команда не будет выполнена, если существуют какие-либо индексы, ссылающиеся на конфигурацию в вызовах to_tsvector. Чтобы удалить такие индексы вместе с конфигурацией текстового поиска, нужно дополнительно указать CASCADE.

Совместимость

В стандарте SQL нет команды DROP TEXT SEARCH CONFIGURATION.

См. также

ALTER TEXT SEARCH CONFIGURATION, CREATE TEXT SEARCH CONFIGURATION

DROP TEXT SEARCH DICTIONARY

DROP TEXT SEARCH DICTIONARY — удалить словарь текстового поиска

Синтаксис

DROP TEXT SEARCH DICTIONARY [ IF EXISTS ] имя [ CASCADE | RESTRICT ]

Описание

Команда DROP TEXT SEARCH DICTIONARY удаляет существующий словарь текстового поиска. Для выполнения этой команды нужно быть владельцем соответствующего словаря.

Параметры

IF EXISTS

Не считать ошибкой, если словарь текстового поиска не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующего словаря текстового поиска (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от словаря текстового поиска, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять словарь текстового поиска, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление словаря текстового поиска english:

DROP TEXT SEARCH DICTIONARY english;

Эта команда не будет выполнена, если существуют какие-либо конфигурации текстового поиска, использующие этот словарь. Чтобы удалить такие конфигурации вместе со словарем, нужно дополнительно указать CASCADE.

Совместимость

В стандарте SQL нет команды DROP TEXT SEARCH DICTIONARY.

См. также

ALTER TEXT SEARCH DICTIONARY, CREATE TEXT SEARCH DICTIONARY

DROP TEXT SEARCH PARSER

DROP TEXT SEARCH PARSER — удалить синтаксический анализатор текстового поиска

Синтаксис

DROP TEXT SEARCH PARSER [ IF EXISTS ] имя [ CASCADE | RESTRICT ]

Описание

Команда DROP TEXT SEARCH PARSER удаляет существующий синтаксический анализатор текстового поиска. Чтобы выполнить эту команду, нужно быть суперпользователем.

Параметры

IF EXISTS

Не считать ошибкой, если синтаксический анализатор текстового поиска не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующего синтаксического анализатора текстового поиска (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от синтаксического анализатора текстового поиска, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять синтаксический анализатор текстового поиска, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление синтаксического анализатора текстового поиска my_parser:

DROP TEXT SEARCH PARSER my_parser;

Эта команда не будет выполнена, если существуют какие-либо конфигурации текстового поиска, использующие этот синтаксический анализатор. Чтобы удалить такие конфигурации вместе с синтаксическим анализатором, нужно дополнительно указать CASCADE.

Совместимость

В стандарте SQL нет команды DROP TEXT SEARCH PARSER.

См. также

ALTER TEXT SEARCH PARSER, CREATE TEXT SEARCH PARSER

DROP TEXT SEARCH TEMPLATE

DROP TEXT SEARCH TEMPLATE — удалить шаблон текстового поиска

Синтаксис

DROP TEXT SEARCH TEMPLATE [ IF EXISTS ] имя [ CASCADE | RESTRICT ]

Описание

Команда DROP TEXT SEARCH TEMPLATE удаляет существующий шаблон текстового поиска. Чтобы выполнить эту команду, нужно быть суперпользователем.

Параметры

IF EXISTS

Не считать ошибкой, если шаблон текстового поиска не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя существующего шаблона текстового поиска (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от шаблона текстового поиска, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять шаблон текстового поиска, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление шаблона текстового поиска thesaurus:

DROP TEXT SEARCH TEMPLATE thesaurus;

Эта команда не будет выполнена, если существуют какие-либо словари текстового поиска, использующие этот шаблон. Чтобы удалить такие словари вместе с шаблоном, нужно дополнительно указать CASCADE.

Совместимость

В стандарте SQL нет команды DROP TEXT SEARCH TEMPLATE.

См. также

ALTER TEXT SEARCH TEMPLATE, CREATE TEXT SEARCH TEMPLATE

DROP TRANSFORM

DROP TRANSFORM — удалить трансформацию

Синтаксис

DROP TRANSFORM [ IF EXISTS ] FOR имя_типа LANGUAGE имя_языка [ CASCADE | RESTRICT ]

Описание

Команда DROP TRANSFORM удаляет ранее определенную трансформацию.

Чтобы удалить трансформацию, нужно быть владельцем соответствующих типа и языка. Такие же требования действуют и при создании трансформации.

Параметры

IF EXISTS

Не считать ошибкой, если трансформация не существует. В этом случае будет выдано соответствующее уведомление.

имя_типа

Имя типа данных трансформации.

имя_языка

Имя языка трансформации.

CASCADE

Автоматически удалять объекты, зависящие от трансформации, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять трансформацию, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление трансформации для типа hstore и языка plpythonu:

DROP TRANSFORM FOR hstore LANGUAGE plpythonu;

Совместимость

Команда DROP TRANSFORM является расширением QHB. Дополнительную информацию см. в разделе CREATE TRANSFORM.

См. также

CREATE TRANSFORM

DROP TRIGGER

DROP TRIGGER — удалить триггер

Синтаксис

DROP TRIGGER [ IF EXISTS ] имя ON имя_таблицы [ CASCADE | RESTRICT ]

Описание

Команда DROP TRIGGER удаляет существующее определение триггера. Для выполнения этой команды нужно быть владельцем таблицы, для которой определен триггер.

Параметры

IF EXISTS

Не считать ошибкой, если триггер не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя триггера, подлежащего удалению.

имя_таблицы

Имя таблицы, для которой определен триггер (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от триггера, и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять триггер, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление триггера if_dist_exists в таблице films:

DROP TRIGGER if_dist_exists ON films;

Совместимость

Команда DROP TRIGGER в QHB несовместима со стандартом SQL. В стандарте SQL имена триггеров не являются локальными для таблиц, поэтому синтаксис команды проще: DROP TRIGGER имя.

См. также

CREATE TRIGGER

DROP TYPE

DROP TYPE — удалить тип данных

Синтаксис

DROP TYPE [ IF EXISTS ] имя [, ...] [ CASCADE | RESTRICT ]

Описание

Команда DROP TYPE удаляет созданный пользователем тип данных. Только владелец типа может его удалить.

Параметры

IF EXISTS

Не считать ошибкой, если тип не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя типа данных, подлежащего удалению (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от типа (например, столбцы таблицы, функции и операторы), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять тип, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Удаление типа данных box:

DROP TYPE box;

Совместимость

Эта команда аналогична соответствующей команде в стандарте SQL, но параметр IF EXISTS является расширением QHB. Обратите внимание, что команда CREATE TYPE и механизмы расширения типов в QHB отличаются от стандарта SQL.

См. также

ALTER TYPE, CREATE TYPE

DROP USER MAPPING

DROP USER MAPPING — удалить сопоставление пользователей для стороннего сервера

Синтаксис

DROP USER MAPPING [ IF EXISTS ] FOR { имя_пользователя | USER | CURRENT_USER | PUBLIC } SERVER имя_сервера

Описание

Команда DROP USER MAPPING удаляет существующее сопоставление пользователя для стороннего сервера.

Владелец стороннего сервера может удалять заданные для этого сервера сопоставления любых пользователей. Кроме того, обычный пользователь может удалить сопоставление для собственного имени, если у него есть право USAGE для соответствующего сервера.

Параметры

IF EXISTS

Не считать ошибкой, если сопоставление не существует. В этом случае будет выдано соответствующее уведомление.

имя_пользователя

Имя пользователя для сопоставления. Значения CURRENT_USER и USER соответствуют имени текущего пользователя. Значение PUBLIC используется для сопоставления всех текущих и будущих имен пользователей в системе.

имя_сервера

Имя сервера, в котором задано сопоставление пользователей.

Примеры

Удаление сопоставления пользователя bob для сервера foo, если оно существует:

DROP USER MAPPING IF EXISTS FOR bob SERVER foo;

Совместимость

Команда DROP USER MAPPING соответствует стандарту ISO/IEC 9075-9 (SQL/MED). Предложение IF EXISTS является расширением QHB.

См. также

CREATE USER MAPPING, ALTER USER MAPPING

DROP USER

DROP USER — удалить роль в базе данных

Синтаксис

DROP USER [ IF EXISTS ] имя [, ...]

Описание

Команда DROP USER — это просто альтернативное написание команды DROP ROLE.

Совместимость

Команда DROP USER является расширением QHB. В стандарте SQL определение пользователей считается зависимым от реализации.

См. также

DROP ROLE

DROP VIEW

DROP VIEW — удалить представление

Синтаксис

DROP VIEW [ IF EXISTS ] имя [, ...] [ CASCADE | RESTRICT ]

Описание

Команда DROP VIEW удаляет существующее представление. Для выполнения этой команды нужно быть владельцем соответствующего представления.

Параметры

IF EXISTS

Не считать ошибкой, если представление не существует. В этом случае будет выдано соответствующее уведомление.

имя

Имя представления, подлежащего удалению (может быть дополнено схемой).

CASCADE

Автоматически удалять объекты, зависящие от представления (например, другие представления), и в свою очередь все объекты, зависящие от этих объектов (см. раздел Отслеживание зависимостей).

RESTRICT

Запретить удалять представление, если от него зависят какие-либо объекты. Это поведение по умолчанию.

Примеры

Эта команда удалит представление с именем kinds:

DROP VIEW kinds;

Совместимость

Команда DROP VIEW соответствует стандарту SQL, за исключением того, что стандарт позволяет удалять в одной команде только одно представление, а также параметра IF EXISTS, который является расширением QHB.

См. также

ALTER VIEW, CREATE VIEW

END

END — фиксация текущей транзакции

Синтаксис

END [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]

Описание

Команда END фиксирует текущую транзакцию. Все изменения, внесенные транзакцией, становятся видимыми для других и гарантированно будут сохранены, если произойдет сбой. Эта команда является расширением QHB и эквивалентна команде COMMIT.

Параметры

WORK
TRANSACTION

Необязательные ключевые слова. Не имеют эффекта.

AND CHAIN

Если указывается AND CHAIN, то сразу после окончания текущей транзакции начинается новая с теми же характеристиками (см. раздел SET TRANSACTION), что и только что завершенная. В противном случае новая транзакция не запускается.

Примечания

Для прерывания транзакции используйте команду ROLLBACK.

Попытка выполнить команду END вне транзакции не вызовет ошибки, но будет выдано предупреждающее сообщение.

Примеры

Фиксация текущей транзакции и сохранение всех изменений:

END;

Совместимость

Команда END является расширением QHB и предоставляет функциональные возможности, эквивалентные команде COMMIT, которые указаны в стандарте SQL.

См. также

BEGIN, COMMIT, ROLLBACK

EXECUTE

EXECUTE — выполнить подготовленный оператор

Синтаксис

EXECUTE имя [ ( параметр [, ...] ) ]

Описание

Команда EXECUTE используется для выполнения ранее подготовленного оператора. Поскольку подготовленные операторы существуют только в течение сеанса, они должны создаваться командой PREPARE, выполненной в текущем сеансе ранее.

Если в команде PREPARE, создавшей оператор, указаны некоторые параметры, то в команду EXECUTE должен быть передан совместимый набор параметров, иначе возникает ошибка. Обратите внимание, что (в отличие от функций) подготовленные операторы не перегружаются на основе типа или числа их параметров; имя подготовленного оператора должно быть уникальным в рамках текущего сеанса.

Дополнительную информацию о создании и использовании подготовленных операторов см. в разделе PREPARE.

Параметры

имя

Имя подготовленного оператора, подлежащего выполнению.

параметр

Фактическое значение параметра для подготовленного оператора. Это должно быть выражение, возвращающее значение, совместимое с типом данных этого параметра, как было определено при создании подготовленного оператора.

Выводимая информация

Метка команды, возвращаемая EXECUTE, соответствует подготовленному оператору, а не оператору EXECUTE.

Примеры

Примеры приведены в разделе «Примеры» документации по команде PREPARE.

Совместимость

Стандарт SQL включает команду EXECUTE, но она предназначена только для использования во встраиваемом SQL. Кроме того, данная версия команды EXECUTE имеет несколько другой синтаксис.

См. также

DEALLOCATE, PREPARE

EXPLAIN

EXPLAIN — показать план выполнения оператора

Синтаксис

EXPLAIN [ ( параметр [, ...] ) ] оператор
EXPLAIN [ ANALYZE ] [ VERBOSE ] оператор

Где параметр может быть:

    ANALYZE [ boolean ]
    VERBOSE [ boolean ]
    COSTS [ boolean ]
    SETTINGS [ boolean ]
    BUFFERS [ boolean ]
    WAL [ boolean ]
    TIMING [ boolean ]
    SUMMARY [ boolean ]
    FORMAT { TEXT | XML | JSON | YAML }

Описание

Команда EXPLAIN отображает план выполнения, который планировщик QHB создает для заданного оператора. План выполнения показывает, как таблица (или таблицы), на которую ссылается оператор, будет сканироваться: простым последовательным сканированием, индексным сканированием и т. д. — и если имеется ссылка на несколько таблиц, то какие алгоритмы объединения будут использоваться для объединения необходимых строк из каждой входной таблицы.

Наиболее важной частью выводимой информации является расчетная стоимость выполнения оператора, которая представляет собой предположение планировщика о том, сколько времени потребуется для выполнения оператора (это значение измеряется в единицах стоимости, которые не имеют точного определения, но обычно это обращение к странице на диске). На самом деле выводятся два числа: начальная стоимость до выдачи первой строки и общая стоимость выдачи всех строк. Для большинства запросов важна общая стоимость, но в таких контекстах, как подзапрос в EXISTS, планировщик выбирает вместо нее наименьшую стартовую стоимость (так как исполнение запроса всё равно завершится сразу после получения одной строки). Кроме того, если ограничить число строк предложением LIMIT, то планировщик делает соответствующую интерполяцию между двумя этими числами, выбирая наиболее выгодный план.

С параметром ANALYZE оператор будет выполнен, а не только запланирован. При этом в вывод добавляются фактические статистические данные времени выполнения, включая общее затраченное время в каждом узле плана (в миллисекундах) и общее количество строк, которые он фактически вернул. Это помогает проверить, близки ли оценки планировщика к реальности.

Важно!

Имейте в виду, что с указанием ANALYZE оператор действительно выполняется. Хотя EXPLAIN отбрасывает результат, который вернул бы SELECT, в остальном все действия выполняются как обычно. Если вы хотите выполнить EXPLAIN ANALYZE с командой INSERT, UPDATE, DELETE, CREATE TABLE AS или EXECUTE, не допуская изменения данных этой командой, используйте:

BEGIN;
EXPLAIN ANALYZE ...;
ROLLBACK;

Без скобок для этого оператора можно указать только параметры ANALYZE и VERBOSE, и только в таком порядке.

Параметры

ANALYZE

Выполнить команду и показать фактическое время выполнения и другую статистику. По умолчанию этот параметр имеет значение FALSE.

VERBOSE

Вывести дополнительную информацию о плане. В частности, включить список выходных столбцов для каждого узла в дереве плана, имена таблиц и функций, отвечающих требованиям схемы, всегда указывать для переменных в выражениях псевдоним их таблицы, а также выводить имена всех триггеров, для которых отображается статистика. По умолчанию этот параметр имеет значение FALSE.

COSTS

Вывести информацию о рассчитанной стоимости запуска и общей стоимости каждого узла плана, а также информацию о предполагаемом количестве строк и предполагаемой ширине каждой строки. По умолчанию этот параметр имеет значение TRUE.

SETTINGS

Вывести информацию о параметрах конфигурации. В частности, вывести параметры, влияющие на планирование запроса со значением, отличным от стандартного значения по умолчанию. По умолчанию этот параметр имеет значение FALSE.

BUFFERS

Вывести информацию об использовании буфера. В частности, вывести число попаданий, блоков прочитанных, загрязненных и записанных в разделяемом и локальном буфере, число прочитанных и записанных временных блоков, а также время, потраченное на чтение и запись блоков файлов данных (в миллисекундах), если включен track_io_timing. Попаданием (hit) считается ситуация, когда требуемый блок уже находится в кэше и чтения с диска удается избежать. Блоки в общем буфере содержат данные из обычных таблиц и индексов, в локальном — данные из временных таблиц и индексов, тогда как временные блоки предназначены для краткосрочного использования при выполнении сортировки, хеширования, материализации и тому подобных узлов плана. Количество загрязненных блоков (dirtied) показывает, сколько ранее не модифицированных блоков изменила данная операция; в то время как количество записанных блоков (written) показывает, сколько ранее загрязненных блоков вытеснило из кэша этим серверным модулем во время обработки запроса. Количество блоков, показанных для узла верхнего уровня, включает блоки, используемые всеми его дочерними узлами. В текстовом формате печатаются только ненулевые значения. По умолчанию имеет значение FALSE.

WAL

Вывести информацию о генерировании записей WAL. В частности, вывести число записей, число полных образов страниц (full page images, fpi) и число сгенерированных WAL в байтах. В текстовом формате печатаются только ненулевые значения. Этот параметр можно использовать только при включенном режиме ANALYZE. По умолчанию имеет значение FALSE.

TIMING

Вывести фактическое время запуска и время, затраченное в каждом узле на выходе. Накладные расходы многократного чтения системных часов могут значительно замедлить запрос в некоторых системах, поэтому когда требуется только фактическое количество строк, а не точное время, может быть полезно установить этот параметр в FALSE. Время выполнения всего оператора всегда измеряется, даже если синхронизация на уровне узла выключена с помощью этого параметра. Этот параметр можно использовать только при включенном режиме ANALYZE. По умолчанию имеет значение TRUE.

SUMMARY

Вывести сводку (например, суммарное время) после плана запроса. При использовании ANALYZE сводная информация выводится по умолчанию, но этот параметр позволяет получить ее и с другими вариантами команды. В EXPLAIN EXECUTE время планирования включает в себя время, требуемое для извлечения плана из кэша, и, при необходимости, время, требуемое для повторного планирования.

FORMAT

Установить формат вывода, который может быть TEXT, XML, JSON или YAML. Нетекстовые форматы содержат ту же информацию, что и TEXT, но больше подходят для программного разбора. По умолчанию этот параметр имеет значение TEXT.

boolean

Указывает, должен ли выбранный параметр быть включен или выключен. Для включения параметра можно написать: TRUE, ON или 1, а для выключения — FALSE, OFF или 0. Значение boolean также может быть опущено; в этом случае предполагается TRUE.

оператор

Любой оператор SELECT, INSERT, UPDATE, DELETE, VALUES, EXECUTE, DECLARE, CREATE TABLE AS или CREATE MATERIALIZED VIEW AS, план выполнения которого вы хотите видеть.

Выводимая информация

Результатом выполнения команды является текстовое описание выбранного для оператора плана, при необходимости дополненное статистикой выполнения. Раздел Использование EXPLAIN описывает предоставленную информацию.

Примечания

Чтобы планировщик запросов QHB мог принимать обоснованные решения при оптимизации запросов, данные в pg_statistic должны быть актуальными для всех таблиц, используемых в запросе. Обычно это автоматически обеспечивает Процесс «Автовакуум». Но если в содержимом таблицы недавно произошли существенные изменения, может потребоваться вручную выполнить ANALYZE, не дожидаясь, пока до них доберется автоочистка.

Чтобы измерить затраты времени на выполнение каждого узла в плане выполнения, текущая реализация EXPLAIN ANALYZE добавляет в выполнение запроса накладные расходы профилирования. В результате выполнение запроса с EXPLAIN ANALYZE иногда может занять значительно больше времени, чем обычное выполнение запроса. Объем накладных расходов зависит от характера запроса, а также от используемой платформы. Наихудший случай возникает в узлах плана, которые сами по себе требуют очень мало времени на выполнение, и в операционных системах, где получение текущего времени является относительно длительной операцией.

Примеры

Получение плана простого запроса для таблицы, содержащей единственный столбец типа integer и 10 000 строк:

EXPLAIN SELECT * FROM foo;

                       QUERY PLAN
---------------------------------------------------------
 Seq Scan on foo  (cost=0.00..155.00 rows=10000 width=4)
(1 row)

План того же запроса, но выведенный в формате JSON:

EXPLAIN (FORMAT JSON) SELECT * FROM foo;
           QUERY PLAN
--------------------------------
 [                             +
   {                           +
     "Plan": {                 +
       "Node Type": "Seq Scan",+
       "Relation Name": "foo", +
       "Alias": "foo",         +
       "Startup Cost": 0.00,   +
       "Total Cost": 155.00,   +
       "Plan Rows": 10000,     +
       "Plan Width": 4         +
     }                         +
   }                           +
 ]
(1 row)

Если в таблице есть индекс, а в запросе присутствует условие WHERE, для которого полезен этот индекс, EXPLAIN может показать другой план:

EXPLAIN SELECT * FROM foo WHERE i = 4;

                         QUERY PLAN
--------------------------------------------------------------
 Index Scan using fi on foo  (cost=0.00..5.98 rows=1 width=4)
   Index Cond: (i = 4)
(2 rows)

План того же запроса, но в формате YAML:

EXPLAIN (FORMAT YAML) SELECT * FROM foo WHERE i='4';
          QUERY PLAN
-------------------------------
 - Plan:                      +
     Node Type: "Index Scan"  +
     Scan Direction: "Forward"+
     Index Name: "fi"         +
     Relation Name: "foo"     +
     Alias: "foo"             +
     Startup Cost: 0.00       +
     Total Cost: 5.98         +
     Plan Rows: 1             +
     Plan Width: 4            +
     Index Cond: "(i = 4)"
(1 row)

Рассмотрение формата XML оставлено в качестве упражнения для читателя.

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

EXPLAIN (COSTS FALSE) SELECT * FROM foo WHERE i = 4;

        QUERY PLAN
----------------------------
 Index Scan using fi on foo
   Index Cond: (i = 4)
(2 rows)

Пример плана для запроса с агрегатной функцией:

EXPLAIN SELECT sum(i) FROM foo WHERE i < 10;

                             QUERY PLAN
---------------------------------------------------------------------
 Aggregate  (cost=23.93..23.93 rows=1 width=4)
   ->  Index Scan using fi on foo  (cost=0.00..23.92 rows=6 width=4)
         Index Cond: (i < 10)
(3 rows)

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

PREPARE query(int, int) AS SELECT sum(bar) FROM test
    WHERE id > $1 AND id < $2
    GROUP BY foo;

EXPLAIN ANALYZE EXECUTE query(100, 200);

                                                       QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=9.54..9.54 rows=1 width=8) (actual time=0.156..0.161 rows=11 loops=1)
   Group Key: foo
   ->  Index Scan using test_pkey on test  (cost=0.29..9.29 rows=50 width=8) (actual time=0.039..0.091 rows=99 loops=1)
         Index Cond: ((id > $1) AND (id < $2))
 Planning time: 0.197 ms
 Execution time: 0.225 ms
(6 rows)

Конечно, показанные здесь конкретные цифры зависят от фактического содержания соответствующих таблиц. Также обратите внимание, что номера и даже выбранная стратегия запроса могут отличаться в разных выпусках QHB из-за улучшений планировщика. Кроме того, команда ANALYZE использует случайную выборку для оценки статистики данных, поэтому оценки затрат могут измениться после нового запуска ANALYZE, даже если фактическое распределение данных в таблице не изменилось.

Совместимость

В стандарте SQL нет команды EXPLAIN.

См. также

ANALYZE

FETCH

FETCH — получить результат запроса через курсор

Синтаксис

FETCH [ направление [ FROM | IN ] ] имя_курсора

Где направление может быть не задано или принимать одно из значений:

    NEXT
    PRIOR
    FIRST
    LAST
    ABSOLUTE число
    RELATIVE число
    число
    ALL
    FORWARD
    FORWARD число
    FORWARD ALL
    BACKWARD
    BACKWARD число
    BACKWARD ALL

Описание

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

Курсор связан с определенным положением, что и использует FETCH. Положение курсора может быть перед первой строкой результата запроса, на любой конкретной строке результата или после последней строки результата. При создании курсор располагается перед первой строкой. После выборки нескольких строк курсор будет расположен на последней полученной строке. Если FETCH выполняется до конца доступных строк, то курсор остается расположенным после последней строки, если выборка выполняется назад, то перед первой строкой. Команды FETCH ALL и FETCH BACKWARD ALL всегда будут оставлять курсор после последней строки или перед первой строкой соответственно.

Формы NEXT, PRIOR, FIRST, LAST, ABSOLUTE и RELATIVE выбирают одну строку после соответствующего перемещения курсора. Если такой строки нет, то возвращается пустой результат и курсор остается расположенным перед первой строкой или после последней строки, в зависимости от ситуации.

Формы с использованием FORWARD и BACKWARD извлекают указанное число строк, сдвигаясь соответственно вперед или назад; в результате курсор оказывается на последней выданной строке (или перед/после всех строк, если число превышает количество доступных строк).

Формы RELATIVE 0, FORWARD 0 и BACKWARD 0 действуют одинаково — запрашивают выборку текущей строки без перемещения курсора, то есть повторно выбирая строку, которая была извлечена последней. Это произойдет успешно, если курсор не будет расположен перед первой строкой или после последней строки; в этом случае строка не возвращается.

Примечание

На этой странице описывается использование курсоров на уровне команд SQL. Если вы пытаетесь использовать курсоры внутри функции PL/pgSQL, правила будут отличаться — см. раздел Курсоры.

Параметры

направление

Параметр направление определяет направление движения и число выбираемых строк. Может быть один из следующих вариантов:

NEXT

Извлечь следующую строку. Это действие подразумевается по умолчанию, если направление опущено.

PRIOR

Извлечь предыдущую строку.

FIRST

Извлечь первую строку запроса (аналогично ABSOLUTE 1).

LAST

Извлечь последнюю строку запроса (аналогично ABSOLUTE -1).

ABSOLUTE число

Извлечь строку под номером число с начала либо под номером abs(число) с конца, если число отрицательно. Если число выходит за границы набора строк, курсор размещается перед первой или после последней строки; в частности, с ABSOLUTE 0 курсор оказывается перед первой строкой.

RELATIVE число

Извлечь строку под номером число, считая со следующей вперед, либо под номером abs(число), считая с предыдущей назад, если число отрицательно. RELATIVE 0 повторно считывает текущую строку, если таковая имеется.

число

Извлечь следующее число строк (аналогично FORWARD число).

ALL

Извлечь все оставшиеся строки (аналогично FORWARD ALL).

FORWARD

Извлечь следующую строку (аналогично NEXT).

FORWARD число

Извлечь следующее число строк. FORWARD 0 повторно выбирает текущую строку.

FORWARD ALL

Извлечь все оставшиеся строки.

BACKWARD

Извлечь предыдущую строку (аналогично PRIOR).

BACKWARD число

Извлечь предыдущее число строк (перемещение назад). BACKWARD 0 повторно извлекает текущую строку.

BACKWARD ALL

Извлечь все предыдущие строки (перемещение назад).

число

Здесь число — это целочисленная константа, возможно, со знаком, определяющая положение или количество выбираемых строк. Для вариантов *FORWARD и BACKWARD указание отрицательного числа равнозначно смене направления FORWARD на BACKWARD и наоборот.

имя_курсора

Имя открытого курсора.

Выводимая информация

При успешном выполнении FETCH возвращает метку команды вида

FETCH число

Где число — количество извлеченных строк (возможно, ноль). Обратите внимание, что в psql метка команды на самом деле не выдается, так как psql отображает вместо этого извлеченные строки.

Примечания

Если перемещение курсора в FETCH не ограничивается вариантами FETCH NEXT или FETCH FORWARD с положительным числом, курсор должен быть объявлен с указанием SCROLL. Для простых запросов QHB позволит выполнять обратное перемещение курсора, объявленного без SCROLL, но на такое поведение лучше не полагаться. Если курсор объявлен с указанием NO SCROLL, обратное перемещение не допускается.

Вариант ABSOLUTE нисколько не быстрее, чем переход к нужной строке с относительным перемещением: базовая реализация в любом случае должна пройти все промежуточные строки. Отрицательные абсолютные выборки еще хуже: запрос должен быть прочитан до конца, чтобы найти последнюю строку, а затем вернуться назад к указанной строке. Тем не менее, перемотка к началу запроса (FETCH ABSOLUTE 0) идет быстро.

Определить курсор позволяет команда DECLARE, а переместить его, не читая данные, — команда MOVE.

Примеры

Следующий пример демонстрирует перемещение курсора в таблице:

BEGIN WORK;

-- Создание курсора:
DECLARE liahona SCROLL CURSOR FOR SELECT * FROM films;

-- Получение первых 5 строк через курсор liahona:
FETCH FORWARD 5 FROM liahona;

 code  |          title          | did | date_prod  |   kind   |  len
-------+-------------------------+-----+------------+----------+-------
 BL101 | The Third Man           | 101 | 1949-12-23 | Drama    | 01:44
 BL102 | The African Queen       | 101 | 1951-08-11 | Romantic | 01:43
 JL201 | Une Femme est une Femme | 102 | 1961-03-12 | Romantic | 01:25
 P_301 | Vertigo                 | 103 | 1958-11-14 | Action   | 02:08
 P_302 | Becket                  | 103 | 1964-02-03 | Drama    | 02:28

-- Получение предыдущей строки:
FETCH PRIOR FROM liahona;

 code  |  title  | did | date_prod  |  kind  |  len
-------+---------+-----+------------+--------+-------
 P_301 | Vertigo | 103 | 1958-11-14 | Action | 02:08

-- Закрытие курсора и завершение транзакции:
CLOSE liahona;
COMMIT WORK;

Совместимость

Стандарт SQL определяет команду FETCH для использования только для встраиваемого SQL. Вариант FETCH, описанный здесь, возвращает данные подобно оператору SELECT, а не помещает их в переменные исполняющей среды. В остальном FETCH имеет полную прямую совместимость со стандартом SQL.

Формы FETCH с указанием FORWARD и BACKWARD, а также формы FETCH число и FETCH ALL (в которых FORWARD подразумевается) являются расширениями QHB.

В стандарте SQL перед именем курсора допускается только указание FROM; возможность указать IN или опустить оба указания является расширением.

См. также

CLOSE, DECLARE, MOVE

GRANT

GRANT — определить права доступа

Синтаксис

GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
    [, ...] | ALL [ PRIVILEGES ] }
    ON { [ TABLE ] имя_таблицы [, ...]
         | ALL TABLES IN SCHEMA имя_схемы [, ...] }
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT { { SELECT | INSERT | UPDATE | REFERENCES } ( имя_столбца [, ...] )
    [, ...] | ALL [ PRIVILEGES ] ( имя_столбца [, ...] ) }
    ON [ TABLE ] имя_таблицы [, ...]
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT { { USAGE | SELECT | UPDATE }
    [, ...] | ALL [ PRIVILEGES ] }
    ON { SEQUENCE имя_последовательности [, ...]
         | ALL SEQUENCES IN SCHEMA имя_схемы [, ...] }
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] }
    ON DATABASE имя_базы_данных [, ...]
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT { USAGE | ALL [ PRIVILEGES ] }
    ON DOMAIN имя_домена [, ...]
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT { USAGE | ALL [ PRIVILEGES ] }
    ON FOREIGN DATA WRAPPER имя_обертки_сторонних_данных [, ...]
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT { USAGE | ALL [ PRIVILEGES ] }
    ON FOREIGN SERVER имя_сервера [, ...]
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT { EXECUTE | ALL [ PRIVILEGES ] }
    ON { { FUNCTION | PROCEDURE | ROUTINE } имя_подпрограммы [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] [, ...]
         | ALL { FUNCTIONS | PROCEDURES | ROUTINES } IN SCHEMA имя_схемы [, ...] }
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT { USAGE | ALL [ PRIVILEGES ] }
    ON LANGUAGE имя_языка [, ...]
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT { { SELECT | UPDATE } [, ...] | ALL [ PRIVILEGES ] }
    ON LARGE OBJECT oid_БО [, ...]
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
    ON SCHEMA имя_схемы [, ...]
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT { CREATE | ALL [ PRIVILEGES ] }
    ON TABLESPACE имя_табличного_пространства [, ...]
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT { USAGE | ALL [ PRIVILEGES ] }
    ON TYPE имя_типа [, ...]
    TO указание_роли [, ...] [ WITH GRANT OPTION ]

GRANT имя_роли [, ...] TO указание_роли [, ...]
    [ WITH ADMIN OPTION ]
    [ GRANTED BY указание_роли ]

Где указание_роли:

    [ GROUP ] имя_роли
  | PUBLIC
  | CURRENT_USER
  | SESSION_USER

Описание

Команда GRANT имеет два основных варианта: один, предоставляющий права доступа к объектам базы данных (таблице, столбцу, представлению, сторонней таблице, последовательности, базе данных, обертке сторонних данных, стороннему серверу, функции, процедуре, процедурному языку, схеме или табличному пространству), и второй, предоставляющий членство в роли. Эти варианты во многом похожи, но и имеют достаточно отличий, чтобы быть описанными отдельно.

GRANT для объектов баз данных

Этот вариант команды GRANT предоставляет определенные права доступа к объекту базы данных для одной или нескольких ролей. Эти права добавляются к уже предоставленным, если таковые имеются.

Ключевое слово PUBLIC указывает, что права должны быть предоставлены всем ролям, включая те, что могут быть созданы позже. PUBLIC может рассматриваться как неявно определенная группа, которая всегда включает все роли. Любая конкретная роль будет иметь сумму прав, предоставленных непосредственно ей, прав, предоставленных любой роли, членом которой она в настоящее время является, и прав, предоставленных PUBLIC.

Если указано WITH GRANT OPTION, получатель права может в свою очередь предоставить его другим лицам. Без этого указания получатель не может этого сделать. Группе PUBLIC право передачи права дать нельзя.

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

Право удалить объект или каким-либо образом изменить его определение в качестве предоставляемого права не рассматривается; оно присуще владельцу и не может быть предоставлено или отозвано. (Однако аналогичный эффект можно получить, предоставив или отозвав членство в роли, которой принадлежит объект; см. ниже.) Также владелец неявно имеет право распоряжения всеми правами для своего объекта.

Возможные права:

SELECT

INSERT

UPDATE

DELETE

TRUNCATE

REFERENCES

TRIGGER

CREATE

CONNECT

TEMPORARY

EXECUTE

USAGE

Особые типы прав, определенные в разделе Права.

TEMP

Альтернативное написание для TEMPORARY.

ALL PRIVILEGES

Предоставление всех права доступа, применимых для типа объекта. В QHB ключевое слово PRIVILEGES является необязательным, хотя оно и обязательно в стандарте SQL.

Синтаксис FUNCTION распространяется для простых, агрегатных и оконных функций, но не для процедур; для последних предназначен синтаксис PROCEDURE. В качестве альтернативы используйте ROUTINE, чтобы ссылаться на функцию, агрегатную функцию, оконную функцию или процедуру, независимо от ее точного типа.

Существует также возможность предоставления прав на все объекты одного типа в рамках одной или нескольких схем. В настоящее время эта функция поддерживается только для таблиц, последовательностей, функций и процедур. ALL TABLES также влияет на представления и сторонние таблицы, аналогично команде GRANT для конкретного объекта. ALL FUNCTIONS также влияет на агрегатные и оконные функции, но не на процедуры — опять же аналогично GRANT для конкретного объекта. Чтобы включить процедуры, воспользуйтесь формой ALL ROUTINES.

GRANT для ролей

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

Участник, получивший членство в роли с указанием WITH ADMIN OPTION, может, в свою очередь, предоставлять членство в роли другим пользователям, а также отменять членство в роли. Без WITH ADMIN OPTION обычные пользователи не могут этого сделать. Роль не имеет права WITH ADMIN OPTION для самой себя, но может предоставить или отозвать членство в роли в сеансе базы данных, в котором пользователь сеанса соответствует роли. Суперпользователи баз данных могут предоставлять или отменять членство в любой роли кому угодно. Роли с правом CREATEROLE могут предоставлять или отменять членство в любой роли, которая не является суперпользователем.

При указании GRANTED BY предоставление права записывается как совершенное конкретной ролью. Этой возможностью могут пользоваться только суперпользователи, за исключением тех случаев, когда она называет ту же роль при выполнении команды.

В отличие от прав, членство в роли не может быть предоставлено группе PUBLIC. Обратите также внимание, что эта форма команды не допускает избыточное слово GROUP в указании_роли.

Примечания

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

Понятия пользователей и групп были объединены в единый вид сущности, называемый ролью. Поэтому больше нет необходимости использовать ключевое слово GROUP, чтобы определить, является ли участник пользователем или группой. Слово GROUP по-прежнему разрешено в команде, но лишено смысловой нагрузки.

Пользователь может выполнить команды SELECT, INSERT и подобные им со столбцом таблицы, если обладает этим правом для конкретного столбца или для всей таблицы. Предоставление права на уровне таблицы, а затем отмена права для одного из столбцов этой таблицы не даст желаемого эффекта: операция с правами на уровне столбцов не затронет право на уровне таблицы.

Если пользователь, не являющийся владельцем объекта, попытается назначить право доступа к объекту (с помощью GRANT), то эта команда завершится ошибкой, если у пользователя нет никаких прав доступа к объекту. Если же пользователь имеет некоторые права, команда будет выполнена, но предоставит только те права, которые даны ему с правом передачи. Формы GRANT ALL PRIVILEGES выдадут предупреждающее сообщение, если у пользователя вообще нет никаких прав передачи, тогда как другие формы выдадут предупреждение, если пользователь не имеет прав распоряжаться конкретными правами, указанными в команде. (В принципе, эти утверждения применимы и к владельцу объекта, но поскольку владелец всегда рассматривается как обладатель всех прав, такие ситуации невозможны.)

Следует отметить, что суперпользователи баз данных могут обращаться ко всем объектам, независимо от настроек прав объекта. Это сравнимо с правами пользователя root в системе Unix. Как и в случае с root, неразумно работать под суперпользователем, за исключением тех случаев, когда это абсолютно необходимо.

Если суперпользователь решает выполнить команду GRANT или REVOKE, команда выполняется так, как если бы она выполнялась владельцем объекта воздействия. В частности, права, предоставленные с помощью такой команды, проявятся как права, назначенные владельцем объекта. (Для членства в роли членство будет представлено как назначенное самой ролью.)

GRANT и REVOKE также могут выполняться ролью, которая не является владельцем объекта воздействия, но является членом роли, которая владеет объектом, или членом роли, которая обладает правами доступа WITH GRANT OPTION для этого объекта. В этом случае права будут записаны как предоставленные ролью, которая фактически владеет объектом или владеет правами доступа WITH GRANT OPTION. Например, если таблица t1 принадлежит роли g1, членом которой является u1, то u1 может предоставлять права на t1 роли u2, но эти права будут предоставлены как назначенные непосредственно ролью g1. Любой другой член роли g1 сможет позже отозвать их.

Если роль, выполняющая команду GRANT, получила на это необходимые права косвенно через несколько путей членства в роли, то не будет определено, какая именно роль назначила право. В таких случаях рекомендуется использовать SET ROLE, чтобы переключиться на конкретную роль, которую вы хотите назначить в качестве выполняющей команду GRANT.

Предоставление прав на таблицу не означает автоматического расширения прав на любые используемые таблицей последовательности, включая те из них, что связаны со столбцами SERIAL. Разрешения на последовательности должны быть установлены отдельно.

Дополнительную информацию о конкретных типах прав, а также о том, как просмотреть права объектов, см. в разделе Права.

Примеры

Разрешить всем добавлять записи в таблицу films:

GRANT INSERT ON films TO PUBLIC;

Дать пользователю manuel все права для представления kinds:

GRANT ALL PRIVILEGES ON kinds TO manuel;

Учтите, что если ее выполнит суперпользователь или владелец представления kinds, эта команда действительно даст субъекту все права, но если ее выполнит обычный пользователь, субъект получит только те права, которые даны этому пользователю с правом передачи.

Включение в роль admins пользователя joe:

GRANT admins TO joe;

Совместимость

Согласно стандарту SQL, слово PRIVILEGES в указании ALL PRIVILEGES является обязательным. Стандарт SQL не поддерживает установку прав для более чем одного объекта на команду.

QHB позволяет владельцу объекта отозвать свои обычные права: например, владелец таблицы может сделать таблицу доступной только для чтения для себя, отозвав свои собственные права INSERT, UPDATE, DELETE и TRUNCATE. В соответствии со стандартом SQL, это невозможно. Причина заключается в том, что QHB рассматривает права владельца как предоставленные им самому себе, поэтому владелец также может и отозвать их. В стандарте SQL права владельца предоставляются предполагаемой сущностью «_SYSTEM». Так как владелец объекта отличается от «_SYSTEM», лишить себя этих прав он не может.

В соответствии со стандартом SQL, право с правом передачи можно дать субъекту PUBLIC; QHB поддерживает его только при предоставлении ролям права передачи прав.

Стандарт SQL позволяет использовать указание GRANTED BY во всех формах GRANT. QHB поддерживает его только при назначении членства ролей, и даже в этом случае только суперпользователи могут использовать его нетривиальным образом.

Стандарт SQL предусматривает распространение права USAGE на другие виды объектов: наборы символов, правила сортировки, переводы, преобразования.

В стандарте SQL последовательности имеют лишь право USAGE, которое контролирует использование выражения NEXT VALUE FOR, эквивалентное функции nextval в QHB. Права SELECT и UPDATE для последовательностей являются расширениями QHB. Применение права USAGE для последовательностей к функции currval также относится к расширениям QHB (как и сама функция).

Права доступа к базам данных, табличным пространствам, схемам и языкам являются расширениями QHB.

См. также

REVOKE, ALTER DEFAULT PRIVILEGES

IMPORT FOREIGN SCHEMA

IMPORT FOREIGN SCHEMA — импортировать определения таблиц со стороннего сервера

Синтаксис

IMPORT FOREIGN SCHEMA удаленная_схема
    [ { LIMIT TO | EXCEPT } ( имя_таблицы [, ...] ) ]
    FROM SERVER имя_сервера
    INTO локальная_схема
    [ OPTIONS ( параметр 'значение' [, ... ] ) ]

Описание

Команда IMPORT FOREIGN SCHEMA создает сторонние таблицы, которые представляют таблицы, существующие на стороннем сервере. Новые сторонние таблицы будут принадлежать пользователю, выполнившему команду, и будут созданы с правильными определениями столбцов и параметров, соответствующих удаленным таблицам.

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

Чтобы использовать IMPORT FOREIGN SCHEMA, необходимо иметь право USAGE на стороннем сервере, а также право CREATE в целевой схеме.

Параметры

удаленная_схема

Удаленная схема, предназначенная для импорта из нее. Что именно представляет собой удаленная схема, зависит от применяемой обертки сторонних данных.

LIMIT TO ( имя_таблицы [, ...] )

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

EXCEPT ( имя_таблицы [, ...] )

Исключить указанные сторонние таблицы из импорта. Все существующие в сторонней схеме таблицы, за исключением перечисленных, будут импортированы.

имя_сервера

Сторонний сервер, предназначенный для импорта из него.

локальная_схема

Схема, в которой будут созданы импортируемые сторонние таблицы.

OPTIONS ( параметр 'значение' [, ...] )

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

Примеры

Импорт определений таблиц из удаленной схемы foreign_films на сервере film_server с созданием сторонних таблиц в локальной схеме films:

IMPORT FOREIGN SCHEMA foreign_films
    FROM SERVER film_server INTO films;

Та же операция, но импортируются только таблицы actors и directors (если они существуют):

IMPORT FOREIGN SCHEMA foreign_films LIMIT TO (actors, directors)
    FROM SERVER film_server INTO films;

Совместимость

Команда IMPORT FOREIGN SCHEMA соответствует стандарту SQL, за исключением параметра OPTIONS, являющегося расширением QHB.

См. также

CREATE FOREIGN TABLE, CREATE SERVER

INSERT

INSERT — добавить новые строки в таблицу

Синтаксис

[ WITH [ RECURSIVE ] запрос_WITH [, ...] ]
INSERT INTO имя_таблицы [ AS псевдоним ] [ ( имя_столбца [, ...] ) ]
    [ OVERRIDING { SYSTEM | USER } VALUE ]
    { DEFAULT VALUES | VALUES ( { выражение | DEFAULT } [, ...] ) [, ...] | запрос }
    [ ON CONFLICT [ объект_конфликта ] действие_при_конфликте ]
    [ RETURNING * | выражение_результата [ [ AS ] имя_результата ] [, ...] ]

где объект_конфликта может быть:

    ( { имя_столбца_индекса | ( выражение_индекса ) } [ COLLATE правило_сортировки ] [ класс_операторов ] [, ...] ) [ WHERE предикат_индекса ]
    ON CONSTRAINT имя_ограничения

и действие_при_конфликте может быть:

    DO NOTHING
    DO UPDATE SET { имя_столбца = { выражение | DEFAULT } |
                    ( имя_столбца [, ...] ) = [ ROW ] ( { выражение | DEFAULT } [, ...] ) |
                    ( имя_столбца [, ...] ) = ( вложенный_SELECT )
                  } [, ...]
              [ WHERE условие ]

Описание

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

Имена целевых столбцов могут быть перечислены в любом порядке. Если список имен столбцов вообще не задан, то по умолчанию используются все столбцы таблицы в их объявленном порядке; либо первые N из них, если от предложения VALUES или запроса поступает только N столбцов. Значения, получаемые от предложения VALUES или запроса, связываются с явно или неявно определенным списком столбцов слева направо.

Каждый столбец, не присутствующий в явном или неявном списке столбцов, будет заполнен значением по умолчанию, если оно задано, либо, в противном случае, значением NULL.

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

Можно использовать предложение ON CONFLICT для указания альтернативного действия, заменяющего возникновение ошибки при нарушении ограничения уникальности или ограничения-исключения. (См. Предложение ON CONFLICT ниже).

С необязательным предложением RETURNING команда INSERT вычислит и возвратит значения каждой строки, фактически добавленной (или измененной, если было использовано предложение ON CONFLICT DO UPDATE). Это в первую очередь полезно для получения значений, присвоенных по умолчанию, таких как последовательный номер записи. Однако в этом предложении можно задать любое выражение со столбцами таблицы. Список RETURNING идентичен синтаксису списка результатов SELECT. Будут возвращены только строки, которые были успешно добавлены или изменены. Например, если строка была заблокирована, но не изменена из-за того, что условие в предложении ON CONFLICT DO UPDATE ... WHERE не удовлетворено, строка не будет возвращена.

Чтобы добавлять строки в таблицу, необходимо иметь для нее право INSERT. Если присутствует предложение ON CONFLICT DO UPDATE, для такой таблицы также потребуется право UPDATE.

Если указан список столбцов, достаточно иметь право INSERT только для перечисленных столбцов. По аналогии предложению ON CONFLICT DO UPDATE требуется право UPDATE только для столбца (или столбцов), который будет обновлен. Однако помимо этого предложение ON CONFLICT DO UPDATE требует наличия права SELECT для всех столбцов, значения которых считываются в выражениях ON CONFLICT DO UPDATE или в условии.

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

Параметры

Добавление

В этом разделе описываются параметры, которые могут использоваться только при добавлении новых строк. Параметры, используемые исключительно с предложением ON CONFLICT, описываются отдельно.

запрос_WITH

Предложение WITH позволяет указать один или несколько вложенных запросов, на которые можно ссылаться по имени в запросе INSERT. Дополнительную информацию см. в разделах Запросы WITH и SELECT.

Сам заданный запрос (оператор SELECT) также может содержать предложение WITH. В этом случае в запросе можно обращаться к обоим наборам запрос_WITH, но у второго будет приоритет, так как он вложен ближе.

имя_таблицы

Имя существующей таблицы (может быть дополнено схемой).

псевдоним

Альтернативное имя для имени_таблицы. При указании псевдонима тот полностью скрывает фактическое имя таблицы. Это особенно полезно, когда в предложении ON CONFLICT DO UPDATE фигурирует таблица с именем excluded, поскольку в противном случае это имя будет присвоено специальной таблице, представляющей строки, которые предназначены для добавления.

имя_столбца

Имя столбца в таблице с именем имя_таблицы. При необходимости имя столбца может быть дополнено именем вложенного поля или индексом в массиве. (Когда данные добавляются только в некоторые поля столбца составного типа, в другие поля записывается значение NULL.) Обращаясь к столбцу в предложении ON CONFLICT DO UPDATE, не следует включать в ссылку на целевой столбец имя таблицы. Например, запись INSERT INTO table_name ... ON CONFLICT DO UPDATE SET table_name.col = 1 недопустима (это соответствует общему поведению команды UPDATE ).

OVERRIDING SYSTEM VALUE

При указании этого предложения все значения, предоставляемые для столбцов идентификации, будут переопределять значения, выдаваемые последовательностью по умолчанию.

Для столбца идентификации, определенного как GENERATED ALWAYS, добавление явного значения (кроме DEFAULT) является ошибкой, если не указано OVERRIDING SYSTEM VALUE или OVERRIDING USER VALUE. (Для столбца идентификации, определенного как GENERATED BY DEFAULT, OVERRIDING SYSTEM VALUE является обычным поведением, поэтому его указание ни на что не влияет, но QHB допускает его в качестве расширения.)

OVERRIDING USER VALUE

При указании этого предложения игнорируются любые значения, предоставляемые для столбцов идентификаторов, и применяются значения, выдаваемые последовательностью по умолчанию.

Это предложение полезно, например, при копировании значений между таблицами. Команда INSERT INTO tbl2 OVERRIDING USER VALUE SELECT \* FROM tbl1 скопирует из tbl1 все столбцы, которые не являются столбцами идентификаторами в tbl2, тогда как значения для столбцов идентификаторов в tbl2 будут генерироваться последовательностями, связанными с tbl2.

DEFAULT VALUES

Все столбцы будут заполнены значениями по умолчанию, как при для явном указании DEFAULT для каждого столбца. (Предложение OVERRIDING в этой форме не допускается.)

выражение

Выражение или значение для присвоения соответствующему столбцу.

DEFAULT

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

запрос

Запрос (оператор SELECT), который выдаст строки для добавления в таблицу. Описание его синтаксиса см. в разделе SELECT.

выражение_результата

Выражение, вычисляемое и возвращаемое командой INSERT после каждой строки, которая добавляется или изменяется. В этом выражении могут использоваться любые имена столбцов таблицы с именем имя_таблицы. Чтобы вернуть все столбцы добавленных или измененных строк, достаточно написать *.

имя_результата

Имя, используемое для возвращаемого столбца.

Предложение ON CONFLICT

Необязательное предложение ON CONFLICT указывает альтернативное действие, заменяющее возникновение ошибки при нарушении ограничения уникальности или ограничения-исключения. Для каждой отдельной предложенной для добавления строки добавление либо продолжается, либо, если нарушается решающее ограничение или индекс, указанный как объект_конфликта, выполняется альтернативное действие_при_конфликте. Вариант ON CONFLICT DO NOTHING в качестве альтернативного действия просто отменяет добавление строки. Вариант ON CONFLICT DO UPDATE обновляет существующую строку, которая конфликтует со строкой, предложенной для добавления.

Задаваемый объект_конфликта может выбирать уникальный индекс. Определение объекта, позволяющее выбрать индекс, включает один или несколько столбцов (их определяет имя_столбца_индекса) и/или выражение_индекса и необязательный предикат_индекса. В качестве решающих индексов выводятся (выбираются) все уникальные индексы в таблице имя_таблицы, которые, без учета порядка столбцов, содержат именно те столбцы/выражения, что определяют объект_конфликта. Если указывается предикат_индекса, он должен, в качестве дополнительного требования выбора, удовлетворять уникальным индексам. Обратите внимание: это означает, что не частичный уникальный индекс (уникальный индекс без предиката) будет выбран (и, следовательно, будет использован в ON CONFLICT), если он удовлетворяет всем остальным критериям. Если попытка выбора неудачна, выдается ошибка.

ON CONFLICT DO UPDATE гарантирует атомарный результат команды INSERT или UPDATE; при отсутствии внешней ошибки даже при высокой параллельной активности гарантируется один из этих двух результатов. Эта операция также известна как UPSERT — «UPDATE или INSERT».

объект_конфликта

Указывает, какие именно конфликты в ON CONFLICT будут решаться альтернативным действием, устанавливая решающие индексы. Это указание позволяет выполнять выбор уникального индекса либо явно задает имя ограничения. Для ON CONFLICT DO NOTHING необязательно указывать объект_конфликта; в этом случае игнорироваться будут все конфликты с любыми ограничениями (и уникальными индексами). Для ON CONFLICT DO UPDATE объект_конфликта должен указываться.

действие_при_конфликте

Параметр действие_при_конфликте задает альтернативное действие в случае конфликта. Это может быть предложение DO NOTHING (не делать ничего) или DO UPDATE (произвести обновление), где указываются точные детали операции UPDATE, выполняемой в случае возникновения конфликта. Предложения SET и WHERE в ON CONFLICT DO UPDATE могут обращаться к существующей строке с использованием имени таблицы (или псевдонима), а также к строкам, предложенным для добавления с помощью специальной таблицы excluded. Для чтения столбцов excluded необходимо иметь право SELECT для соответствующих столбцов в целевой таблице.

Обратите внимание, что эффекты действий всех триггеров уровня строк BEFORE INSERT отражаются в значениях excluded, так как эти эффекты могут привести к исключению строки из добавления.

имя_столбца_индекса

Имя столбца в таблице имя_таблицы. Используется для выбора решающих индексов. Задается в формате CREATE INDEX. Чтобы запрос выполнился, для столбца имя_столбца_индекса требуется право SELECT.

выражение_индекса

Подобно указанию имя_столбца_индекса, но используется для выбора индекса по выражениям со столбцами таблицы имя_таблицы, обозначенными в определениях индексов (а не по простым столбцам). Задается в формате CREATE INDEX. Для всех столбцов, к которым обращается выражение_индекса, требуется право SELECT.

правило_сортировки

При указании устанавливает, что соответствующий столбец имя_столбца_индекса или выражение_индекса должен использовать определенный порядок сортировки для сопоставления во время выбора индекса. Обычно это указание опущено, так как правила сортировки в большинстве случаев не влияют на то, произойдет ли нарушение ограничений. Задается в формате CREATE INDEX.

класс_операторов

При указании устанавливает, что соответствующий столбец имя_столбца_индекса или выражение_индекса должен использовать определенный класс оператора для сопоставления во время выбора индекса. Обычно это указание опущено, поскольку семантика равенства все равно часто эквивалентна в разных классах операторов типа либо поскольку достаточно полагаться на то, что заданные уникальные индексы имеют адекватное определение равенства. Задается в формате CREATE INDEX.

предикат_индекса

Используется для разрешения выбора частичных уникальных индексов. Могут быть выбраны любые индексы, удовлетворяющие предикату (причем в действительности им необязательно быть частичными индексами). Задается в формате CREATE INDEX. Для всех столбцов, задействованных в предикате_индекса, требуется право SELECT.

имя_ограничения

Явно указывает решающее ограничение по имени, что заменяет неявный выбор ограничения или индекса.

условие

Выражение, которое выдает значение типа boolean. Будут обновлены только те строки, для которых это выражение выдает true, хотя при выборе операции ON CONFLICT DO UPDATE все строки будут заблокированы. Обратите внимание, что условие оценивается последним, после определения конфликта в качестве кандидата на обновление.

Примите к сведению, что ограничения-исключения не могут быть решающими в ON CONFLICT DO UPDATE. Во всех случаях в качестве решающих поддерживаются только неоткладываемые (NOT DEFERRABLE) ограничения и уникальные индексы.

Команда INSERT с предложением ON CONFLICT DO UPDATE является «детерминированной». Это означает, что команде не разрешено влиять на одну существующую строку более одного раза; в случае такой ситуации возникнет ошибка нарушения мощности множества. Строки, предлагаемые для добавления, не должны дублировать друг друга с точки зрения атрибутов, ограниченных решающим индексом или ограничением.

Обратите внимание, что в настоящее время это не работает для предложения ON CONFLICT DO UPDATE команды INSERT, применяемой к партиционированной таблице с целью обновления ключа разбиения в конфликтующей строке таким образом, что эта строка должна быть перенесена в новую партицию.

Совет
Часто предпочтительнее вместо непосредственного указания ограничения в виде ON CONFLICT ON CONSTRAINT имя_ограничения использовать неявный выбор уникального индекса. Выбор продолжит работать правильно, когда нижележащий индекс будет заменен другим более или менее эквивалентным индексом методом наложения, например при использовании CREATE UNIQUE INDEX ... CONCURRENTLY с последующим удалением заменяемого индекса.

Выводимая информация

После успешного завершения команда INSERT возвращает метку команды в виде

INSERT oid число

Здесь число представляет количество добавленных или обновленных строк. Поле oid всегда содержит 0 (раньше это был OID, назначенный добавленной строке, если число равнялось 1 и целевая таблица была объявлена указанием WITH OIDS, и 0 в противном случае, но создание таблицы с характеристикой WITH OIDS больше не поддерживается).

Если команда INSERT содержит предложение RETURNING, результат будет похож на результат команды SELECT (с теми же столбцами и значениями, что содержатся в списке RETURNING), полученный для строк, добавленных или обновленных этой командой.

Примечания

Если указанная таблица является партиционированной таблицей, то каждая строка направляется в соответствующую партицию и добавляется в нее. Если указанная таблица является партицией и какая-либо из входных строк нарушает ограничение этой партиции, происходит ошибка.

Примеры

Добавление одной строки в таблицу films:

INSERT INTO films VALUES
    ('UA502', 'Bananas', 105, '1971-07-13', 'Comedy', '82 minutes');

В этом примере столбец len опускается и, как следствие, получает значение по умолчанию:

INSERT INTO films (code, title, did, date_prod, kind)
    VALUES ('T_601', 'Yojimbo', 106, '1961-06-16', 'Drama');

В этом примере для столбца с датой задается указание DEFAULT, а не явное значение:

INSERT INTO films VALUES
    ('UA502', 'Bananas', 105, DEFAULT, 'Comedy', '82 minutes');
INSERT INTO films (code, title, did, date_prod, kind)
    VALUES ('T_601', 'Yojimbo', 106, DEFAULT, 'Drama');

Добавление строки, полностью состоящей из значений по умолчанию:

INSERT INTO films DEFAULT VALUES;

Добавление нескольких строк с использованием многострочного синтаксиса VALUES:

INSERT INTO films (code, title, did, date_prod, kind) VALUES
    ('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
    ('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy');

В этом примере в таблицу films добавляются некоторые строки из таблицы tmp_films, имеющей ту же структуру столбцов, что и films:

INSERT INTO films SELECT * FROM tmp_films WHERE date_prod < '2004-05-07';

Этот пример демонстрирует добавление данных в столбцы с типом массива:

-- Создание пустого поля 3x3 для игры в крестики-нолики
INSERT INTO tictactoe (game, board[1:3][1:3])
    VALUES (1, '{{" "," "," "},{" "," "," "},{" "," "," "}}');
-- Указания индексов в предыдущей команде могут быть опущены
INSERT INTO tictactoe (game, board)
    VALUES (2, '{{X," "," "},{" ",O," "},{" ",X," "}}');

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

INSERT INTO distributors (did, dname) VALUES (DEFAULT, 'XYZ Widgets')
   RETURNING did;

Увеличение счетчика продаж для продавца, занимающегося компанией Acme Corporation, и сохранение всей обновленной строки вместе с текущим временем в таблице журнала:

WITH upd AS (
  UPDATE employees SET sales_count = sales_count + 1 WHERE id =
    (SELECT sales_person FROM accounts WHERE name = 'Acme Corporation')
    RETURNING *
)
INSERT INTO employees_log SELECT *, current_timestamp FROM upd;

Добавить дистрибьюторов или обновить существующие данные должным образом. Предполагается, что в таблице определен уникальный индекс, ограничивающий значения в столбце did. Обратите внимание, что для обращения к значениям, изначально предлагаемым для добавления, используется специальная таблица excluded:

INSERT INTO distributors (did, dname)
    VALUES (5, 'Gizmo Transglobal'), (6, 'Associated Computing, Inc')
    ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname;

Добавить дистрибьютора или не делать ничего для строк, предложенных для добавления, если уже есть существующая исключающая строка (строка, содержащая конфликтующие значения в столбце или столбцах после срабатывания триггеров перед добавлением строки). В данном примере предполагается, что определен уникальный индекс, ограничивающий значения в столбце did:

INSERT INTO distributors (did, dname) VALUES (7, 'Redline GmbH')
    ON CONFLICT (did) DO NOTHING;

Добавить дистрибьюторов или обновить существующие данные должным образом. В данном примере предполагается, что в таблице определен уникальный индекс, ограничивающий значения в столбце did. Предложение WHERE позволяет ограничить набор фактически обновляемых строк (однако любая существующая строка, не подлежащая обновлению, всё равно будет заблокирована):

-- Не менять данные существующих дистрибьюторов в зависимости от почтового индекса
INSERT INTO distributors AS d (did, dname) VALUES (8, 'Anvil Distribution')
    ON CONFLICT (did) DO UPDATE
    SET dname = EXCLUDED.dname || ' (formerly ' || d.dname || ')'
    WHERE d.zipcode <> '21201';

-- Указать имя ограничения непосредственно в операторе (связанный индекс
-- применяется для принятия решения о выполнении действия DO NOTHING)
INSERT INTO distributors (did, dname) VALUES (9, 'Antwerp Design')
    ON CONFLICT ON CONSTRAINT distributors_pkey DO NOTHING;

Добавить дистрибьютора, если возможно; в противном случае не делать ничего (DO NOTHING). В данном примере предполагается, что в таблице определен уникальный индекс, ограничивающий значения в столбце did по подмножеству строк, в котором логический столбец is_active содержит true:

-- Этот оператор может выбрать частичный уникальный индекс по "did"
-- с предикатом "WHERE is_active", а может, просто использовать
-- обычное ограничение уникальности по столбцу "did"
INSERT INTO distributors (did, dname) VALUES (10, 'Conrad International')
    ON CONFLICT (did) WHERE is_active DO NOTHING;

Совместимость

Команда INSERT соответствует стандарту SQL, за исключением того, что предложение RETURNING является расширением QHB, как и возможность использовать WITH с INSERT, а также возможность указать альтернативное действие с помощью ON CONFLICT. Кроме того, ситуация, при которой список имен столбцов опущен, но не все столбцы получают значения из предложения VALUES или запроса, стандартом не допускается.

Стандарт SQL определяет, что предложение OVERRIDING SYSTEM VALUE может быть указано только в том случае, если существует столбец идентификации, для которого всегда генерируется значение. QHB разрешает это предложение в любом случае и игнорирует его, если оно не применимо.

Возможные ограничения по использованию предложения запрос описаны в разделе SELECT.

LISTEN

LISTEN — прослушивать уведомления

Синтаксис

LISTEN канал

Описание

Команда LISTEN регистрирует текущий сеанс в качестве прослушивателя на канале уведомлений с именем канал. Если текущий сеанс уже зарегистрирован в качестве прослушивателя для этого канала уведомлений, ничего не происходит.

Всякий раз, когда вызывается команда NOTIFY канал (в этом сеансе или в другом, подключенном к той же базе данных), все сеансы, которые в настоящий момент прослушивают этот канал уведомлений, получают уведомление и в свою очередь передают его своему подключенному клиентскому приложению.

Сеанс может быть отменен для данного канала уведомлений с помощью команды UNLISTEN. Регистрация прослушивания канала автоматически очищается по окончании сеанса.

Метод, который клиентское приложение должно использовать для обнаружения событий уведомления, зависит от того, какой программный интерфейс приложения QHB оно использует. С библиотекой libpq приложение выполняет команду LISTEN как обычную команду SQL, а затем должно периодически вызывать функцию PQnotifies, чтобы узнать, были ли получены какие-либо уведомления. Другие интерфейсы, например libpgtcl, предоставляют более высокоуровневые методы для обработки событий уведомлений; более того, с libpgtcl программисту приложения даже не нужно запускать LISTEN или UNLISTEN напрямую. Дополнительную информацию см. в документации по используемому интерфейсу.

Параметры

канал

Наименование канала уведомления (любой идентификатор).

Примечания

Команда LISTEN вступает в силу при фиксации транзакции. Если LISTEN или UNLISTEN выполняется в транзакции, которая позже откатывается, набор прослушиваемых каналов уведомлений остается неизменным.

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

При первой настройке сеанса на прослушивание существует условие гонки: если параллельно фиксирующиеся транзакции посылают уведомления о событии, какое из них получит свежеподписавшийся сеанс? Ответ: сеанс получит все события, зафиксированные после момента фиксации транзакции, регистрирующей подписку. Но эти уведомления будут отражать состояния базы данных с некоторым запозданием по сравнению с тем, которое эта транзакция могла бы наблюдать в своих запросах. Это приводит к следующему правилу использования LISTEN: сначала выполнить (и зафиксировать!) эту команду, затем в новой транзакции проверить состояние базы данных согласно требованиям логики приложения, затем воспользоваться уведомлениями для выявления последующих изменений состояния базы данных. Первые несколько уведомлений могут относиться к изменениям, которые уже наблюдались при исходной проверке базы данных, но обычно в этом нет вреда.

В разделе NOTIFY можно найти более подробный разбор использования команд LISTEN and NOTIFY.

Примеры

Настройка и выполнение последовательности listen/notify от psql:

LISTEN virtual;
NOTIFY virtual;
Asynchronous notification "virtual" received from server process with PID 8448.

Совместимость

В стандарте SQL нет команды LISTEN.

См. также

NOTIFY, UNLISTEN

LOAD

LOAD — загрузить файл разделяемой библиотеки

Синтаксис

LOAD 'имя_файла'

Описание

Команда LOAD загружает файл разделяемой библиотеки в адресное пространство сервера QHB. Если файл уже загружен, команда ничего не делает. Файлы разделяемой библиотеки, содержащие функции на C, автоматически загружаются при первом вызове одной из их функций. Поэтому явно выполнять LOAD обычно требуется только для загрузки библиотек, которые изменяют поведение сервера, внедряя свои обработчики, а не предоставляют набор функций.

Имя файла библиотеки обычно задается как простое имя файла, которое ищется в пути поиска библиотек сервера (задается параметром dynamic_library_path). Кроме того, он может быть задан в качестве полного имени пути. В любом случае стандартное расширение имени файла разделяемой библиотеки платформы может быть опущено.

Не суперпользователи могут применять LOAD только для загрузки файлов библиотек, расположенных в $libdir/plugins/ — указанное имя_файла должно начинаться именно с этой строки. (Ответственность за установку только «безопасных» библиотек лежит на администраторе базы данных.)

Совместимость

LOAD является расширением QHB.

См. также

CREATE FUNCTION

LOCK

LOCK — заблокировать таблицу

Синтаксис

LOCK [ TABLE ] [ ONLY ] имя [ * ] [, ...][ IN  режим_блокировки ] [ NOWAIT ]

где режим_блокировки является одним из следующих:

    ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE
    | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE

Описание

LOCK TABLE получает блокировку уровня таблицы, при необходимости ожидая освобождения всех конфликтующих блокировок. Если указывается NOWAIT, LOCK TABLE не ожидает освобождения таблицы: если блокировку нельзя получить немедленно, команда прерывается и выдается ошибка. После получения блокировка удерживается до конца текущей транзакции. (Команды UNLOCK TABLE не существует; блокировки всегда освобождаются в конце транзакции.)

Когда блокируется представление, все отношения, появляющиеся в запросе определения представления, также рекурсивно блокируются с тем же режимом блокировки.

При автоматическом получении блокировок для команд, ссылающихся на таблицы, QHB всегда использует наименее ограничительный режим блокировки. Команда LOCK TABLE предназначена для случаев, когда может потребоваться более строгая блокировка. Например, предположим, что приложение выполняет транзакцию на уровне изоляции READ COMMITTED и должно обеспечить неизменность данных в таблице на протяжении всей транзакции. Для этого можно перед запросом получить над таблицей блокировку в режиме SHARE. Это предотвратит одновременные изменения данных и обеспечит стабильное представление зафиксированных данных при последующем чтении таблицы, потому что режим блокировки SHARE конфликтует с запрашиваемой при записи блокировкой ROW EXCLUSIVE, а команда LOCK TABLE имя IN SHARE MODE будет ждать, пока все параллельные транзакции с блокировкой ROW EXCLUSIVE не будут зафиксированы или отменены. Таким образом, в момент получения такой блокировки не останется никаких открытых незафиксированных операций записи; более того, до снятия блокировки никто не сможет записывать в таблицу.

Для достижения аналогичного эффекта при выполнении транзакции на уровнях изоляции REPEATABLE READ или SERIALIZABLE нужно выполнить команду LOCK TABLE перед выполнением первой команды SELECT или команды, изменяющей данные. Представление данных для транзакции уровня REPEATABLE READ или SERIALIZABLE будет заморожено в момент, когда начнет выполняться этот запрос. Если команда LOCK TABLE выполняется в транзакции позже, она также исключает параллельную запись, но не гарантирует, что транзакция будет считывать последние зафиксированные значения.

Если транзакция такого рода будет изменять данные в таблице, следует использовать режим блокировки SHARE ROW EXCLUSIVE вместо SHARE. Это гарантирует, что в один момент времени будет выполняться только одна транзакция такого типа. Без этого ограничения возможна взаимная блокировка: обе транзакции могут получить блокировки SHARE, после чего не смогут получить блокировку ROW EXCLUSIVE, чтобы выполнить изменения. (Обратите внимание, что собственные блокировки транзакции никогда не конфликтуют, поэтому транзакция может получить блокировку ROW EXCLUSIVE, когда владеет блокировкой SHARE — но не тогда, когда блокировку SHARE удерживает другая транзакция.) Чтобы избежать взаимоблокировок, убедитесь, что все транзакции запрашивают блокировки одних и тех же объектов в одном и том же порядке, и если для одного объекта запрашиваются блокировки с разными режимами, то транзакции всегда должны сначала запрашивать наиболее строгую блокировку.

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

Параметры

имя

Имя существующей таблицы, подлежащей блокировке (может быть дополнено схемой). Если перед именем таблицы указывается ONLY, то блокируется только эта таблица. Если ONLY не указан, то блокируются таблица и все ее потомки (если таковые имеются). После имени таблицы можно добавить необязательное указание *, чтобы явно обозначить, что включены и все дочерние таблицы.

Команда LOCK TABLE a, b; эквивалентна LOCK TABLE a; LOCK TABLE b;. Таблицы блокируются одна за другой в порядке, указанном в команде LOCK TABLE.

режим блокировки

Режим блокировки определяет, с какими блокировками эта блокировка конфликтует. Режимы блокировки описаны в разделе Явная блокировка.

Если режим блокировки не указан, то применяется самый строгий режим ACCESS EXCLUSIVE.

NOWAIT

Указывает, что LOCK TABLE не должна ожидать освобождения конфликтующих блокировок: если указанные блокировки не могут быть получены немедленно без ожидания, транзакция прерывается.

Примечания

Команда LOCK TABLE ... IN ACCESS SHARE MODE требует права SELECT к целевой таблице. Команда LOCK TABLE ... IN ROW EXCUSIVE MODE требует прав INSERT, UPDATE, DELETE или TRUNCATE к целевой таблице. Все другие формы LOCK требуют прав UPDATE, DELETE или TRUNCATE к целевой таблице.

Пользователь, выполняющий блокировку представления, должен иметь соответствующие права для этого представления. Кроме того, владелец представления должен иметь соответствующие права доступа к нижележащим базовым отношениям, но пользователь, выполняющий блокировку, не нуждается в каких-либо правах на базовые отношения.

LOCK TABLE бесполезна вне блока транзакций: блокировка будет удерживаться только до завершения операции. Поэтому если LOCK используется вне блока транзакций, QHB сообщает об ошибке. Для определения блока транзакций используйте команды BEGIN и COMMIT (или ROLLBACK).

LOCK TABLE имеет дело только с блокировками уровня таблицы, и поэтому имена режимов, включающие ROW, не вполне корректны. Эти имена режимов обычно следует рассматривать как указывающие на намерение пользователя получить блокировки на уровне строк в заблокированной таблице. Кроме того, ROW EXCLUSIVE — это разделяемая блокировка таблицы. Имейте в виду, что для LOCK TABLE все режимы блокировки имеют одинаковую семантику, отличаясь только правилами, определяющими, какие режимы конфликтуют друг с другом. Информацию о том, как получить фактическую блокировку уровня строки, см. в разделах Блокировки на уровне строк и Предложение блокировки в справочной документации по команде SELECT.

Примеры

Получение блокировки SHARE для первичного ключа таблицы при выполнении операций добавления в подчиненную таблицу:

BEGIN WORK;
LOCK TABLE films IN SHARE MODE;
SELECT id FROM films
    WHERE name = 'Star Wars: Episode I - The Phantom Menace';
-- Будет выполнен откат, если запись не будет возвращена
INSERT INTO films_user_comments VALUES
    (id, 'GREAT! I was waiting for it for so long!');
COMMIT WORK;

Установка блокировки SHARE ROW EXCLUSIVE в таблице первичного ключа перед выполнением операции удаления:

BEGIN WORK;
LOCK TABLE films IN SHARE ROW EXCLUSIVE MODE;
DELETE FROM films_user_comments WHERE id IN
    (SELECT id FROM films WHERE rating < 5);
DELETE FROM films WHERE rating < 5;
COMMIT WORK;

Совместимость

В стандарте SQL нет команды LOCK TABLE; вместо этого для указания уровней изоляции в транзакциях в нём используется команда SET TRANSACTION. QHB поддерживает и этот вариант; дополнительную информацию см. в разделе SET TRANSACTION.

За исключением ACCESS SHARE, ACCESS EXCLUSIVE и SHARE UPDATE EXCLUSIVE, режимы блокировки и синтаксис команды LOCK TABLE в QHB совместимы с имеющимися в СУБД Oracle.

MOVE

MOVE — переместить курсор

Синтаксис

MOVE [ направление [ FROM | IN ] ] имя_курсора

Где направление может быть пустым или принимать одно из значений:

    NEXT
    PRIOR
    FIRST
    LAST
    ABSOLUTE число
    RELATIVE число
    число
    ALL
    FORWARD
    FORWARD число
    FORWARD ALL
    BACKWARD
    BACKWARD число
    BACKWARD ALL

Описание

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

Параметры команды MOVE идентичны параметрам команды FETCH; подробную информацию о синтаксисе и использовании см. в разделе FETCH.

Выводимая информация

После успешного завершения команда MOVE возвращает метку команды в виде

MOVE число

Где число — это число строк, которое вернула бы команда FETCH с теми же параметрами (возможно, ноль).

Примеры

BEGIN WORK;
DECLARE liahona CURSOR FOR SELECT * FROM films;

-- Пропустить первые 5 строк:
MOVE FORWARD 5 IN liahona;
MOVE 5

-- Выбрать 6-ю строку из курсора liahona:
FETCH 1 FROM liahona;
 code  | title  | did | date_prod  |  kind  |  len
-------+--------+-----+------------+--------+-------
 P_303 | 48 Hrs | 103 | 1982-10-22 | Action | 01:37
(1 row)

-- Закрыть курсор liahona и завершить транзакцию:
CLOSE liahona;
COMMIT WORK;

Совместимость

В стандарте SQL нет команды MOVE.

См. также

CLOSE, DECLARE, FETCH

NOTIFY

NOTIFY — сгенерировать уведомление

Синтаксис

NOTIFY канал [ , полезная_информация ]

Описание

Команда NOTIFY отправляет событие уведомления вместе с необязательной строкой «полезная информация» каждому клиентскому приложению, которое ранее выполнило команду LISTEN канал для указанного имени канала в текущей базе данных. Уведомления отображаются для всех пользователей.

NOTIFY предоставляет простой механизм межпроцессной связи для набора процессов, обращающихся к одной и той же базе данных QHB. Строка полезной информации может быть отправлена вместе с уведомлением, а при использовании таблиц в базе данных для передачи дополнительных данных от уведомителя к прослушивателю (или прослушивателям) можно создать механизмы более высокого уровня для передачи структурированных данных.

Информация, передаваемая клиенту для события уведомления, включает имя канала уведомления, идентификатор PID серверного процесса сеанса уведомления и строку полезной информации, которая остается пустой, если не задать сообщение.

Выбор подходящих имен каналов и их назначения остается на усмотрение проектировщика базы данных. Обычно имя канала совпадает с именем некоторой таблицы в базе данных, и событие, по существу, означает: «я изменил эту таблицу, взгляните на нее, чтобы увидеть, что нового». Но командами NOTIFY и LISTEN подобная ассоциация не навязывается. Например, проектировщик базы данных может использовать несколько различных имен каналов для передачи сигналов о различных видах изменений в одной таблице. Кроме того, строка полезной информации может использоваться для дифференциации различных случаев.

Когда NOTIFY используется для сигнализации о появлении изменений в определенной таблице, полезным методом программирования является помещение NOTIFY в триггер команды, который запускается обновлениями таблицы. Таким образом, уведомление происходит автоматически при изменении таблицы, и программист приложения не может случайно забыть это сделать.

NOTIFY взаимодействует с транзакциями SQL несколькими важными способами. Во-первых, если NOTIFY выполняется внутри транзакции, уведомляющие события не доставляются до тех пор, пока транзакция не будет зафиксирована. Это целесообразно, так как при прерывании транзакции действие всех команд внутри нее, включая NOTIFY, аннулируется. Но это может привести в замешательство, если пользователь ожидает, что события уведомления будут доставлены немедленно. Во-вторых, если во время нахождения в транзакции сеанс прослушивания получает сигнал уведомления, это событие не будет доставлено своему подключенному клиенту сразу после завершения (фиксации или прерывания) транзакции. Опять же, смысл в том, что если уведомление было доставлено в рамках транзакции, которая позже была прервана, кто-нибудь может захотеть как-то отменить его — но сервер не может «забрать» уведомление, которое уже отправлено клиенту. Поэтому события уведомлений доставляются только между транзакциями. Подводя итог вышесказанному, приложениям, использующим NOTIFY для сигнализации в режиме реального времени, следует по возможности максимально сокращать размер своих транзакций.

Если из одной транзакции несколько раз сигнализируется одно и то же имя канала с одинаковыми строками полезной информации, сервер баз данных может принять решение о доставке только одного уведомления. С другой стороны, уведомления с различными строками полезной информации всегда будут доставляться по отдельности. Аналогично никогда не будут объединены и уведомления от различных транзакций. За исключением удаления более поздних экземпляров повторяющихся уведомлений, NOTIFY гарантирует, что уведомления от одной и той же транзакции будут доставлены в том же порядке, в котором были отправлены. Также гарантируется, что сообщения от различных транзакций доставляются в том же порядке, в котором были совершены.

Часто бывает, что клиент, выполнивший NOTIFY, ожидает уведомления на том же канале. В этом случае он получит свое же событие уведомления, как и любой другой сеанс прослушивания. В зависимости от логики приложения, это может привести к бесполезной работе, например чтению таблицы базы данных для поиска тех же обновлений, которые только что были записаны в этом сеансе. Подобной дополнительной работы можно избежать, отследив, не совпадает ли PID процесса сервера уведомляющего сеанса (предоставленный в сообщении о событии уведомления) с PID собственного сеанса (доступным из libpq). Если они одинаковы, значит, это вернувшееся событие уведомления о ваших собственных действиях и его можно игнорировать.

Параметры

канал

Название канала уведомлений, который будет оповещен (любой идентификатор).

полезная_информация

Строка «полезной информации», передаваемая вместе с уведомлением. Она должна задаваться простой текстовой константой. В стандартной конфигурации ее длина должна быть меньше 8000 байт. (Если требуется передать двоичные данные или большой объем информации, лучше поместить их в таблицу базы данных и передать ключ этой записи.)

Примечания

Существует очередь, содержащая уведомления, которые были отправлены, но еще не обработаны всеми сеансами прослушивания. Если эта очередь переполняется, транзакции, вызывающие NOTIFY, не смогут выполнить фиксацию. Очередь довольно большая (8 ГБ в стандартной установке), так что ее должно хватать для почти каждого случая использования. Однако если в сеансе выполняется LISTEN, а затем продолжается очень длительная транзакция, очередь не очищается. Как только эта очередь заполняется наполовину, в журнал записываются предупреждения, где указывается, какой сеанс препятствует очистке очереди. В этом случае следует добиться завершения текущей транзакции в указанном сеансе, чтобы очередь была очищена.

Функция pg_notification_queue_usage возвращает долю очереди, которая в настоящее время занята ожидающими уведомлениями. Дополнительную информацию см. в разделе Расширенные функции SQL.

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

pg_notify

Чтобы отправить уведомление, также можно использовать функцию pg_notify(text, text). Функция принимает имя канала в качестве первого аргумента, а полезные данные — в качестве второго. Эта функция намного проще в использовании, чем команда NOTIFY, если нужно работать с изменяемыми именами каналов и полезными нагрузками.

Примеры

Настройка и выполнение последовательности ожидания/получения уведомления от psql:

LISTEN virtual;
NOTIFY virtual;
Asynchronous notification "virtual" received from server process with PID 8448.
NOTIFY virtual, 'This is the payload';
Asynchronous notification "virtual" with payload "This is the payload" received
from server process with PID 8448.

LISTEN foo;
SELECT pg_notify('fo' || 'o', 'pay' || 'load');
Asynchronous notification "foo" with payload "payload" received from server
process with PID 14728.

Совместимость

В стандарте SQL нет команды NOTIFY.

См. также

LISTEN, UNLISTEN

PREPARE

PREPARE — подготовить оператор для исполнения

Синтаксис

PREPARE имя [ ( тип_данных [, ...] ) ] AS оператор

Описание

Команда PREPARE создает подготовленный оператор. Подготовленный оператор — это объект на стороне сервера, который можно использовать для оптимизации производительности. При выполнении команды PREPARE указанный оператор разбирается, анализируется и перезаписывается. При последующем выполнении команды EXECUTE подготовленный оператор планируется и исполняется. Это разделение труда предотвращает повторный синтаксический анализ запроса, при этом позволяя выбрать наилучший план выполнения в зависимости от определенных значений параметров.

Подготовленные операторы могут принимать параметры: значения, которые подставляются в оператор при его выполнении. При создании подготовленного оператора к этим параметрам можно обращаться по порядковому номеру, используя запись $1, $2 и т. д. При необходимости можно указать соответствующий список типов данных параметров. Если тип данных параметра не указан или объявлен как unknown (неизвестный), тип выводится из контекста, в котором произошло первое обращение к данному параметру (если это возможно). При выполнении оператора укажите команде EXECUTE фактические значения этих параметров. Дополнительную информацию об этом см. в разделе EXECUTE.

Подготовленные операторы существуют только в течение текущего сеанса работы с базой данных. По окончании сеанса система забывает подготовленный оператор, поэтому перед повторным использованием его необходимо воссоздать. Это также означает, что один подготовленный оператор не может использоваться одновременно несколькими клиентами базы данных; тем не менее каждый клиент может создать свой собственный подготовленный оператор для использования. Подготовленные операторы можно очистить вручную с помощью команды DEALLOCATE.

Подготовленные операторы потенциально обладают наибольшим преимуществом производительности, когда один сеанс используется для выполнения большого количества однотипных операторов. Разница в производительности будет особенно существенной, если операторы сложны для планирования или перезаписи, например если запрос включает соединение многих таблиц или требует применения нескольких правил. Если оператор относительно прост в планировании и переписывании, но относительно дорог в выполнении, преимущество производительности подготовленных операторов будет менее заметным.

Параметры

имя

Произвольное имя, данное этому конкретному подготовленному оператору. Оно должно быть уникальным в пределах одного сеанса и впоследствии используется для выполнения или освобождения ранее подготовленного оператора.

тип_данных

Тип данных параметра для подготовленного оператора. Если тип данных конкретного параметра не определен или указан как unknown, он будет выведен из контекста, в котором произошло первое обращение к этому параметру. Для обращения к параметрам в самом подготовленном операторе используйте запись $1, $2 и т. д.

оператор

Любой оператор SELECT, INSERT, UPDATE, DELETE или VALUES.

Примечания

Подготовленный оператор может быть выполнен либо с общим планом, либо со специализированным планом. Общий план является одинаковым для всех выполнений, тогда как специализированный создается для конкретного выполнения с использованием значений параметров, заданных в этом вызове. Использование общего плана позволяет избежать накладных расходов на планирование, но в некоторых ситуациях выполнение специализированного плана будет гораздо более эффективным, поскольку планировщик может подстроиться под значения параметров. (Конечно, если подготовленный оператор не имеет параметров, это неактуально и всегда используется общий план.)

По умолчанию (то есть когда plan_cache_mode установлен в auto), сервер автоматически выберет, какой план использовать для подготовленного оператора с параметрами: общий или специализированный. Текущее правило состоит в том, что первые пять исполнений выполняются со специализированными планами, и рассчитывается средняя расчетная стоимость этих планов. Затем создается общий план, и его расчетная стоимость сравнивается со средней стоимостью специализированного плана. Последующие выполнения используют общий план, если его стоимость по сравнению со стоимостью специализированных не настолько велика, чтобы делать повторное перепланирование.

Эту логику можно переопределить, заставляя сервер использовать либо только общие, либо специализированные планы, установив для параметра plan_cache_mode значение force_generic_plan или force_custom_plan соответственно. Этот параметр в первую очередь полезен, если по какой-то причине плохо работает оценка общего плана, так как позволяет выбрать его, даже если его фактическая стоимость намного больше, чем у специализированного плана.

Чтобы узнать план запроса, который QHB выбирает для подготовленного оператора, используйте команду EXPLAIN, например:

EXPLAIN EXECUTE имя(значения_параметров);

Если используется общий план, он будет содержать символы параметров $n, в то время как специализированный план будет иметь подставленные в него фактические значения параметров.

Дополнительную информацию о планировании запросов и статистике, собранной QHB для этой цели, см. в документации по команде ANALYZE.

Хотя основной целью подготовленных операторов является предотвращение повторного разбора и планирования оператора, QHB будет принудительно заново анализировать и планировать выполнение оператора всякий раз, когда используемые в операторе объекты базы данных подвергаются изменениям определения (DDL) с момента предыдущего использования подготовленного оператора. Кроме того, если значение search_path изменяется от одного использования к другому, оператор будет повторно разобран с новым search_path. С этими правилами использование подготовленного оператора по сути почти не отличается от выполнения одного и того же запроса снова и снова, но с преимуществом производительности, если определения объектов не меняются (особенно если оптимальный план из раза в раз остается неизменным). Примером случая, когда различия всё же могут проявиться, служит то, что если оператор обращается к таблице по неполному имени, а затем новая таблица с таким же именем создается в схеме, стоящей в пути search_path раньше, автоматический пересмотр запроса не происходит, так как ни один используемый в операторе объект не изменился. Однако если какое-то другое изменение вызывает повторный анализ, при последующем выполнении запроса будет задействована новая таблица.

Получить список всех доступных в сеансе подготовленных операторов можно, обратившись к системному представлению pg_prepared_statements.

Примеры

Создание подготовленного оператора для команды INSERT, который затем выполняется:

PREPARE fooplan (int, text, bool, numeric) AS
    INSERT INTO foo VALUES($1, $2, $3, $4);
EXECUTE fooplan(1, 'Hunter Valley', 't', 200.00);

Создание подготовленного оператора для команды SELECT, который затем выполняется:

PREPARE usrrptplan (int) AS
    SELECT * FROM users u, logs l WHERE u.usrid=$1 AND u.usrid=l.usrid
    AND l.date = $2;
EXECUTE usrrptplan(1, current_date);

В этом примере тип данных второго параметра не указывается, поэтому он выводится из контекста, в котором используется $2.

Совместимость

Стандарт SQL включает команду PREPARE, но она предназначена только для использования во встраиваемом SQL. Эта версия команды PREPARE также использует несколько иной синтаксис.

См. также

DEALLOCATE, EXECUTE

PREPARE TRANSACTION

PREPARE TRANSACTION — подготовить текущую транзакцию к двухфазной фиксации

Синтаксис

PREPARE TRANSACTION id_транзакции

Описание

Команда PREPARE TRANSACTION подготавливает текущую транзакцию к двухфазной фиксации. После выполнения этой команды транзакция лишается связи с текущим сеансом; вместо этого ее состояние полностью сохраняется на диске, и она с очень большой вероятностью будет успешно зафиксирована, даже если сбой базы данных произойдет до запроса фиксации.

После подготовки транзакцию можно впоследствии зафиксировать или откатить с помощью COMMIT PREPARED или ROLLBACK PREPARED соответственно. Эти команды могут быть выполнены из любого сеанса, а не только из того, в котором эта транзакция создавалась.

С точки зрения сеанса, выполняющего команду, PREPARE TRANSACTION не отличается от команды ROLLBACK: после ее выполнения нет активной текущей транзакции, и эффекты подготовленной транзакции больше не видны. (Последствия снова станут видимыми, если транзакция зафиксирована.)

Если команда PREPARE TRANSACTION по какой-либо причине завершается неудачно, то она действует как ROLLBACK: текущая транзакция отменяется.

Параметры

id_транзакции

Произвольный идентификатор, который позже идентифицирует эту транзакцию для COMMIT PREPARED или ROLLBACK PREPARED. Идентификатор должен быть записан в виде строковой константы и иметь длину менее 200 байт. Он не должен совпадать с идентификатором, используемым для любой текущей подготовленной транзакции.

Примечания

Команда PREPARE TRANSACTION не предназначена для использования в приложениях или интерактивных сеансах. Ее цель — позволить внешнему менеджеру транзакций выполнять атомарные глобальные транзакции, охватывающие несколько баз данных или других транзакционных ресурсов. Если вы не разрабатываете собственный менеджер транзакций, вероятно, вам не стоит использовать PREPARE TRANSACTION.

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

В настоящее время команда PREPARE не способна подготавливать транзакцию, в которой выполнялись какие-либо операции с временными таблицами или временным пространством имен сеанса, создавались какие-либо курсоры WITH HOLD или выполнялись команды LISTEN, UNLISTEN или NOTIFY. Эти функции слишком тесно связаны с текущим сеансом, чтобы быть полезными в транзакции, подлежащей подготовке.

Если транзакция изменила какие-либо параметры времени выполнения командой SET (без указания LOCAL), их значения сохраняются после PREPARE TRANSACTION и не будут затронуты более поздним выполнением команд COMMIT PREPARED или ROLLBACK PREPARED. То есть в этом отношении PREPARE TRANSACTION действует скорее как COMMIT, чем как ROLLBACK.

Все доступные в настоящее время подготовленные транзакции перечислены в системном представлении pg_prepared_xacts.

Внимание

Оставлять транзакции в подготовленном состоянии на длительное время неразумно. Это будет препятствовать способности команды VACUUM высвобождать пространство и в крайних случаях может привести к отключению базы данных для предотвращения зацикливания ID транзакций (см. раздел Предотвращение ошибок зацикливания идентификатора транзакции). Также имейте в виду, что транзакция продолжает удерживать все свои блокировки. Предполагаемое использование этой функции заключается в том, что подготовленная транзакция обычно фиксируется или откатывается, как только внешний менеджер транзакций убедится, что другие базы данных также готовы к фиксации.

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

Примеры

Подготовка текущей транзакции для двухфазной фиксации, при этом ей назначается идентификатор foobar:

PREPARE TRANSACTION 'foobar';

Совместимость

Команда PREPARE TRANSACTION является расширением QHB. Она предназначена для использования внешними системами управления транзакциями, некоторые из которых включены в стандарты (например, X/Open XA), но сторона SQL этих систем не стандартизирована.

См. также

COMMIT PREPARED, ROLLBACK PREPARED

REASSIGN OWNED

REASSIGN OWNED — сменить владельца объектов базы данных, принадлежащих роли базы данных

Синтаксис

REASSIGN OWNED BY { старая_роль | CURRENT_USER | SESSION_USER } [, ...]
               TO { новая_роль | CURRENT_USER | SESSION_USER }

Описание

Команда REASSIGN OWNED дает системе указание сменить владельца объектов базы данных, принадлежащих одной из старых_ролей, на новую роль.

Параметры

старая_роль

Название роли. Права собственности на все объекты в текущей базе данных и на все общие объекты (базы данных, табличные пространства), принадлежащие этой роли, будут переназначены на новую_роль.

новая_роль

Имя роли, которая будет сделана новым владельцем затронутых объектов.

Примечания

Команда REASSIGN OWNED часто используется для подготовки к удалению одной или нескольких ролей. Поскольку REASSIGN OWNED не влияет на объекты в других базах данных, обычно необходимо выполнить эту команду в каждой базе данных, которая содержит объекты, принадлежащие удаляемой роли.

Для выполнения команды REASSIGN OWNED требуется членство как в исходной(ых), так и в целевой ролях.

Альтернативой является команда DROP OWNED, которая просто удаляет все объекты базы данных, принадлежащие одной или нескольким ролям.

Команда REASSIGN OWNED не влияет ни на какие права, предоставленные старым_ролям на объекты, которые им не принадлежат. Кроме того, она не влияет на права по умолчанию, созданные с помощью команды ALTER DEFAULT PRIVILEGES. Отозвать эти права можно, воспользовавшись командой DROP OWNED.

Дополнительную информацию см. в разделе Удаление ролей.

Совместимость

Команда REASSIGN OWNED является расширением QHB.

См. также

DROP OWNED, DROP ROLE, ALTER DATABASE

REFRESH MATERIALIZED VIEW

REFRESH MATERIALIZED VIEW — заменить содержимое материализованного представления

Синтаксис

REFRESH MATERIALIZED VIEW [ CONCURRENTLY ] имя
    [ WITH [ NO ] DATA ]

Описание

Команда REFRESH MATERIALIZED VIEW полностью заменяет содержимое материализованного представления. Для выполнения этой команды нужно быть владельцем соответствующего материализованного представления. Старое содержимое удаляется. Если указывается WITH DATA (или по умолчанию), то для предоставления новых данных выполняется нижележащий запрос, а материализованное представление остается в состоянии сканирования. Если указывается WITH NO DATA, то новые данные не выдаются, а материализованное представление остается в несканируемом состоянии.

Параметр WITH NO DATA нельзя задавать вместе с параметром CONCURRENTLY.

Параметры

CONCURRENTLY

Обновить материализованное представление, не блокируя параллельные выборки из него. Без этого параметра обновление, влияющее на значительное количество строк, в большинстве случаев будет использовать меньше ресурсов и завершаться быстрее, но может блокировать другие соединения, которые пытаются читать из материализованного представления. Этот режим может быть быстрее, если затрагивается небольшое количество строк.

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

Этот параметр нельзя использовать, если материализованное представление еще не заполнено.

Даже с этим параметром в один момент времени допускается только одно обновление (REFRESH) материализованного представления.

имя

Имя материализованного представления, подлежащего обновлению (может быть дополнено схемой).

Примечания

Хотя индекс по умолчанию для будущих операций CLUSTER сохраняется, команда REFRESH MATERIALIZED VIEW не упорядочивает по нему генерируемые строки. Если вы хотите, чтобы данные были упорядочены при создании, в определяющем запросе следует использовать предложение ORDER BY.

Примеры

Эта команда заменит содержимое материализованного представления order_summary, используя запрос из определения материализованного представления, и оставит его в сканируемом состоянии:

REFRESH MATERIALIZED VIEW order_summary;

Эта команда освободит пространство, связанное с материализованным представлением annual_statistics_basis, и оставит это представление в несканируемом состоянии:

REFRESH MATERIALIZED VIEW annual_statistics_basis WITH NO DATA;

Совместимость

Команда REFRESH MATERIALIZED VIEW является расширением QHB.

См. также

CREATE MATERIALIZED VIEW, ALTER MATERIALIZED VIEW, DROP MATERIALIZED VIEW

REINDEX

REINDEX — перестроить индексы

Синтаксис

RREINDEX [ ( параметр [, ...] ) ] { INDEX | TABLE | SCHEMA | DATABASE | SYSTEM } [ CONCURRENTLY ] имя

Где параметр может быть:

    VERBOSE

Описание

Команда REINDEX перестраивает индекс, обрабатывая данные, хранящиеся в таблице индекса, и заменяя его старую копию. Существует несколько сценариев, в которых можно использовать команду REINDEX:

Индекс был поврежден, и его содержимое стало некорректным. Хотя в теории этого никогда не должно произойти, на практике индексы могут быть повреждены из-за ошибок программного обеспечения или аппаратных сбоев. В таких случаях REINDEX предоставляет метод восстановления.

Индекс стал «раздутым», то есть содержит много пустых или почти пустых страниц. Это может произойти с индексами B-деревьями в QHB при определенных необычных сценариях использования. REINDEX предоставляет способ сократить объем, занимаемый индексом, записывая новую версию индекса без «мертвых» страниц. Дополнительную информацию см. в разделе Регулярная переиндексация.

Вы изменили для индекса параметр хранения (например, коэффициент заполнения) и хотите убедиться, что изменение вступило в силу.

Если построение индекса завершается неудачно в режиме CONCURRENTLY, этот индекс остается в «нерабочем» состоянии. Такие индексы бесполезны, но с помощью REINDEX их можно легко перестроить. Обратите внимание, что перестраивать индекс в неблокирующем режиме способна только команда REINDEX INDEX.

Параметры

INDEX

Перестраивает указанный индекс.

TABLE

Перестраивает все индексы указанной таблицы. Если таблица имеет дополнительную таблицу «TOAST», то она также будет переиндексирована.

SCHEMA

Перестраивает все индексы указанной схемы. Если таблица этой схемы имеет дополнительную таблицу «TOAST», то она также будет переиндексирована. Также обрабатываются индексы в общих системных каталогах. Эта форма REINDEX не может быть выполнена внутри блока транзакций.

DATABASE

Перестраивает все индексы в текущей базе данных. Также обрабатываются индексы в общих системных каталогах. Эта форма REINDEX не может быть выполнена внутри блока транзакций.

SYSTEM

Перестраивает все индексы в системных каталогах в текущей базе данных. Индексы в общих системных каталогах также будут обработаны. Индексы в пользовательских таблицах не обрабатываются. Эта форма REINDEX не может быть выполнена внутри блока транзакций.

имя

Имя конкретного индекса, таблицы или базы данных, подлежащих переиндексации. Имена индексов и таблиц могут быть дополнены схемой. В настоящее время REINDEX DATABASE и REINDEX SYSTEM могут переиндексировать только текущую базу данных, поэтому параметр должен соответствовать имени текущей базы данных.

CONCURRENTLY

Если используется этот параметр, QHB будет перестраивать индекс, не устанавливая никаких блокировок, которые предотвращают параллельные добавления, изменения или удаления в таблице; тогда как стандартное перестроение индекса блокирует запись (но не чтение) в таблице вплоть до своего завершения. Существует несколько предупреждений, которые следует учитывать при использовании этого параметра, — см. подраздел Неблокирующее перестроение индексов ниже.

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

VERBOSE

Выводит отчет о ходе работы по мере переиндексации каждого индекса.

Примечания

При подозрении на повреждение индекса в пользовательской таблице можно просто перестроить этот индекс или все индексы в таблице, используя REINDEX INDEX или REINDEX TABLE.

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

Один из способов сделать это — выключить сервер и запустить QHB в однопользовательском режиме с параметром -P в командной строке. Затем можно выполнить REINDEX DATABASE, REINDEX SYSTEM, REINDEX TABLE или REINDEX INDEX, в зависимости от того, что вы хотите восстановить. Если сомневаетесь, используйте команду REINDEX SYSTEM, чтобы перестроить все системные индексы в базе данных. После этого завершите однопользовательский сеанс сервера и перезапустите сервер в обычном режиме. Дополнительную информацию о том, как работать с сервером в однопользовательском интерфейсе, см. в разделе Процесс qhb.

Как вариант, можно запустить обычный экземпляр сервера, но добавить в параметры командной строки -P. В разных клиентах это может делаться по-разному, но во всех клиентах на базе libpq можно установить для переменной окружения PGOPTIONS значение -P до запуска клиента. Обратите внимание, что хотя этот метод не требует блокировки других клиентов, всё же было бы разумно запретить другим пользователям подключаться к поврежденной базе данных до завершения восстановления.

Действие REINDEX подобно удалению и повторному созданию индекса в том, что содержимое индекса перестраивается с нуля. Тем не менее, блокировки при этом устанавливаются другие. Команда REINDEX блокирует операции записи, но не чтение из родительской таблицы индекса. Она также устанавливает исключительную блокировку на обрабатываемый индекс, которая блокирует чтение, задействующее этот индекс. DROP INDEX, напротив, мгновенно устанавливает исключительную блокировку на родительской таблице, блокируя как запись, так и чтение. Последующая CREATE INDEX блокирует запись, но не чтение; поскольку индекс отсутствует, при чтении не будет попыток обращения к нему, а это значит, что не будет никакой блокировки, но чтение может непроизвольно превратиться в дорогостоящее последовательное сканирование.

Для перестраивания одного индекса или таблицы нужно быть владельцем этого индекса или таблицы. Для переиндексирования схемы или базы данных нужно быть владельцем этой схемы или базы данных. Обратите внимание, что из-за этого в ряде случаев не только суперпользователи могут перестраивать индексы таблиц, принадлежащих другим пользователям. Однако из этих правил есть исключение: когда команду REINDEX DATABASE, REINDEX SCHEMA или REINDEX SYSTEM выполняет не суперпользователь, индексы общих каталогов будут пропущены, если только пользователь не владеет каталогом (как правило, это не так). Конечно, суперпользователи всегда могут переиндексировать всё без ограничений.

Переиндексирование партиционированных таблиц или партиционированных индексов не поддерживается. Вместо этого каждую партицию можно переиндексировать отдельно.

Неблокирующее перестроение индексов

Перестроение индекса может помешать регулярной работе базы данных. Обычно QHB блокирует на запись таблицу, индекс которой перестраивается, и выполняет всю операцию перестройки индекса за одно сканирование таблицы. Другие транзакции по-прежнему могут читать таблицу, но при попытке добавления, изменения или удаления строк в таблице они будут блокироваться до завершения перестроения индекса. Это может иметь серьезные последствия, если система является производственной базой данных. Индексирование очень больших таблиц может занять много часов, и даже у таблиц меньшего размера перестройка индекса может заблокировать запись в таблицы на неприемлемо долгий для производственной системы период.

QHB поддерживает перестроение индексов с минимальной блокировкой записи. Этот режим вызывается путем указания CONCURRENTLY команды REINDEX. При использовании этого параметра QHB должна выполнить два сканирования таблицы для каждого индекса, который необходимо перестроить, и дождаться завершения всех существующих транзакций, которые потенциально могут использовать индекс. Этот метод требует, в целом, большего количества действий, чем стандартное перестроение индекса, и весь процесс занимает значительно больше времени, поскольку нужно дождаться незавершенных транзакций, которые могут изменить индекс. Однако поскольку он позволяет продолжить обычные операции во время перестроения индекса, этот метод полезен для перестроения индексов в производственной среде. Разумеется, дополнительная загрузка процессора, памяти и ввода-вывода, продиктованная перестроением индекса, может замедлить другие операции.

При неблокирующем переиндексировании выполняются следующие шаги. Каждый шаг выполняется в отдельной транзакции. Если необходимо перестроить несколько индексов, то каждый шаг выполняется для всех индексов, прежде чем перейти к следующему шагу.

В каталог pg_index добавляется новое определение временного индекса. Это определение будет использовано для замены старого индекса. Чтобы предотвратить любые изменения в схеме во время обработки, на уровне сеанса выполняется блокировка SHARE UPDATE EXCLUSIVE переиндексируемых индексов, а также связанных с ними таблиц.

Первый проход для построения индекса выполняется для каждого нового индекса. После того, как индекс построен, его флаг pg_index.indisready переключается в «true», чтобы этот индекс был готов к добавлениям, тем самым делая его видимым для других сеансов сразу после окончания построившей его транзакции. Этот шаг выполняется в отдельной транзакции для каждого индекса.

Затем выполняется второй проход для внесения в индекс кортежей, которые были добавлены во время выполнения первого прохода. Этот шаг также выполняется в отдельной транзакции для каждого индекса.

Все ограничения, относящиеся к индексу, переключаются на определение нового индекса; также изменяются имена индексов. В этот момент флаг pg_index.indisvalid переключается в «true» для нового индекса и в «false» для старого, и производится сброс кэша, в результате чего все сеансы, которые ссылались на старый индекс, становятся недействительными.

Старые индексы имеют флаг pg_index.indisready, который переключается в «false», чтобы предотвратить новые добавления кортежей, как только завершатся текущие запросы, которые могут ссылаться на старый индекс.

Старые индексы удаляются. Блокировки SHARE UPDATE EXCLUSIVE уровня сеанса, установленные для индексов и таблиц, снимаются.

Если при перестроении индексов возникает проблема, например нарушение уникальности в уникальном индексе, команда REINDEX завершится ошибкой, но оставит после себя «нерабочий» новый индекс в дополнение к уже существующему. Этот индекс будет игнорироваться запросами, поскольку может быть неполным; однако он всё равно будет обновляться при изменении данных, что повлечет дополнительные издержки. Команда psql \d сообщит о таком индексе как об INVALID:

qhb=# \d tab
       Table "public.tab"
 Column |  Type   | Modifiers
--------+---------+-----------
 col    | integer |
Indexes:
    "idx" btree (col)
    "idx_ccnew" btree (col) INVALID

Если индекс, отмеченный INVALID, имеет имя с окончанием ccnew, то он соответствует временному индексу, созданному во время совмещенной операции, и рекомендуемый метод восстановления состоит в удалении такого индекса при помощи команды DROP INDEX и повторной попытке выполнить REINDEX CONCURRENTLY. Если нерабочий индекс имеет окончание ccold, то он соответствует исходному индексу, который удалить нельзя; рекомендуемый метод восстановления состоит в простом удалении нерабочего индекса, раз уж перестроение индекса прошло успешно.

Обычные построения индексов позволяют другим построениям индексов в одной и той же таблице выполняться одновременно, но неблокирующее построение индексов в таблице может выполняться только одно за раз. В обоих случаях никакие другие изменения схемы таблицы в это время не разрешаются. Другое отличие состоит в том, что обычные команды REINDEX TABLE или REINDEX INDEX могут выполняться в блоке транзакций, а REINDEX CONCURRENTLY — нет.

REINDEX SYSTEM не поддерживает указание CONCURRENTLY, так как системные каталоги нельзя переиндексировать в неблокирующем режиме.

Более того, индексы, связанные с ограничениями-исключениями, тоже нельзя переиндексировать в неблокирующем режиме. Если явно указать имя такого индекса в этой команде, будет выдана ошибка. Если таблица или база данных с с такими индексами переиндексируются в неблокирующем режиме, эти индексы будут пропущены. (Можно переиндексировать такие индексы без параметра CONCURRENTLY.)

Примеры

Перестроение одного индекса:

REINDEX INDEX my_index;

Перестроение всех индексов таблицы my_table:

REINDEX TABLE my_table;

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

$ export PGOPTIONS="-P"
$ psql broken_db
...
broken_db=> REINDEX DATABASE broken_db;
broken_db=> \q

Перестроение индексов таблицы без блокировки операций чтения и записи в затрагиваемых процессом отношениях:

REINDEX TABLE CONCURRENTLY my_broken_table;

Совместимость

В стандарте SQL нет команды REINDEX.

См. также

CREATE INDEX, DROP INDEX, reindexdb

RELEASE SAVEPOINT

RELEASE SAVEPOINT — уничтожить ранее определенную точку сохранения

Синтаксис

RELEASE [ SAVEPOINT ] имя_точки_сохранения

Описание

Команда RELEASE SAVEPOINT уничтожает точку сохранения, ранее определенную в текущей транзакции.

Уничтожение точки сохранения делает ее неприменимой в качестве точки отката, но никаких других видимых для пользователя проявлений эта команда не имеет. Она не отменяет действие команд, выполненных после установки точки сохранения. (Для этого предназначена команда ROLLBACK TO SAVEPOINT.) Уничтожение ставшей ненужной точки сохранения позволяет системе восстановить некоторые ресурсы до завершения транзакции.

RELEASE SAVEPOINT также уничтожает все точки сохранения, которые были установлены после указанной.

Параметры

имя_точки_сохранения

Имя точки сохранения, подлежащей уничтожению.

Примечания

Указание имени точки сохранения, которая ранее не была определена, является ошибкой.

Невозможно уничтожить точку сохранения, когда транзакция находится в прерванном состоянии.

Если несколько точек сохранения имеют одинаковое имя, уничтожается только та, которая была определена последней.

Примеры

Установка и последующее уничтожение точки сохранения:

BEGIN;
    INSERT INTO table1 VALUES (3);
    SAVEPOINT my_savepoint;
    INSERT INTO table1 VALUES (4);
    RELEASE SAVEPOINT my_savepoint;
COMMIT;

Данная транзакция добавит значения 3 и 4.

Совместимость

Команда RELEASE SAVEPOINT соответствует стандарту SQL. Стандарт указывает, что ключевое слово SAVEPOINT является обязательным, но QHB позволяет его опустить.

См. также

BEGIN, COMMIT, ROLLBACK, ROLLBACK TO SAVEPOINT, SAVEPOINT

RESET

RESET — восстановить значение параметра времени выполнения до значения по умолчанию

Синтаксис

RESET параметр_конфигурации
RESET ALL

Описание

Команда RESET восстанавливает параметры времени выполнения до их значений по умолчанию. RESET — это альтернативное написание команды

SET параметр_конфигурации TO DEFAULT

Дополнительную информацию см. в разделе SET.

Значение по умолчанию определяется как значение, которое имел бы параметр, если бы для него в текущем сеансе не выполнялась команда SET. Фактическим источником этого значения может быть скомпилированное значение по умолчанию, файл конфигурации, параметры командной строки или параметры по умолчанию для каждой базы данных или для каждого пользователя. Это немного отличается от определения его как «значение, которое параметр имел при запуске сеанса», потому что если значение пришло из файла конфигурации, оно будет сброшено к тому, что указано в файле конфигурации в настоящий момент. См. дополнительную информацию в главе Конфигурация сервера.

Транзакционное поведение команды RESET совпадает с SET: ее действия будут отменены откатом транзакции.

Параметры

параметр_конфигурации

Имя настраиваемого параметра времени выполнения. Доступные параметры описаны в главе Конфигурация сервера и в разделе SET.

ALL

Сбрасывает все настраиваемые параметры времени выполнения до значений по умолчанию.

Примеры

Установить переменную конфигурации timezone в значение по умолчанию:

RESET timezone;

Совместимость

Команда RESET является расширением QHB.

См. также

SET, SHOW

REVOKE

REVOKE — отозвать права доступа

Синтаксис

REVOKE [ GRANT OPTION FOR ]
    { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
    [, ...] | ALL [ PRIVILEGES ] }
    ON { [ TABLE ] имя_таблицы [, ...]
         | ALL TABLES IN SCHEMA имя_схемы [, ...] }
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { { SELECT | INSERT | UPDATE | REFERENCES } ( имя_столбца [, ...] )
    [, ...] | ALL [ PRIVILEGES ] ( имя_столбца [, ...] ) }
    ON [ TABLE ] имя_таблицы [, ...]
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { { USAGE | SELECT | UPDATE }
    [, ...] | ALL [ PRIVILEGES ] }
    ON { SEQUENCE имя_последовательности [, ...]
         | ALL SEQUENCES IN SCHEMA имя_схемы [, ...] }
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { { CREATE | CONNECT | TEMPORARY | TEMP } [, ...] | ALL [ PRIVILEGES ] }
    ON DATABASE имя_базы_данных [, ...]
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { USAGE | ALL [ PRIVILEGES ] }
    ON DOMAIN имя_домена [, ...]
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { USAGE | ALL [ PRIVILEGES ] }
    ON FOREIGN DATA WRAPPER имя_обертки_сторонних_данных [, ...]
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { USAGE | ALL [ PRIVILEGES ] }
    ON FOREIGN SERVER имя_сервера [, ...]
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { EXECUTE | ALL [ PRIVILEGES ] }
    ON { { FUNCTION | PROCEDURE | ROUTINE } имя_функции [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] [, ...]
         | ALL { FUNCTIONS | PROCEDURES | ROUTINES } IN SCHEMA имя_схемы [, ...] }
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { USAGE | ALL [ PRIVILEGES ] }
    ON LANGUAGE имя_языка [, ...]
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { { SELECT | UPDATE } [, ...] | ALL [ PRIVILEGES ] }
    ON LARGE OBJECT oid_БО [, ...]
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
    ON SCHEMA имя_схемы [, ...]
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { CREATE | ALL [ PRIVILEGES ] }
    ON TABLESPACE имя_табличного_пространства [, ...]
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ GRANT OPTION FOR ]
    { USAGE | ALL [ PRIVILEGES ] }
    ON TYPE имя_типа [, ...]
    FROM указание_роли [, ...]
    [ CASCADE | RESTRICT ]

REVOKE [ ADMIN OPTION FOR ]
    имя_роли [, ...] FROM указание_роли [, ...]
    [ GRANTED BY указание_роли ]
    [ CASCADE | RESTRICT ]

Где указание_роли:

    [ GROUP ] имя_роли
  | PUBLIC
  | CURRENT_USER
  | SESSION_USER

Описание

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

Определение типов прав см. в описании команды GRANT.

Обратите внимание, что любая конкретная роль будет иметь сумму прав, предоставленных непосредственно ей, прав, предоставленных любой роли, членом которой она в настоящее время является, и прав, предоставленных группе PUBLIC. Таким образом, например, удаление у роли PUBLIC права SELECT не обязательно означает, что все роли потеряют права SELECT для объекта: те, кто получил право непосредственно или через другую роль, всё равно будут иметь право SELECT. Аналогично удаление у пользователя права SELECT может не повлиять на его возможность пользоваться этим правом, если оно выдано группе PUBLIC или другой роли, в которую включен пользователь.

Если указано GRANT OPTION FOR, отзывается только право передачи права, а не само право. В противном случае будут отменены и право, и возможность передачи.

Если пользователь имеет право с правом передачи и предоставил право другим пользователям, то такие права называются зависимыми. Если при отмене у первого пользователя самого права или права его передачи указано CASCADE, то зависимые права (если таковые существуют) тоже отменяются; в противном случае операция завершается ошибкой. Это рекурсивное аннулирование прав влияет только на права, предоставленные через цепочку пользователей, которая отслеживается до пользователя, являющегося субъектом команды REVOKE. Таким образом, пользователи могут в итоге сохранить это право, если оно было также получено через других пользователей.

При отмене прав на таблицу соответствующие права для столбца (если таковые имеются) также автоматически отменяются для каждого столбца таблицы. С другой стороны, если роли были предоставлены права на таблицу, то отзыв тех же прав для отдельных столбцов не будет иметь никакого эффекта.

При отзыве членства в роли указание GRANT OPTION меняется на ADMIN OPTION, но поведение очень похоже. Эта форма команды также принимает указание GRANTED BY, но в настоящее время оно игнорируется (за исключением проверки указанной в нем роли). Также обратите внимание, что эта форма команды не принимает избыточное слово GROUP в указании_роли.

Примечания

Пользователь может отозвать только те права, которые были предоставлены непосредственно им. Если, например, пользователь A предоставил право с правом передачи пользователю B, а пользователь B в свою очередь предоставил его пользователю C, то пользователь A не может напрямую отозвать право у пользователя C. Вместо этого пользователь A может отозвать право передачи у пользователя B и применить параметр CASCADE, чтобы право в свою очередь было отозвано у пользователя C. Другой пример: если и A, и B предоставили одно и то же право C, A может отозвать только то право, которое предоставил он сам, но не В, поэтому в итоге C всё равно будет иметь это право.

Если команду REVOKE пытается выполнить не владелец заданного объекта, она завершится ошибкой, если у пользователя нет никаких прав доступа к этому объекту. Если же у пользователя есть некоторые права, команда будет выполняться, но отменит только те права, которые пользователь может передавать. Формы REVOKE ALL PRIVILEGES будут выдавать предупреждающее сообщение, если у пользователя нет никаких прав на передачу, тогда как другие формы выдадут предупреждение, если пользователь не имеет права распоряжаться именно теми правами, которые указаны в команде. (В принципе, эти утверждения применимы и к владельцу объекта, но поскольку всегда считается, что ему разрешено распоряжаться всем правами, такие ситуации невозможны.)

Если суперпользователь запускает команду GRANT или REVOKE, она выполняется так, как если бы была запущена владельцем заданного объекта. Поскольку все права в конечном счете исходят от владельца объекта (возможно, косвенно через цепочки или через право распоряжением правом), суперпользователь может отозвать все права, но это может потребовать использования CASCADE, как уже говорилось выше.

Команда REVOKE также может быть выполнена ролью, которая не является владельцем заданного объекта, но является членом роли, которой принадлежит объект, или членом роли, обладающей правами доступа WITH GRANT OPTION для объекта. В этом случае команда выполняется так, как если бы ее задала содержащая роль, которая фактически владеет объектом или имеет права доступа WITH GRANT OPTION. Например, если таблица t1 принадлежит роли g1, членом которой является роль u1, то u1 может отзывать права на t1, которые записаны как предоставленные ролью g1. Это могут быть как права, данные ролью u1, так и предоставленные другими членами роли g1.

Если роль, выполняющая команду REVOKE, получила права косвенно через несколько путей членства в ролях, то не определено, какая именно роль будет выбрана для выполнения команды. В таких случаях рекомендуется использовать команду SET ROLE, чтобы переключиться на конкретную роль, от имени которой вы хотите выполнить команду REVOKE. Невыполнение этого требования может привести к отмене совсем не тех прав, которые были запланированы, или вообще ничего не отменить.

Дополнительную информацию о конкретных типах прав, а также о том, как проверять права объектов, см. в разделе Права.

Примеры

Удаление у группы public права добавлять данные в таблицу films:

REVOKE INSERT ON films FROM PUBLIC;

Удаление у пользователя manuel всех прав для представления kinds:

REVOKE ALL PRIVILEGES ON kinds FROM manuel;

Обратите внимание, что на самом деле это означает «Удаление всех прав, которые были выданы мной».

Исключение из членов роли admins пользователя joe:

REVOKE admins FROM joe;

Совместимость

Примечания по совместимости команды GRANT аналогично применимы и для команды REVOKE. В соответствии со стандартом требуется ключевое слово RESTRICT или CASCADE, но QHB предполагает RESTRICT по умолчанию.

См. также

GRANT, ALTER DEFAULT PRIVILEGES

ROLLBACK

ROLLBACK — прервать текущую транзакцию

Синтаксис

ROLLBACK [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]

Описание

Команда ROLLBACK откатывает текущую транзакцию и приводит к отмене всех изменений, произведенных этой транзакцией.

Параметры

WORK
TRANSACTION

Необязательные ключевые слова. Не имеют никакого эффекта.

AND CHAIN

Если указывается AND CHAIN, то сразу после окончания текущей транзакции начинается новая с теми же характеристиками (см. раздел SET TRANSACTION), что и только что завершенная. В противном случае новая транзакция не начинается.

Примечания

Для успешного завершения и фиксации транзакции используйте команду COMMIT.

При выполнении ROLLBACK вне блока транзакций будет выдано предупреждение и больше ничего не произойдет. Однако выполнение ROLLBACK AND CHAIN вне блока транзакции вызывает ошибку.

Примеры

Отмена всех изменений:

ROLLBACK;

Совместимость

Команда ROLLBACK соответствует стандарту SQL. Форма ROLLBACK TRANSACTION является расширением QHB.

См. также

BEGIN, COMMIT, ROLLBACK TO SAVEPOINT

ROLLBACK PREPARED

ROLLBACK PREPARED — отменить транзакцию, которая ранее была подготовлена для двухфазной фиксации

Синтаксис

ROLLBACK PREPARED id_транзакции

Описание

Команда ROLLBACK PREPARED откатывает транзакцию, которая находится в подготовленном состоянии.

Параметры

id_транзакции

Идентификатор транзакции, которую нужно откатить.

Примечания

Чтобы откатить подготовленную транзакцию, нужно быть тем же пользователем, который выполнял транзакцию изначально, либо суперпользователем. Но при этом не обязательно работать в том же сеансе, где выполнялась транзакция.

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

Все доступные в настоящее время подготовленные транзакции перечислены в системном представлении pg_prepared_xacts.

Примеры

Откат транзакции, имеющей идентификатор foobar:

ROLLBACK PREPARED 'foobar';

Совместимость

Команда ROLLBACK PREPARED является расширением QHB. Она предназначена для использования внешними системами управления транзакциями, некоторые из которых охватываются стандартами (например, X/Open XA), но сторона SQL этих систем не стандартизирована.

См. также

PREPARE TRANSACTION, COMMIT PREPARED

ROLLBACK TO SAVEPOINT

ROLLBACK TO SAVEPOINT — откатить к точке сохранения

Синтаксис

ROLLBACK [ WORK | TRANSACTION ] TO [ SAVEPOINT ] имя_точки_сохранения

Описание

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

Команда ROLLBACK TO SAVEPOINT неявно уничтожает все точки сохранения, которые были установлены после указанной.

Параметры

имя_точки_сохранения

Точка сохранения, к которой нужно откатиться.

Примечания

Чтобы уничтожить точку сохранения, не отменяя действия команд, выполненных после ее установки, используйте команду RELEASE SAVEPOINT.

Указание имени точки сохранения, которое не было установлено, является ошибкой.

Курсоры имеют несколько нетранзакционное поведение по отношению к точкам сохранения. Любой курсор, открытый внутри точки сохранения, будет закрыт при ее откате. Если команда FETCH или MOVE перемещает ранее открытый курсор внутри точки сохранения, которая затем откатывается назад, то этот курсор остается в том положении, в котором оказался после выполнения FETCH (т. е. движение курсора, вызванное FETCH, не откатывается). Также при откате не отменяется и закрытие курсора. Однако другие побочные эффекты, вызванные запросом курсора (например, побочные действия вызываемых запросом изменчивых функций) отменяются, если они происходят после точки сохранения, к которой затем происходит откат. Курсор, выполнение которого приводит к прерыванию транзакции, переводится в нерабочее состояние, поэтому, хотя транзакцию можно восстановить с помощью ROLLBACK TO SAVEPOINT, такой курсор больше использовать нельзя.

Примеры

Отмена действия команд, выполненных после установки точки сохранения my_savepoint:

ROLLBACK TO SAVEPOINT my_savepoint;

Откат к точке сохранения не отражается на положении курсора:

BEGIN;

DECLARE foo CURSOR FOR SELECT 1 UNION SELECT 2;

SAVEPOINT foo;

FETCH 1 FROM foo;
 ?column?
----------
        1

ROLLBACK TO SAVEPOINT foo;

FETCH 1 FROM foo;
 ?column?
----------
        2

COMMIT;

Совместимость

Стандарт SQL указывает, что ключевое слово SAVEPOINT является обязательным, но QHB и Oracle позволяют его опустить. SQL позволяет в качестве избыточного слова после ROLLBACK только WORK, но не TRANSACTION. Кроме того, в SQL есть необязательное предложение AND [ NO ] CHAIN, которое в настоящее время не поддерживается QHB. В остальном эта команда соответствует стандарту SQL.

Смотрите также

BEGIN, COMMIT, RELEASE SAVEPOINT, ROLLBACK, SAVEPOINT

SAVEPOINT

SAVEPOINT — определить новую точку сохранения в текущей транзакции

Синтаксис

SAVEPOINT имя_точки_сохранения

Описание

Команда SAVEPOINT устанавливает новую точку сохранения в текущей транзакции.

Точка сохранения — это специальная метка внутри транзакции, которая позволяет откатить действие всех команд, выполненных после того, как она была установлена, восстанавливая транзакцию до того состояния, каким оно было на момент установки этой точки.

Параметры

имя_точки_сохранения

Имя, присваиваемое новой точке сохранения.

Примечания

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

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

Примеры

Установка точки сохранения с последующей отменой действий всех команд, выполненных после ее создания:

BEGIN;
  INSERT INTO table1 VALUES (1);
    SAVEPOINT my_savepoint;
    INSERT INTO table1 VALUES (2);
    ROLLBACK TO SAVEPOINT my_savepoint;
    INSERT INTO table1 VALUES (3);
COMMIT;

Вышеуказанная транзакция добавит в таблицу значения 1 и 3, но не 2.

Установка и последующее уничтожение точки сохранения:

BEGIN;
    INSERT INTO table1 VALUES (3);
    SAVEPOINT my_savepoint;
    INSERT INTO table1 VALUES (4);
    RELEASE SAVEPOINT my_savepoint;
COMMIT;

Данная транзакция добавит значения 3 и 4.

Совместимость

Стандарт SQL требует, чтобы точка сохранения уничтожалась автоматически при установке другой точки сохранения с тем же именем. В QHB сохраняется и старая точка сохранения, хотя при откате и освобождении будет использоваться только самая последняя точка. (Освобождение новой точки сохранения с помощью RELEASE SAVEPOINT приведет к тому, что старая точка сохранения снова станет доступной для команд ROLLBACK TO SAVEPOINT и RELEASE SAVEPOINT.) В остальном команда SAVEPOINT полностью соответствует стандарту.

См. также

BEGIN, COMMIT, RELEASE SAVEPOINT, ROLLBACK, ROLLBACK TO SAVEPOINT

SECURITY LABEL

SECURITY LABEL — определить или изменить метку безопасности, примененную к объекту

Синтаксис

SECURITY LABEL [ FOR провайдер ] ON
{
  TABLE имя_объекта |
  COLUMN имя_таблицы.имя_столбца |
  AGGREGATE имя_агрегатной_функции ( сигнатура_агрегатной_функции ) |
  DATABASE имя_объекта |
  DOMAIN имя_объекта |
  EVENT TRIGGER имя_объекта |
  FOREIGN TABLE имя_объекта
  FUNCTION имя_функции [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] |
  LARGE OBJECT oid_большого_объекта |
  MATERIALIZED VIEW имя_объекта |
  [ PROCEDURAL ] LANGUAGE имя_объекта |
  PROCEDURE имя_процедуры [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] |
  PUBLICATION имя_объекта |
  ROLE имя_объекта |
  ROUTINE имя_подпрограммы [ ( [ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [, ...] ] ) ] |
  SCHEMA имя_объекта |
  SEQUENCE имя_объекта |
  SUBSCRIPTION имя_объекта |
  TABLESPACE имя_объекта |
  TYPE имя_объекта |
  VIEW имя_объекта
} IS 'метка'

Где сигнатура_агрегатной_функции это:

* |
[ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ] |
[ [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ] ] ORDER BY [ режим_аргумента ] [ имя_аргумента ] тип_аргумента [ , ... ]

Описание

Команда SECURITY LABEL применяет метку безопасности к объекту базы данных. С объектом базы данных можно связать произвольное количество меток безопасности, по одной на каждого провайдера. Провайдеры меток — это загружаемые модули, которые регистрируются с помощью функции register_label_provider.

Примечание
register_label_provider не является SQL-функцией; она может быть вызвана только из кода C, загруженного в серверную часть.

Провайдер меток определяет, является ли данная метка допустимой и разрешено ли присвоить эту метку данному объекту. Смысл заданной метки тоже определяет провайдер меток. QHB не накладывает никаких ограничений на то, как провайдер меток должен интерпретировать метки безопасности; он просто предоставляет механизм их хранения. На практике это средство предназначено для обеспечения интеграции с системами обязательного контроля доступа на основе меток (MAC), такими как SELinux. Такие системы принимают все решения по управлению доступом на основе меток объектов, а не на традиционных концепциях избирательного управления доступом (discretionary access control, DAC), таких как пользователи и группы.

Параметры

имя_объекта
имя_таблицы.имя_столбца
имя_агрегатной_функции
имя_функции
имя_процедуры
имя_подпрограммы

Имя маркируемого объекта. Имена таблиц, агрегатных и обычных функций, доменов, сторонних таблиц, процедур, подпрограмм, последовательностей, типов и представлений могут быть дополнены схемой.

провайдер

Имя провайдера, с которым должна быть связана эта метка. Указанный провайдер должен быть загружен и быть готов к предложенной операции маркировки. Если загружен только один провайдер, имя провайдера может быть опущено для краткости.

режим_аргумента

Режим аргумента функции, процедуры или агрегатной функции: IN, OUT, INOUT или VARIADIC. Если этот параметр опущен, значение по умолчанию равно IN. Обратите внимание, что SECURITY LABEL на самом деле не обращает никакого внимания на аргументы OUT, так как для определения идентичности функции необходимы только входные аргументы. Так что достаточно перечислить аргументы IN, INOUT и VARIADIC.

имя_аргумента

Имя аргумента функции, процедуры или агрегатной функции. Обратите внимание, что SECURITY LABEL на самом деле не обращает никакого внимания на имена аргументов, поскольку для определения идентификатора функции необходимы только типы данных аргументов.

тип_аргумента

Тип данных аргумента функции, процедуры или агрегата.

oid_большого_объекта

OID большого объекта.

PROCEDURAL

Это слово не несет смысловой нагрузки.

метка

Новая метка безопасности, записанная как строковая константа, либо NULL чтобы удалить метку безопасности.

Примеры

Следующий пример показывает, как можно изменить метку безопасности для таблицы.

SECURITY LABEL FOR selinux ON TABLE mytable IS 'system_u:object_r:sepgsql_table_t:s0';

Совместимость

В стандарте SQL нет команды SECURITY LABEL.

SELECT INTO

SELECT INTO — определить новую таблицу из результатов запроса

Синтаксис

[ WITH [ RECURSIVE ] запрос_WITH [, ...] ]
SELECT [ ALL | DISTINCT [ ON ( выражение [, ...] ) ] ]
    * | выражение [ [ AS ] имя_результата ] [, ...]
    INTO [ TEMPORARY | TEMP | UNLOGGED ] [ TABLE ] новая_таблица
    [ FROM элемент_FROM [, ...] ]
    [ WHERE условие ]
    [ GROUP BY выражение [, ...] ]
    [ HAVING условие [, ...] ]
    [ WINDOW имя_окна AS ( определение_окна ) [, ...] ]
    [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ]
    [ ORDER BY выражение [ ASC | DESC | USING оператор ] [ NULLS { FIRST | LAST } ] [, ...] ]
    [ LIMIT { число | ALL } ]
    [ OFFSET начало [ ROW | ROWS ] ]
    [ FETCH { FIRST | NEXT } [ число ] { ROW | ROWS } ONLY ]
    [ FOR { UPDATE | SHARE } [ OF имя_таблицы [, ...] ] [ NOWAIT ] [...] ]

Описание

Команда SELECT INTO создает новую таблицу и заполняет ее данными, вычисленными с помощью запроса. Данные не возвращаются клиенту, как это происходит с обычным SELECT. Столбцы новой таблицы имеют имена и типы данных, связанные с выходными столбцами SELECT.

Параметры

TEMPORARY или TEMP

Если этот параметр указан, таблица создается как временная таблица. Дополнительную информацию см. в разделе CREATE TABLE.

UNLOGGED

Если этот параметр указан, таблица создается как нежурналируемая таблица. Дополнительную информацию см. в разделе CREATE TABLE.

новая_таблица

Имя создаваемой таблицы (может быть дополнено схемой).

Все остальные параметры подробно описаны в разделе SELECT.

Примечания

Команда SELECT INTO функционально аналогична команде CREATE TABLE AS. Однако рекомендуется использовать CREATE TABLE AS, так как форма SELECT INTO в ECPG или PL/pgSQL не поддерживается, поскольку они интерпретируют предложение INTO по-своему. Кроме того, CREATE TABLE AS, предлагает больший набор функций, чем SELECT INTO.

В отличие от CREATE TABLE AS, команда SELECT INTO не позволяет указывать некоторые свойства, например, метод доступа к таблице с помощью USING метод или табличное пространство таблицы с помощью TABLESPACE имя_табличного_пространства. Поэтому при возникновении такой необходимости лучше использовать команду CREATE TABLE AS. Таким образом, для новой таблицы выбирается метод доступа к таблице по умолчанию. Дополнительную информацию см. в разделе default_table_access_method.

Примеры

Создание таблицы films_recent, содержащей только последние записи из таблицы films:

SELECT * INTO films_recent FROM films WHERE date_prod >= '2002-01-01';

Совместимость

Стандарт SQL применяет команду SELECT INTO для передачи скалярных значений клиентской программе, а не для создания новой таблицы. Именно это применение описано в ECPG и PL/pgSQL (см. PL/pgSQL). В QHB применение SELECT INTO для создания таблиц имеет исторические причины. В новом коде для этой цели лучше всего использовать CREATE TABLE AS.

См. также

CREATE TABLE AS

SELECT

SELECT, TABLE, WITH — получить строки из таблицы или представления

Синтаксис

[ WITH [ RECURSIVE ] запрос_WITH [, ...] ]
SELECT [ ALL | DISTINCT [ ON ( выражение [, ...] ) ] ]
    [ * | выражение [ [ AS ] имя_результата ] [, ...] ]
    [ FROM элемент_FROM [, ...] ]
    [ WHERE условие ]
    [ GROUP BY элемент_группирования [, ...] ]
    [ HAVING условие ]
    [ WINDOW имя_окна AS ( определение_окна ) [, ...] ]
    [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] выборка ]
    [ ORDER BY выражение [ ASC | DESC | USING оператор ] [ NULLS { FIRST | LAST } ] [, ...] ]
    [ LIMIT { число | ALL } ]
    [ OFFSET начало [ ROW | ROWS ] ]
    [ FETCH { FIRST | NEXT } [ число ] { ROW | ROWS } { ONLY | WITH TIES } ]
    [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF имя_таблицы [, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ]

Где элемент_FROM может быть:

    [ ONLY ] имя_таблицы [ * ] [ [ AS ] псевдоним [ ( псевдоним_столбца [, ...] ) ] ]
                [ TABLESAMPLE метод_выборки ( аргумент [, ...] ) [ REPEATABLE ( затравка ) ] ]
    [ LATERAL ] ( выборка ) [ AS ] псевдоним [ ( псевдоним_столбца [, ...] ) ]
    имя_запроса_WITH [ [ AS ] псевдоним [ ( псевдоним_столбца [, ...] ) ] ]
    [ LATERAL ] имя_функции ( [ аргумент [, ...] ] )
                [ WITH ORDINALITY ] [ [ AS ] псевдоним [ ( псевдоним_столбца [, ...] ) ] ]
    [ LATERAL ] имя_функции ( [ аргумент [, ...] ] ) [ AS ] псевдоним ( определение_столбца [, ...] )
    [ LATERAL ] имя_функции ( [ аргумент [, ...] ] ) AS ( определение_столбца [, ...] )
    [ LATERAL ] ROWS FROM( имя_функции ( [ аргумент [, ...] ] ) [ AS ( определение_столбца [, ...] ) ] [, ...] )
                [ WITH ORDINALITY ] [ [ AS ] псевдоним [ ( псевдоним_столбца [, ...] ) ] ]
    элемент_FROM [ NATURAL ] тип_соединения элемент_FROM [ ON условие_соединения | USING ( столбец_соединения [, ...] ) ]

Где элемент_группирования может быть:

    ( )
    выражение
    ( выражение [, ...] )
    ROLLUP ( { выражение | ( выражение [, ...] ) } [, ...] )
    CUBE ( { выражение | ( выражение [, ...] ) } [, ...] )
    GROUPING SETS ( элемент_группирования [, ...] )

Где запрос_WITH может быть:

    имя_запроса_WITH [ ( имя_столбца [, ...] ) ] AS [ [ NOT ] MATERIALIZED ] ( select | values | insert | update | delete )

TABLE [ ONLY ] имя_таблицы [ * ]

Описание

Команда SELECT извлекает строки из перечня таблиц, возможно, пустого. Общая обработка при помощи SELECT выглядит следующим образом:

Вычисляются все запросы в списке WITH. Они фактически служат временными таблицами, к которым можно обращаться в списке FROM. Запрос WITH, на который в FROM обращаются более одного раза, вычисляется только один раз, если иное не указано в параметре NOT MATERIALIZED. (См. подраздел Предложение WITH ниже.)

Вычисляются все элементы в списке FROM. (Каждый элемент в списке FROM — это реальная или виртуальная таблица.) Если в списке
указано более одного элемента, они объединяются перекрестным соединением. (См. подраздел Предложение FROM ниже.)

Если указано предложение WHERE, все строки, которые не удовлетворяют условию, исключаются из результата. (См. подраздел Предложение WHERE ниже.)

Если указано предложение GROUP BY или вызываются агрегатные функции, то вывод объединяется в группы строк, соответствующие одному или нескольким значениям, и вычисляются результаты агрегатных функций. Если присутствует предложение HAVING, оно исключает группы, не удовлетворяющие данному условию. (См. подразделы Предложение GROUP BY и Предложение HAVING ниже.)

Вычисляются фактические выходные строки по заданным в SELECT выходным выражениям для каждой выбранной строки или группы строк. (См. подраздел Список SELECT ниже.)

SELECT DISTINCT удаляет повторяющиеся строки из результата. SELECT DISTINCT ON удаляет строки, соответствующие всем указанным выражениям. SELECT ALL (значение по умолчанию) возвращает все строки-кандидаты, включая дубликаты. (См. подраздел Предложение DISTINCT ниже.)

Операторы UNION, INTERSECT и EXCEPT объединяют выходные данные нескольких команд SELECT в один результирующий набор. Оператор UNION возвращает все строки, находящиеся в одном или обоих результирующих наборах. Оператор INTERSECT возвращает все строки, которые строго находятся в обоих результирующих наборах. Оператор EXCEPT возвращает строки, которые находятся в первом результирующем наборе, но не во втором. Во всех трех случаях повторяющиеся строки исключаются, если только явно не задан оператор ALL. Можно добавить необязательный оператор DISTINCT, чтобы явно указать, что требуется устранение повторяющихся строк. Заметьте, что в данном случае DISTINCT — это поведение по умолчанию, хотя в самой команде SELECT по умолчанию подразумевается ALL. (См. подразделы Предложение UNION, Предложение INTERSECT и Предложение EXCEPT ниже.)

Если указано предложение ORDER BY, возвращаемые строки сортируются в указанном порядке. Если ORDER BY не задается, строки возвращаются в любом порядке, который система считает наиболее быстрым для получения. (См. подраздел Предложение ORDER BY ниже.)

Если указано предложение LIMIT (или FETCH FIRST) или OFFSET, команда SELECT возвращает только подмножество результирующих строк. (См. подраздел Предложение LIMIT ниже.)

Если указано FOR UPDATE, FOR NO KEY UPDATE, FOR SHARE или FOR KEY SHARE, команда SELECT блокирует выбранные строки от одновременных обновлений. (См. подраздел Предложение блокировки ниже.)

Для всех столбцов, задействованных в команде SELECT, необходимо иметь право SELECT. Использование блокировок FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE и FOR KEY SHARE требует права UPDATE (по меньшей мере для одного столбца каждой таблицы, выбранной для блокировки таким образом).

Параметры

Предложение WITH

Предложение WITH позволяет указать один или несколько подзапросов, на которые можно ссылаться по имени в основном запросе. Подзапросы в сущности действуют как временные таблицы или представления на протяжении выполнения всего основного запроса. Каждый подзапрос может представлять собой оператор SELECT, TABLE, VALUES, INSERT, UPDATE или DELETE. При использовании в WITH операторов, изменяющих данные (INSERT, UPDATE или DELETE), обычно добавляется предложение RETURNING. Именно результат RETURNING, а не нижележащая таблица, изменяемая запросом, формирует временную таблицу, которую затем читает основной запрос. Если RETURNING опущено, оператор всё равно выполняется, но не выдает никаких выходных данных, поэтому на него нельзя ссылаться в качестве таблицы в основном запросе.

Имя (без схемы) должно быть указано для каждого запроса WITH. При необходимости можно указать список имен столбцов; если этот список опущен, имена столбцов выводятся из подзапроса.

Если указано RECURSIVE, подзапрос SELECT может ссылаться сам на себя по имени. Такой подзапрос должен иметь вид

нерекурсивная_часть UNION [ ALL | DISTINCT ] рекурсивная_часть

где рекурсивная_часть должна появиться в правой части от UNION. Для каждого запроса допускается только одна рекурсивная ссылка. Операторы изменения данных не могут быть рекурсивными, но в них можно использовать результаты рекурсивного запроса SELECT. Пример см. в разделе Запросы WITH.

Еще одна особенность RECURSIVE заключается в том, что запросы WITH не должны быть упорядоченными: один запрос может ссылаться на другой, который находится в списке после него. (Однако циклические ссылки или взаимная рекурсия не поддерживаются.) Без RECURSIVE запросы в WITH могут ссылаться только на запросы того же уровня в WITH, предшествующие им в списке WITH.

Когда в предложении WITH содержится несколько запросов, RECURSIVE нужно написать только один раз, сразу после WITH. Этот параметр будет действовать на все запросы в предложении WITH, кроме тех, которые не используют рекурсию или ссылки на последующие запросы.

Основной запрос и запросы WITH, условно говоря, выполняются одновременно. Это означает, что действие оператора, изменяющего данные в WITH, не будет видно из других частей запроса, кроме как при чтении его вывода в RETURNING. Если два таких оператора изменения данных пытаются изменить одну и ту же строку, результат будет неопределенным.

Ключевое свойство запросов WITH состоит в том, что обычно они вычисляются только один раз для всего основного запроса, даже если тот ссылается на них более одного раза. В частности, операторы изменения данных гарантированно выполняются один и только один раз, независимо от того, будет ли их результат прочитан основным запросом и в каком объеме.

Однако, добавив для запроса WITH пометку NOT MATERIALIZED, можно отказаться от этой гарантии. В таком случае запрос WITH может быть свернут в основной запрос так же, как если бы это был простой подзапрос SELECT внутри предложения FROM основного запроса. Это приводит к дублированию вычислений, если основной запрос ссылается на запрос в WITH более одного раза; но если для каждого такого использования требуется только несколько строк из всего результата запроса WITH, указание NOT MATERIALIZED, позволяющее оптимизировать запросы совместно, в целом, может оказаться выгодным. NOT MATERIALIZED игнорируется в запросе WITH, если тот является рекурсивным или не свободен от побочных эффектов (т. е. когда не является простым SELECT без изменчивых функций).

По умолчанию запрос WITH без побочных эффектов включается в основной запрос, если в своем предложении FROM тот используется ровно один раз. Это позволяет совместно оптимизировать два уровня запросов в ситуациях, когда это должно быть семантически незаметно. Однако такое сворачивание можно предотвратить, если пометить запрос WITH как MATERIALIZED. Это может быть полезно, например, если предложение WITH используется в качестве преграды для оптимизатора, чтобы предотвратить выбор планировщиком плохого плана.

Дополнительную информацию см. в разделе Запросы WITH.

Предложение FROM

Предложение FROM указывает одну или несколько исходных таблиц, служащих источником данных для SELECT. Если указано несколько источников, результатом является декартово произведение (перекрестное соединение) всех строк. Но обычно в запрос добавляются уточняющие условия (через WHERE), чтобы ограничить возвращаемый набор строк небольшим подмножеством декартова произведения.

Предложение FROM может содержать следующие элементы:

имя_таблицы

Имя существующей таблицы или представления (может быть дополнено схемой). Если перед именем таблицы указывается ONLY, сканируется только эта таблица. Если ONLY не указано, сканируются таблица и все ее потомки (если таковые имеются). После имени таблицы можно добавить необязательное указание *, чтобы явно обозначить, что блокировка затрагивает и все дочерние таблицы.

псевдоним

Псевдоним, заменяющий имя для элемента списка FROM. Используется для краткости или для устранения неоднозначности с замкнутыми соединениями (где одна и та же таблица сканируется несколько раз). Когда задается псевдоним, он полностью скрывает фактическое имя таблицы или функции; например, задано FROM foo AS f, остальная часть запроса SELECT должна обращаться к этому элементу FROM по имени f, а не foo. Если задан псевдоним таблицы, то в целях обеспечения замены имени для одного или нескольких столбцов таблицы также может быть записан список псевдонимов.

TABLESAMPLE метод_выборки ( аргумент [, ...] ) [ REPEATABLE ( затравка ) ]

Предложение TABLESAMPLE после имени_таблицы указывает на то, что для получения подмножества строк в этой таблице должен использоваться заданный метод_выборки. Этот метод_выборки предшествует применению любых других фильтров, например, предложения WHERE. Стандартный дистрибутив QHB включает два метода выборки, BERNOULLI и SYSTEM, а другие методы выборки могут быть установлены в базе данных с помощью расширений.

Методы выборки BERNOULLI и SYSTEM принимают единственный аргумент, который определяет, какой процент таблицы должен попасть в выборку в диапазоне от 0 до 100. Этот аргумент может быть любым выражением со значением типа real. (Другие методы выборки могут принимать большее число аргументов или другие аргументы.) Эти два метода возвращают случайную выборку таблицы, которая будет содержать приблизительно указанный процент строк таблицы. Метод BERNOULLI сканирует всю таблицу и выбирает или игнорирует отдельные строки независимо с заданной вероятностью. Метод SYSTEM делает выборку на уровне блоков, для каждого блока определяя шанс его задействовать; возвращаются все строки в каждом выбранном блоке. Метод SYSTEM значительно быстрее метода BERNOULLI, когда выбирается небольшой процент строк, но в результате эффектов кучности он может возвращать менее случайную выборку таблицы.

В необязательном предложении REPEATABLE определяется аргумент затравка — число или выражение, используемое для генерации случайных чисел в рамках метода выборки. Значением затравки может быть любое отличное от NULL число с плавающей запятой. Два запроса, в которых указаны одинаковые значения затравки и аргумента, выдадут одну и ту же выборку таблицы при условии неизменности содержимого последней. Но разные значения затравки обычно создают разные выборки. В отсутствие предложения REPEATABLE для каждого запроса выбирается новая случайная выборка, основанная на затравке, сгенерированной системой. Обратите внимание, что некоторые дополнительные методы выборки не принимают предложение REPEATABLE и выдают разные выборки при каждом использовании.

выборка

Предложение FROM может содержать подзапрос SELECT. Это действует так, словно на время выполнения основной команды SELECT его выходные данные были созданы в виде временной таблицы. Обратите внимание, что подзапрос SELECT должен быть заключен в круглые скобки и для него должен быть задан псевдоним. Здесь также можно использовать команду VALUES.

имя_запроса_WITH

На запрос WITH можно ссылаться по имени, как если бы имя запроса было именем таблицы. (На самом деле запрос WITH скрывает любую реальную таблицу с тем же именем для целей основного запроса. При необходимости можно обратиться к реальной таблице с тем же именем, указав имя таблицы в схеме.) Так же, как и для имени таблицы, для этого имени можно задать псевдоним.

имя_функции

В предложении FROM могут содержаться вызовы функций. (Это особенно полезно для функций, возвращающих результирующие множества, но можно использовать любую функцию.) Это действует так, как если бы выходные данные функции были созданы как временная таблица на время выполнения основной команды SELECT. Когда к вызову функции добавляется необязательное предложение WITH ORDINALITY, после всех выдаваемых функцией столбцов функции добавляется новый столбец с номерами строк.

Так же, как и для таблицы, для функции можно задать псевдоним. Если псевдоним задан, то также можно задать список псевдонимов столбцов, чтобы предоставить альтернативные имена для одного или нескольких атрибутов составного типа результата функции, включая имя столбца, добавленного предложением ORDINALITY, если оно присутствует.

Несколько вызовов функций могут быть объединены в одном элементе предложения FROM, если заключить их в конструкцию ROWS FROM( ... ). Результатом такого элемента будет сцепление первых строк из каждой функции, затем вторых строк из каждой функции и т. д. Если какие-то функции выдают меньше строк, чем другие, то отсутствующие данные заменяются значением NULL, так что общее количество возвращаемых строк всегда такое же, как для функции, которая выдала наибольшее количество строк.

Если функция была определена как возвращающая тип данных record, для нее нужно указать псевдоним или ключевое слово AS, а затем список определений столбцов в форме ( имя_столбца тип_данных [, ... ]). Список определений столбцов должен соответствовать фактическому количеству и типам столбцов, возвращаемых функцией.

Если при использовании синтаксиса ROWS FROM( ... ) одной из функций требуется список определений столбцов, предпочтительно поместить список определений столбцов после вызова функции внутри ROWS FROM( ... ). Список определений столбцов можно поместить после конструкции ROWS FROM( ... ), только если вызывается всего одна функция, а предложение WITH ORDINALITY отсутствует.

Для того чтобы использовать WITH ORDINALITY вместе со списком определений столбцов, необходимо использовать запись ROWS FROM( ... ) и поместить список определений столбцов внутрь ROWS FROM( ... ).

тип_соединения

Один из

[INNER ] JOIN

LEFT [OUTER ] JOIN

RIGHT [OUTER ] JOIN

FULL [OUTER ] JOIN

CROSS JOIN

Для типов соединений INNER и OUTER должно быть указано условие соединения, а именно одно из предложений NATURAL, ON условие_соединения или USING (столбец_соединения [, ... ]). См. их описания ниже. Для CROSS JOIN ни одно из этих предложений не допускается.

Предложение JOIN объединяет в себе два элемента списка FROM, которые для удобства будем называть «таблицами», хотя на самом деле они могут быть любого типа, допустимого в качестве элемента FROM. При необходимости используйте круглые скобки для определения порядка вложенности. При отсутствии круглых скобок предложение JOIN обрабатывается слева направо. В любом случае, JOIN связывает элементы сильнее, чем разделяющие запятые в списке FROM.

CROSS JOIN и INNER JOIN формируют простое декартово произведение — такой же результат, какой можно получить, указав две таблицы на верхнем уровне FROM, но ограниченный возможным условием соединения (если таковое имеется). Предложение CROSS JOIN эквивалентно INNER JOIN ON (TRUE), то есть никакие строки не удаляются по условию. Эти типы соединения введены исключительно для удобства записи, так как они не делают ничего, что нельзя было бы сделать с помощью простого FROM и WHERE.

LEFT OUTER JOIN возвращает все строки ограниченного декартова произведения (т. е. все объединенные строки, попадающие под условие соединения) плюс все строки в таблице слева, для которых не находится строк в таблице справа, удовлетворяющих условию соединения. Левая строка дополняется значениями NULL справа до полной ширины объединенной таблицы. Обратите внимание, что только условие самого предложения JOIN учитывается при принятии решения о том, какие строки двух таблиц соответствуют друг другу. И только затем применяются внешние условия.

Напротив, RIGHT OUTER JOIN возвращает все соединенные строки, а также одну строку для каждой строки справа, не имеющей соответствия слева (дополненные значениями NULL слева). Это предложение введено для удобства, так как его можно преобразовать в LEFT OUTER JOIN, изменив положение левой и правой таблиц.

FULL OUTER JOIN возвращает все соединенные строки плюс все строки слева, не имеющие соответствия справа (дополненные значениями NULL справа), плюс все строки справа, не имеющие соответствия слева (дополненные значениями NULL слева).

ON условие_соединения

Аргумент условие_соединения является выражением, возвращающим значение типа boolean (как в предложении WHERE) и указывающим, какие строки при соединении считаются сопоставимыми.

USING ( столбец_соединения [, ...] )

Предложение вида USING ( a, b, ... ) представляет собой сокращенную форму записи ON таблица_слева.a = таблица_справа.a AND таблица_слева.b = таблица_справа.b .... Также USING подразумевает, что только один из каждой пары эквивалентных столбцов будет включен в результат соединения, а не оба.

NATURAL

NATURAL является сокращенной формой USING со списком, который содержит все столбцы в двух таблицах, имеющие одинаковые имена. Если нет одинаковых имен столбцов, NATURAL эквивалентно ON TRUE.

LATERAL

Ключевое слово LATERAL может предшествовать подзапросу SELECT в списке FROM. Оно позволяет в этом вложенном SELECT обращаться к столбцам элементов FROM, которые появляются перед ним в списке FROM. (Без LATERAL каждый вложенный SELECT обрабатывается независимо и поэтому не может перекрестно ссылаться на другие элементы списка FROM.)

LATERAL может также предшествовать вызову функции в списке FROM, но в данном случае оно будет избыточным, потому что выражения с функциями в любом случае могут ссылаться на предыдущие элементы списка FROM.

Элемент LATERAL может быть указан и в верхнем уровне списка FROM, и в дереве JOIN. В последнем случае он может ссылаться на любые элементы, находящиеся в левой части JOIN, от которого он находится справа.

Когда элемент FROM содержит ссылки LATERAL, запрос будет выполнен следующим образом: для каждой строки элемента FROM, предоставляющего столбец (или столбцы) с перекрестной ссылкой, или набора строк из нескольких элементов FROM, предоставляющих столбцы, элемент LATERAL вычисляется с использованием этой строки или значений набора строк столбцов. Результирующие строки, как обычно, объединяются со строками, из которых они были вычислены. Эта процедура повторяется для каждой строки или набора строк из исходной таблицы (или таблиц).

Исходная таблица (или таблицы) столбца должна быть связана с элементом LATERAL соединением INNER или LEFT, иначе не образуется однозначно определенного набора строк, из которого можно было бы вычислить набор строк для LATERAL. Таким образом, хотя такая конструкция, как X RIGHT JOIN LATERAL Y, является синтаксически допустимой, фактически Y не может обращаться к X.

Предложение WHERE

Необязательное предложение WHERE

WHERE условие

где условие — это любое выражение, которое выдает результат типа boolean. Любая строка, которая не удовлетворяет этому условию, будет исключена из результата. Строка удовлетворяет условию, если возвращает true, когда вместо ссылок на переменные подставляются фактические значения строки.

Предложение GROUP BY

Необязательное предложение GROUP BY имеет общую форму

GROUP BY элемент_группирования [, ...]

GROUP BY собирает в одну строку все выбранные строки, имеющие одинаковые значения для выражений группировки. Таким выражением, использованным внутри элемента_группирования, может быть имя входного столбца, либо имя или порядковый номер выходного столбца (из списка элементов SELECT), либо произвольное выражение, сформированное из значений входных столбцов. В случае неоднозначности имя в GROUP BY будет интерпретироваться как имя входного, а не выходного столбца.

Если в элементе группирования задается GROUPING SETS, ROLLUP или CUBE, то предложение GROUP BY в целом определяет некоторое количество независимых наборов группирования. Эффект от этого равнозначен объединению подзапросов (с UNION ALL) с отдельными наборами группирования в качестве их предложений GROUP BY. Дополнительную информацию об обработке наборов группирования см. в разделе GROUPING SETS, CUBE и ROLLUP.

Агрегатные функции, если они используются, вычисляются по всем строкам, составляющим каждую группу, выдавая отдельное значение для каждой группы. (Если указаны агрегатные функции, но нет предложения GROUP BY, запрос выполняется как если бы была одна группа, содержащая все выбранные строки.) Набор строк, подаваемых в каждую агрегатную функцию, может быть дополнительно отфильтрован путем добавления предложения FILTER к вызову агрегатной функции; дополнительную информацию см. в разделе Агрегатные выражения. С предложением FILTER на вход агрегатной функции будут поступать только строки, соответствующие фильтру.

Когда указано предложение GROUP BY или какая-либо агрегатная функция, выражения в списке SELECT не могут обращаться к негруппируемым столбцам, кроме как в агрегатных функциях или если негруппируемый столбец функционально зависит от группируемых столбцов, поскольку в противном случае в негруппируемом столбце вернулось бы более одного возможного значения. Функциональная зависимость появляется, если группируемые столбцы (или их подмножество) являются первичным ключом таблицы, содержащей негруппируемый столбец.

Имейте в виду, что все агрегатные функции вычисляются перед вычислением любых «скалярных» выражений в предложении HAVING или списке SELECT. Это означает, что, например, нельзя использовать выражение CASE, чтобы пропустить вычисление агрегатной функции; см. раздел Правила вычисления выражений.

В настоящий момент вместе с GROUP BY нельзя задать указания FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE и FOR KEY SHARE.

Предложение HAVING

Необязательное предложение HAVING имеет общую форму

HAVING условие

где условие задается так же, как и в предложении WHERE.

Предложение HAVING исключает из результата не удовлетворяющие условию строки групп. HAVING отличается от WHERE: WHERE фильтрует отдельные строки перед применением GROUP BY, тогда как HAVING фильтрует группы строк, созданные с помощью GROUP BY. Каждый столбец, указанный в условии, должен однозначно ссылаться на группируемый столбец, если только ссылка не находится внутри агрегатной функции или негруппируемый столбец функционально зависит от группируемых.

Наличие предложения HAVING превращает запрос в группируемый, даже если отсутствует предложение GROUP BY. То же самое происходит, когда запрос содержит агрегатные функции, но нет предложения GROUP BY. Все выбранные строки считаются образующими одну группу, а в списке SELECT и предложении HAVING можно обращаться к столбцам таблицы только из числа агрегатных функций. Такой запрос будет выдавать одну строку, если результат условия HAVING является true, и ноль строк в противном случае.

В настоящий момент вместе с HAVING нельзя задать указания FOR NO KEY UPDATE, *FOR UPDATE, FOR SHARE и FOR KEY SHARE.

Предложение WINDOW

Необязательное предложение WINDOW имеет общую форму

WINDOW имя_окна AS ( определение_окна ) [, ...]

где имя_окна — это имя, на которое можно ссылаться из предложения OVER или последующих определений окна, а определение_окна — это:

[ имя_существующего_окна ]
[ PARTITION BY выражение [, ...] ]
[ ORDER BY выражение [ ASC | DESC | USING оператор ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ предложение_рамки ]

Если имя_существующего_окна указано, оно должно ссылаться на предшествующую запись в списке WINDOW; новое окно копирует предложение разбиения из этой записи, а также предложение сортировки, если таковое имеется. В этом случае для нового окна нельзя указать свое собственное предложение PARTITION BY, а предложение ORDER BY можно определить, только если его не было у копируемого окна. Новое окно всегда использует свое собственное предложение рамки; в копируемом окне предложение рамки не должно быть указано.

Элементы списка PARTITION BY интерпретируется во многом так же, как элементы предложения GROUP BY, за исключением того, что они всегда являются простыми выражениями и никогда не являются именем или номером выходного столбца. Другое отличие заключается в том, что эти выражения могут содержать вызовы агрегатных функций, которые не разрешены в обычном предложении GROUP BY. Они разрешены здесь, потому что формирование окна происходит после группировки и агрегации.

Аналогично элементы списка ORDER BY интерпретируется во многом так же, как элементы предложения ORDER BY, за исключением того, что выражения всегда принимаются как простые выражения и никогда как имя или номер выходного столбца.

Необязательное предложение_рамки определяет рамку окна для оконных функций, которые зависят от рамки (не все они зависят). Рамка окна представляет собой набор связанных строк для каждой строки запроса (называемой текущей строкой ). предложение_рамки может быть:

{ RANGE | ROWS | GROUPS } начало_рамки [ исключение_рамки ]
{ RANGE | ROWS | GROUPS } BETWEEN начало_рамки AND конец_рамки [ исключение_рамки ]

где начало_рамки и конец_рамки может быть:

UNBOUNDED PRECEDING
смещение PRECEDING
CURRENT ROW
смещение FOLLOWING
UNBOUNDED FOLLOWING

и исключение_рамки может быть:

EXCLUDE CURRENT ROW
EXCLUDE GROUP
EXCLUDE TIES
EXCLUDE NO OTHERS

Если конец_рамки не указан, то по умолчанию подразумевается CURRENT ROW. Ограничения заключаются в том, что в качестве начала_рамки не может быть указано UNBOUNDED FOLLOWING, а конец_рамки не может быть указан как UNBOUNDED PRECEDING и не может появиться перед началом_рамки в приведенном выше списке параметров начала_рамки и конца_рамки — например, синтаксис RANGE BETWEEN CURRENT ROW AND смещение PRECEDING не допускается.

Рамка окна образуется по умолчанию предложением RANGE UNBOUNDED PRECEDING, которое равнозначно RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW; оно устанавливает рамку так, чтобы та включала все строки от начала раздела до последней строки, родственной текущей (строка, которая, согласно указанному для окна предложению ORDER BY, считается эквивалентной текущей; если предложение ORDER BY отсутствует, все строки являются родственными). В целом, UNBOUNDED PRECEDING означает, что рамка начинается с первой строки раздела, и подобным же образом UNBOUNDED FOLLOWING означает, что рамка заканчивается последней строкой раздела, независимо от того, какой установлен режим: RANGE, ROWS или GROUPS. В режиме ROWS указание CURRENT ROW означает, что рамка начинается или заканчивается текущей строкой; но в режиме RANGE или GROUPS это означает, что рамка начинается или заканчивается первой или последней строкой, родственной текущей, согласно порядку ORDER BY. Параметры смещение PRECEDING и смещение FOLLOWING различаются по значению в зависимости от режима рамки. В режиме ROWS смещение является целым числом, показывающим количество строк, на которое начало или конец рамки смещается от текущей строки (вверх или вниз соответственно). В режиме GROUPS смещение является целым числом, показывающим, количество групп родственных строк, на которое начало или конец рамки смещается от текущей строки (вверх или вниз соответственно), где группа родственных строк — это группа строк, эквивалентных в соответствии с предложением ORDER BY для данного окна. В режиме RANGE для указания смещения требуется, чтобы в определении окна присутствовал ровно один столбец ORDER BY. Тогда рамка содержит те строки, где значение упорядочивающего столбца не более чем на смещение меньше (для PRECEDING) или больше (для FOLLOWING) значения упорядочивающего столбца текущей строки. В этих случаях тип данных выражения смещение зависит от типа данных упорядочивающего столбца. Для числовых столбцов это обычно тот же числовой тип, а для столбцов с типом дата/время — тип interval. Во всех этих случаях значение смещения должно быть отличным от NULL и неотрицательным. Кроме того, несмотря на то, что смещение не обязательно должно быть простой константой, оно не может содержать переменные, агрегатные или оконные функции.

Параметр исключение_рамки позволяет исключать из рамки строки, окружающие текущую строку, даже если они должны быть включены в соответствии с параметрами начала и конца рамки. Предложение EXCLUDE CURRENT ROW исключает текущую строку из рамки. EXCLUDE GROUP исключает из рамки текущую строку и родственные ей согласно порядку сортировки. EXCLUDE TIES исключает из рамки только строки, родственные текущей строке, но не саму текущую строку. EXCLUDE NO OTHERS просто указывает явно поведение по умолчанию: не исключать ни текущую строку, ни родственные ей.

Будьте осторожны: в режиме ROWS могут выдаваться непредсказуемые результаты, если согласно порядку, указанному в ORDER BY, строки сортируются неоднозначно. Режимы RANGE и GROUPS предназначены для обеспечения того, чтобы строки, являющиеся родственными в порядке ORDER BY, обрабатывались одинаково: все строки данной родственной группы попадут в одну рамку или будут исключены из нее.

Назначение предложения WINDOW — управление поведением оконных функций, фигурирующих в параметрах списка SELECT или предложения ORDER BY запроса. Эти функции могут ссылаться на элементы WINDOW по имени в своих предложениях OVER. Однако элемент WINDOW не обязательно задействовать в запросе: если он не используется в запросе, то просто игнорируется. Можно использовать оконные функции вообще без предложения WINDOW, так как в вызове оконной функции можно указать ее определение окна непосредственно в предложении OVER. Тем не менее, предложение WINDOW позволяет сократить текст запроса, когда одно и то же определение окна требуется для более чем одной оконной функции.

В настоящий момент вместе с WINDOW нельзя задать указания FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE и FOR KEY SHARE.

Функции окон подробно описаны в разделах Руководство по оконным функциям, Вызовы оконных функций и Обработка оконных функций.

Список SELECT

Список SELECT (между ключевыми словами SELECT и FROM) задает выражения, которые формируют выходные строки команды SELECT. Выражения могут (и обычно это делают) ссылаться на столбцы, вычисленные в предложении FROM.

Так же, как и в таблице, каждый выходной столбец SELECT имеет имя. В простом SELECT это имя используется только для обозначения столбца для отображения, но когда SELECT является подзапросом большего запроса, имя рассматривается большим запросом как имя столбца виртуальной таблицы, созданной подзапросом. Чтобы указать имя, используемое для выходного столбца, нужно написать AS выходное_имя после выражения столбца. (Можно не указывать слово AS, но только если желаемое выходное имя не совпадает с ключевыми словами QHB (см. раздел Ключевые слова SQL). Для защиты от возможных будущих добавлений ключевых слов рекомендуется всегда либо писать AS, либо заключать имя в двойные кавычки.) Если не задать имя столбца, QHB выберет его автоматически. Если выражение столбца является простой ссылкой на столбец, то выбранное имя совпадет с именем этого столбца. В более сложных случаях может использоваться имя функции или типа, либо система может сгенерировать имя, например ?column?.

Имя выходного столбца может использоваться для обращения к его значению в предложениях ORDER BY и GROUP BY, но не в предложениях WHERE или HAVING; там следует написать вместо имени всё выражение.

Вместо выражения в выходной список можно записать * в качестве краткого обозначения всех столбцов выбранных строк. Кроме того, можно написать имя_таблицы.* как краткое обозначение для столбцов, получаемых из этой таблицы. В этих случаях невозможно указать новые имена с помощью AS; имена выходных столбцов будут совпадать с именами столбцов таблицы.

Согласно стандарту SQL, выражения в выходном списке должны быть вычислены перед применением DISTINCT, ORDER BY или LIMIT. Это безусловно необходимо при использовании DISTINCT, так как иначе неясно, какие значения должны выдаваться как уникальные. Однако во многих случаях удобнее, если выходные выражения вычисляются после ORDER BY и LIMIT; особенно если выходной список содержит какие-либо изменчивые или дорогостоящие функции. В этом случае порядок вычисления функций является более интуитивным, а для строк, которые не попадут в результат, не будут производиться вычисления. QHB будет фактически вычислять выходные выражения после сортировки и ограничения, если эти выражения не фигурируют в DISTINCT, ORDER BY или GROUP BY. (В качестве примера, в запросе SELECT f(x) FROM tab ORDER BY 1 функция f(x), несомненно, должна вычисляться перед сортировкой.) Выходные выражения, содержащие возвращающие множества функции, фактически вычисляются после сортировки и перед ограничением количества строк, так что LIMIT будет отбрасывать строки, выдаваемые функцией, возвращающей множество.

Предложение DISTINCT

Если указывается SELECT DISTINCT, то все повторяющиеся строки исключаются из результирующего набора (сохраняется одна строка из каждой группы дубликатов). SELECT ALL указывает на обратное: все строки сохраняются; это поведение по умолчанию.

SELECT DISTINCT ON ( выражение [, ...] ) сохраняет только первую строку каждого набора строк, в котором заданные выражения дают одинаковые значения. Выражения DISTINCT ON интерпретируются с использованием тех же правил, что и ORDER BY (см. выше). Обратите внимание, что «первая строка» каждого набора непредсказуема, если для обеспечения того, чтобы нужная строка появилась первой, не указано ORDER BY. Например:

SELECT DISTINCT ON (location) location, time, report
    FROM weather_reports
    ORDER BY location, time DESC;

извлекает самый последний отчет о погоде для каждого местоположения. Но если бы мы не воспользовались ORDER BY, чтобы принудительно установить нисходящий порядок значений времени для каждого местоположения, мы бы получили для каждого местоположения отчет от непредсказуемого времени.

Выражение (или выражения) DISTINCT ON должно соответствовать самому левому выражению в ORDER BY. Предложение ORDER BY обычно содержит дополнительное выражение (или выражения), определяющее желаемый приоритет строк внутри каждой группы DISTINCT ON.

В настоящий момент вместе с DISTINCT нельзя задать указания FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE и FOR KEY SHARE.

Предложение UNION

Предложение UNION имеет общую форму:

оператор_SELECT UNION [ ALL | DISTINCT ] оператор_SELECT

Где оператор_SELECT — это любой подзапрос SELECT без предложений ORDER BY, LIMIT, FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE или FOR KEY SHARE. (ORDER BY и LIMIT можно присоединить к вложенному выражению, если оно заключено в круглые скобки. Без скобок эти предложения будут восприняты для применения к результату UNION, а не к выражению в его правой части.)

Оператор UNION вычисляет объединение набора строк, возвращенных указанными запросами SELECT. Строка находится в объединении двух результирующих наборов, если появляется по крайней мере в одном из них. Два оператора SELECT, представляющие прямые операнды объекта UNION, должны выдавать одинаковое количество столбцов, а соответствующие столбцы должны иметь совместимые типы данных.

Результат UNION не будет содержать повторяющихся строк, если не указан параметр ALL. ALL предотвращает устранение дубликатов. (Следовательно, UNION ALL обычно значительно быстрее, чем UNION, поэтому по возможности следует использовать ALL.) Можно написать DISTINCT, чтобы явно указать, что повторяющиеся строки должны быть удалены (это поведение по умолчанию).

Несколько операторов UNION в одном запросе SELECT вычисляются слева направо, если круглыми скобками не определен иной порядок.

В настоящий момент ни для результата с UNION, ни для любого из подзапросов UNION невозможно задать указания FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE и FOR KEY SHARE.

Предложение INTERSECT

Предложение INTERSECT имеет общую форму:

оператор_SELECT INTERSECT [ ALL | DISTINCT ] оператор_SELECT

Где оператор_SELECT — это любой подзапрос SELECT без предложений ORDER BY, LIMIT, FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE или FOR KEY SHARE.

Оператор INTERSECT вычисляет пересечение набора строк, возвращаемых соответствующими запросами SELECT. Строка находится в пересечении двух результирующих наборов, если присутствует в них обоих.

Результат INTERSECT не будет содержать повторяющихся строк, если не указан параметр ALL. С ALL строка, повторяющаяся m раз в левой таблице и n раз в правой, будет выдана в результирующем наборе min(m,n) раз. Можно написать DISTINCT, чтобы явно указать, что повторяющиеся строки должны быть удалены (это поведение по умолчанию).

Несколько операторов INTERSECT в одном запросе SELECT вычисляются слева направо, если круглыми скобками не определен иной порядок. INTERSECT связывает свои подзапросы сильнее, чем UNION. Иными словами, A UNION B INTERSECT C будет восприниматься как A UNION (B INTERSECT C).

В настоящий момент ни для результата с INTERSECT, ни для любого из подзапросов INTERSECT нельзя задать указания FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE и FOR KEY SHARE.

Предложение EXCEPT

Предложение EXCEPT имеет общую форму:

оператор_SELECT EXCEPT [ ALL | DISTINCT ] оператор_SELECT

Где оператор_SELECT — это любой подзапрос SELECT без предложений ORDER BY, LIMIT, FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE или FOR KEY SHARE.

Оператор EXCEPT вычисляет набор строк, которые находятся в результате выполнения левого запроса SELECT, но не в результате выполнения правого.

Результат EXCEPT не будет содержать каких-либо повторяющихся строк, если не указан параметр ALL. С ALL строка, повторяющаяся m раз в левой таблице и n раз в правой, будет выдана в результирующем наборе max(m-n,0) раз. Можно написать DISTINCT, чтобы явно указать, что повторяющиеся строки должны быть удалены (это поведение по умолчанию).

Несколько операторов EXCEPT в одном запросе SELECT вычисляются слева направо, если круглыми скобками не определен иной порядок. EXCEPT связывает свои подзапросы так же сильно, как UNION.

В настоящий момент ни для результата с EXCEPT, ни для любого из подзапросов EXCEPT нельзя задать указания FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE и FOR KEY SHARE.

Предложение ORDER BY

Необязательное предложение ORDER BY имеет общую форму:

ORDER BY выражение [ ASC | DESC | USING оператор ] [ NULLS { FIRST | LAST } ] [, ...]

Предложение ORDER BY указывает, что результирующие строки запроса должны быть отсортированы в соответствии с указанным выражением (или выражениями). Если значения двух строк равны по самому левому выражению, то они сравниваются по следующему выражению и т. д. Если их значения равны по всем указанным выражениям, строки возвращаются в порядке, определяемом реализацией.

В качестве выражения может быть указано имя или порядковый номер выходного столбца (элемент списка SELECT) либо произвольное выражение, сформированное из значений входного столбца.

Порядковый номер означает последовательный номер (нумерация слева направо) позиции выходного столбца. Эта функция позволяет определить порядок сортировки по столбцу, который не имеет уникального имени. В этом нет абсолютной необходимости, потому что имя выходному столбцу всегда можно назначить с помощью предложения AS.

Также в предложении ORDER BY можно использовать произвольные выражения, в том числе столбцы, которые не отображаются в списке результатов SELECT. Таким образом, следующий запрос вполне корректен:

SELECT name FROM distributors ORDER BY code;

Однако здесь есть ограничение: если предложение ORDER BY применяется к результату предложений UNION, INTERSECT или EXCEPT, в нем можно указывать только имя или номер выходного столбца, но не выражение.

Если выражение ORDER BY — простое имя, которое соответствует имени как выходного, так и входного столбца,ORDER BY будет интерпретировать его как имя выходного столбца. Это противоположно выбору, который в такой же ситуации сделает GROUP BY. Данное несоответствие допущено для совместимости со стандартом SQL.

Дополнительно можно добавить ключевое слово ASC (по возрастанию) или DESC (по убыванию) после любого выражения в предложении ORDER BY. Если ничего не указано, по умолчанию предполагается ASC. Кроме того, можно указать имя специфического оператора сортировки в предложении USING. Оператор сортировки должен быть членом «меньше» или «больше» некоторого семейства операторов B-дерева. ASC обычно эквивалентно USING <, а DESC эквивалентно USING >. (Но создатель пользовательского типа данных может точно определить порядок сортировки по умолчанию, который может соответствовать операторам с другими именами.)

Если указывается NULLS LAST, значения NULL будут отсортированы после всех значений не NULL; если указывается NULLS FIRST, значения NULL будут отсортированы перед всеми значениями не NULL. Если ни то, ни другое не указано, то при явном или косвенном выборе порядка ASC по умолчанию подразумевается NULLS LAST, и наоборот, при явном или косвенном выборе порядка DESC по умолчанию подразумевается NULLS FIRST (следовательно, по умолчанию считается, что значения NULL больше значений не NULL). С предложением USING порядок NULL по умолчанию зависит от того, является ли заданный оператор оператором «меньше» или «больше».

Обратите внимание, что параметры сортировки применяются только к выражению, за которым они следуют: например, ORDER BY x, y DESC не то же самое, что ORDER BY x DESC, y DESC.

Данные символьных строк сортируются в соответствии с правилом сортировки, установленным для сортируемого столбца. При необходимости это поведение можно переопределить путем включения в выражение предложения COLLATE, например, так: ORDER BY mycolumn COLLATE "en_US". Дополнительную информацию см. в разделах Применение правил сортировки и Поддержка правил сортировки.

Предложение LIMIT

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

LIMIT { число | ALL }
OFFSET начало

Где число задает максимальное количество строк, которое должно быть выдано, в то время как начало указывает количество строк, которые необходимо пропустить, прежде чем начать выдавать строки. Если указаны оба значения, сначала строки пропускаются в количестве, заданном значением начало, а потом отсчитывается заданное значением число количество следующих строк, которые будут выданы.

Если в результате вычисления выражения число оказывается NULL, предложение обрабатывается как LIMIT ALL, то есть без ограничения числа строк. Если начало принимает значение NULL, оно обрабатывается так же, как OFFSET 0.

SQL:2008 представил другой синтаксис для получения того же результата, который также поддерживает QHB. Это:

OFFSET начало { ROW | ROWS }
FETCH { FIRST | NEXT } [ число ] { ROW | ROWS } ONLY

В этом синтаксисе стандарт требует, чтобы значение начало или число было буквальной константой, параметром или именем переменной; QHB разрешает использовать другие выражения, но они обычно должны быть заключены в круглые скобки, чтобы избежать неоднозначности. Если в предложении FETCH число опускается, то по умолчанию принимается значение 1. Слова ROW и ROWS, а также FIRST и NEXT являются незначащими и не влияют на поведение этих предложений. Согласно стандарту, предложение OFFSET должно предшествовать предложению FETCH, если присутствуют оба; но QHB менее строг и позволяет любой порядок.

При использовании LIMIT имеет смысл использовать и предложение ORDER BY, чтобы результирующие строки выдавались в определенном порядке. В противном случае вы получите непредсказуемое подмножество строк запроса — можно запросить строки с десятой по двадцатую, но в каком порядке? Порядок будет неизвестен, если не указать ORDER BY.

Планировщик запросов учитывает ограничение LIMIT при создании плана запроса, поэтому высока вероятность, что вы получите разные планы (и разный же порядок строк) в зависимости от того, какие значения указаны для LIMIT и OFFSET. Таким образом, различные значения LIMIT/OFFSET при выборе различных подмножеств результата запроса приведут к несогласованным результатам, если не установить предсказуемый порядок сортировки результатов с помощью ORDER BY. Это не ошибка, а неотъемлемое следствие того факта, что SQL не гарантирует вывод результатов запроса в каком-то конкретном порядке, если тот не задан предложением ORDER BY.

Возможно даже, что без предложения ORDER BY, принудительно выбирающего детерминированное подмножество, при повторном выполнении одного и того же запроса с LIMIT будут получены разные подмножества строк таблицы. Это опять же не ошибка; в подобном случае детерминированность результатов просто не гарантируется.

Предложение блокировки

Предложениями блокировки являются операторы FOR UPDATE, FOR NO KEY UPDATE, FOR SHARE и FOR KEY SHARE; они влияют на то, как SELECT блокирует строки по мере их получения из таблицы.

Блокирующее предложение имеет общую форму

FOR вариант_блокировки [ OF имя_таблицы [, ...] ] [ NOWAIT | SKIP LOCKED ]

где вариант_блокировки может быть одним из

UPDATE
NO KEY UPDATE
SHARE
KEY SHARE

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

Чтобы операция не ожидала фиксации других транзакций, используйте указание NOWAIT или SKIP LOCKED. С NOWAIT оператор сообщает об ошибке, а не ждет, если выбранную строку нельзя заблокировать немедленно. С указанием SKIP LOCKED все выбранные строки, которые нельзя немедленно заблокировать, пропускаются. Пропуск заблокированных строк формирует несогласованное представление данных, поэтому этот вариант не подходит для общего применения, но может быть использован для предотвращения конфликта блокировок при обращении множества потребителей к таблице типа очереди. Обратите внимание, что указания NOWAIT и SKIP LOCKED можно применить только к блокировке (или блокировкам) уровня строк — необходимая блокировка ROW SHARE уровня таблицы по-прежнему запрашивается обычным способом (см. главу Параллельный контроль). Если нужно запросить блокировку уровня таблицы без ожидания, можно использовать команду LOCK с указанием NOWAIT.

Если в предложении блокировки заданы определенные имена таблиц, то будут блокироваться только строки, поступающие из этих таблиц; любые другие таблицы, указанные в SELECT, будут прочитаны как обычно. Предложение блокировки без списка таблиц влияет на все таблицы, указанные в операторе. Если предложение блокировки применяется к представлению или подзапросу, оно влияет на все таблицы, используемые в представлении или подзапросе. Но эти предложения не применяются к запросам WITH, на которые ссылается основной запрос. Если вы хотите, чтобы блокировка строк происходила в пределах запроса WITH, укажите предложение блокировки непосредственно в этом запросе WITH.

Можно указать в запросе несколько предложений блокировки, если необходимо указать различное поведение блокировки для разных таблиц. При этом если одна и та же таблица упоминается (или неявно затрагивается) более чем в одном предложении блокировки, то блокировка устанавливается так, как если бы было указано только самое сильное из них. Аналогично таблица обрабатывается в режиме NOWAIT, если тот указан в любом из затрагивающих ее предложений. В противном случае таблица обрабатывается в режиме SKIP LOCKED, если тот указан в любом из затрагивающих ее предложений.

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

Когда предложение блокировки появляется на верхнем уровне запроса SELECT, блокируются именно те строки, которые возвращаются запросом; в случае запроса объединения блокируются те строки, которые участвуют в возвращаемых строках объединения. Кроме того, строки, удовлетворяющие условиям запроса на момент создания снимка запроса, будут заблокированы, хотя и не будут возвращены, если были обновлены с момента снимка и больше не удовлетворяют условиям запроса. Если используется LIMIT, блокировка остановится, как только будет возвращено достаточное количество строк, чтобы удовлетворить пределу (но обратите внимание, что строки, пропускаемые указанием OFFSET, будут блокироваться). Аналогично если предложение блокировки используется в запросе курсора, то блокируются только строки, фактически извлеченные или пройденные курсором.

Когда предложение блокировки появляется в подзапросе SELECT, блокируются те строки, которые будут возвращены внешним запросом от подзапроса. Этих строк может оказаться меньшее количество, чем предполагает анализ только подзапроса, поскольку условия из внешнего запроса могут способствовать оптимизации выполнения подзапроса. Например,

SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss WHERE col1 = 5;

будет блокировать только строки c col1 = 5, хотя в такой записи условие не относится к подзапросу.

В предыдущих версиях не удавалось сохранить блокировку, которая была обновлена более поздней точкой сохранения. Например, этому коду:

BEGIN;
SELECT * FROM mytable WHERE key = 1 FOR UPDATE;
SAVEPOINT s;
UPDATE mytable SET ... WHERE key = 1;
ROLLBACK TO s;

не удалось бы сохранить блокировку FOR UPDATE после ROLLBACK TO.

Внимание

Команда SELECT, запущенная с уровнем изоляции транзакций READ COMMITTED с использованием ORDER BY и предложением блокировки, возможно, будет возвращать строки не по порядку. Это связано с тем, что ORDER BY применяется в первую очередь. Команда сортирует результат, но затем может быть заблокирована при попытке получить блокировку одной или нескольких строк. Как только блокировка с SELECT будет снята, некоторые значения сортируемых столбцов могут быть изменены, что приведет к тому, что порядок может быть нарушен (хотя они были упорядочены с точки зрения исходных значений столбцов). При необходимости можно обойти эту проблему, поместив FOR UPDATE/SHARE в подзапрос, например:

SELECT * FROM (SELECT * FROM mytable FOR UPDATE) SS ORDER BY column1;

Обратите внимание, что этот запрос приведет к блокировке всех строк таблицы mytable, но если бы предложение FOR UPDATE было указано на верхнем уровне, были бы заблокированы только фактически возвращенные строки. Это может значительно влиять на производительность, особенно если ORDER BY совмещено с LIMIT или другими ограничениями. Таким образом, этот метод рекомендуется только в том случае, если ожидаются параллельные обновления сортируемых столбцов и требуется строго отсортированный результат.

На уровнях изоляции транзакций REPEATABLE READ и SERIALIZABLE это приведет к ошибке сериализации (с SQLSTATE '40001'), поэтому на этих уровнях изоляции нет никакой возможности получения строк не по порядку.

Команда TABLE

Команда

TABLE имя

равнозначна

SELECT * FROM имя

Команда TABLE может использоваться как команда верхнего уровня или как вариант более краткой записи внутри сложных запросов. С командой TABLE могут быть использованы только предложения WITH, UNION, INTERSECT, EXCEPT, ORDER BY, LIMIT, OFFSET, FETCH и предложения блокировки FOR; предложение WHERE и любые формы агрегирования использовать нельзя.

Примеры

Соединение таблицы films с таблицей distributors:

SELECT f.title, f.did, d.name, f.date_prod, f.kind
    FROM distributors d, films f
    WHERE f.did = d.did

       title       | did |     name     | date_prod  |   kind
-------------------+-----+--------------+------------+----------
 The Third Man     | 101 | British Lion | 1949-12-23 | Drama
 The African Queen | 101 | British Lion | 1951-08-11 | Romantic
 ...

Суммирование значений столбца len (продолжительность) для всех фильмов и группирование результатов по столбцу kind (тип фильма):

SELECT kind, sum(len) AS total FROM films GROUP BY kind;

   kind   | total
----------+-------
 Action   | 07:34
 Comedy   | 02:58
 Drama    | 14:28
 Musical  | 06:42
 Romantic | 04:38

Суммирование значений столбца len для всех фильмов, группирование результатов по столбцу kind и вывод только тех групп, общая продолжительность которых меньше 5 часов:

SELECT kind, sum(len) AS total
    FROM films
    GROUP BY kind
    HAVING sum(len) < interval '5 hours';

   kind   | total
----------+-------
 Comedy   | 02:58
 Romantic | 04:38

Следующие два примера демонстрируют равнозначные способы сортировки результатов по содержимому второго столбца (name):

SELECT * FROM distributors ORDER BY name;
SELECT * FROM distributors ORDER BY 2;

 did |       name
-----+------------------
 109 | 20th Century Fox
 110 | Bavaria Atelier
 101 | British Lion
 107 | Columbia
 102 | Jean Luc Godard
 113 | Luso films
 104 | Mosfilm
 103 | Paramount
 106 | Toho
 105 | United Artists
 111 | Walt Disney
 112 | Warner Bros.
 108 | Westward

Следующий пример показывает объединение таблиц distributors и actors, ограниченное именами, начинающимися с буквы W в каждой таблице. Интерес представляют только неповторяющиеся строки, поэтому ключевое слово ALL опущено.

distributors:               actors:
 did |     name              id |     name
-----+--------------        ----+----------------
 108 | Westward               1 | Woody Allen
 111 | Walt Disney            2 | Warren Beatty
 112 | Warner Bros.           3 | Walter Matthau
 ...                         ...

SELECT distributors.name
    FROM distributors
    WHERE distributors.name LIKE 'W%'
UNION
SELECT actors.name
    FROM actors
    WHERE actors.name LIKE 'W%';

      name
----------------
 Walt Disney
 Walter Matthau
 Warner Bros.
 Warren Beatty
 Westward
 Woody Allen

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

CREATE FUNCTION distributors(int) RETURNS SETOF distributors AS $$
    SELECT * FROM distributors WHERE did = $1;
$$ LANGUAGE SQL;

SELECT * FROM distributors(111);
 did |    name
-----+-------------
 111 | Walt Disney

CREATE FUNCTION distributors_2(int) RETURNS SETOF record AS $$
    SELECT * FROM distributors WHERE did = $1;
$$ LANGUAGE SQL;

SELECT * FROM distributors_2(111) AS (f1 int, f2 text);
 f1  |     f2
-----+-------------
 111 | Walt Disney

Пример функции с добавленным столбцом нумерации:

SELECT * FROM unnest(ARRAY['a','b','c','d','e','f']) WITH ORDINALITY;
 unnest | ordinality
--------+----------
 a      |        1
 b      |        2
 c      |        3
 d      |        4
 e      |        5
 f      |        6
(6 rows)

Этот пример показывает, как использовать простое предложение WITH:

WITH t AS (
    SELECT random() as x FROM generate_series(1, 3)
  )
SELECT * FROM t
UNION ALL
SELECT * FROM t

         x
--------------------
  0.534150459803641
  0.520092216785997
 0.0735620250925422
  0.534150459803641
  0.520092216785997
 0.0735620250925422

Обратите внимание, что запрос WITH выполняется всего один раз, поэтому мы получаем два одинаковых набора по три случайных значения.

В этом примере WITH RECURSIVE применяется для поиска всех подчиненных Мэри (непосредственных или косвенных) и вывода их уровня косвенности в таблице с информацией только о непосредственных подчиненных:

WITH RECURSIVE employee_recursive(distance, employee_name, manager_name) AS (
    SELECT 1, employee_name, manager_name
    FROM employee
    WHERE manager_name = 'Mary'
  UNION ALL
    SELECT er.distance + 1, e.employee_name, e.manager_name
    FROM employee_recursive er, employee e
    WHERE er.employee_name = e.manager_name
  )
SELECT distance, employee_name FROM employee_recursive;

Обратите внимание, что это типичная форма рекурсивных запросов: начальное условие, последующий UNION, а затем рекурсивная часть запроса. Убедитесь в том, что рекурсивная часть запроса в конце концов перестанет возвращать строки, иначе запрос окажется в бесконечном цикле. (Другие примеры см. в разделе Запросы WITH.)

В этом примере используется LATERAL для применения возвращающей множество функции get_product_names() для каждой строки таблицы manufacturers:

SELECT m.name AS mname, pname
FROM manufacturers m, LATERAL get_product_names(m.id) pname;

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

SELECT m.name AS mname, pname
FROM manufacturers m LEFT JOIN LATERAL get_product_names(m.id) pname ON true;

Совместимость

Разумеется, команда SELECT совместима со стандартом SQL. Но есть некоторые расширения и некоторые отсутствующие функции.

Необязательное предложение FROM

QHB позволяет опустить предложение FROM. Это упрощает вычисление результатов простых выражений:

SELECT 2+2;

 ?column?
----------
        4

Некоторые другие базы данных SQL могут сделать это только путем введения фиктивной таблицы с одной строкой, из которой нужно сделать SELECT.

Обратите внимание, что если предложение FROM не указано, запрос не может ссылаться ни на какие таблицы базы данных. Например, недопустим следующий запрос:

SELECT distributors.* WHERE distributors.name = 'Westward';

Пустые списки SELECT

Список выходных выражений после SELECT может быть пустым, создавая таблицу результатов без столбцов. Согласно стандарту SQL это недопустимый синтаксис. QHB допускает его, так как это согласуется с возможностью иметь таблицы без столбцов. Однако при использовании DISTINCT пустой список не допускается.

Необязательное ключевое слово AS

В стандарте SQL необязательное ключевое слово AS может быть опущено перед именем выходного столбца, когда новое имя столбца является допустимым именем столбца (то есть отличающимся от всех зарезервированных ключевых слов). QHB более строг: AS требуется, если новое имя столбца соответствует любому ключевому слову вообще, независимо от того, зарезервировано оно или нет. Рекомендуемая практика заключается в использовании AS или заключении имен выходных столбцов в кавычки, чтобы предотвратить любой возможный конфликт в будущем при добавлении ключевых слов.

В списке FROM как стандарт SQL, так и QHB позволяют пропускать AS перед псевдонимом, который является незарезервированным ключевым словом. Но это нецелесообразно для имен выходных столбцов из-за синтаксической неоднозначности.

ONLY и наследование

Стандарт SQL требует заключать имя таблицы в круглые скобки при написании ONLY, например SELECT * FROM ONLY (tab1), ONLY (tab2) WHERE .... QHB считает эти скобки необязательными. QHB допускает добавлять в конце *, чтобы явно указать что дочерние таблицы включаются в рассмотрение, в отличие от поведения с ONLY. Стандарт SQL этого не допускает.

(Эти соображения применяются ко всем командам SQL, поддерживающим параметр ONLY.)

Ограничения предложения TABLESAMPLE

Предложение TABLESAMPLE в настоящее время принимается только для обычных таблиц и материализованных представлений. Согласно стандарту SQL должна быть возможность применить его к любым элементам списка FROM.

Вызовы функций в предложении FROM

QHB позволяет записывать вызов функции непосредственно в качестве элемента списка FROM. В стандарте SQL было бы необходимо обернуть такой вызов функции в подзапрос SELECT; то есть запись FROM функция(...) псевдоним примерно равнозначна записи FROM LATERAL (SELECT функция(...)) псевдоним. Обратите внимание, что указание LATERAL считается неявным; это связано с тем, что стандарт требует поведения LATERAL для элемента UNNEST() в предложении FROM. QHB обрабатывает UNNEST() так же, как и другие функции, возвращающие множества.

Пространства имен в GROUP BY и ORDER BY

В стандарте SQL-92 предложение ORDER BY может ссылаться только на имена или номера выходных столбцов, в то время как предложение GROUP BY может ссылаться только на выражения, основанные на именах входных столбцов. QHB расширяет оба предложения, позволяя также применять другие варианты (но он использует интерпретацию стандарта, если есть неоднозначность). QHB также позволяет указывать произвольные выражения в обоих предложениях. Обратите внимание, что имена, фигурирующие в выражениях, всегда будут приниматься как имена входных столбцов, а не выходных.

В SQL:1999 и более поздних стандартах введено немного другое определение, которое не полностью совместимо с SQL-92. В большинстве случаев, однако, QHB будет интерпретировать выражение ORDER BY или GROUP BY так, как требует SQL:1999.

Функциональные зависимости

QHB распознает функциональную зависимость (позволяет пропускать столбцы из GROUP BY) только в том случае, если первичный ключ таблицы включен в список GROUP BY. Стандарт SQL задает дополнительные условия, которые должны быть учтены.

LIMIT и OFFSET

Предложения LIMIT и OFFSET являются специфичным синтаксисом QHB, также используемым в MySQL. Стандарт SQL:2008 для той же цели вводит предложения OFFSET ... FETCH {FIRST|NEXT} ..., рассмотренные ранее в подразделе Предложение LIMIT. Этот синтаксис также используется IBM DB2. (Приложения, написанные для Oracle, часто используют обходной путь, включая автоматически генерируемый столбец rownum, который отсутствует в QHB, чтобы реализовать эффект этих предложений.)

FOR NO KEY UPDATE, FOR UPDATE, FOR SHARE, FOR KEY SHARE

Хотя в стандарте SQL есть предложение блокировки FOR UPDATE, стандарт позволяет использовать его только в предложении DECLARE CURSOR. QHB же позволяет его использовать в любом запросе SELECT, а также подзапросах SELECT, но это является расширением. Варианты FOR NO KEY UPDATE, FOR SHARE и FOR KEY SHARE, а также NOWAIT и SKIP LOCKED не отображены в стандарте.

Изменение данных в WITH

QHB позволяет использовать INSERT, UPDATE и DELETE в в качестве запросов WITH. Этого нет в стандарте SQL.

Нестандартные предложения

DISTINCT ON ( ... ) является расширением стандарта SQL.

ROWS FROM( ... ) является расширением стандарта SQL.

Указания MATERIALIZED и NOT MATERIALIZED предложения WITH также относятся к расширениям стандарта SQL.

SET CONSTRAINTS

SET CONSTRAINTS — установить время проверки ограничений для текущей транзакции

Синтаксис

SET CONSTRAINTS { ALL | имя [, ...] } { DEFERRED | IMMEDIATE }

Описание

Команда SET CONSTRAINTS задает поведение при проверке ограничений в рамках текущей транзакции. Ограничения IMMEDIATE проверяются в конце каждого оператора. Ограничения DEFERRED откладываются до фиксации транзакции. Каждое из ограничений IMMEDIATE или DEFERRED задается независимо.

При создании ограничения задается одна из трех характеристик: DEFERRABLE INITIALLY DEFERRED, DEFERRABLE INITIALLY IMMEDIATE или NOT DEFERRABLE. Третий вариант всегда подразумевает IMMEDIATE и не зависит от команды SET CONSTRAINTS. Первые два вариант запускаются в каждой транзакции в указанном режиме, но их поведение может быть изменено в рамках транзакции командой SET CONSTRAINTS.

SET CONSTRAINTS со списком имен ограничений изменяет режим только этих ограничений (которые все должны быть откладываемыми). Каждое имя ограничения может быть дополнено схемой. Если имя схемы не указано, для поиска первого совпадающего имени используется текущий путь поиска схемы. SET CONSTRAINTS ALL изменяет режим всех откладываемых ограничений.

Когда SET CONSTRAINTS меняет режим ограничения с DEFERRED на IMMEDIATE, новый режим начинает действовать в обратную сторону: все изменения данных, которые были бы проверены в конце транзакции, вместо этого проверяются во время выполнения команды SET CONSTRAINTS. Если какое-либо такое ограничение нарушается, при выполнении SET CONSTRAINTS происходит ошибка (и не изменяется режим ограничения). Таким образом, SET CONSTRAINTS можно использовать для принудительной проверки ограничений в определенной точке транзакции.

В настоящее время такое поведение распространяется только на ограничения UNIQUE, PRIMARY KEY, REFERENCES (внешний ключ) и EXCLUDE. Ограничения NOT NULL и CHECK всегда проверяются немедленно, в момент, когда строка добавляется или изменяется (не в конце оператора). Ограничения уникальности и исключения, которые не были объявлены как EFERRABLE, также проверяются немедленно.

Запуск триггеров, объявленных как «триггеры ограничений», также контролируется этой командой — они срабатывают в то время, когда должна произойти проверка связанного ограничения.

Примечания

Поскольку QHB не требует, чтобы имена ограничений были уникальными в схеме (только в своей таблице), возможно существование нескольких соответствующих ограничений для указанного имени ограничения. В этом случае SET CONSTRAINTS будет действовать на все эти ограничения. Для имен без указания схемы действие команды будет распространяться только на ограничение, найденное в первой из схем в пути поиска, остальные схемы просматриваться не будут.

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

Совместимость

Эта команда соответствует поведению, определенному в стандарте SQL, за исключением того, что QHB не влияет на проверку ограничения NOT NULL и CHECK. Кроме того, QHB проверяет неоткладываемые ограничения уникальности сразу же, а не в конце оператора, как предполагает стандарт.

SET ROLE

SET ROLE — установить идентификатор текущего пользователя для активного сеанса

Синтаксис

SET [ SESSION | LOCAL ] ROLE имя_роли
SET [ SESSION | LOCAL ] ROLE NONE
RESET ROLE

Описание

Эта команда задает идентификатор текущего пользователя для активного сеанса SQL как имя_роли. Имя роли может быть записано как идентификатор или строковая константа. После SET ROLE проверка права доступа для команд SQL выполняется так, как если бы сеанс изначально был установлен с этим именем роли.

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

Модификаторы SESSION и LOCAL действуют так же, как и для обычной команды SET.

Формы NONE и RESET сбрасывают идентификатор текущего пользователя, в результате чего активным становится идентификатор пользователя активного сеанса. Эти формы может выполнить любой пользователь.

Примечания

С помощью этой команды можно как добавить права, так и ограничить их. Если роль пользователя сеанса имеет атрибут INHERIT, она автоматически получает права всех ролей, на которые может переключиться с помощью SET ROLE; в этом случае SET ROLE по сути удаляет все права, назначенные непосредственно пользователю сеанса и другим ролям, членом которых он является, оставляя только права, доступные для указанной роли. С другой стороны, если роль пользователя сеанса имеет атрибут NOINHERIT, то SET ROLE удаляет права, назначенные непосредственно пользователю сеанса, и вместо этого назначает права, доступные для указанной роли.

В частности, когда суперпользователь переключается через SET ROLE на роль, не являющуюся суперпользователем, он теряет свои права суперпользователя.

SET ROLE оказывает действие, сравнимое с SET SESSION AUTHORIZATION, но проверки прав при этом совсем другие. Кроме того, SET SESSION AUTHORIZATION определяет, какие роли допустимы для последующей SET ROLE, тогда как изменение ролей с помощью SET ROLE не изменяет набор ролей, допустимых для последующей команды SET ROLE.

SET ROLE не обрабатывает переменные сеанса, заданные в свойствах команды ALTER ROLE для данной роли; они устанавливаются только во время входа в систему.

Команду SET ROLE нельзя использовать в пределах функций с характеристикой SECURITY DEFINER.

Примеры

SELECT SESSION_USER, CURRENT_USER;

 session_user | current_user
--------------+--------------
 peter        | peter

SET ROLE 'paul';

SELECT SESSION_USER, CURRENT_USER;

 session_user | current_user
--------------+--------------
 peter        | paul

Совместимость

QHB допускает синтаксис идентификаторов (параметр «имя_роли»), в то время как стандарт SQL требует, чтобы имя роли было записано как строковая константа. Стандарт SQL не разрешает выполнять эту команду во время транзакции; в QHB такого ограничения нет, потому что для него нет никаких причин. Указания SESSION и LOCAL являются расширением QHB, так же, как и синтаксис RESET.

См. также

SET SESSION AUTHORIZATION

SET SESSION AUTHORIZATION

SET SESSION AUTHORIZATION — установить идентификатор пользователя сеанса и идентификатор текущего пользователя для активного сеанса

Синтаксис

SET [ SESSION | LOCAL ] SESSION AUTHORIZATION имя_пользователя
SET [ SESSION | LOCAL ] SESSION AUTHORIZATION DEFAULT
RESET SESSION AUTHORIZATION

Описание

Эта команда устанавливает идентификатор пользователя сеанса и идентификатор текущего пользователя активного сеанса SQL как имя_пользователя. Имя пользователя может быть записано как идентификатор или строковая константа. Используя эту команду, можно, например, временно переключиться на непривилегированного пользователя, а затем снова переключиться на суперпользователя.

Идентификатор пользователя сеанса изначально задается как имя пользователя (возможно, прошедшего проверку подлинности), введенное клиентом. Идентификатор текущего пользователя обычно равен идентификатору пользователя сеанса, но может временно измениться в рамках функций с характеристикой SECURITY DEFINER и других схожих механизмов; также его можно изменить с помощью SET ROLE. Идентификатор текущего пользователя принимается во внимание при проверке разрешений.

Идентификатор пользователя сеанса может быть изменен, только если исходный пользователь сеанса (аутентифицированный пользователь) имел право суперпользователя. В противном случае команда принимается, только если в ней указано имя аутентифицированного пользователя.

Указания SESSION и LOCAL действуют так же, как и для обычной команды SET.

Формы DEFAULT и RESET сбрасывают идентификаторы текущего пользователя и пользователя сеанса, так что текущим становится изначально аутентифицированный пользователь. Эти формы может выполнить любой пользователь.

Примечания

Команду SET SESSION AUTHORIZATION нельзя использовать в функциях с характеристикой SECURITY DEFINER.

Примеры

SELECT SESSION_USER, CURRENT_USER;

 session_user | current_user
--------------+--------------
 peter        | peter

SET SESSION AUTHORIZATION 'paul';

SELECT SESSION_USER, CURRENT_USER;

 session_user | current_user
--------------+--------------
 paul         | paul

Совместимость

Стандарт SQL позволяет вместо строковой константы имя_пользователя указывать некоторые другие выражения, но эти варианты не важны на практике. QHB допускает синтаксис идентификаторов (параметр «имя_пользователя»), а стандарт SQL — нет. SQL не разрешает выполнять эту команду во время транзакции; в QHB этого ограничения нет, потому что для него нет никаких причин. Указания SESSION и LOCAL являются расширением QHB, как и синтаксис RESET.

Права доступа, необходимые для выполнения этой команды, согласно стандарту, определяются реализацией.

См. также

SET ROLE

SET TRANSACTION

SET TRANSACTION — установить характеристики текущей транзакции

Синтаксис

SET TRANSACTION режим_транзакции [, ...]
SET TRANSACTION SNAPSHOT id_снимка
SET SESSION CHARACTERISTICS AS TRANSACTION режим_транзакции [, ...]

где режим_транзакции является одним из следующих:

    ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
    READ WRITE | READ ONLY
    [ NOT ] DEFERRABLE

Описание

Команда SET TRANSACTION задает характеристики текущей транзакции. Она не влияет на последующие транзакции. SET SESSION CHARACTERISTICS задает характеристики транзакции по умолчанию для последующих транзакций сеанса. Затем эти характеристики по умолчанию можно переопределить для отдельной транзакции командой SET TRANSACTION.

Доступные характеристики транзакции — это уровень изоляции транзакции, режим доступа к транзакции (чтение/запись или только чтение) и режим отложенного доступа. Кроме того, можно выбрать снимок, но только для текущей транзакции, а не для сеанса по умолчанию.

Уровень изоляции транзакции определяет, какие данные транзакция может видеть, когда параллельно выполняются другие транзакции:

READ COMMITTED

Оператор может видеть только строки, зафиксированные до начала выполнения транзакции. Это значение по умолчанию.

REPEATABLE READ

Все операторы текущей транзакции могут видеть только строки, зафиксированные до выполнения первого запроса или оператора изменения данных в этой транзакции.

SERIALIZABLE

Все операторы текущей транзакции могут видеть только строки, зафиксированные до выполнения первого запроса или оператора изменения данных в этой транзакции. Если наложение операций чтения и записи среди параллельных сериализуемых транзакций создаст ситуацию, невозможную при однопоточном (поочередном) их выполнении, произойдет откат одной из транзакций с ошибкой serialization_failure (сбой сериализации).

Стандарт SQL определяет один дополнительный уровень, READ UNCOMMITTED. В QHB READ UNCOMMITTED трактуется как READ COMMITTED.

Уровень изоляции транзакции не может быть изменен после выполнения первого запроса или оператора изменения данных (SELECT, INSERT, DELETE, UPDATE, FETCH или COPY) в текущей транзакции. Дополнительную информацию об изоляции транзакции см. в главе Управление параллельным доступом.

Режим доступа к транзакции определяет, является ли транзакция доступной для чтения/записи или только для чтения. Чтение/запись — это поведение по умолчанию. Когда транзакция доступна только для чтения, запрещены следующие команды SQL: INSERT, UPDATE, DELETE и COPY FROM (если таблица, в которую будут писать, не является временной таблицей), любые команды CREATE, ALTER и DROP, а также COMMENT, GRANT, REVOKE, TRUNCATE; EXPLAIN ANALYZE и EXECUTE запрещаются, если команда, которую они будут выполнять, находится в числе вышеперечисленных. Это высокоуровневое определение режима только для чтения, которое не препятствует записи на диск.

Свойство DEFERRABLE транзакции не имеет эффекта, если транзакция не находится в режиме SERIALIZABLE и READ ONLY. Если для транзакции выбраны все три этих свойства, она может быть заблокирована при первой попытке получить свой снимок данных, после чего сможет выполняться без обычных для режима SERIALIZABLE накладных расходов и без какого-либо риска пострадать или быть отмененной из-за сбоя сериализации. Этот режим хорошо подходит для длительных построений отчетов или выполнения резервных копий.

Команда SET TRANSACTION SNAPSHOT позволяет выполнить новую транзакцию с тем же снимком данных, что и существующая транзакция. Ранее существовавшая транзакция должна экспортировать этот снимок с помощью функции pg_export_snapshot (см. раздел Функции синхронизации снимков). Эта функция возвращает идентификатор снимка, который необходимо передать команде SET TRANSACTION SNAPSHOT, чтобы указать, какой снимок будет импортирован. Идентификатор должен быть записан в виде строковой константы, например ’000003A1-1’. SET TRANSACTION SNAPSHOT может выполняться только в начале транзакции, до выполнения первого запроса или оператора изменения данных (SELECT, INSERT, DELETE, UPDATE, FETCH или COPY) этой транзакции. Более того, в транзакции уже должен быть установлен уровень изоляции SERIALIZABLE или REPEATABLE READ (иначе снимок будет немедленно удален, так как на уровне READ COMMITTED создается новый снимок для каждой команды). Если импортирующая транзакция имеет уровне изоляции SERIALIZABLE, то транзакция, экспортировавшая снимок, тоже должна использовать этот уровень изоляции. Кроме того, сериализуемая транзакция в режиме чтение/запись не может импортировать снимок из транзакции в режиме «только чтение».

Примечания

При выполнении команды SET TRANSACTION без предварительного выполнения START TRANSACTION или BEGIN будет выдано предупреждение и больше ничего не произойдет.

Можно обойтись без SET TRANSACTION, указав вместо нее желаемый режим_транзакции в операторах BEGIN или START TRANSACTION. Но такой возможности не предусмотрено для SET TRANSACTION SNAPSHOT.

Режимы транзакций сеанса по умолчанию также можно задать в параметрах конфигурации default_transaction_isolation, default_transaction_read_only и default_transaction_deferrable. (На самом деле SET SESSION CHARACTERISTICS — это более многословная альтернатива для установки этих переменных с помощью команды SET.) Это означает, что значения по умолчанию могут быть установлены в файле конфигурации, через ALTER DATABASE и т.д. Дополнительную информацию см. в главе Конфигурация сервера.

Примеры

Чтобы начать новую транзакцию со снимком данных, который получила уже существующая транзакция, его нужно сначала экспортировать из первой транзакции. При этом будет получен идентификатор снимка, например:

BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT pg_export_snapshot();
 pg_export_snapshot
---------------------
 00000003-0000001B-1
(1 строка)

Затем этот идентификатор нужно передать команде SET TRANSACTION SNAPSHOT в начале новой транзакции:

BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET TRANSACTION SNAPSHOT '00000003-0000001B-1';

Совместимость

Эти команды определены в стандарте SQL, за исключением режима транзакции DEFERRABLE и формы SET TRANSACTION SNAPSHOT, которые являются расширениями QHB.

В стандарте уровнем изоляции транзакции по умолчанию является SERIALIZABLE. В QHB обычно используется значение по умолчанию READ COMMITTED, но, как упоминалось выше, его можно изменить.

В стандарте SQL есть еще одна характеристика транзакции, которую нельзя задать с помощью этих команд: размер диагностической области. Эта концепция специфична для встраиваемого SQL и поэтому не реализована в сервере QHB.

Стандарт SQL требует запятых между последовательными режимами_транзакции, но по историческим причинам QHB позволяет опустить запятые.

SET

SET — изменить параметр времени выполнения

Синтаксис

SET [ SESSION | LOCAL ] параметр_конфигурации { TO | = } { значение | 'значение' | DEFAULT }
SET [ SESSION | LOCAL ] TIME ZONE { часовой_пояс | LOCAL | DEFAULT }

Описание

Команда SET изменяет параметры конфигурации времени выполнения. С помощью SET можно динамически изменить многие из параметров времени выполнения, перечисленных в главе Конфигурация сервера. (Но некоторые из них требуют для изменения прав суперпользователя, а другие нельзя изменить после запуска сервера или сеанса.) Команда SET влияет только на значение, используемое в текущем сеансе.

Если команда SET (или эквивалентная ей SET SESSION) выполняется внутри транзакции, которая позже прерывается, при откате транзакции эффекты команды SET исчезают. Если же окружающая транзакция фиксируется, эффекты сохранятся до конца сеанса, если их не переопределит другая команда SET.

Эффекты от выполнения SET LOCAL сохраняются только до конца текущей транзакции, независимо от того, фиксируется она или нет. Особый случай представляет использование SET с последующей SET LOCAL в пределах одной транзакции: значение SET LOCAL будет сохраняться до конца транзакции, но после этого (если транзакция зафиксирована) восстановится значение, заданное командой SET.

Эффекты от выполнения SET или SET LOCAL также отменяются при откате к точке сохранения, которая установлена до выполнения команды.

Если SET LOCAL используется в функции, где параметр SET устанавливает значение той же переменной (см. CREATE FUNCTION), эффекты действия SET LOCAL при выходе из функции исчезают, то есть в любом случае восстанавливается значение, действующее при вызове функции. Это позволяет устанавливать значение SET LOCAL для динамических или неоднократных изменений параметра внутри функции, при этом благодаря SET имея возможность сохранить и восстановить значение, полученное извне. Однако обычная команда SET переопределяет любой параметр SET окружающей функции; ее эффект будет сохраняться, если не произойдет откат транзакции.

Параметры

SESSION

Указывает, что команда вступает в силу для текущего сеанса. (Это значение по умолчанию, если не указано SESSION или LOCAL.)

LOCAL

Указывает, что команда вступает в силу только для текущей транзакции. После выполнения COMMIT или ROLLBACK снова вступают в силу настройки уровня сеанса. При выполнении такой команды вне блока транзакций будет выдано предупреждение и больше ничего не произойдет.

параметр_конфигурации

Имя настраиваемого параметра времени выполнения. Доступные параметры описаны в главе Конфигурация сервера и ниже.

значение

Новое значение параметра. Значения можно указывать в виде строковых констант, идентификаторов, чисел или разделенных запятыми списков из них, в зависимости от конкретного параметра. Можно записать указание DEFAULT, чтобы сбросить параметр к значению по умолчанию (то есть к тому значению, которое он имел бы, если бы в текущем сеансе не выполнялись команды SET).

Помимо параметров конфигурации, описанных в главе Конфигурация сервера, есть несколько параметров, которые можно настроить только с помощью команды SET или которые имеют специальный синтаксис:

SCHEMA

SET SCHEMA 'значение' является альтернативным написанием команды SET search_path TO значение. С помощью этого синтаксиса можно указать только одну схему.

NAMES

SET NAMES значение является альтернативным написанием команды SET client_encoding TO значение.

SEED

Задает внутреннее начальное значение для генератора случайных чисел (функции random). Допустимые значения — это числа с плавающей запятой от -1 до 1, которые затем умножаются на 2³¹-1.

Начальное значение также может быть установлено путем вызова функции setseed:

SELECT setseed(значение);

TIME ZONE

SET TIME ZONE значение является альтернативным написанием команды SET timezone TO значение. Синтаксис SET TIME ZONE позволяет использовать специальный синтаксис для указания часового пояса. Вот примеры допустимых значений:

'PST8PDT'

Часовой пояс для города Беркли, штат Калифорния.

'Europe / Rome'

Часовой пояс для Италии.

-7

Часовой пояс, сдвинутый от UTC на 7 часов к западу (эквивалент PDT). Положительные значения означают сдвиг от UTC к востоку.

INTERVAL '-08:00' HOUR TO MINUTE

Часовой пояс, сдвинутый от UTC на 8 часов к западу (эквивалент PST).

LOCAL
DEFAULT

Устанавливает в качестве часового пояса местный часовой пояс (т. е. значение по умолчанию серверного параметра timezone).

Значения часового пояса, заданные в виде чисел или интервалов, преобразуются внутри в формат часового пояса POSIX. Например, после SET TIME ZONE -7 команда SHOW TIME ZONE покажет <-07>+07.

Дополнительную информацию о часовых поясах см. в разделе Часовые пояса.

Примечания

Изменить значение параметра можно также с помощью функции set_config; см. раздел Функции системного администрирования. Кроме того, системное представление pg_settings можно обновить командой UPDATE, что является эквивалентом SET.

Примеры

Установка пути поиска схем:

SET search_path TO my_schema, public;

Установка традиционного стиля даты POSTGRES с форматом ввода «день, месяц, год»:

SET datestyle TO postgres, dmy;

Установка часового пояса для Беркли, штат Калифорния:

SET TIME ZONE 'PST8PDT';

Установка часового пояса Италии:

SET TIME ZONE 'Europe/Rome';

Совместимость

КомандаSET TIME ZONE расширяет синтаксис, определенный в стандарте SQL. Стандарт допускает только числовые смещения часовых поясов, в то время как QHB допускает для последних более гибкие спецификации. Все остальные функции SET являются расширениями QHB.

См. также

RESET, SHOW

SHOW

SHOW — показать значение параметра времени выполнения

Синтаксис

SHOW имя
SHOW ALL

Описание

Команда SHOW отображает текущую настройку параметров времени выполнения. Эти переменные можно установить с помощью команды SET, путем редактирования конфигурационного файла qhb.conf, через переменную окружения PGOPTIONS (при использовании libpq или приложений на основе libpq) или через параметры командной строки при запуске сервера QHB. Дополнительную информацию см. в главе Конфигурация сервера.

Параметры

имя

Имя параметра времени выполнения. Доступные параметры описаны в главе Конфигурация сервера и в справочном разделе по команде SET. Кроме того, есть несколько параметров, которые могут быть показаны, но не установлены:

SERVER_VERSION

Показывает номер версии сервера.

SERVER_ENCODING

Показывает кодировку набора символов на стороне сервера. В настоящее время этот параметр может быть показан, но не задан, поскольку кодировка определяется во время создания базы данных.

LC_COLLATE

Показывает параметр локали базы данных для правил сортировки (упорядочение текста). В настоящее время этот параметр можно отобразить, но не задать, так как он определяется во время создания базы данных.

LC_CTYPE

Показывает параметр локали базы данных для классификации символов. В настоящее время этот параметр можно отобразить, но не задать, так как он определяется во время создания базы данных.

IS_SUPERUSER

Возвращает true, если текущая роль имеет права суперпользователя.

ALL

Показать значения всех параметров конфигурации с описаниями.

Примечания

Ту же информацию выдает функция current_setting; см. раздел Функции системного администрирования. Также эту информацию содержит системное представление pg_settings.

Примеры

Просмотр текущего значения параметра DateStyle:

SHOW DateStyle;
 DateStyle
-----------
 ISO, MDY
(1 row)

Просмотр текущего значения параметра geqo:

SHOW geqo;
 geqo
------
 on
(1 row)

Просмотр всех параметров:

SHOW ALL;
            name         | setting |                description
-------------------------+---------+-------------------------------------------------
 allow_system_table_mods | off     | Allows modifications of the structure of ...
    .
    .
    .
 xmloption               | content | Sets whether XML data in implicit parsing ...
 zero_damaged_pages      | off     | Continues processing past damaged page headers.
(196 rows)

Совместимость

Команда SHOW является расширением QHB.

См. также

SET, RESET

START TRANSACTION

START TRANSACTION — начать блок транзакции

Синтаксис

START TRANSACTION [ режим_транзакции [, ...] ]

где режим_транзакции может быть одним из следующих:

ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
    READ WRITE | READ ONLY
    [ NOT ] DEFERRABLE

Описание

Эта команда начинает новый блок транзакции. Если указан уровень изоляции, режим чтения/записи или отложенный режим, то новая транзакция имеет те же характеристики, как если бы была выполнена команда SET TRANSACTION. Данная команда равнозначна команде BEGIN.

Параметры

Информацию о значении параметров для данной команды см. в описании SET TRANSACTION.

Совместимость

Согласно стандарту, необязательно выполнять команду START TRANSACTION, чтобы начать блок транзакции: любая команда SQL неявно начинает блок. Поведение QHB можно рассматривать как неявное выполнение COMMIT после каждой команды, которая не следует за START TRANSACTION (или BEGIN), и поэтому его часто называют «автоматической фиксацией». Другие системы реляционных баз данных могут предлагать функцию автоматической фиксации в качестве удобной возможности.

Значение DEFERRABLE параметра режим_транзакции является расширением языка QHB.

Стандарт SQL требует разделения последовательных режимов_транзакции запятыми, но по историческим причинам QHB позволяет их опустить.

Также сведения о совместимости см. в описании SET TRANSACTION.

См. также

BEGIN, COMMIT, ROLLBACK, SAVEPOINT, SET TRANSACTION

TRUNCATE

TRUNCATE — опустошить таблицу или набор таблиц

Синтаксис

TRUNCATE [ TABLE ] [ ONLY ] имя [ * ] [, ... ]
    [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]

Описание

Команда TRUNCATE быстро удаляет все строки из набора таблиц. Она действует так же, как и выполненная без условий команда DELETE для каждой заданной таблицы, но поскольку команда TRUNCATE фактически не сканирует таблицы, она быстрее. Более того, она сразу же освобождает место на диске и не требует последующей операции VACUUM. Это наиболее удобно на больших таблицах.

Параметры

имя

Имя таблицы для опустошения (может быть дополнено схемой). Если перед именем таблицы указывается ONLY, то опустошается только эта таблица. Если ONLY не указано, опустошаются таблица и все ее потомки (если таковые имеются). После имени таблицы можно добавить необязательное указание *, чтобы явно обозначить, что включены дочерние таблицы.

RESTART IDENTITY

Автоматически перезапускать последовательности, принадлежащие столбцам опустошаемой таблицы (или таблиц).

CONTINUE IDENTITY

Не изменять значения последовательностей. Это поведение по умолчанию.

CASCADE

Автоматически выполнять TRUNCATE для всех таблиц, имеющих ссылки по внешнему ключу на любую из заданных таблиц или на любые таблицы, затронутые в результате действия CASCADE.

RESTRICT

Запретить опустошение, если какая-либо таблица имеет ссылки по внешнему ключу из таблиц, которые не перечислены в команде. Это поведение по умолчанию.

Примечания

Чтобы опустошить таблицу, необходимо иметь право TRUNCATE для этой таблицы.

Команда TRUNCATE запрашивает блокировку ACCESS EXCLUSIVE для каждой таблицы, которую обрабатывает, тем самым блокируя все другие параллельные операции на таблице. Когда указано RESTART IDENTITY, все последовательности, которые должны быть перезапущены, также блокируются. Если требуется параллельный доступ к таблице, следует использовать команду DELETE.

TRUNCATE нельзя использовать для таблицы, имеющей ссылки по внешним ключам из других таблиц, если только все эти таблицы не опустошаются в той же команде. В таких случаях проверка допустимости операции потребует сканирования таблиц, а весь смысл команды заключается в том, чтобы этого не делать. Можно использовать CASCADE для автоматической обработки всех зависимых таблиц — но будьте очень осторожны при использовании этого указания, иначе можете потерять данные, которые не собирались удалять! В частности, следует отметить, что когда таблица, предназначенная к опустошению, является партицией, родственные партиции остаются нетронутыми, но все без исключения связанные таблицы и их партиции подвергаются каскадному опустошению.

При выполнении TRUNCATE не будут срабатывать триггеры ON DELETE, которые могут существовать для таблиц. Но будут срабатывать триггеры ON TRUNCATE. Если триггеры ON TRUNCATE определены для любой из таблиц, то все триггеры BEFORE TRUNCATE срабатывают до того, как произойдет опустошение, а все триггеры AFTER TRUNCATE срабатывают после опустошения последней таблицы из списка и сброса всех последовательностей. Триггеры будут срабатывать в том же порядке, в каком таблицы должны быть обработаны (сначала те, что перечислены в команде, а затем те, что затрагиваются каскадно).

Команда TRUNCATE не является безопасной с точки зрения MVCC. После опустошения таблица будет выглядеть пустой для параллельных транзакций, если они используют снимок, сделанный до того, как произошло опустошение. Более подробную информацию см. в разделе Ограничения.

TRUNCATE является безопасной транзакционной операцией по отношению к данным в таблицах: если окружающая транзакция не будет зафиксирована, опустошение будет безопасно отменено.

Когда указывается RESTART IDENTITY, подразумеваемые команды ALTER SEQUENCE RESTART также выполняются транзакционно, то есть они будут отменены, если окружающая транзакция не будет зафиксирована. Обратите внимание, что если перед откатом транзакции на повторно запущенных последовательностях выполняются какие-либо дополнительные операции, эффект этих операций также будет отменен, но не их влияние на currval(); то есть после завершения транзакции currval() будет продолжать возвращать последнее значение последовательности, полученное внутри неудачной транзакции, даже если сама последовательность уже может быть не согласованной с ним. Обычно подобным образом currval() ведет себя и после сбоя транзакции.

В настоящее время TRUNCATE не поддерживается для сторонних таблиц. Это означает, что если у указанной таблицы есть какие-либо дочерние таблицы, которые являются сторонними, команда завершится ошибкой.

Примеры

Опустошение таблиц bigtable и fattable:

TRUNCATE bigtable, fattable;

Та же операция и сброс любых связанных генераторов последовательностей:

TRUNCATE bigtable, fattable RESTART IDENTITY;

Опустошение таблицы othertable и каскадная обработка таблиц, которые ссылаются на othertable через ограничения внешнего ключа:

TRUNCATE othertable CASCADE;

Совместимость

Стандарт SQL:2008 включает команду TRUNCATE с синтаксисом TRUNCATE TABLE имя_таблицы. Предложения CONTINUE IDENTITY/RESTART IDENTITY также включены в стандарт, но имеют несколько другое, хотя и похожее назначение. Согласно стандарту, поведение этой команды при параллельных операциях определяется реализацией, поэтому при необходимости вышеуказанные примечания следует рассматривать и сопоставлять с другими реализациями.

См. также

DELETE

UNLISTEN

UNLISTEN — остановить прослушивание уведомления

Синтаксис

UNLISTEN { канал | * }

Описание

Команда UNLISTEN используется для удаления существующей подписки на события NOTIFY. UNLISTEN отменяет любую существующую подписку текущего сеанса QHB на канал уведомлений с именем канал. Специальный знак * отменяет все подписки текущего сеанса.

Страница NOTIFY содержит более подробное описание команд LISTEN и NOTIFY.

Параметры

канал

Имя канала уведомления (любой идентификатор).

*

Все текущие подписки на уведомления для этого сеанса отменяются.

Примечания

Вы можете отменить подписку на канал, на который не подписаны; при этом не появится никакого предупреждения или ошибки.

В конце каждого сеанса UNLISTEN * выполняется автоматически.

Транзакцию, которая выполняла UNLISTEN, нельзя подготовить к двухфазной фиксации.

Примеры

Подписка на получение события:

LISTEN virtual;
NOTIFY virtual;
Asynchronous notification "virtual" received from server process with PID 8448.

После того, как UNLISTEN была выполнена, дальнейшие сообщения NOTIFY будут игнорироваться:

UNLISTEN virtual;
NOTIFY virtual;
-- событие NOTIFY не поступает

Совместимость

В стандарте SQL нет команды UNLISTEN.

См. также

LISTEN, NOTIFY

UPDATE

UPDATE — изменить строки таблицы

Синтаксис

[ WITH [ RECURSIVE ] запрос_WITH [, ...] ]
UPDATE [ ONLY ] имя_таблицы [ * ] [ [ AS ] псевдоним ]
    SET { имя_столбца = { выражение | DEFAULT } |
          ( имя_столбца [, ...] ) = [ ROW ] ( { выражение | DEFAULT } [, ...] ) |
          ( имя_столбца [, ...] ) = ( вложенный_SELECT )
        } [, ...]
    [ FROM элемент_FROM [, ...] ]
    [ WHERE условие | WHERE CURRENT OF имя_курсора ]
    [ RETURNING * | выражение_результата [ [ AS ] имя_результата ] [, ...] ]

Описание

Команда UPDATE изменяет значения указанных столбцов во всех строках, удовлетворяющих условию. В предложении SET следует указывать только те столбцы, которые необходимо изменить; столбцы, явно не измененные, сохраняют свои предыдущие значения.

Существует два способа изменить строки в таблице, используя информацию, содержащуюся в других таблицах базы данных: с помощью подзапросов или указав дополнительные таблицы в предложении FROM. Какой метод является более подходящим, зависит от конкретных обстоятельств.

Необязательное предложение RETURNING указывает, что UPDATE должна вычислить и возвратить значение (или значения) каждой фактически обновленной строки. Можно вычислить любое выражение, использующее столбцы данной таблицы и/или столбцы других таблиц, упомянутых в списке FROM. При этом при вычислении используются новые (после обновления) значения столбцов таблицы. Список RETURNING имеет тот же синтаксис, что и список результатов SELECT.

Нужно иметь право UPDATE для таблицы или, по крайней мере, для столбцов, которые перечислены для обновления. Также необходимо иметь право SELECT для всех столбцов, значения которых считываются в выражениях или условии.

Параметры

запрос_WITH

Предложение WITH позволяет указать один или несколько вложенных запросов, на которые можно ссылаться по имени в запросе UPDATE. Дополнительную информацию см. в разделах Запросы WITH и SELECT.

имя_таблицы

Имя таблицы, подлежащей обновлению (может быть дополнено схемой). Если перед именем таблицы добавлено ONLY, соответствующие строки обновятся только в указанной таблице. Если ONLY не указано, строки также обновятся в любых таблицах, наследуемых от указанной. После имени таблицы можно добавить необязательное указание *, чтобы явно обозначить, что в операцию включены и дочерние таблицы.

псевдоним

Альтернативное имя целевой таблицы. Когда указывается псевдоним, он полностью скрывает фактическое имя таблицы. Например, в запросе UPDATE foo AS f остальная часть инструкции UPDATE должна ссылаться на таблицу по имени f, а не foo.

имя_столбца

Имя столбца в таблице с именем имя_таблицы. При необходимости имя столбца можно определить с помощью имени вложенного поля или индекса массива. Имя таблицы добавлять к имени целевого столбца не нужно — например, UPDATE table_name SET table_name.col = 1 является недопустимой командой.

выражение

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

DEFAULT

Установить для столбца значение по умолчанию (которое будет равно NULL, если ему не было присвоено никакого конкретного выражения по умолчанию).

вложенный_SELECT

Подзапрос SELECT, который создает столько выходных столбцов, сколько перечислено в предшествующем ему списке столбцов, заключенном в скобки. Подзапрос должен выдавать не более одной строки. Если он выдает одну строку, значения его столбцов назначаются целевым столбцам; если он не возвращает строк, целевым столбцам назначаются значения NULL. Подзапрос может ссылаться на предыдущие значения текущей обновляемой строки таблицы.

элемент_FROM

Табличное выражение, позволяющее столбцам из других таблиц появляться в условии WHERE и в обновленных выражениях. В нем используется тот же синтаксис, что и в предложении FROM команды SELECT. Не повторяйте имя целевой таблицы в предложении FROM, если не собираетесь определить замкнутое соединение (в этом случае для данного имени в элемент_FROM должен появиться псевдоним).

условие

Выражение, которое возвращает значение типа boolean. Будут обновлены только те строки, для которых это выражение возвращает true.

имя_курсора

Имя курсора, который будет использован в условии WHERE CURRENT OF. С таким условием будет изменена строка, выбранная из этого курсора последней. Курсор должен образовываться негруппирующим запросом к целевой таблице команды UPDATE. Обратите внимание, что WHERE CURRENT OF нельзя указывать вместе с логическим условием. Дополнительную информацию об использовании курсоров с помощью WHERE CURRENT OF см. в разделе DECLARE.

выражение_результата

Выражение, которое вычисляется и возвращается командой UPDATE после обновления каждой строки. Выражение может использовать любые имена столбцов таблицы имя_таблицы или таблиц, перечисленных в списке FROM. Чтобы вернуть все столбцы, напишите *.

имя_результата

Имя, используемое для возвращаемого столбца.

Выводимая информация

После успешного завершения команда UPDATE возвращает метку команды в форме

UPDATE число

Где число — это число обновленных строк, включая соответствующие строки, значения которых не изменились. Обратите внимание, что это число может быть меньше, чем число строк, которые соответствуют условию, когда обновления были подавлены триггером BEFORE UPDATE. Если число равно 0, значит, ни одна строка не была обновлена запросом (это не считается ошибкой).

Если команда UPDATE содержит предложение RETURNING, результат будет похож на тот, что возвращает команда SELECT (содержащий столбцы и значения, определенные в списке RETURNING), полученный для измененных этой командой строк.

Примечания

Когда присутствует предложение FROM, целевая таблица, по сути, соединяется с таблицами из списка элемент_FROM, и каждая выходная строка соединения представляет собой операцию обновления для целевой таблицы. При использовании FROM необходимо убедиться, что соединение создает не более одной выходной строки для каждой строки, подлежащей изменению. Другими словами, целевая строка должна соединяться максимум с одной строкой из других таблиц. При нарушении данного условия для обновления целевой строки будет использоваться только одна из присоединенных строк, но какая именно, предсказать трудно.

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

Применительно к партиционированной таблице обновление строки может привести к тому, что она перестанет удовлетворять ограничению содержащей ее партиции. В таком случае если в дереве партиционирования есть какая-либо другая партиция, ограничению которой удовлетворяет эта строка, последняя перемещается в эту партицию. Если такая партиция отсутствует, произойдет ошибка. На самом деле движение строки фактически является операцией DELETE и INSERT.

Существует вероятность того, что одновременное выполнение команд UPDATE или DELETE перемещаемой строки приведет к ошибке сериализации. Предположим, что сеанс 1 выполняет UPDATE ключа разбиения, и в это же время параллельный сеанс 2, для которого эта строка видна, выполняет операцию UPDATE или DELETE этой строки. В этом случае UPDATE или DELETE сеанса 2 обнаружит перемещение строки и вызовет ошибку сбоя сериализации (которая всегда возвращается с кодом SQLSTATE ’40001’). Если это произойдет, приложения могут попытаться повторить транзакцию. В обычных условиях, когда таблица не партиционирована или нет никакого перемещения строки, сеанс 2 определил бы недавно обновленную строку и выполнил бы UPDATE/DELETE на этой новой версии строки.

Обратите внимание, что хотя строки могут быть перемещены из локальных партиций в партицию сторонней таблицы (при условии, что обертка сторонних данных поддерживает маршрутизацию кортежей), их нельзя переместить из партиции сторонней таблицы в другую партицию.

Примеры

Изменение слова Drama на Dramatic в столбце kind таблицы films:

UPDATE films SET kind = 'Dramatic' WHERE kind = 'Drama';

Изменение значений температуры и сброс уровня осадков к значению по умолчанию в одной строке таблицы weather:

UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT
  WHERE city = 'San Francisco' AND date = '2003-07-03';

Выполнение той же операции с получением измененных записей:

UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT
  WHERE city = 'San Francisco' AND date = '2003-07-03'
  RETURNING temp_lo, temp_hi, prcp;

Такое же изменение с применением альтернативного синтаксиса со списком столбцов:

UPDATE weather SET (temp_lo, temp_hi, prcp) = (temp_lo+1, temp_lo+15, DEFAULT)
  WHERE city = 'San Francisco' AND date = '2003-07-03';

Увеличение счетчика продаж для менеджера, занимающегося компанией Acme Corporation, с применением предложения FROM:

UPDATE employees SET sales_count = sales_count + 1 FROM accounts
  WHERE accounts.name = 'Acme Corporation'
  AND employees.id = accounts.sales_person;

Выполнение той же операции с подзапросом в предложении WHERE:

UPDATE employees SET sales_count = sales_count + 1 WHERE id =
  (SELECT sales_person FROM accounts WHERE name = 'Acme Corporation');

Изменение имени контакта в таблице счетов (это должно быть имя назначенного менеджера по продажам):

UPDATE accounts SET (contact_first_name, contact_last_name) =
    (SELECT first_name, last_name FROM salesmen
     WHERE salesmen.id = accounts.sales_id);

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

UPDATE accounts SET contact_first_name = first_name,
                    contact_last_name = last_name
  FROM salesmen WHERE salesmen.id = accounts.sales_id;

Однако если salesmen.id — не уникальный ключ, второй запрос может давать непредсказуемые результаты, тогда как первый запрос гарантированно выдаст ошибку, если найдется несколько записей с одним id. Кроме того, если не найдется запись, соответствующая запросу accounts.sales_id, первый запрос запишет в поля имени NULL, а второй вовсе не изменит строку.

Обновление статистики в сводной таблице в соответствии с текущими данными:

UPDATE summary s SET (sum_x, sum_y, avg_x, avg_y) =
    (SELECT sum(x), sum(y), avg(x), avg(y) FROM data d
     WHERE d.group_id = s.group_id);

Попытка добавить новый продукт вместе с количеством. Если такая запись уже существует, то вместо этого увеличить количество данного продукта в существующей записи. Чтобы реализовать этот подход, не откатывая всю транзакцию, можно использовать точки сохранения:

BEGIN;
-- другие операции
SAVEPOINT sp1;
INSERT INTO wines VALUES('Chateau Lafite 2003', '24');
-- Предполагая, что здесь возникает ошибка из-за нарушения уникальности ключа,
-- мы выполняем следующие команды:
ROLLBACK TO sp1;
UPDATE wines SET stock = stock + 24 WHERE winename = 'Chateau Lafite 2003';
-- Продолжение других операций и в завершение...
COMMIT;

Изменение значения столбца kind таблицы films в строке, на которой в данный момент находится курсор c_films:

UPDATE films SET kind = 'Dramatic' WHERE CURRENT OF c_films;

Совместимость

Команда UPDATE соответствует стандарту SQL, за исключением предложений FROM и RETURNING, которые являются расширениями QHB, как и возможность применять WITH с UPDATE.

В некоторых других СУБД также поддерживается дополнительное предложение FROM, но предполагается, что целевая таблица должна еще раз упоминаться в этом предложении. QHB интерпретирует FROM не так. Будьте осторожны при портировании приложений, которые используют это расширение.

Согласно стандарту, исходным значением для вложенного списка имен целевых столбцов, данного в скобках, может быть любое строковое выражение, возвращающее строку с правильным числом столбцов. QHB принимает в качестве этого значения только конструктор строк или подзапрос SELECT. Изменяемое значение отдельного столбца можно указать как DEFAULT в конструкторе строки, но не внутри подзапроса SELECT.

VACUUM

VACUUM — почистить память и при необходимости проанализировать базу данных

Синтаксис

VACUUM [ ( параметр [, ...] ) ] [ таблица_и_столбцы [, ...] ]
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ таблица_и_столбцы [, ...] ]

Где параметр может быть следующим:

    FULL [ логическое_значение ]
    FREEZE [ логическое_значение ]
    VERBOSE [ логическое_значение ]
    ANALYZE [ логическое_значение ]
    DISABLE_PAGE_SKIPPING [ логическое_значение ]
    SKIP_LOCKED [ логическое_значение ]
    INDEX_CLEANUP [ логическое_значение ]
    TRUNCATE [ логическое_значение ]
    PARALLEL целое_число

Где таблица_и_столбцы это:

    имя_таблицы [ ( имя_столбца [, ...] ) ]

Описание

Команда VACUUM освобождает пространство, занятое «мертвыми» кортежами. В обычной работе QHB удаленные или устаревшие из-за обновления кортежи физически не удаляются из своей таблицы, а остаются в ней до тех пор, пока не будет вызвана команда VACUUM. Поэтому VACUUM нужно периодически запускать, особенно на часто обновляемых таблицах.

Без списка таблица_и_столбцы команда VACUUM обрабатывает каждую таблицу и материализованное представление в текущей базе данных, на очистку которых текущий пользователь имеет разрешение. При наличии списка VACUUM обрабатывает только заданную таблицу(ы).

VACUUM ANALYZE выполняет VACUUM (очистку), а затем ANALYZE (анализ) для каждой выбранной таблицы. Это удобная комбинированная форма для сценариев регламентного обслуживания. Дополнительную информацию о его обработке см. в разделе ANALYZE.

Обычный VACUUM (без FULL) просто восстанавливает пространство и делает его доступным для повторного использования. Эта форма команды может работать параллельно с обычным чтением и записью таблицы, так как исключительная блокировка не возникает. Однако дополнительное пространство не возвращается в операционную систему (в большинстве случаев); оно просто остается доступным для повторного использования в той же таблице. VACUUM FULL перезаписывает всё содержимое таблицы в новый дисковый файл без дополнительного места, позволяя возвращать неиспользуемое пространство в операционную систему. Эта форма работает намного медленнее и при обработке требует исключительной блокировки для каждой таблицы.

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

Параметры

FULL

Выбирает «полную» очистку, которая может освободить больше места, но занимает гораздо больше времени и исключительно блокирует таблицы. Этот метод также требует дополнительного дискового пространства, так как он записывает новую копию таблицы и не освобождает старую копию до завершения операции. Обычно это следует использовать только тогда, когда требуется освободить значительное пространство внутри таблицы.

FREEZE

Выбирает агрессивное «замораживание» кортежей. Указание FREEZE эквивалентно выполнению VACUUM с параметрами vacuum_freeze_min_age и vacuum_freeze_table_age, установленными в ноль. Агрессивное замораживание всегда выполняется при перезаписи таблицы, поэтому при указании FULL этот параметр избыточен.

VERBOSE

Печатает подробный отчет о процедуре очистки для каждой таблицы.

ANALYZE

Обновляет статистику, используемую планировщиком для определения наиболее эффективного способа выполнения запроса.

DISABLE_PAGE_SKIPPING

Обычно VACUUM пропускает страницы, основанные на карте видимости. Страницы, где все кортежи значатся замороженными, всегда можно пропустить, а те, где все кортежи будут видны для всех транзакций, можно пропустить, за исключением случаев, когда выполняется агрессивная очистка. Кроме того, за тем же исключением, можно пропустить и некоторые страницы, чтобы не ждать, когда их закончат использовать другие сеансы. Этот параметр выключает все операции пропуска страниц и предназначен для использования только в том случае, если содержимое карты видимости является подозрительным, что должно происходить, только когда существует аппаратная или программная проблема, вызывающая повреждение базы данных.

SKIP_LOCKED

Указывает, что при начале работы над таблицей VACUUM не должен ждать освобождения конфликтующих блокировок: если таблицу нельзя немедленно заблокировать без ожидания, она пропускается. Обратите внимание, что даже при использовании этого параметра VACUUM по-прежнему может блокироваться при открытии индексов. Также VACUUM ANALYZE может блокироваться при получении выборочных строк из партиций, дочерних элементов наследования таблиц и некоторых типов сторонних таблиц. Кроме того, хотя обычно VACUUM обрабатывает все партиции указанных партиционированных таблиц, этот параметр позволит VACUUM пропустить их, если на партиционированной таблице имеется конфликтующая блокировка.

INDEX_CLEANUP

Указывает, что VACUUM должен пытаться удалить записи индекса, указывающие на мертвые кортежи. Обычно это желаемое поведение и значение по умолчанию, если только параметр vacuum_index_cleanup подлежащей очистке таблицы не был установлен в значение false. Установка этого параметра в значение false может быть полезна при необходимости как можно сильнее ускорить процесс очистки, например, чтобы избежать предстоящего обхода идентификатора транзакции (см. раздел Предотвращение ошибок зацикливания идентификатора транзакции). Однако без регулярной очистки индекса производительность может пострадать, потому что при изменении таблицы индексы будут накапливать мертвые кортежи, а сама таблица будет накапливать указатели мертвых строк, которые нельзя удалить до завершения очистки индекса. Данный параметр не действует для таблиц, которые не имеют индекса, и игнорируется при использовании VACUUM FULL.

TRUNCATE

Указывает, что VACUUM должен попытаться обрезать любые пустые страницы в конце таблицы и освободить дисковое пространство для усеченных страниц, которые будут возвращены в операционную систему. Обычно это желаемое поведение и значение по умолчанию, если только параметр vacuum_truncate подлежащей очистке таблицы не был установлен в значение false. Установка этого параметра в false может быть полезна, чтобы избежать блокировки ACCESS EXCLUSIVE таблицы, которая требуется для усечения. Этот параметр игнорируется при использовании FULL.

PARALLEL

Осуществляет фазы очистки и уборки индексов при параллельном выполнении VACUUM, используя целое_число фоновых рабочих процессов (подробную информацию о каждой фазе очистки см. в таблице Фазы VACUUM). Число рабочих процессов, используемых для этой операции, равно числу индексов в отношении, подходящих для параллельной очистки. Это число ограничено числом рабочих процессов, заданных с помощью параметра PARALLEL (если он указан), которые дополнительно ограничены параметром max_parallel_maintenance_workers. Индекс может обрабатываться в режиме параллельной очистки только в том случае, если размер этого индекса больше указанного в параметре min_parallel_index_scan_size. Пожалуйста, учтите, что это не гарантирует, что при выполнении данной операции будет задействовано столько параллельных рабочих процессов, сколько задано параметром целое_число. Вполне возможно, что в процессе очистки рабочие процессы будут использоваться в меньшем количестве или не будут использоваться вовсе. На обработку каждого индекса может быть задействован только один рабочий процесс, поэтому параллельные рабочие процессы запускаются, только если в таблице имеется как минимум 2 индекса. Рабочие процессы очистки запускаются до начала каждой фазы и завершаются после ее окончания. В будущих релизах это поведение может измениться. Данный параметр нельзя использовать вместе с FULL.

логическое_значение

Указывает, должен ли выбранный параметр быть включен или выключен. Можно написать TRUE, ON или 1, чтобы включить этот параметр, и FALSE, OFF или 0, чтобы выключить. логическое_значение может быть опущено, в этом случае предполагается TRUE.

целое_число

Указывает неотрицательное целое значение, передаваемое выбранному параметру.

имя_таблицы

Имя конкретной таблицы или материализованного представления, подлежащего очистке (может быть дополнено схемой). Если указанная таблица является партиционированной, все ее конечные партиции также очищаются.

имя_столбца

Имя конкретного столбца для анализа. По умолчанию производится анализ всех столбцов. Если указан список столбцов, также должно быть указано ANALYZE.

Вывод на консоль

Когда указывается VERBOSE, команда VACUUM выдает сообщения о том, какая таблица обрабатывается в настоящее время. Также печатаются различные статистические данные о таблицах.

Примечания

Чтобы очистить таблицу, обычно нужно быть ее владельцем или суперпользователем. Однако владельцам баз данных разрешено очищать все таблицы в своих базах данных, за исключением общих каталогов. (Ограничение для общих каталогов означает, что истинную очистку всей базы данных может выполнить только суперпользователь.) VACUUM будет пропускать все таблицы, которые вызывающий пользователь не имеет права очищать.

Команду VACUUM нельзя выполнить внутри блока транзакций.

Для таблиц с индексами GIN VACUUM (в любой форме) также завершает все отложенные добавления индекса, перемещая отложенные записи индекса в соответствующие места в основной структуре индекса GIN. Дополнительную информацию см. в разделе Быстрое обновление GIN.

Для удаления мертвых строк рекомендуется часто (хотя бы каждую ночь) очищать активные промышленные базы данных. После добавления или удаления большого количества строк может быть полезно выполнить для обработанной таблицы команду VACUUM ANALYZE. Это позволит обновить системные каталоги с результатами всех последних изменений, а также позволит планировщику запросов QHB принимать лучшие решения при планировании запросов.

Параметр FULL не рекомендуется для рутинного использования, но может быть полезен в особых случаях. Например, если вы удалили или изменили большую часть строк в таблице и хотели бы, чтобы таблица физически уменьшилась, стала занимать меньше места на диске и позволила бы проводить более быстрое сканирование таблицы. Обычно VACUUM FULL сжимает таблицу более эффективно, чем простой VACUUM.

Параметр PARALLEL используется только в целях очистки. Если этот параметр указан вместе с ANALYZE, на работу ANALYZE он не влияет.

Команда VACUUM вызывает значительное увеличение трафика ввода/вывода, что может привести к низкой производительности для других активных сеансов. Поэтому иногда рекомендуется использовать функцию задержки очистки на основе затрат. При параллельной очистке каждый рабочий процесс переходит в неактивный режим на время, пропорциональное объему произведенной им работы. Подробную информацию см. в разделе Определение предела стоимости работы процесса очистки.

QHB включает в себя установку «autovacuum» (автоочистка), которая может автоматизировать регулярную очистку. Дополнительную информацию об автоматической и ручной очистке см. в разделе Регулярная очистка.

Примеры

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

VACUUM (VERBOSE, ANALYZE) onek;

Совместимость

В стандарте SQL нет команды VACUUM.

См. также

vacuumdb, Определение предела стоимости работы процесса очистки, Процесс «Автовакуум»

VALUES

VALUES — вычислить набор строк

Синтаксис

VALUES ( выражение [, ...] ) [, ...]
    [ ORDER BY выражение_сортировки [ ASC | DESC | USING оператор ] [, ...] ]
    [ LIMIT { число | ALL } ]
    [ OFFSET начало [ ROW | ROWS ] ]
    [ FETCH { FIRST | NEXT } [ число ] { ROW | ROWS } ONLY ]

Описание

Команда VALUES вычисляет значение или набор значений строки, заданных выражениями значений. Чаще всего она используется для формирования «таблицы констант» в рамках более крупной команды, но может быть использована и самостоятельно.

Если указано более одной строки, то все строки должны иметь одинаковое количество элементов. Типы данных столбцов результирующей таблицы определяются путем объединения явных и неявных типов выражений, заданных для этих столбцов, с использованием тех же правил, что и для UNION.

В составе других команд синтаксис допускает использование VALUES везде, где допускается SELECT. Так как грамматически она воспринимается как SELECT, с командой VALUES можно использовать предложения ORDER BY, LIMIT (или равнозначное FETCH FIRST) и OFFSET.

Параметры

выражение

Константа или выражение, подлежащее для вычислению и добавлению в указанное место в результирующей таблице (наборе строк). В списке VALUES, отображаемом на верхнем уровне INSERT, выражение можно заменить на значение DEFAULT, указывая, что должно быть добавлено значение по умолчанию столбца-получателя. DEFAULT нельзя использовать, когда VALUES употребляется в других контекстах.

выражение_сортировки

Выражение или целочисленная константа, указывающая, как сортировать результирующие строки. Это выражение может ссылаться на столбцы результата VALUES по именам column1, column2 и т. д. Дополнительную информацию см. в описании предложения ORDER BY команды SELECT.

оператор

Оператор сортировки. Дополнительную информацию см. в описании предложения ORDER BY команды SELECT.

число

Максимальное количество строк, которое должно быть возвращено. Дополнительную информацию см. в описании предложения LIMIT команды SELECT.

начало

Число строк, которые необходимо пропустить перед началом возврата строк. Дополнительную информацию см. в описании предложения LIMIT команды SELECT.

Примечания

Следует избегать списков VALUES с очень большим количеством строк, так как можно столкнуться с нехваткой памяти или низкой производительностью. Применение VALUES в команде INSERT является особым случаем (поскольку нужные типы столбцов известны из целевой таблицы INSERT, и их не надо вычислять, сканируя весь список VALUES), поэтому может обрабатывать более объемные списки, чем это практично в других контекстах.

Примеры

Простая команда VALUES:

VALUES (1, 'one'), (2, 'two'), (3, 'three');

Эта команда выдаст таблицу из двух столбцов и трех строк. По сути она равнозначна запросу:

SELECT 1 AS column1, 'one' AS column2
UNION ALL
SELECT 2, 'two'
UNION ALL
SELECT 3, 'three';

Более часто VALUES используют в составе другой команды SQL. Чаще всего она применяется в INSERT:

INSERT INTO films (code, title, did, date_prod, kind)
    VALUES ('T_601', 'Yojimbo', 106, '1961-06-16', 'Drama');

В контексте INSERT список VALUES может содержать слово DEFAULT, указывающее, что здесь вместо некоторого значения должно использоваться значение столбца по умолчанию:

INSERT INTO films VALUES
    ('UA502', 'Bananas', 105, DEFAULT, 'Comedy', '82 minutes'),
    ('T_601', 'Yojimbo', 106, DEFAULT, 'Drama', DEFAULT);

VALUES также можно применять там, где есть возможность написать подзапрос SELECT, например, в предложении FROM:

SELECT f.*
  FROM films f, (VALUES('MGM', 'Horror'), ('UA', 'Sci-Fi')) AS t (studio, kind)
  WHERE f.studio = t.studio AND f.kind = t.kind;

UPDATE employees SET salary = salary * v.increase
  FROM (VALUES(1, 200000, 1.2), (2, 400000, 1.4)) AS v (depno, target, increase)
  WHERE employees.depno = v.depno AND employees.sales >= v.target;

Обратите внимание, что когда VALUES используется в предложении FROM, требуется предложение AS, как и для SELECT. При этом имена всех столбцов указывать в AS не обязательно, но рекомендуется. (В QHB именами столбцов по умолчанию являются column1, column2 и т. д., но в других СУБД эти имена могут быть другими.)

Когда VALUES используется в команде INSERT, значения автоматически будут приведены к типу данных соответствующего целевого столбца. Когда же она используется в других контекстах, может потребоваться указать нужный тип данных. Если все записи представлены строковыми константами в кавычках, достаточно привести к нужному типу значения в первой строке, чтобы задать тип для всех строк:

SELECT * FROM machines
WHERE ip_address IN (VALUES('192.168.0.1'::inet), ('192.168.0.10'), ('192.168.1.43'));

Совет
Для проверок на включение IN лучше полагаться на форму IN со списком скаляров, чем писать запрос VALUES, как показано выше. Метод со списком скаляров проще для записи и зачастую более эффективен.

Совместимость

Команда VALUES соответствует стандарту SQL. Параметры LIMIT и OFFSET являются расширениями QHB; см. также раздел SELECT.

См. также

INSERT, SELECT

Поддержка SQL-стандарта

Содержание

Коды ошибок

Поддержка даты и времени

Ключевые слова

Соответствие стандарту SQL

Расширенный SQL

Ключевые слова SQL

В Таблице 1 перечислены все лексемы, которые являются ключевыми словами в стандарте SQL и в QHB. Исходную информацию можно найти в подразделе Идентификаторы и ключевые слова (для экономии места включены только две последние версии стандарта SQL и SQL-92 для исторического сравнения. Различия между ними и другими промежуточными версиями стандарта невелики).

В SQL есть разница между зарезервированными и незарезервированными ключевыми словами. Согласно стандарту, по-настоящему ключевыми словами являются только зарезервированные слова; они не могут быть идентификаторами. Незарезервированные ключевые слова имеют особое значение только в определенных контекстах и могут использоваться как идентификаторы в других. Большинство незарезервированных ключевых слов на самом деле являются именами встроенных таблиц и функций, определенных в SQL. По сути концепция незарезервированных ключевых слов существует только для того, чтобы показать, что в отдельных контекстах слово имеет некоторое предопределенное значение.

В QHB синтаксический анализатор сталкивается с дополнительными трудностями. Существует несколько различных классов лексем, начиная с тех, что никогда не могут использоваться как идентификаторы, и заканчивая теми, что для анализатора абсолютно ничем не отличаются от обычных идентификаторов. (Последнее обычно характерно для функций, определенных в SQL.) Даже зарезервированные ключевые слова не полностью зарезервированы в QHB, а могут использоваться в качестве меток столбцов (например, можно написать SELECT 55 AS CHECK, хотя CHECK и является зарезервированным ключевым словом).

В Таблице 1, в столбце QHB, пометка «не зарезервировано» дается тем ключевым словам, которые явно известны синтаксическому анализатору, но допускаются в качестве имен столбцов или таблиц. Некоторые ключевые слова, которые не могут использоваться в качестве имен функций или типов данных, но в остальном не отличаются от незарезервированных, помечены соответствующим образом. (Большинство этих слов представляют собой встроенные функции или типы данных со специальным синтаксисом. Такие функции или типы все еще доступны, но пользователь уже не может их переопределить.) Меткой «зарезервировано» обозначаются те лексемы, которые не допускаются в качестве имен столбцов или таблиц. Некоторые зарезервированные ключевые слова могут быть именами функций или типов данных; это также показано в таблице. Если такой пометки нет, зарезервированное ключевое слово допускается только в качестве имени метки столбца «AS».

Вообще, при получении нетипичных ошибок во время анализа команд, содержащих в качестве идентификатора любое из перечисленных ключевых слов, для решения проблемы стоит попробовать заключить идентификатор в кавычки.

Перед изучением Таблицы 1 важно понять, что отсутствие ключевого слова в перечне зарезервированных слов в QHB не означает, что функциональность, связанная с этим словом, не реализована. И наоборот, присутствие ключевого слова не указывает на наличие соответствующей функциональности.

Таблица 1. Ключевые слова SQL

Ключевое слово | QHB | SQL: 2016 | SQL: 2011 | SQL-92
A |  | не зарезервировано | не зарезервировано |
ABORT | не зарезервировано |  |  |
ABS |  | зарезервировано | зарезервировано |
ABSENT |  | не зарезервировано | не зарезервировано |
ABSOLUTE | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
ACCESS | не зарезервировано |  |  |
ACCORDING |  | не зарезервировано | не зарезервировано |
ACOS |  | зарезервировано |  |
ACTION | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
ADA |  | не зарезервировано | не зарезервировано | не зарезервировано
ADD | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
ADMIN | не зарезервировано | не зарезервировано | не зарезервировано |
AFTER | не зарезервировано | не зарезервировано | не зарезервировано |
AGGREGATE | не зарезервировано |  |  |
ALL | зарезервировано | зарезервировано | зарезервировано | зарезервировано
ALLOCATE |  | зарезервировано | зарезервировано | зарезервировано
ALSO | не зарезервировано |  |  |
ALTER | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
ALWAYS | не зарезервировано | не зарезервировано | не зарезервировано |
ANALYSE | зарезервировано |  |  |
ANALYZE | зарезервировано |  |  |
AND | зарезервировано | зарезервировано | зарезервировано | зарезервировано
ANY | зарезервировано | зарезервировано | зарезервировано | зарезервировано
APPEND_ONLY | зарезервировано (может быть функцией или типом) | не зарезервировано | не зарезервировано | не зарезервировано
ARE |  | зарезервировано | зарезервировано | зарезервировано
ARRAY | зарезервировано | зарезервировано | зарезервировано |
ARRAY_AGG |  | зарезервировано | зарезервировано |
ARRAY_MAX_CARDINALITY |  | зарезервировано | зарезервировано |
AS | зарезервировано | зарезервировано | зарезервировано | зарезервировано
ASC | зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
ASENSITIVE |  | зарезервировано | зарезервировано |
ASIN |  | зарезервировано |  |
ASSERTION | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
ASSIGNMENT | не зарезервировано | не зарезервировано | не зарезервировано |
ASYMMETRIC | зарезервировано | зарезервировано | зарезервировано |
AT | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
ATAN |  | зарезервировано |  |
ATOMIC |  | зарезервировано | зарезервировано |
ATTACH | не зарезервировано |  |  |
ATTRIBUTE | не зарезервировано | не зарезервировано | не зарезервировано |
ATTRIBUTES |  | не зарезервировано | не зарезервировано |
AUTHORIZATION | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
AVG |  | зарезервировано | зарезервировано | зарезервировано
BACKWARD | не зарезервировано |  |  |
BASE64 |  | не зарезервировано | не зарезервировано |
BEFORE | не зарезервировано | не зарезервировано | не зарезервировано |
BEGIN | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
BEGIN_FRAME |  | зарезервировано | зарезервировано |
BEGIN_PARTITION |  | зарезервировано | зарезервировано |
BERNOULLI |  | не зарезервировано | не зарезервировано |
BETWEEN | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
BIGINT | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
BINARY | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано |
BIT | не зарезервировано (не может быть функцией или типом) |  |  | зарезервировано
BIT_LENGTH |  |  |  | зарезервировано
BLOB |  | зарезервировано | зарезервировано |
BLOCKED |  | не зарезервировано | не зарезервировано |
BOM |  | не зарезервировано | не зарезервировано |
BOOLEAN | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
BOTH | зарезервировано | зарезервировано | зарезервировано | зарезервировано
BREADTH |  | не зарезервировано | не зарезервировано |
BY | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
C |  | не зарезервировано | не зарезервировано | не зарезервировано
CACHE | не зарезервировано |  |  |
CALL | не зарезервировано | зарезервировано | зарезервировано |
CALLED | не зарезервировано | зарезервировано | зарезервировано |
CARDINALITY |  | зарезервировано | зарезервировано |
CASCADE | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
CASCADED | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
CASE | зарезервировано | зарезервировано | зарезервировано | зарезервировано
CAST | зарезервировано | зарезервировано | зарезервировано | зарезервировано
CATALOG | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
CATALOG_NAME |  | не зарезервировано | не зарезервировано | не зарезервировано
CEIL |  | зарезервировано | зарезервировано |
CEILING |  | зарезервировано | зарезервировано |
CHAIN | не зарезервировано | не зарезервировано | не зарезервировано |
CHAINING |  | не зарезервировано |  |
CHAR | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
CHARACTER | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
CHARACTERISTICS | не зарезервировано | не зарезервировано | не зарезервировано |
CHARACTERS |  | не зарезервировано | не зарезервировано |
CHARACTER_LENGTH |  | зарезервировано | зарезервировано | зарезервировано
CHARACTER_SET_CATALOG |  | не зарезервировано | не зарезервировано | не зарезервировано
CHARACTER_SET_NAME |  | не зарезервировано | не зарезервировано | не зарезервировано
CHARACTER_SET_SCHEMA |  | не зарезервировано | не зарезервировано | не зарезервировано
CHAR_LENGTH |  | зарезервировано | зарезервировано | зарезервировано
CHECK | зарезервировано | зарезервировано | зарезервировано | зарезервировано
CHECKPOINT | не зарезервировано |  |  |
CLASS | не зарезервировано |  |  |
CLASSIFIER |  | зарезервировано |  |
CLASS_ORIGIN |  | не зарезервировано | не зарезервировано | не зарезервировано
CLOB |  | зарезервировано | зарезервировано |
CLOSE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
CLUSTER | не зарезервировано |  |  |
COALESCE | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
COBOL |  | не зарезервировано | не зарезервировано | не зарезервировано
COLLATE | зарезервировано | зарезервировано | зарезервировано | зарезервировано
COLLATION | зарезервировано (может быть функцией или типом) | не зарезервировано | не зарезервировано | зарезервировано
COLLATION_CATALOG |  | не зарезервировано | не зарезервировано | не зарезервировано
COLLATION_NAME |  | не зарезервировано | не зарезервировано | не зарезервировано
COLLATION_SCHEMA |  | не зарезервировано | не зарезервировано | не зарезервировано
COLLECT |  | зарезервировано | зарезервировано |
COLUMN | зарезервировано | зарезервировано | зарезервировано | зарезервировано
COLUMNS | не зарезервировано | не зарезервировано | не зарезервировано |
COLUMN_NAME |  | не зарезервировано | не зарезервировано | не зарезервировано
COMMAND_FUNCTION |  | не зарезервировано | не зарезервировано | не зарезервировано
COMMAND_FUNCTION_CODE |  | не зарезервировано | не зарезервировано |
COMMENT | не зарезервировано |  |  |
COMMENTS | не зарезервировано |  |  |
COMMIT | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
COMMITTED | не зарезервировано | не зарезервировано | не зарезервировано | не зарезервировано
CONCURRENTLY | зарезервировано (может быть функцией или типом) |  |  |
CONDITION |  | зарезервировано | зарезервировано |
CONDITIONAL |  | не зарезервировано |  |
CONDITION_NUMBER |  | не зарезервировано | не зарезервировано | не зарезервировано
CONFIGURATION | не зарезервировано |  |  |
CONFLICT | не зарезервировано |  |  |
CONNECT |  | зарезервировано | зарезервировано | зарезервировано
CONNECTION | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
CONNECTION_NAME |  | не зарезервировано | не зарезервировано | не зарезервировано
CONSTRAINT | зарезервировано | зарезервировано | зарезервировано | зарезервировано
CONSTRAINTS | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
CONSTRAINT_CATALOG |  | не зарезервировано | не зарезервировано | не зарезервировано
CONSTRAINT_NAME |  | не зарезервировано | не зарезервировано | не зарезервировано
CONSTRAINT_SCHEMA |  | не зарезервировано | не зарезервировано | не зарезервировано
CONSTRUCTOR |  | не зарезервировано | не зарезервировано |
CONTAINS |  | зарезервировано | зарезервировано |
CONTENT | не зарезервировано | не зарезервировано | не зарезервировано |
CONTINUE | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
CONTROL |  | не зарезервировано | не зарезервировано |
CONVERSION | не зарезервировано |  |  |
CONVERT |  | зарезервировано | зарезервировано | зарезервировано
COPY | не зарезервировано | зарезервировано |  |
CORR |  | зарезервировано | зарезервировано |
CORRESPONDING |  | зарезервировано | зарезервировано | зарезервировано
COS |  | зарезервировано |  |
COSH |  | зарезервировано |  |
COST | не зарезервировано |  |  |
COUNT |  | зарезервировано | зарезервировано | зарезервировано
COVAR_POP |  | зарезервировано | зарезервировано |
COVAR_SAMP |  | зарезервировано | зарезервировано |
CREATE | зарезервировано | зарезервировано | зарезервировано | зарезервировано
CROSS | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
CSV | не зарезервировано |  |  |
CUBE | не зарезервировано | зарезервировано | зарезервировано |
CUME_DIST |  | зарезервировано | зарезервировано |
CURRENT | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
CURRENT_CATALOG | зарезервировано | зарезервировано | зарезервировано |
CURRENT_DATE | зарезервировано | зарезервировано | зарезервировано | зарезервировано
CURRENT_DEFAULT_TRANSFORM_GROUP |  | зарезервировано | зарезервировано |
CURRENT_PATH |  | зарезервировано | зарезервировано |
CURRENT_ROLE | зарезервировано | зарезервировано | зарезервировано |
CURRENT_ROW |  | зарезервировано | зарезервировано |
CURRENT_SCHEMA | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано |
CURRENT_TIME | зарезервировано | зарезервировано | зарезервировано | зарезервировано
CURRENT_TIMESTAMP | зарезервировано | зарезервировано | зарезервировано | зарезервировано
CURRENT_TRANSFORM_GROUP_FOR_TYPE |  | зарезервировано | зарезервировано |
CURRENT_USER | зарезервировано | зарезервировано | зарезервировано | зарезервировано
CURSOR | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
CURSOR_NAME |  | не зарезервировано | не зарезервировано | не зарезервировано
CYCLE | не зарезервировано | зарезервировано | зарезервировано |
DATA | не зарезервировано | не зарезервировано | не зарезервировано | не зарезервировано
DATABASE | не зарезервировано |  |  |
DATALINK |  | зарезервировано | зарезервировано |
DATE |  | зарезервировано | зарезервировано | зарезервировано
DATETIME_INTERVAL_CODE |  | не зарезервировано | не зарезервировано | не зарезервировано
DATETIME_INTERVAL_PRECISION |  | не зарезервировано | не зарезервировано | не зарезервировано
DAY | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
DB |  | не зарезервировано | не зарезервировано |
DEALLOCATE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
DEC | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
DECFLOAT |  | зарезервировано |  |
DECIMAL | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
DECLARE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
DEFAULT | зарезервировано | зарезервировано | зарезервировано | зарезервировано
DEFAULTS | не зарезервировано | не зарезервировано | не зарезервировано |
DEFERRABLE | зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
DEFERRED | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
DEFINE |  | зарезервировано |  |
DEFINED |  | не зарезервировано | не зарезервировано |
DEFINER | не зарезервировано | не зарезервировано | не зарезервировано |
DEGREE |  | не зарезервировано | не зарезервировано |
DELETE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
DELIMITER | не зарезервировано |  |  |
DELIMITERS | не зарезервировано |  |  |
DENSE_RANK |  | зарезервировано | зарезервировано |
DEPENDS | не зарезервировано |  |  |
DEPTH |  | не зарезервировано | не зарезервировано |
DEREF |  | зарезервировано | зарезервировано |
DERIVED |  | не зарезервировано | не зарезервировано |
DESC | зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
DESCRIBE |  | зарезервировано | зарезервировано | зарезервировано
DESCRIPTOR |  | не зарезервировано | не зарезервировано | зарезервировано
DETACH | не зарезервировано |  |  |
DETERMINISTIC |  | зарезервировано | зарезервировано |
DIAGNOSTICS |  | не зарезервировано | не зарезервировано | зарезервировано
DICTIONARY | не зарезервировано |  |  |
DISABLE | не зарезервировано |  |  |
DISCARD | не зарезервировано |  |  |
DISCONNECT |  | зарезервировано | зарезервировано | зарезервировано
DISPATCH |  | не зарезервировано | не зарезервировано |
DISTINCT | зарезервировано | зарезервировано | зарезервировано | зарезервировано
DLNEWCOPY |  | зарезервировано | зарезервировано |
DLPREVIOUSCOPY |  | зарезервировано | зарезервировано |
DLURLCOMPLETE |  | зарезервировано | зарезервировано |
DLURLCOMPLETEONLY |  | зарезервировано | зарезервировано |
DLURLCOMPLETEWRITE |  | зарезервировано | зарезервировано |
DLURLPATH |  | зарезервировано | зарезервировано |
DLURLPATHONLY |  | зарезервировано | зарезервировано |
DLURLPATHWRITE |  | зарезервировано | зарезервировано |
DLURLSCHEME |  | зарезервировано | зарезервировано |
DLURLSERVER |  | зарезервировано | зарезервировано |
DLVALUE |  | зарезервировано | зарезервировано |
DO | зарезервировано |  |  |
DOCUMENT | не зарезервировано | не зарезервировано | не зарезервировано |
DOMAIN | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
DOUBLE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
DROP | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
DYNAMIC |  | зарезервировано | зарезервировано |
DYNAMIC_FUNCTION |  | не зарезервировано | не зарезервировано | не зарезервировано
DYNAMIC_FUNCTION_CODE |  | не зарезервировано | не зарезервировано |
EACH | не зарезервировано | зарезервировано | зарезервировано |
ELEMENT |  | зарезервировано | зарезервировано |
ELSE | зарезервировано | зарезервировано | зарезервировано | зарезервировано
EMPTY |  | зарезервировано | не зарезервировано |
ENABLE | не зарезервировано |  |  |
ENCODING | не зарезервировано | не зарезервировано | не зарезервировано |
ENCRYPTED | не зарезервировано |  |  |
END | зарезервировано | зарезервировано | зарезервировано | зарезервировано
END-EXEC |  | зарезервировано | зарезервировано | зарезервировано
END_FRAME |  | зарезервировано | зарезервировано |
END_PARTITION |  | зарезервировано | зарезервировано |
ENFORCED |  | не зарезервировано | не зарезервировано |
ENUM | не зарезервировано |  |  |
EQUALS |  | зарезервировано | зарезервировано |
ERROR |  | не зарезервировано |  |
ESCAPE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
EVENT | не зарезервировано |  |  |
EVERY |  | зарезервировано | зарезервировано |
EXCEPT | зарезервировано | зарезервировано | зарезервировано | зарезервировано
EXCEPTION |  |  |  | зарезервировано
EXCLUDE | не зарезервировано | не зарезервировано | не зарезервировано |
EXCLUDING | не зарезервировано | не зарезервировано | не зарезервировано |
EXCLUSIVE | не зарезервировано |  |  |
EXEC |  | зарезервировано | зарезервировано | зарезервировано
EXECUTE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
EXISTS | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
EXP |  | зарезервировано | зарезервировано |
EXPLAIN | не зарезервировано |  |  |
EXPRESSION |  | не зарезервировано | не зарезервировано |
EXTENSION | не зарезервировано |  |  |
EXTERNAL | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
EXTRACT | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
FALSE | зарезервировано | зарезервировано | зарезервировано | зарезервировано
FAMILY | не зарезервировано |  |  |
FETCH | зарезервировано | зарезервировано | зарезервировано | зарезервировано
FILE |  | не зарезервировано | не зарезервировано |
FILTER | не зарезервировано | зарезервировано | зарезервировано |
FINAL |  | не зарезервировано | не зарезервировано |
FINISH |  | не зарезервировано |  |
FIRST | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
FIRST_VALUE |  | зарезервировано | зарезервировано |
FLAG |  | не зарезервировано | не зарезервировано |
FLOAT | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
FLOOR |  | зарезервировано | зарезервировано |
FOLLOWING | не зарезервировано | не зарезервировано | не зарезервировано |
FOR | зарезервировано | зарезервировано | зарезервировано | зарезервировано
FORCE | не зарезервировано |  |  |
FOREIGN | зарезервировано | зарезервировано | зарезервировано | зарезервировано
FORMAT |  | не зарезервировано |  |
FORTRAN |  | не зарезервировано | не зарезервировано | не зарезервировано
FORWARD | не зарезервировано |  |  |
FOUND |  | не зарезервировано | не зарезервировано | зарезервировано
FRAME_ROW |  | зарезервировано | зарезервировано |
FREE |  | зарезервировано | зарезервировано |
FREEZE | зарезервировано (может быть функцией или типом) |  |  |
FROM | зарезервировано | зарезервировано | зарезервировано | зарезервировано
FS |  | не зарезервировано | не зарезервировано |
FULFILL |  | не зарезервировано |  |
FULL | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
FUNCTION | не зарезервировано | зарезервировано | зарезервировано |
FUNCTIONS | не зарезервировано |  |  |
FUSION |  | зарезервировано | зарезервировано |
G |  | не зарезервировано | не зарезервировано |
GENERAL |  | не зарезервировано | не зарезервировано |
GENERATED | не зарезервировано | не зарезервировано | не зарезервировано |
GET |  | зарезервировано | зарезервировано | зарезервировано
GLOBAL | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
GO |  | не зарезервировано | не зарезервировано | зарезервировано
GOTO |  | не зарезервировано | не зарезервировано | зарезервировано
GRANT | зарезервировано | зарезервировано | зарезервировано | зарезервировано
GRANTED | не зарезервировано | не зарезервировано | не зарезервировано |
GREATEST | не зарезервировано (не может быть функцией или типом) |  |  |
GROUP | зарезервировано | зарезервировано | зарезервировано | зарезервировано
GROUPING | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
GROUPS | не зарезервировано | зарезервировано | зарезервировано |
HANDLER | не зарезервировано |  |  |
HAVING | зарезервировано | зарезервировано | зарезервировано | зарезервировано
HEADER | не зарезервировано |  |  |
HEX |  | не зарезервировано | не зарезервировано |
HIERARCHY |  | не зарезервировано | не зарезервировано |
HOLD | не зарезервировано | зарезервировано | зарезервировано |
HOUR | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
ID |  | не зарезервировано | не зарезервировано |
IDENTITY | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
IF | не зарезервировано |  |  |
IGNORE |  | не зарезервировано | не зарезервировано |
ILIKE | зарезервировано (может быть функцией или типом) |  |  |
IMMEDIATE | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
IMMEDIATELY |  | не зарезервировано | не зарезервировано |
IMMUTABLE | не зарезервировано |  |  |
IMPLEMENTATION |  | не зарезервировано | не зарезервировано |
IMPLICIT | не зарезервировано |  |  |
IMPORT | не зарезервировано | зарезервировано | зарезервировано |
IN | зарезервировано | зарезервировано | зарезервировано | зарезервировано
INCLUDE | не зарезервировано |  |  |
INCLUDING | не зарезервировано | не зарезервировано | не зарезервировано |
INCREMENT | не зарезервировано | не зарезервировано | не зарезервировано |
INDENT |  | не зарезервировано | не зарезервировано |
INDEX | не зарезервировано |  |  |
INDEXES | не зарезервировано |  |  |
INDICATOR |  | зарезервировано | зарезервировано | зарезервировано
INHERIT | не зарезервировано |  |  |
INHERITS | не зарезервировано |  |  |
INITIAL |  | зарезервировано |  |
INITIALLY | зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
INLINE | не зарезервировано |  |  |
INNER | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
INOUT | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
INPUT | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
INSENSITIVE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
INSERT | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
INSTANCE |  | не зарезервировано | не зарезервировано |
INSTANTIABLE |  | не зарезервировано | не зарезервировано |
INSTEAD | не зарезервировано | не зарезервировано | не зарезервировано |
INT | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
INTEGER | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
INTEGRITY |  | не зарезервировано | не зарезервировано |
INTERSECT | зарезервировано | зарезервировано | зарезервировано | зарезервировано
INTERSECTION |  | зарезервировано | зарезервировано |
INTERVAL | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
INTO | зарезервировано | зарезервировано | зарезервировано | зарезервировано
INVOKER | не зарезервировано | не зарезервировано | не зарезервировано |
IS | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
ISNULL | зарезервировано (может быть функцией или типом) |  |  |
ISOLATION | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
JOIN | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
JSON |  | не зарезервировано |  |
JSON_ARRAY |  | зарезервировано |  |
JSON_ARRAYAGG |  | зарезервировано |  |
JSON_EXISTS |  | зарезервировано |  |
JSON_OBJECT |  | зарезервировано |  |
JSON_OBJECTAGG |  | зарезервировано |  |
JSON_QUERY |  | зарезервировано |  |
JSON_TABLE |  | зарезервировано |  |
JSON_TABLE_PRIMITIVE |  | зарезервировано |  |
JSON_VALUE |  | зарезервировано |  |
K |  | не зарезервировано | не зарезервировано |
KEEP |  | не зарезервировано |  |
KEY | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
KEYS |  | не зарезервировано |  |
KEY_MEMBER |  | не зарезервировано | не зарезервировано |
KEY_TYPE |  | не зарезервировано | не зарезервировано |
LABEL | не зарезервировано |  |  |
LAG |  | зарезервировано | зарезервировано |
LANGUAGE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
LARGE | не зарезервировано | зарезервировано | зарезервировано |
LAST | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
LAST_VALUE |  | зарезервировано | зарезервировано |
LATERAL | зарезервировано | зарезервировано | зарезервировано |
LEAD |  | зарезервировано | зарезервировано |
LEADING | зарезервировано | зарезервировано | зарезервировано | зарезервировано
LEAKPROOF | не зарезервировано |  |  |
LEAST | не зарезервировано (не может быть функцией или типом) |  |  |
LEFT | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
LENGTH |  | не зарезервировано | не зарезервировано | не зарезервировано
LEVEL | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
LIBRARY |  | не зарезервировано | не зарезервировано |
LIKE | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
LIKE_REGEX |  | зарезервировано | зарезервировано |
LIMIT | зарезервировано | не зарезервировано | не зарезервировано |
LINK |  | не зарезервировано | не зарезервировано |
LISTAGG |  | зарезервировано |  |
LISTEN | не зарезервировано |  |  |
LN |  | зарезервировано | зарезервировано |
LOAD | не зарезервировано |  |  |
LOCAL | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
LOCALTIME | зарезервировано | зарезервировано | зарезервировано |
LOCALTIMESTAMP | зарезервировано | зарезервировано | зарезервировано |
LOCATION | не зарезервировано | не зарезервировано | не зарезервировано |
LOCATOR |  | не зарезервировано | не зарезервировано |
LOCK | не зарезервировано |  |  |
LOCKED | не зарезервировано |  |  |
LOG |  | зарезервировано |  |
LOG10 |  | зарезервировано |  |
LOGGED | не зарезервировано |  |  |
LOWER |  | зарезервировано | зарезервировано | зарезервировано
M |  | не зарезервировано | не зарезервировано |
MAP |  | не зарезервировано | не зарезервировано |
MAPPING | не зарезервировано | не зарезервировано | не зарезервировано |
MATCH | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
MATCHED |  | не зарезервировано | не зарезервировано |
MATCHES |  | зарезервировано |  |
MATCH_NUMBER |  | зарезервировано |  |
MATCH_RECOGNIZE |  | зарезервировано |  |
MATERIALIZED | не зарезервировано |  |  |
MAX |  | зарезервировано | зарезервировано | зарезервировано
MAXVALUE | не зарезервировано | не зарезервировано | не зарезервировано |
MEASURES |  | зарезервировано |  |
MEMBER |  | зарезервировано | зарезервировано |
MERGE |  | зарезервировано | зарезервировано |
MESSAGE_LENGTH |  | не зарезервировано | не зарезервировано | не зарезервировано
MESSAGE_OCTET_LENGTH |  | не зарезервировано | не зарезервировано | не зарезервировано
MESSAGE_TEXT |  | не зарезервировано | не зарезервировано | не зарезервировано
METHOD | не зарезервировано | зарезервировано | зарезервировано |
MIN |  | зарезервировано | зарезервировано | зарезервировано
MINUTE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
MINVALUE | не зарезервировано | не зарезервировано | не зарезервировано |
MOD |  | зарезервировано | зарезервировано |
MODE | не зарезервировано |  |  |
MODIFIES |  | зарезервировано | зарезервировано |
MODULE |  | зарезервировано | зарезервировано | зарезервировано
MONTH | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
MORE |  | не зарезервировано | не зарезервировано | не зарезервировано
MOVE | не зарезервировано |  |  |
MULTISET |  | зарезервировано | зарезервировано |
MUMPS |  | не зарезервировано | не зарезервировано | не зарезервировано
NAME | не зарезервировано | не зарезервировано | не зарезервировано | не зарезервировано
NAMES | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
NAMESPACE |  | не зарезервировано | не зарезервировано |
NATIONAL | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
NATURAL | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
NCHAR | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
NCLOB |  | зарезервировано | зарезервировано |
NESTED |  | не зарезервировано |  |
NESTING |  | не зарезервировано | не зарезервировано |
NEW | не зарезервировано | зарезервировано | зарезервировано |
NEXT | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
NFC |  | не зарезервировано | не зарезервировано |
NFD |  | не зарезервировано | не зарезервировано |
NFKC |  | не зарезервировано | не зарезервировано |
NFKD |  | не зарезервировано | не зарезервировано |
NIL |  | не зарезервировано | не зарезервировано |
NO | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
NONE | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
NORMALIZE |  | зарезервировано | зарезервировано |
NORMALIZED |  | не зарезервировано | не зарезервировано |
NOT | зарезервировано | зарезервировано | зарезервировано | зарезервировано
NOTHING | не зарезервировано |  |  |
NOTIFY | не зарезервировано |  |  |
NOTNULL | зарезервировано (может быть функцией или типом) |  |  |
NOWAIT | не зарезервировано |  |  |
NTH_VALUE |  | зарезервировано | зарезервировано |
NTILE |  | зарезервировано | зарезервировано |
NULL | зарезервировано | зарезервировано | зарезервировано | зарезервировано
NULLABLE |  | не зарезервировано | не зарезервировано | не зарезервировано
NULLIF | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
NULLS | не зарезервировано | не зарезервировано | не зарезервировано |
NUMBER |  | не зарезервировано | не зарезервировано | не зарезервировано
NUMERIC | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
OBJECT | не зарезервировано | не зарезервировано | не зарезервировано |
OCCURRENCES_REGEX |  | зарезервировано | зарезервировано |
OCTETS |  | не зарезервировано | не зарезервировано |
OCTET_LENGTH |  | зарезервировано | зарезервировано | зарезервировано
OF | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
OFF | не зарезервировано | не зарезервировано | не зарезервировано |
OFFSET | зарезервировано | зарезервировано | зарезервировано |
OIDS | не зарезервировано |  |  |
OLD | не зарезервировано | зарезервировано | зарезервировано |
OMIT |  | зарезервировано |  |
ON | зарезервировано | зарезервировано | зарезервировано | зарезервировано
ONE |  | зарезервировано |  |
ONLY | зарезервировано | зарезервировано | зарезервировано | зарезервировано
OPEN |  | зарезервировано | зарезервировано | зарезервировано
OPERATOR | не зарезервировано |  |  |
OPTION | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
OPTIONS | не зарезервировано | не зарезервировано | не зарезервировано |
OR | зарезервировано | зарезервировано | зарезервировано | зарезервировано
ORDER | зарезервировано | зарезервировано | зарезервировано | зарезервировано
ORDERING |  | не зарезервировано | не зарезервировано |
ORDINALITY | не зарезервировано | не зарезервировано | не зарезервировано |
OTHERS | не зарезервировано | не зарезервировано | не зарезервировано |
OUT | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
OUTER | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
OUTPUT |  | не зарезервировано | не зарезервировано | зарезервировано
OVER | не зарезервировано | зарезервировано | зарезервировано |
OVERFLOW |  | не зарезервировано |  |
OVERLAPS | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
OVERLAY | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
OVERRIDING | не зарезервировано | не зарезервировано | не зарезервировано |
OWNED | не зарезервировано |  |  |
OWNER | не зарезервировано |  |  |
P |  | не зарезервировано | не зарезервировано |
PAD |  | не зарезервировано | не зарезервировано | зарезервировано
PARALLEL | не зарезервировано |  |  |
PARAMETER |  | зарезервировано | зарезервировано |
PARAMETER_MODE |  | не зарезервировано | не зарезервировано |
PARAMETER_NAME |  | не зарезервировано | не зарезервировано |
PARAMETER_ORDINAL_POSITION |  | не зарезервировано | не зарезервировано |
PARAMETER_SPECIFIC_CATALOG |  | не зарезервировано | не зарезервировано |
PARAMETER_SPECIFIC_NAME |  | не зарезервировано | не зарезервировано |
PARAMETER_SPECIFIC_SCHEMA |  | не зарезервировано | не зарезервировано |
PARSER | не зарезервировано |  |  |
PARTIAL | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
PARTITION | не зарезервировано | зарезервировано | зарезервировано |
PASCAL |  | не зарезервировано | не зарезервировано | не зарезервировано
PASS |  | не зарезервировано |  |
PASSING | не зарезервировано | не зарезервировано | не зарезервировано |
PASSTHROUGH |  | не зарезервировано | не зарезервировано |
PASSWORD | не зарезервировано |  |  |
PAST |  | не зарезервировано |  |
PATH |  | не зарезервировано | не зарезервировано |
PATTERN |  | зарезервировано |  |
PER |  | зарезервировано |  |
PERCENT |  | зарезервировано | зарезервировано |
PERCENTILE_CONT |  | зарезервировано | зарезервировано |
PERCENTILE_DISC |  | зарезервировано | зарезервировано |
PERCENT_RANK |  | зарезервировано | зарезервировано |
PERIOD |  | зарезервировано | зарезервировано |
PERMISSION |  | не зарезервировано | не зарезервировано |
PERMUTE |  | зарезервировано |  |
PLACING | зарезервировано | не зарезервировано | не зарезервировано |
PLAN |  | не зарезервировано |  |
PLANS | не зарезервировано |  |  |
PLI |  | не зарезервировано | не зарезервировано | не зарезервировано
POLICY | не зарезервировано |  |  |
PORTION |  | зарезервировано | зарезервировано |
POSITION | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
POSITION_REGEX |  | зарезервировано | зарезервировано |
POWER |  | зарезервировано | зарезервировано |
PRECEDES |  | зарезервировано | зарезервировано |
PRECEDING | не зарезервировано | не зарезервировано | не зарезервировано |
PRECISION | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
PREPARE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
PREPARED | не зарезервировано |  |  |
PRESERVE | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
PRIMARY | зарезервировано | зарезервировано | зарезервировано | зарезервировано
PRIOR | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
PRIVATE |  | не зарезервировано |  |
PRIVILEGES | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
PROCEDURAL | не зарезервировано |  |  |
PROCEDURE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
PROCEDURES | не зарезервировано |  |  |
PROGRAM | не зарезервировано |  |  |
PRUNE |  | не зарезервировано |  |
PTF |  | зарезервировано |  |
PUBLIC |  | не зарезервировано | не зарезервировано | зарезервировано
PUBLICATION | не зарезервировано |  |  |
QUOTE | не зарезервировано |  |  |
QUOTES |  | не зарезервировано |  |
RANGE | не зарезервировано | зарезервировано | зарезервировано |
RANK |  | зарезервировано | зарезервировано |
READ | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
READS |  | зарезервировано | зарезервировано |
REAL | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
REASSIGN | не зарезервировано |  |  |
RECHECK | не зарезервировано |  |  |
RECOVERY |  | не зарезервировано | не зарезервировано |
RECURSIVE | не зарезервировано | зарезервировано | зарезервировано |
REF | не зарезервировано | зарезервировано | зарезервировано |
REFERENCES | зарезервировано | зарезервировано | зарезервировано | зарезервировано
REFERENCING | не зарезервировано | зарезервировано | зарезервировано |
REFRESH | не зарезервировано |  |  |
REGR_AVGX |  | зарезервировано | зарезервировано |
REGR_AVGY |  | зарезервировано | зарезервировано |
REGR_COUNT |  | зарезервировано | зарезервировано |
REGR_INTERCEPT |  | зарезервировано | зарезервировано |
REGR_R2 |  | зарезервировано | зарезервировано |
REGR_SLOPE |  | зарезервировано | зарезервировано |
REGR_SXX |  | зарезервировано | зарезервировано |
REGR_SXY |  | зарезервировано | зарезервировано |
REGR_SYY |  | зарезервировано | зарезервировано |
REINDEX | не зарезервировано |  |  |
RELATIVE | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
RELEASE | не зарезервировано | зарезервировано | зарезервировано |
RENAME | не зарезервировано |  |  |
REPEATABLE | не зарезервировано | не зарезервировано | не зарезервировано | не зарезервировано
REPLACE | не зарезервировано |  |  |
REPLICA | не зарезервировано |  |  |
REQUIRING |  | не зарезервировано | не зарезервировано |
RESET | не зарезервировано |  |  |
RESPECT |  | не зарезервировано | не зарезервировано |
RESTART | не зарезервировано | не зарезервировано | не зарезервировано |
RESTORE |  | не зарезервировано | не зарезервировано |
RESTRICT | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
RESULT |  | зарезервировано | зарезервировано |
RETURN |  | зарезервировано | зарезервировано |
RETURNED_CARDINALITY |  | не зарезервировано | не зарезервировано |
RETURNED_LENGTH |  | не зарезервировано | не зарезервировано | не зарезервировано
RETURNED_OCTET_LENGTH |  | не зарезервировано | не зарезервировано | не зарезервировано
RETURNED_SQLSTATE |  | не зарезервировано | не зарезервировано | не зарезервировано
RETURNING | зарезервировано | не зарезервировано | не зарезервировано |
RETURNS | не зарезервировано | зарезервировано | зарезервировано |
REVOKE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
RIGHT | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
ROLE | не зарезервировано | не зарезервировано | не зарезервировано |
ROLLBACK | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
ROLLUP | не зарезервировано | зарезервировано | зарезервировано |
ROUTINE | не зарезервировано | не зарезервировано | не зарезервировано |
ROUTINES | не зарезервировано |  |  |
ROUTINE_CATALOG |  | не зарезервировано | не зарезервировано |
ROUTINE_NAME |  | не зарезервировано | не зарезервировано |
ROUTINE_SCHEMA |  | не зарезервировано | не зарезервировано |
ROW | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
ROWS | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
ROW_COUNT |  | не зарезервировано | не зарезервировано | не зарезервировано
ROW_NUMBER |  | зарезервировано | зарезервировано |
RULE | не зарезервировано |  |  |
RUNNING |  | зарезервировано |  |
SAVEPOINT | не зарезервировано | зарезервировано | зарезервировано |
SCALAR |  | не зарезервировано |  |
SCALE |  | не зарезервировано | не зарезервировано | не зарезервировано
SCHEMA | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
SCHEMAS | не зарезервировано |  |  |
SCHEMA_NAME |  | не зарезервировано | не зарезервировано | не зарезервировано
SCOPE |  | зарезервировано | зарезервировано |
SCOPE_CATALOG |  | не зарезервировано | не зарезервировано |
SCOPE_NAME |  | не зарезервировано | не зарезервировано |
SCOPE_SCHEMA |  | не зарезервировано | не зарезервировано |
SCROLL | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
SEARCH | не зарезервировано | зарезервировано | зарезервировано |
SECOND | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
SECTION |  | не зарезервировано | не зарезервировано | зарезервировано
SECURITY | не зарезервировано | не зарезервировано | не зарезервировано |
SEEK |  | зарезервировано |  |
SELECT | зарезервировано | зарезервировано | зарезервировано | зарезервировано
SELECTIVE |  | не зарезервировано | не зарезервировано |
SELF |  | не зарезервировано | не зарезервировано |
SENSITIVE |  | зарезервировано | зарезервировано |
SEQUENCE | не зарезервировано | не зарезервировано | не зарезервировано |
SEQUENCES | не зарезервировано |  |  |
SERIALIZABLE | не зарезервировано | не зарезервировано | не зарезервировано | не зарезервировано
SERVER | не зарезервировано | не зарезервировано | не зарезервировано |
SERVER_NAME |  | не зарезервировано | не зарезервировано | не зарезервировано
SESSION | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
SESSION_USER | зарезервировано | зарезервировано | зарезервировано | зарезервировано
SET | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
SETOF | не зарезервировано (не может быть функцией или типом) |  |  |
SETS | не зарезервировано | не зарезервировано | не зарезервировано |
SHARE | не зарезервировано |  |  |
SHOW | не зарезервировано | зарезервировано |  |
SIMILAR | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано |
SIMPLE | не зарезервировано | не зарезервировано | не зарезервировано |
SIN |  | зарезервировано |  |
SINH |  | зарезервировано |  |
SIZE |  | не зарезервировано | не зарезервировано | зарезервировано
SKIP | не зарезервировано | зарезервировано |  |
SMALLINT | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
SNAPSHOT | не зарезервировано |  |  |
SOME | зарезервировано | зарезервировано | зарезервировано | зарезервировано
SOURCE |  | не зарезервировано | не зарезервировано |
SPACE |  | не зарезервировано | не зарезервировано | зарезервировано
SPECIFIC |  | зарезервировано | зарезервировано |
SPECIFICTYPE |  | зарезервировано | зарезервировано |
SPECIFIC_NAME |  | не зарезервировано | не зарезервировано |
SQL | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
SQLCODE |  |  |  | зарезервировано
SQLERROR |  |  |  | зарезервировано
SQLEXCEPTION |  | зарезервировано | зарезервировано |
SQLSTATE |  | зарезервировано | зарезервировано | зарезервировано
SQLWARNING |  | зарезервировано | зарезервировано |
SQRT |  | зарезервировано | зарезервировано |
STABLE | не зарезервировано |  |  |
STANDALONE | не зарезервировано | не зарезервировано | не зарезервировано |
START | не зарезервировано | зарезервировано | зарезервировано |
STATE |  | не зарезервировано | не зарезервировано |
STATEMENT | не зарезервировано | не зарезервировано | не зарезервировано |
STATIC |  | зарезервировано | зарезервировано |
STATISTICS | не зарезервировано |  |  |
STDDEV_POP |  | зарезервировано | зарезервировано |
STDDEV_SAMP |  | зарезервировано | зарезервировано |
STDIN | не зарезервировано |  |  |
STDOUT | не зарезервировано |  |  |
STORAGE | не зарезервировано |  |  |
STORED | не зарезервировано |  |  |
STRICT | не зарезервировано |  |  |
STRING |  | не зарезервировано |  |
STRIP | не зарезервировано | не зарезервировано | не зарезервировано |
STRUCTURE |  | не зарезервировано | не зарезервировано |
STYLE |  | не зарезервировано | не зарезервировано |
SUBCLASS_ORIGIN |  | не зарезервировано | не зарезервировано | не зарезервировано
SUBMULTISET |  | зарезервировано | зарезервировано |
SUBSCRIPTION | не зарезервировано |  |  |
SUBSET |  | зарезервировано |  |
SUBSTRING | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
SUBSTRING_REGEX |  | зарезервировано | зарезервировано |
SUCCEEDS |  | зарезервировано | зарезервировано |
SUM |  | зарезервировано | зарезервировано | зарезервировано
SUPPORT | не зарезервировано |  |  |
SYMMETRIC | зарезервировано | зарезервировано | зарезервировано |
SYSID | не зарезервировано |  |  |
SYSTEM | не зарезервировано | зарезервировано | зарезервировано |
SYSTEM_TIME |  | зарезервировано | зарезервировано |
SYSTEM_USER |  | зарезервировано | зарезервировано | зарезервировано
T |  | не зарезервировано | не зарезервировано |
TABLE | зарезервировано | зарезервировано | зарезервировано | зарезервировано
TABLES | не зарезервировано |  |  |
TABLESAMPLE | зарезервировано (может быть функцией или типом) | зарезервировано | зарезервировано |
TABLESPACE | не зарезервировано |  |  |
TABLE_NAME |  | не зарезервировано | не зарезервировано | не зарезервировано
TAN |  | зарезервировано |  |
TANH |  | зарезервировано |  |
TEMP | не зарезервировано |  |  |
TEMPLATE | не зарезервировано |  |  |
TEMPORARY | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
TEXT | не зарезервировано |  |  |
THEN | зарезервировано | зарезервировано | зарезервировано | зарезервировано
THROUGH |  | не зарезервировано |  |
TIES | не зарезервировано | не зарезервировано | не зарезервировано |
TIME | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
TIMESTAMP | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
TIMEZONE_HOUR |  | зарезервировано | зарезервировано | зарезервировано
TIMEZONE_MINUTE |  | зарезервировано | зарезервировано | зарезервировано
TO | зарезервировано | зарезервировано | зарезервировано | зарезервировано
TOKEN |  | не зарезервировано | не зарезервировано |
TOP_LEVEL_COUNT |  | не зарезервировано | не зарезервировано |
TRAILING | зарезервировано | зарезервировано | зарезервировано | зарезервировано
TRANSACTION | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
TRANSACTIONS_COMMITTED |  | не зарезервировано | не зарезервировано |
TRANSACTIONS_ROLLED_BACK |  | не зарезервировано | не зарезервировано |
TRANSACTION_ACTIVE |  | не зарезервировано | не зарезервировано |
TRANSFORM | не зарезервировано | не зарезервировано | не зарезервировано |
TRANSFORMS |  | не зарезервировано | не зарезервировано |
TRANSLATE |  | зарезервировано | зарезервировано | зарезервировано
TRANSLATE_REGEX |  | зарезервировано | зарезервировано |
TRANSLATION |  | зарезервировано | зарезервировано | зарезервировано
TREAT | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
TRIGGER | не зарезервировано | зарезервировано | зарезервировано |
TRIGGER_CATALOG |  | не зарезервировано | не зарезервировано |
TRIGGER_NAME |  | не зарезервировано | не зарезервировано |
TRIGGER_SCHEMA |  | не зарезервировано | не зарезервировано |
TRIM | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
TRIM_ARRAY |  | зарезервировано | зарезервировано |
TRUE | зарезервировано | зарезервировано | зарезервировано | зарезервировано
TRUNCATE | не зарезервировано | зарезервировано | зарезервировано |
TRUSTED | не зарезервировано |  |  |
TYPE | не зарезервировано | не зарезервировано | не зарезервировано | не зарезервировано
TYPES | не зарезервировано |  |  |
UESCAPE |  | зарезервировано | зарезервировано |
UNBOUNDED | не зарезервировано | не зарезервировано | не зарезервировано |
UNCOMMITTED | не зарезервировано | не зарезервировано | не зарезервировано | не зарезервировано
UNCONDITIONAL |  | не зарезервировано |  |
UNDER |  | не зарезервировано | не зарезервировано |
UNENCRYPTED | не зарезервировано |  |  |
UNION | зарезервировано | зарезервировано | зарезервировано | зарезервировано
UNIQUE | зарезервировано | зарезервировано | зарезервировано | зарезервировано
UNKNOWN | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
UNLINK |  | не зарезервировано | не зарезервировано |
UNLISTEN | не зарезервировано |  |  |
UNLOGGED | не зарезервировано |  |  |
UNMATCHED |  | зарезервировано |  |
UNNAMED |  | не зарезервировано | не зарезервировано | не зарезервировано
UNNEST |  | зарезервировано | зарезервировано |
UNTIL | не зарезервировано |  |  |
UNTYPED |  | не зарезервировано | не зарезервировано |
UPDATE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
UPPER |  | зарезервировано | зарезервировано | зарезервировано
URI |  | не зарезервировано | не зарезервировано |
USAGE |  | не зарезервировано | не зарезервировано | зарезервировано
USER | зарезервировано | зарезервировано | зарезервировано | зарезервировано
USER_DEFINED_TYPE_CATALOG |  | не зарезервировано | не зарезервировано |
USER_DEFINED_TYPE_CODE |  | не зарезервировано | не зарезервировано |
USER_DEFINED_TYPE_NAME |  | не зарезервировано | не зарезервировано |
USER_DEFINED_TYPE_SCHEMA |  | не зарезервировано | не зарезервировано |
USING | зарезервировано | зарезервировано | зарезервировано | зарезервировано
UTF16 |  | не зарезервировано |  |
UTF32 |  | не зарезервировано |  |
UTF8 |  | не зарезервировано |  |
VACUUM | не зарезервировано |  |  |
VALID | не зарезервировано | не зарезервировано | не зарезервировано |
VALIDATE | не зарезервировано |  |  |
VALIDATOR | не зарезервировано |  |  |
VALUE | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
VALUES | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
VALUE_OF |  | зарезервировано | зарезервировано |
VARBINARY |  | зарезервировано | зарезервировано |
VARCHAR | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано | зарезервировано
VARIADIC | зарезервировано |  |  |
VARYING | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
VAR_POP |  | зарезервировано | зарезервировано |
VAR_SAMP |  | зарезервировано | зарезервировано |
VERBOSE | зарезервировано (может быть функцией или типом) |  |  |
VERSION | не зарезервировано | не зарезервировано | не зарезервировано |
VERSIONING |  | зарезервировано | зарезервировано |
VIEW | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
VIEWS | не зарезервировано |  |  |
VOLATILE | не зарезервировано |  |  |
WHEN | зарезервировано | зарезервировано | зарезервировано | зарезервировано
WHENEVER |  | зарезервировано | зарезервировано | зарезервировано
WHERE | зарезервировано | зарезервировано | зарезервировано | зарезервировано
WHITESPACE | не зарезервировано | не зарезервировано | не зарезервировано |
WIDTH_BUCKET |  | зарезервировано | зарезервировано |
WINDOW | зарезервировано | зарезервировано | зарезервировано |
WITH | зарезервировано | зарезервировано | зарезервировано | зарезервировано
WITHIN | не зарезервировано | зарезервировано | зарезервировано |
WITHOUT | не зарезервировано | зарезервировано | зарезервировано |
WORK | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
WRAPPER | не зарезервировано | не зарезервировано | не зарезервировано |
WRITE | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано
XML | не зарезервировано | зарезервировано | зарезервировано |
XMLAGG |  | зарезервировано | зарезервировано |
XMLATTRIBUTES | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
XMLBINARY |  | зарезервировано | зарезервировано |
XMLCAST |  | зарезервировано | зарезервировано |
XMLCOMMENT |  | зарезервировано | зарезервировано |
XMLCONCAT | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
XMLDECLARATION |  | не зарезервировано | не зарезервировано |
XMLDOCUMENT |  | зарезервировано | зарезервировано |
XMLELEMENT | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
XMLEXISTS | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
XMLFOREST | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
XMLITERATE |  | зарезервировано | зарезервировано |
XMLNAMESPACES | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
XMLPARSE | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
XMLPI | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
XMLQUERY |  | зарезервировано | зарезервировано |
XMLROOT | не зарезервировано (не может быть функцией или типом) |  |  |
XMLSCHEMA |  | не зарезервировано | не зарезервировано |
XMLSERIALIZE | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
XMLTABLE | не зарезервировано (не может быть функцией или типом) | зарезервировано | зарезервировано |
XMLTEXT |  | зарезервировано | зарезервировано |
XMLVALIDATE |  | зарезервировано | зарезервировано |
YEAR | не зарезервировано | зарезервировано | зарезервировано | зарезервировано
YES | не зарезервировано | не зарезервировано | не зарезервировано |
ZONE | не зарезервировано | не зарезервировано | не зарезервировано | зарезервировано

Коды ошибок QHB

Всем сообщениям, которые выдает сервер QHB, назначены пятисимвольные коды ошибок, соответствующие кодам «SQLSTATE», описанным в стандарте SQL. Приложения, которые должны знать, какое условие ошибки имело место, обычно должны проверять код ошибки, а не обращаться к текстовому сообщению о ней. Коды ошибок, скорее всего, не изменятся от выпуска к выпуску QHB, а кроме того, они не меняются при локализации сообщений об ошибках. Обратите внимание, что не все коды ошибок, которые выдает QHB, определены стандартом SQL; некоторые дополнительные коды ошибок для условий, не описанных стандартом, были добавлены независимо или позаимствованы из других баз данных.

Согласно стандарту, первые два символа кода ошибки обозначают класс ошибок, а последние три символа обозначают определенное условие в этом классе. Благодаря этому, приложение, не знающее значение определенного кода ошибки, все равно может понять, что делать, по классу ошибки.

В Таблице 1 перечислены все коды ошибок, определенные в QHB. (Некоторые из них в настоящее время не используются, хотя и определены в стандарте SQL.) Также показаны классы ошибок. Для каждого класса ошибок имеется «стандартный» код ошибки с последними тремя символами 000. Этот код выдается только для тех условий ошибок, которые относятся к некоторому классу, но не имеют более определенного кода.

Символ, указанный во втором столбце, — имя условия в PL/pgSQL. Имена условий могут записываться в верхнем или нижнем регистре. (Обратите внимание, что PL/pgSQL, в отличие от ошибок, не распознает предупреждения; то есть классы 00, 01 и 02.)

Для некоторых типов ошибок сервер сообщает имя объекта базы данных (таблицу, столбец таблицы, тип данных или ограничение), связанного с ошибкой; например, имя уникального ограничения, вызвавшего ошибку unique_violation. Такие имена передаются в отдельных полях сообщения об ошибке, чтобы приложениям не пришлось извлекать их из возможно локализованного текста сообщения для человека. Пока что полностью охвачены этой особенностью только ошибки класса SQLSTATE 23 (нарушения ограничений целостности), но в будущем, скорее всего, будут охвачены и другие классы.

Таблица 1. Коды ошибок QHB

Код ошибки | Имя условия
Класс 00 — Успешное завершение |
00000 | successful_completion
Класс 01 — Предупреждение |
01000 | warning
0100C | dynamic_result_sets_returned
01008 | implicit_zero_bit_padding
01003 | null_value_eliminated_in_set_function
01007 | privilege_not_granted
01006 | privilege_not_revoked
01004 | string_data_right_truncation
01P01 | deprecated_feature
Класс 02 — Нет данных (это также класс предупреждений согласно стандарту SQL) |
02000 | no_data
02001 | no_additional_dynamic_result_sets_returned
Класс 03 — Оператор SQL еще не завершен |
03000 | sql_statement_not_yet_complete
Класс 08 — Исключение, связанное с подключением |
08000 | connection_exception
08003 | connection_does_not_exist
08006 | connection_failure
08001 | sqlclient_unable_to_establish_sqlconnection
08004 | sqlserver_rejected_establishment_of_sqlconnection
08007 | transaction_resolution_unknown
08P01 | protocol_violation
Класс 09 — Исключение с действием триггера |
09000 | triggered_action_exception
Класс 0A — Неподдерживаемая функциональность |
0A000 | feature_not_supported
Класс 0B — Неверное начало транзакции |
0B000 | invalid_transaction_initiation
Класс 0F — Исключение с указателем на данные |
0F000 | locator_exception
0F001 | invalid_locator_specification
Класс 0L — Неверный праводатель |
0L000 | invalid_grantor
0LP01 | invalid_grant_operation
Класс 0P — Неверное указание роли |
0P000 | invalid_role_specification
Класс 0Z — Исключение диагностики |
0Z000 | diagnostics_exception
0Z002 | stacked_diagnostics_accessed_without_active_handler
Класс 20 — Case не найден |
20000 | case_not_found
Класс 21 — Нарушение количества |
21000 | cardinality_violation
Класс 22 — Исключение в данных |
22000 | data_exception
2202E | array_subscript_error
22021 | character_not_in_repertoire
22008 | datetime_field_overflow
22012 | division_by_zero
22005 | error_in_assignment
2200B | escape_character_conflict
22022 | indicator_overflow
22015 | interval_field_overflow
2201E | invalid_argument_for_logarithm
22014 | invalid_argument_for_ntile_function
22016 | invalid_argument_for_nth_value_function
2201F | invalid_argument_for_power_function
2201G | invalid_argument_for_width_bucket_function
22018 | invalid_character_value_for_cast
22007 | invalid_datetime_format
22019 | invalid_escape_character
2200D | invalid_escape_octet
22025 | invalid_escape_sequence
22P06 | nonstandard_use_of_escape_character
22010 | invalid_indicator_parameter_value
22023 | invalid_parameter_value
22013 | invalid_preceding_or_following_size
2201B | invalid_regular_expression
2201W | invalid_row_count_in_limit_clause
2201X | invalid_row_count_in_result_offset_clause
2202H | invalid_tablesample_argument
2202G | invalid_tablesample_repeat
22009 | invalid_time_zone_displacement_value
2200C | invalid_use_of_escape_character
2200G | most_specific_type_mismatch
22004 | null_value_not_allowed
22002 | null_value_no_indicator_parameter
22003 | numeric_value_out_of_range
2200H | sequence_generator_limit_exceeded
22026 | string_data_length_mismatch
22001 | string_data_right_truncation
22011 | substring_error
22027 | trim_error
22024 | unterminated_c_string
2200F | zero_length_character_string
22P01 | floating_point_exception
22P02 | invalid_text_representation
22P03 | invalid_binary_representation
22P04 | bad_copy_file_format
22P05 | untranslatable_character
2200L | not_an_xml_document
2200M | invalid_xml_document
2200N | invalid_xml_content
2200S | invalid_xml_comment
2200T | invalid_xml_processing_instruction
22030 | duplicate_json_object_key_value
22032 | invalid_json_text
22033 | invalid_sql_json_subscript
22034 | more_than_one_sql_json_item
22035 | no_sql_json_item
22036 | non_numeric_sql_json_item
22037 | non_unique_keys_in_a_json_object
22038 | singleton_sql_json_item_required
22039 | sql_json_array_not_found
2203A | sql_json_member_not_found
2203B | sql_json_number_not_found
2203C | sql_json_object_not_found
2203D | too_many_json_array_elements
2203E | too_many_json_object_members
2203F | sql_json_scalar_required
Класс 23 — Нарушение ограничения целостности |
23000 | integrity_constraint_violation
23001 | restrict_violation
23502 | not_null_violation
23503 | foreign_key_violation
23505 | unique_violation
23514 | check_violation
23P01 | exclusion_violation
Класс 24 — Неверное состояние курсора |
24000 | invalid_cursor_state
Класс 25 — Неверное состояние транзакции |
25000 | invalid_transaction_state
25001 | active_sql_transaction
25002 | branch_transaction_already_active
25008 | held_cursor_requires_same_isolation_level
25003 | inappropriate_access_mode_for_branch_transaction
25004 | inappropriate_isolation_level_for_branch_transaction
25005 | no_active_sql_transaction_for_branch_transaction
25006 | read_only_sql_transaction
25007 | schema_and_data_statement_mixing_not_supported
25P01 | no_active_sql_transaction
25P02 | in_failed_sql_transaction
25P03 | idle_in_transaction_session_timeout
Класс 26 — Неверное имя оператора SQL |
26000 | invalid_sql_statement_name
Класс 27 — Нарушение при изменении данных в триггере |
27000 | triggered_data_change_violation
Класс 28 — Неверное указание авторизации |
28000 | invalid_authorization_specification
28P01 | invalid_password
Класс 2B — Зависимые описания прав все еще существуют |
2B000 | dependent_privilege_descriptors_still_exist
2BP01 | dependent_objects_still_exist
Класс 2D — Неверное завершение транзакции |
2D000 | invalid_transaction_termination
Класс 2F — Исключение в подпрограмме SQL |
2F000 | sql_routine_exception
2F005 | function_executed_no_return_statement
2F002 | modifying_sql_data_not_permitted
2F003 | prohibited_sql_statement_attempted
2F004 | reading_sql_data_not_permitted
Класс 34 — Неверное имя курсора |
34000 | invalid_cursor_name
Класс 38 — Исключение во внешней подпрограмме |
38000 | external_routine_exception
38001 | containing_sql_not_permitted
38002 | modifying_sql_data_not_permitted
38003 | prohibited_sql_statement_attempted
38004 | reading_sql_data_not_permitted
Класс 39 — Исключение при вызове внешней подпрограммы |
39000 | external_routine_invocation_exception
39001 | invalid_sqlstate_returned
39004 | null_value_not_allowed
39P01 | trigger_protocol_violated
39P02 | srf_protocol_violated
39P03 | event_trigger_protocol_violated
Класс 3B — Исключение точки сохранения |
3B000 | savepoint_exception
3B001 | invalid_savepoint_specification
Класс 3D — Неверное имя каталога |
3D000 | invalid_catalog_name
Класс 3F — Неверное имя схемы |
3F000 | invalid_schema_name
Класс 40 — Откат транзакции |
40000 | transaction_rollback
40002 | transaction_integrity_constraint_violation
40001 | serialization_failure
40003 | statement_completion_unknown
40P01 | deadlock_detected
Класс 42 — Ошибка синтаксиса или нарушение правила доступа |
42000 | syntax_error_or_access_rule_violation
42601 | syntax_error
42501 | insufficient_privilege
42846 | cannot_coerce
42803 | grouping_error
42P20 | windowing_error
42P19 | invalid_recursion
42830 | invalid_foreign_key
42602 | invalid_name
42622 | name_too_long
42939 | reserved_name
42804 | datatype_mismatch
42P18 | indeterminate_datatype
42P21 | collation_mismatch
42P22 | indeterminate_collation
42809 | wrong_object_type
428C9 | generated_always
42703 | undefined_column
42883 | undefined_function
42P01 | undefined_table
42P02 | undefined_parameter
42704 | undefined_object
42701 | duplicate_column
42P03 | duplicate_cursor
42P04 | duplicate_database
42723 | duplicate_function
42P05 | duplicate_prepared_statement
42P06 | duplicate_schema
42P07 | duplicate_table
42712 | duplicate_alias
42710 | duplicate_object
42702 | ambiguous_column
42725 | ambiguous_function
42P08 | ambiguous_parameter
42P09 | ambiguous_alias
42P10 | invalid_column_reference
42611 | invalid_column_definition
42P11 | invalid_cursor_definition
42P12 | invalid_database_definition
42P13 | invalid_function_definition
42P14 | invalid_prepared_statement_definition
42P15 | invalid_schema_definition
42P16 | invalid_table_definition
42P17 | invalid_object_definition
Класс 44 — Нарушение WITH CHECK OPTION |
44000 | with_check_option_violation
Класс 53 — Нехватка ресурсов |
53000 | insufficient_resources
53100 | disk_full
53200 | out_of_memory
53300 | too_many_connections
53400 | configuration_limit_exceeded
Класс 54 — Превышение ограничения программы |
54000 | program_limit_exceeded
54001 | statement_too_complex
54011 | too_many_columns
54023 | too_many_arguments
Класс 55 — Объект не в требуемом состоянии |
55000 | object_not_in_prerequisite_state
55006 | object_in_use
55P02 | cant_change_runtime_param
55P03 | lock_not_available
55P04 | unsafe_new_enum_value_usage
Класс 57 — Вмешательство оператора |
57000 | operator_intervention
57014 | query_canceled
57P01 | admin_shutdown
57P02 | crash_shutdown
57P03 | cannot_connect_now
57P04 | database_dropped
Класс 58 — Ошибка системы (ошибка, внешняя по отношению к PostgreSQL) |
58000 | system_error
58030 | io_error
58P01 | undefined_file
58P02 | duplicate_file
Класс 72 — Ошибка снимка |
72000 | snapshot_too_old
Класс F0 — Ошибка файла конфигурации |
F0000 | config_file_error
F0001 | lock_file_exists
Класс HV — Ошибка обертки сторонних данных (SQL/MED) |
HV000 | fdw_error
HV005 | fdw_column_name_not_found
HV002 | fdw_dynamic_parameter_value_needed
HV010 | fdw_function_sequence_error
HV021 | fdw_inconsistent_descriptor_information
HV024 | fdw_invalid_attribute_value
HV007 | fdw_invalid_column_name
HV008 | fdw_invalid_column_number
HV004 | fdw_invalid_data_type
HV006 | fdw_invalid_data_type_descriptors
HV091 | fdw_invalid_descriptor_field_identifier
HV00B | fdw_invalid_handle
HV00C | fdw_invalid_option_index
HV00D | fdw_invalid_option_name
HV090 | fdw_invalid_string_length_or_buffer_length
HV00A | fdw_invalid_string_format
HV009 | fdw_invalid_use_of_null_pointer
HV014 | fdw_too_many_handles
HV001 | fdw_out_of_memory
HV00P | fdw_no_schemas
HV00J | fdw_option_name_not_found
HV00K | fdw_reply_handle
HV00Q | fdw_schema_not_found
HV00R | fdw_table_not_found
HV00L | fdw_unable_to_create_execution
HV00M | fdw_unable_to_create_reply
HV00N | fdw_unable_to_establish_connection
Класс P0 — Ошибка PL/pgSQL |
P0000 | plpgsql_error
P0001 | raise_exception
P0002 | no_data_found
P0003 | too_many_rows
P0004 | assert_failure
Класс XX — Внутренняя ошибка |
XX000 | internal_error
XX001 | data_corrupted
XX002 | index_corrupted

Поддержка даты и времени

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

Это приложение включает информацию о содержимом этих справочных таблиц и описывает шаги, используемые синтаксическим анализатором для распознавания даты и времени.

Интерпретация ввода даты и времени

Строки ввода даты/времени распознаются по следующей схеме.

Разбить входную строку на базовые элементы и классифицировать каждый элемент как строку, время, часовой пояс или число.

Если числовой элемент содержит двоеточие (:), то это строка времени. Включаются все последующие цифры и двоеточия.

Если числовой элемент содержит тире (-), косую черту (/) или две или более точек (.), то это строка даты, которая может содержать название месяца. Если элемент даты уже встречался, то он интерпретируется как название часового пояса (например, America/New_York).

Если элемент только числовой, то это либо отдельное поле, либо объединенная дата ISO 8601 (например, 19990113 для 13 января 1999 года) или время (например, 141516 для 14:15:16).

Если элемент начинается с плюса (+) или минуса (-), то это либо числовой часовой пояс, либо специальное поле.

Если элемент представляет собой буквенную строку, сопоставить ее с возможными строками:

Проверить, соответствует ли элемент какой-либо известной аббревиатуре часового пояса. Эти сокращения предоставляются файлом конфигурации, описанным в подразделе Файлы конфигурации даты/времени.

Если соответствие не найдено, проверить по внутренней таблице, не совпадает ли элемент со специальной строкой (например, today), днем недели (например, Thursday), месяцем (например, January) или пропускаемым словом (например, at, on).

Если соответствие все же не найдено, вывести ошибку.

Когда элемент является числом или числовым полем:

Если получено восемь или шесть цифр и если никакие другие поля даты не были ранее прочитаны, интерпретировать их как «объединенную дату» (например, 19990118 или 990118). Интерпретация такой даты — ГГГГММДД или ГГММДД.

Если элемент состоит из трех цифр и год уже был прочитан, интерпретировать его как день года.

Если это четыре или шесть цифр и год уже был прочитан, интерпретировать их как время (ЧЧММ или ЧЧММСС).

Если это три или более цифр и поля даты еще не были найдены, интерпретировать их как год (это приводит к установке порядка гг-мм-дд для оставшихся полей даты).

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

Если был указан год до н. э. (BC), отнять год и добавить единицу для внутреннего хранения. (В григорианском календаре нет нулевого года, поэтому 1 год до н. э. становится нулевым.)

Если год до н. э. не был указан и поле года было двухзначным, исправить запись года на четырехзначную. Если поле меньше 70, добавить 2000, в противном случае добавить 1900.

Совет
Годы с 1 по 99 н. э. по григорианскому календарю можно ввести, используя четырехзначное число с ведущими нулями (например, 0099 — это 99 год н. э.).

Обработка недопустимых или неоднозначных временных меток

Обычно, если строка даты/времени является синтаксически допустимой, но содержит поля со значениями вне диапазона, выдается ошибка. Например, при введении даты 31 февраля строка будет отклонена.

При переходе на летнее время допустимая на первый взгляд строка даты/времени может отображать несуществующую или неоднозначную метку времени. В таких случаях строка не отклоняется; неоднозначность решается путем определения того, какое смещение UTC применить. Например, предположим, что для параметра TimeZone установлено значение America/New_York, и рассмотрим следующий пример:

=> SELECT ’2018-03-11 02:30’::timestamptz;
timestamptz
------------------------
2018-03-11 03:30:00-04
(1 row)

Поскольку в этом часовом поясе в этот день стрелки часов переводились вперед, по гражданскому времени момента 2:30 не существовало; часы перескочили вперед с 2:00 по восточному стандартному времени (EST) на 3:00 по летнему восточному времени (EDT). QHB воспринимает данное время, как если бы оно было стандартным временем (часовой пояс UTC-5), которое затем преобразовалось в 3:30 EDT (или часовой пояс UTC-4).

И наоборот, рассмотрим поведение во время перевода времени назад:

=> SELECT ’2018-11-04 02:30’::timestamptz;
timestamptz
------------------------
2018-11-04 02:30:00-05
(1 row)

В этот день возможны две интерпретации времени 2:30; было 2:30 в часовом поясе EDT, а затем, через час после возврата к стандартному времени, наступило 2:30 в часовом поясе EST. Опять же, QHB воспринимает данное время, как если бы оно было стандартным (часовой пояс UTC-5). Можно форсировать ситуацию, указав часовой пояс летнего времени:

=> SELECT ’2018-11-04 02:30 EDT’::timestamptz;
timestamptz
------------------------
2018-11-04 01:30:00-05
(1 row)

Эту временную метку можно корректно представить как 2:30 в UTC-4 или 1:30 в UTC-5; код вывода метки времени выбирает второй вариант.

Точное правило, применяемое в таких случаях, состоит в том, что недопустимая временная метка, которая предположительно попадает в момент перевода часов вперед на летнее время, привязывается к смещению UTC, действовавшему в этом часовом поясе непосредственно перед переходом, а неоднозначная временная метка, которая может попасть в оба интервала при переводе часов назад, привязывается к смещению UTC, действовавшему сразу после перехода. В большинстве часовых поясов это равносильно утверждению, что «в случае сомнений предпочтительна интерпретация со стандартным временем».

Во любом случае смещение от UTC, связанное с меткой времени, можно задать явно, написав числовое смещение или аббревиатуру часового пояса, соответствующую фиксированному смещению от UTC. Вышеуказанное правило применяется только тогда, когда необходимо рассчитать смещение от UTC для часового пояса, в котором оно меняется.

Ключевые слова даты и времени

В таблице 1 показаны элементы, которые распознаются как названия месяцев.

Таблица 1. Названия месяцев

Месяц | Аббревиатуры
January | Jan
February | Feb
March | Mar
April | Apr
May |
June | Jun
July | Jul
August | Aug
September | Sep, Sept
October | Oct
November | Nov
December | Dec

В таблице 2 показаны элементы, которые распознаются как названия дней недели.

Таблица 2. Названия дней недели

День | Аббревиатуры
Sunday | Sun
Monday | Mon
Tuesday | Tue, Tues
Wednesday | Wed, Weds
Thursday | Thu, Thur, Thurs
Friday | Fri
Saturday | Sat

В таблице 3 показаны элементы, которые выполняют различные функции модификаторов.

Таблица 3. Модификаторы поля даты/времени

Идентификатор | Описание
AM | Время до 12:00
AT | Игнорируется
JULIAN , JD , J | Следующее поле — юлианская дата
ON | Игнорируется
PM | Время 12:00 и более позднее
T | Следующее поле — время

Файлы конфигурации даты/времени

Поскольку аббревиатуры часовых поясов недостаточно стандартизированы, QHB предоставляет средства для самостоятельного определения набора аббревиатур, принимаемых сервером. Параметр времени выполнения timezone_abbreviations определяет активный набор аббревиатур. Хотя этот параметр может быть изменен любым пользователем базы данных, возможные значения для него контролируются администратором базы данных и на самом деле являются именами файлов конфигурации, хранящихся в .../share/timezonesets/ каталога установки. Добавляя или изменяя файлы в этом каталоге, администратор может назначить локальную политику для аббревиатур часовых поясов.

Значение timezone_abbreviations можно установить в любое имя файла в .../share/timezonesets/, если то полностью состоит из букв. (Запрет на использование небуквенных символов в timezone_abbreviations препятствует чтению файлов, находящихся вне заданного каталога, а также чтению резервных файлов редактора и других посторонних файлов.)

Файл аббревиатур часовых поясов может содержать пустые строки и комментарии, начинающиеся с #. Строки без комментариев должны иметь один из следующих форматов:

аббревиатура_пояса смещение
аббревиатура_пояса смещение D
аббревиатура_пояса имя_часового_пояса
@INCLUDE имя_файла
@OVERRIDE

Аббревиатура_пояса просто задает определяемую аббревиатуру. Смещение — это целое число, задающее эквивалентное смещение от UTC в секундах: положительное — к востоку от Гринвича, а отрицательное - к западу. Например, -18000 означало бы пять часов к западу от Гринвича или североамериканское восточное стандартное время. D указывает, что название зоны представляет местное летнее, а не стандартное время.

Как вариант, можно задать имя_часового_пояса, ссылающееся на имя зоны, определенное в базе данных часовых поясов IANA. Определение зоны позволяет узнать, используется или использовалась ли аббревиатура в этой зоне, и если да, то выбирается соответствующее значение, то есть значение, действовавшее в указанный момент времени, или действовавшее непосредственно перед ним, если текущее не было определено, или самое старое значение, если оно начало действовать только после этого момент. Такое поведение важно для работы с аббревиатурами, значение которых менялось в ходе истории. Также разрешается определять аббревиатуру через имя часового пояса, в котором этой аббревиатуры нет; в таком случае использование аббревиатуры равнозначно написанию просо имени часового пояса.

Совет
Простое целочисленное смещение предпочтительнее использовать при определении аббревиатуры, смещение которой от UTC никогда не менялось, поскольку обрабатывать такие аббревиатуры гораздо легче, чем те, что требуют обращения к определению часового пояса.

Синтаксис @INCLUDE позволяет включать другой файл в каталоге .../share/timezonesets/. Включение может быть вложенным до ограниченной глубины.

Синтаксис @OVERRIDE указывает, что последующие записи в файле могут переопределять предыдущие (как правило, это записи, полученные из включенных файлов). Без этого указания конфликтующие определения аббревиатуры одного и того же часового пояса считаются ошибкой.

При установке без изменений файл Default содержит все не конфликтующие аббревиатуры часовых поясов для большей части мира. Дополнительные файлы Australia и India предоставляются для данных регионов: эти файлы сначала включают файл Default, а затем добавляют или изменяют аббревиатуры по мере необходимости.

В справочных целях стандартная установка также содержит файлы Africa.txt, America.txt и т. д., содержащие информацию о каждой используемой, согласно базе данных часовых поясов IANA, аббревиатуре часового пояса. Определения имен часовых поясов, находящихся в этих файлах, можно копировать и вставлять в пользовательский файл конфигурации по мере необходимости. Обратите внимание, что эти файлы нельзя напрямую указывать как значение параметра timezone_abbreviations, поскольку их имена содержат точку.

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

Внимание!
Аббревиатуры часовых поясов, определенные в файле конфигурации, переопределяют не относящиеся к часовым поясам значения, встроенные в QHB. Например, файл конфигурации Australia определяет SAT (для южно-австралийского стандартного времени, South Australian Standard Time). Когда этот файл активен, SAT не будет распознаваться как сокращение слова «суббота».

Внимание!
Если вы изменяете файлы в .../share/timezonesets/, резервное копирование остается на ваше усмотрение — обычный дамп базы данных не будет включать этот каталог.

История единиц измерения времени

Стандарт SQL гласит, что «В определении "литерала даты и времени" "значения даты и времени" ограничены естественными правилами для дат и времени согласно григорианскому календарю». Следуя примеру стандарта SQL, QHB считает даты исключительно в григорианском календаре, в том числе те годы, когда этот календарь не использовался. Это правило известно как пролептический григорианский календарь.

Юлианский календарь был введен Юлием Цезарем в 45 году до н. э. Он широко использовался в западном мире вплоть до 1582 года, когда страны начали переходить на григорианский календарь. В юлианском календаре тропический год равен примерно 365 1/4 дням = 365,25 дням. В результате каждые 128 лет накапливается приблизительно 1 лишний день.

Накапливающаяся календарная погрешность побудила Папу Григория XIII реформировать календарь в соответствии с предписаниями Трентского Собора. В григорианском календаре тропический год равен примерно 365 + 97/400 дням = 365,2425 дням. Таким образом, в григорианском календаре погрешность в один день тропического года накапливается примерно за 3300 лет.

Приблизительное число 365 + 97/400 достигается за счет того, что каждые 97 лет из 400 становятся високосными с помощью следующих правил:

Каждый год, кратный 4, является високосным.
Однако каждый год, кратный 100, не является високосным.
Однако каждый год, кратный 400, все-таки является високосным.

То есть 1700, 1800, 1900, 2100 и 2200 не являются високосными годами. Но 1600, 2000 и 2400 — високосные годы. А вот в старом юлианском календаре високосными являются все годы, кратные 4.

Папская булла, изданная в феврале 1582 года, постановила, что октябрь 1582 года должен быть на 10 дней короче, чтобы 15 октября следовало сразу за 4 октября. Этому предписанию последовали Италия, Польша, Португалия и Испания. Вскоре к ним присоединились и другие католические страны, но протестантские страны вводили эти изменения неохотно, а страны греческой православной церкви не проводили реформу до начала 20-го века. В 1752 году реформа была проведена в Великобритании и ее доминионах (включая территорию нынешних США). Таким образом, за 2 сентября 1752 года последовало 14 сентября 1752 года. Вот почему в системах Unix, имеющих программу cal, выводится следующее:

$ cal 9 1752
September 1752
S M Tu W Th F S
1 2 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

Но, конечно, этот календарь действителен только для Великобритании и доминионов, а в других местах он не действует. Поскольку отслеживание реальных календарей, которые использовались в разных местах в разное время, сопряжено с трудностями и неясностями, QHB вместо этого применяет правила григорианского календаря ко всем датам, хотя этот метод и не является исторически достоверным.

Различные календари были разработаны в разных частях света, многие из которых появились до григорианской системы. Например, появление китайского календаря относится к 14 веку до н. э. Легенда гласит, что император Хуанди изобрел этот календарь в 2637 году до н. э. В Китайской Народной Республике григорианский календарь используется в гражданских целях. Китайский календарь используется для определения праздничных дат.

Юлианские даты

Система юлианских дат — это способ исчисления периодов времени. Он не имеет отношения к юлианскому календарю, хотя схожее название может сбить с толку. Система юлианских дат была изобретена французским ученым Жозефом Юстусом Скалигером (1540-

и, вероятно, получила свое название в честь его отца, итальянского ученого Юлия Цезаря Скалигера (1484-1558).

В системе юлианских дат каждый день имеет порядковый номер, начинающийся с JD 0 (который иногда называют номером юлианской даты). JD 0 соответствует 1 января 4713 г. до н. э. по юлианскому календарю или 24 ноября 4714 г. до н. э. по григорианскому календарю. Исчисление по юлианским датам чаще всего используется астрономами для маркировки своих ночных наблюдений, и поэтому день длится с полудня до полудня UTC, а не с полуночи до полуночи: JD 0 (первый юлианский день) обозначает 24 часа с полудня UTC 24 ноября 4714 г. до н. э. до полудня UTC 25 ноября 4714 г. до н. э.

Хотя QHB поддерживает юлианскую дату для записи входных и выходных дат (а также использует юлианские даты для некоторых внутренних вычислений даты и времени), он не придерживается концепции с отсчетом суток от полудня. QHB считает юлианский день длящимся от полуночи до полуночи, как и обычный день.

Однако при необходимости эта формулировка позволяет получить астрономическое определение, произведя вычисления в часовом поясе UTC+12. Например,

=> SELECT extract(julian from '2021-06-23 7:00:00-04'::timestamptz at time zone 'UTC+12');
     date_part
--------------------
 2459388.9583333335
(1 row)
=> SELECT extract(julian from '2021-06-23 8:00:00-04'::timestamptz at time zone 'UTC+12');
 date_part
-----------
   2459389
(1 row)
=> SELECT extract(julian from date '2021-06-23');
 date_part
-----------
   2459389
(1 row)

Соответствие стандарту SQL

В этом разделе в общих чертах описывается, в какой степени QHB соответствует текущему стандарту SQL. Следующая информация не является полноценной декларацией соответствия, но при этом представляет основные темы достаточно подробно, чтобы быть полезной и целесообразной для пользователей.

Официально стандарт SQL называется ISO/IEC 9075 «Database Language SQL» (Язык баз данных SQL). Время от времени выпускается обновленная версия стандарта; последнее обновление было сделано в 2016 году. Эта версия стандарта получила название ISO/IEC 9075:2016, или просто SQL:2016. Ранее были выпущены версии SQL:2011, SQL:2008, SQL:2006, SQL:2003, SQL:1999 и SQL-92. Каждая версия заменяет предыдущую, поэтому заявления о соответствии более ранним версиям не имеют официальной ценности. Разработка QHB нацелена на соответствие последней официальной версии стандарта, если оно не противоречит традиционной функциональности или здравому смыслу. Поддерживается большая часть функциональности, требуемого стандартом SQL, хотя иногда с немного отличающимся синтаксисом или функциями. Можно ожидать, что со временем степень соответствия стандарту будет постепенно возрастать.

SQL-92 определил три уровня функционального соответствия стандарту: начальный (Entry), промежуточный (Intermediate) и полный (Full). Большинство СУБД, заявлявших о соответствии стандарту SQL, соответствовали ему только на начальном уровне, поскольку общий набор функций на промежуточном и полном уровнях либо был слишком обширным, либо конфликтовал с устаревшими моделями поведения.

Начиная с SQL:1999, вместо трех малоприменимых пространных уровней, вычлененных в SQL-92, в стандарте SQL определено множество отдельных функциональных возможностей. Большое его подмножество представляет собой «Основную» функциональность, которую должны обеспечивать все реализации, совместимые с SQL. Поддержка остальных возможностей не являются обязательной. Некоторые дополнительные возможности сгруппированы вместе, формируя «пакеты», о поддержке которых могут заявлять реализации SQL, тем самым декларируя соответствие определенным группам возможностей.

Версии стандарта, начиная с SQL:2003, также разбиты на несколько частей. Каждая часть имеет краткое название. Обратите внимание, что нумерация этих частей непоследовательная.

ISO/IEC 9075-1 Структура (SQL/Framework)

ISO/IEC 9075-2 Основа (SQL/Foundation)

ISO/IEC 9075-3 Интерфейс уровня вызовов (SQL/CLI)

ISO/IEC 9075-4 Модули постоянного хранения (SQL/PSM)

ISO/IEC 9075-9 Управление внешними данными (SQL/MED)

ISO/IEC 9075-10 Привязки объектных языков (SQL/OLB)

ISO/IEC 9075-11 Схемы информации и определений (SQL/Schemata)

ISO/IEC 9075-13 Программы и типы, использующие язык Java (SQL/JRT)

ISO/IEC 9075-14 Спецификации, связанные с XML (SQL/XML)

Ядро QHB реализует части 1, 2, 9, 11 и 14. Часть 3 реализуется драйвером ODBC, а часть 13 — плагином PL/Java, но точное соответствие этих компонентов стандарту на данный момент не проверено. Части 4 и 10 в QHB в настоящее время не реализованы.

QHB поддерживает почти всю основную функциональность SQL:2016. Из 179 обязательных возможностей, необходимых для полного соответствия «Основной» функциональности, QHB обеспечивает соответствие как минимум для 160. Кроме того, он поддерживает длинный список дополнительных возможностей. Следует отметить, что на момент написания этой документации ни одна из существующих версий СУБД не претендовала на полное соответствие «Основной» функциональности SQL:2016.

В следующих двух разделах мы предоставляем список функциональных возможностей, которые поддерживает QHB, а также список возможностей, определенных в SQL:2016, которые в QHB еще не поддерживаются. Оба списка носят приблизительный характер: возможность, указанная как поддерживаемая, может отличаться от стандарта в незначительных деталях, а неподдерживаемая возможность на самом деле может быть по большей части реализована. Наиболее точная информация о том, что работает, а что нет, содержится в основной документации.

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

Поддерживаемые возможности

Идентификатор | Пакет | Описание | Комментарий
B012 |  | Встроенный C |
B021 |  | Непосредственный SQL |
E011 | Основа | Числовые типы данных |
E011-01 | Основа | Типы данных INTEGER и SMALLINT |
E011-02 | Основа | Типы данных REAL, DOUBLE PRECISION и FLOAT |
E011-03 | Основа | Типы данных DECIMAL и NUMERIC |
E011-04 | Основа | Арифметические операторы |
E011-05 | Основа | Числовое сравнение |
E011-06 | Основа | Неявное приведение среди числовых типов данных |
E021 | Основа | Символьные типы данных |
E021-01 | Основа | Тип данных CHARACTER |
E021-02 | Основа | Тип данных CHARACTER VARYING |
E021-03 | Основа | Символьные литералы |
E021-04 | Основа | Функция CHARACTER_LENGTH | убирает конечные пробелы из значений CHARACTER перед подсчетом
E021-05 | Основа | Функция OCTET_LENGTH |
E021-06 | Основа | Функция SUBSTRING |
E021-07 | Основа | Конкатенация символов |
E021-08 | Основа | Функции UPPER и LOWER |
E021-09 | Основа | Функция TRIM |
E021-10 | Основа | Неявное приведение среди типов символьных строк |
E021-11 | Основа | Функция POSITION |
E021-12 | Основа | Сравнение символов |
E031 | Основа | Идентификаторы |
E031-01 | Основа | Идентификаторы с разделителями |
E031-02 | Основа | Идентификаторы в нижнем регистре |
E031-03 | Основа | Завершающее подчеркивание |
E051 | Основа | Базовая спецификация запросов |
E051-01 | Основа | SELECT DISTINCT |
E051-02 | Основа | Предложение GROUP BY |
E051-04 | Основа | GROUP BY может содержать столбцы не из <списка выборки> |
E051-05 | Основа | Элементы списка выборки можно переименовывать |
E051-06 | Основа | Предложение HAVING |
E051-07 | Основа | Уточняющий символ * в списке выборки |
E051-08 | Основа | Корреляционные имена в предложении FROM |
E051-09 | Основа | Переименование столбцов в предложении FROM |
E061 | Основа | Базовые предикаты и условия поиска |
E061-01 | Основа | Предикат сравнения |
E061-02 | Основа | Предикат BETWEEN |
E061-03 | Основа | Предикат IN со списком значений |
E061-04 | Основа | Предикат LIKE |
E061-05 | Основа | Предложение ESCAPE в предикате LIKE |
E061-06 | Основа | Предикат NULL |
E061-07 | Основа | Предикаты количественного сравнения |
E061-08 | Основа | Предикат EXISTS |
E061-09 | Основа | Подзапросы в предикате сравнения |
E061-11 | Основа | Подзапросы в предикате IN |
E061-12 | Основа | Подзапросы в предикате количественного сравнения |
E061-13 | Основа | Коррелирующие подзапросы |
E061-14 | Основа | Условие поиска |
E071 | Основа | Базовые выражения запросов |
E071-01 | Основа | Табличный оператор UNION DISTINCT |
E071-02 | Основа | Табличный оператор UNION ALL |
E071-03 | Основа | Табличный оператор EXCEPT DISTINCT |
E071-05 | Основа | Столбцам, объединенным с помощью табличных операторов, необязательно иметь одинаковый тип данных. |
E071-06 | Основа | Табличные операторы в подзапросах |
E081 | Основа | Основные права доступа |
E081-01 | Основа | Право на SELECT |
E081-02 | Основа | Право на DELETE |
E081-03 | Основа | Право на INSERT на уровне таблицы |
E081-04 | Основа | Право на UPDATE на уровне таблицы |
E081-05 | Основа | Право на UPDATE на уровне столбца |
E081-06 | Основа | Право REFERENCES на уровне таблицы |
E081-07 | Основа | Право REFERENCES на уровне столбца |
E081-08 | Основа | Предложение WITH GRANT OPTION |
E081-09 | Основа | Право USAGE |
E081-10 | Основа | Право на EXECUTE |
E091 | Основа | Функции множеств |
E091-01 | Основа | AVG |
E091-02 | Основа | COUNT |
E091-03 | Основа | MAX |
E091-04 | Основа | MIN |
E091-05 | Основа | SUM |
E091-06 | Основа | Квантификатор ALL |
E091-07 | Основа | Квантификатор DISTINCT |
E101 | Основа | Базовые манипуляции с данными |
E101-01 | Основа | Оператор INSERT |
E101-03 | Основа | Оператор UPDATE с критерием отбора |
E101-04 | Основа | Оператор DELETE с критерием отбора |
E111 | Основа | Оператор SELECT, возвращающий одну строку |
E121 | Основа | Базовая поддержка курсоров |
E121-01 | Основа | DECLARE CURSOR |
E121-02 | Основа | Столбцы ORDER BY, которым необязательно быть в списке выборки |
E121-03 | Основа | Выражения значений в предложении ORDER BY |
E121-04 | Основа | Оператор OPEN |
E121-06 | Основа | Оператор UPDATE с позиционированием |
E121-07 | Основа | Оператор DELETE с позиционированием |
E121-08 | Основа | Оператор CLOSE |
E121-10 | Основа | Оператор FETCH с неявным NEXT |
E121-17 | Основа | Курсоры WITH HOLD |
E131 | Основа | Поддержка значения NULL (NULL вместо значений) |
E141 | Основа | Основные ограничения целостности |
E141-01 | Основа | Ограничения NOT NULL |
E141-02 | Основа | Ограничения UNIQUE столбцов NOT NULL |
E141-03 | Основа | Ограничения PRIMARY KEY |
E141-04 | Основа | Основное ограничение FOREIGN KEY со значением по умолчанию NO ACTION (без действия) как для операции удаления со ссылками, так и для операций изменения со ссылками |
E141-06 | Основа | Ограничения CHECK |
E141-07 | Основа | Значения столбцов по умолчанию |
E141-08 | Основа | NOT NULL распространяется на PRIMARY KEY |
E141-10 | Основа | Имена во внешнем ключе можно указывать в любом порядке |
E151 | Основа | Поддержка транзакций |
E151-01 | Основа | Оператор COMMIT |
E151-02 | Основа | Оператор ROLLBACK |
E152 | Основа | Базовый оператор SET TRANSACTION |
E152-01 | Основа | Оператор SET TRANSACTION: предложение ISOLATION LEVEL SERIALIZABLE |
E152-02 | Основа | Оператор SET TRANSACTION: предложения READ ONLY и READ WRITE |
E153 | Основа | Обновляемые запросы с подзапросами |
E161 | Основа | Комментарии SQL, начинающиеся с двух минусов |
E171 | Основа | Поддержка SQLSTATE |
F021 | Основа | Базовая информационная схема |
F021-01 | Основа | Представление COLUMNS |
F021-02 | Основа | Представление TABLES |
F021-03 | Основа | Представление VIEWS |
F021-04 | Основа | Представление TABLE_CONSTRAINTS |
F021-05 | Основа | Представление REFERENTIAL_CONSTRAINTS |
F021-06 | Основа | Представление CHECK_CONSTRAINTS |
F031 | Основа | Базовые манипуляции со схемой |
F031-01 | Основа | Оператор CREATE TABLE для создания хранимых базовых таблиц |
F031-02 | Основа | Оператор CREATE VIEW |
F031-03 | Основа | Оператор GRANT |
F031-04 | Основа | Оператор ALTER TABLE: предложение ADD COLUMN |
F031-13 | Основа | Оператор DROP TABLE: предложение RESTRICT |
F031-16 | Основа | Оператор DROP VIEW: предложение RESTRICT |
F031-19 | Основа | Оператор REVOKE: предложение RESTRICT |
F032 |  | Каскадное удаление (CASCADE) |
F033 |  | Оператор ALTER TABLE: предложение DROP COLUMN |
F034 |  | Расширенный оператор REVOKE |
F034-01 |  | Оператор REVOKE, выполняемый не только владельцем объекта схемы |
F034-02 |  | Оператор REVOKE: предложение GRANT OPTION FOR |
F034-03 |  | Оператор REVOKE для отзыва права, данного субъекту с указанием WITH GRANT OPTION |
F041 | Основа | Базовое соединение таблиц |
F041-01 | Основа | Внутреннее соединение (но не обязательно с ключевым словом INNER) |
F041-02 | Основа | Ключевое слово INNER |
F041-03 | Основа | LEFT OUTER JOIN |
F041-04 | Основа | RIGHT OUTER JOIN |
F041-05 | Основа | Внешние соединения могут быть вложенными |
F041-07 | Основа | Внутренняя таблица с левой или правой стороны внешнего соединения также может использоваться во внутреннем соединении |
F041-08 | Основа | Поддерживаются все операторы сравнения (а не только =) |
F051 | Основа | Базовая поддержка даты и времени |
F051-01 | Основа | Тип данных DATE (включая поддержку литерала DATE) |
F051-02 | Основа | Тип данных TIME (включая поддержку литерала TIME) с точностью до доли секунды как минимум с 0 знаков после запятой |
F051-03 | Основа | Тип данных TIMESTAMP (включая поддержку литерала TIMESTAMP) с точностью до доли секунды как минимум с 0 и 6 знаками после запятой |
F051-04 | Основа | Предикат сравнения для типов данных DATE, TIME и TIMESTAMP |
F051-05 | Основа | Явное приведение (CAST) между типами даты/времени и типами символьных строк |
F051-06 | Основа | CURRENT_DATE |
F051-07 | Основа | LOCALTIME |
F051-08 | Основа | LOCALTIMESTAMP |
F052 | Расширенные средства работы с датами/временем | Арифметика с интервалами и датами/временем |
F053 |  | Предикат OVERLAPS |
F081 | Основа | UNION и EXCEPT в представлениях |
F111 |  | Уровни изоляции, отличные от SERIALIZABLE |
F111-01 |  | Уровень изоляции READ UNCOMMITTED |
F111-02 |  | Уровень изоляции READ COMMITTED |
F111-03 |  | Уровень изоляции REPEATABLE READ |
F131 | Основа | Операции группировки |
F131-01 | Основа | Предложения WHERE, GROUP BY и HAVING, поддерживаемые в запросах со сгруппированными представлениями |
F131-02 | Основа | Поддержка нескольких таблиц в запросах со сгруппированными представлениями |
F131-03 | Основа | Поддержка функций множеств в запросах со сгруппированными представлениями |
F131-04 | Основа | Подзапросы с предложениями GROUP BY и HAVING и сгруппированными представлениями |
F131-05 | Основа | SELECT, возвращающий одну строку, с предложениями GROUP BY и HAVING и сгруппированными представлениями |
F171 |  | Несколько схем для одного пользователя |
F191 | Расширенное управление целостностью | Действия по удалению со ссылками |
F200 |  | Оператор TRUNCATE TABLE |
F201 | Основа | Функция CAST |
F202 |  | TRUNCATE TABLE: возможность перезапуска столбца идентификаторов |
F221 | Основа | Явные значения по умолчанию |
F222 |  | Оператор INSERT: предложение DEFAULT VALUES |
F231 |  | Таблицы прав |
F231-01 |  | Представление TABLE_PRIVILEGES |
F231-02 |  | Представление COLUMN_PRIVILEGES |
F231-03 |  | Представление USAGE_PRIVILEGES |
F251 |  | Поддержка доменов |
F261 | Основа | Выражение CASE |
F261-01 | Основа | Простой оператор CASE |
F261-02 | Основа | Оператор CASE с критериями отбора |
F261-03 | Основа | NULLIF |
F261-04 | Основа | COALESCE |
F262 |  | Расширенное выражение CASE |
F271 |  | Составные символьные литералы |
F281 |  | Улучшенный оператор LIKE |
F302 |  | Табличный оператор INTERSECT |
F302-01 |  | Табличный оператор INTERSECT DISTINCT |
F302-02 |  | Табличный оператор INTERSECT ALL |
F304 |  | Табличный оператор EXCEPT ALL |
F311-01 | Основа | CREATE SCHEMA |
F311-02 | Основа | CREATE TABLE для хранимых базовых таблиц |
F311-03 | Основа | CREATE VIEW |
F311-04 | Основа | CREATE VIEW: WITH CHECK OPTION |
F311-05 | Основа | Оператор GRANT |
F321 |  | Авторизация пользователей |
F361 |  | Поддержка подпрограмм |
F381 |  | Расширенные манипуляции со схемой |
F381-01 |  | Оператор ALTER TABLE: предложение ALTER COLUMN |
F381-02 |  | Оператор ALTER TABLE: предложение ADD CONSTRAINT |
F381-03 |  | Оператор ALTER TABLE: предложение DROP CONSTRAINT |
F382 |  | Изменение типа данных столбца |
F383 |  | Предложение, устанавливающее NOT NULL для столбца |
F384 |  | Предложение, удаляющее свойство идентификаторов |
F386 |  | Предложение, устанавливающее генерирование значений столбца идентификаторов |
F391 |  | Длинные идентификаторы |
F392 |  | Управляющие символы Unicode в идентификаторах |
F393 |  | Управляющие символы Unicode в литералах |
F401 |  | Расширенное соединение таблиц |
F401-01 |  | NATURAL JOIN |
F401-02 |  | FULL OUTER JOIN |
F401-04 |  | CROSS JOIN |
F402 |  | Соединения по именам столбцов для больших объектов, массивов и мультимножеств |
F411 | Расширенные средства работы с датами/временем | Указание часового пояса | отличия в интерпретации литералов
F421 |  | Национальные символы |
F431 |  | Прокручиваемые курсоры только для чтения |
F431-01 |  | FETCH с явным NEXT |
F431-02 |  | FETCH FIRST |
F431-03 |  | FETCH LAST |
F431-04 |  | FETCH PRIOR |
F431-05 |  | FETCH ABSOLUTE |
F431-06 |  | FETCH RELATIVE |
F441 |  | Расширенная поддержка функций множеств |
F442 |  | Смешанные ссылки на столбцы в функциях множеств |
F471 | Основа | Скалярные значения подзапросов |
F481 | Основа | Расширенный предикат NULL |
F491 | Расширенное управление целостностью | Управление ограничениями |
F501 | Основа | Представления возможностей и соответствия |
F501-01 | Основа | Представление SQL_FEATURES |
F501-02 | Основа | Представление SQL_SIZING |
F501-03 | Основа | Представление SQL_LANGUAGES |
F502 |  | Таблицы расширенной документации |
F502-01 |  | Представление SQL_SIZING_PROFILES |
F502-02 |  | Представление SQL_IMPLEMENTATION_INFO |
F502-03 |  | Представление SQL_PACKAGES |
F531 |  | Временные таблицы |
F555 | Расширенные средства работы с датами/временем | Дополнительная точность в секундах |
F561 |  | Полные выражения значений |
F571 |  | Проверки значений истинности |
F591 |  | Производные таблицы |
F611 |  | Типы данных для индикаторов |
F641 |  | Конструкторы строк и таблиц |
F651 |  | Спецификаторы имен каталогов |
F661 |  | Простые таблицы |
F672 |  | Ретроспективные проверочные ограничения |
F690 |  | Поддержка правил сортировки | но без поддержки наборов символов
F692 |  | Расширенная поддержка правил сортировки |
F701 | Расширенное управление целостностью | Действия при обновлении со ссылками |
F711 |  | ALTER для домена |
F731 |  | Права на INSERT для столбцов |
F751 |  | Усовершенствования CHECK для представлений |
F761 |  | Управление сеансами |
F762 |  | *CURRENT_CAT |
F763 |  | CURRENT_SCHEMA |
F771 |  | Управление соединением |
F781 |  | Самоссылающиеся операции |
F791 |  | Нечувствительные курсоры |
F801 |  | Полные функции множеств |
F850 |  | <Предложение order by> на верхнем уровне в <выражении запроса> |
F851 |  | <Предложение order by> в подзапросах |
F852 |  | <Предложение order by> на верхнем уровне в представлениях |
F855 |  | Вложенное <предложение order by> в <выражении запроса> |
F856 |  | Вложенное <предложение fetch first> в <предложении запроса> |
F857 |  | <Предложение fetch first> на верхнем уровне в <выражении запроса> |
F858 |  | <Предложение fetch first> в подзапросах |
F859 |  | <Предложение fetch first> на верхнем уровне в представлениях |
F860 |  | <Указание числа строк> в <предложении fetch first> |
F861 |  | <Предложение offset для результата> на верхнем уровне в <выражении запроса> |
F862 |  | <Предложение offset для результата> в подзапросах |
F863 |  | Вложенное <предложение offset для результата> в <выражении запроса> |
F864 |  | <Предложение offset для результата> на верхнем уровне в представлениях |
F865 |  | <Указание числа строк> с <предложением offset для результата> |
S071 | Расширенная поддержка объектов | Пути SQL при разрешении имен функций и типов |
S092 |  | Массивы пользовательских типов |
S095 |  | Конструкторы массива из запроса |
S096 |  | Необязательное указание границ массива |
S098 |  | ARRAY_AGG |
S111 | Расширенная поддержка объектов | ONLY в выражениях запросов |
S201 |  | Вызываемые из SQL подпрограммы для массивов |
S201-01 |  | Параметры-массивы |
S201-02 |  | Массивы в качестве типа результата функций |
S211 | Расширенная поддержка объектов | Пользовательские функции приведений |
S301 |  | Расширенный UNNEST |
T031 |  | Тип данных BOOLEAN |
T071 |  | Тип данных BIGINT |
T121 |  | WITH (без RECURSIVE) в выражении запроса |
T122 |  | WITH (без RECURSIVE) в подзапросе |
T131 |  | Рекурсивный запрос |
T132 |  | Рекурсивный запрос в подзапросе |
T141 |  | Предикат SIMILAR |
T151 |  | Предикат DISTINCT |
T152 |  | Предикат DISTINCT с отрицанием |
T171 |  | Предложение LIKE в определении таблицы |
T172 |  | Предложение подзапроса AS в определении таблицы |
T173 |  | Расширенное предложение LIKE в определении таблицы |
T174 |  | Столбцы идентификаторов |
T177 |  | Поддержка генератора последовательностей: возможность простого перезапуска |
T178 |  | Столбцы идентификаторов: возможность простого перезапуска |
T191 | Расширенное управление целостностью | Действие RESTRICT при нарушении ссылок |
T201 | Расширенное управление целостностью | Сравнимые типы данных для ссылочных ограничений |
T211-01 | Активная база данных, расширенное управление целостностью | Триггеры, активируемые при UPDATE, INSERT или DELETE в одной базовой таблице |
T211-02 | Активная база данных, расширенное управление целостностью | Триггеры BEFORE |
T211-03 | Активная база данных, расширенное управление целостностью | Триггеры AFTER |
T211-04 | Активная база данных, расширенное управление целостностью | Триггеры FOR EACH ROW |
T211-05 | Активная база данных, расширенное управление целостностью | Возможность задать условие поиска, которое должно быть истинным перед вызовом триггера |
T211-07 | Активная база данных, расширенное управление целостностью | Право TRIGGER |
T212 | Расширенное управление целостностью | Расширенные возможности триггеров |
T213 |  | Триггеры INSTEAD OF |
T231 |  | Чувствительные курсоры |
T241 |  | Оператор START TRANSACTION |
T261 |  | Цепочечные транзакции |
T271 |  | Точки сохранения |
T281 |  | Право SELECT на уровне столбцов |
T285 |  | Расширенные имена производных столбцов |
T312 |  | Функция OVERLAY |
T321-01 | Основа | Пользовательские функции без перегрузки |
T321-02 | Основа | Пользовательские хранимые процедуры без перегрузки |
T321-03 | Основа | Вызов функций |
T321-04 | Основа | Оператор CALL |
T321-06 | Основа | Представление ROUTINES |
T321-07 | Основа | Представление PARAMETERS |
T323 |  | Явная настройка безопасности внешних подпрограмм |
T325 |  | Уточненные ссылки на параметры SQL |
T331 |  | Базовые роли |
T341 |  | Перегрузка вызываемых из SQL функций и процедур |
T351 |  | Скобочные комментарии SQL (комментарии /*...*/) |
T431 | OLAP | Расширенные возможности группирования |
T432 |  | Вложения и конкатенация GROUPING SETS |
T433 |  | Многоаргументная функция GROUPING |
T441 |  | Функции ABS и MOD |
T461 |  | Симметричный предикат BETWEEN |
T491 |  | Производная таблица LATERAL |
T501 |  | Расширенный предикат EXISTS |
T521 |  | Именованные аргументы в операторе CALL |
T523 |  | Значения по умолчанию для параметров INOUT процедур, вызываемых из SQL |
T524 |  | Именованные аргументы при вызовах подпрограмм без применения оператора CALL |
T525 |  | Значения по умолчанию для параметров функций, вызываемых из SQL |
T551 |  | Необязательные ключевые слова для синтаксиса по умолчанию |
T581 |  | Функция подстроки регулярного выражения |
T591 |  | Ограничения UNIQUE для столбцов, принимающих NULL |
T611 | OLAP | Элементарные операции OLAP |
T613 |  | Выборка |
T614 |  | Функция NTILE |
T615 |  | Функции LEAD и LAG |
T617 |  | Функции FIRST_VALUE и LAST_VALUE |
T620 |  | Предложение WINDOW: параметр GROUPS |
T621 |  | Расширенные числовые функции |
T622 |  | Тригонометрические функции |
T623 |  | Общие логарифмические функции |
T624 |  | Стандартные логарифмические функции |
T631 | Основа | Предикат IN с одним элементом списка |
T651 |  | Операторы SQL-схемы в подпрограммах SQL |
T655 |  | Циклически зависимые подпрограммы |
T831 |  | Язык путей SQL/JSON: строгий режим |
T833 |  | Язык путей SQL/JSON: множественные индексы |
T834 |  | Язык путей SQL/JSON: метод доступа к членам по символу подстановки (*) |
T835 |  | Язык путей SQL/JSON: выражения фильтров |
T836 |  | Язык путей SQL/JSON: предикат «начинается с» |
T837 |  | Язык путей SQL/JSON: предикат regex_like |
X010 |  | Тип XML |
X011 |  | Массивы типа XML |
X014 |  | Атрибуты типа XML |
X016 |  | Хранимые значения XML |
X020 |  | XMLConcat |
X031 |  | XMLElement |
X032 |  | XMLForest |
X034 |  | XMLAgg |
X035 |  | XMLAgg: параметр ORDER BY |
X036 |  | XMLComment |
X037 |  | XMLPI |
X040 |  | Базовое отображение таблиц |
X041 |  | Базовое отображение таблиц: отсутствуют значения NULL |
X042 |  | Базовое отображение таблиц: NULL в виде nil |
X043 |  | Базовое отображение таблиц: таблица в виде леса элементов |
X044 |  | Базовое отображение таблицы: таблица в виде элемента |
X045 |  | Базовое отображение таблиц: с целевым пространством имен |
X046 |  | Базовое отображение таблиц: отображение данных |
X047 |  | Базовое отображение таблиц: отображение метаданных |
X048 |  | Базовое отображение таблиц: кодирование двоичных строк в base64 |
X049 |  | Базовое отображение таблиц: кодирование двоичных строк в шестнадцатеричном виде |
X050 |  | Расширенное отображение таблиц |
X051 |  | Расширенное отображение таблиц: отсутствуют значения NULL |
X052 |  | Расширенное отображение таблицы: NULL в виде nil |
X053 |  | Расширенное отображение таблиц: таблица в виде леса элементов |
X054 |  | Расширенное отображение таблицы: таблица в виде элемента |
X055 |  | Расширенное отображение таблиц: с целевым пространством имен |
X056 |  | Расширенное отображение таблиц: отображение данных |
X057 |  | Расширенное отображение таблиц: отображение метаданных |
X058 |  | Расширенное отображение таблиц: кодирование двоичных строк в base64 |
X059 |  | Расширенное отображение таблиц: кодирование двоичных строк в шестнадцатеричном виде |
X060 |  | XMLParse: ввод символьных строк и параметр CONTENT |
X061 |  | XMLParse: ввод символьных строк и параметр DOCUMENT |
X070 |  | XMLSerialize: сериализация символьных строк и параметр CONTENT |
X071 |  | XMLSerialize: сериализация символьных строк и параметр DOCUMENT |
X072 |  | XMLSerialize: сериализация символьных строк |
X090 |  | Предикат XML-документа |
X120 |  | Параметры XML в подпрограммах SQL |
X121 |  | Параметры XML во внешних подпрограммах |
X221 |  | Механизм передачи XML BY VALUE |
X301 |  | XMLTable: выбор списка производных столбцов |
X302 |  | XMLTable: выбор столбца нумерации |
X303 |  | XMLTable: выбор столбца по умолчанию |
X304 |  | XMLTable: передача элемента контекста | требуется XML DOCUMENT
X400 |  | Сопоставление имени и идентификаторов |
X410 |  | Изменение типа данных столбца: поддержка типа XML |

Неподдерживаемые возможности

Следующие возможности, определенные в SQL:2016, не реализованы в этом выпуске QHB. В некоторых случаях доступна равнозначная им функциональность.

Идентификатор | Пакет | Описание | Комментарий
B011 |  | Встроенный язык Ada |
B013 |  | Встроенный язык COBOL |
B014 |  | Встроенный язык Fortran |
B015 |  | Встроенный язык MUMPS |
B016 |  | Встроенный язык Pascal |
B017 |  | Встроенный язык PL/I |
B031 |  | Базовый динамический SQL |
B032 |  | Расширенный динамический SQL |
B032-01 |  | <Оператор describe input> |
B033 |  | Нетипизированные аргументы функции, вызываемой из SQL |
B034 |  | Динамическое указание атрибутов курсора |
B035 |  | Нерасширенные имена дескрипторов |
B041 |  | Расширения встроенных объявлений исключений SQL |
B051 |  | Расширенные права для выполнения |
B111 |  | Язык модулей — Ada |
B112 |  | Язык модулей — C |
B113 |  | Язык модулей — COBOL |
B114 |  | Язык модулей — Fortran |
B115 |  | Язык модулей — MUMPS |
B116 |  | Язык модулей — Pascal |
B117 |  | Язык модулей — PL/I |
B121 |  | Язык подпрограмм — Ada |
B122 |  | Язык подпрограмм — C |
B123 |  | Язык подпрограмм — COBOL |
B124 |  | Язык подпрограмм — Fortran |
B125 |  | Язык подпрограмм — MUMPS |
B126 |  | Язык подпрограмм — Pascal |
B127 |  | Язык подпрограмм — PL/I |
B128 |  | Язык подпрограмм — SQL |
B200 |  | Полиморфные табличные функции (PTF) |
B201 |  | Более одного обобщенного табличного параметра PTF |
B202 |  | Соразделение PTF |
B203 |  | Более одного указания соразделения |
B204 |  | PRUNE WHEN EMPTY |
B205 |  | Сквозное использование столбцов |
B206 |  | Параметры дескриптора PTF |
B207 |  | Векторное произведение партиционирований |
B208 |  | Интерфейс компонентных процедур PTF |
B209 |  | Расширенные имена PTF |
B211 |  | Язык модулей — Ada: поддержка VARCHAR и NUMERIC |
B221 |  | Язык подпрограмм — Ada: поддержка VARCHAR и NUMERIC |
E182 | Основа | Язык модулей |
F054 |  | TIMESTAMP в списке приоритетов типа DATE |
F121 |  | Базовое управление диагностикой |
F121-01 |  | Оператор GET DIAGNOSTICS |
F121-02 |  | Оператор SET TRANSACTION: предложение DIAGNOSTICS SIZE |
F122 |  | Расширенное управление диагностикой |
F123 |  | Вся диагностика |
F181 | Основа | Поддержка множества модулей |
F263 |  | Разделенные запятыми предикаты в простом выражении CASE |
F291 |  | Предикат UNIQUE |
F301 |  | CORRESPONDING в выражениях запросов |
F311 | Основа | Оператор определения схемы |
F312 |  | Оператор MERGE | возможная альтернатива — INSERT ... ON CONFLICT DO UPDATE
F313 |  | Расширенный оператор MERGE |
F314 |  | Оператор MERGE с ветвью DELETE |
F341 |  | Таблицы использования | без таблиц ROUTINE_*_USAGE
F385 |  | Предложение удаления выражения, генерирующего значения столбца |
F394 |  | Необязательное указание нормальной формы |
F403 |  | Партиционированные соединенные таблицы |
F404 |  | Переменная диапазона для имен общих столбцов |
F451 |  | Определение набора символов |
F461 |  | Именованные наборы символов |
F492 |  | Необязательное указание соблюдения ограничений таблицы |
F521 | Расширенное управление целостностью | Утверждения |
F671 | Расширенное управление целостностью | Подзапросы в CHECK | намеренно опущен
F673 |  | Вызовы подпрограммы, читающей SQL-данные, в условиях CHECK |
F693 |  | Порядок сопоставления символов для SQL-сеансов и клиентских модулей |
F695 |  | Поддержка перекодировки |
F696 |  | Дополнительная документация по перекодировке |
F721 |  | Откладываемые ограничения | только сторонние и уникальные ключи
F741 |  | Типы ссылочных совпадений MATCH | пока без частичного совпадения
F812 | Основа | Базовое флагирование |
F813 |  | Расширенное флагирование |
F821 |  | Ссылки на локальные таблицы |
F831 |  | Полное изменение курсора |
F831-01 |  | Изменяемые прокручиваемые курсоры |
F831-02 |  | Обновляемые упорядоченные курсоры |
F841 |  | Предикат LIKE_REGEX |
F842 |  | Функция OCCURRENCES_REGEX |
F843 |  | Функция POSITION_REGEX |
F844 |  | Функция SUBSTRING_REGEX |
F845 |  | Функция TRANSLATE_REGEX |
F846 |  | Поддержка октетов в операторах регулярных выражений |
F847 |  | Неконстантные регулярные выражения |
F866 |  | Предложение FETCH FIRST: параметр PERCENT |
F867 |  | Предложение FETCH FIRST: опция WITH TIES |
R010 |  | Распознавание шаблона строк: предложение FROM |
R020 |  | Распознавание шаблона строк: предложение WINDOW |
R030 |  | Распознавание шаблона строк: полная поддержка агрегатов |
S011 | Основа | Различные типы данных |
S011-01 | Основа | Представление USER_DEFINED_TYPES |
S023 | Базовая поддержка объектов | Базовые структурированные типы |
S024 | Расширенная поддержка объектов | Расширенные структурированные типы |
S025 |  | Окончательные структурированные типы |
S026 |  | Самоссылающиеся структурированные типы |
S027 |  | Создание метода по заданному имени метода |
S028 |  | Допускающий перестановку список параметров UDT |
S041 | Базовая поддержка объектов | Базовые ссылочные типы |
S043 | Расширенная поддержка объектов | Расширенные ссылочные типы |
S051 | Базовая поддержка объектов | Создание таблицы из типа | частично поддерживается
S081 | Расширенная поддержка объектов | Подтаблицы |
S091 |  | Базовая поддержка массивов | частично поддерживается
S091-01 |  | Массивы встроенных типов данных |
S091-02 |  | Массивы отдельных типов |
S091-03 |  | Выражения с массивами |
S094 |  | Массивы ссылочных типов |
S097 |  | Присвоение значения элементу массива |
S151 | Базовая поддержка объектов | Предикат типа |
S161 | Расширенная поддержка объектов | Приведение подтипов |
S162 |  | Приведение подтипов для ссылочных типов |
S202 |  | Вызываемые из SQL подпрограммы для мультимножеств |
S231 | Расширенная поддержка объектов | Указатели на структурные типы |
S232 |  | Указатели на массивы |
S233 |  | Указатели на мультимножества |
S241 |  | Функции преобразований |
S242 |  | Оператор изменения преобразования |
S251 |  | Упорядочивания, определяемые пользователем |
S261 |  | Метод SPECIFICTYPE |
S271 |  | Базовая поддержка мультимножеств |
S272 |  | Мультимножества пользовательских типов |
S274 |  | Мультимножества ссылочных типов |
S275 |  | Расширенная поддержка мультимножеств |
S281 |  | Типы вложенных коллекций |
S291 |  | Ограничение уникальности для всей строки |
S401 |  | Различные типы на основе типов массивов |
S402 |  | Различные типы на основе отдельных типов |
S403 |  | ARRAY_MAX_CARDINALITY |
S404 |  | TRIM_ARRAY |
T011 |  | Метка времени в информационной схеме |
T021 |  | Типы данных BINARY и VARBINARY |
T022 |  | Расширенная поддержка типов данных BINARY и VARBINARY |
T023 |  | Составные двоичные литералы |
T024 |  | Пробелы в двоичных литералах |
T041 | Базовая поддержка объектов | Базовая поддержка типов данных LOB |
T041-01 | Базовая поддержка объектов | Тип данных BLOB |
T041-02 | Базовая поддержка объектов | Тип данных CLOB |
T041-03 | Базовая поддержка объектов | Функции POSITION, LENGTH, LOWER, TRIM, UPPER и SUBSTRING для типов данных LOB |
T041-04 | Базовая поддержка объектов | Конкатенация типов данных LOB |
T041-05 | Базовая поддержка объектов | Указатель на LOB: неудерживаемый |
T042 |  | Расширенная поддержка типов данных LOB |
T043 |  | Множитель T |
T044 |  | Множитель P |
T051 |  | Типы строк |
T053 |  | Явные псевдонимы для ссылки на все поля |
T061 |  | Поддержка UCS |
T076 |  | Тип данных DECFLOAT |
T101 |  | Расширенное определение допустимости значений NULL |
T111 |  | Изменяемые соединения, объединения и столбцы |
T175 |  | Генерируемые столбцы |
T176 |  | Поддержка генератора последовательностей |
T180 |  | Системно версионируемые таблицы |
T181 |  | Таблицы с периодом времени применения |
T211 | Активная база данных, расширенное управление целостностью | Базовые возможности триггеров |
T211-06 | Активная база данных, расширенное управление целостностью | Поддержка правил времени выполнения для взаимодействия триггеров и ограничений |
T211-08 | Активная база данных, расширенное управление целостностью | Несколько триггеров для одного события выполняются в том порядке, в каком они были созданы в каталоге | намеренно опущено
T251 |  | Оператор SET TRANSACTION: параметр LOCAL |
T272 |  | Расширенное управление точками сохранения |
T301 |  | Функциональные зависимости | частично поддерживается
T321 | Основа | Базовые подпрограммы, вызываемые из SQL |
T321-05 | Основа | Оператор RETURN |
T322 | PSM | Объявляемые атрибуты типа данных |
T324 |  | Явная настройка безопасности подпрограмм SQL |
T326 |  | Табличные функции |
T332 |  | Расширенные роли | в основном поддерживается
T434 |  | GROUP BY DISTINCT |
T471 |  | Наборы результатов в качестве возвращаемого значения |
T472 |  | DESCRIBE CURSOR |
T495 |  | Совмещенное изменение и извлечение данных | другой синтаксис
T502 |  | Предикаты периодов |
T511 |  | Счетчики транзакций |
T522 |  | Значения по умолчанию для параметров IN процедур, вызываемых из SQL | поддерживаются, за исключением ключевого слова DEFAULT в вызове
T561 |  | Удерживаемые указатели |
T571 |  | Возвращающие массивы внешние функции, вызываемые из SQL |
T572 |  | Возвращающие мультимножества внешние функции, вызываемые из SQL |
T601 |  | Ссылки на локальные курсоры |
T612 |  | Расширенные операции OLAP | некоторые формы поддерживаются
T616 |  | Варианты обработки NULL для функций LEAD и LAG |
T618 |  | Функция NTH_VALUE | функция существует, но некоторые возможности отсутствуют
T619 |  | Вложенные оконные функции |
T625 |  | LISTAGG |
T641 |  | Присвоение значения нескольким столбцам | поддерживаются только некоторые варианты синтаксиса
T652 |  | Операторы динамического SQL в подпрограммах SQL |
T653 |  | Операторы модификации схемы SQL во внешних подпрограммах |
T654 |  | Операторы динамического SQL во внешних подпрограммах |
T811 |  | Базовые функции-конструкторы SQL/JSON |
T812 |  | SQL/JSON: JSON_OBJECTAGG |
T813 |  | SQL/JSON: JSON_ARRAYAGG с ORDER BY |
T814 |  | Двоеточие в JSON_OBJECT или JSON_OBJECTAGG |
T821 |  | Базовые операторы запросов SQL/JSON |
T822 |  | SQL/JSON: предикат IS JSON WITH UNIQUE KEYS |
T823 |  | SQL/JSON: предложение PASSING |
T824 |  | JSON_TABLE: специфичное предложение PLAN |
T825 |  | SQL/JSON: предложения ON EMPTY и ON ERROR |
T826 |  | Выражение общего значения в предложениях ON ERROR или ON EMPTY |
T827 |  | JSON_TABLE: одноуровневые предложения NESTED COLUMNS |
T828 |  | JSON_QUERY |
T829 |  | JSON_QUERY: варианты обертки массива |
T830 |  | Требование уникальных ключей в функциях-конструкторах SQL/JSON |
T832 |  | Язык пути SQL/JSON: метод элемента | функция datetime() еще не реализована
T838 |  | JSON_TABLE: предложение PLAN DEFAULT |
T839 |  | Форматируемое приведение даты/времени из символьных строк и обратно |
M001 |  | Передача данных |
M002 |  | Передача данных через SQL/CLI |
M003 |  | Передача данных через встроенный SQL |
M004 |  | Поддержка сторонних данных | частично поддерживается
M005 |  | Поддержка сторонних схем |
M006 |  | Подпрограмма GetSQLString |
M007 |  | TransmitRequest |
M009 |  | Подпрограммы GetOpts и GetStatistics |
M010 |  | Поддержка обертки сторонних данных | другой API
M011 |  | Передача данных через Ada |
M012 |  | Передача данных через C |
M013 |  | Передача данных через COBOL |
M014 |  | Передача данных через Fortran |
M015 |  | Передача данных через MUMPS |
M016 |  | Передача данных через Pascal |
M017 |  | Передача данных через PL/I |
M018 |  | Подпрограммы интерфейса обертки сторонних данных на языке Ada |
M019 |  | Подпрограммы интерфейса обертки сторонних данных на языке C | другой API
M020 |  | Подпрограммы интерфейса обертки сторонних данных на языке COBOL |
M021 |  | Подпрограммы интерфейса обертки сторонних данных на языке Fortran |
M022 |  | Подпрограммы интерфейса обертки сторонних данных на языке MUMPS |
M023 |  | Подпрограммы интерфейса обертки сторонних данных на языке Pascal |
M024 |  | Подпрограммы интерфейса обертки сторонних данных на языке PL/I |
M030 |  | Поддержка сторонних данных SQL-сервера |
M031 |  | Общие подпрограммы обертки сторонних данных |
X012 |  | Мультимножества типа XML |
X013 |  | Различные типы на основе типа XML |
X015 |  | Поля типа XML |
X025 |  | XMLCAST |
X030 |  | XMLDocument |
X038 |  | XMLText |
X065 |  | XMLParse: ввод BLOB и вариант CONTENT |
X066 |  | XMLParse: ввод BLOB и вариант DOCUMENT |
X068 |  | XMLSerialize: BOM |
X069 |  | XMLSerialize: INDENT |
X073 |  | XMLSerialize: сериализация BLOB и вариант CONTENT |
X074 |  | XMLSerialize: сериализация BLOB и вариант DOCUMENT |
X075 |  | XMLSerialize: сериализация BLOB |
X076 |  | XMLSerialize: VERSION |
X077 |  | XMLSerialize: явное указание ENCODING |
X078 |  | XMLSerialize: явное объявление XML |
X080 |  | Пространства имен при публикации XML |
X081 |  | Объявления пространств имен XML на уровне запроса |
X082 |  | Объявления пространств имен XML в DML |
X083 |  | Объявления пространств имен XML в DDL |
X084 |  | Объявления пространств имен XML в составных операторах |
X085 |  | Предопределенные префиксы пространств имен |
X086 |  | Объявления пространств имен XML в XMLTable |
X091 |  | Предикат содержимого XML |
X096 |  | XMLExists | Только XPath 1.0
X100 |  | Поддержка базового языка для XML: вариант CONTENT |
X101 |  | Поддержка базового языка для XML: вариант DOCUMENT |
X110 |  | Поддержка базового языка для XML: сопоставление VARCHAR |
X111 |  | Поддержка базового языка для XML: сопоставление CLOB |
X112 |  | Поддержка базового языка для XML: сопоставление BLOB |
X113 |  | Поддержка базового языка для XML: параметр STRIP WHITESPACE |
X114 |  | Поддержка базового языка для XML: параметр PRESERVE WHITESPACE |
X131 |  | Предложение XMLBINARY на уровне запроса |
X132 |  | Предложение XMLBINARY в DML |
X133 |  | Предложение XMLBINARY в DDL |
X134 |  | Предложение XMLBINARY в составных операторах |
X135 |  | Предложение XMLBINARY в подзапросах |
X141 |  | Предикат IS VALID: управляемый данными |
X142 |  | Предикат IS VALID: предложение ACCORDING TO |
X143 |  | Предикат IS VALID: предложение ELEMENT |
X144 |  | Предикат IS VALID: расположение схемы |
X145 |  | Предикат IS VALID вне проверочных ограничений |
X151 |  | Предикат IS VALID с вариантом DOCUMENT |
X152 |  | Предикат IS VALID с вариантом CONTENT |
X153 |  | Предикат IS VALID с вариантом SEQUENCE |
X155 |  | Предикат IS VALID: NAMESPACE без предложения ELEMENT |
X157 |  | Предикат IS VALID: NO NAMESPACE с предложением ELEMENT |
X160 |  | Базовая информационная схема для зарегистрированных XML-схем |
X161 |  | Расширенная информационная схема для зарегистрированных XML-схем |
X170 |  | Варианты обработки NULL с XML |
X171 |  | Вариант NIL ON NO CONTENT |
X181 |  | Тип XML(DOCUMENT (UNTYPED)) |
X182 |  | Тип XML(DOCUMENT(ANY)) |
X190 |  | Тип XML(SEQUENCE) |
X191 |  | Тип XML(DOCUMENT(XMLSCHEMA)) |
X192 |  | Тип XML(CONTENT(XMLSCHEMA)) |
X200 |  | XMLQuery |
X201 |  | XMLQuery: RETURNING CONTENT |
X202 |  | XMLQuery: RETURNING SEQUENCE |
X203 |  | XMLQuery: передача элемента контекста |
X204 |  | XMLQuery: инициализация переменной XQuery |
X205 |  | XMLQuery: указание EMPTY ON EMPTY |
X206 |  | XMLQuery: указание NULL ON EMPTY |
X211 |  | Поддержка XML 1.1 |
X222 |  | Механизм передачи XML BY REF | анализатор принимает BY REF, но игнорирует его; отбор всегда идет по BY VALUE
X231 |  | Тип XML(CONTENT (UNTYPED)) |
X232 |  | Тип XML(CONTENT (ANY)) |
X241 |  | RETURNING CONTENT при публикации XML |
X242 |  | RETURNING SEQUENCE при публикации XML |
X251 |  | Хранимые значения XML типа XML(DOCUMENT (UNTYPED)) |
X252 |  | Хранимые значения XML типа XML(DOCUMENT (ANY)) |
X253 |  | Хранимые значения XML типа XML(CONTENT (UNTYPED)) |
X254 |  | Хранимые значения XML типа XML(CONTENT (ANY)) |
X255 |  | Хранимые значения XML типа XML(SEQUENCE) |
X256 |  | Хранимые значения XML типа XML(DOCUMENT (XMLSCHEMA)) |
X257 |  | Хранимые значения XML типа XML(CONTENT (XMLSCHEMA)) |
X260 |  | Тип XML: предложение ELEMENT |
X261 |  | Тип XML: NAMESPACE без предложения ELEMENT |
X263 |  | Тип XML: NO NAMESPACE с предложением ELEMENT |
X264 |  | Тип XML: расположение схемы |
X271 |  | XMLValidate: управляемый данными |
X272 |  | XMLValidate: предложение ACCORDING TO |
X273 |  | XMLValidate: предложение ELEMENT |
X274 |  | XMLValidate: расположение схемы |
X281 |  | XMLValidate с вариантом DOCUMENT |
X282 |  | XMLValidate с вариантом CONTENT |
X283 |  | XMLValidate с вариантом SEQUENCE |
X284 |  | XMLValidate: NAMESPACE без предложения ELEMENT |
X286 |  | XMLValidate: NO NAMESPACE с предложением ELEMENT |
X300 |  | XMLTable | Только XPath 1.0
X305 |  | XMLTable: инициализация переменной XQuery |

Ограничения XML и соответствие SQL/XML

В SQL:2006 в спецификации ISO/IEC 9075-14 (SQL/XML), посвященные XML, были внесены существенные изменения. Реализация типа данных XML и связанных с ним функций в QHB во большей степени соответствует более ранней редакции от 2003 года, с некоторыми заимствованиями из последующих редакций. В частности:

Тогда как в текущем стандарте предусмотрено семейство типов данных XML, содержащих «документы» или «содержимое» в нетипизированных вариантах или вариантах с типами XML Schema, а также тип XML(SEQUENCE), содержащий произвольные фрагменты содержимого XML, в QHB есть только один тип xml, который может содержать «документ» или «содержимое». Эквивалента определенному в стандарте типу «последовательность» в QHB не существует.

QHB предоставляет две функции, появившиеся в SQL:2006, но вместо языка XML Query, указанного для них в стандарте, в этих вариантах используется язык XPath 1.0.

В этом разделе описаны некоторые проистекающие из этого различия, с которыми вы можете столкнуться.

Запросы ограничены XPath 1.0

Специфичные для QHB функции xpath() и xpath_exists() выполняют запросы к документам XML на языке XPath. QHB также предоставляет стандартные функции XMLEXISTS и XMLTABLE, поддерживающие только XPath, хотя официально они должны поддерживать язык XQuery. Для всех этих функций QHB использует библиотеку libxml2, которая поддерживает только XPath 1.0.

Существует тесная связь между языком XQuery и XPath версии 2.0 и новее: любое выражение, которое синтаксически допустимо и успешно выполняется, выдает в обоих языках одинаковый результат (с небольшим исключением для выражений, содержащих ссылки на числовые символы или предварительно определенные сущности, которые XQuery заменяет соответствующими символами, а XPath оставляет в исходном виде). Но между этими языками и XPath 1.0 такой связи нет; он появился раньше и во многих отношениях отличается от них.

Следует иметь в виду две категории ограничений: ограничение языка XQuery до XPath для функций, указанных в стандарте SQL, и ограничение XPath до версии 1.0 как для стандартных, так и для специфичных функций QHB.

Ограничение языка XQuery относительно XPath

В число возможностей XQuery, отсутствующих у XPath, входят:

Выражения XQuery могут не только возвращать все возможные значения XPath, но и конструировать новые узлы XML. XPath может создавать и возвращать значения атомарных типов (числа, строки и т. д.), но возвращать способен только те узлы XML, которые уже присутствовали в документах, поступивших на вход выражения.

В XQuery есть управляющие конструкции для итерации, сортировки и группировки.

XQuery позволяет объявлять и использовать локальные функции.

В последних версиях XPath начинают появляться возможности, пересекающиеся с возможностями XQuery (например, функциональные конструкции for-each и *sort, анонимные функции и функция parse-xml, создающая узел из строки), но до XPath 3.0 они были недоступны.

Ограничение XPath до версии 1.0

Разработчикам, знакомым с XQuery и XPath 2.0 или новее, приходится сталкиваться с рядом отличий XPath 1.0:

Фундаментальный тип выражения XQuery/XPath, тип sequence, который может содержать узлы XML, атомарные значения или и то, и другое, в XPath 1.0 отсутствует. В 1.0 выражение может создавать только множество узлов (содержащих ноль или более узлов XML) или одно атомарное значение.

В отличие от последовательности XQuery/XPath, которая может содержать любые требуемые элементы в любом желаемом порядке, во множестве узлов XPath 1.0 нет гарантированного порядка и оно, как и любое множество, не допускает несколько появлений одного и того же элемента.

Примечание
По-видимому, библиотека libxml2 всегда возвращает в QHB множества узлов с внутренними членами в том же относительном порядке, в каком они идут во входном документе. В ее документации это поведение не указано, а выражение XPath 1.0 не может им управлять.

Тогда как XQuery/XPath предоставляет все типы, определенные в стандарте XML Schema, а также множество операторов и функций для этих типов, XPath 1.0 поддерживает только множества узлов и три атомарных типа: boolean, double и string.

В XPath 1.0 отсутствует условный оператор. Выражение XQuery/XPath вида if ( hat ) then hat/@size else "no hat" не имеет эквивалента в XPath 1.0.

В XPath 1.0 отсутствует оператор сравнения строк с упорядочиванием. Условия "cat" < "dog" и "cat" > "dog" оба являются ложными, поскольку представляют собой числовые сравнения двух значений NaN. Условия же = и !=, напротив, сравнивают строки в виде строк.

XPath 1.0 размывает разницу между сравнениями значений и общими сравнениями, как они определены в XQuery/XPath. Сравнения sale/@hatsize = 7 и sale/@customer = "alice" по сути оба являются количественными сравнениями, и их результатом будет истина, если существует элемент sale с заданным значением атрибута, но sale/@taxable = false() — это сравнение значений с фактическим логическим значением всего множества узлов. Его результат будет истиной, только если ни у одного элемента sale вообще не будет атрибута taxable.

В модели данных XQuery/XPath узел документа может иметь либо форму документа (т. е. содержать ровно один элемент верхнего уровня, снаружи которого допускаются только комментарии и инструкции по обработке), либо форму содержимого (с ослабленными ограничениями). В XPath 1.0 ему соответствует корневой узел, который может иметь только форму документа. Это одна из причин, по которой значение типа xml, передаваемое в качестве элемента контекста любой функции QHB на базе XPath, должно быть в форме документа.

Кроме различий, отмеченных здесь, есть и другие. В языках XQuery и XPath 2.0 и новее существует режим совместимости с XPath 1.0, а имеющийся в документации W3C список изменений библиотеки функций и языковых изменений, применяемых в этом режиме, предоставляет более полное (но все же не исчерпывающее) описание различий. Режим совместимости не обеспечивает полную идентичность XPath 1.0 и более поздних языков.

Сопоставления типов данных и значений между SQL и XML

В SQL:2006 и более поздних версиях точно определены преобразования между стандартными типами данных SQL и типами стандарта XML Schema в обе стороны. Однако эти правила выражаются в типах и семантике XQuery/XPath и не могут быть напрямую применены к другой модели данных, характерной для XPath 1.0.

Когда QHB сопоставляет значения данных SQL с XML (как в функции xmlelement) или XML с SQL (как в выходных столбцах xmltable), за исключением нескольких специально обрабатываемых случаев, QHB просто предполагает, что строковая форма XPath 1.0, содержащая данные типа XML, будет допустима в качестве формы ввода в виде текста в типе данных SQL, и наоборот. Это правило подкупает своей простотой, и при этом результаты для многих типов данных именно такие, какими, согласно стандарту, должны быть у преобразований.

В тех случаях, когда требуется взаимодействие с другими системами, для некоторых типов данных может возникнуть необходимость в явном использовании функций форматирования типов данных (например, описанных в разделе Функции форматирования типов данных) для получения сопоставлений, в точности соответствующих стандарту.

Непреднамеренные ограничения реализации

В этом разделе рассматриваются ограничения, которые присущи не самой библиотеке libxml2, а текущей реализации в QHB.

Поддерживается только механизм передачи параметров по значению (BY VALUE)

В стандарте SQL определены два механизма передачи параметров, которые применяются при передаче аргумента XML из SQL в функцию XML или получении результата: BY REF, в котором конкретное значение XML сохраняет идентификатор узла, и BY VALUE, в котором передается содержимое XML, но идентификатор узла теряется. Задать механизм можно перед списком параметров, в качестве механизма по умолчанию для всех параметров, или после каждого параметра, тем самым переопределив выбор по умолчанию.

Чтобы проиллюстрировать разницу, рассмотрим следующие два запроса. Если x является значением XML, в среде SQL:2006 они выдадут значения true и false соответственно:

SELECT XMLQUERY(’$a is $b’ PASSING BY REF x AS a, x AS b NULL ON EMPTY);

SELECT XMLQUERY(’$a is $b’ PASSING BY VALUE x AS a, x AS b NULL ON EMPTY);

QHB примет указания BY VALUE и BY REF в конструкции XMLEXISTS или XMLTABLE, но проигнорирует их. Тип данных xml содержит сериализованное представление символьной строки, поэтому идентификатор узла, который нужно сохранять, отсутствует, и фактически передача всегда производится по значению (BY VALUE).

Невозможно передать в запросы именованные параметры

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

Отсутствие типа XML(SEQUENCE)

Тип данных xml в QHB может содержать значение только в форме документа (DOCUMENT) или содержимого (CONTENT). Элемент контекста выражения XQuery/XPath должен быть одиночным узлом XML или атомарным значением, но XPath 1.0 дополнительно ограничивает его только узлом XML и не имеет типа узла, содержащего CONTENT. В результате единственной формой значения XML, которую в качестве элемента контекста XPath может предоставить QHB, является корректно сформированный документ (DOCUMENT).

Расширенный SQL

-----------------------------
-- Наследование:
--	Таблица может наследоваться от нескольких таблиц. Запрос может получить как записи из таблицы
--	так дополнительно и записи из всех ее наследников.
-----------------------------

-- Для примера, таблица capitals (столицы) наследуется от таблицы  cities (города), при этом будут унаследованы все поля.

CREATE TABLE cities (
	name		text,
	population	float8,
	altitude	int		-- (в футах)
);

CREATE TABLE capitals (
	state		char(2)
) INHERITS (cities);

-- Теперь заполним таблицы:

INSERT INTO cities VALUES ('San Francisco', 7.24E+5, 63);
INSERT INTO cities VALUES ('Las Vegas', 2.583E+5, 2174);
INSERT INTO cities VALUES ('Mariposa', 1200, 1953);

INSERT INTO capitals VALUES ('Sacramento', 3.694E+5, 30, 'CA');
INSERT INTO capitals VALUES ('Madison', 1.913E+5, 845, 'WI');

SELECT * FROM cities;
SELECT * FROM capitals;

-- Можно найти все города, включая столицы, которые расположены на высоте 500 футов и выше.

SELECT c.name, c.altitude
FROM cities c
WHERE c.altitude > 500;

-- Для того чтобы в запросе участвовала только родительская таблица, используйте ключевое слово ONLY:

SELECT name, altitude
FROM ONLY cities
WHERE altitude > 500;

-- Очистка таблиц (сначала надо удалить дочерние таблицы):

DROP TABLE capitals;
DROP TABLE cities;

Приложения

Внутренние сообщения в QHB

Форматы сообщений

Поля сообщений с ошибками и замечаниями

Расширения

Расширения QHB

Специализированные и экспериментальные модули PostgreSQL

Клиентские приложения

Сторонние расширения

Поддерживаемые платформы

Поддержка решений 1С

Особенности использования QHB

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

Страница загрузки

Краткие инструкции

Краткая инструкция по начальной загрузке и установке

Краткая инструкция по обновлению

Краткая инструкция по миграции кластера баз данных в QHB

Запуск и остановка сервера с помощью сервиса systemd

Пример настройки синхронной реплики

Инструкция по использованию QSS в режиме хранения пользовательского ключа на USB носителе

Инструкция по использованию QSS в режиме хранения пользовательского ключа на крипто-токене

Инструкция по установке и использованию QCP

Работа с 1С

Инструкция по установке QHB под 1С

Конфигурирование QHB под 1С

Резервное копирование QHB под 1С

Сокращения

Глоссарий

Поддержка цветового оформления

Лицензирование

Замечания к релизу

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

Вы можете обратиться к официальной документации PostgreSQL по адресу https://www.postgresql.org/docs/

Внутренние сообщения в QHB

Форматы сообщений

Поля сообщений с ошибками и замечаниями

Форматы сообщений

В этом разделе подробно описывается формат каждого сообщения. Все сообщения помечены символами, обозначающими, какая сторона может их отправить: клиент (F, frontend), сервер (B, backend) или обе (F и B). Обратите внимание, что хотя каждое сообщение включает в себя счетчик байтов в начале, формат сообщения определяется таким образом, чтобы конец сообщения можно было найти, не обращаясь к счетчику байтов. Это помогает проверке на допустимость сообщений. (Исключением является сообщение CopyData, поскольку оно представляет собой часть потока данных; содержимое любого отдельного сообщения CopyData нельзя интерпретировать само по себе.)

Authentication (B)

Byte1('R')

Определяет, что сообщение является запросом аутентификации.

Int32(8)

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

Int32(0)

Указывает, что аутентификация прошла успешно.

AuthenticationKerberosV5 (B)

Byte1('R')

Определяет, что сообщение является запросом аутентификации.

Int32(8)

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

Int32(2)

Указывает, что требуется аутентификация по протоколу Kerberos V5.

AuthenticationCleartextPassword (B)

Byte1('R')

Определяет, что сообщение является запросом аутентификации.

Int32(8)

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

Int32(3)

Указывает, что требуется незашифрованный пароль.

AuthenticationMD5Password (B)

Byte1('R')

Определяет, что сообщение является запросом аутентификации.

Int32(12)

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

Int32(5)

Указывает, что требуется пароль, зашифрованный в хеш MD5.

Byte4

Значение соли, с которой должен шифроваться пароль.

AuthenticationSCMCredential (B)

Byte1('R')

Определяет, что сообщение является запросом аутентификации.

Int32(8)

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

Int32(6)

Указывает, что требуется сообщение с учетными данными SCM.

Authenticationss (B)

Byte1('R')

Определяет, что сообщение является запросом аутентификации.

Int32(8)

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

Int32(7)

Указывает, что требуется аутентификация на базе GSSAPI.

AuthenticationSSPI (B)

Byte1('R')

Определяет, что сообщение является запросом аутентификации.

Int32(8)

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

Int32(9)

Указывает, что требуется аутентификация на базе SSPI.

Authenticationsscontinue (B)

Byte1('R')

Определяет, что сообщение является запросом аутентификации.

Int32

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

Int32(8)

Указывает, что это сообщение содержит данные GSSAPI или SSPI.

Byten

Данные аутентификации для GSSAPI или SSPI.

AuthenticationSASL (B)

Byte1('R')

Определяет, что сообщение является запросом аутентификации.

Int32

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

Int32(10)

Указывает, что требуется аутентификация на базе SASL.

Тело сообщения представляет собой список механизмов аутентификации SASL в порядке предпочтений сервера. За последним именем механизма аутентификации должен стоять завершающий нулевой байт. Для каждого механизма передается следующее:

String

Имя механизма аутентификации SASL.

AuthenticationSASLContinue (B)

Byte1('R')

Определяет, что сообщение является запросом аутентификации.

Int32

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

Int32(11)

Указывает, что это сообщение содержит данные вызова SASL.

Byten

Данные SASL, специфичные для используемого механизма SASL.

AuthenticationSASLFinal (B)

Byte1('R')

Определяет, что сообщение является запросом аутентификации.

Int32

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

Int32(12)

Указывает, что аутентификация SASL завершена.

Byten

«Дополнительные данные» результата SASL, специфичные для используемого механизма SASL.

BackendKeyData (B)

Byte1('K')

Определяет, что сообщение содержит ключевые данные для отмены запросов. Клиент должен сохранить эти значения, если ему нужна возможность впоследствии выдавать сообщения CancelRequest.

Int32(12)

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

Int32

PID обслуживающего процесса.

Int32

Секретный ключ этого обслуживающего процесса.

Bind (F)

Byte1('B')

Определяет, что сообщение содержит команду Bind.

Int32

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

String

Имя целевого портала (при пустой строке выбирается безымянный портал).

String

Имя исходного подготовленного оператора (при пустой строке выбирается безымянный подготовленный оператор).

Int16

Число кодов форматов следующих параметров (обозначается ниже символом C). Может равняться нулю, указывая, что параметров нет или все они используют формат по умолчанию (текстовый); либо одному, и тогда указанный код формата применяется ко всем параметрам; либо может равняться фактическому числу параметров.

Int16[C]

Код формата параметров. В настоящее время допускаются коды ноль (текстовый формат) или один (двоичный формат).

Int16

Число следующих значений параметров (может быть нулевым). Оно должно соответствовать числу параметров, необходимых для запроса.

Затем для каждого параметра отображается следующая пара полей:

Int32

Длина значения параметра, в байтах (сам счетчик длины не считается). Может равняться нулю. В качестве особого случая -1 указывает на значение NULL. В случае NULL никакие байты значений далее не следуют.

Byten

Значение параметра в формате, указанном соответствующим кодом формата. Переменная n задает длину значения.

За последним параметром отображаются следующие поля:

Int16

Число кодов форматов для следующих столбцов результата (обозначается ниже символом R). Может равняться нулю, указывая, что столбцов результата нет или для них для всех должен использоваться формат по умолчанию (текстовый); либо одному, и тогда указанный код формата применяется ко всем столбцам результата (если таковые имеются); либо может равняться фактическому числу столбцов результата запроса.

Int16[R]

Код формата столбцов результата. В настоящее время допускаются коды ноль (текстовый формат) или один (двоичный формат).

BindComplete (B)

Byte1('2')

Определяет, что сообщение сигнализирует о завершении Bind.

Int32(4)

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

CancelRequest (F)

Int32(16)

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

Int32 (80877102)

Код запроса на отмену. Это значение отбирается так, чтобы содержать 1234 в старших 16 битах и 5678 в младших 16 битах. (Во избежание путаницы этот код не должен совпадать с номером версии протокола.)

Int32

PID целевого обслуживающего процесса.

Int32

Секретный ключ целевого обслуживающего процесса.

Close (F)

Byte1('C')

Определяет, что сообщение содержит команду Close.

Int32

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

Byte1

'S' для закрытия подготовленного оператора или 'P' для закрытия портала.

String

Имя подготовленного оператора или портала, подлежащего закрытию (при пустой строке выбирается безымянный подготовленный оператор или портал).

CloseComplete (B)

Byte1('3')

Определяет, что сообщение сигнализирует о завершении Close.

Int32(4)

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

CommandComplete (B)

Byte1('C')

Определяет, что это сообщение об успешном завершении команды.

Int32

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

String

Командный тег. Обычно это одно слово, определяющее, какая команда SQL была завершена.

Для команды INSERT в качестве тега передается INSERT oid число_строк, где число_строк — это количество вставленных строк. В поле oid передавался идентификатор объекта вставленной строки, если число_строк было равно 1 и в целевой таблице содержались OID, но системные столбцы с OID больше не поддерживаются; поэтому oid всегда равно 0.

Для команды DELETE в качестве тега передается DELETE число_строк, где число_строк — это количество удаленных строк.

Для команды UPDATE в качестве тега передается UPDATE число_строк, где число_строк — это количество обновленных строк.

Для команды SELECT или CREATE TABLE AS в качестве тега передается SELECT число_строк, где число_строк — это количество извлеченных строк.

Для команды MOVE в качестве тега передается MOVE число_строк, где число_строк — это количество строк, на которое изменилась позиция курсора.

Для команды FETCH в качестве тега передается FETCH число_строк, где число_строк — это количество строк, извлеченных через курсор.

Для команды COPY в качестве тега передается COPY число_строк, где число_строк — это количество скопированных строк.

CopyData (F и B)

Byte1('d')

Определяет, что в сообщении передаются данные COPY.

Int32

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

Byten

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

CopyDone (F & B)

Byte1('c')

Определяет, что сообщение сигнализирует о завершении COPY.

Int32(4)

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

CopyFail (F)

Byte1('f')

Определяет, что сообщение сигнализирует об ошибке выполнения COPY.

Int32

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

String

Сообщение об ошибке, описывающее ее причину.

CopyInResponse (B)

Byte1('G')

Определяет, что сообщение содержит ответ на запуск входящего копирования. Теперь клиент должен начать отправлять данные операции входящего копирования (если клиент не готов это сделать, передается сообщение CopyFail).

Int32

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

Int8

0 указывает на то, что для всей операции COPY применяется текстовый формат (строки разделяются символами новой строки, столбцы разделяются символами- разделителями и т. д.). 1 указывает, что для всей операции COPY применяется двоичный формат (подобный формату DataRow). Дополнительную информацию см. в описании команды COPY.

Int16

Количество столбцов в копируемых данных (обозначается ниже символом N).

Int16[N]

Код формата для всех столбцов. В настоящее время допускаются коды ноль (текстовый формат) или один (двоичный формат). Если для всей операции копирования применяется текстовый формат, все эти коды должны быть равны нулю.

CopyOutResponse (B)

Byte1('H')

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

Int32

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

Int8

0 указывает на то, что для всей операции COPY применяется текстовый формат (строки разделяются символами новой строки, столбцы разделяются символами- разделителями и т. д.). 1 указывает, что для всей операции COPY применяется двоичный формат (подобный формату DataRow). Дополнительную информацию см. в описании команды COPY.

Int16

Количество столбцов в копируемых данных (обозначается ниже символом N).

Int16[N]

Код формата для всех столбцов. В настоящее время допускаются коды ноль (текстовый формат) или один (двоичный формат). Если для всей операции копирования применяется текстовый формат, все эти коды должны быть равны нулю.

CopyBothResponse (B)

Byte1('W')

Определяет, что сообщение содержит ответ на запуск двустороннего копирования. Это сообщение используется только для потоковой репликации.

Int32

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

Int8

0 указывает на то, что для всей операции COPY применяется текстовый формат (строки разделяются символами новой строки, столбцы разделяются символами- разделителями и т. д.). 1 указывает, что для всей операции COPY применяется двоичный формат (подобный формату DataRow). Дополнительную информацию см. в описании команды COPY.

Int16

Количество столбцов в копируемых данных (обозначается ниже символом N).

Int16[N]

Код формата для всех столбцов. В настоящее время допускаются коды ноль (текстовый формат) или один (двоичный формат). Если для всей операции копирования применяется текстовый формат, все эти коды должны быть равны нулю.

DataRow (B)

Byte1('D')

Определяет, что сообщение содержит строку данных.

Int32

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

Int16

Число последующих значений столбцов (может равняться нулю).

Затем для каждого столбца отображается следующая пара полей:

Int32

Длина значения столбца, в байтах (сам счетчик длины не считается). Может равняться нулю. В качестве особого случая -1 указывает на значение NULL. В случае NULL никакие байты значений далее не следуют.

Byten

Значение столбца в формате, определенном соответствующим кодом формата. Переменная n задает длину значения.

Describe (F)

Byte1('D')

Определяет, что сообщение содержит команду Describe.

Int32

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

Byte1

'S' для описания подготовленного оператора или 'P' для описания портала.

String

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

EmptyQueryResponse (B)

Byte1('I')

Определяет, что сообщение содержит ответ на пустую строку запроса. (Это сообщение заменяет CommandComplete.)

Int32(4)

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

ErrorResponse (B)

Byte1('E')

Определяет, что сообщение об ошибке.

Int32

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

Тело сообщения состоит из одного или нескольких определенных полей, за которыми идет завершающий нулевой байт. Поля могут отображаться в любом порядке. Для каждого поля передается следующее:

Byte1

Код, определяющий тип поля; ноль обозначает конец сообщения, после которого не следует никаких строк. Типы полей, определенные в настоящее время, перечислены в разделе Поля сообщений с ошибками и замечаниями. Поскольку в будущем могут добавиться и другие типы полей, клиенты должны просто игнорировать поля нераспознанного типа.

String

Значение поля.

Execute (F)

Byte1('E')

Определяет, что сообщение содержит команду Execute.

Int32

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

String

Имя портала, подлежащего выполнению (при пустой строка выбирается безымянный портал).

Int32

Максимальное число строк, которое должно быть возвращено, если портал содержит запрос, возвращающий строки (в противном случае игнорируется). Ноль означает «без ограничений».

Flush (F)

Byte1('H')

Определяет, что сообщение содержит команду Flush.

Int32(4)

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

FunctionCall (F)

Byte1('F')

Определяет, что сообщение содержит вызов функции.

Int32

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

Int32

Задает идентификатор объекта вызываемой функции.

Int16

Число кодов форматов следующих аргументов (обозначается ниже символом C). Может равняться нулю, указывая, что аргументов нет или для них для всех должен использоваться формат по умолчанию (текстовый); либо одному, и тогда указанный код формата применяется ко всем аргументам; либо может равняться фактическому числу аргументов.

Int16[C]

Код формата аргументов. В настоящее время допускаются коды ноль (текстовый формат) или один (двоичный формат).

Int16

Задает число аргументов, передаваемых функции.

Затем для каждого аргумента отображается следующая пара полей:

Int32

Длина значения аргумента, в байтах (сам счетчик длины не считается). Может равняться нулю. В качестве особого случая -1 указывает на значение NULL. В случае NULL никакие байты значений далее не следуют.

Byten

Значение аргумента в формате, определенном соответствующим кодом формата. Переменная n задает длину значения.

За последним аргументом идет следующее поле:

Int16

Код формата результата функции. В настоящее время допускаются коды ноль (текстовый формат) или один (двоичный формат).

FunctionCallResponse (B)

Byte1('V')

Определяет что сообщение содержит результат вызова функции.

Int32

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

Int32

Длина значения результата функции, в байтах (сам счетчик длины не считается). Может равняться нулю. В качестве особого случая -1 указывает на значение NULL. В случае NULL никакие байты значений далее не следуют.

Byten

Значение результата функции в формате, определенном соответствующим кодом формата. Переменная n задает длину значения.

GSSResponse (F)

Byte1('p')

Определяет, что сообщение содержит ответ GSSAPI или SSPI. Обратите внимание, что оно также используется для ответов SASL и при аутентификации пароля. Точный тип сообщения можно определить из контекста.

Int32

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

Byten

Данные сообщения, специфичные для GSSAPI/SSPI.

NegotiateProtocolVersion (B)

Byte1('v')

Определяет, что сообщение содержит согласование версии протокола.

Int32

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

Int32

Новейшая младшая версия протокола, поддерживаемая сервером, для запрошенной клиентом старшей версии.

Int32

Число параметров протокола, не распознанных сервером.

Затем для параметров протокола, не распознанного сервером, передается следующее:

String

Имя параметра.

NoData (B)

Byte1('n')

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

Int32(4)

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

NoticeResponse (B)

Byte1('N')

Определяет, что сообщение содержит замечание.

Int32

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

Тело сообщения состоит из одного или нескольких определенных полей, за которыми идет завершающий нулевой байт. Поля могут отображаться в любом порядке. Для каждого поля передается следующее:

Byte1

Код, определяющий тип поля; ноль обозначает конец сообщения, после которого не следует никаких строк. Типы полей, определенные в настоящее время, перечислены в разделе Поля сообщений с ошибками и замечаниями. Поскольку в будущем могут добавиться и другие типы полей, клиенты должны просто игнорировать поля нераспознанного типа.

String

Значение поля.

NotificationResponse (B)

Byte1('A')

Определяет, что сообщение содержит уведомление.

Int32

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

Int32

PID обслуживающего процесса, отправляющего уведомление.

String

Имя канала, для которого было выдано уведомление.

String

Строка «информационного наполнения», сопровождающего уведомление.

ParameterDescription (B)

Byte1('t')

Определяет, что сообщение содержит описание параметра.

Int32

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

Int16

Число параметров, используемых оператором (может равняться нулю).

Затем для каждого параметра передается следующее:

Int32

Задает идентификатор объекта типа данных параметра.

ParameterStatus (B)

Byte1('S')

Определяет, что сообщение содержит отчет о состоянии параметра времени выполнения.

Int32

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

String

Имя параметра времени выполнения, состояние которого передается.

String

Текущее значение параметра.

Parse (F)

Byte1('P')

Определяет, что сообщение содержит команду Parse.

Int32

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

String

Имя целевого подготовленного оператора (при пустой строке выбирается безымянный подготовленный оператор).

String

Строка запроса, подлежащая разбору.

Int16

Число заданных типов данных параметров (может равняться нулю). Обратите внимание, что это значение представляет не число параметров, которые могут отображаться в строке запроса, а число параметров, для которых клиент хочет предварительно задать типы.

Затем для каждого параметра передается следующее:

Int32

Задает идентификатор объекта типа данных параметра. Указание нулевого значения равнозначно отсутствию указания типа.

ParseComplete (B)

Byte1('1')

Определяет, что сообщение сигнализирует о завершении Parse.

Int32(4)

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

PasswordMessage (F)

Byte1('p')

Определяет, что сообщение содержит пароль. Обратите внимание, что оно также используется для ответов GSSAPI, SSPI и SASL. Точный тип сообщения можно определить из контекста.

Int32

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

String

Пароль (зашифрованный, если требуется).

PortalSuspended (B)

Byte1('s')

Определяет, что сообщение сигнализирует о приостановке работы портала. Обратите внимание, что оно выдается только при достижении предела числа строк, заданного в сообщении Execute.

Int32(4)

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

Query (F)

Byte1('Q')

Определяет, что сообщение содержит простой запрос.

Int32

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

String

Собственно строка запроса.

ReadyForQuery (B)

Byte1('Z')

Определяет тип сообщения. Сообщение ReadyForQuery передается всякий раз, когда сервер готов к новому циклу запросов.

Int32(5)

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

Byte1

Индикатор текущего состояния транзакции на сервере. Возможные значения: 'I' — транзакция неактивна (вне блока транзакции); 'T' в блоке транзакции, или 'E' в блоке прерванной транзакции (запросы не будут обрабатываться до завершения блока).

RowDescription (B)

Byte1('T')

Определяет, что сообщение содержит описание строки.

Int32

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

Int16

Задает количество полей в строке (может равняться нулю).

Затем для каждого поля передается следующее:

String

Имя поля.

Int32

Если поле можно определить как столбец определенной таблицы, идентификатор объекта таблицы; в противном случае — ноль.

Int16

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

Int32

Идентификатор объекта типа данных поля.

Int16

Размер типа данных (см. pg_type.typlen). Обратите внимание, что отрицательные значения означают, что тип имеет переменную ширины.

Int32

Модификатор типа (см. pg_attribute.atttypmod). Смысл модификатора зависит от типа.

Int16

Код формата для данного поля. В настоящее время допускаются коды ноль (текстовый формат) или один (двоичный формат). В сообщении RowDescription, возвращаемом Describe в варианте оператора, код формата еще не известен и всегда будет равен нулю.

SASLInitialResponse (F)

Byte1('p')

Определяет, что сообщение содержит начальный ответ SASL. Обратите внимание, что оно также используется для ответов GSSAPI, SSPI и при аутентификации по паролю. Точный тип сообщения определяется из контекста.

Int32

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

String

Имя механизма аутентификации SASL, выбранного клиентом.

Int32

Длина последующего сообщения «Начальный ответ клиента», специфичного для механизма SASL, либо -1, если начального ответа нет.

Byten

Данные сообщения «Начальный ответ», специфичные для механизма SASL.

SASLResponse (F)

Byte1('p')

Определяет, что сообщение содержит ответ SASL. Обратите внимание, что оно также используется для ответов GSSAPI, SSPI и при аутентификации по паролю. Точный тип сообщения можно определить из контекста.

Int32

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

Byten

Данные сообщения, специфичные для механизма SASL.

SSLRequest (F)

Int32(8)

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

Int32 (80877103)

Код запроса SSL. Это значение отбирается так, чтобы содержать 1234 в старших 16 битах и 5679 в младших 16 битах. (Во избежание путаницы этот код не должен совпадать с номером версии протокола.)

GSSENCRequest (F)

Int32(8)

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

Int32 (80877104)

Код запроса GSSAPI Encryption. Это значение отбирается так, чтобы содержать 1234 в старших 16 битах и 5680 в младших 16 битах. (Во избежание путаницы этот код не должен совпадать с номером версии протокола.)

StartupMessage (F)**

Int32

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

Int32(196608)

Номер версии протокола. В старших 16 битах задается старший номер версии, в младших 16 битах — младший.

За номером версии протокола следует одна или несколько пар из имени параметра и строки его значения. За последней парой имя/значение должен стоять завершающий нулевой байт. Параметры могут отображаться в любом порядке. Обязательным является только параметр user, остальные могут отсутствовать. Каждый параметр задается так:

String

Имя параметра. В настоящее время принимаются имена:

user

Имя пользователя базы данных, с которым выполняется подключение. Является обязательным; значения по умолчанию нет.

database

База данных, к которой выполняется подключение. По умолчанию используется имя пользователя.

options

Аргументы командной строки для обслуживающего процесса. (Этот способ не рекомендуется; лучше устанавливать отдельные параметры времени выполнения.) Пробелы в этой строке воспринимаются как отдельные аргументы, если не экранировать их обратным слэшем (\); чтобы представить обратный слэш буквально, нужно его продублировать (\\).

replication

Используется для подключения в режиме потоковой репликации, где вместо операторов SQL может выполняться небольшой набор команд репликации. Допустимые значение: true, false (по умолчанию) и database.

В дополнение к вышеуказанным могут задаваться и другие параметры. Имена параметров, начинающиеся с _pq_., зарезервированы для использования в качестве расширений протокола, а остальные воспринимаются как параметры времени выполнения, которые задаются во время запуска сервера. Такие параметры будут применяться при запуске обслуживающего процесса (после разбора аргументов командной строки, если таковые имеются) и будут действовать как параметры сеанса по умолчанию.

String

Значение параметра.

Sync (F)

Byte1('S')

Определяет, что сообщение содержит команду Sync.

Int32(4)

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

Terminate (F)

Byte1('X')

Определяет, что сообщение завершает сеанс.

Int32(4)

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

Поля сообщений с ошибками и замечаниями

В этом разделе описываются поля, которые могут содержаться в сообщениях ErrorResponse и NoticeResponse. У каждого типа поля имеется свой однобайтовый идентификационный маркер. Обратите внимание, что поле любого из этих типов должно содержаться в сообщении не более одного раза.

S

Важность: это поле содержит ERROR, FATAL или PANIC (в сообщении об ошибке), либо WARNING, NOTICE, DEBUG, INFO или LOG (в сообщении с замечанием), либо их переведенные значения (ОШИБКА, ФАТАЛЬНО, ПАНИКА, ПРЕДУПРЕЖДЕНИЕ, ЗАМЕЧАНИЕ, ОТЛАДКА, ИНФОРМАЦИЯ, СООБЩЕНИЕ соответственно). Присутствует всегда.

V

Важность: это поле содержит ERROR, FATAL или PANIC (в сообщении об ошибке), либо WARNING, NOTICE, DEBUG, INFO или LOG (в сообщении с замечанием). Оно подобно полю S, но его содержимое никогда не переводится. Присутствует всегда.

C

Код: код SQLSTATE выданной ошибки (см. раздел Коды ошибок QHB). Не переводится. Присутствует всегда.

M

Сообщение: основное сообщение об ошибке, удобное для восприятия человеком. Оно должно быть точным, но кратким (обычно в одну строку). Присутствует всегда.

D

Детализация: необязательное дополнительное сообщение об ошибке, содержащее более детальную информацию о проблеме. Может состоять из нескольких строк.

H

Подсказка: необязательное предложение решения проблемы. Оно должно отличаться от сообщения Detail тем, что предлагает совет (не исключено, что неподходящий), а не голые факты. Может состоять из нескольких строк.

P

Позиция: значение этого поля является целочисленным числом в ASCII, указывающим на положение курсора ошибки как на индекс в исходной строке запроса. Индекс первого символа — 1, при этом позиции отсчитываются по символам, а не по байтам.

p

Внутренняя позиция: она определяется так же, как поле P, но применяется, когда положение курсора относится ко внутренне сгенерированной команде, а не к команде, отправленной клиентом. Вместе с этим полем всегда присутствует поле q.

q

Внутренний запрос: текст внутренне сгенерированной команды, в которой произошла ошибка. Это может быть, например, запрос SQL, выполняемый функцией на PL/pgSQL.

W

Где: указание на контекст, в котором произошла ошибка. В настоящее время включает обратную трассировку стека текущих функций на процедурном языке и внутренне сгенерированных запросов. Записи трассировки разделяются по строкам, вначале идет последняя.

s

Имя схемы: если ошибка связана с определенным объектом базы данных, в этом поле указывается имя схемы, содержащей этот объект, если таковая имеется.

t

Имя таблицы: если ошибка связана с определенной таблицей, в этом поле указывается имя этой таблицы. (Узнать имя схемы таблицы можно, обратившись к соответствующему полю.)

c

Имя столбца: если ошибка связана с определенным столбцом таблицы, в этом поле указывается имя этого столбца. (Идентифицировать таблицу можно, обратившись к полям, содержащим имя схемы и таблицы.)

d

Имя типа данных: если ошибка связана с определенным типом данных, в этом поле указывается имя этого типа данных. (Узнать имя схемы типа данных можно, обратившись к соответствующему полю.)

n

Имя ограничения: если ошибка связана с определенным ограничением, в этом поле указывается имя этого ограничения. Узнать, к какой таблице или домену оно относится, можно, обратившись к полям, описанным выше. (В данном случае индексы считаются ограничениями, даже если они были созданы не с синтаксисом ограничений.)

F

Файл: имя файла в исходном коде, в котором была обнаружена ошибка.

L

Строка: номер строки в исходном коде, в которой была обнаружена ошибка.

R

Программа: имя программы в исходном коде, в которой была обнаружена ошибка.

Примечание
Поля, содержащие имена схемы, таблицы, столбца, типа данных и ограничения, выдаются только для ограниченного числа типов ошибок; см. раздел Коды ошибок QHB. Клиенты не должны рассчитывать на то, что присутствие одного из этих полей гарантирует присутствие другого поля. Системные источники ошибок устанавливают связь между ними, но пользовательские функции могут использовать эти поля по-другому. Аналогичным образом, клиенты не должны рассчитывать, что эти поля указывают на актуальные объекты в текущей базе данных.

Клиент отвечает за форматирование отображаемой информации в соответствии со своими нуждами; в частности, он должен разбивать длинные строки, как требуется. Символы новой строки, встречающиеся в полях сообщения об ошибке, должны считаться разрывами абзацев, а не строк.

Расширения

Расширения QHB

Расширение Rbytea, внешнее хранение двоичных данных

Расширение Qbim, реализующее битемпоральную модель данных

Элементарный пример для Qbim

Пример использования Qbim со связанными объектами

Расширение Uref, обобщённый ссылочный тип

Расширение Qbayes, методы статистического анализа на основе условных вероятностей Байеса

Расширение Part_Import, частичный импорт файлов данных из «внешней» БД

Специализированные и экспериментальные модули PostgreSQL

adminpack

amcheck

auth_delay

auto_explain

bloom

btree_gin

btree_gist

citext

cube

dblink

dict_int

dict_xsyn

earthdistance

file_fdw

fuzzystrmatch

hstore

intagg

intarray

isn

lo

ltree

old_snapshot

pageinspect

passwordcheck

pg_buffercache

pgcrypto

pg_freespacemap

pg_prewarm

pgrowlocks

pg_stat_statements

pgstattuple

pg_surgery

pg_trgm

pg_visibility

postgres_fdw

seg

sepgsql

spi

sslinfo

tablefunc

tcn

test_decoding

tsm_system_rows

tsm_system_time

unaccent

uuid-ossp

xml2

Клиентские приложения

oid2name

vacuumlo

Сторонние расширения

PostGIS (Spatial and Geographic Objects for PostgreSQL)

Patroni (Управление высокодоступными кластерами)

В данном разделе содержится информация о модулях, которые можно найти в каталоге share/extension дистрибутива QHB. В их число входят средства портирования, утилиты анализа и подключаемые функции, не включенные в состав основной системы QHB, в основном потому что они предназначены для ограниченной аудитории или чересчур экспериментальны, чтобы быть частью основного дерева кода. Тем не менее, это не отменяет их полезность.

В этом приложении рассматриваются расширения и другие подключаемые серверные модули, находящиеся в пакете qhb-contrib, который необходимо установить в соответствии с описанием по установке.

Многие модули предоставляют новые пользовательские функции, операторы и типы. Чтобы использовать один из этих модулей после установки его кода, вы должны зарегистрировать новые объекты SQL в СУБД. Для этого нужно выполнить команду CREATE EXTENSION. В чистой базе данных можно просто выполнить

CREATE EXTENSION имя_модуля;

Эта команда регистрирует новые объекты SQL только в текущей базе данных, поэтому ее необходимо выполнять в каждой базе данных, в которой вам требуется функциональность этого модуля. Как вариант, эту команду можно запустить в базе-шаблоне template1, чтобы нужное расширение копировалось во все создаваемые впоследствии базы данных по умолчанию.

Для всех этих модулей CREATE EXTENSION должен выполнять суперпользователь, если только модуль не рассматривается как «доверенный» — тогда его может устанавливать любой пользователь, имеющий право CREATE в текущей базе данных. В разделах, где описываются модули, отмечено, какие из них являются доверенными. В целом, доверенными модулями считаются те, которые не предоставляют доступ к функциональности за рамками базы данных.

Многие модули позволяют устанавливать свои объекты в схему по выбору. Для этого нужно добавить SCHEMA имя_схемы в команду CREATE EXTENSION. По умолчанию объекты устанавливаются в текущую схему для создаваемых объектов, которой, в свою очередь, по умолчанию становится public.

Однако обратите внимание, что некоторые из этих модулей не являются «расширениями» в этом смысле, а загружаются на сервер по-другому, например, через параметр shared_preload_libraries. Более подробную информацию можно найти в документации каждого модуля.

Расширения QHB

В данном разделе представлены расширения специально разработанные для QHB.

Расширение Rbytea, внешнее хранение двоичных данных

Расширение Qbim, реализующее битемпоральную модель данных

Элементарный пример для Qbim

Пример использования Qbim со связанными объектами

Расширение Uref, обобщённый ссылочный тип

Расширение Qbayes, методы статистического анализа на основе условных вероятностей Байеса

Расширение Part_Import, частичный импорт файлов данных из «внешней» БД

Расширение Rbytea, внешнее хранение двоичных данных

Тип данных rbytea

Тип данных rbytea предназначен для хранения двоичных данных. Он аналогичен типу bytea, с той лишь разницей, что сами данные хранятся не в табличном пространстве базы, а во внешнем хранилище. В качестве внешнего хранилища в текущей версии QHB выступает файловая система. Это может быть смонтированный в определенную точку файловой системы сервера внешний том или символическая ссылка.

Основной целью расширения является вынос двоичных данных из таблиц базы данных в нетранзакционное хранилище, тем самым разгрузив базу данных (зачастую двоичные данные имеют большой объем, который занимает значительный процент от общего размера базы, усложняя администрирование и обслуживание).

Тип данных rbytea в записи базы данных оставляет только небольшой заголовок, в котором содержатся служебные поля и ссылка на файл во внешнем хранилище. В качестве ссылки используется тип данных uuid, а генерация случайных идентификаторов использует возможности модуля pgcrypto.

Данные во внешнем хранилище могут быть зашифрованы алгоритмом Кузнечик.

Установка расширения

Установка расширения производится командой CREATE EXTENSION:

CREATE EXTENSION rbytea CASCADE;

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

shared_preload_libraries = 'librbytea'

Описание параметров расширения приведены ниже в разделе Параметры конфигурации расширения.

Функции для работы с типом rbytea

Имя | Тип результата | Описание
uuid(col rbytea) | uuid | Получить идентификатор данных
len(col rbytea) | bigint | Получить длину данных в байтах
len_full(col rbytea) | bigint | Получить длину в байтах данных с учетом выравнивания для зашифрования
qss_mode(col rbytea) | bigint | Возвращает признак зашифрования данных (1) или его отсутствия (0)
md5sum(col rbytea) | text | Возвращает md5 сумму данных
sha256sum(col rbytea) | text | Возвращает sha256 сумму данных
md5store(col rbytea) | text | Возвращает md5 сумму зашифрованных данных во внешнем хранилище
sha256store(col rbytea) | text | Возвращает sha256 сумму зашифрованных данных во внешнем хранилище
rvacuum() | bigint | Выполняет очистку устаревших данных в хранилище

Фоновый процесс очистки устаревших копий

Поскольку на файловую систему не распространяется транзакционность базы данных, во внешнем хранилище могут оставаться данные полей таблиц типа rbytea, которые были удалены или сохранены в незаконченных, отмененных транзакциях. Поэтому периодически запускается фоновый процесс очистки, который проходит по некоторому диапазону транзакций, очищая данные. При этом файлы перемещаются в каталог TRASH, который создается для каждой базы данных, указанной в параметре rbytea.databases_for_vacuuming.

После каждого запуска максимальный номер транзакции запоминается и при следующем запуске используется как нижняя граница диапазона сканирования. В качестве верхней границы диапазона сканирования используется последняя завершенная транзакция.

Параметры запуска указаны в разделе ниже.

Параметры конфигурации расширения

Для работы расширений нужно установить несколько параметров в конфигурационном файле:

rbytea.filesystem_storage_path (string)

Определение каталога (точки монтирования файловой системы / тома) для сохранения образов данных.

Двоичные данные будут сохраняться в данном каталоге сервера. Для каждой базы будет создаваться свой подкаталог (по oid базы), а внутри него, во множестве подкаталогов, — собственно файлы с данными.

Имена вложенных каталогов от каталога базы данных до файла будут составлять uuid типа rbytea, а расширение файла — номер транзакции, в которой данные впервые появились в системе.

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

По умолчанию, если параметр опущен или пуст, каталогом для сохранения двоичных данных назначается '<каталог базы данных>/rbytea'.

shared_preload_libraries (string)

Загрузка разделяемой библиотеки при старте QHB.

Данный параметр обеспечивает загрузку разделяемой библиотеки при старте QHB и инициализацию фонового процесса для очистки устаревших образов. В противном случае автоочистка устаревших образов производится не будет.

Необходимо добавить значение 'librbytea'.

Примечание
Если параметр shared_preload_libraries уже содержит указание на загрузку других библиотек, нужно не перезаписать его значение, а добавить через разделитель librbytea.

rbytea.worker_restart_time (integer)

Интервал запуска фонового процесса очистки.

Фоновый процесс не работает постоянно. Поскольку данные

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

По умолчанию параметр устанавливается в значение 86400 секунд (сутки).

rbytea.databases_for_vacuuming (string)

Задание баз данных для фонового процесса очистки.

Параметр указывает, какие базы данных подлежат очистке. Указываются названия баз данных через запятую. Главный процесс базы данных будет запускать столько фоновых процессов очистки, сколько баз данных перечислено в данном параметре.

По умолчанию, параметр устанавливается в значение qhb.

rbytea.filesystem_qss_mode (integer)

Включение фонового шифрования.

Если в системе доступно фоновое шифрование при записи на диск, данный параметр позволяет зашифровывать также и двоичные данные rbytea. Для шифрования параметр необходимо установить в значение 1.

При зашифровании данные дополняются (выравниваются) до границы 16-байтных блоков, поэтому функции len(rbytea) и len_full(rbytea) для одних и тех же данных могут возвращать разное значение. А функции md5store(rbytea) и sha256store(rbytea) подсчитывают контрольные суммы для зашифрованных данных, дополненных до границы 16-байтного блока.

Вопросы смены ключа шифрования должны решаться администратором базы данных.

По умолчанию этот параметр устанавливается в значение 0.

Расширение Qbim, пример реализации битемпоральной модели данных

Примеры:

Элементарный пример для Qbim

Пример использования Qbim со связанными объектами

Предпосылки

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

Шестая нормальная форма введена Кристофером Дейтом как обобщение пятой нормальной формы для хронологической базы данных в своей книге Введение в системы баз данных.

Расширение Qbim представляет собой конструктор для разработки ERM на темпоральной основе, то есть, моделируемые объекты представляются временными срезами, последовательностями своих состояний во времени. Основное внимание и акцент в данном расширении сделан не на "декомпозиции до конца", а на поддержании темпоральности данных. Расширение реализует краткий набор логики хранения (создания, изменения) объектов в реляционной СУБД (на примере qhb), реализованный в слое хранимых процедур.

Модель данных

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

В предлагаемой реализации наряду с идентификатором кортежа данных указывается еще и интервал времени, в течение которого эти данные считаются актуальными. Эти интервалы могут относиться или пересекаться с настоящим временем, находиться в прошлом или описывать будущие значения. Интервалы хранятся в двух специально выделенных для этого полях типа timestamp, образующих полузакрытый интервал диапазонного типа tsrange.

Серия данных с одним идентификатором (далее объект), может образовывать непересекающиеся отрезки, или «версии», на временной оси «действия» этих данных (business time).

Дополнительная временная ось (system time) фиксирует, когда эти версии были созданы или изменены.

Реализация модели

Терминология

Термин
Определение

Объект
Некоторая оцифрованная, смоделированная из реального мира сущность. Обладает свойствами (атрибутами) и операциями над ними (методами). В определённые временные интервалы имеет фиксированный набор атрибутов. Но во времени, от интервала к интервалу, может менять набор атрибутов. В базе данных набор Объектов представляется сущностью/таблицей, его атрибуты представлены полями таблицы, методы реализованы хранимыми процедурами и функциями.

Реализация объекта
Один из представителей объекта, выделяемый среди других с помощью идентификатора. В базе данных представляет собой серию записей одной таблицы/сущности.

Версия реализации объекта
Один из наборов атрибутов объекта, постоянный в течении определенного временного интервала. В базе данных - одна из записей таблицы/сущности, принадлежащая объекту (по идентификатору), с определенным интервалом действия.

Идентификатор реализации объекта
Идентификатор (id - обычно целое число, но может быть UUID-ом), однозначно определяющий реализацию объекта среди других объектов сущности. Все версии одного и того же объекта имеют один и тот же идентификатор.

Идентификатор версии реализации объекта
Кортеж из идентификатора и интервала действия версии ( <id, [sd, ed)>), однозначно определяющий объект и версию.

Ограничения и умолчания

Зарезервированные поля

Для идентификатора и границ интервала действия в версионированных таблицах резервируются три поля:

Резервируемое поле | Тип | Назначение
id | bigint или uuid | идентификатор
sd | timestamp | start date, начало периода действия
ed | timestamp | end date, окончание периода действия

Далее для bigint может использоваться термин «локальный идентификатор», а для uuid — «глобальный идентификатор».

Любой версионированный объект представляется набором своих версий реализации, связанных уникальным идентификатором реализации (id) и набором атрибутов / свойств. Каждая его версия - кортежем id и периодом действия этой версии (период представляется интервалом [sd, ed) - включается левая граница и не включается правая, типы границ - timestamp).

Первичный ключ версии объекта - составной: <id,[sd,ed)>.

Версии не пересекаются и не содержат разрывов

Интервалы действия версий образуются как tsrange(sd,ed,'[)') — полуоткрытый интервал. Отсутствие пересечений интервалов реализуется ограничением вида:

constraint ... exclude using gist (id with =, tsrange(sd, ed) with &&) where ((sd <> ed))

Следует заметить, что при таких условиях допустимыми являются версии, в которых sd == ed.

Это допущение сделано намеренно, поскольку:

такие версии имеют нулевую длину, не пересекаются с другими версиями, не влияют на многие сценарии выбора версий и серий версий (непустых);

при наличии фиксации времени изменения (system time) не пропадает и не требует изменения привязка к версии события (поле action.link_dt), поскольку привязка осуществляется именно к значению поля sd версионированной таблицы.

Описание версионируемых таблиц

Все версионируемые таблицы и их поля описываются в специально выделяемых таблицах obj и obj_item.

Таблица obj:

Поле | Тип | Назначение
id | bigserial | Идентификатор
tab | text | Название таблицы, где хранится объект. Если требуется, вместе с именем схемы
dsc | text | Описание
parent | bigint | Ссылка на ID верхнего в иерархии объекта
is_temporal | bool | Признак темпоральности объекта
is_logging | bool | Признак логирования действий пользователя для объекта
is_global | bool | Признак глобального идентификатора у объекта. Для глобальных идентификаторов используется тип UUID, для локальных — bigint

Таблица obj_item:

Поле | Тип | Назначение
id | bigserial | Идентификатор
up | bigint | Ссылка на объект (obj.id)
ord | bigint | Порядковый номер атрибута
name | text | Имя
r_cls | bigint | Если имеется словарное поле, номер словаря (@CLS.ID)
r_obj | bigint | Если имеется ссылка на объект, номер объекта
option | jsonb | Дополнительные параметры. Например, указание о замене типа на массив для методов
dsc | text | Описание атрибута

Таблица action:

Поле | Тип | Назначение
id | bigserial | Идентификатор
r_object | uref | Ссылка на изменяемый объект и его идентификатор
lnk_dt | timestamp | Дата привязки к версии реализации события (SD)
fct_dt | timestamp | Реальная дата версии (по началу транзакции)
action | bigint | Произведенное действие. Словарь
database | text | База данных, где расположен измененный объект
schema | text | Схема, где расположен измененный объект
dbuser | text | Пользователь базы данных, совершивший действие
pid | text | Идентификатор процесса (Process ID), в рамках работы которого произведено изменение
userid | uuid | UUID пользователя, вошедшего в систему и совершившего действие

В таблицу action, если указано в obj.is_logging, записываются основания действий с объектами.

«Действие» (поле action) имеет следующую кодировку:

Код | Действие
1 | Создание
2 | Изменение (при создании новой версии)
3 | Изменение (при сокращении периода действия)
4 | Закрытие версии

Важно!
Для функционирования приложения достаточно важно, чтобы записи в эту таблицу записывались максимально быстро. Для этого есть возможность создавать таблицу как unlogged, а при включенном TARQ — также ... using append_only.

Методы работы с объектами

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

Методы выборки оставляются на выбор пользователя или приложения. Возможно выбрать конкретную версию, действующую в указанный момент времени, или серии версий за указанный период вместе со схлопнувшимися версиями (промежуточные версии с нулевой длительностью, sd == ed) или без них. Также возможны различные выборки совместно со связанными объектами.

Методы создания объекта

Для создания объекта (начальной версии объекта) предоставляются методы qbim_create():

CREATE FUNCTION qbim_create(
  obj_id int8, tab text,
  id int8,
  sd timestamp, ed timestamp,
  userid uuid,
  variadic others_args "any")
returns int8 as 'MODULE_PATHNAME','create_local_reg' language c volatile security definer;

CREATE FUNCTION qbim_create(
  obj_id int8, tab text,
  id uuid,
  sd timestamp, ed timestamp,
  userid uuid,
  variadic others_args "any")
returns uuid as 'MODULE_PATHNAME','create_global_reg' language c volatile security definer;

В одном из первых двух параметрах метода указывается идентификатор создаваемого объекта: по идентификатору в таблице obj описателя или по имени объекта (таблицы); возможно указание и идентификатора, и имени, но они должны соответствовать описателю.

В зависимости от типа идентификатора, используемого в объекте, третий параметр может иметь тип bigint или uuid; его пропуск (указание null::bigint или null::uuid) требует автоматической генерации идентификатора. В случае bigint значение может выбираться из соответствующей последовательности, а в случае uuid — генерируется случайное значение uuid.

Следующая пара параметров задает интервал действия версии, при пропуске (указании null:timestamp) используются значения по умолчанию, указанные при создании таблицы. Как правило, для sd это текущее время (CURRENT_TIMESTAMP), а для ed — «бесконечность» (infintity::timestamp).

В параметре userid можно указать идентификатор конечного пользователя, производящего операцию создания объекта, если таковой используется в приложении. Если не используется, можно пропустить (указать null::uuid).

Далее в переменном списке параметров (variadic "any") следует передавать параметры парами — имя и значение, или не передавать вовсе. В любом случае количество переменных должно быть четным. Атрибуты объекта, пропущенные в этом списке, получат значения NULL или будут установлены в соответствии с умолчаниями, заданными при создании таблицы.

При создании объекта, если это указано в obj, будет зафиксировано действие в таблице action.

Методы изменения объекта (создания версии)

Для изменения объекта предоставляются методы qbim_change(). Создание версии возможно в любой период, пересекающий периоды имеющихся у объекта версий (не должно получаться разрывов).

Параметры аналогичны методам qbim_create(). Умолчания для sd и ed — текущее время и «бесконечность» соответственно.

Новая версия может дополнять (с любой стороны) имеющиеся, пересекать любое количество имеющихся версий, покрывать полностью или частично уже имеющиеся версии.

В случае, если в переменном списке параметров опущено какое-то из полей, оно будет подставлено из соответствующей более ранней версии объекта, подходящей по времени.

Если в результате изменения какие-то из имеющихся версий будут полностью перекрыты новой версией, они будут считаться схлопнувшимися (sd == ed).

Методы qbim_change():

CREATE FUNCTION qbim_change(
  obj_id int8, tab text,
  id int8,
  sd timestamp, ed timestamp,
  userid uuid,
  variadic others_args "any")
returns int8 as 'MODULE_PATHNAME','change_local_reg' language c volatile security definer;

CREATE FUNCTION qbim_change(
  obj_id int8, tab text,
  id uuid,
  sd timestamp, ed timestamp,
  userid uuid,
  variadic others_args "any")
returns int8 as 'MODULE_PATHNAME','change_global_reg' language c volatile security definer;

При изменении объекта, если это указано в obj, будет зафиксировано действие в таблице action.

Методы закрытия объекта (версии)

Пара методов, закрывающих версии объекта текущей или указанной датой:

CREATE FUNCTION qbim_close(
  obj_id int8, tab text,
  id int8,
  ed timestamp,
  userid uuid)
returns int8 as 'MODULE_PATHNAME','close_local' language c volatile security definer;

CREATE FUNCTION qbim_close(
  obj_id int8, tab text,
  id uuid,
  ed timestamp,
  userid uuid)
returns int8 as 'MODULE_PATHNAME','close_global' language c volatile security definer;

В параметре ed указывается необходимое время для закрытия версий. Если пропущено (указано null::timestamp), то устанавливается текущее время. Остальные параметры аналогичны параметрам в методах qbim_create и qbim_change.

При закрытии объекта, если это указано в obj, будет зафиксировано действие в таблице action.

Пример использования Qbim для тестового объекта

Установка расширения

CREATE EXTENSION qbim CASCADE;

Создание тестового объекта

CREATE TABLE test (
	id BIGSERIAL NOT NULL,
	sd TIMESTAMP NOT NULL DEFAULT current_timestamp,
	ed TIMESTAMP NOT NULL DEFAULT 'infinity'::TIMESTAMP WITHOUT TIME ZONE,
	name TEXT NOT NULL,
	state BIGINT NOT NULL,
	val BIGINT NOT NULL,
	dsc TEXT NOT NULL,
	CONSTRAINT cls_id_sd_ed_excl exclude USING gist (id WITH =, tsrange(sd, ed) WITH &&) WHERE ((sd <> ed))
);
CREATE INDEX test_id_sd_ed_excl ON test USING gist (id, tsrange(sd, ed)) WHERE (sd <> ed);

Описание в таблицах описания объектов

INSERT INTO obj (tab,dsc,parent,is_temporal,is_logging,is_global) VALUES
	 ('test','Тестовый объект',NULL,true,true,false);

INSERT INTO obj_item (up,ord,"name",r_cls,r_obj,"option",dsc) VALUES
	 (1,1,'id',NULL,NULL,NULL,'идентификатор'),
	 (1,2,'sd',NULL,NULL,NULL,'время начала действия версии'),
	 (1,3,'ed',NULL,NULL,NULL,'время окончания действия версии'),
	 (1,4,'name',NULL,NULL,NULL,'название'),
	 (1,5,'state',NULL,NULL,NULL,'состояние объекта'),
	 (1,6,'val',NULL,NULL,NULL,'значение'),
	 (1,7,'dsc',NULL,NULL,NULL,'комментарий');

Создание реализации объекта

Примечание
Здесь и далее при вызове интерфейсных методов указывается случайный идентификатор пользователя '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid, который используется при регистрации действия.

SELECT qbim_create(1::bigint,null::text,null::bigint,
  '-infinity'::timestamp,null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'name','Test record'::text,
  'state',1::bigint,
  'val',5::bigint
);

Изменение объекта - создание версий

SELECT qbim_change(1::bigint,null::text,1::bigint,
  '2021-03-30 13:35:30.0'::timestamp,null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'state',2::bigint
);

SELECT qbim_change(1::bigint,null::text,1::bigint,
  '2021-03-30 13:36:30.0'::timestamp,null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'state',3::bigint
);

SELECT qbim_change(1::bigint,null::text,1::bigint,
  '2021-03-30 13:35:30.0'::timestamp,'2021-03-30 13:36:00.0'::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'val',6::bigint
);

SELECT qbim_change(1::bigint,null::text,1::bigint,
  '2021-03-30 13:35:20.0'::timestamp,'2021-03-30 13:36:15.0'::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'val',7::bigint
);

SELECT qbim_change(1::bigint,null::text,1::bigint,
  '2021-03-30 13:36:00.0'::timestamp,null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'val',9::bigint
);

SELECT qbim_change(1::bigint,null::text,1::bigint,
  '2021-03-30 13:35:20.0'::timestamp,'2021-03-30 13:36:15.0'::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'val',10::bigint,
  'state', 6::bigint
);

SELECT qbim_change(1::bigint,null::text,1::bigint,
  '2021-03-30 13:35:20.0'::timestamp,'2021-03-30 13:36:30.0'::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'val',11::bigint,
  'state', 7::bigint
);

Выборка, просмотр версий

Можно запускать на разных этапах изменения объекта.

Запрос версии «на момент времени»

SELECT * FROM test
WHERE '2021-03-30 13:35:45.0'::timestamp <@ tsrange(sd,ed)
ORDER BY id, sd, ed;

Запрос всех версий, кроме «схлопнувшихся»

SELECT * FROM test WHERE sd<>ed
ORDER BY id, sd, ed;

Запрос всех версий

SELECT * FROM test
ORDER BY id, sd, ed;

Закрытие версии

SELECT qbim_close(1::bigint,null::text,1::bigint,
  '2021-03-30 13:35:15.0'::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid
);

Регистрация действий

SELECT
    r_object, lnk_dt, fct_dt, action, pid, userid
FROM action;

r_object | lnk_dt | fct_dt | action | pid | userid
1:1 | -infinity | 2021-05-05 11:47:43.957595 | 1 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | -infinity | 2021-05-05 11:48:08.117459 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:30 | 2021-05-05 11:48:08.117649 | 2 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:30 | 2021-05-05 11:48:09.595363 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:36:30 | 2021-05-05 11:48:09.596457 | 2 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | -infinity | 2021-05-05 11:48:10.988799 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:30 | 2021-05-05 11:48:10.989053 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:30 | 2021-05-05 11:48:10.989264 | 2 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | -infinity | 2021-05-05 11:48:12.515611 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:30 | 2021-05-05 11:48:12.516679 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:36:00 | 2021-05-05 11:48:12.517692 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:20 | 2021-05-05 11:48:12.518829 | 2 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:30 | 2021-05-05 11:48:12.519755 | 2 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:30 | 2021-05-05 11:48:15.094103 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:36:15 | 2021-05-05 11:48:15.094302 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:36:30 | 2021-05-05 11:48:15.094489 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:36:00 | 2021-05-05 11:48:15.094663 | 2 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:36:30 | 2021-05-05 11:48:15.094829 | 2 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | -infinity | 2021-05-05 11:48:17.110891 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:20 | 2021-05-05 11:48:17.111094 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:30 | 2021-05-05 11:48:17.111286 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:36:00 | 2021-05-05 11:48:17.111476 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:20 | 2021-05-05 11:48:17.111692 | 2 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | -infinity | 2021-05-05 11:48:19.36584 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:20 | 2021-05-05 11:48:19.366077 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:36:15 | 2021-05-05 11:48:19.366265 | 3 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:20 | 2021-05-05 11:48:19.366443 | 2 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-03-30 13:35:15 | 2021-05-05 11:51:57.079878 | 4 | 2522 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a

Значения полей описаны в разделе Описание версионируемых таблиц.

Примечание
Поля fct_dt и pid могут отличаться; в данной таблице они приведены как пример.

Пример использования Qbim со связанными объектами

Установка расширения

CREATE EXTENSION qbim CASCADE;

Создание тестовых объектов

В данном примере рассматривается реализация с двумя объектами, связанными между собой.

Для описания связи будет использоваться составной первичный ключ <id, sd, ed> в одной таблице, и «тройка» <r_position, sd, ed> во второй. Записи (версии) будут считаться связанными, если первое поле в указанных тройках совпадает, а интервалы, создаваемые оставшимися двумя значениями, пересекаются.

Механизмы описания связей (foreing key) в темпоральной парадигме пока не позволяют сделать это на уровне задания ограничений таблиц.

Примечание
Но такое задание связи, вероятно, будет реализовано в следующих версиях QHB.

Моделируется предметная область — кадровый учет. Создаются два объекта: «персона» и «должность». Для демонстрации возможностей Qbim атрибутивный состав объектов сокращен.

CREATE TABLE person (
	id BIGSERIAL NOT NULL,
	sd TIMESTAMP NOT NULL DEFAULT current_timestamp,
	ed TIMESTAMP NOT NULL DEFAULT 'infinity'::TIMESTAMP WITHOUT TIME ZONE,
	last_name VARCHAR(50) NOT NULL,
	first_name VARCHAR(50) NOT NULL,
	patronym VARCHAR(50),
	r_position BIGINT NOT NULL,
	CONSTRAINT person_id_sd_ed_excl exclude USING gist (id WITH =, tsrange(sd, ed) WITH &&) WHERE ((sd <> ed))
);

CREATE TABLE position (
	id BIGSERIAL NOT NULL,
	sd TIMESTAMP NOT NULL DEFAULT current_timestamp,
	ed TIMESTAMP NOT NULL DEFAULT 'infinity'::TIMESTAMP WITHOUT TIME ZONE,
	position_name VARCHAR(100) NOT NULL,
	CONSTRAINT position_id_sd_ed_excl exclude USING gist (id WITH =, tsrange(sd, ed) WITH &&) WHERE ((sd <> ed))
);

Описание в таблицах описания объектов

INSERT INTO obj (id,tab,dsc,parent,is_temporal,is_logging,is_global) VALUES
	 (1,'person','Персона',NULL,true,true,false);
INSERT INTO obj (id,tab,dsc,parent,is_temporal,is_logging,is_global) VALUES
	 (2,'position','Должность',NULL,true,true,false);

INSERT INTO obj_item (up,ord,"name",dsc) VALUES
	 (1,1,'id','идентификатор'),
	 (1,2,'sd','время начала действия версии'),
	 (1,3,'ed','время окончания действия версии'),
	 (1,4,'last_name','Фамилия'),
	 (1,5,'first_name','Имя'),
	 (1,6,'patronym','Отчество'),
	 (1,7,'r_position','Ссылка на должность');
INSERT INTO obj_item (up,ord,"name",dsc) VALUES
	 (2,1,'id','идентификатор'),
	 (2,2,'sd','время начала действия версии'),
	 (2,3,'ed','время окончания действия версии'),
	 (2,4,'position_name','Название доллжности');

Создание реализаций объектов

Примечание
Здесь и далее при вызове интерфейсных методов указывается случайный идентификатор пользователя '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid, который используется при регистрации действия.

Должности. Создаем три должности с 1 января 2021 года:

SELECT qbim_create(2::bigint, null::text, null::bigint,
  '2021-01-01 00:00:00.0'::timestamp, null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'position_name','Инженер');

SELECT qbim_create(2::bigint, null::text, null::bigint,
  '2021-01-01 00:00:00.0'::timestamp, null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'position_name','Старший инженер');

SELECT qbim_create(2::bigint, null::text, null::bigint,
  '2021-01-01 00:00:00.0'::timestamp, null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'position_name','Менеджер');

Сотрудники. Создаем сотрудников с даты их фактического приема в штат, назначения на конкретные должности:

SELECT qbim_create(1::bigint, null::text, null::bigint,
  '2021-01-11 00:00:00.0'::timestamp, null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'last_name','Начальников',
  'first_name','Игорь',
  'patronym','Петрович',
  'r_position',3);
SELECT qbim_create(1::bigint, null::text, null::bigint,
  '2021-01-12 00:00:00.0'::timestamp, null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'last_name','Половинкин',
  'first_name','Семен',
  'patronym','Андреевич',
  'r_position',2);
SELECT qbim_create(1::bigint, null::text, null::bigint,
  '2021-01-15 00:00:00.0'::timestamp, null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'last_name','Иванов',
  'first_name','Петр',
  'patronym','Васильевич',
  'r_position',1);
SELECT qbim_create(1::bigint, null::text, null::bigint,
  '2021-01-16 00:00:00.0'::timestamp, null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'last_name','Малкин',
  'first_name','Иван',
  'patronym','Олегович',
  'r_position',2);
SELECT qbim_create(1::bigint, null::text, null::bigint,
  '2021-01-17 00:00:00.0'::timestamp, null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'last_name','Селиванов',
  'first_name','Илья',
  'patronym','Максимович',
  'r_position',1);
SELECT qbim_create(1::bigint, null::text, null::bigint,
  '2021-01-18 00:00:00.0'::timestamp, null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'last_name','Васин',
  'first_name','Сергей',
  'patronym','Петрович',
  'r_position',1);

Выведем обобщенную информацию по подразделению. В качестве интервалов выводим минимальные/максимальные границы интервалов. Условие tsrange(p.sd,p.ed) && tsrange(d.sd,d.ed) означает пересечение интервалов [p.sd,p.ed) и [d.sd,d.ed) соответственно.

Запрос обобщенной информации

SELECT
	p.id,
	greatest(p.sd,d.sd) AS sd,
	least(p.ed,d.ed) AS ed,
	p.last_name||' '||p.first_name||' '||p.patronym AS "ФИО",
	d.position_name AS "Должность"
FROM person p JOIN position d
	ON p.r_position = d.id AND tsrange(p.sd,p.ed) && tsrange(d.sd,d.ed)
ORDER BY 1,2;

Результат:

id | sd | ed | ФИО | Должность
1 | 2021-01-11 00:00:00 | infinity | Начальников Игорь Петрович | Менеджер
2 | 2021-01-12 00:00:00 | infinity | Половинкин Семен Андреевич | Старший инженер
3 | 2021-01-15 00:00:00 | infinity | Иванов Петр Васильевич | Инженер
4 | 2021-01-16 00:00:00 | infinity | Малкин Иван Олегович | Старший инженер
5 | 2021-01-17 00:00:00 | infinity | Селиванов Илья Максимович | Инженер
6 | 2021-01-18 00:00:00 | infinity | Васин Сергей Петрович | Инженер

Запрос информации "на дату"

Условие '2021-02-15 00:00:00.0'::timestamp <@ tsrange(t.sd,t.ed) означает, что указанный момент времени должен содержаться внутри интервала.

SELECT * FROM (
	SELECT
		p.id,
		greatest(p.sd,d.sd) AS sd,
		least(p.ed,d.ed) AS ed,
		p.last_name||' '||p.first_name||' '||p.patronym AS "ФИО",
		d.position_name AS "Должность"
	FROM person p JOIN position d
		ON p.r_position = d.id AND tsrange(p.sd,p.ed) && tsrange(d.sd,d.ed)
) t
WHERE '2021-02-15 00:00:00.0'::timestamp <@ tsrange(t.sd,t.ed)
ORDER BY 1,2;

Результат выглядит одинаково:

id | sd | ed | ФИО | Должность
1 | 2021-01-11 00:00:00 | infinity | Начальников Игорь Петрович | Менеджер
2 | 2021-01-12 00:00:00 | infinity | Половинкин Семен Андреевич | Старший инженер
3 | 2021-01-15 00:00:00 | infinity | Иванов Петр Васильевич | Инженер
4 | 2021-01-16 00:00:00 | infinity | Малкин Иван Олегович | Старший инженер
5 | 2021-01-17 00:00:00 | infinity | Селиванов Илья Максимович | Инженер
6 | 2021-01-18 00:00:00 | infinity | Васин Сергей Петрович | Инженер

Изменения

С 1 марта сотрудник с id=5 был переведен на другую должность:

SELECT qbim_change(1::bigint,null::text,5::bigint,
  '2021-03-01 00:00:00.0'::timestamp,null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'r_position',2::bigint
);

Выведем моментальные снимки на даты 28 февраля и 1 марта:

SELECT * FROM (
	SELECT
		p.id,
		greatest(p.sd,d.sd) AS sd,
		least(p.ed,d.ed) AS ed,
		p.last_name||' '||p.first_name||' '||p.patronym AS "ФИО",
		d.position_name AS "Должность"
	FROM person p JOIN position d
		ON p.r_position = d.id AND tsrange(p.sd,p.ed) && tsrange(d.sd,d.ed)
) t
WHERE '2021-02-28 00:00:00.0'::timestamp <@ tsrange(t.sd,t.ed)
ORDER BY 1,2;

SELECT * FROM (
	SELECT
		p.id,
		greatest(p.sd,d.sd) AS sd,
		least(p.ed,d.ed) AS ed,
		p.last_name||' '||p.first_name||' '||p.patronym AS "ФИО",
		d.position_name AS "Должность"
	FROM person p JOIN position d
		ON p.r_position = d.id AND tsrange(p.sd,p.ed) && tsrange(d.sd,d.ed)
) t
WHERE '2021-03-01 00:00:00.0'::timestamp <@ tsrange(t.sd,t.ed)
ORDER BY 1,2;

Результаты различаются. Сотрудник в первом случае имеет одну должность, а во втором — другую.

Результат на 28 февраля:

id | sd | ed | ФИО | Должность
1 | 2021-01-11 00:00:00 | infinity | Начальников Игорь Петрович | Менеджер
2 | 2021-01-12 00:00:00 | infinity | Половинкин Семен Андреевич | Старший инженер
3 | 2021-01-15 00:00:00 | infinity | Иванов Петр Васильевич | Инженер
4 | 2021-01-16 00:00:00 | infinity | Малкин Иван Олегович | Старший инженер
5 | 2021-01-17 00:00:00 | 2021-03-01 00:00:00 | Селиванов Илья Максимович | Инженер
6 | 2021-01-18 00:00:00 | infinity | Васин Сергей Петрович | Инженер

Результат на 1 марта:

id | sd | ed | ФИО | Должность
1 | 2021-01-11 00:00:00 | infinity | Начальников Игорь Петрович | Менеджер
2 | 2021-01-12 00:00:00 | infinity | Половинкин Семен Андреевич | Старший инженер
3 | 2021-01-15 00:00:00 | infinity | Иванов Петр Васильевич | Инженер
4 | 2021-01-16 00:00:00 | infinity | Малкин Иван Олегович | Старший инженер
5 | 2021-03-01 00:00:00 | infinity | Селиванов Илья Максимович | Старший инженер
6 | 2021-01-18 00:00:00 | infinity | Васин Сергей Петрович | Инженер

Выведем обобщенную информацию по подразделению (см. раздел Запрос обобщенной информации). Видно, что у записи, соответствующей переведенному сотруднику, выводится две версии, соответствующие его двум различным должностям:

id | sd | ed | ФИО | Должность
1 | 2021-01-11 00:00:00 | infinity | Начальников Игорь Петрович | Менеджер
2 | 2021-01-12 00:00:00 | infinity | Половинкин Семен Андреевич | Старший инженер
3 | 2021-01-15 00:00:00 | infinity | Иванов Петр Васильевич | Инженер
4 | 2021-01-16 00:00:00 | infinity | Малкин Иван Олегович | Старший инженер
5 | 2021-01-17 00:00:00 | 2021-03-01 00:00:00 | Селиванов Илья Максимович | Инженер
5 | 2021-03-01 00:00:00 | infinity | Селиванов Илья Максимович | Старший инженер
6 | 2021-01-18 00:00:00 | infinity | Васин Сергей Петрович | Инженер

С 15 марта произошло изменение — название должности «Инженер» изменили на «Младший инженер»:

SELECT qbim_change(2::bigint,null::text,1::bigint,
  '2021-03-15 00:00:00.0'::timestamp,null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'position_name','Младший инженер');

Посмотрим, как изменился вывод обобщенной информации:

id | sd | ed | ФИО | Должность
1 | 2021-01-11 00:00:00 | infinity | Начальников Игорь Петрович | Менеджер
2 | 2021-01-12 00:00:00 | infinity | Половинкин Семен Андреевич | Старший инженер
3 | 2021-01-15 00:00:00 | 2021-03-15 00:00:00 | Иванов Петр Васильевич | Инженер
3 | 2021-03-15 00:00:00 | infinity | Иванов Петр Васильевич | Младший инженер
4 | 2021-01-16 00:00:00 | infinity | Малкин Иван Олегович | Старший инженер
5 | 2021-01-17 00:00:00 | 2021-03-01 00:00:00 | Селиванов Илья Максимович | Инженер
5 | 2021-03-01 00:00:00 | infinity | Селиванов Илья Максимович | Старший инженер
6 | 2021-01-18 00:00:00 | 2021-03-15 00:00:00 | Васин Сергей Петрович | Инженер
6 | 2021-03-15 00:00:00 | infinity | Васин Сергей Петрович | Младший инженер

Видно, что у всех сотрудников, имеющих должность «Инженер», появились новые версии с новым названием должности.

Вариант запроса информации «на дату» для конкретного сотрудника

SELECT
	p.id,
	greatest(p.sd,d.sd) AS sd,
	least(p.ed,d.ed) AS ed,
	p.last_name||' '||p.first_name||' '||p.patronym AS "ФИО",
	d.position_name AS "Должность"
FROM person p JOIN position d
	ON p.r_position = d.id
WHERE p.id=5 AND '2021-02-28 00:00:00.0'::timestamp <@ tsrange(p.sd,p.ed)
    AND '2021-02-28 00:00:00.0'::timestamp <@ tsrange(d.sd,d.ed)
ORDER BY 1,2;

id | sd | ed | ФИО | Должность
5 | 2021-01-17 00:00:00 | 2021-03-01 00:00:00 | Селиванов Илья Максимович | Инженер

Вариант запроса информации для конкретного сотрудника за весь период

SELECT
	p.id,
	greatest(p.sd,d.sd) AS sd,
	least(p.ed,d.ed) AS ed,
	p.last_name||' '||p.first_name||' '||p.patronym AS "ФИО",
	d.position_name AS "Должность"
FROM person p JOIN position d
	ON p.r_position = d.id AND tsrange(p.sd,p.ed) && tsrange(d.sd,d.ed)
WHERE p.id=5
ORDER BY 1,2;

id | sd | ed | ФИО | Должность
5 | 2021-01-17 00:00:00 | 2021-03-01 00:00:00 | Селиванов Илья Максимович | Инженер
5 | 2021-03-01 00:00:00 | infinity | Селиванов Илья Максимович | Старший инженер

Отражаются версии «до» и «после» перевода на другую должность.

Еще один вариант запроса информации для конкретного сотрудника за весь период

Если уже после смены названия должности перевести сотрудника с id=6 на другую должность:

SELECT qbim_change(1::bigint,null::text,6::bigint,
  '2021-03-20 00:00:00.0'::timestamp,null::timestamp,
  '463404e8-ee5d-4597-b9a9-e3b69e29d18a'::uuid,
  'r_position',2::bigint);

и запросить все версии по этому сотруднику с помощью запроса Вариант запроса информации для конкретного сотрудника за весь период, но с id=6, то в результате отобразятся версии, соответствующие периодам «до» и «после» смены названия должности, а также периоду «после» перевода на другую должность:

id | sd | ed | ФИО | Должность
6 | 2021-01-18 00:00:00 | 2021-03-15 00:00:00 | Васин Сергей Петрович | Инженер
6 | 2021-03-15 00:00:00 | 2021-03-20 00:00:00 | Васин Сергей Петрович | Младший инженер
6 | 2021-03-20 00:00:00 | infinity | Васин Сергей Петрович | Старший инженер

Регистрация действий

Поскольку в примере при описании объектов указывалось, что необходимо логировать информацию по действиям с объектами (поле obj.is_logging), все указанные в примере создания и изменения объектов отражаются в служебной таблице action.

SELECT
    r_object, lnk_dt, fct_dt, action, pid, userid
FROM action;

r_object | lnk_dt | fct_dt | action | pid | userid
2:1 | 2021-01-01 00:00:00 | 2021-04-30 19:43:24.586786 | 1 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
2:2 | 2021-01-01 00:00:00 | 2021-04-30 19:43:27.352193 | 1 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
2:3 | 2021-01-01 00:00:00 | 2021-04-30 19:44:10.525477 | 1 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:1 | 2021-01-11 00:00:00 | 2021-04-30 19:55:28.109557 | 1 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:2 | 2021-01-12 00:00:00 | 2021-04-30 19:55:30.363525 | 1 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:3 | 2021-01-15 00:00:00 | 2021-04-30 19:55:32.670876 | 1 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:4 | 2021-01-16 00:00:00 | 2021-04-30 19:55:34.283938 | 1 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:5 | 2021-01-17 00:00:00 | 2021-04-30 19:55:39.770958 | 1 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:6 | 2021-01-18 00:00:00 | 2021-04-30 19:55:42.7058 | 1 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:5 | 2021-01-17 00:00:00 | 2021-04-30 20:43:03.597479 | 3 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:5 | 2021-03-01 00:00:00 | 2021-04-30 20:43:03.597673 | 2 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
2:1 | 2021-01-01 00:00:00 | 2021-04-30 20:56:12.488844 | 3 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
2:1 | 2021-03-15 00:00:00 | 2021-04-30 20:56:12.489592 | 2 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:6 | 2021-01-18 00:00:00 | 2021-04-30 21:38:37.966772 | 3 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a
1:6 | 2021-03-20 00:00:00 | 2021-04-30 21:38:37.966962 | 2 | 2521 | 463404e8-ee5d-4597-b9a9-e3b69e29d18a

Значения полей описаны в разделе Описание версионируемых таблиц.

Примечание
Поля fct_dt и pid могут отличаться; в данной таблице они приведены как пример.

Расширение Uref, обобщённый ссылочный тип

Тип данных uref используется в расширении Qbim как ссылочный тип на различные экземпляры объектов.

В настоящей реализации тип uref используется в поле r_object таблицы action, но также может использоваться в ссылочных полях объектов.

Структура типа uref

Тип данных uref состоит из двух частей:

идентификатора объекта, представленного типом bigint (obj.id),

идентификатора экземпляра объекта, представленного типом bigint или uuid (соответственно "локальным" или "глобальным").

Методы для работы с типом uref

Расширение Uref предоставляет семейство функций для работы с типом uref.

Назначение | Метод | Аргументы | Пример вызова | Результат
Конструктор с локальным идентификатором экземпляра |  | bigint, bigint | '1:34'::uref | 1:34
| uref_local | bigint, bigint | uref_local(1,34) | 1:34
| uref | bigint, bigint | uref(1,34) | 1:34
Конструктор с глобальным идентификатором экземпляра |  | bigint, uuid | '1:463404eb-ee5d-4597-b9a9-e3b69e29d18a'::uuid | 1:463404eb-ee5d-4597-b9a9-e3b69e29d18a
| uref_global | bigint, uuid | uref_global(1,'463404eb-ee5d-4597-b9a9-e3b69e29d18a'::uuid) | 1:463404eb-ee5d-4597-b9a9-e3b69e29d18a
| uref | bigint, uuid | uref(1,'463404eb-ee5d-4597-b9a9-e3b69e29d18a'::uuid) | 1:463404eb-ee5d-4597-b9a9-e3b69e29d18a
Проверка, что идентификатор локальный | uref_is_id_local | uref | uref_is_id_local('1:34'::uref) | true
|  |  | uref_is_id_local('1:463404eb-ee5d-4597-b9a9-e3b69e29d18a'::uref) | false
Проверка, что идентификатор глобальный | uref_is_id_global | uref | uref_is_id_global('1:34'::uref) | false
|  |  | uref_is_id_global('1:463404eb-ee5d-4597-b9a9-e3b69e29d18a'::uref) | true
Получение идентификатора объекта | uref_obj | uref | uref_obj('1:34'::uref) | 1
|  |  | uref_obj('1:463404eb-ee5d-4597-b9a9-e3b69e29d18a'::uref) | 1
Получение идентификатора экземпляра объекта | uref_id_local | uref | uref_id_local('1:34'::uref) | 34
| uref_id_global | uref | uref_id_global('1:463404eb-ee5d-4597-b9a9-e3b69e29d18a'::uref) | 463404eb-ee5d-4597-b9a9-e3b69e29d18a

Индексирование полей типа uref

Поля типа uref могут использоваться в индексах btree. Для этого в расширении Uref созданы необходимые операторы и operator class.

Расширение Qbayes, методы статистического анализа на основе условных вероятностей Байеса

Назначение расширения

Расширение QHB Qbayes — это созданный командой разработчиков QHB инструментарий для задач расчета вероятностных моделей с применением сетей Байеса.

Исходные данные для различных вычислений могут храниться в таблицах и представлениях внутри базы данных QHB. К таким данным относятся исторические факты (срабатывание или несрабатывание гипотез) и соответствующие им причины (параметры). В качестве параметров рассматриваются логические (бинарные) значения или скалярные величины, которые методами расширения приводятся к дискретным значениям и разбиваются на диапазоны, определяемые пользователем. Значения гипотез всегда предполагаются логическими (бинарными).

После предварительной статистической обработки исторических фактов (обучения), модель позволяет рассчитывать условные вероятности тех же самых гипотез при новых условиях (новых значениях параметров).

Алгоритмы Байесовского предсказания

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

Наивный байесовский классификатор

Простой классификатор, основанный на предположении о независимости параметров.

Этот тип модели используется при обучении с параметром model_type = 'naive' (Naive Bayesian classificator).

Классификатор, основанный на функции взаимной информации

Взаимная информация — это статистическая функция двух случайных величин, описывающая количество информации, содержащееся в одной случайной величине относительно другой.

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

Этот тип модели используется при обучении с параметром model_type = 'mi' (Mutual Information).

Методы Монте-Карло с цепями Маркова

Класс алгоритмов, моделирующих некоторое распределение вероятностей. В расширении Qbayes один из таких алгоритмов семплирования применяется для установления зависимых параметров для гипотез.

В расширении Qbayes предполагается использовать различные алгоритмы, в релизе QHB 1.4.0 используется одна из реализаций алгоритма Метрополиса — Гастингса.

Этот тип модели используется при обучении с параметром model_type = 'mcmc' (Markov chain Monte Carlo).

Методы расширения

При создании модели создается таблица описания моделей:

CREATE TABLE IF NOT EXISTS qbayes_model_description (
    имя_модели VARCHAR NOT NULL,
    параметры_модели json NOT NULL,
    CONSTRAINT qbayes_model_un UNIQUE (model_name)
);

Затем, после обучения статистической выборкой, создается таблица с данными:

CREATE TABLE IF NOT EXISTS qbayes_model_data (
    имя_модели VARCHAR NOT NULL,
    имя_графа VARCHAR NOT NULL,
    данные json,
    CONSTRAINT qbayes_model_graph_un UNIQUE (model_name, graph_name)
);

Примечание
Указанные таблицы создаются автоматически при вызове соответствующих методов в текущей схеме базы данных по умолчанию. Если таблицы уже существуют, они дополняются. При этом действуют ограничения уникальности на имя модели в первой таблице и имя модели с именем графа во второй.

Определение модели

Для определения модели используется метод:

CREATE OR REPLACE FUNCTION qbayes_create_model(
    имя_модели cstring,
    описание json)
returns BIGINT AS 'MODULE_PATHNAME' language c volatile security definer;

Возвращаемое значение

При успешном выполнении возвращается 0 или генерируется ошибка при неуспешном.

Параметры:

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

описание описывает параметры и гипотезы модели. Каждый параметр или гипотеза описываются json-выражением.

Его поля:

"name" — символическое имя параметра или гипотезы,

"type" — тип параметра или гипотезы в исходном представлении; поддерживаются только типы bool (логический), discrete_integer (дискретный при перечислении) или float8 (с плавающей запятой, при создании диапазонов значений),

"distrib" — тип параметра или гипотезы в модели; поддерживаются только типы bool (логический) или discrete (дискретный),

"ranges" — границы разделения на интервалы для создания дискретного параметра, используется только вместе с параметром с типом float8,

"acceptable" - значения, допустимые при перечислении, используется только вместе с параметром с типом discrete_integer,

"is_hypothesis" — признак гипотезы, по умолчанию имеет значение false.

Типы параметров и гипотез

Все алгоритмы Байеса в расширении используют только бинарные и дискретные типы параметров моделей, а гипотезы - только бинарные типы.

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

Бинарные параметры

...
"type": "bool",
"distrib": "bool",
...

В этом случае параметр считается бинарным и во внешнем представлении, и во внутреннем преставлении модели. И при обучении модели, и при расчёте прогнозных значений гипотез, допустимые входные значения - true и false.

Дискретные параметры при разбиении на диапазоны

...
"type": "float8",
"distrib": "discrete",
"ranges": [r0, r1, ... ,rN]

В этом случае параметр считается дискретным во внутреннем преставлении модели. В качестве значения во внешнем представлении выступают числа типа float8, которые затем разбиваются на диапазоны по границам r0, r1, ..., rN и во внутреннем представлении обозначают номер диапазона, в который попадает значение.

Диапазоны задаются полем ranges. Перечисление значений в его массиве указывают на границы диапазонов, причём создаётся N+1 диапазонов по следующим правилам:

0: (-infinity; r0)
1: [r0; r1)
...
N: [r{N-1}; rN)
N+1: [rN; +infinity)

И при обучении модели, и при расчёте прогнозных значений гипотез, на вход параметров передаются значения типа float8, методы расширения уже сами определяют номер диапазона.

Дискретные параметры при перечислении

...
"type": "discrete_integer",
"distrib": "discrete",
"acceptable": [m0, m1, ... ,mN]

В этом случае параметр считается дискретны во внутреннем преставлении модели. В качестве значения во внешнем представлении выступают дискретные значения от 0 до N из поля acceptable. Сами значения параметра могут быть произвольными целыми числами, из перечисленных m0, m1, ..., mN, необязательно идущими подряд или начинающимися с 0.

И при обучении модели, и при расчёте прогнозных значений гипотез, на вход параметров передаются значения из поля acceptable, методы расширения уже сами определяют дискретное значение, соответствующее значению параметра. Если внешнее значение не соответствует ни одному из перечисленных m0, m1, ... mN, то генерируется ошибка.

Внешнее и внутреннее представление гипотез

Для гипотез допустима только одна комбинация "type" и "distrib":

...
"type": "bool",
"distrib": "bool",
...

Обучение модели

Данные (факты) с выделенными параметрами могут содержаться либо в уже подготовленных таблицах (relation), либо в представлении (view), созданном на основе этих таблиц.

Ограничения для таблиц и представлений:

Они должны содержать поле value типа json, в котором содержатся по крайней мере такие же поля, какие были объявлены при определении модели. Могут существовать и другие поля, однако они не принимаются в расчет при обучении.

Поддерживаются бинарные или дискретные параметры (предполагается независимость параметров; используемый тип модели — наивная модель Байеса).

Обучение производится следующим методом:

CREATE OR REPLACE FUNCTION qbayes_learn(
  имя_модели cstring,
  имя_графа cstring,
  тип_модели cstring,
  имя_отношения cstring,
  число_итераций BIGINT)
returns BIGINT AS 'MODULE_PATHNAME' language c volatile security definer;

Возвращаемое значение

При успешном выполнении возвращается 0 или генерируется ошибка при неуспешном.

Параметры:

имя_модели указывает имя модели;

имя_графа указывает имя графа;

тип_модели указывает тип модели ('naive', 'mi' или 'mcmc'; см. Алгоритмы Байесовского предсказания);

имя_отношения указывает на подготовленные исходные данные с фактами и гипотезами;

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

Примечание
Параметр число_итераций имеет значения только для модели типа mcmc, для остальных он просто игнорируется.

Дообучение модели

Примечание
Дообучение имеет смысл проводить только для модели типа mcmc, для остальных типов функция дообучения просто ничего не делает.

CREATE OR REPLACE FUNCTION qbayes_learn_again(
  имя_модели cstring,
  имя_графа cstring,
  тип_модели cstring,
  имя_отношения cstring,
  число_итераций BIGINT)
returns DOUBLE PRECISION AS 'MODULE_PATHNAME' language c volatile security definer;

Возвращаемое значение

Возвращается число с плавающей запятой от 0 до 1, отражающее степень совпадения получившегося распределения с идеальным, получаемую как среднее для всех гипотез модели на данных обучающей выборки.

При неуспешном выполнении генерируется ошибка.

Параметры

Параметры аналогичны функции qbayes_learn, но происходит уточнение распределения на число_итераций.

Предсказание

Вычисление вероятностей гипотез при заданных параметрах производится следующим образом:

CREATE OR REPLACE FUNCTION qbayes_solve(
  имя_модели cstring,
  имя_графа cstring,
  элементарное_событие json)
returns json AS 'MODULE_PATHNAME' language c volatile security definer;

Возвращаемое значение

При успешном выполнении возвращается json, в котором перечисляются пары:

символическое имя гипотезы,

вероятность, с которыми эти гипотеза сбудется.

При неуспешном выполнении генерируется ошибка.

Параметры:

имя_модели указывает имя модели.

имя_графа указывает имя графа.

элементарное_событие задает вектор параметров для расчета гипотез в виде json-выражения.

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

CREATE OR REPLACE FUNCTION qbayes_solve_view(
  имя_модели cstring,
  имя_графа cstring,
  имя_отношения cstring)
returns setof json AS 'MODULE_PATHNAME' language c volatile security definer;

Примечание
Дополнительно предлагается группа методов для обработки большого количества гипотез: qbayes_learn_mt, qbayes_solve_mt и qbayes_solve_view_mt. Они выполняются в многопоточном режиме и позволяют ускорить обучение и расчет. По параметрам эти методы полностью совпадают с соответствующими однопоточными версиями.
Следует отметить, что на малом количестве гипотез ускорение может не произойти, более того, из-за накладных расходов на организацию многопоточной обработки может наблюдаться замедление.
В ранних, экспериментальных реализациях расширения Qbayes некоторые многопоточные методы реализованы точно так же, как и однопоточные. Сигнатуры для многопоточных методов зарезервированы для реализации оптимизированных алгоритмов при стабилизации расширения.

Очистка

Дополнительно предлагаются методы очистки данных и моделей:

CREATE OR REPLACE FUNCTION qbayes_drop_graph(имя_модели cstring, имя_графа cstring)
returns BIGINT AS 'MODULE_PATHNAME' language c volatile security definer;

CREATE OR REPLACE FUNCTION qbayes_drop_model(имя_модели cstring)
returns BIGINT AS 'MODULE_PATHNAME' language c volatile security definer;

Возвращаемое значение

При успешном выполнении возвращается 0 или генерируется ошибка при неуспешном.

Ограничения и параметры

Ограничения

Как правило, вероятностные модели содержат большое количество различных параметров и гипотез. На небольших моделях, где число параметров и гипотез исчисляется десятками, скорее всего не возникнет каких либо затруднений. При работе же с большими моделями полезно знать некоторые факты.

Для различных данных в расширении Qbayes намерено выбран типа данных json.

Этот тип описания выбран потому, что позволяет передать большее количество параметров, по сравнению, например, с таблицами СУБД. В таблице можно задать не более 1600 столбцов (см. Ограничения QHB), в то время, как в одной строке json можно разместить несколько тысяч и даже несколько десятков тысяч описаний параметров и гипотез.

Тем ни менее, и у типа данных json есть свои ограничения.
Длина строки не должна превышать размер 1 Гбайт, если она символьная (определяется ограничениями varlena). Если же в ней содержатся бинарные данные, то этот лимит установлен для символьного представления данных, а фактически, бинарные данные ограничены вдвое меньшим лимитом.

Этим ограничениям должны соответствовать строки json с описаниями параметров модели, данными для обучения, обученные модели (поля data таблицы qbayes_model_data) и результаты предсказания.

В некоторых случаях, при работе алгоритмов создаётся ситуация, когда происходит экспоненциальный рост количества ресурсов, требуемых для расчётов. Иногда такая ситуация называется комбинаторным взрывом. Она может быть вызвана, например, слишком большим количеством вариантов значений у зависимых переменных (параметров), хотя для качественного определения вероятностей гипотез и достаточно их основных значений, которые вносят наибольший вклад. Для предотвращения таких ситуаций, в расширении Qbayes есть возможность ограничения расходования ресурсов с помощью параметров.

Параметры

qbayes.max_dependencies

Параметр qbayes.max_dependencies применим для алгоритмов mi (Mutual Information) и mcmc (Markov chain Monte Carlo). Обозначает максимальное количество зависимых переменных, которые может иметь гипотеза.
Значение по умолчанию 5.

qbayes.max_variants

Параметр qbayes.max_variants применим для алгоритмов mi (Mutual Information) и mcmc (Markov chain Monte Carlo). Используется для дополнительного ограничения количества зависимых переменных, которое может иметь гипотеза: если количество комбинаций значений зависимых переменных превышает qbayes.max_variants, то количество зависимых переменных уменьшается до той поры, пока количество комбинаций их значений не станет укладываться в qbayes.max_variants. Рекомендации по установке параметра следующие: фактически qbayes.max_variants вносит максимальный вклад в занимаемую память, потребляя qbayes.max_variants × "количество гипотез" × 16 байт.
Значение по умолчанию 50000 (пятьдесят тысяч).

Замечание
Параметры устанавливаются в файле конфигурации. См. Настройка параметров

Пример использования

Необходимо установить расширение Qbayes:

CREATE EXTENSION IF NOT EXISTS qbayes cascade;

Создадим данные для обучения модели (в примере указан всего один вектор параметров и гипотез, в действительности их должно быть много):

CREATE OR REPLACE VIEW view1 AS SELECT
'{"size": 15, "volume": 18.35, "is_roundy": true, "h1": false}'::json AS value;

Опишем модель:

SELECT qbayes_create_model('MyModel',
  '[{
    "name": "size",
    "type": "discrete_integer",
    "distrib": "discrete",
    "acceptable": [1, 2, 15]
  }, {
    "name": "volume",
    "type": "float8",
    "distrib": "discrete",
    "ranges": [0.1, 1, 10, 100, 1000, 1e4, 1e5, 1e6, 1e7, 1e8]
  }, {
    "name": "is_roundy",
    "is_hypothesis": true,
    "type": "bool",
    "distrib": "bool"
  }, {
    "name": "h1",
    "is_hypothesis": true,
    "type": "bool",
    "distrib": "bool"
  }]'::json);

Можем просмотреть описание модели:

SELECT * FROM qbayes_model_description;

Обучение:

SELECT qbayes_learn('MyModel', 'MyGraph', 'naive', 'view1', 0);

Можем просмотреть данные модели:

SELECT * FROM qbayes_model_data;

Предсказание одного события:

SELECT qbayes_solve('MyModel', 'MyGraph', '{ "size": 15, "volume": 1e6 }'::json );

Предсказание нескольких событий (в примере используется тот же массив данных, на самом деле он может быть другим):

SELECT qbayes_solve_view('MyModel', 'MyGraph', 'view1');

Очистка:

SELECT qbayes_drop_graph('MyModel', 'MyGraph');

SELECT qbayes_drop_model('MyModel');

Примечание
В поставке расширения Qbayes прилагаются и другие примеры с расширенными наборами параметров и гипотез, с использованием различных методов.
Эти примеры можно найти в SQL-скриптах, прилагаемых к расширению.

Замечания, примеры и варианты работы с расширением Qbayes

Предсказания по большому набору параметров

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

Первый способ:

select qbayes_solve_view('Model1', 'Graph1', 'input_data');

Здесь предполагается, что Model1 и Graph1 некоторые модель и граф. То есть, этап обучения уже прошёл. И совершенно не важно, с каким алгоритмом мы имели дело.

А input_data - это набор параметров, по которому и производится расчёт вероятностей гипотез.

Второй способ:

create function solve_loop() returns setof json
language plpgsql
as $$
declare
  r record;
  rr json;
begin
  for r in (select value from input_data) loop
    select qbayes_solve('Model1', 'Graph1', r.value) into rr;
  return next rr;
end loop;
end
$$

select solve_loop();

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

При выборке первым способом, сокращается время на чтение описания модели, десериализацию описаний параметров и гипотез, это происходит один раз. Во втором же случае, чтение и десериализация происходит при каждом вызове функции qbayes_solve. Если объём модели не очень большой, а объём input_data не запредельный, это сокращение может оказаться несущественным.

Но, в первом случае, можно учесть особенности работы некоторых клиентов при работе с большими выборками. Бывает так, что они ограничивают выборку, "оборачивая" запрос указаниями LIMIT и OFFSET, формируя таким образом порции результата. Иногда это может приводить к повторному расчёту вероятностей при каждом скроллинге к следующей порции. Кроме того, такие ограничения неэффективно работают с контекстом памяти процесса на сервере. Скроллинг может сопровождаться сообщением "transaction left non-empty SPI stack" в системном логе сервера QHB.

Расширение Part_Import: частичный импорт файлов данных из внешней базы данных

О расширении Part_Import

Данное расширение содержит две функции:

partial_import(строка_подключения, имена_таблиц) — основная функция расширения, которая служит для копирования файлов, содержащих данные таблиц, из внешней базы данных в локальную.
Под таблицами локальной базы данных подразумеваются таблицы, доступные в текущем сеансе подключения, под таблицами внешней — те, доступ к которым мы получаем, воспользовавшись строкой_подключения. Имена таблиц для копирования передаются в параметре имена_таблиц. Этот параметр имеет тип строкового массива, где каждая строка содержит имя одной таблицы. Имя может быть как полным — т. е. включать пространство имен, так и неполным — т.е. без пространства имен. В последнем случае сервер должен понимать по неполному имени, о какой конкретно таблице идет речь.

table_script(oid) — функция, возвращающая SQL-скрипт, позволяющий создать таблицу, OID которой передан в параметре. Скрипт содержит индексы, внешние индексы, ограничения, последовательности, комментарии.

partial_import

Особенности работы частичного импорта:

Блокировки на внешние таблицы не накладываются, блокировки на локальные таблицы накладываются только перед переключением на "новые" файлы с данными, и блокировки предельно короткие. Перенесённые данные нескольких таблиц согласованы между собой.

Поскольку копируются файлы с данными - импорт заботится, чтобы эти файлы были корректными, для чего сверяет метаданные таблиц внешней и локальной БД. В процессе сверки убеждаемся, что названия, типы и порядок полей, включая удалённые колонки - должны полностью совпадать. Должны совпадать ограничения колонок, тип хранения колонки. TOAST-таблицы должны быть либо у обеих таблиц, либо ни у одной из них. Должно совпадать количество индексов, их наименования и состав колонок. Не имеет значение, если не совпадает табличное пространство. Допустим импорт материализованных табличных представлений, не должно быть проблем с переносом партиций.

В случае наличия ограничения внешнего ключа будут предложены скрипты по их удалению в локальной БД и восстановлению по окончании импорта. Если при импорте будет переноситься не только таблица с внешним ключом, но и таблица, на которую внешний ключ ссылается - никаких ограничений не возникнет, при этом данные будут согласованы.

Ввиду отсутствия блокировок внешних таблиц теоретически возможно выполнение операций DDL с внешней таблицей в период между завершением сверки метаданных и началом переноса файлов с данными таблиц. В этом случае результат работы непредсказуем.

В процессе работы переносятся только файлы с данными, поэтому рекомендуется пересобрать статистику по импортированными таблицам по окончанию работы импорта.

Хотя импорт и инициирует создание контрольной точки (checkpoint) на локальной БД по окончанию своей работы, теоретически возможно аварийное завершение работы БД между моментами переключения на новые файлы и завершением создания контрольной точки - в этом случае процедура аварийного восстановления с данными импортированных таблиц может дать непредсказуемые результаты.

table_script

Функция является адаптацией кода утилиты pg_dump. Взята часть, отвечающая исключительно за генерацию скриптов таблиц, индексов, ограничений и последовательностей. Вся остальная функциональность, посвященная подготовке содержимого таблиц к выгрузке, и сама выгрузка отброшены.

Тестовые сценарии

partial_import

Тестировать расширение можно и в рамках одного экземпляра базы данных, но полноценное тестирование следует проводить на разных экземплярах базы данных, причем в качестве внешней выбрать ту, в которой работа уже ведется продолжительное время. Последнее можно проверить, зайдя в папку с базой данных и посмотрев количество и размер файлов в подпапке pg_xact: если файлов несколько, значит, работа в базе ведется какое-то заметное время. Экземпляр с локальной базой (т. е. той, в которую следует производить импорт) лучше создать с нуля.

В локальной базе должно быть установлено расширение part_import, во внешней достаточно наличия правильных бинарных файлов нужной версии. Установить расширение можно командой:

CREATE EXTENSION part_import;

Теперь во внешней базе данных нужно создать таблицы и заполнить их данными. Для этого создадим три таблицы: type_a и type_b как обычные словари и еще одну таблицу для тестирования TOAST и сохранения работоспособности таблицы с HOT- оптимизацией. После первого этапа будет добавлена четвертая таблица many2many_ab как «точка пересечения» таблиц type_a и type_b (эмуляция отношения «многие ко многим»). Для создания таблиц type_a, type_b и accounts воспользуемся следующим скриптом (создавать таблицы следует и на внешнем и на локальном серверах баз данных):

CREATE TABLE public.type_a (
    id integer NOT NULL,
    type_id smallint NOT NULL,
    name_1 character varying(40) NOT NULL,
    date_ins date,
    name_hash_1 integer NOT NULL
);
ALTER TABLE ONLY public.type_a
    ADD CONSTRAINT pk_type_a PRIMARY KEY (id);
CREATE INDEX idx_type_a_name_1 ON public.type_a USING btree (name_1);
CREATE INDEX idx_type_a_name_hash_1 ON public.type_a USING btree (name_hash_1);

CREATE TABLE public.type_b (
    id integer NOT NULL,
    name character varying(40) NOT NULL,
    date_ins date,
    name_hash integer NOT NULL
);
ALTER TABLE ONLY public.type_b
    ADD CONSTRAINT pk_type_b PRIMARY KEY (id);
CREATE INDEX idx_type_b_name ON public.type_b USING btree (name);
CREATE INDEX idx_type_b_name_hash ON public.type_b USING btree (name_hash);

CREATE TABLE public.accounts (
    id integer NOT NULL,
    number text,
    client text,
    amount numeric,
    CONSTRAINT amount_is_positive CHECK ((amount >= 0.0))
);
ALTER TABLE ONLY public.accounts ALTER COLUMN number SET STORAGE EXTERNAL;
ALTER TABLE public.accounts ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (
    SEQUENCE NAME public.accounts_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1
);
ALTER TABLE ONLY public.accounts
    ADD CONSTRAINT accounts_number_key UNIQUE (number);
ALTER TABLE ONLY public.accounts
    ADD CONSTRAINT accounts_pkey PRIMARY KEY (id);

Далее следует заполнить таблицы внешней базы данных данными. В случае таблиц type_a и type_b это могут быть практически любые данные, желательно только, чтобы параметр id был линейным и начинался со значения 1, а количества данных было достаточно для того, чтобы файлов хранения было несколько сегментов хотя бы у одной из таблиц. Примерная оценка объема данных для таблицы type_a — 10 миллионов строк. Для таблицы type_b вполне достаточно 10 тысяч строк. Таблицу accounts нужно заполнить так, чтобы строки обязательно попали в TOAST- таблицу. По количеству строк особых требований нет, так как по этому параметру достаточно тестирования таблицы type_a. Для заполнения и изменения данных таблицы accounts воспользуемся следующим скриптом (по мотивам статьи Егора Рогова):

INSERT INTO accounts(id, "number", client, amount) VALUES(1, 1000, 'masha', 1000.),
    (2, 2001, 'vasya', 2000.), (3, 2002, 'misha', 1200.), (4, 2003, 'irina', 1300.);
UPDATE accounts SET client = (
  SELECT string_agg( chr(trunc(65+random()*26)::integer), '') FROM generate_series(1,2000)
);
UPDATE accounts SET amount = amount + 100 + id;
UPDATE accounts SET amount = amount + 100 + id;
UPDATE accounts SET amount = amount + 100 + id;
UPDATE accounts SET amount = amount + 100 + id;

После этого в таблице accounts должны появиться 4 (видимые) строки. Последние четыре команды UPDATE не меняют данные в индексируемых столбцах, поэтому должна быть задействована оптимизация HOT-обновления.

Теперь импортируем данные таблиц type_a, type_b и accounts на локальный сервер, для чего выполним команду:

SELECT partial_import('host=<external_db_adderss> user=<external_db_user> dbname=<external_db_name>', ARRAY['type_a', 'type_b', 'accounts']::text[]);

Первый параметр — это строка соединения с внешней базой данных. С этими параметрами у пользователя должно быть достаточно прав для чтения системных каталогов и выполнения команды BASE_BACKUP.

По окончании работы функции в локальной базе данных заполнение таблиц type_a, type_b и accounts должно совпадать с таковыми во внешней базе. Также следует убедиться, что запросы:

EXPLAIN ANALYZE SELECT * FROM type_a WHERE id = 7;
EXPLAIN ANALYZE SELECT * FROM type_b WHERE id = 7;
EXPLAIN ANALYZE SELECT * FROM accounts WHERE id = 2;

строят индексный план извлечения данных, например, такой:

Index Scan using pk_type_a on type_a  (cost=0.42..8.44 rows=1 width=55) (actual time=0.019..0.020 rows=1 loops=1)
  Index Cond: (id = 7)
Planning Time: 0.057 ms
Execution Time: 0.032 ms

а запросы:

SELECT * FROM type_a WHERE id = 7;
SELECT * FROM type_b WHERE id = 7;
SELECT * FROM accounts WHERE id = 2;

действительно извлекают данные (подразумевается, что строки с указанными значениями id действительно существуют).

Описанный сценарий можно расширить следующим образом:

Заполнить таблицы type_a, type_b и accounts, как описано выше;

Запомнить количество строк каждой из таблиц;

Запустить скрипт дополнительного заполнения данными таблицы type_a или type_b и параллельно запустить импорт;

По окончании работы импорта убедиться, что количество строк в таблице, в которую параллельно заливались данные, больше, чем на начало импорта (в теории это должно быть количество на момент LSN-окончания снимка, но точный подсчет затруднен).

Создадим во внешней и локальной базах данных таблицу many2many_ab с помощью следующего скрипта:

CREATE TABLE public.many2many_ab (
    ida integer NOT NULL,
    idb integer NOT NULL
);
ALTER TABLE ONLY public.many2many_ab
    ADD CONSTRAINT pk_many2many_ab PRIMARY KEY (ida, idb);
CREATE INDEX many2many_ab_idb ON public.many2many_ab USING btree (idb);
ALTER TABLE ONLY public.many2many_ab
    ADD CONSTRAINT fk_net_ab_type_a_id FOREIGN KEY (ida) REFERENCES public.type_a(id);
ALTER TABLE ONLY public.many2many_ab
    ADD CONSTRAINT fk_net_ab_type_b_id FOREIGN KEY (idb) REFERENCES public.type_b(id);

При попытке выполнить предыдущую команду частичного импорта, а именно:

SELECT partial_import('host=<external_db_adderss> user=<external_db_user> dbname=<external_db_name>', ARRAY['type_a', 'type_b', 'accounts']::text[]);

мы должны получить сообщение об ошибке:

ERROR:  Input tables list refer to 2 foreign keys, which not import from remote server.
For guarantee reference integrity you must drop this foreign keys before running
partial import and recreate it when done in an "not valid" state, and than try validate.
The scripts below will help you perform the described actions.
(ОШИБКА: Список вводимых таблиц ссылается на 2 внешних ключа, которые не были импортированы с внешнего сервера.
Для гарантии ссылочной целостности вам следует удалить эти внешние ключи, прежде
чем запустить частичный импорт, и воссоздать их в состоянии «недопустимые», а затем попробовать их валидировать.
Следующие далее скрипты помогут вам выполнить описанные действия.)

BEGIN;
  -- Этот код удалит все мешающие внешние ключи --
  ALTER TABLE IF EXISTS ONLY public.many2many_ab DROP CONSTRAINT IF EXISTS fk_net_ab_type_a_id;
  ALTER TABLE IF EXISTS ONLY public.many2many_ab DROP CONSTRAINT IF EXISTS fk_net_ab_type_b_id;
END;

------------------------------------------------------------------------------------
---- Здесь вы должны запустить процедуру "partial_import" с теми же параметрами, ----
---- что и в предыдущий раз.                                                   ----
------------------------------------------------------------------------------------

BEGIN;
  -- Этот код воссоздаст удаленные ранее внешние ключи в состоянии «недопустимые» --
  ALTER TABLE ONLY public.many2many_ab
    ADD CONSTRAINT fk_net_ab_type_a_id FOREIGN KEY (ida) REFERENCES type_a(id) NOT VALID;

  ALTER TABLE ONLY public.many2many_ab
    ADD CONSTRAINT fk_net_ab_type_b_id FOREIGN KEY (idb) REFERENCES type_b(id) NOT VALID;
END;

BEGIN;
  -- Этот код попытается валидировать удаленные, а затем воссозданные внешние ключи --
  ALTER TABLE IF EXISTS ONLY public.many2many_ab VALIDATE CONSTRAINT fk_net_ab_type_a_id;
  ALTER TABLE IF EXISTS ONLY public.many2many_ab VALIDATE CONSTRAINT fk_net_ab_type_b_id;
END;

SQL state: XX000

Сообщение об ошибке говорит нам, что у нас есть внешние ключи, целостность которых мы не можем гарантировать по окончании импорта, и предлагает последовательность действий, благодаря которым можно выполнить импорт, хотя и ценой потери временного контроля целостности ключа. Однако если мы добавим в импорт таблицу, ставшую причиной ошибки, то он отработает без ошибок:

SELECT partial_import('host=<external_db_adderss> user=<external_db_user> dbname=<external_db_name>', ARRAY['type_a', 'type_b', 'many2many_ab', 'accounts']::text[]);

Импорт устойчив к расхождениям физического хранения атрибутов, расхождениям в их количестве, их порядку, но завершается ошибкой на стадии верификации таблиц. Выполним на локальной базе данных следующий скрипт для таблицы accounts:

ALTER TABLE public.accounts ADD amount2 numeric;
ALTER TABLE public.accounts DROP COLUMN amount;
ALTER TABLE public.accounts RENAME COLUMN amount2 TO amount;
ALTER TABLE public.accounts ADD CONSTRAINT amount_is_positive CHECK ((amount >= 0.0));

После его выполнения декларативное объявление таблиц на внешнем и локальном серверах выглядят идентично, однако попытка выполнить команду импорта:

SELECT partial_import('host=<external_db_adderss> user=<external_db_user> dbname=<external_db_name>', ARRAY['accounts']::text[]);

приведет к ошибке:

ERROR:  Remote table public.accounts2 has 4 columns, but local one has 5. It is possible that some attributes in one or both of the tables have been deleted earlier.
(ОШИБКА: У внешней таблицы public.accounts2 имеется 4 столбца, однако у локальной таблицы 5 столбцов. Возможно, некоторые атрибуты в одной или обеих таблицах ранее были удалены.)
SQL state: XX000

Специализированные и экспериментальные модули PostgreSQL

adminpack

amcheck

auth_delay

auto_explain

bloom

btree_gin

btree_gist

citext

cube

dblink

dict_int

dict_xsyn

earthdistance

file_fdw

fuzzystrmatch

hstore

intagg

intarray

isn

lo

ltree

old_snapshot

pageinspect

passwordcheck

pg_buffercache

pgcrypto

pg_freespacemap

pg_prewarm

pgrowlocks

pg_stat_statements

pgstattuple

pg_surgery

pg_trgm

pg_visibility

postgres_fdw

seg

sepgsql

spi

sslinfo

tablefunc

tcn

test_decoding

tsm_system_rows

tsm_system_time

unaccent

uuid-ossp

xml2

Для некоторых расширений PostgreSQL пока отсутствует переведенная документация. Вы можете обратиться к официальной документации PostgreSQL по адресу https://www.postgresql.org/docs/

adminpack

Модуль adminpack предоставляет ряд вспомогательных функций, которыми могут пользоваться pgAdmin и другие средства администрирования и управления для предоставления дополнительной функциональности, например, для удаленного управления файлами журналов сервера. По умолчанию использовать все эти функции разрешено только суперпользователю, но такое право можно дать и другим пользователям с помощью команды GRANT.

Функции, приведенные в Таблице 1, предоставляют возможность записи в файлы на компьютере, где работает сервер. (См. также функции в Таблице 94, которые открывают доступ только на чтение.) Обычным пользователям они предоставляют доступ только к файлам в каталоге кластера базы данных, но не ограничивают суперпользователей или членов ролей pg_read_server_files или pg_write_server_files. При этом путь может задаваться как относительный, так и абсолютный.

Таблица 1. Функции модуля adminpack

Функция | Описание
pg_catalog.pg_file_write ( filename text, data text, append boolean ) → bigint | Записывает или дописывает данные в текстовый файл.
pg_catalog.pg_file_sync ( filename text ) → void | Сбрасывает файл или каталог на диск.
pg_catalog.pg_file_rename ( oldname text, newname text [, archivename text ] ) → boolean | Переименовывает файл.
pg_catalog.pg_file_unlink ( filename text ) → boolean | Удаляет файл.
pg_catalog.pg_logdir_ls () → setof record | Выводит список файлов журналов в каталоге log_directory.

Функция pg_file_write записывает заданные данные (data) в файл с именем filename. Если флаг append сброшен, этот файл не должен существовать. Если же флаг append установлен, существование этого файла допускается, и в этом случае данные будут дописаны в него. Возвращает число записанных байт.

Функция pg_file_sync синхронизирует с файловой системой заданный файл или каталог с именем filename. В случае неудачи (например, если указанный файл не существует) выдается ошибка. Обратите внимание, что значение data_sync_retry на эту функцию не влияет, и поэтому даже при сбое во время сброса файлов базы данных не будет выдана ошибка уровня PANIC.

Функция pg_file_rename переименовывает файл. Если параметр archivename опущен или равен NULL, она просто переименовывает файл oldname в newname (файл с новым именем не должен существовать). Если параметр archivename задан, она сначала переименовывает newname в archivename (файл с таким именем не должен существовать), а затем переименовывает oldname в newname. В случае сбоя на втором этапе переименовывания она попытается переименовать archivename обратно в newname, прежде чем выдать ошибку. Возвращает true в случае успеха и false, если исходные файлы отсутствуют или их невозможно изменить; в остальных случаях выдаются ошибки.

Функция pg_file_unlink удаляет заданный файл. Возвращает true в случае успеха и false, если заданный файл отсутствует либо при сбое в вызове unlink(); в остальных случаях выдаются ошибки.

Функция pg_logdir_ls возвращает временные метки и пути всех файлов журналов в каталоге log_directory. Чтобы эта функция работала, параметр log_filename должен иметь значение по умолчанию (qhb-%Y-%m-%d_%H%M%S.log).

amcheck

Модуль amcheck предоставляет функции, позволяющие проверять логическую целостность структуры отношений.

Функции проверки B-дерева проверяют различные инварианты в структуре представления определенных отношений. Корректность работы функций метода доступа, стоящих за сканированием индекса и другими важными операциями, зависит от всегда соблюдаемых инвариантов. Например, определенные функции проверяют, в том числе, что все страницы B-дерева содержат элементы в «логическом» порядке (например, у индексов B-деревьев по столбцу text кортежи должны быть упорядочены в лексическом порядке по правилу сортировки). Если этот конкретный инвариант каким-то образом нарушается, можно ожидать, что двоичный поиск на затронутой странице задаст ошибочное направление сканированию индекса, приведя к неверным ответам на запросы SQL. Если структура оказывается допустимой, ошибок не возникает.

Проверка осуществляется теми же процедурами, которые используются при сканировании индекса, и это может быть код пользовательского класса операторов. Например, проверка индекса B-дерева прибегает к сравнениям, выполняемым одной или несколькими вспомогательными функциями B-дерева под номером 1.

В отличие функций проверки B-дерева, которые сообщают о повреждениях, выдавая ошибки, функция проверки кучи verify_heapam проверяет таблицу и пытается вернуть набор строк: по одной строке на каждое обнаруженное повреждение. Несмотря на это, если будут повреждены средства, которые задействует сама функция verify_heapam, она не сможет продолжить работу и вместо этого может выдать ошибку.

Право на выполнение функций amcheck можно выдавать и не суперпользователям, но перед этим следует тщательно проанализировать аспекты безопасности и конфиденциальности данных. Хотя сообщения о повреждениях, выдаваемые этими функциями, делают акцент не на содержимом поврежденных данных, а скорее на их структуре и характере найденных повреждений, злоумышленник, получивший разрешение на выполнение этих функций (особенно если ему также удастся вызвать повреждение данных), сможет из таких сообщений узнать что-то о самих данных.

Функции

bt_index_check(index regclass, heapallindexed boolean) returns void

bt_index_check проверяет, соблюдаются ли в целевом объекте, индексе B-дереве, различные инварианты. Пример использования:

test=# SELECT bt_index_check(index => c.oid, heapallindexed => i.indisunique),
               c.relname,
               c.relpages
FROM pg_index i
JOIN pg_opclass op ON i.indclass[0] = op.oid
JOIN pg_am am ON op.opcmethod = am.oid
JOIN pg_class c ON i.indexrelid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE am.amname = 'btree' AND n.nspname = 'pg_catalog'
-- Не проверять временные таблицы, т. к. они могут относиться к другим сеансам:
AND c.relpersistence != 't'
-- Функция может выдать ошибку без этих условий:
AND c.relkind = 'i' AND i.indisready AND i.indisvalid
ORDER BY c.relpages DESC LIMIT 10;
 bt_index_check |             relname             | relpages
----------------+---------------------------------+----------
                | pg_depend_reference_index       |       43
                | pg_depend_depender_index        |       40
                | pg_proc_proname_args_nsp_index  |       31
                | pg_description_o_c_o_index      |       21
                | pg_attribute_relid_attnam_index |       14
                | pg_proc_oid_index               |       10
                | pg_attribute_relid_attnum_index |        9
                | pg_amproc_fam_proc_index        |        5
                | pg_amop_opr_fam_index           |        5
                | pg_amop_fam_strat_index         |        5
(10 rows)

Этот пример демонстрирует сеанс проверки 10 самых больших индексов каталогов в базе данных «test». Проверка наличия кортежей кучи в виде индексных кортежей запрашивается только для тех из этих индексов, которые являются уникальными. Поскольку ошибки не было, все проверенные индексы представляются логически целостными. Естественно, этот запрос легко можно изменить, чтобы функция bt_index_check вызывалась для всех индексов в базе данных, которые поддерживают эту проверку.

Функция bt_index_check запрашивает блокировку AccessShareLock для целевого индекса и отношения в куче, которому он принадлежит. Это тот же режим блокировки, что запрашивается для отношений обычными операторами SELECT. bt_index_check не проверяет инварианты, охватывающие взаимоотношения потомок/родитель, но проверит наличие всех кортежей кучи в индексе в виде индексных кортежей, когда параметр heapallindexed равен true. Когда в работающей производственной среде требуется регулярная упрощенная проверка, использование bt_index_check часто предоставляет наилучший компромисс между полнотой проверки и ограничением влияния на производительность и доступность приложения.

bt_index_parent_check(index regclass, heapallindexed boolean, rootdescend boolean) returns void

Функция bt_index_parent_check проверяет, соблюдаются ли в целевом объекте, индексе B-дереве, различные инварианты respects. Кроме того, когда аргумент heapallindexed равен true, эта функция проверяет наличие всех кортежей кучи, которые должны в нем находиться. Когда необязательный аргумент rootdescend равен true, при проверке для каждого кортежа на уровне листьев проводится повторный поиск, начиная с корневой страницы. Проверки, которые может осуществить bt_index_parent_check, являются расширенным набором проверок, выполняемых функцией bt_index_check. Функцию bt_index_parent_check можно считать более полным вариантом bt_index_check: в отличие от bt_index_check, bt_index_parent_check проверяет еще и инварианты, которые охватывают взаимоотношения потомок/родитель, в том числе на отсутствие потерянных связей в структуре индекса. bt_index_parent_check следует общему соглашению о выдаче ошибки в случае обнаружения логической несогласованности или другой проблемы.

Функция bt_index_parent_check запрашивает в целевом индексе блокировку ShareLock (которая также запрашивается в отношении в куче). Эти блокировки предотвращают одновременное изменение данных командами INSERT, UPDATE и DELETE. Эти блокировки также предотвращают одновременную обработку нижележащего отношения командой VACUUM и другими сервисными командами. Обратите внимание, что эта функция удерживает блокировки только во время выполнения, а не на протяжении всей транзакции.

Дополнительная проверка, проводимая функцией bt_index_parent_check, в большей степени ориентирована на выявление различных патологических случаев. Такие случаи могут включать в себя неправильно реализованный класс операторов B-дерева, используемый проверяемым индексом, или, гипотетически, неизвестные ошибки в нижележащем коде метода доступа индекса B-дерева. Обратите внимание, что bt_index_parent_check нельзя применять, когда включен режим горячего резерва (т. е. на физических репликах в режиме «только чтение»), в отличие от bt_index_check.

Совет
Функции bt_index_check и bt_index_parent_check выводят сообщения журнала о процессе проверки на уровнях важности DEBUG1 и DEBUG2. Эти сообщения содержат подробную информацию о процессе проверки, которая может представлять интерес для разработчиков QHB. Также эта информация может быть полезна для продвинутых пользователей, поскольку обеспечивает дополнительный контекст в случае, если при проверке действительно обнаружится несогласованность. Чтобы получать сообщения о процессе проверки с подходящим уровнем детализации, необходимо перед запуском проверяющего запроса выполнить в интерактивном сеансе psql:

 SET client_min_messages = DEBUG1;

verify_heapam(relation regclass, on_error_stop boolean, check_toast boolean, skip text, startblock bigint, endblock bigint, blkno OUT bigint, offnum OUT integer, attnum OUT integer, msg OUT text) returns setof record

Проверяет таблицу на наличие структурных повреждений (когда страницы отношения неправильно отформатированы) и логических повреждений (когда страницы структурно не повреждены, но не соответствуют остальной части кластера баз данных).

Принимаются следующие необязательные аргументы:

on_error_stop
Если true, проверка на наличие повреждений останавливается в конце первого блока, в котором обнаружены какие-либо повреждения.
По умолчанию false.

check_toast
Если true, значения TOAST проверяются по TOAST-таблице целевого отношения.
Эта проверка может быть медленной. Кроме того, если TOAST-таблица или ее индекс повреждены, проверка значений TOAST потенциально может привести к сбою сервера, хотя во многих случаях это просто вызовет ошибку.
По умолчанию false.

skip
Если указано значение, отличное от none, проверка на наличие повреждений пропускает блоки, помеченные как полностью видимые или полностью замороженные, в зависимости от указанного значения. Допустимые варианты: all-visible (полностью видимые), all-frozen (полностью замороженные) и none.
По умолчанию none.

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

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

Для каждого выявленного повреждения verify_heapam возвращает строку со следующими столбцами:

blkno
Номер блока, содержащего поврежденную страницу.

offnum
Номер смещения поврежденного кортежа.

attnum
Номер атрибута поврежденного столбца в кортеже, если поврежден именно столбец, а не весь кортеж.

msg
Сообщение с описанием выявленной проблемы.

Дополнительная проверка heapallindexed

Когда аргумент heapallindexed функций проверки B-дерева равен true, для таблицы, связанной с отношением целевого индекса, проводится дополнительная фаза проверки. Она включает «фиктивную» операцию CREATE INDEX, которая проверяет наличие всех гипотетических новых индексных кортежей по временной сводной структуре в памяти (она создается при необходимости во время основной первой фазы проверки). Сводная структура «помечает» каждый кортеж, найденный в целевом индексе. На высоком уровне основная идея проверки heapallindexed состоит в том, что новый индекс, равнозначный уже существующему, целевому индексу, должен содержать только те записи, которые можно найти в существующей структуре.

Дополнительная фаза heapallindexed приводит к значительным издержкам: как правило, проверка выполняется в несколько раз дольше обычного. Однако никакие новые блокировки уровня отношения при проверке heapallindexed не запрашиваются.

Сводная структура ограничивается по объему значением maintenance_work_mem. Чтобы убедиться, что вероятность пропустить несогласованность в кортежах кучи, которые должны быть представлены в индексе, составляет менее 2%, требуется приблизительно 2 байта памяти на кортеж. По мере уменьшения объема памяти в пересчете на кортеж эта вероятность медленно растет. Такой подход значительно ограничивает издержки проверки, при этом лишь немного снижая вероятность выявления проблемы, особенно в инсталляциях, где проверка включена в процедуру регулярного обслуживания. При очередной проверке возникает новый шанс выявить пропущенное единичное отсутствие или повреждение кортежа.

Эффективное использование amcheck

Модуль amcheck может быть полезен для выявления различных типов отказов, которые могут остаться незамеченными при включении контрольных сумм страниц данных. В частности это:

Структурные несоответствия, вызванные некорректной реализацией класса операторов.

В том числе это проблемы, вызванные изменениями правил сравнения в операционной системе. Сравнения данных сортируемого типа, например text, должны быть постоянными (как и все сравнения, применяемые при сканировании индекса B дерева), что подразумевает полную неизменность правил сортировки в операционной системе. Подобные проблемы, хоть и редко, могут возникать при обновлениях правил сортировки в операционной системе. Гораздо чаще проявляются несоответствия порядка сортировки между главным и резервным серверами, вероятно, из-за различия основной версии используемой операционной системы. Такие несоответствия обычно возникают только на резервных серверах, поэтому и выявить их обычно можно только на них.

Если возникает подобная проблема, она может затрагивать не полностью все индексы, построенные с пострадавшим правилом сортировки, просто потому что индексированные значения могут иметь тот же абсолютный порядок, независящий от различий поведения. Дополнительную информацию об использовании в QHB локалей и правил сортировки операционной системы см. в разделах Поддержка языковых стандартов и Поддержка правил сортировки.

Структурные несоответствия между индексами и проиндексированными отношениями в куче (когда выполняется проверка heapallindexed).

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

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

Автоматическая проверка структурной целостности индексов играет важную роль в общем тестировании новых или предлагаемых возможностей QHB, с которыми возможно возникновение логической несогласованности. Такую же роль играет проверка структуры таблицы и связанной информации о видимости и состоянии транзакций. Поэтому одна из очевидных стратегий тестирования — регулярно вызывать функции amcheck при проведении стандартных регрессионных тестов.

Ошибки в файловой системе или подсистеме хранения, когда просто не включены контрольные суммы.

Обратите внимание, что amcheck изучает страницу в том виде, в котором она представлена в некоем буфере общей памяти на момент проверки, если при обращении к блоку тот уже находится в разделяемом буфере. Как следствие, amcheck не обязательно просматривает данные, считанные из файловой системы на момент проверки. Обратите внимание, что когда включены контрольные суммы, amcheck может выдать ошибку из-за несоответствия контрольных сумм, когда в буфер считывается поврежденный блок.

Повреждение, вызванное дефектным ОЗУ или вообще подсистемой памяти.

QHB не защищает от корректируемых ошибок памяти и предполагает, что вы будете применять в работе ОЗУ, использующее коды исправления ошибок (Error Correcting Codes, ECC), соответствующие отраслевому стандарту, или лучшую защиту. Однако память ECC memory обычно защищена только от ошибок в одном бите, и не следует считать ее абсолютной защитой от сбоев, приводящих к повреждению памяти.

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

Структурное повреждение может произойти по причине неисправности оборудования систем хранения или перезаписи либо изменения файлов отношения сторонним ПО. Повреждение такого рода также можно выявить с помощью контрольных сумм страниц данных.

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

В производственных системах наблюдаются разнообразные причины логических повреждений, в том числе: ошибки в серверном ПО QHB, неисправные и непродуманные инструменты резервного копирования и восстановления и ошибки пользователей.

Поврежденные отношения особенно опасны в работающей производственной среде, а точнее, в той среде, где менее всего приветствуется деятельность, сопряженная с высоким риском. По этой причине для диагностики повреждения данных без излишнего риска была разработана функция verify_heapam. Она не в состоянии защитить от всех сбоев сервера, так как даже выполнение вызывающего запроса может быть небезопасным на сильно поврежденной системе. Если повреждены сами каталоги, возможны проблемы с доступом к их таблицам.

В целом, amcheck может доказать только наличие повреждения, но не его отсутствие.

Исправление повреждений

Когда amcheck сигнализирует о повреждением данных, ложные срабатывания практически исключены. amcheck выдает ошибки при возникновении условий, которые никогда не должны наблюдаться по определению, поэтому ошибки amcheck зачастую требуют тщательного анализа.

Общего метода устранения проблем, выявляемых amcheck, не существует. Следует искать корень проблемы, приводящей к нарушению инварианта. Полезную роль в диагностике повреждений, которые выявляет amcheck, может сыграть [pageinspect]. Одна лишь команда REINDEX может быть неэффективна в деле исправления повреждений.

auth_delay

Модуль auth_delay заставляет сервер сделать небольшую паузу перед тем, как выдать сообщение об ошибке аутентификации, чтобы усложнить атаки методом прямого подбора паролей к базам данных. Обратите внимание, что это никак не препятствует атакам типа «отказ в обслуживании» и может даже усугубить их, поскольку процессы, ожидающие сообщения об ошибке аутентификации, все равно будут занимать слоты подключения.

Чтобы этот модуль работал, его нужно загрузить посредством параметра конфигурации shared_preload_libraries в qhb.conf.

Параметры конфигурации

auth_delay.milliseconds (int)

Пауза (в миллисекундах), которую нужно выдержать, прежде чем сообщать об ошибке аутентификации. Значение по умолчанию — 0.

Эти параметры следует задавать в qhb.conf. Обычное использование может выглядеть так:

# qhb.conf
shared_preload_libraries = 'auth_delay'

auth_delay.milliseconds = '500'

Автор

КайГай Кохэй (kaigai@ak.jp.nec.com)

auto_explain

Модуль auto_explain предоставляет возможность автоматического протоколирования планов выполнения медленных операторов, без необходимости выполнения EXPLAIN вручную. Это особенно полезно для обнаружения неоптимизированных запросов в больших приложениях.

Этот модуль не предоставляет функций, доступных из SQL. Чтобы использовать его, просто загрузите его на сервер. Это можно сделать в отдельном сеансе:

LOAD 'auto_explain';

(Для этого нужно быть суперпользователем.) Более типична практика, когда он предварительно загружается в некоторые или все сеансы путем включения auto_explain в переменную session_preload_libraries или shared_preload_libraries в файле конфигурации qhb.conf. После этого можно отслеживать неожиданно медленные запросы, независимо от того, где они происходят. Разумеется, это приводит к издержкам.

Параметры конфигурации

Есть несколько параметров конфигурации, которые управляют поведением auto_explain. Обратите внимание, что по умолчанию они ничего не делают, поэтому если вы хотите получить какие-либо результаты, необходимо установить как минимум параметр auto_explain.log_min_duration.

auto_explain.log_min_duration (integer)

Параметр auto_explain.log_min_duration задает минимальное время выполнения оператора в миллисекундах, при превышении которого план оператора будет протоколироваться. При значении 0 протоколируются все планы. При значении -1 (по умолчанию) протоколирование планов выключается. Например, если установить значение 250ms, то будут протоколироваться все операторы, выполняющиеся 250 мс и дольше. Изменить этот параметр могут только суперпользователи.

auto_explain.log_analyze (boolean)

При включении параметра auto_explain.log_analyze в журнал будет записываться вывод команды EXPLAIN ANALYZE, а не простой EXPLAIN. По умолчанию этот параметр выключен. Изменить этот параметр могут только суперпользователи.

Примечание
Когда этот параметр включен, замер времени на уровне узлов плана производится для всех операторов, даже если они выполняются недостаточно долго для протоколирования. Это может оказать крайне негативное влияние на производительность. Выключение auto_explain.log_timing нейтрализует это влияние, но при этом собирается меньше информации.

auto_explain.log_buffers (boolean)

Параметр auto_explain.log_buffers определяет, будет ли при протоколировании плана выполнения выводиться статистика использования буферов; он равнозначен параметру BUFFERS команды EXPLAIN. Этот параметр действует только при включенном параметре auto_explain.log_analyze. По умолчанию этот параметр выключен. Изменить этот параметр могут только суперпользователи.

auto_explain.log_wal (boolean)

Параметр auto_explain.log_wal определяет, будет ли при протоколировании плана выполнения выводиться статистка использования WAL; он равнозначен параметру WAL команды EXPLAIN. Этот параметр действует только при включенном параметре auto_explain.log_analyze. По умолчанию этот параметр выключен. Изменить этот параметр могут только суперпользователи.

auto_explain.log_timing (boolean)

Параметр auto_explain.log_timing определяет, будет ли при протоколировании плана выполнения выводиться информация о длительности на уровне узлов; он равнозначен параметру TIMING команды EXPLAIN. Издержки от постоянного чтения системных часов могут значительно замедлить запросы в некоторых системах, поэтому может иметь смысл выключать этот параметр, когда нужно знать только количество строк, а не точную длительность каждого узла. Этот параметр действует только при включенном параметре auto_explain.log_analyze. По умолчанию этот параметр выключен. Изменить этот параметр могут только суперпользователи.

auto_explain.log_triggers (boolean)

При включении параметра auto_explain.log_triggers в журнал будет записываться статистика выполнения триггеров. Этот параметр действует только при включенном параметре auto_explain.log_analyze. По умолчанию этот параметр выключен. Изменить этот параметр могут только суперпользователи.

auto_explain.log_verbose (boolean)

Параметр auto_explain.log_verbose определяет, будут ли при протоколировании плана выполнения выводиться подробные сведения; он равнозначен параметру VERBOSE команды EXPLAIN. По умолчанию этот параметр выключен. Изменить этот параметр могут только суперпользователи.

auto_explain.log_settings (boolean)

Параметр auto_explain.log_settings определяет, будет ли при протоколировании плана выполнения выводиться информация об измененных параметрах конфигурации. При этом в вывод попадут только те параметры, которые влияют на планирование запросов и имеют значения, отличающиеся от встроенных значений по умолчанию. По умолчанию этот параметр выключен. Изменить этот параметр могут только суперпользователи.

auto_explain.log_format (enum)

Параметр auto_explain.log_format выбирает формат вывода для команды EXPLAIN. Допустимые значения: text, xml, json и yaml. Значение по умолчанию — text. Изменить этот параметр могут только суперпользователи.

auto_explain.log_level (enum)

Параметр auto_explain.log_level выбирает уровень, с которым auto_explain будет протоколировать планы запросов. Допустимые значения: DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, INFO, NOTICE, WARNING и LOG. Значение по умолчанию — LOG. Изменить этот параметр могут только суперпользователи.

auto_explain.log_nested_statements (boolean)

При включении параметра auto_explain.log_nested_statements протоколированию могут подлежать и вложенные операторы (операторы, выполняемые внутри функции). При его выключении протоколируются только планы запросов верхнего уровня. По умолчанию этот параметр выключен. Изменить этот параметр могут только суперпользователи.

auto_explain.sample_rate (real)

Параметр auto_explain.sample_rate задает для auto_explain процент операторов, который будет рассматриваться в каждом сеансе. Значение по умолчанию равно 1, то есть рассматриваются все запросы. Вложенные операторы рассматриваются полностью — либо все, либо никакой из них. Изменить этот параметр могут только суперпользователи.

В обычной ситуации эти параметры устанавливаются в qhb.conf, хотя суперпользователи могут изменять их «на лету» в рамках своих сеансов. Типичное их использование может выглядеть так:

# qhb.conf
session_preload_libraries = 'auto_explain'

auto_explain.log_min_duration = '3s'

Пример

qhb=# LOAD 'auto_explain';
qhb=# SET auto_explain.log_min_duration = 0;
qhb=# SET auto_explain.log_analyze = true;
qhb=# SELECT count(*)
        FROM pg_class, pg_index
        WHERE oid = indrelid AND indisunique;

В результате можно получить такой вывод:

LOG:  duration: 3.651 ms  plan:
  Query Text: SELECT count(*)
              FROM pg_class, pg_index
              WHERE oid = indrelid AND indisunique;
  Aggregate  (cost=16.79..16.80 rows=1 width=0) (actual time=3.626..3.627 rows=1 loops=1)
    ->  Hash Join  (cost=4.17..16.55 rows=92 width=0) (actual time=3.349..3.594 rows=92 loops=1)
          Hash Cond: (pg_class.oid = pg_index.indrelid)
          ->  Seq Scan on pg_class  (cost=0.00..9.55 rows=255 width=4) (actual time=0.016..0.140 rows=255 loops=1)
          ->  Hash  (cost=3.02..3.02 rows=92 width=4) (actual time=3.238..3.238 rows=92 loops=1)
                Buckets: 1024  Batches: 1  Memory Usage: 4kB
                ->  Seq Scan on pg_index  (cost=0.00..3.02 rows=92 width=4) (actual time=0.008..3.187 rows=92 loops=1)
                      Filter: indisunique

Автор

Такахиро Итагаки (itagaki.takahiro@oss.ntt.co.jp)

bloom

Модуль bloom предоставляет индексный метод доступа, основанный на фильтрах Блума.

Фильтр Блума представляет собой экономичную структуру данных, которая применяется для проверки того, является ли элемент членом множества. В виде индексного метода доступа он позволяет быстро исключать неподходящие кортежи по сигнатурам, размер которых определяется при создании индекса.

Сигнатура — это неточное представление проиндексированных атрибутов, вследствие чего оно склонно выдавать ложноположительные результаты; то есть оно может показывать, что элемент содержится в множестве, хотя это не так. Поэтому результаты поиска по индексу всегда должны перепроверяться с помощью фактических значений атрибутов из записи в куче. Чем больше размер сигнатуры, тем меньше вероятность ложного срабатывания и, следовательно, число бесполезных обращений к куче, но, разумеется, это приводит к увеличению индекса и, соответственно, замедлению сканирования.

Этот тип индекса наиболее полезен, когда в таблице много атрибутов и в запросах проверяются их произвольные сочетания. Традиционный индекс В-дерево быстрее индекса Блума, но для поддержки всех возможных запросов может потребоваться много индексов В-деревьев, при том что индекс Блума нужен всего один. Обратите внимание, однако, что индексы Блума поддерживают только запросы на проверку равенства, тогда как индексы В-деревья могут также применяться при проверке неравенств и поисках в диапазоне.

Параметры

Индекс bloom принимает в своем предложении WITH следующие параметры:

length

Длина каждой сигнатуры (записи индекса) в битах, округленная вверх до ближайшего числа, кратного 16. Значение по умолчанию — 80 бит, а максимальное значение — 4096.

col1 — col32

Количество битов, генерируемых для каждого столбца индекса. В имени параметра отражается номер столбца индекса, которым этот параметр управляет. Значение по умолчанию — 2 бита, а максимальное значение — 4095. Параметры для неиспользуемых столбцов индекса игнорируются.

Примеры

Это пример создания индекса bloom:

CREATE INDEX bloomidx ON tbloom USING bloom (i1,i2,i3)
       WITH (length=80, col1=2, col2=2, col3=4);

Будет создан индекс с длиной сигнатуры 80 битов, в которой атрибуты i1 и i2 отображаются в 2 бита, а атрибут i3 — в 4. Указания length, col1 и col2 можно было бы опустить, поскольку в них задаются значения по умолчанию.

Ниже приводится более полный пример определения и использования индекса Блума, а также его сравнение с равнозначным индексом В-деревом. Индекс Блума значительно меньше индекса В-дерева, и при этом может работать быстрее.

=# CREATE TABLE tbloom AS
   SELECT
     (random() * 1000000)::int as i1,
     (random() * 1000000)::int as i2,
     (random() * 1000000)::int as i3,
     (random() * 1000000)::int as i4,
     (random() * 1000000)::int as i5,
     (random() * 1000000)::int as i6
   FROM
  generate_series(1,10000000);
SELECT 10000000

Последовательное сканирование по этой большой таблице занимает много времени:

=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451;
                                              QUERY PLAN
-------------------------------------------------------------------​-----------------------------------
 Seq Scan on tbloom  (cost=0.00..2137.14 rows=3 width=24) (actual time=16.971..16.971 rows=0 loops=1)
   Filter: ((i2 = 898732) AND (i5 = 123451))
   Rows Removed by Filter: 100000
 Planning Time: 0.346 ms
 Execution Time: 16.988 ms
(5 rows)

Даже с индексом В-деревом сканирование остается последовательным:

=# CREATE INDEX btreeidx ON tbloom (i1, i2, i3, i4, i5, i6);
CREATE INDEX
=# SELECT pg_size_pretty(pg_relation_size('btreeidx'));
 pg_size_pretty
----------------
 3976 kB
(1 row)
=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451;
                                              QUERY PLAN
-------------------------------------------------------------------​-----------------------------------
 Seq Scan on tbloom  (cost=0.00..2137.00 rows=2 width=24) (actual time=12.805..12.805 rows=0 loops=1)
   Filter: ((i2 = 898732) AND (i5 = 123451))
   Rows Removed by Filter: 100000
 Planning Time: 0.138 ms
 Execution Time: 12.817 ms
(5 rows)

Если определить для таблицы индекс Блума, поиск такого рода осуществляется эффективнее, нежели с индексом В-деревом:

=# CREATE INDEX bloomidx ON tbloom USING bloom (i1, i2, i3, i4, i5, i6);
CREATE INDEX
=# SELECT pg_size_pretty(pg_relation_size('bloomidx'));
 pg_size_pretty
----------------
 1584 kB
(1 row)
=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451;
                                                     QUERY PLAN
-------------------------------------------------------------------​--------------------------------------------------
 Bitmap Heap Scan on tbloom  (cost=1792.00..1799.69 rows=2 width=24) (actual time=0.388..0.388 rows=0 loops=1)
   Recheck Cond: ((i2 = 898732) AND (i5 = 123451))
   Rows Removed by Index Recheck: 29
   Heap Blocks: exact=28
   ->  Bitmap Index Scan on bloomidx  (cost=0.00..1792.00 rows=2 width=0) (actual time=0.356..0.356 rows=29 loops=1)
         Index Cond: ((i2 = 898732) AND (i5 = 123451))
 Planning Time: 0.099 ms
 Execution Time: 0.408 ms
(8 rows)

Итак, основная проблема поиска по В-дереву состоит в том, что В-дерево неэффективно, когда условия поиска не ограничивают ведущие столбцы индекса. Более успешной стратегией при использовании В-дерева будет создание отдельного индекса для каждого столбца. Тогда планировщик выберет примерно такой план:

=# CREATE INDEX btreeidx1 ON tbloom (i1);
CREATE INDEX
=# CREATE INDEX btreeidx2 ON tbloom (i2);
CREATE INDEX
=# CREATE INDEX btreeidx3 ON tbloom (i3);
CREATE INDEX
=# CREATE INDEX btreeidx4 ON tbloom (i4);
CREATE INDEX
=# CREATE INDEX btreeidx5 ON tbloom (i5);
CREATE INDEX
=# CREATE INDEX btreeidx6 ON tbloom (i6);
CREATE INDEX
=# EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451;
                                                        QUERY PLAN
-------------------------------------------------------------------​--------------------------------------------------------
 Bitmap Heap Scan on tbloom  (cost=24.34..32.03 rows=2 width=24) (actual time=0.028..0.029 rows=0 loops=1)
   Recheck Cond: ((i5 = 123451) AND (i2 = 898732))
   ->  BitmapAnd  (cost=24.34..24.34 rows=2 width=0) (actual time=0.027..0.027 rows=0 loops=1)
         ->  Bitmap Index Scan on btreeidx5  (cost=0.00..12.04 rows=500 width=0) (actual time=0.026..0.026 rows=0 loops=1)
               Index Cond: (i5 = 123451)
         ->  Bitmap Index Scan on btreeidx2  (cost=0.00..12.04 rows=500 width=0) (never executed)
               Index Cond: (i2 = 898732)
 Planning Time: 0.491 ms
 Execution Time: 0.055 ms
(9 rows)

Хотя этот запрос выполняется быстрее, чем с каким-либо одиночным индексом, мы платим за это увеличением размера индекса. Каждый индекс В-дерево занимает 2 МБ, так что общий объем индексов составляет 12 МБ, что в восемь раз больше объема индекса Блума.

Интерфейс класса операторов

Класс операторов для индексов Блума требует наличия только хеш-функции для индексируемого типа данных и оператора равенства для поиска. Этот пример демонстрирует определение класса операторов для типа данных text:

CREATE OPERATOR CLASS text_ops
DEFAULT FOR TYPE text USING bloom AS
    OPERATOR    1   =(text, text),
    FUNCTION    1   hashtext(text);

Ограничения

В этот модуль включены только классы операторов для int4 и text.

При поиске поддерживается только оператор =. Но в будущем возможно добавление поддержки для массивов с операциями объединения и пересечения.

Метод доступа bloom не поддерживает уникальные индексы (UNIQUE).

Метод доступа bloom не поддерживает поиск значений NULL.

Авторы

Федор Сигаев (teodor@postgrespro.ru), Postgres Professional, Москва, Россия

Александр Коротков (a.korotkov@postgrespro.ru), Postgres Professional, Москва, Россия

Олег Бартунов (obartunov@postgrespro.ru), Postgres Professional, Москва, Россия

btree_gin

Модуль btree_gin предоставляет типовые классы операторов GIN, реализующие поведение, схожее с тем, что реализуют классы В-дерева, для типов данных int2, int4, int8, float4, float8, timestamp with time zone, timestamp without time zone, time with time zone, time without time zone, date, interval, oid, money, "char", varchar, text, bytea, bit, varbit, macaddr, macaddr8, inet, cidr, uuid, name, bool, bpchar и всех перечисляемых типов (enum).

В целом, эти классы операторов не будут работать быстрее аналогичных стандартных методов индекса В-дерева, и им не хватает одной важной функциональности стандартного кода В-дерева: способности обеспечивать соблюдение уникальности. Тем не менее, они полезны для тестирования GIN, а также в качестве основы для разработки других классов операторов GIN. Кроме того, для запросов, где проверяется и столбец с индексом GIN, и столбец с индексом B-деревом, возможно, удобнее будет создать многостолбцовый индекс GIN, который использует один из этих классов операторов, чем создавать два отдельных индекса, которые придется объединять, вычисляя AND битовых карт.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Пример использования

CREATE TABLE test (a int4);
-- создать индекс
CREATE INDEX testidx ON test USING GIN (a);
-- запрос
SELECT * FROM test WHERE a < 10;

Авторы

Федор Сигаев (teodor@stack.net) и Олег Бартунов (oleg@sai.msu.su). Дополнительную информацию можно найти на странице http://www.sai.msu.su/~megera/oddmuse/index.cgi/Gin.

btree_gist

Модуль btree_gist предоставляет классы операторов индексов GiST, реализующие поведение, схожее с тем, что реализуют классы В-дерева, для типов данных int2, int4, int8, float4, float8, numeric, timestamp with time zone, timestamp without time zone, time with time zone, time without time zone, date, interval, oid, money, char, varchar, text, bytea, bit, varbit, macaddr, macaddr8, inet, cidr, uuid и всех перечисляемых типов (enum).

В целом, эти классы операторов не будут работать быстрее аналогичных стандартных методов индекса В-дерева, и им не хватает одной важной функциональности стандартного кода В-дерева: способности обеспечивать соблюдение уникальности. Тем не менее, они предоставляют несколько других функциональностей (описаны ниже), недоступных с индексом В-деревом. Кроме того, эти классы операторов полезны, когда требуется многостолбцовый индекс GiST, в котором некоторые столбцы имеют типы данных, индексируемые только с GiST, а другие — простые типы данных. Наконец, эти классы операторов полезны для тестирования GiST, а также в качестве основы для разработки других классов операторов GiST.

Помимо типичных операторов поиска по B-дереву, btree_gist также поддерживает использование индекса для операции <> («не равно»). Это может быть полезно в сочетании с ограничением-исключением, как описано ниже.

Также, для типов данных, имеющих естественную метрику расстояния, btree_gist определяет оператор расстояния <-> и поддерживает использование индексов GiST для поиска ближайших соседей с помощью этого оператора. Операторы расстояния предоставлены для типов int2, int4, int8, float4, float8, timestamp with time zone, timestamp without time zone, time without time zone, date, interval, oid и money.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Пример использования

Простой пример использования btree_gist вместо btree:

CREATE TABLE test (a int4);
-- создать индекс
CREATE INDEX testidx ON test USING GIST (a);
-- запрос
SELECT * FROM test WHERE a < 10;
-- поиск ближайших соседей: найти десять записей, ближайший к "42"
SELECT *, a <-> 42 AS dist FROM test ORDER BY a <-> 42 LIMIT 10;

Использование ограничения-исключения для реализации правила, по которому в клетке в зоопарке могут содержаться животные только одного вида:

=> CREATE TABLE zoo (
  cage   INTEGER,
  animal TEXT,
  EXCLUDE USING GIST (cage WITH =, animal WITH <>)
);

=> INSERT INTO zoo VALUES(123, 'zebra');
INSERT 0 1
=> INSERT INTO zoo VALUES(123, 'zebra');
INSERT 0 1
=> INSERT INTO zoo VALUES(123, 'lion');
ERROR:  conflicting key value violates exclusion constraint "zoo_cage_animal_excl"
DETAIL:  Key (cage, animal)=(123, lion) conflicts with existing key (cage, animal)=(123, zebra).
=> INSERT INTO zoo VALUES(124, 'lion');
INSERT 0 1

Авторы

Федор Сигаев (teodor@stack.net), Олег Бартунов (oleg@sai.msu.su), Янко Рихтер (jankorichter@yahoo.de) и Пол Юнгвирт (pj@illuminatedcomputing.com). Дополнительную информацию можно найти на странице http://www.sai.msu.su/~megera/postgres/gist/.

citext

Модуль citext предоставляет тип данных для строк символов, не учитывающих регистр, citext. По сути, сравнивая значения, он вызывает внутри себя функцию lower. В остальном он ведет себя почти так же, как тип text.

Совет
Возможно, вместо этого модуля имеет смысл использовать недетерминированные правила сортировки (см. подраздел Недетерминированные правила сортировки). Их можно использовать для сравнения без учета регистра, без учета ударения и в других комбинациях; при этом они более корректно обрабатывают особые случаи Unicode.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Обоснование

Стандартный способ выполнить сравнение строк без учета регистра в QHB заключается в использовании функции lower при сравнении значений, например

SELECT * FROM tab WHERE lower(col) = LOWER(?);

Этот подход работает довольно хорошо, но имеет ряд недостатков:

Операторы SQL становятся громоздкими, и нужно не забывать всегда обрабатывать функцией lower и столбец, и значение запроса.

Индекс не будет использоваться, если только не создать функциональный индекс с помощью функции lower.

Если объявить столбец как UNIQUE или PRIMARY KEY, неявно сгенерированный индекс будет чувствительным к регистру. Поэтому он бесполезен для регистронезависимого поиска и не будет обеспечивать уникальность без учета регистра.

Тип данных citext позволяет исключить вызовы lower в запросах SQL и позволяет сделать первичный ключ регистронезависимым. Тип citext учитывает локаль, так же, как тип text, что означает, что сравнение символов в верхнем и нижнем регистре зависит от настройки параметра LC_CTYPE для базы данных. Это поведение, опять же, идентично использованию lower в запросах. Но поскольку оно реализуется прозрачно типом данных, в самих запросах дополнительно не нужно ничего делать.

Как его использовать

Ниже приведен простой пример использования:

CREATE TABLE users (
    nick CITEXT PRIMARY KEY,
    pass TEXT   NOT NULL
);

INSERT INTO users VALUES ( 'larry',  sha256(random()::text::bytea) );
INSERT INTO users VALUES ( 'Tom',    sha256(random()::text::bytea) );
INSERT INTO users VALUES ( 'Damian', sha256(random()::text::bytea) );
INSERT INTO users VALUES ( 'NEAL',   sha256(random()::text::bytea) );
INSERT INTO users VALUES ( 'Bjørn',  sha256(random()::text::bytea) );

SELECT * FROM users WHERE nick = 'Larry';

Оператор SELECT вернет один кортеж, несмотря на то, что в столбце nick указано значение larry, а в запросе — Larry.

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

Модуль citext выполняет сравнения, приводя каждую строку к нижнему регистру (как если бы была вызвана функция lower) и затем сравнивая результаты как обычно. Так, например, две строки считаются равными, если функция lower выдаст для них одинаковые результаты.

Чтобы как можно точнее имитировать правило сортировки без учета регистра, в этом модуле имеются специальные, citext-специфичные версии ряда операторов и функций для обработки строки. Так, например, операторы регулярных выражений ~ и ~* демонстрируют то же поведение, когда применяются к типу citext: они оба не учитывают регистр. Это же распространяется на операторы !~ и !~*, а также операторы LIKE ~~, ~~*, !~~ и !~~*. Если требуется, чтобы эти операторы учитывали регистр, можно привести их аргументы к типу text.

Точно так же, все следующие функции выполняют сопоставления без учета регистра, если их аргументы имеют тип citext:

regexp_match()

regexp_matches()

regexp_replace()

regexp_split_to_array()

regexp_split_to_table()

replace()

split_part()

strpos()

translate()

Для функций с регулярными выражениями, если требуется сопоставление с учетом регистра, можно добавить флаг «c», чтобы принудительно включить этот режим. Или, как вариант, привести аргумент к типу text, прежде чем вызвать одну из этих функций.

Ограничения

Смена регистра символов в citext зависит от настройки параметра LC_CTYPE для вашей базы данных. Таким образом, как будут сравниваться значения, определяется при создании базы данных. По определениям стандарта Unicode это сравнение не будет истинно регистронезависимым. В сущности, это означает, что если вас устраивает установленное правило сортировки, вас должны устраивать и сравнения citext. Но если в вашей базе данных хранятся данные на разных языках, пользователи одного языка могут получать неожиданные результаты запросов, если правило сортировки предназначено для другого языка.

К значениям данных или столбцам citext можно добавлять указание COLLATE. В настоящее время операторы citext принимают такое явное указание COLLATE при сравнении строк в нижнем регистре, но изначальное приведение к нижнему регистру всегда выполняется согласно настройкам LC_CTYPE базы данных (как если бы указывалось COLLATE "default"). Это может измениться в будущем, чтобы на обоих этапах учитывалось указание COLLATE во входных данных.

Тип citext не так эффективен, как text, потому что функции операторов и функции сравнения для B-дерева должны делать копии данных и переводить их в нижний регистр для сравнения. Кроме того, только тип text может поддерживать исключение дубликатов в B-дереве. Тем не менее, с citext сопоставление без учета регистра реализуется немного эффективнее, чем с использованием lower.

Тип citext не особенно поможет, если вам нужно сравнивать данные с учетом регистра в одних контекстах, и без учета регистра — в других. Стандартное решение — использовать тип text и вручную применять функцию lower, когда нужно выполнить сравнение без учета регистра; это прекрасно работает, если регистронезависимое сравнение требуется выполнять изредка. Если же сравнение почти всегда должно быть регистронезависимым и только иногда проводится с учетом регистра, имеет смысл сохранять данные в виде citext и явно приводить столбец к типу text, когда требуется регистрозависимое сравнение. В любом случае, чтобы оба варианта поиска были быстрыми, вам понадобится два индекса.

Схема, содержащая операторы citext, должна находиться в текущем пути поиска (search_path; обычно это схема public); в противном случае будут вызываться обычные, чувствительные к регистру операторы для типа text.

Подход с переводом строк в нижний регистр для сравнения некорректно работает в некоторых особых случаях Unicode, например, когда одной букве в верхнем регистре соответствуют две буквы в нижнем регистре. По этой причине в Unicode различаются понятия преобразование регистра и выравнивание регистра. Чтобы сравнение производилось корректно и в этих случаях, используйте вместо citext недетерминированные правила сортировки.

Автор

Дэвид Е. Уилер (david@kineticode.com)

Разработку вдохновил оригинальный модуль citext авторства Дональда Фрейзера.

cube

Этот модуль реализует тип данных cube для представления многомерных кубов.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Синтаксис

В Таблице 2 показаны допустимые внешние представления типа cube. Буквы x, y и т. д. обозначают числа с плавающей запятой.

Таблица 2. Внешние представления кубов

Внешний синтаксис | Значение
x | Одномерная точка (или одномерный интервал нулевой длины)
(x) | То же, что и выше
x1,x2,...,xn | Точка в n-мерном пространстве, представленная внутри как куб нулевого объема
(x1,x2,...,xn) | То же, что и выше
(x),(y) | Одномерный интервал, начинающийся в точке x и заканчивающийся в y, либо наоборот; порядок значения не имеет
[(x),(y)] | То же, что и выше
(x1,...,xn),(y1,...,yn) | n-мерный куб, представленный парой диагонально противоположных углов
[(x1,...,xn),(y1,...,yn)] | То же, что и выше

В каком порядке вводятся противоположные углы куба, не имеет значения. Функции, принимающие тип cube, автоматически меняют углы местами, если нужно создать единое внутреннее представление «левый нижний — правый верхний». Когда эти углы совмещаются, в cube для экономии пространства хранится только один угол с флагом «является точкой».

Пробелы во входных данных игнорируются, так что [(x),(y)] не отличается от [ ( x ), ( y ) ].

Точность

Значения хранятся внутри как 64-битные числа с плавающей запятой. Это означает, что числа с более чем 16 значащими цифрами будут усекаться.

Применение

В Таблице 3 показаны специальные операторы, предоставляемые для работы с типом cube.

Таблица 3. Операторы для кубов

Оператор
Описание

cube && cube → boolean
Кубы пересекаются?

cube @> cube → boolean
Первый куб содержит второй?

cube <@ cube → boolean
Первый куб содержится во втором?

cube -> integer → float8
Извлекает n-ю координату куба (считая с 1).

cube ~> integer → float8
Извлекает n-ю координату куба, считая следующим образом: n = 2 * k - 1 обозначает нижнюю границу куба k-й размерности, а n = 2 * k обозначает верхнюю границу k-й размерности. Отрицательные n обозначают обратное значение соответствующей положительной координаты. Этот оператор разработан для поддержки KNN-GiST.

cube <-> cube → float8
Вычисляет евклидово расстояние между двумя кубами.

cube <#> cube → float8
Вычисляет расстояние городских кварталов (метрику L-1) между двумя кубами.

cube <=> cube → float8
Вычисляет расстояние Чебышева (метрику L-бесконечность) между двумя кубами.

Помимо операторов, показанных выше, для типа cube имеются обычные операторы сравнения, показанные в Таблице 1 раздела Функции и операторы сравнения. Эти операторы сначала сравнивают первые координаты, и если они равны, сравнивают вторые и т. д. Они предназначены в основном для поддержки класса операторов индекса В-дерева для типа cube, который может быть полезен, например, если вы хотите создать наложить ограничение UNIQUE на столбец типа cube. В других случаях эта сортировка не очень полезна с практической точки зрения.

Модуль cube также предоставляет класс операторов индекса GiST для значений cube. Индекс GiST для cube можно использовать для поиска значений при помощи операторов =, &&, @> и <@ в предложениях WHERE.

Кроме того, индекс GiST для cube можно использовать для поиска ближайших соседей при помощи операторов метрики <->, <#> и <=> в предложениях ORDER BY. Например, ближайшего соседа точки в трехмерном пространстве (0.5, 0.5, 0.5) можно эффективно найти так:

SELECT c FROM test ORDER BY c <-> cube(array[0.5,0.5,0.5]) LIMIT 1;

Оператор ~> можно также использовать таким образом, чтобы эффективно выбрать первые несколько значений, отсортированных по выбранной координате. Например, чтобы получить первые несколько кубов, упорядоченных по возрастанию первой координаты (левого нижнего угла), можно использовать следующий запрос:

SELECT c FROM test ORDER BY c ~> 1 LIMIT 5;

А чтобы получить двумерные кубы, упорядоченные по убыванию первой координаты (правого верхнего угла):

SELECT c FROM test ORDER BY c ~> 3 DESC LIMIT 5;

В Таблице 4 показаны все доступные функции.

Таблица 4. Функции для работы с кубами

Функция
Описание
Пример(ы)

cube ( float8 ) → cube
Создает одномерный куб, у которого обе координаты равны.
cube(1) → (1)

cube ( float8, float8 ) → cube
Создает одномерный куб.
cube(1, 2) → (1),(2)

cube ( float8[] ) → cube
Создает куб нулевого объема, используя координаты, определяемые массивом.
cube(ARRAY[1,2,3]) → (1, 2, 3)

cube ( float8[], float8[] ) → cube
Создает куб с координатами правого верхнего и левого нижнего углов, определяемыми двумя массивами, которые должны быть одинаковой длины.
cube(ARRAY[1,2], ARRAY[3,4]) → (1, 2),(3, 4)

cube ( cube, float8 ) → cube
Создает новый куб, добавляя размерность к существующему кубу с одинаковым значением новой координаты для обоих углов. Это полезно для поэтапного построения кубов из вычисляемых значений.
cube('(1,2),(3,4)'::cube, 5) → (1, 2, 5),(3, 4, 5)

cube ( cube, float8, float8 ) → cube
Создает новый куб, добавляя размерность к существующему кубу. Это полезно для поэтапного построения кубов из вычисляемых значений.
cube('(1,2),(3,4)'::cube, 5, 6) → (1, 2, 5),(3, 4, 6)

cube_dim ( cube ) → integer
Возвращает число размерностей куба.
cube_dim('(1,2),(3,4)') → 2

cube_ll_coord ( cube, integer ) → float8
Возвращает значение n-й координаты левого нижнего угла куба.
cube_ll_coord('(1,2),(3,4)', 2) → 2

cube_ur_coord ( cube, integer ) → float8
Возвращает значение n-й координаты правого верхнего угла куба.
cube_ur_coord('(1,2),(3,4)', 2) → 4

cube_is_point ( cube ) → boolean
Возвращает true, если куб является точкой, то есть если два определяющих его угла совпадают.
cube_is_point(cube(1,1)) → t

cube_distance ( cube, cube ) → float8
Возвращает расстояние между двумя кубами. Если оба куба являются точками, вычисляется обычная функция расстояния.
cube_distance('(1,2)', '(3,4)') → 2.8284271247461903

cube_subset ( cube, integer[] ) → cube
Создает новый куб из существующего, используя список индексов размерностей из массива. Может применяться для получения конечных точек в одном измерении, для удаления измерений или изменения их порядка.
cube_subset(cube('(1,3,5),(6,7,8)'), ARRAY[2]) → (3),(7)
cube_subset(cube('(1,3,5),(6,7,8)'), ARRAY[3,2,1,1]) → (5, 3, 1, 1),(8, 7, 6, 6)

cube_union ( cube, cube ) → cube
Создает объединение двух кубов.
cube_union('(1,2)', '(3,4)') → (1, 2),(3, 4)

cube_inter ( cube, cube ) → cube
Создает пересечение двух кубов.
cube_inter('(1,2)', '(3,4)') → (3, 4),(1, 2)

cube_enlarge ( c cube, r double, n integer ) → cube
Увеличивает размер куба на заданный радиус r как минимум в n измерениях. Если радиус отрицательных, куб, наоборот, уменьшается. Все определенные размерности изменяются на величину радиуса r. Координаты левого нижнего угла уменьшаются на r, а координаты правого верхнего угла увеличиваются на r. Если координата левого нижнего угла становится больше соответствующей координаты правого верхнего угла (это происходит, только когда r < 0), обе координаты устанавливаются в свое среднее значение. Если n превышает число определенных размерностей и куб увеличивается (r > 0), то добавляются дополнительные размерности, недостающие до n; в качестве начального значения для дополнительных координат используется 0. Эта функция полезна для создания вокруг точки ограничивающих прямоугольников с целью поиска ближайших точек.
cube_enlarge('(1,2),(3,4)', 0.5, 3) → (0.5, 1.5, -0.5),(3.5, 4.5, 0.5)

Поведение по умолчанию

Я полагаю, что это объединение:

select cube_union('(0,5,2),(2,3,1)', '0');
cube_union
-------------------
(0, 0, 0),(2, 5, 2)
(1 row)

не противоречит здравому смыслу, как и это пересечение

select cube_inter('(0,-1),(1,1)', '(-2),(2)');
cube_inter
-------------
(0, 0),(1, 0)
(1 row)

Во всех бинарных операциях с кубами разных размерностей я полагаю, что куб с меньшей размерностью будет декартовой проекцией; т. е. в опущенных в строковом представлении координатах находятся нули. Значит, показанные выше примеры равнозначны следующим:

cube_union('(0,5,2),(2,3,1)','(0,0,0),(0,0,0)');
cube_inter('(0,-1),(1,1)','(-2,0),(2,0)');

В следующем предикате включения применяется синтаксис точек, хотя фактически второй аргумент представляется внутри кубом. Этот синтаксис избавляет от необходимости определять отдельный тип точек и функции для предикатов (box,point).

select cube_contains('(0,0),(1,1)', '0.5,0.5');
cube_contains
--------------
t
(1 row)

Примечания

Примеры использования можно увидеть в регрессионном тесте sql/cube.sql.

Во избежание некорректного применения этого типа, число размерностей кубов ограничено значением 100. Если вы хотите его увеличить, его можно изменить в cubedata.h.

Благодарности

Оригинальный автор: Джин Селков мл. (selkovjr@mcs.anl.gov), Аргоннская национальная лаборатория, Отдел математики и компьютерных наук.

Я благодарен в первую очередь профессору Джо Геллерштейну (https://dsf.berkeley.edu/jmh/) за пояснение сути GiST (http://gist.cs.berkeley.edu/) и его бывшему студенту, Энди Донгу, за пример, написанный для Illustra. Я также признателен всем разработчикам Postgres в настоящем и прошлом за возможность создать мой собственный мир и спокойно жить в нем. Еще я хотел бы выразить признательность Аргоннской лаборатории и Министерству энергетики США за годы постоянной поддержки моих исследований в области баз данных.

Небольшие изменения в этот пакет внес Бруно Вольф III (bruno@wolff.to) в августе/сентябре 2002 г. В том числе он перешел от одинарной к двойной точности и добавил несколько новых функций.

Дополнительные изменения внес Джошуа Рейх (josh@root.net) в июле 2006 г. В частности, он добавил cube(float8[], float8[]), подчистил код и перевел его на протокол вызовов версии V1 с устаревшего протокола V0.

dblink

Модуль dblink обеспечивает подключения к другим базам данных QHB из сеанса базы данных.

См. также описание модуля [postgres_fdw], который предоставляет примерно ту же функциональность, используя более современную и стандартизированную инфраструктуру.

dblink_connect

dblink_connect — открывает постоянное подключение к удаленной базе данных.

Синтаксис

dblink_connect(text connstr) returns text
dblink_connect(text connname, text connstr) returns text

Описание

Функция dblink_connect() устанавливает подключение к удаленной базе данных QHB. Целевые сервер и база данных указываются в стандартной строке подключения libpq. При необходимости этому подключению можно назначить имя. Одновременно могут быть открытыми несколько именованных подключений, но только одно подключение без имени. Подключение будет сохраняться, пока не будет закрыто или до завершения сеанса базы данных.

В строке подключения также можно задать имя существующего стороннего сервера. Для определения стороннего сервера рекомендуется использовать обертку сторонних данных dblink_fdw. См. пример ниже, а также CREATE SERVER and CREATE USER MAPPING.

Аргументы

connname
Имя, назначаемое этому подключению; если опущено, открывается безымянное подключение, заменяя любое существующее безымянное подключение.

connstr
Строка подключения в стиле libpq, например hostaddr=127.0.0.1 port=5432 dbname=mydb user=postgres password=mypasswd options=-csearch_path=.

Как вариант, в ней можно задавать имя стороннего сервера.

Возвращаемое значение

Возвращает состояние (Это всегда строка OK, поскольку в случае любого сбоя функция выдает ошибку и не возвращает никакие значения).

Примечания

Если к базе данных, в которой не применяется шаблон безопасного использования схем, имеют доступ недоверенные пользователи, начинайте каждый сеанс с удаления общедоступных для записи схем из пути поиска (search_path). Например, для этого можно добавить options=-csearch_path= в connstr. Это касается не только dblink, но и любых других интерфейсов для выполнения произвольных команд SQL.

Только суперпользователи могут использовать dblink_connect для создания подключений, не требующих аутентификации по паролю. Если эта возможность нужна обычным пользователям, следует воспользоваться функцией dblink_connect_u.

Использовать в именах подключений знаки равенства не рекомендуется, так как при этом возникает риск перепутать их со строками подключения в других функциях dblink.

Примеры

SELECT dblink_connect('dbname=postgres options=-csearch_path=');
 dblink_connect
----------------
 OK
(1 row)

SELECT dblink_connect('myconn', 'dbname=postgres options=-csearch_path=');
 dblink_connect
----------------
 OK
(1 row)

-- Функциональность обертки сторонних данных (FOREIGN DATA WRAPPER)
-- Примечание: чтобы это работало как следует, для локальных подключений требуется аутентификация по паролю
--       Иначе, вызвав dblink_connect(), вы получите следующую ошибку:
--       ERROR:  password is required
--       DETAIL:  Non-superuser cannot connect if the server does not request a password.
--       HINT:  Target server's authentication method must be changed.
--
--       (ОШИБКА:  требуется пароль
--       ПОДРОБНОСТИ:  Обычный пользователь не может подключиться, если сервер не требует пароль.
--       ПОДСКАЗКА:  Необходимо изменить метод аутентификации целевого сервера.)

CREATE SERVER fdtest FOREIGN DATA WRAPPER dblink_fdw OPTIONS (hostaddr '127.0.0.1', dbname 'contrib_regression');

CREATE USER regress_dblink_user WITH PASSWORD 'secret';
CREATE USER MAPPING FOR regress_dblink_user SERVER fdtest OPTIONS (user 'regress_dblink_user', password 'secret');
GRANT USAGE ON FOREIGN SERVER fdtest TO regress_dblink_user;
GRANT SELECT ON TABLE foo TO regress_dblink_user;

\set ORIGINAL_USER :USER
\c - regress_dblink_user
SELECT dblink_connect('myconn', 'fdtest');
 dblink_connect
----------------
 OK
(1 row)

SELECT * FROM dblink('myconn', 'SELECT * FROM foo') AS t(a int, b text, c text[]);
 a  | b |       c
----+---+---------------
  0 | a | {a0,b0,c0}
  1 | b | {a1,b1,c1}
  2 | c | {a2,b2,c2}
  3 | d | {a3,b3,c3}
  4 | e | {a4,b4,c4}
  5 | f | {a5,b5,c5}
  6 | g | {a6,b6,c6}
  7 | h | {a7,b7,c7}
  8 | i | {a8,b8,c8}
  9 | j | {a9,b9,c9}
 10 | k | {a10,b10,c10}
(11 rows)

\c - :ORIGINAL_USER
REVOKE USAGE ON FOREIGN SERVER fdtest FROM regress_dblink_user;
REVOKE SELECT ON TABLE foo FROM regress_dblink_user;
DROP USER MAPPING FOR regress_dblink_user SERVER fdtest;
DROP USER regress_dblink_user;
DROP SERVER fdtest;

dblink_connect_u

dblink_connect_u — открывает постоянное подключение к удаленной базе данных, небезопасно

Синтаксис

dblink_connect_u(text connstr) returns text
dblink_connect_u(text connname, text connstr) returns text

Описание

Функция dblink_connect_u() идентична функции dblink_connect(), за исключением того, что она позволяет подключаться обычным пользователям с любым методом аутентификации.

Если удаленный сервер выбирает метод аутентификации без пароля, возможна имперсонация и последующее повышение прав, поскольку сеанс будет установлен от имени пользователя, который исполняет локальный процесс QHB. Кроме того, даже если удаленный сервер и запрашивает пароль, этот пароль можно получить из среды сервера, например, из файла ~/.pgpass, принадлежащего пользователю сервера. Это чревато не только имперсонацией, но и возможностью выдачи пароля не заслуживающему доверия удаленному серверу. Поэтому dblink_connect_u() изначально устанавливается так, что роль PUBLIC лишена всех прав на ее использование, т. е. вызвать ее могут только суперпользователи. В некоторых ситуациях может оказаться допустимым дать право EXECUTE для dblink_connect_u() определенным пользователям, которые считаются доверенными, но это следует делать с осторожностью. Также рекомендуется, чтобы файл ~/.pgpass, принадлежащий пользователю сервера, не содержал никаких записей с подстановочным знаком (например, *) в качестве имени узла.

Дополнительную информацию см. в описании dblink_connect().

dblink_disconnect

dblink_disconnect — закрывает постоянное подключение к удаленной базе данных

Синтаксис

dblink_disconnect() returns text
dblink_disconnect(text connname) returns text

Описание

Функция dblink_disconnect() закрывает подключение, ранее открытое функцией dblink_connect(). Форма без аргументов закрывает безымянное подключение.

Аргументы

connname
Имя закрываемого именованного подключения.

Возвращаемое значение

Возвращает состояние (Это всегда строка OK, поскольку в случае любого сбоя функция выдает ошибку и не возвращает никакие значения).

Примеры

SELECT dblink_disconnect();
 dblink_disconnect
-------------------
 OK
(1 row)

SELECT dblink_disconnect('myconn');
 dblink_disconnect
-------------------
 OK
(1 row)

dblink

dblink — выполняет запрос в удаленной базе данных

Синтаксис

dblink(text connname, text sql [, bool fail_on_error]) returns setof record
dblink(text connstr, text sql [, bool fail_on_error]) returns setof record
dblink(text sql [, bool fail_on_error]) returns setof record

Описание

Функция dblink выполняет запрос (обычно SELECT, но это может быть и любой другой оператор SQL, возвращающий строки) в удаленной базе данных.

Когда этой функции передаются два аргумента типа text, первый сначала рассматривается как имя постоянного подключения; если таковое находится, команда выполняется для него. Если подключение не находится, первый аргумент воспринимается как строка подключения, как для функции dblink_connect, и указанное подключение устанавливается только на время выполнения этой команды.

Аргументы

connname
Имя используемого подключения; чтобы использовать безымянное подключение, нужно опустить этот параметр.

connstr
Строка подключения, описанная ранее для dblink_connect.

sql
Запрос SQL, который вы хотите выполнить в удаленной базе данных, например select * from foo.

fail_on_error
Если равен true (это значение по умолчанию, поэтому его необязательно указывать), то в случае ошибки, выданной на удаленной стороне соединения, ошибка также выдается локально. Если равен false, удаленная ошибка выдается локально как замечание (уровень важности NOTICE), и функция не возвращает строки.

Возвращаемое значение

Эта функция возвращает строки, выдаваемые в результате запроса. Поскольку dblink может выполнять любые запросы, она объявлена как возвращающая тип record, а не некоторый определенный набор столбцов. Это означает, что нужно указать ожидаемый набор столбцов в вызывающем запросе — иначе QHB не будет знать, чего ожидать. Например:

SELECT *
    FROM dblink('dbname=mydb options=-csearch_path=',
                'select proname, prosrc from pg_proc')
      AS t1(proname name, prosrc text)
    WHERE proname LIKE 'bytea%';

В части «псевдонима» предложения FROM нужно указывать имена столбцов и типы, которые будет возвращать функция. (Указание имен столбцов в псевдонимах на самом деле относится к стандартному синтаксису SQL, а вот указание типов столбцов является расширением QHB.) Это позволяет системе понять, во что должен разворачиваться знак * и на что ссылается proname в предложении WHERE, прежде чем пытаться выполнить эту функцию. Если во время выполнения действительный результат запроса из удаленной базы данных не будет содержать столько столбцов, сколько указано в предложении FROM, произойдет ошибка. Однако имена столбцов могут не совпадать, а еще dblink не настаивает на точном совпадении типов. Функция завершится успешно, если возвращаемые строки данных будут допустимыми для ввода в тип столбца, объявленный в предложении FROM.

Примечания

Использовать dblink с предопределенными запросами будет удобнее, если создать представление. Это позволит скрыть в его определении информацию о типах столбцов и не прописывать ее в каждом запросе. Например:

CREATE VIEW myremote_pg_proc AS
  SELECT *
    FROM dblink('dbname=postgres options=-csearch_path=',
                'select proname, prosrc from pg_proc')
    AS t1(proname name, prosrc text);

SELECT * FROM myremote_pg_proc WHERE proname LIKE 'bytea%';

Примеры

SELECT * FROM dblink('dbname=postgres options=-csearch_path=',
                     'select proname, prosrc from pg_proc')
  AS t1(proname name, prosrc text) WHERE proname LIKE 'bytea%';
  proname   |   prosrc
------------+------------
 byteacat   | byteacat
 byteaeq    | byteaeq
 bytealt    | bytealt
 byteale    | byteale
 byteagt    | byteagt
 byteage    | byteage
 byteane    | byteane
 byteacmp   | byteacmp
 bytealike  | bytealike
 byteanlike | byteanlike
 byteain    | byteain
 byteaout   | byteaout
(12 rows)

SELECT dblink_connect('dbname=postgres options=-csearch_path=');
 dblink_connect
----------------
 OK
(1 row)

SELECT * FROM dblink('select proname, prosrc from pg_proc')
  AS t1(proname name, prosrc text) WHERE proname LIKE 'bytea%';
  proname   |   prosrc
------------+------------
 byteacat   | byteacat
 byteaeq    | byteaeq
 bytealt    | bytealt
 byteale    | byteale
 byteagt    | byteagt
 byteage    | byteage
 byteane    | byteane
 byteacmp   | byteacmp
 bytealike  | bytealike
 byteanlike | byteanlike
 byteain    | byteain
 byteaout   | byteaout
(12 rows)

SELECT dblink_connect('myconn', 'dbname=regression options=-csearch_path=');
 dblink_connect
----------------
 OK
(1 row)

SELECT * FROM dblink('myconn', 'select proname, prosrc from pg_proc')
  AS t1(proname name, prosrc text) WHERE proname LIKE 'bytea%';
  proname   |   prosrc
------------+------------
 bytearecv  | bytearecv
 byteasend  | byteasend
 byteale    | byteale
 byteagt    | byteagt
 byteage    | byteage
 byteane    | byteane
 byteacmp   | byteacmp
 bytealike  | bytealike
 byteanlike | byteanlike
 byteacat   | byteacat
 byteaeq    | byteaeq
 bytealt    | bytealt
 byteain    | byteain
 byteaout   | byteaout
(14 rows)

dblink_exec

dblink_exec — выполняет команду в удаленной базе данных

Синтаксис

dblink_exec(text connname, text sql [, bool fail_on_error]) returns text
dblink_exec(text connstr, text sql [, bool fail_on_error]) returns text
dblink_exec(text sql [, bool fail_on_error]) returns text

Описание

Функция dblink_exec выполняет команду (то есть любой оператор SQL, не возвращающий строки) в удаленной базе данных.

Когда этой функции передаются два аргумента типа text, первый сначала рассматривается как имя постоянного подключения; если таковое находится, команда выполняется для него. Если подключение не находится, первый аргумент воспринимается как строка подключения, как для функции dblink_connect, и указанное подключение устанавливается только на время выполнения этой команды.

Аргументы

connname
Имя используемого подключения; чтобы использовать безымянное подключение, нужно опустить этот параметр.

connstr
Строка подключения, описанная ранее для dblink_connect.

sql
Команда SQL, которую вы хотите выполнить в удаленной базе данных, например insert into foo values(0, 'a', '{"a0","b0","c0"}').

fail_on_error
Если равен true (это значение по умолчанию, поэтому его необязательно указывать), то в случае ошибки, выданной на удаленной стороне соединения, ошибка также выдается локально. Если равен false, удаленная ошибка выдается локально как замечание (уровень важности NOTICE), и возвращаемым значением функции будет ERROR.

Возвращаемое значение

Возвращает состояние (либо строки состояния команды, либо ERROR).

Примеры

SELECT dblink_connect('dbname=dblink_test_standby');
 dblink_connect
----------------
 OK
(1 row)

SELECT dblink_exec('insert into foo values(21, ''z'', ''{"a0","b0","c0"}'');');
   dblink_exec
-----------------
 INSERT 943366 1
(1 row)

SELECT dblink_connect('myconn', 'dbname=regression');
 dblink_connect
----------------
 OK
(1 row)

SELECT dblink_exec('myconn', 'insert into foo values(21, ''z'', ''{"a0","b0","c0"}'');');
   dblink_exec
------------------
 INSERT 6432584 1
(1 row)

SELECT dblink_exec('myconn', 'insert into pg_class values (''foo'')',false);
NOTICE:  sql error
DETAIL:  ERROR:  null value in column "relnamespace" violates not-null constraint

 dblink_exec
-------------
 ERROR
(1 row)

dblink_open

dblink_open — открывает курсор в удаленной базе данных

Синтаксис

dblink_open(text cursorname, text sql [, bool fail_on_error]) returns text
dblink_open(text connname, text cursorname, text sql [, bool fail_on_error]) returns text

Описание

Функция dblink_open() открывает курсор в удаленной базе данных. Затем этим курсором можно будет манипулировать с помощью функций dblink_fetch() и dblink_close().

Аргументы

connname
Имя используемого подключения; чтобы использовать безымянное подключение, нужно опустить этот параметр.

cursorname
Имя, назначаемое курсору.

sql
Оператор SELECT, который вы хотите выполнять в удаленной базе данных, например select * from pg_class.

fail_on_error
Если равен true (это значение по умолчанию, поэтому его необязательно указывать), то в случае ошибки, выданной на удаленной стороне соединения, ошибка также выдается локально. Если равен false, удаленная ошибка выдается локально как замечание (уровень важности NOTICE), и возвращаемым значением функции будет ERROR.

Возвращаемое значение

Возвращает состояние (OK или ERROR).

Примечания

Поскольку курсор может существовать только в рамках транзакции, функция dblink_open начинает явный блок транзакций (командой BEGIN) на удаленной стороне, если транзакция там еще не начата. Эта транзакция будет снова закрыта при соответствующем выполнении функции dblink_close. Обратите внимание, что если вы используете dblink_exec для изменения данных между вызовами dblink_open и dblink_close, а затем происходит ошибка, или если вы вызываете dblink_disconnect перед dblink_close, ваши изменения будут потеряны, так как транзакция прервется.

Примеры

SELECT dblink_connect('dbname=postgres options=-csearch_path=');
 dblink_connect
----------------
 OK
(1 row)

SELECT dblink_open('foo', 'select proname, prosrc from pg_proc');
 dblink_open
-------------
 OK
(1 row)

dblink_fetch

dblink_fetch — возвращает строки из открытого курсора в удаленной базе данных

Синтаксис

dblink_fetch(text cursorname, int howmany [, bool fail_on_error]) returns setof record
dblink_fetch(text connname, text cursorname, int howmany [, bool fail_on_error]) returns setof record

Описание

Функция dblink_fetch выбирает строки из курсора, ранее установленного функцией dblink_open.

Аргументы

connname
Имя используемого подключения; чтобы использовать безымянное подключение, нужно опустить этот параметр.

cursorname
Имя курсора, из которого будут выбираться данные.

howmany
Максимальное число строк, которое нужно получить. Выбираются следующие howmany строк, начиная с текущей позиции курсора и двигаясь вперед. Когда курсор доходит до конца, строки больше не выдаются.

fail_on_error
Если равен true (это значение по умолчанию, поэтому его необязательно указывать), то в случае ошибки, выданной на удаленной стороне соединения, ошибка также выдается локально. Если равен false, удаленная ошибка выдается локально как замечание (уровень важности NOTICE), и функция не возвращает строки.

Возвращаемое значение

Эта функция возвращает строки, выбираемые через курсор. Для использования этой функции нужно задать ожидаемый набор столбцов, как ранее говорилось в описании dblink.

Примечания

При несовпадении числа возвращаемых столбцов, указанного в предложении FROM, с фактическим числом столбцов, возвращенных удаленным курсором, будет выдана ошибка. В этом случае удаленный курсор все равно продвигается на столько строк, на сколько он продвинулся бы, если бы ошибка не произошла. То же самое верно для любых других ошибок, происходящих в локальном запроса после выполнения удаленной команды FETCH.

Примеры

SELECT dblink_connect('dbname=postgres options=-csearch_path=');
 dblink_connect
----------------
 OK
(1 row)

SELECT dblink_open('foo', 'select proname, prosrc from pg_proc where proname like ''bytea%''');
 dblink_open
-------------
 OK
(1 row)

SELECT * FROM dblink_fetch('foo', 5) AS (funcname name, source text);
 funcname |  source
----------+----------
 byteacat | byteacat
 byteacmp | byteacmp
 byteaeq  | byteaeq
 byteage  | byteage
 byteagt  | byteagt
(5 rows)

SELECT * FROM dblink_fetch('foo', 5) AS (funcname name, source text);
 funcname  |  source
-----------+-----------
 byteain   | byteain
 byteale   | byteale
 bytealike | bytealike
 bytealt   | bytealt
 byteane   | byteane
(5 rows)

SELECT * FROM dblink_fetch('foo', 5) AS (funcname name, source text);
  funcname  |   source
------------+------------
 byteanlike | byteanlike
 byteaout   | byteaout
(2 rows)

SELECT * FROM dblink_fetch('foo', 5) AS (funcname name, source text);
 funcname | source
----------+--------
(0 rows)

dblink_close

dblink_close — закрывает курсор в удаленной базе данных

Синтаксис

dblink_close(text cursorname [, bool fail_on_error]) returns text
dblink_close(text connname, text cursorname [, bool fail_on_error]) returns text

Описание

Функция dblink_close закрывает курсор, ранее открытый функцией dblink_open.

Аргументы

connname
Имя используемого подключения; чтобы использовать безымянное подключение, нужно опустить этот параметр.

cursorname
Имя закрываемого курсора.

fail_on_error
Если равен true (это значение по умолчанию, поэтому его необязательно указывать), то в случае ошибки, выданной на удаленной стороне соединения, ошибка также выдается локально. Если равен false, удаленная ошибка выдается локально как замечание (уровень важности NOTICE), и возвращаемым значением функции будет ERROR.

Возвращаемое значение

Возвращает состояние (OK или ERROR).

Примечания

Если вызов dblink_open начал явный блок транзакции и это последний открытый курсор, оставшийся в этом подключении, то dblink_close выполнит соответствующую команду COMMIT.

Примеры

SELECT dblink_connect('dbname=postgres options=-csearch_path=');
 dblink_connect
----------------
 OK
(1 row)

SELECT dblink_open('foo', 'select proname, prosrc from pg_proc');
 dblink_open
-------------
 OK
(1 row)

SELECT dblink_close('foo');
 dblink_close
--------------
 OK
(1 row)

dblink_get_connections

dblink_get_connections — возвращает имена всех открытых именованных подключений dblink

Синтаксис

dblink_get_connections() returns text[]

Описание

Функция dblink_get_connections возвращает массив имен всех открытых именованных подключений dblink.

Возвращаемое значение

Возвращает текстовый массив имен подключений или NULL, если они отсутствуют.

Примеры

SELECT dblink_get_connections();

dblink_error_message

dblink_error_message — выдает сообщение последней ошибки для именованного подключения

Синтаксис

dblink_error_message(text connname) returns text

Описание

Функция dblink_error_message извлекает самое последнее сообщение удаленной ошибки для заданного подключения.

Аргументы

connname
Имя используемого подключения.

Возвращаемое значение

Возвращает последнее сообщение ошибки или OK, если в сеансе этого подключения не было ошибок.

Примечания

Когда функцией dblink_send_query передаются асинхронные запросы, сообщение об ошибке, связанное с подключением, может не обновиться до получения от сервера ответного сообщения. Обычно это означает, что до вызова dblink_error_message следует вызвать функцию dblink_is_busy или dblink_get_result, чтобы были видны все ошибки, возникшие при выполнении асинхронного запроса.

Примеры

SELECT dblink_error_message('dtest1');

dblink_send_query

dblink_send_query — передает асинхронный запрос в удаленную базу данных

Синтаксис

dblink_send_query(text connname, text sql) returns int

Описание

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

После успешной передачи асинхронного запроса состояние его завершения можно проверить при помощи функции dblink_is_busy, и в итоге получить результаты собранные функцией dblink_get_result. Также можно попытаться отменить активный асинхронный запрос, вызвав функцию dblink_cancel_query.

Аргументы

connname
Имя используемого подключения.

sql
Оператор SQL, который вы хотите выполнить в удаленной базе данных, например select * from pg_class.

Возвращаемое значение

Возвращает 1, если запрос был успешно отправлен на обработку, или 0 в противном случае.

Примеры

SELECT dblink_send_query('dtest1', 'SELECT * FROM foo WHERE f1 < 3');

dblink_is_busy

dblink_is_busy — проверяет, не занято ли подключение асинхронным запросом

Синтаксис

dblink_is_busy(text connname) returns int

Описание

Функция dblink_is_busy проверяет, не выполняется ли асинхронный запрос.

Аргументы

connname
Имя проверяемого подключения.

Возвращаемое значение

Возвращает 1, если подключение занято, или 0 в противном случае. Если эта функция возвращает 0, гарантируется, что вызов dblink_get_result не будет заблокирован.

Примеры

SELECT dblink_is_busy('dtest1');

dblink_get_notify

dblink_get_notify — выдает асинхронные уведомления подключения

Синтаксис

dblink_get_notify() returns setof (notify_name text, be_pid int, extra text)
dblink_get_notify(text connname) returns setof (notify_name text, be_pid int, extra text)

Описание

Функция dblink_get_notify выдает уведомления либо безымянного подключения, либо подключения с указанным именем. Для получения уведомлений через dblink необходимо сначала выполнить LISTEN, используя функцию dblink_exec. Дополнительную информацию см. в описании команд LISTEN и NOTIFY.

Аргументы

connname
Имя именованного подключения, уведомления которого нужно получить.

Возвращаемое значение

Возвращает setof (notify_name text, be_pid int, extra text) или пустой набор, если уведомлений нет.

Examples

SELECT dblink_exec('LISTEN virtual');
 dblink_exec
-------------
 LISTEN
(1 row)

SELECT * FROM dblink_get_notify();
 notify_name | be_pid | extra
-------------+--------+-------
(0 rows)

NOTIFY virtual;
NOTIFY

SELECT * FROM dblink_get_notify();
 notify_name | be_pid | extra
-------------+--------+-------
 virtual     |   1229 |
(1 row)

dblink_get_result

dblink_get_result — получает результат асинхронного запроса

Синтаксис

dblink_get_result(text connname [, bool fail_on_error]) returns setof record

Описание

Функция dblink_get_result собирает результаты асинхронного запроса, ранее переданного функцией dblink_send_query. Если запрос еще выполняется, dblink_get_result будет ждать его завершения.

Аргументы

connname
Имя используемого подключения.

fail_on_error
Если равен true (это значение по умолчанию, поэтому его необязательно указывать), то в случае ошибки, выданной на удаленной стороне соединения, ошибка также выдается локально. Если равен false, удаленная ошибка выдается локально как замечание (уровень важности NOTICE), и функция не возвращает строки.

Возвращаемое значение

Для асинхронного запроса (то есть оператора SQL, возвращающего строки) эта функция возвращает строки, полученные в результате запроса. Чтобы использовать эту функцию, нужно задать ожидаемый набор столбцов, как говорилось ранее в описании dblink.

Для асинхронной команды (то есть оператора SQL, не возвращающего строки) эта функция возвращает одну строку с одним текстовым столбцом, содержащим строку состояния команды. Однако в предложении FROM для такого вызова необходимо указать, что результат будет содержать один текстовый столбец.

Примечания

Эта функция должна вызываться, если dblink_send_query вернула 1. Ее нужно вызывать по одному разу для каждого отправленного запроса, а затем еще раз для получения результата с пустым набором, прежде чем подключение можно будет пользоваться снова.

Когда используются dblink_send_query и dblink_get_result, dblink извлекает результат удаленного запроса целиком, прежде чем передавать любую его часть локальному процессору запросов. Если запрос возвращает большое количество строк, это может занимать много памяти в локальном сеансе. Возможно, в этом случае будет лучше открыть такой запрос как курсор с помощью dblink_open, а затем извлекать приемлемыми порциями. Как вариант, можно воспользоваться простой функцией dblink(), которая избегает заполнения памяти, выгружая большие наборы результатов на диск.

Примеры

contrib_regression=# SELECT dblink_connect('dtest1', 'dbname=contrib_regression');
 dblink_connect
----------------
 OK
(1 row)

contrib_regression=# SELECT * FROM
contrib_regression-# dblink_send_query('dtest1', 'select * from foo where f1 < 3') AS t1;
 t1
----
  1
(1 row)

contrib_regression=# SELECT * FROM dblink_get_result('dtest1') AS t1(f1 int, f2 text, f3 text[]);
 f1 | f2 |     f3
----+----+------------
  0 | a  | {a0,b0,c0}
  1 | b  | {a1,b1,c1}
  2 | c  | {a2,b2,c2}
(3 rows)

contrib_regression=# SELECT * FROM dblink_get_result('dtest1') AS t1(f1 int, f2 text, f3 text[]);
 f1 | f2 | f3
----+----+----
(0 rows)

contrib_regression=# SELECT * FROM
contrib_regression-# dblink_send_query('dtest1', 'select * from foo where f1 < 3; select * from foo where f1 > 6') AS t1;
 t1
----
  1
(1 row)

contrib_regression=# SELECT * FROM dblink_get_result('dtest1') AS t1(f1 int, f2 text, f3 text[]);
 f1 | f2 |     f3
----+----+------------
  0 | a  | {a0,b0,c0}
  1 | b  | {a1,b1,c1}
  2 | c  | {a2,b2,c2}
(3 rows)

contrib_regression=# SELECT * FROM dblink_get_result('dtest1') AS t1(f1 int, f2 text, f3 text[]);
 f1 | f2 |      f3
----+----+---------------
  7 | h  | {a7,b7,c7}
  8 | i  | {a8,b8,c8}
  9 | j  | {a9,b9,c9}
 10 | k  | {a10,b10,c10}
(4 rows)

contrib_regression=# SELECT * FROM dblink_get_result('dtest1') AS t1(f1 int, f2 text, f3 text[]);
 f1 | f2 | f3
----+----+----
(0 rows)

dblink_cancel_query

dblink_cancel_query — отменяет любой активный запрос в именованном подключении

Синтаксис

dblink_cancel_query(text connname) returns text

Описание

Функция dblink_cancel_query пытается отменить любой запрос, выполняющийся через именованное подключение. Обратите внимание, что ее вызов не обязательно будет успешным (например, потому что удаленный запрос может уже завершиться). Запрос отмены просто увеличивает шансы на то, что этот запрос вскоре будет прерван. При этом все равно нужно будет завершить обычную процедуру обработки запроса, например, вызвав dblink_get_result.

Аргументы

connname
Имя используемого подключения.

Возвращаемое значение

Возвращает OK, если запрос отмены был отправлен, или текст сообщения об ошибке в случае неудачи.

Примеры

SELECT dblink_cancel_query('dtest1');

dblink_get_pkey

dblink_get_pkey — возвращает позиции и имена полей первичного ключа отношения

Синтаксис

dblink_get_pkey(text relname) returns setof dblink_pkey_results

Описание

Функция dblink_get_pkey предоставляет информацию о первичном ключе отношения в локальной базе данных. Иногда это полезно при формировании запросов, отправляемых в удаленные базы данных.

Аргументы

relname
Имя локального отношения, например foo или myschema.mytab. Заключите его в кавычки, если это имя в смешанном регистре или содержит специальные символы, например "FooBar"; без кавычек эта строка приводится к нижнему регистру.

Возвращаемое значение

Возвращает по одной строке для каждого поля первичного ключа или не возвращает строк, если в отношении нет первичного ключа. Тип результирующей строки определяется как

CREATE TYPE dblink_pkey_results AS (position int, colname text);

В столбце position содержатся просто числа от 1 до N; это номер поля в первичном ключе, а не номер столбца таблицы.

Примеры

CREATE TABLE foobar (
    f1 int,
    f2 int,
    f3 int,
    PRIMARY KEY (f1, f2, f3)
);
CREATE TABLE

SELECT * FROM dblink_get_pkey('foobar');
 position | colname
----------+---------
        1 | f1
        2 | f2
        3 | f3
(3 rows)

dblink_build_sql_insert

dblink_build_sql_insert — формирует оператор INSERT из локального кортежа, заменяя значения полей первичного ключа переданными альтернативными значениями

Синтаксис

dblink_build_sql_insert(text relname,
                        int2vector primary_key_attnums,
                        integer num_primary_key_atts,
                        text[] src_pk_att_vals_array,
                        text[] tgt_pk_att_vals_array) returns text

Описание

Функция dblink_build_sql_insert может быть полезна при избирательной репликации локальной таблицы в удаленную базу данных. Она выбирает строку из локальной таблицы по заданному первичному ключу, а затем формирует команду SQL INSERT, которая дублирует эту строку, но заменяет в ней значения первичного ключа значениями из последнего аргумента. (Чтобы получить точную копию строки, просто укажите одинаковые значения в двух последних аргументах.)

Аргументы

relname
Имя локального отношения, например foo или myschema.mytab. Заключите его в кавычки, если это имя в смешанном регистре или содержит специальные символы, например "FooBar"; без кавычек эта строка приводится к нижнему регистру.

primary_key_attnums
Номера атрибутов (начиная с 1) полей первичного ключа, например 1 2.

num_primary_key_atts
Число полей первичного ключа.

src_pk_att_vals_array
Значения полей первичного ключа, по которым будет выполняться поиск локального кортежа. Каждое поле представляется в текстовом виде. Если локальной строки с этими значениями первичного ключа нет, выдается ошибка.

tgt_pk_att_vals_array
Значения полей первичного ключа, которые будут помещены в результирующую команду INSERT. Каждое поле представляется в текстовом виде.

Возвращаемое значение

Возвращает запрошенный оператор SQL в текстовом виде.

Примечания

Номера атрибутов в primary_key_attnums воспринимаются как логические номера столбцов, соответствующие позициям столбцов в SELECT * FROM relname, а не как физические позиции столбцов.

Примеры

SELECT dblink_build_sql_insert('foo', '1 2', 2, '{"1", "a"}', '{"1", "b''a"}');
             dblink_build_sql_insert
--------------------------------------------------
 INSERT INTO foo(f1,f2,f3) VALUES('1','b''a','1')
(1 row)

dblink_build_sql_delete

dblink_build_sql_delete — формирует оператор DELETE со значениями, передаваемыми для полей первичного ключа

Синтаксис

dblink_build_sql_delete(text relname,
                        int2vector primary_key_attnums,
                        integer num_primary_key_atts,
                        text[] tgt_pk_att_vals_array) returns text

Описание

Функция dblink_build_sql_delete может быть полезна при избирательной репликации локальной таблицы в удаленную базу данных. Она формирует команду SQL DELETE, которая удалит строку с заданными значениями первичного ключа.

Аргументы

relname
Имя локального отношения, например foo или myschema.mytab. Заключите его в кавычки, если это имя в смешанном регистре или содержит специальные символы, например "FooBar"; без кавычек эта строка приводится к нижнему регистру.

primary_key_attnums
Номера атрибутов (начиная с 1) полей первичного ключа, например 1 2.

num_primary_key_atts
Число полей первичного ключа.

tgt_pk_att_vals_array
Значения полей первичного ключа, которые будут использоваться в результирующей команде DELETE. Каждое поле представляется в текстовом виде.

Возвращаемое значение

Возвращает запрошенный оператор SQL в текстовом виде.

Примечания

Номера атрибутов в primary_key_attnums воспринимаются как логические номера столбцов, соответствующие позициям столбцов в SELECT * FROM relname, а не как физические позиции столбцов.

Примеры

SELECT dblink_build_sql_delete('"MyFoo"', '1 2', 2, '{"1", "b"}');
           dblink_build_sql_delete
---------------------------------------------
 DELETE FROM "MyFoo" WHERE f1='1' AND f2='b'
(1 row)

dblink_build_sql_update

dblink_build_sql_update — формирует оператор UPDATE из локального кортежа, заменяя значения полей первичного ключа переданными альтернативными значениями

Синтаксис

dblink_build_sql_update(text relname,
                        int2vector primary_key_attnums,
                        integer num_primary_key_atts,
                        text[] src_pk_att_vals_array,
                        text[] tgt_pk_att_vals_array) returns text

Описание

Функция dblink_build_sql_update может быть полезна при избирательной репликации локальной таблицы в удаленную базу данных. Она выбирает строки из локальной таблицы по первичному ключу, а затем формирует команду SQL UPDATE, которая дублирует эту строку, но заменяет значения первичного ключа на значения из последнего аргумента. (Чтобы получить точную копию строки, просто укажите одинаковые значения в двух последних аргументах.) Команда UPDATE всегда присваивает значения всем полям строки — главное отличие этой функции от dblink_build_sql_insert в том, что она предполагает, что целевая строка уже существует в удаленной таблице.

Аргументы

relname
Имя локального отношения, например foo или myschema.mytab. Заключите его в кавычки, если это имя в смешанном регистре или содержит специальные символы, например "FooBar"; без кавычек эта строка приводится к нижнему регистру.

primary_key_attnums
Номера атрибутов (начиная с 1) полей первичного ключа, например 1 2.

num_primary_key_atts
Число полей первичного ключа.

src_pk_att_vals_array
Значения полей первичного ключа, по которым будет выполняться поиск локального кортежа. Каждое поле представляется в текстовом виде. Если локальной строки с этими значениями первичного ключа нет, выдается ошибка.

tgt_pk_att_vals_array
Значения полей первичного ключа, которые будут помещены в результирующую команду UPDATE. Каждое поле представляется в текстовом виде.

Возвращаемое значение

Возвращает запрошенный оператор SQL в текстовом виде.

Примечания

Номера атрибутов в primary_key_attnums воспринимаются как логические номера столбцов, соответствующие позициям столбцов в SELECT * FROM relname, а не как физические позиции столбцов.

Примеры

SELECT dblink_build_sql_update('foo', '1 2', 2, '{"1", "a"}', '{"1", "b"}');
                   dblink_build_sql_update
-------------------------------------------------------------
 UPDATE foo SET f1='1',f2='b',f3='1' WHERE f1='1' AND f2='b'
(1 row)

dict_int

Модуль dict_int представляет собой пример дополнительного шаблона словаря для полнотекстового поиска. Этот пример словаря был создан для управления индексацией целых чисел (со знаком и без); он позволяет индексировать такие числа и при этом избежать чрезмерного возрастания количества уникальных слов, что значительно увеличивает производительность поиска.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

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

Этот словарь принимает три параметра:

Параметр maxlen задает максимальное количество цифр, из которого может состоять целое число parameter. Значение по умолчанию — 6.

Параметр rejectlong определяет, должны ли чрезмерно длинные числа усекаться или игнорироваться. Если rejectlong имеет значение false (по умолчанию), этот словарь возвращает первые maxlen цифр целого числа. Если rejectlong имеет значение true, этот словарь воспринимает чрезмерно длинное число как стоп-слово, поэтому оно не будет индексироваться. Обратите внимание, что это означает, что такое целое число нельзя будет найти.

Параметр absval определяет, должны ли удаляться стоящие перед целыми числами знаки «+» и «-». Значение по умолчанию — false. При значении true знак удаляется до применения ограничения maxlen.

Использование

При установке расширения dict_int создается шаблон текстового поиска intdict_template и словарь intdict на его основе, с параметрами по умолчанию. Эти параметры можно изменить, например:

mydb# ALTER TEXT SEARCH DICTIONARY intdict (MAXLEN = 4, REJECTLONG = true);
ALTER TEXT SEARCH DICTIONARY

или создать новые словари на основе этого шаблона.

Протестировать этот словарь можно так:

mydb# select ts_lexize('intdict', '12345678');
 ts_lexize
-----------
 {123456}

но для практического применения его нужно включить в конфигурацию текстового поиска. Это может выглядеть так:

ALTER TEXT SEARCH CONFIGURATION english
    ALTER MAPPING FOR int, uint WITH intdict;

dict_xsyn

Модуль dict_xsyn (Extended Synonym Dictionary, расширенный словарь синонимов) представляет собой пример дополнительного шаблона словаря для полнотекстового поиска. Этот тип словаря заменяет слова группами их синонимов, тем самым позволяя находить слово по одному из его синонимов.

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

Словарь dict_xsyn принимает следующие параметры:

Параметр matchorig определяет, будет ли словарь принимать исходное слово. Значение по умолчанию — true (включен).

Параметр matchsynonyms определяет, будет ли словарь принимать синонимы. Значение по умолчанию — false (выключен).

Параметр keeporig определяет, будет ли в вывод словаря включаться исходное слово. Значение по умолчанию — true (включен).

Параметр keepsynonyms определяет, будут ли в вывод словаря включаться синонимы. Значение по умолчанию — true (включен).

Параметр rules задает базовое имя файла, содержащего список синонимов. Этот файл должен храниться в каталоге $SHAREDIR/tsearch_data/ (где под $SHAREDIR подразумевается каталог с общими данными установки QHB). Имя файла должно заканчиваться расширением .rules (которое не нужно указывать в параметре rules).

Файл правил имеет следующий формат:

Каждая строка представляет группу синонимов для одного слова, которое задается первым в этой строке. Синонимы разделяются пробелами, поэтому строка выглядит так:

слово син1 син2 син3

Символ решетки (#) является знаком-разграничителем для комментария. Он может находиться в любом месте строки. Следующая за ним часть строки будет пропущена.

Пример словаря можно найти в файле xsyn_sample.rules, устанавливаемом в $SHAREDIR/tsearch_data/.

Применение

При установке расширения dict_xsyn в базе создается шаблон текстового поиска xsyn_template и словарь xsyn на его основе, с параметрами по умолчанию. Эти параметры можно изменить, например так:

mydb# ALTER TEXT SEARCH DICTIONARY xsyn (RULES='my_rules', KEEPORIG=false);
ALTER TEXT SEARCH DICTIONARY

или создать новые словари на основе этого шаблона.

Протестировать этот словарь можно так:

mydb=# SELECT ts_lexize('xsyn', 'word');
      ts_lexize
-----------------------
 {syn1,syn2,syn3}

mydb# ALTER TEXT SEARCH DICTIONARY xsyn (RULES='my_rules', KEEPORIG=true);
ALTER TEXT SEARCH DICTIONARY

mydb=# SELECT ts_lexize('xsyn', 'word');
      ts_lexize
-----------------------
 {word,syn1,syn2,syn3}

mydb# ALTER TEXT SEARCH DICTIONARY xsyn (RULES='my_rules', KEEPORIG=false, MATCHSYNONYMS=true);
ALTER TEXT SEARCH DICTIONARY

mydb=# SELECT ts_lexize('xsyn', 'syn1');
      ts_lexize
-----------------------
 {syn1,syn2,syn3}

mydb# ALTER TEXT SEARCH DICTIONARY xsyn (RULES='my_rules', KEEPORIG=true, MATCHORIG=false, KEEPSYNONYMS=false);
ALTER TEXT SEARCH DICTIONARY

mydb=# SELECT ts_lexize('xsyn', 'syn1');
      ts_lexize
-----------------------
 {word}

Для практического же применения его нужно включить в конфигурацию текстового поиска. Это может выглядеть примерно так:

ALTER TEXT SEARCH CONFIGURATION english
    ALTER MAPPING FOR word, asciiword WITH xsyn, english_stem;

earthdistance

Модуль earthdistance предоставляет два разных способа вычисления ортодромии (расстояния по дуге большого круга; кратчайшего расстояния между двумя точками на поверхности Земли). Первый описанный способ зависит от модуля cube. Второй основан на встроенном типе данных point, использующий в качестве координат долготу и широту.

В этом модуле Земля считается идеальной сферой. (Если для вас это недостаточно точно, обратите внимание на проект PostGIS.)

Прежде чем устанавливать earthdistance, необходимо установить модуль cube (хотя можно воспользоваться параметром CASCADE команды CREATE EXTENSION и установить сразу оба расширения).

Внимание!
Настоятельно рекомендуется устанавливать расширения earthdistance и cube в одну схему, и при этом в данной схеме недоверенные пользователи не должны в настоящем и будущем иметь право CREATE. В противном случае, если схема earthdistance будет содержать объекты, определенные злонамеренным пользователем, во время установки возникнет угроза безопасности. Более того, при использовании функции earthdistance после установки расширения весь путь поиска должен содержать только доверенные схемы.

Земные расстояния по кубам

Данные хранятся в кубах, представляющих собой точки (оба угла куба совпадают) по 3 координатам, выражающим расстояния x, y и z от центра Земли. Этот модуль предоставляет домен earth на базе cube, включающий проверки того, что значение соответствует этим ограничениям и достаточно близко к реальной поверхности Земли.

Радиус Земли выдает функция earth(). Он выражается в метрах. Однако, изменив одну эту функцию, можно сделать так, чтобы модуль работал с какими-то другими единицами, либо использовать другое значение радиуса, которое кажется более подходящим.

Этот пакет также может применяться в астрономических базах данных. Вероятно, астрономы предпочтут изменить функцию earth(), чтобы та возвращала радиус, равный 180/pi(), и, как следствие, расстояния выдавались в градусах.

В этом модуле реализованы функции, поддерживающие ввод данных, выражающих широту и долготу (в градусах), вывод широты и долготы, вычисление ортодромии между двумя точками и простое указание ограничивающего прямоугольника, полезного для поиска по индексу.

Предоставляемые функции показаны в Таблице 5.

Таблица 5. Функции земных расстояний по кубам

Функция
Описание

earth () → float8
Возвращает предполагаемый радиус Земли.

sec_to_gc ( float8 ) → float8
Переводит расстояние по обычной прямой (секущей) между двумя точками на поверхности Земли в ортодромическое расстояние между ними.

gc_to_sec ( float8 ) → float8
Переводит ортодромическое расстояние между двумя точками на поверхности Земли в расстояние по обычной прямой (секущей) между ними.

ll_to_earth ( float8, float8 ) → earth
Возвращает положение точки на поверхности Земли по заданным широте (аргумент 1) и долготе (аргумент 2) в градусах.

latitude ( earth ) → float8
Возвращает широту (в градусах) точки на поверхности Земли.

longitude ( earth ) → float8
Возвращает долготу (в градусах) точки на поверхности Земли.

earth_distance ( earth, earth ) → float8
Возвращает ортодромическое расстояние между двумя точками на поверхности Земли.

earth_box ( earth, float8 ) → cube
Возвращает ограничивающий прямоугольник, подходящий для поиска по индексу с применением реализованного для типа cube оператора @> для точек в пределах заданной ортодромии от цели. Некоторые точки в этом прямоугольнике будут отстоять от цели дальше, чем на заданную ортодромию, поэтому в запрос следует включить вторую проверку с функцией earth_distance.

Земные расстояния по точкам

Вторая часть этого модуля основана на представлении точек на Земле в виде значений типа point, в которых первый компонент представляет долготу в градусах, а второй — широту в градусах. Точки воспринимаются как (долгота, широта), а не наоборот, так как долгота ближе к интуитивному представлению оси x, а широта — оси y.

Модуль предоставляет один оператор, показанный в Таблице 6.

Таблица 6. Операторы земных расстояний по точкам

Оператор | Описание
point <@> point → float8 | Вычисляет расстояние в сухопутных милях между двумя точками на поверхности Земли.

Обратите внимание, что, в отличие от части модуля на основе cube, в этой части единицы запрограммированы жестко: изменение функции earth() не повлияет на результаты этого оператора.

Единственный недостаток представления в виде долготы/широты состоит в том, что вам придется учитывать граничные условия возле полюсов и в районе +/- 180 градусов долготы. Представление на основе cube лишено таких нарушений непрерывности.

file_fdw

Модуль file_fdw предоставляет обертку сторонних данных file_fdw, с помощью которой можно обращаться к файлам данных в файловой системе сервера или выполнять программы на сервере и читать их вывод. Файлы данных и вывод программ должны быть в формате, который может прочесть команда COPY FROM; подробную информацию см. в описании COPY. В настоящее время файлы данных доступны только для чтения.

Для сторонней таблицы, создаваемой посредством этой обертки, можно задать следующие параметры:

filename
Задает имя файла, который нужно прочитать. Относительные пути рассматриваются от каталога данных. Следует указать либо параметр filename, либо параметр program, но не оба сразу.

program
Задает команду, которую нужно выполнить. Стандартный вывод этой команды будет прочитан так же, как если бы выполнялась COPY FROM PROGRAM. Следует указать либо параметр program, либо параметр filename, но не оба сразу.

format
Задает формат данных аналогично параметру FORMAT команды COPY.

header
Определяет, будут ли данные содержать строку заголовка, аналогично параметру HEADER команды COPY.

delimiter
Задает символ-разделитель данных аналогично параметру DELIMITER команды COPY.

quote
Задает символ для заключения данных в кавычки аналогично параметру QUOTE команды COPY.

escape
Задает символ, экранирующий данные, аналогично параметру ESCAPE команды COPY.

null
Задает нулевую строку данных аналогично параметру NULL команды COPY.

encoding
Задает кодировку данных аналогично параметру ENCODING команды COPY.

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

Для столбцов сторонней таблицы, создаваемой посредством этой обертки, можно задать следующие параметры:

force_not_null
Это логический параметр. true указывает, что значения столбца не должны сверяться с нулевой строкой (то есть со значением, заданным параметром null на уровне таблицы). Эффект аналогичен включению столбца в список параметра FORCE_NOT_NULL команды COPY.

force_null
Это логический параметр. true указывает, что значения столбца, соответствующие нулевой строке, должны возвращаться как NULL, даже если они заключены в кавычки. Без этого параметра как NULL будут возвращаться только значения без кавычек. Эффект аналогичен включению столбца в список параметра FORCE_NULL команды COPY.

В настоящее время file_fdw не поддерживает параметр FORCE_QUOTE команды COPY.

Эти параметры можно задать только для сторонней таблицы или ее столбцов, но не для обертки сторонних данных file_fdw и не сервера или сопоставления пользователя, использующих эту обертку.

По соображениям безопасности для изменения параметров на уровне таблицы нужно быть суперпользователем или иметь права роли pg_read_server_files (для указания имени файла) или роли pg_execute_server_program (для указания программы): только избранные пользователи должны иметь возможность выбирать, какой файл читать или какую программу запускать. В принципе, право изменять остальные параметры можно предоставить и обычным пользователям, но в настоящее время это не поддерживается.

Задавая параметр program, не забывайте, что эта строка выполняется оболочкой операционной системы. Если нужно передать заданной команде аргументы из недоверенного источника, позаботьтесь об исключении или экранировании всех символов, которые могут иметь особое значение для оболочки. По соображениям безопасности лучше использовать фиксированную командную строку или как минимум не передавать в ней данные, поступающие от пользователя.

Для сторонних таблиц, использующих file_fdw, команда EXPLAIN показывает имя читаемого файла или запускаемой программы. Если не указывать COSTS OFF, то показывается также размер файла (в байтах).

Пример 1. Создание сторонней таблицы для журналов сервера QHB

Одно из очевидных применений file_fdw — это предоставления запросам доступа к журналу операций QHB как к таблице. Для этого необходимо заранее настроить вывод сообщений в файл CSV, который мы далее будем называть pglog.csv. Сначала установите расширение file_fdw:

CREATE EXTENSION file_fdw;

Затем создайте сторонний сервер:

CREATE SERVER pglog FOREIGN DATA WRAPPER file_fdw;

Теперь вы готовы к созданию сторонней таблицы. В команде CREATE FOREIGN TABLE нужно определить столбцы таблицы, имя файла CSV и его формат:

CREATE FOREIGN TABLE pglog (
  log_time timestamp(3) with time zone,
  user_name text,
  database_name text,
  process_id integer,
  connection_from text,
  session_id text,
  session_line_num bigint,
  command_tag text,
  session_start_time timestamp with time zone,
  virtual_transaction_id text,
  transaction_id bigint,
  error_severity text,
  sql_state_code text,
  message text,
  detail text,
  hint text,
  internal_query text,
  internal_query_pos integer,
  context text,
  query text,
  query_pos integer,
  location text,
  application_name text,
  backend_type text,
  leader_pid integer,
  query_id bigint
) SERVER pglog
OPTIONS ( filename 'log/pglog.csv', format 'csv' );

Вот и все — теперь вы можете выполнять запросы напрямую к журналу сервера. Конечно, в производственной среде еще понадобится как-то разобраться с ротацией файлов журнала.

fuzzystrmatch

Модуль fuzzystrmatch предоставляет несколько функций для определения схожести и расстояния между строками.

Внимание
В настоящее время функции soundex, metaphone, dmetaphone и dmetaphone_alt плохо работают с многобайтными кодировками (такими, как UTF-8).

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Soundex

Система Soundex представляет собой метод сопоставления похожих по звучанию имен путем приведения их к одному коду. Изначально она использовалась для обработки данных переписи населения США в 1880, 1900 и 1910 г. Обратите внимание, что Soundex не очень полезна для неанглоязычных имен.

Модуль fuzzystrmatch предоставляет две функции для работы с кодами Soundex:

soundex(text) returns text
difference(text, text) returns int

Функция soundex преобразует строку в ее код Soundex. Функция difference преобразует две строки в их коды Soundex, а затем сообщает количество совпадающих позиций в этих кодах. Поскольку коды Soundex состоят из четырех символов, результат находится в диапазоне от нуля до четырех, где ноль обозначает полное несоответствие, а четыре — точное совпадение. (Таким образом, имя этой функции не корректно — лучшим именем для нее было бы similarity.)

Несколько примеров использования:

SELECT soundex('hello world!');

SELECT soundex('Anne'), soundex('Ann'), difference('Anne', 'Ann');
SELECT soundex('Anne'), soundex('Andrew'), difference('Anne', 'Andrew');
SELECT soundex('Anne'), soundex('Margaret'), difference('Anne', 'Margaret');

CREATE TABLE s (nm text);

INSERT INTO s VALUES ('john');
INSERT INTO s VALUES ('joan');
INSERT INTO s VALUES ('wobbly');
INSERT INTO s VALUES ('jack');

SELECT * FROM s WHERE soundex(nm) = soundex('john');

SELECT * FROM s WHERE difference(s.nm, 'john') > 2;

Левенштейн

Эта функция вычисляет расстояние Левенштейна между двумя строками:

levenshtein(text source, text target, int ins_cost, int del_cost, int sub_cost) returns int
levenshtein(text source, text target) returns int
levenshtein_less_equal(text source, text target, int ins_cost, int del_cost, int sub_cost, int max_d) returns int
levenshtein_less_equal(text source, text target, int max_d) returns int

И в source, и в target может быть передана любая строка, отличная от NULL, не длиннее 255 символов. Параметры стоимости (ins_cost, del_cost, sub_cost) определяют цену добавления, удаления или замены символов соответственно. Эти параметры можно опустить, как во второй версии функции; в этом случае все они по умолчанию равны 1.

Функция levenshtein_less_equal является ускоренной версией функции Левенштейна, предназначенной для использования, когда интерес представляют только небольшие расстояния. Если фактическое расстояние меньше или равно max_d, то levenshtein_less_equal возвращает его точное значение; в противном случае она возвращает некоторое значение, превышающее max_d. Если значение max_d отрицательное, она работает так же, как функция levenshtein.

Примеры:

test=# SELECT levenshtein('GUMBO', 'GAMBOL');
 levenshtein
-------------
           2
(1 row)

test=# SELECT levenshtein('GUMBO', 'GAMBOL', 2, 1, 1);
 levenshtein
-------------
           3
(1 row)

test=# SELECT levenshtein_less_equal('extensive', 'exhaustive', 2);
 levenshtein_less_equal
------------------------
                      3
(1 row)

test=# SELECT levenshtein_less_equal('extensive', 'exhaustive', 4);
 levenshtein_less_equal
------------------------
                      4
(1 row)

Metaphone

Metaphone, как и Soundex, основывается на идее составления кода, представляющего входную строку. Две строки признаются похожими, если их коды совпадают.

Эта функция вычисляет кода метафона входной строки:

metaphone(text source, int max_output_length) returns text

В source должна быть передана строка, отличная от NULL, не длиннее 255 символов. Параметр max_output_length устанавливает максимальную длину выходного кода метафона; если код оказывается длиннее, выходная строка обрезается до этой длины.

Пример:

test=# SELECT metaphone('GUMBO', 4);
 metaphone
-----------
 KM
(1 row)

Double Metaphone

Система Double Metaphone (двойной метафон) вычисляет две строки «похожего звучания» для заданной строки — «первичную» и «альтернативную». В большинстве случаев они совпадают, но для неанглоязычных имен в особенности они могут немного отличаться, в зависимости от произношения. Эти функции вычисляют первичный и альтернативный коды:

dmetaphone(text source) returns text
dmetaphone_alt(text source) returns text

Ограничение на длину входных строк отсутствует.

Пример:

test=# SELECT dmetaphone('gumbo');
 dmetaphone
------------
 KMP
(1 row)

hstore

Этот модуль реализует тип данных hstore для хранения наборов пар ключ/значение внутри одного значения QHB. Это может быть полезно в различных сценариях, например для строк со множеством редко проверяемых атрибутов или частично структурированных данных. Ключи и значения представляют собой простые текстовые строки.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Внешнее представление hstore

Текстовое представление типа hstore, применяемое для ввода и вывода, включает ноль или более пар ключ => значение, разделенных запятыми. Несколько примеров:

k => v
foo => bar, baz => whatever
"1-a" => "anything at all"

Порядок пар не имеет значения (и может не воспроизводиться в выводе). Пробелы между парами и вокруг знака => игнорируются. Ключи и значения, содержащие пробелы, запятые и знаки = или >, нужно заключать в кавычки. Чтобы включить в ключ или значение обратный слэш или кавычки, следует экранировать его обратным слэшем.

Все ключи в hstore уникальны. Если объявить hstore с дублирующимися ключами, в нем будет сохранен только один ключ без гарантии выбора, какой именно:

SELECT 'a=>1,a=>2'::hstore;
  hstore
----------
 "a"=>"1"

Значением (но не ключом) может быть SQL NULL. Например:

key => NULL

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

Примечание
Учтите, что когда текстовый формат hstore используется для ввода данных, он применяется до всех требуемых кавычек или управляющих символов. Если вы передаете литерал hstore в параметре, в дополнительной обработке нет необходимости. Но если вы передаете его в виде строковой константы в кавычках, то все символы апострофов и (в зависимости от параметра конфигурации standard_conforming_strings) обратного слэша нужно корректно экранировать. Подробнее о работе со строковыми константами можно узнать в подразделе Строковые константы.

При выводе ключи и значения всегда заключаются в кавычки, даже если в этом нет особой необходимости.

Операторы и функции hstore

Операторы, предоставляемые модулем hstore, перечислены в Таблице 7, функции — в Таблице 8.

Таблица 7. Операторы hstore

Оператор | Описание | Пример(ы)
hstore -> text → text | Возвращает значение, связанное с заданным ключом, или NULL, если ключ отсутствует. | 'a=>x, b=>y'::hstore -> 'a' → x
hstore -> text[] → text[] | Возвращает значения, связанные с заданными ключами, или NULL, если ключи отсутствуют. | 'a=>x, b=>y, c=>z'::hstore -> ARRAY['c','a'] → {"z","x"}
hstore || hstore → hstore | Конкатенирует два набора hstores. | 'a=>b, c=>d'::hstore || 'c=>x, d=>q'::hstore → "a"=>"b", "c"=>"x", "d"=>"q"
hstore ? text → boolean | Набор hstore содержит ключ? | 'a=>1'::hstore ? 'a' → t
hstore ?& text[] → boolean | Набор hstore содержит все указанные ключи? | 'a=>1,b=>2'::hstore ?& ARRAY['a','b'] → t
hstore ?| text[] → boolean | Набор hstore содержит какой-либо из указанных ключей? | 'a=>1,b=>2'::hstore ?| ARRAY['b','c'] → t
hstore @> hstore → boolean | Левый операнд содержит правый? | 'a=>b, b=>1, c=>NULL'::hstore @> 'b=>1' → t
hstore <@ hstore → boolean | Левый операнд содержится в правом? | 'a=>c'::hstore <@ 'a=>b, b=>1, c=>NULL' → f
hstore - text → hstore | Удаляет ключ из левого операнда. | 'a=>1, b=>2, c=>3'::hstore - 'b'::text → "a"=>"1", "c"=>"3"
hstore - text[] → hstore | Удаляет ключи из левого операнда. | 'a=>1, b=>2, c=>3'::hstore - ARRAY['a','b'] → "c"=>"3"
hstore - hstore → hstore | Удаляет из левого операнда пары ключ/значение, совпадающие с парами в правом. | 'a=>1, b=>2, c=>3'::hstore - 'a=>4, b=>2'::hstore → "a"=>"1", "c"=>"3"
anyelement #= hstore → anyelement | Заменяет поля в левом операнде (который должен иметь составной тип) соответствующими значениями из hstore. | ROW(1,3) #= 'f1=>11'::hstore → (11,3)
%% hstore → text[] | Преобразует hstore в массив перемежающихся ключей и значений. | %% 'a=>foo, b=>bar'::hstore → {a,foo,b,bar}
%# hstore → text[] | Преобразует hstore в двумерный массив ключей/значений. | %# 'a=>foo, b=>bar'::hstore → {{a,foo},{b,bar}}

Таблица 8. Функции hstore

Функция
Описание
Пример(ы)

hstore ( record ) → hstore
Формирует hstore из записи или кортежа.
hstore(ROW(1,2)) → "f1"=>"1", "f2"=>"2"

hstore ( text[] ) → hstore
Формирует hstore из массива, который может быть двумерным или содержать перемежающиеся ключи/значения.
hstore(ARRAY['a','1','b','2']) → "a"=>"1", "b"=>"2"
hstore(ARRAY[['c','3'],['d','4']]) → "c"=>"3", "d"=>"4"

hstore ( text[], text[] ) → hstore
Формирует hstore из отдельных массивов ключей и значений.
hstore(ARRAY['a','b'], ARRAY['1','2']) → "a"=>"1", "b"=>"2"

hstore ( text, text ) → hstore
Формирует hstore с одним элементом.
hstore('a', 'b') → "a"=>"b"

akeys ( hstore ) → text[]
Извлекает ключи из hstore в виде массива.
akeys('a=>1,b=>2') → {a,b}

skeys ( hstore ) → setof text
Извлекает ключи из hstore в виде множества.
skeys('a=>1,b=>2') →
a
b

avals ( hstore ) → text[]
Извлекает значения из hstore в виде массива.
avals('a=>1,b=>2') → {1,2}

svals ( hstore ) → setof text
Извлекает значения из hstore в виде множества.
svals('a=>1,b=>2') →
1
2

hstore_to_array ( hstore ) → text[]
Извлекает ключи и значения из hstore в виде массива перемежающихся ключей и значений.
hstore_to_array('a=>1,b=>2') → {a,1,b,2}

hstore_to_matrix ( hstore ) → text[]
Извлекает ключи и значения из hstore в виде двумерного массива.
hstore_to_matrix('a=>1,b=>2') → {{a,1},{b,2}}

hstore_to_json ( hstore ) → json
Преобразует hstore в json, конвертируя все отличные от NULL в строки JSON.
Эта функция используется неявно, когда значение hstore приводится к типу json.
hstore_to_json('"a key"=>1, b=>t, c=>null, d=>12345, e=>012345, f=>1.234, g=>2.345e+4') → {"a key": "1", "b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4"}

hstore_to_jsonb ( hstore ) → jsonb
Преобразует hstore в jsonb, конвертируя все отличные от NULL в строки JSON.
Эта функция используется неявно, когда значение hstore приводится к типу jsonb.
hstore_to_jsonb('"a key"=>1, b=>t, c=>null, d=>12345, e=>012345, f=>1.234, g=>2.345e+4') → {"a key": "1", "b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4"}

hstore_to_json_loose ( hstore ) → json
Преобразует hstore в json, но при этом пытается распознать числовые и логические и передать их в JSON без кавычек.
hstore_to_json_loose('"a key"=>1, b=>t, c=>null, d=>12345, e=>012345, f=>1.234, g=>2.345e+4') → {"a key": 1, "b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4}

hstore_to_jsonb_loose ( hstore ) → jsonb
Преобразует hstore в jsonb, но при этом пытается распознать числовые и логические и передать их в JSON без кавычек.
hstore_to_jsonb_loose('"a key"=>1, b=>t, c=>null, d=>12345, e=>012345, f=>1.234, g=>2.345e+4') → {"a key": 1, "b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4}

slice ( hstore, text[] ) → hstore
Извлекает из hstore подмножество, содержащее только указанные ключи.
slice('a=>1,b=>2,c=>3'::hstore, ARRAY['b','c','x']) → "b"=>"2", "c"=>"3"

each ( hstore ) → setof record ( key text, value text )
Извлекает из hstore ключи и значения в виде набора записей.
select * from each('a=>1,b=>2') →
key | value
-----+-------
a | 1
b | 2

exist ( hstore, text ) → boolean
hstore содержит ключ?
exist('a=>1', 'a') → t

defined ( hstore, text ) → boolean
hstore содержит отличное от NULL значение для ключа?
defined('a=>NULL', 'a') → f

delete ( hstore, text ) → hstore
Удаляет пару с соответствующим ключом.
delete('a=>1,b=>2', 'b') → "a"=>"1"

delete ( hstore, text[] ) → hstore
Удаляет пары с соответствующими ключами.
delete('a=>1,b=>2,c=>3', ARRAY['a','b']) → "c"=>"3"

delete ( hstore, hstore ) → hstore
Удаляет пары, соответствующие парам во втором аргументе.
delete('a=>1,b=>2', 'a=>4,b=>2'::hstore) → "a"=>"1"

populate_record ( anyelement, hstore ) → anyelement
Заменяет поля в левом операнде (который должен иметь составной тип) соответствующими значениями из hstore.
populate_record(ROW(1,2), 'f1=>42'::hstore) → (42,2)

Помимо этих операторов и функций к значениям типа hstore можно обратиться по индексу, позволяя им действовать подобно ассоциативным массивам. Можно указать только один индекс типа text; он воспринимается как ключ, для которого извлекается или сохраняется соответствующее значение. Например:

CREATE TABLE mytable (h hstore);
INSERT INTO mytable VALUES ('a=>b, c=>d');
SELECT h['a'] FROM mytable;
 h
---
 b
(1 row)

UPDATE mytable SET h['c'] = 'new';
SELECT h FROM mytable;
          h
----------------------
 "a"=>"b", "c"=>"new"
(1 row)

При обращении по индексу возвращается NULL, если индекс — NULL или указанный ключ не существует в hstore. (Таким образом, обращение по индексу не сильно отличается от оператора ->.) Изменение по индексу не выполняется, если индекс — NULL; в остальных случаях заменяется значение указанного ключа (если такой ключ еще не существует, он добавляется в hstore).

Индексы

Тип hstore поддерживает индексы GiST и GIN для операторов @>, ?, ?& и ?|. Например:

CREATE INDEX hidx ON testhstore USING GIST (h);

CREATE INDEX hidx ON testhstore USING GIN (h);

Класс операторов GiST gist_hstore_ops аппроксимирует набор пар ключ/значение как сигнатуру битовой карты. Его необязательный целочисленный параметр siglen определяет длину сигнатуры в байтах. Значение по умолчанию — 16 байтов. Допустима длина сигнатуры в пределах от 1 до 2024 байтов. При увеличении размера сигнатур поиск работает точнее (сканируется меньшая область индекса и меньше страниц кучи), но сам индекс становится больше.

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

CREATE INDEX hidx ON testhstore USING GIST (h gist_hstore_ops(siglen=32));

Тип hstore также поддерживает индексы btree и hash для оператора =. Это позволяет объявлять столбцы hstore как уникальные (UNIQUE) или использовать их в выражениях GROUP BY, ORDER BY или DISTINCT. Порядок сортировки значений hstore не имеет особого смысла, но эти индексы могут быть полезны для поиска по равенству. Создать индексы для сравнений с помощью = можно так:

CREATE INDEX hidx ON testhstore USING BTREE (h);

CREATE INDEX hidx ON testhstore USING HASH (h);

Примеры

Присвоение значения новому или уже существующему ключу:

UPDATE tab SET h['c'] = '3';

Другой способ сделать то же самое:

UPDATE tab SET h = h || hstore('c', '3');

Если в одной операции нужно добавить или изменить несколько ключей, эффективнее использовать конкатенацию, а не обращение по индексу:

UPDATE tab SET h = h || hstore(array['q', 'w'], array['11', '12']);

Удаление ключа:

UPDATE tab SET h = delete(h, 'k1');

Преобразование типа record в тип hstore:

CREATE TABLE test (col1 integer, col2 text, col3 text);
INSERT INTO test VALUES (123, 'foo', 'bar');

SELECT hstore(t) FROM test AS t;
                   hstore
---------------------------------------------
 "col1"=>"123", "col2"=>"foo", "col3"=>"bar"
(1 row)

Преобразование типа hstore в предопределенный тип record:

CREATE TABLE test (col1 integer, col2 text, col3 text);

SELECT * FROM populate_record(null::test,
                              '"col1"=>"456", "col2"=>"zzz"');
 col1 | col2 | col3
------+------+------
  456 | zzz  |
(1 row)

Изменение существующей записи с использованием значений из hstore:

CREATE TABLE test (col1 integer, col2 text, col3 text);
INSERT INTO test VALUES (123, 'foo', 'bar');

SELECT (r).* FROM (SELECT t #= '"col3"=>"baz"' AS r FROM test t) s;
 col1 | col2 | col3
------+------+------
  123 | foo  | baz
(1 row)

Статистика

Из-за свойственной ему либеральности тип hstore может содержать множество различных ключей. Проверка ключей на допустимость является задачей этого приложения. Следующие примеры демонстрируют несколько приемов проверки ключей и получения статистики.

Простой пример:

SELECT * FROM each('aaa=>bq, b=>NULL, ""=>1');

С использованием таблицы:

CREATE TABLE stat AS SELECT (each(h)).key, (each(h)).value FROM testhstore;

Актуальная статистика:

SELECT key, count(*) FROM
  (SELECT (each(h)).key FROM testhstore) AS stat
  GROUP BY key
  ORDER BY count DESC, key;
    key    | count
-----------+-------
 line      |   883
 query     |   207
 pos       |   203
 node      |   202
 space     |   197
 status    |   195
 public    |   194
 title     |   190
 org       |   189
...................

Трансформации

Также имеются дополнительные расширения, которые реализуют трансформацию типа hstore для языков PL/Perl и PL/Python. Расширения для PL/Perl называются hstore_plperl и hstore_plperlu, для доверенного и недоверенного PL/Perl соответственно. Если вы установите эти трансформации и укажите их при создании функции, значения hstore будут отображаться в хеши Perl. Расширения для PL/Python называются hstore_plpythonu, hstore_plpython2u и hstore_plpython3u. Если вы воспользуетесь ими, значения hstore будут отображаться в словари Python.

Внимание
Настоятельно рекомендуется устанавливать расширения трансформации в одну схему с hstore. Если выбрать какую-либо другую схему, которая может содержать объекты, определенные злонамеренным пользователем, то при установке расширения возникнут угрозы безопасности.

Авторы

Олег Бартунов (oleg@sai.msu.su), Москва, Московский государственный университет имени М. В. Ломоносова, Россия

Федор Сигаев (teodor@sigaev.ru), Москва, ООО «Дельта-Софт», Россия

Дополнительные улучшения внес Эндрю Гирт (andrew@tao11.riddles.org.uk), Великобритания

intagg

Модуль intagg предоставляет агрегатор и нумератор целых чисел. На данный момент intagg считается устаревшим, поскольку в QHB имеются встроенные функции, предлагающие более широкие возможности. Однако этот модуль по-прежнему поставляется в целях обратной совместимости, в качестве обертки для встроенных функций.

Функции

Агрегатор реализуется функцией int_array_aggregate(integer), которая выдает массив целых чисел, содержащий в точности те числа, что переданы ей. Это обертка встроенной функции array_agg, которая делает то же самое для массива любого типа.

Нумератор реализуется функцией int_array_enum(integer[]), которая возвращает множество целых чисел (setof integer). По сути его действие обратно действию агрегатора: получив массив целых чисел, нумератор разворачивает его в набор строк. Это обертка встроенной функции unnest, которая делает то же самое для массива любого типа.

Примеры использования

Во многих СУБД есть понятие таблицы отношений «один ко многим». Такая таблица обычно находится между двумя индексированными таблицами, например:

CREATE TABLE left (id INT PRIMARY KEY, ...);
CREATE TABLE right (id INT PRIMARY KEY, ...);
CREATE TABLE one_to_many(left INT REFERENCES left, right INT REFERENCES right);

Обычно она используется так:

SELECT right.* from right JOIN one_to_many ON (right.id = one_to_many.right)
  WHERE one_to_many.left = item;

Этот запрос вернет все элементы из таблицы справа для записи в таблице слева. Это очень распространенная конструкция в SQL.

Однако этот подход может оказаться трудоемким, если в таблице one_to_many очень большое количество записей. Зачастую такое соединение приводит к сканированию индекса и выборке для каждой записи в таблице справа для определенного элемента слева. Если у вас крайне динамическая система, с этим ничего не поделать. Однако если некоторые данные относительно статичны, вы можете создать сводную таблицу при помощи агрегатора.

CREATE TABLE summary AS
  SELECT left, int_array_aggregate(right) AS right
  FROM one_to_many
  GROUP BY left;

Эта команда создаст таблицу, содержащую одну строку для каждого элемента слева с массивом элементов справа. Она довольно бесполезна, пока нет подходящего способа использования этого массива; именно для этого и нужен нумератор массива. Вы можете выполнить:

SELECT left, int_array_enum(right) FROM summary WHERE left = item;

Приведенный выше запрос с вызовом int_array_enum выдает те же результаты, что и

SELECT left, right FROM one_to_many WHERE left = item;

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

В отдельной системе команда EXPLAIN показала, что стоимость запроса снизилась с 8488 до 329. Исходный запрос выполнял соединение с таблицей one_to_many и был заменен на:

SELECT right, count(right) FROM
  ( SELECT left, int_array_enum(right) AS right
    FROM summary JOIN (SELECT left FROM left_table WHERE left = item) AS lefts
         ON (summary.left = lefts.left)
  ) AS list
  GROUP BY right
  ORDER BY count DESC;

intarray

Модуль intarray предоставляет ряд полезных функций и операторов для работы с массивами целых чисел без NULL. Также для некоторых из этих операторов он поддерживает поиск по индексу.

Все эти операции выдадут ошибку, если передаваемый массив содержит элементы NULL.

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

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Функции и операторы intarray

Предоставляемые модулем intarray функции перечислены в Таблице 9, а операторы — в Таблице 10.

Таблица 9. Функции intarray

Функция
Описание
Пример(ы)

icount ( integer[] ) → integer
Возвращает число элементов в массиве.
icount('{1,2,3}'::integer[]) → 3

sort ( integer[], dir text ) → integer[]
Сортирует массив в порядке возрастания или убывания. Направление задает параметр dir, значением которого должно быть asc (по возрастанию) или desc (по убыванию).
sort('{1,3,2}'::integer[], 'desc') → {3,2,1}

sort ( integer[] ) → integer[]
sort_asc ( integer[] ) → integer[]
Сортирует в порядке возрастания.
sort(array[11,77,44]) → {11,44,77}

sort_desc ( integer[] ) → integer[]
Сортирует в порядке убывания.
sort_desc(array[11,77,44]) → {77,44,11}

uniq ( integer[] ) → integer[]
Убирает соседние дубликаты.
uniq(sort('{1,2,3,2,1}'::integer[])) → {1,2,3}

idx ( integer[], item integer ) → integer
Возвращает индекс первого элемента массива, соответствующего item, или 0, если такого элемента нет.
idx(array[11,22,33,22,11], 22) → 2

subarray ( integer[], start integer, len integer ) → integer[]
Извлекает часть массива, начинающуюся с позиции start и содержащую len элементов.
subarray('{1,2,3,2,1}'::integer[], 2, 3) → {2,3,2}

subarray ( integer[], start integer ) → integer[]
Извлекает часть массива, начинающуюся с позиции start.
subarray('{1,2,3,2,1}'::integer[], 2) → {2,3,2,1}

intset ( integer ) → integer[]
Создает массив с одним элементом.
intset(42) → {42}

Таблица 10. Операторы intarray

Оператор | Описание
integer[] && integer[] → boolean | Массивы пересекаются (имеют хотя бы один общий элемент)?
integer[] @> integer[] → boolean | Левый массив содержит правый?
integer[] <@ integer[] → boolean | Левый массив содержится в правом?
# integer[] → integer | Возвращает число элементов в массиве.
integer[] # integer → integer | Возвращает индекс первого элемента массива, соответствующего правому аргументу, или 0, если такого элемента нет. (Аналог функции idx.)
integer[] + integer → integer[] | Добавляет элемент в конец массива.
integer[] + integer[] → integer[] | Конкатенирует массивы.
integer[] - integer → integer[] | Убирает из массива записи, соответствующие правому аргументу.
integer[] - integer[] → integer[] | Убирает из левого массива элементы правого массива.
integer[] | integer → integer[] | Вычисляет объединение аргументов.
integer[] | integer[] → integer[] | Вычисляет объединение аргументов.
integer[] & integer[] → integer[] | Вычисляет пересечение аргументов.
integer[] @@ query_int → boolean | Массив удовлетворяет запросу? (см. ниже)
query_int ~~ integer[] → boolean | Массив удовлетворяет запросу? (коммутатор к @@)

Операторы &&, @> и <@ равнозначны встроенным операторам QHB с теми же именами, за исключением того, что они работают только с целочисленными массивами, не содержащими NULL, тогда как встроенные операторы работают с массивами любого типа. Из-за этого ограничения они в большинстве случаев работают быстрее встроенных операторов.

Операторы @@ и ~~ проверяют, удовлетворяет ли массив запросу, который выражается в виде значения специализированного типа данных query_int. Запрос состоит из целочисленных значений, сравниваемых с элементами массива, возможно, с применением операторов & (AND), | (OR) и ! (NOT). При необходимости можно использовать скобки. Например, запросу 1&(2\|3) соответствуют массивы, которые содержат 1 и также содержат 2 или 3.

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

Модуль intarray поддерживает индексы для операторов &&, @> и @@ наряду с обычной проверкой равенства массивов.

Модуль предоставляет два параметризированных класса операторов индексов GiST: gist__int_ops (используемый по умолчанию) подходит для маленьких и средних по размеру наборов данных, тогда как gist__intbig_ops применяет сигнатуру большего размера и лучше подходит для индексации больших наборов данных (т. е. столбцов, содержащих много различных значений массива). В этой реализации используется структура данных RD-дерева со встроенным сжатием с потерями.

Класс gist__int_ops аппроксимирует набор целых чисел в виде массива целочисленных диапазонов. Его необязательный целочисленный параметр numranges определяет максимальное количество диапазонов в одном ключе индекса. Значение по умолчанию — 100. Допустимые значения лежат в пределах от 1 до 253. При увеличении размера массивов, составляющих ключ индекса GiST, поиск работает точнее (сканируется меньшая область индекса и меньше страниц кучи), но сам индекс становится больше.

Класс gist__intbig_ops аппроксимирует набор целых чисел в виде сигнатуры битовой карты. Его необязательный целочисленный параметр siglen определяет длину сигнатуры в байтах. Значение по умолчанию — 16 байтов. Допустима длина сигнатуры в пределах от 1 до 2024 байтов. При увеличении размера сигнатур поиск работает точнее (сканируется меньшая область индекса и меньше страниц кучи), но сам индекс становится больше.

Имеется также нестандартный класс операторов GIN, gin__int_ops, который поддерживает эти операторы наряду с <@.

Выбор между поиском по индексам GiST и GIN зависит от относительных характеристик производительности GiST и GIN, которые здесь не рассматриваются.

Пример

-- сообщение может находиться в одной или нескольких «секциях»
CREATE TABLE message (mid INT PRIMARY KEY, sections INT[], ...);

-- создать специализированный индекс с длиной сигнатуры 32 байта
CREATE INDEX message_rdtree_idx ON message USING GIST (sections gist__intbig_ops (siglen = 32));

-- выбрать сообщения в секции 1 ИЛИ 2 - оператор ПЕРЕСЕЧЕНИЯ
SELECT message.mid FROM message WHERE message.sections && '{1,2}';

-- выбрать сообщения в секциях 1 И 2 - оператор ВКЛЮЧЕНИЯ
SELECT message.mid FROM message WHERE message.sections @> '{1,2}';

-- то же самое, но с применением оператора ЗАПРОСА
SELECT message.mid FROM message WHERE message.sections @@ '1&2'::query_int;

Тестирование производительности

В каталоге исходного кода contrib/intarray/bench содержится набор тестов производительности, которые можно провести на установленном сервере QHB. (Для этого также нужно установить пакет DBD::Pg.) Для запуска тестов выполните:

cd .../contrib/intarray/bench
createdb TEST
psql -c "CREATE EXTENSION intarray" TEST
./create_test.pl | psql TEST
./bench.pl

Скрипт bench.pl имеет несколько параметров, которые отображаются, если запустить его без аргументов.

Авторы

Разработку осуществили Федор Сигаев (teodor@sigaev.ru) и Олег Бартунов (oleg@sai.msu.su). Дополнительную информацию см. на странице http://www.sai.msu.su/~megera/postgres/gist/. Андрей Октябрьский проделал отличную работу, добавив новые функции и операторы.

isn

Модуль isn предоставляет типы данных для следующих международных стандартов нумерации товаров: EAN13, UPC, ISBN (книги), ISMN (музыка) и ISSN (серийные номера). Номера проверяются на входе согласно жестко закодированному списку префиксов; этот список префиксов также используется для разбивки (при помощи дефиса) номеров при выводе. Поскольку время от времени появляются новые префиксы, этот список префиксов может устаревать. Ожидается, что будущая версия этого модуля будет получать список префиксов из одной или нескольких таблиц, которые при необходимости смогут легко обновлять пользователи; однако в настоящее время этот список можно обновить, только модифицировав и перекомпилировав исходный код. Также есть вероятность, что из будущей версии этого модуля будет удалена возможность проверки префиксов и поддержка разбивки номеров.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Типы данных

В Таблице 11 перечислены типы данных, предоставляемые модулем isn.

Таблица 11. Типы данных isn

Тип данных | Описание
EAN13 | Европейский номер товара (European Article Number), всегда отображается в формате EAN13
ISBN13 | Международный стандартный книжный номер (International Standard Book Number), отображаемый в новом формате EAN13
ISMN13 | Международный стандартный музыкальный номер (International Standard Music Number), отображаемый в новом формате EAN13
ISSN13 | Международный стандартный серийный номер (International Standard Serial Number), отображаемый в новом формате EAN13
ISBN | Международный стандартный книжный номер, отображаемый в старом коротком формате
ISMN | Международный стандартный музыкальный номер, отображаемый в старом коротком формате
ISSN | Международный стандартный серийный номер, отображаемый в старом коротком формате
UPC | Универсальный код товара (Universal Product Code)

Несколько примечаний:

Номера ISBN13, ISMN13, ISSN13 являются номерами EAN13.

Не все номера EAN13 (только некоторые) представляют ISBN13, ISMN13 или ISSN13.

Некоторые номера ISBN13 можно отобразить в формате ISBN.

Некоторые номера ISMN13 можно отобразить в формате ISMN.

Некоторые номера ISSN13 можно отобразить в формате ISSN.

Номера UPC являются подмножеством номеров EAN13 (по сути они являются номерами EAN13 без первой цифры 0).

Все номера UPC, ISBN, ISMN и ISSN можно представить как номера EAN13.

Внутри все эти типы используют одинаковое представление (64-битное целое число), и все они взаимозаменяемы. Различные типы предоставляются для управления форматированием при выводе и для более строгой проверки правильности ввода, что и должно определять конкретный тип номера.

Типы ISBN, ISMN и ISSN отображают короткую версию номера (ISxN 10), когда это возможно, и выбирают формат ISxN 13 для номеров, не умещающихся в короткую версию. Типы EAN13, ISBN13, ISMN13 и ISSN13 всегда отображают длинную версию ISxN (EAN13).

Приведения

Модуль isn предоставляет следующие пары приведений типов:

ISBN13 <=> EAN13

ISMN13 <=> EAN13

ISSN13 <=> EAN13

ISBN <=> EAN13

ISMN <=> EAN13

ISSN <=> EAN13

UPC <=> EAN13

ISBN <=> ISBN13

ISMN <=> ISMN13

ISSN <=> ISSN13

При приведении EAN13 к другому типу проводится проверка времени выполнения, соответствует ли это значение целевому типу, и если это не так, выдается ошибка. При других приведениях значения просто перемечаются, поэтому они всегда выполняются успешно.

Функции и операторы

Модуль isn предоставляет стандартные операторы сравнения плюс поддержку индексов по хешу и B-деревьев для всех этих типов данных. Кроме того, он реализует несколько специализированных функций, перечисленных в Таблице 12. В этой таблице isn обозначает один из типов данных модуля.

Таблица 12. Функции isn

Функция | Описание
isn_weak ( boolean ) → boolean | Устанавливает ослабленный режим проверки ввода и возвращает новую настройку.
isn_weak () → boolean | Возвращает текущее состояние ослабленного режима.
make_valid ( isn ) → isn | Валидирует неверный номер (сбрасывает флаг ошибки).
is_valid ( isn ) → boolean | Проверяет присутствие флага ошибки.

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

Зачем может понадобиться ослабленный режим? Ну, может случиться так, что у вас есть большой набор номеров ISBN, и очень многие из них по каким-то странным причинам содержат неверную контрольную цифру (возможно, эти номера были отсканированы с печатного листа и были неправильно распознаны, возможно, они вводились вручную... кто знает). В любом случае, смысл в том, что вы хотите с этим разобраться, но вам все равно нужно загрузить все эти номера в вашу базу данных и, может быть, использовать дополнительный инструмент для поиска неверных номеров в базе данных, чтобы было проще проверить и исправить информацию; так что, например, вам нужно будет выбрать все неверные номера в таблице.

Если вставить в таблице неверные номер в ослабленном режиме, этот номер будет вставлен с исправленной проверочной цифрой, но отобразится с восклицательным знаком (!) в конце, например 0-11-000322-5!. Этот маркер ошибки можно проверить с помощью функции is_valid и очистить функцией make_valid.

Также можно принудительно вставить неверные номера даже не в ослабленном режиме, добавив символ ! в конце номера.

Есть еще одна специальная возможность — во время ввода вместо проверочной цифры можно написать ?, и правильная проверочная цифра будет вставлена автоматически.

Примеры

--Использование типов напрямую:
SELECT isbn('978-0-393-04002-9');
SELECT isbn13('0901690546');
SELECT issn('1436-4522');

--Приведение типов:
-- обратите внимание, что номер ean13 можно привести к другому типу, только когда
-- этот номер будет верным по меркам целевого типа;
-- таким образом, это НЕ будет работать: select isbn(ean13('0220356483481'));
-- а это будет:
SELECT upc(ean13('0220356483481'));
SELECT ean13(upc('220356483481'));

--Создание таблицы с одним столбцом, который будет содержать номера ISBN:
CREATE TABLE test (id isbn);
INSERT INTO test VALUES('9780393040029');

--Автоматическое вычисление проверочных цифр (обратите внимание на '?'):
INSERT INTO test VALUES('220500896?');
INSERT INTO test VALUES('978055215372?');

SELECT issn('3251231?');
SELECT ismn('979047213542?');

--Использование ослабленного режима:
SELECT isn_weak(true);
INSERT INTO test VALUES('978-0-11-000533-4');
INSERT INTO test VALUES('9780141219307');
INSERT INTO test VALUES('2-205-00876-X');
SELECT isn_weak(false);

SELECT id FROM test WHERE NOT is_valid(id);
UPDATE test SET id = make_valid(id) WHERE id = '2-205-00876-X!';

SELECT * FROM test;

SELECT isbn13(id) FROM test;

Библиография

Информация, на основе которой реализован этот модуль, была собрана с нескольких сайтов, включая:

https://www.isbn-international.org/

https://www.issn.org/

https://www.ismn-international.org/

https://www.wikipedia.org/

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

https://www.gs1.org/standards/id-keys

https://en.wikipedia.org/wiki/List_of_ISBN_identifier_groups

https://www.isbn-international.org/content/isbn-users-manual/29

https://en.wikipedia.org/wiki/International_Standard_Music_Number

https://www.ismn-international.org/ranges/tools

Алгоритмы были созданы со всей тщательностью и были скрупулезно сверены с алгоритмами, предлагаемыми в официальных руководствах ISBN, ISMN, ISSN.

Автор

Герман Мендез Браво (Kronuz), 2004–2006

Этот модуль был написан под влиянием кода isbn_issn Гаррета А. Уоллмена.

lo

Модуль lo предоставляет поддержку управления большими объектами (также называемыми БО или LO (Large Objects) либо BLOB (Binary Large OBjects)). Это включает в себя тип данных lo и триггер lo_manage.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Обоснование

Одна из проблем с драйвером JDBC (которая распространяется и на драйвер ODBC), в том, что спецификация предполагает, что ссылки на BLOB хранятся в таблице, и если эта запись меняется, связанный BLOB удаляется из базы данных.

Однако с QHB этого не происходит. Большие объекты обрабатываются как самостоятельные объекты; запись в таблице может ссылаться на большой объект по OID, но при этом на один и тот же OID большого объекта могут ссылаться несколько записей таблицы, так что система не удаляет большой объект только потому, что вы меняете или удаляете одну такую запись.

Это не проблема для приложений, специфичных для QHB, но стандартный код, использующий JDBC или ODBC, не будет удалять эти объекты, в результате чего они окажутся потерянными — объектами, на которые ничто не ссылается и которые просто занимают место на диске.

Модуль lo позволяет это исправить, добавляя триггер к таблицам, которые содержат столбцы, ссылающиеся на БО. Этот триггер по сути просто вызывает lo_unlink, когда вы удаляете или изменяете значение, ссылающееся на большой объект. При использовании этого триггера предполагается, что на любой большой объект, на который ссылается управляемый триггером столбец, указывает только одна ссылка базы данных!

Этот модуль также предоставляет тип данных lo, который на самом деле является просто доменом на базе типа oid. Это полезно для дифференциации столбцов базы данных, хранящих ссылки на большие объекты, от столбцов, содержащих OID или какие-то другие данные. Для использования триггера применять тип lo необязательно, но он может быть полезен для отслеживания того, какие столбцы в вашей базе данных предоставляют большие объекты, которыми управляет триггер. Также есть мнение, что драйвер ODBC не работает корректно, если не использовать для столбцов BLOB тип lo.

Как его использовать

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

CREATE TABLE image (title text, raster lo);

CREATE TRIGGER t_raster BEFORE UPDATE OR DELETE ON image
    FOR EACH ROW EXECUTE FUNCTION lo_manage(raster);

Для каждого столбца, который будет содержать уникальные ссылки на большие объекты, создайте триггер BEFORE UPDATE OR DELETE и передайте имя столбца в качестве единственного аргумента триггера. Также можно ограничить этот триггер, чтобы он срабатывал только при изменениях в столбце, указав BEFORE UPDATE OF имя_столбца. Если вам нужно иметь в одной таблице несколько столбцов lo, создайте отдельный триггер для каждого, не забывая давать всем этим триггерам разные имена.

Ограничения

При удалении таблицы относящиеся к ней объекты все равно будут потеряны, так как триггер не выполнится. Этого можно избежать, выполнив перед командой DROP TABLE команду DELETE FROM таблица.

То же касается и команды TRUNCATE.

Если у вас уже есть, или вы подозреваете, что есть потерянные большие объекты, обратите внимание на модуль vacuumlo, который поможет вам их вычистить. Имеет смысл периодически запускать vacuumlo в качестве поддержки триггера lo_manage.

Некоторые клиентские части приложений могут создавать собственные таблицы, но не будут создавать для них соответствующие триггеры. Кроме того, пользователи могут забыть и не создать такие триггеры (или не знать, как это делается).

Автор

Питер Маунт (peter@retep.org.uk)

ltree

Этот модуль реализует тип данных ltree для представления меток данных, хранящихся в древовидной иерархической структуре. Он также предоставляет расширенные средства для поиска в деревьях меток.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Определения

Метка — это последовательность буквенно-цифровых символов и знаков подчеркивания (например, в локали C допускаются символы A-Za-z0-9_). Длина метки должна быть меньше 256 символов.

Примеры: 42, Personal_Services

Путь метки — это последовательность из нуля и более разделенных точками меток, например L1.L2.L3, представляющая путь от корня иерархического дерева к конкретному узлу. Длина пути метки не может составлять более 65535 меток.

Пример: Top.Countries.Europe.Russia

Модуль ltree предоставляет несколько типов данных:

ltree хранит путь метки.

lquery представляет похожий на регулярные выражения шаблон для поиска соответствующих значений ltree. В нем простое слово выбирает соответствующую метку в пути, а символ звездочки (*) — ноль и более любых меток. Отдельные элементы можно соединить точками, чтобы сформировать шаблон, которому должен соответствовать весь путь указанных меток. Например:

foo         Выбирает путь ровно с одной меткой foo
*.foo.*     Выбирает любой путь, содержащий метку foo
*.foo       Выбирает любой путь с последней меткой foo

И для символов звездочки, и для простых слов можно добавить количественное значение, ограничивающее число меток, которые будут соответствовать этому элементу:

*{n}        Выбирает ровно n меток
*{n,}       Выбирает как минимум n меток
*{n,m}      Выбирает как минимум n, но не более m меток
*{,m}       Выбирает не более m меток — равнозначно *{0,m}
foo{n,m}    Выбирает как минимум n, но не более m вхождений foo
foo{,}      Выбирает любое количество вхождений foo, включая ноль

В отсутствие явного квантификатора символу звездочки по умолчанию соответствует любое количество меток (то есть {,}), тогда как простому слову — ровно одна (то есть {1}).

Существует несколько модификаторов, которые можно добавить после отличного от звездочки элемента lquery, что позволит выбирать не только точные совпадения:

@           Выбирает совпадение без учета регистра, например шаблону a@ соответствует A
*           Выбирает любую метку с этим префиксом, например foo* соответствует foobar
%           Выбирает в метке начальные слова, разделенные подчеркиваниями

Поведение модификатора % несколько замысловатое. Он пытается найти соответствие по словам, а не по всей метке. Например, шаблон foo_bar% выбирает foo_bar_baz, но не foo_barbaz. В сочетании с * сопоставление префикса применяется отдельно к каждому слову, например, шаблон foo_bar%* выбирает foo1_bar2_baz, но не foo1_br2_baz.

Кроме того, можно записать несколько различных (в том числе модифицированных) элементов, отличных от звездочек, через знак | (ИЛИ) для выбора любой из этих меток или добавить знак ! (НЕ) в начале группа без звездочки для выбора любой метки, не соответствующей ни одной из указанных альтернатив. Если задается квантификатор, он ставится в конце группы; это означает, что он действует на всю группу в целом (то есть ограничивает количество меток, соответствующих или не соответствующих любой из альтернатив).

Расширенный пример lquery:

Top.*{0,2}.sport*@.!football|tennis{1,}.Russ*|Spain
a.  b.     c.      d.                   e.

Этот запрос выберет любой путь, который:
a. начинается с метки Top
b. затем включает от нуля до двух меток до
c. метки, начинающейся с префикса sport (без учета регистра)
d. затем одну или более меток, отличных от football и tennis
e. и заканчивается меткой, которая начинается с Russ или точно соответствует Spain.

ltxtquery представляет похожий на полнотекстовый запрос шаблон для поиска соответствующих значений ltree. Значение ltxtquery содержит слова, возможно, с модификаторами @, *, % в конце; эти модификаторы имеют тот же смысл, что и в lquery Слова можно комбинировать с символами & (И), | (ИЛИ), ! (НЕ) и скобками. Ключевое отличие от lquery состоит в том, что ltxtquery выбирает слова независимо от их положения в пути метки.

Пример ltxtquery:

Europe & Russia*@ & !Transportation

Этот шаблон выберет пути, содержащие метку Europe и любую метку, начинающуюся с Russia (без учета регистра), но не пути, содержащие метку Transportation. Положение этих слов в пути не имеет значения. Кроме того, когда используется %, слово может быть сопоставлено с любым другим отделенным подчеркиванием словом в метке, независимо от его положения.

Примечание: ltxtquery допускает пробелы между символами, а ltree и lquery — нет.

Операторы и функции

Для типа ltree определены обычные операторы сравнения =, <>, <, >, <=, >=. Сравнение сортирует пути в порядке движения по дереву, а потомки узла сортируются по тексту метки. В дополнение к ним доступны специализированные операторы, перечисленные в Таблице 13.

Таблица 13. Операторы ltree

Оператор
Описание

ltree @> ltree → boolean
Левый аргумент является предком правого (или равен ему)?

ltree <@ ltree → boolean
Левый аргумент является потомком правого (или равен ему)?

ltree ~ lquery → boolean
lquery ~ ltree → boolean
Значение ltree соответствует lquery?

ltree ? lquery[] → boolean
lquery[] ? ltree → boolean
Значение ltree соответствует какому-либо lquery в массиве?

ltree @ ltxtquery → boolean
ltxtquery @ ltree → boolean
Значение ltree соответствует ltxtquery?

ltree || ltree → ltree
Конкатенирует пути ltree.

ltree || text → ltree
text || ltree → ltree
Преобразует текст в ltree и конкатенирует его с путем.

ltree[] @> ltree → boolean
ltree <@ ltree[] → boolean
Массив содержит предка ltree?

ltree[] <@ ltree → boolean
ltree @> ltree[] → boolean
Массив содержит потомка ltree?

ltree[] ~ lquery → boolean
lquery ~ ltree[] → boolean
Массив содержит какой-либо путь, соответствующий lquery?

ltree[] ? lquery[] → boolean
lquery*[] ? ltree[*] → boolean
Массив ltree содержит какой-либо путь, соответствующий какому-либо lquery?

ltree[] @ ltxtquery → boolean
ltxtquery @ ltree[] → boolean
Массив содержит какой-либо путь, соответствующий ltxtquery?

ltree[] ?@> ltree → ltree
Возвращает первую запись массива, являющуюся предком ltree, или NULL, если такой записи нет.

ltree[] ?<@ ltree → ltree
Возвращает первую запись массива, являющуюся потомком ltree, или NULL, если такой записи нет.

ltree[] ?~ lquery → ltree
Возвращает первую запись массива, соответствующую lquery, или NULL, если такой записи нет.

ltree[] ?@ ltxtquery → ltree
Возвращает первую запись массива, соответствующую ltxtquery, или NULL, если такой записи нет.

У операторов <@, @>, @ и ~ есть аналоги ^<@, ^@>, ^@, ^~, которые отличаются только тем, что не используют индексы. Они полезны только для тестирования.

Доступные функции перечислены в Таблице 14.

Таблица 14. Функции ltree

Функция | Описание | Пример(ы)
subltree ( ltree, start integer, end integer ) → ltree | Возвращает подпуть ltree от позиции start до позиции end-1 (считая от 0). | subltree('Top.Child1.Child2', 1, 2) → Child1
subpath ( ltree, offset integer, len integer ) → ltree | Возвращает подпуть ltree, начиная с позиции offset, длиной len. Если offset отрицательный, подпуть начинается с этого смещения от конца пути. Если len отрицательный, функция отбрасывает заданное количество меток с конца пути. | subpath('Top.Child1.Child2', 0, 2) → Top.Child1
subpath ( ltree, offset integer ) → ltree | Возвращает подпуть ltree, начиная с позиции offset и до конца пути. Если offset отрицательный, подпуть начинается с этого смещения от конца пути. | subpath('Top.Child1.Child2', 1) → Child1.Child2
nlevel ( ltree ) → integer | Возвращает количество меток в пути. | nlevel('Top.Child1.Child2') → 3
index ( a ltree, b ltree ) → integer | Возвращает позицию первого вхождения b в a или -1, если такое вхождение не найдено. | index('0.1.2.3.5.4.5.6.8.5.6.8', '5.6') → 6
index ( a ltree, b ltree, offset integer ) → integer | Возвращает позицию первого вхождения b в a или -1, если такое вхождение не найдено. Поиск начинается с позиции offset; отрицательный offset означает, что начальное смещение составляет -offset меток от конца пути. | index('0.1.2.3.5.4.5.6.8.5.6.8', '5.6', -4) → 9
text2ltree ( text ) → ltree | Приводит значение text к типу ltree. |
ltree2text ( ltree ) → text | Приводит значение ltree к типу text. |
lca ( ltree [, ltree [, ... ]] ) → ltree | Вычисляет длиннейшего общего предка путей (способна принимать до 8 аргументов). | lca('1.2.3', '1.2.3.4.5.6') → 1.2
lca ( ltree[] ) → ltree | Вычисляет длиннейшего общего предка путей в массиве. | lca(array['1.2.3'::ltree,'1.2.3.4']) → 1.2

Индексы

ltree поддерживает несколько типов индексов, которые могут ускорить указанные операторы:

Индекс B-дерево по значениям ltree: <, <=, =, >=, >

Индекс GiST по значениям ltree (класс операторов gist_ltree_ops): <, <=, =, >=, >, @>, <@, @, ~, ?

Класс операторов GiST gist_ltree_ops аппроксимирует набор меток пути в виде сигнатуры битовой карты. Его необязательный целочисленный параметр siglen определяет длину сигнатуры в байтах. Значение по умолчанию — 8 байтов. Допустима длина сигнатуры в пределах от 1 до 2024 байтов. При увеличении размера сигнатур поиск работает точнее (сканируется меньшая область индекса и меньше страниц кучи), но сам индекс становится больше.

Пример создания такого индекса с длиной сигнатуры по умолчанию (8 байтов):

CREATE INDEX path_gist_idx ON test USING GIST (path);

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

CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));

Индекс GiST по массиву ltree[] (класс операторов gist__ltree_ops): ltree[] <@ ltree, ltree @> ltree[], @, ~, ?

Класс операторов GiST gist__ltree_ops работает подобно gist_ltree_ops и также принимает в качестве параметра длину сигнатуры. По умолчанию значение siglen в gist__ltree_ops составляет 28 байтов.

Пример создания такого индекса с длиной сигнатуры по умолчанию (28 байтов):

CREATE INDEX path_gist_idx ON test USING GIST (array_path);

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

CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=100));

Примечание: Индекс этого типа является неточным.

Пример

Для этого примера используются следующие данные (также их можно найти в файле contrib/ltree/ltreetest.sql в дистрибутиве исходного кода):

CREATE TABLE test (path ltree);
INSERT INTO test VALUES ('Top');
INSERT INTO test VALUES ('Top.Science');
INSERT INTO test VALUES ('Top.Science.Astronomy');
INSERT INTO test VALUES ('Top.Science.Astronomy.Astrophysics');
INSERT INTO test VALUES ('Top.Science.Astronomy.Cosmology');
INSERT INTO test VALUES ('Top.Hobbies');
INSERT INTO test VALUES ('Top.Hobbies.Amateurs_Astronomy');
INSERT INTO test VALUES ('Top.Collections');
INSERT INTO test VALUES ('Top.Collections.Pictures');
INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy');
INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Stars');
INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Galaxies');
INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Astronauts');
CREATE INDEX path_gist_idx ON test USING GIST (path);
CREATE INDEX path_idx ON test USING BTREE (path);

Итак, мы получаем таблицу test, наполненную данными, описывающими представленную ниже иерархию:

                        Top
                     /   |  \
             Science Hobbies Collections
                 /       |              \
        Astronomy   Amateurs_Astronomy Pictures
           /  \                            |
Astrophysics  Cosmology                Astronomy
                                        /  |    \
                                 Galaxies Stars Astronauts

Мы можем выбрать потомков в иерархии наследования:

ltreetest=> SELECT path FROM test WHERE path <@ 'Top.Science';
                path
------------------------------------
 Top.Science
 Top.Science.Astronomy
 Top.Science.Astronomy.Astrophysics
 Top.Science.Astronomy.Cosmology
(4 rows)

Несколько примеров выборки по путям:

ltreetest=> SELECT path FROM test WHERE path ~ '*.Astronomy.*';
                     path
-----------------------------------------------
 Top.Science.Astronomy
 Top.Science.Astronomy.Astrophysics
 Top.Science.Astronomy.Cosmology
 Top.Collections.Pictures.Astronomy
 Top.Collections.Pictures.Astronomy.Stars
 Top.Collections.Pictures.Astronomy.Galaxies
 Top.Collections.Pictures.Astronomy.Astronauts
(7 rows)

ltreetest=> SELECT path FROM test WHERE path ~ '*.!pictures@.Astronomy.*';
                path
------------------------------------
 Top.Science.Astronomy
 Top.Science.Astronomy.Astrophysics
 Top.Science.Astronomy.Cosmology
(3 rows)

Несколько примеров полнотекстового поиска:

ltreetest=> SELECT path FROM test WHERE path @ 'Astro*% & !pictures@';
                path
------------------------------------
 Top.Science.Astronomy
 Top.Science.Astronomy.Astrophysics
 Top.Science.Astronomy.Cosmology
 Top.Hobbies.Amateurs_Astronomy
(4 rows)

ltreetest=> SELECT path FROM test WHERE path @ 'Astro* & !pictures@';
                path
------------------------------------
 Top.Science.Astronomy
 Top.Science.Astronomy.Astrophysics
 Top.Science.Astronomy.Cosmology
(3 rows)

Создание пути с помощью функций:

ltreetest=> SELECT subpath(path,0,2)||'Space'||subpath(path,2) FROM test WHERE path <@ 'Top.Science.Astronomy';
                 ?column?
------------------------------------------
 Top.Science.Space.Astronomy
 Top.Science.Space.Astronomy.Astrophysics
 Top.Science.Space.Astronomy.Cosmology
(3 rows)

Эту процедуру можно упростить, создав функцию SQL, вставляющую метку в заданную позицию в пути:

CREATE FUNCTION ins_label(ltree, int, text) RETURNS ltree
    AS 'select subpath($1,0,$2) || $3 || subpath($1,$2);'
    LANGUAGE SQL IMMUTABLE;

ltreetest=> SELECT ins_label(path,2,'Space') FROM test WHERE path <@ 'Top.Science.Astronomy';
                ins_label
------------------------------------------
 Top.Science.Space.Astronomy
 Top.Science.Space.Astronomy.Astrophysics
 Top.Science.Space.Astronomy.Cosmology
(3 rows)

Трансформации

Также имеются дополнительные расширения, которые реализуют трансформацию типа ltree для языка PL/Python. Эти расширения называются ltree_plpythonu, ltree_plpython2u и ltree_plpython3u. Если вы установите эти трансформации и укажите их при создании функции, значения ltree будут отображаться в списки Python. (Однако обратное преобразование в настоящее время не поддерживается.)

Внимание
Настоятельно рекомендуется устанавливать расширения трансформации в одну схему с ltree. Если выбрать какую-либо другую схему, которая может содержать объекты, определенные злонамеренным пользователем, то при установке расширения возникнут угрозы безопасности.

Авторы

Разработку осуществили Федор Сигаев (teodor@stack.net) и Олег Бартунов (oleg@sai.msu.su). Дополнительную информацию см. на странице http://www.sai.msu.su/~megera/postgres/gist/. Авторы выражают благодарность Евгению Родичеву за полезные дискуссии. Комментарии и сообщения об ошибках приветствуются.

old_snapshot

Модуль old_snapshot позволяет ознакомиться с состоянием сервера, которое используется в реализации old_snapshot_threshold.

Функции

pg_old_snapshot_time_mapping(array_offset OUT int4, end_timestamp OUT timestamptz, newest_xmin OUT xid) returns setof record

Возвращает все записи из серверного массива сопоставлений меток времени и XID. Каждая запись представляет последний xmin снимка, сделанного в соответствующую минуту.

pageinspect

Модуль pageinspect предоставляет функции, позволяющие просматривать содержимое страниц базы данных на низком уровне, что полезно для отладки. Все эти функции могут применять только суперпользователи.

Функции общего назначения

get_raw_page(relname text, fork text, blkno bigint) returns bytea

Функция get_raw_page читает заданный блок именованного отношения и возвращает его копию в виде значения bytea. Это позволяет получить одну согласованную во времени копию блока. В параметре fork нужно передать 'main', чтобы обратиться к основной ветви данных, 'fsm' — к карте свободного пространства, 'vm' — к карте видимости, или 'init' — к ветви инициализации.

get_raw_page(relname text, blkno bigint) returns bytea

Упрощенная версия функции get_raw_page для чтения данных из основной ветви. Равнозначна get_raw_page(relname, 'main', blkno)

page_header(page bytea) returns record

Функция page_header показывает поля, общие для всех страниц кучи и индекса QHB.

В качестве аргумента ей следует передавать образ страницы, полученный с помощью get_raw_page. Например:

test=# SELECT * FROM page_header(get_raw_page('pg_class', 0));
    lsn    | checksum | flags  | lower | upper | special | pagesize | version | prune_xid
-----------+----------+--------+-------+-------+---------+----------+---------+-----------
 0/24A1B50 |        0 |      1 |   232 |   368 |    8192 |     8192 |       4 |         0

Возвращаемые столбцы соответствуют полям в структуре PageHeaderData. Подробную информацию см. в src/include/storage/bufpage.h.

Поле checksum содержит контрольную сумму, сохраненную в странице. Эта сумма может быть неверной, если страница каким-то образом повреждена. Если в данном экземпляре СУБД контрольные суммы выключены, сохраненное значение не имеет смысла.

page_checksum(page bytea, blkno bigint) returns smallint

Функция page_checksum вычисляет контрольную сумму для страницы так, будто эта страница находилась в заданном блоке.

В качестве аргумента ей следует передавать образ страницы, полученный с помощью get_raw_page. Например:

test=# SELECT page_checksum(get_raw_page('pg_class', 0), 0);
 page_checksum
---------------
         13443

Обратите внимание, что вычисление контрольной суммы зависит от номера блока, поэтому обеим функциям следует передавать одинаковые номера блоков (за исключением случаев эзотерической отладки).

Контрольные суммы, вычисленные этой функцией, можно сравнить с результирующим полем checksum функции page_header. Если в данном экземпляре СУБД контрольные суммы включены, эти значения должны быть равны.

fsm_page_contents(page bytea) returns text

Функция fsm_page_contents показывает внутреннюю структуру узла на странице FSM. Например:

test=# SELECT fsm_page_contents(get_raw_page('pg_class', 'fsm', 0));

Вывод состоит из нескольких текстовых строк, по одной строке для каждого узла двоичного дерева на заданной странице. Выводятся только ненулевые узлы. Также выводится так называемый указатель «вперед», которые указывает на следующий слот, возвращаемый с этой страницы.

Более подробную информацию о структуре страницы FSM см. в src/backend/storage/freespace/README.

Функции для исследования кучи

heap_page_items(page bytea) returns setof record

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

В качестве аргумента ей следует передавать образ страницы кучи, полученный с помощью get_raw_page. Например:

test=# SELECT * FROM heap_page_items(get_raw_page('pg_class', 0));

Описание возвращаемых полей см. в src/include/storage/itemid.h и src/include/access/htup_details.h.

Распаковать биты флагов t_infomask and t_infomask2 для кортежей кучи можно с помощью функции heap_tuple_infomask_flags.

tuple_data_split(rel_oid oid, t_data bytea, t_infomask integer, t_infomask2 integer, t_bits text [, do_detoast bool]) returns bytea[]

Функция tuple_data_split разделяет данные кортежа на атрибуты так же, как это происходит внутри сервера.

test=# SELECT tuple_data_split('pg_class'::regclass, t_data, t_infomask, t_infomask2, t_bits) FROM heap_page_items(get_raw_page('pg_class', 0));

Вызывая эту функцию, ей следует передавать в аргументах те атрибуты, которые возвращает функция heap_page_items.

Если параметр do_detoast равен true, атрибуты будут распаковываться по мере необходимости. Значение по умолчанию — false.

heap_page_item_attrs(page bytea, rel_oid regclass [, do_detoast bool]) returns setof record

Функция heap_page_item_attrs равнозначна функции heap_page_items, за исключением того, что она возвращает исходные данные кортежа в виде массива атрибутов, которые при необходимости могут быть распакованы, если установлен флаг do_detoast (по умолчанию false, т. е. они не распаковываются).

В качестве аргумента ей следует передавать образ страницы кучи, полученный с помощью get_raw_page. Например:

test=# SELECT * FROM heap_page_item_attrs(get_raw_page('pg_class', 0), 'pg_class'::regclass);

heap_tuple_infomask_flags(t_infomask integer, t_infomask2 integer) returns record

Функция heap_tuple_infomask_flags декодирует значения t_infomask и t_infomask2, возвращаемые функцией heap_page_items, в множество удобочитаемых массивов с именами флагов, где в одном столбце перечислены все флаги по отдельности, а в другом — комбинированные флаги. Например:

test=# SELECT t_ctid, raw_flags, combined_flags
         FROM heap_page_items(get_raw_page('pg_class', 0)),
           LATERAL heap_tuple_infomask_flags(t_infomask, t_infomask2)
         WHERE t_infomask IS NOT NULL OR t_infomask2 IS NOT NULL;

Вызывая эту функцию, ей следует передавать в аргументах те атрибуты, которые возвращает функция heap_page_items.

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

Описания возвращаемых имен флагов см. в src/include/access/htup_details.h.

Функции для индексов B-деревьев

bt_metap(relname text) returns record

Функция bt_metap возвращает информацию о метастранице индекса B-дерева. Например:

test=# SELECT * FROM bt_metap('pg_cast_oid_index');
-[ RECORD 1 ]-------------+-------
magic                     | 340322
version                   | 4
root                      | 1
level                     | 0
fastroot                  | 1
fastlevel                 | 0
last_cleanup_num_delpages | 0
last_cleanup_num_tuples   | 230
allequalimage             | f

bt_page_stats(relname text, blkno bigint) returns record

Функция bt_page_stats возвращает сводную информацию по единичным страницам индексов B-деревьев. Например:

test=# SELECT * FROM bt_page_stats('pg_cast_oid_index', 1);
-[ RECORD 1 ]-+-----
blkno         | 1
type          | l
live_items    | 224
dead_items    | 0
avg_item_size | 16
page_size     | 8192
free_size     | 3668
btpo_prev     | 0
btpo_next     | 0
btpo_level    | 0
btpo_flags    | 3

bt_page_items(relname text, blkno bigint) returns setof record

Функция bt_page_items возвращает подробную информацию обо всех элементах на странице индекса B-дерева. Например:

test=# SELECT itemoffset, ctid, itemlen, nulls, vars, data, dead, htid, tids[0:2] AS some_tids
        FROM bt_page_items('tenk2_hundred', 5);
 itemoffset |   ctid    | itemlen | nulls | vars |          data           | dead |  htid  |      some_tids
------------+-----------+---------+-------+------+-------------------------+------+--------+---------------------
          1 | (16,1)    |      16 | f     | f    | 30 00 00 00 00 00 00 00 |      |        |
          2 | (16,8292) |     616 | f     | f    | 24 00 00 00 00 00 00 00 | f    | (1,6)  | {"(1,6)","(10,22)"}
          3 | (16,8292) |     616 | f     | f    | 25 00 00 00 00 00 00 00 | f    | (1,18) | {"(1,18)","(4,22)"}
          4 | (16,8292) |     616 | f     | f    | 26 00 00 00 00 00 00 00 | f    | (4,18) | {"(4,18)","(6,17)"}
          5 | (16,8292) |     616 | f     | f    | 27 00 00 00 00 00 00 00 | f    | (1,2)  | {"(1,2)","(1,19)"}
          6 | (16,8292) |     616 | f     | f    | 28 00 00 00 00 00 00 00 | f    | (2,24) | {"(2,24)","(4,11)"}
          7 | (16,8292) |     616 | f     | f    | 29 00 00 00 00 00 00 00 | f    | (2,17) | {"(2,17)","(11,2)"}
          8 | (16,8292) |     616 | f     | f    | 2a 00 00 00 00 00 00 00 | f    | (0,25) | {"(0,25)","(3,20)"}
          9 | (16,8292) |     616 | f     | f    | 2b 00 00 00 00 00 00 00 | f    | (0,10) | {"(0,10)","(0,14)"}
         10 | (16,8292) |     616 | f     | f    | 2c 00 00 00 00 00 00 00 | f    | (1,3)  | {"(1,3)","(3,9)"}
         11 | (16,8292) |     616 | f     | f    | 2d 00 00 00 00 00 00 00 | f    | (6,28) | {"(6,28)","(11,1)"}
         12 | (16,8292) |     616 | f     | f    | 2e 00 00 00 00 00 00 00 | f    | (0,27) | {"(0,27)","(1,13)"}
         13 | (16,8292) |     616 | f     | f    | 2f 00 00 00 00 00 00 00 | f    | (4,17) | {"(4,17)","(4,21)"}
(13 rows)

Это листовая страница B-дерева. Все кортежи, указывающие на таблицу, оказались кортежами со списками идентификаторов (в каждом из которых хранится в сумме 100 шестибайтных TID (идентификаторов кортежей)). Кроме того, по смещению itemoffset 1 находится кортеж «верхний ключ». В данном примере ctid используется для хранения закодированной информации о каждом кортеже, хотя на листовой странице кортежи часто содержат в поле ctid непосредственно TID кучи. Поле tids содержит список TID в виде списка идентификаторов.

На внутренней странице (здесь не показана) часть номера блока в ctid является «ссылкой вниз», то есть номером блока другой страницы в самом индексе. Часть смещения (второе число) ctid содержит закодированную информацию о кортеже, в частности, количество столбцов в нем (при отсечении суффикса могут быть удалены ненужные заключительные столбцы). Отсекаемые столбцы воспринимаются как содержащие значение «минус бесконечность».

В поле htid показывается TID кортежа в куче, независимо от его нижележащего представления. Это значение может совпадать со ctid или может быть декодировано из альтернативных представлений, используемых в кортежах со списками идентификаторов и кортежами со внутренних страниц. В кортежах на внутренних страницах столбец с TID кучи на уровне реализации обычно отсекается, что выглядит как значение NULL в столбце htid.

Обратите внимание, что первый элемент в любой, кроме самой правой, странице (т. е. в любой странице с ненулевым значением в поле btpo_next) является «верхним ключом», то есть его поле data служит верхней границей для всех элементов, находящихся на этой странице, а поле ctid не указывает на другой блок. Кроме того, на внутренних страницах в первом действительном элементе с данными (в первом элементе после верхнего ключа) обязательно будут усечены все столбцы, не оставив в поле data фактического значения. Однако такой элемент содержит в своем поле ctid корректную ссылку вниз.

Дополнительную информацию о структуре индексов B-деревьев см. в подразделе Структура В-дерева. Дополнительную информацию об исключении дубликатов и списках идентификаторов см. в подразделе Дедупликация.

bt_page_items(page bytea) returns setof record

Функции bt_page_items также можно передать страницу в виде значения bytea. Вызывая эту функцию, ей следует передавать в аргументе образ страницы, полученный с помощью функции get_raw_page. Так, последний пример можно переписать следующим образом:

test=# SELECT itemoffset, ctid, itemlen, nulls, vars, data, dead, htid, tids[0:2] AS some_tids
        FROM bt_page_items(get_raw_page('tenk2_hundred', 5));
 itemoffset |   ctid    | itemlen | nulls | vars |          data           | dead |  htid  |      some_tids
------------+-----------+---------+-------+------+-------------------------+------+--------+---------------------
          1 | (16,1)    |      16 | f     | f    | 30 00 00 00 00 00 00 00 |      |        |
          2 | (16,8292) |     616 | f     | f    | 24 00 00 00 00 00 00 00 | f    | (1,6)  | {"(1,6)","(10,22)"}
          3 | (16,8292) |     616 | f     | f    | 25 00 00 00 00 00 00 00 | f    | (1,18) | {"(1,18)","(4,22)"}
          4 | (16,8292) |     616 | f     | f    | 26 00 00 00 00 00 00 00 | f    | (4,18) | {"(4,18)","(6,17)"}
          5 | (16,8292) |     616 | f     | f    | 27 00 00 00 00 00 00 00 | f    | (1,2)  | {"(1,2)","(1,19)"}
          6 | (16,8292) |     616 | f     | f    | 28 00 00 00 00 00 00 00 | f    | (2,24) | {"(2,24)","(4,11)"}
          7 | (16,8292) |     616 | f     | f    | 29 00 00 00 00 00 00 00 | f    | (2,17) | {"(2,17)","(11,2)"}
          8 | (16,8292) |     616 | f     | f    | 2a 00 00 00 00 00 00 00 | f    | (0,25) | {"(0,25)","(3,20)"}
          9 | (16,8292) |     616 | f     | f    | 2b 00 00 00 00 00 00 00 | f    | (0,10) | {"(0,10)","(0,14)"}
         10 | (16,8292) |     616 | f     | f    | 2c 00 00 00 00 00 00 00 | f    | (1,3)  | {"(1,3)","(3,9)"}
         11 | (16,8292) |     616 | f     | f    | 2d 00 00 00 00 00 00 00 | f    | (6,28) | {"(6,28)","(11,1)"}
         12 | (16,8292) |     616 | f     | f    | 2e 00 00 00 00 00 00 00 | f    | (0,27) | {"(0,27)","(1,13)"}
         13 | (16,8292) |     616 | f     | f    | 2f 00 00 00 00 00 00 00 | f    | (4,17) | {"(4,17)","(4,21)"}
(13 rows)

В остальном данная функция работает так, как описано выше.

Функции для индексов BRIN

brin_page_type(page bytea) returns text

Функция brin_page_type возвращает тип страницы для заданной страницы индекса BRIN или выдает ошибку, если эта страница не является корректной страницей BRIN. Например:

test=# SELECT brin_page_type(get_raw_page('brinidx', 0));
 brin_page_type
----------------
 meta

brin_metapage_info(page bytea) returns record

Функция brin_metapage_info возвращает разнообразную информацию о метастранице индекса BRIN. Например:

test=# SELECT * FROM brin_metapage_info(get_raw_page('brinidx', 0));
   magic    | version | pagesperrange | lastrevmappage
------------+---------+---------------+----------------
 0xA8109CFA |       1 |             4 |              2

brin_revmap_data(page bytea) returns setof tid

Функция brin_revmap_data возвращает список идентификаторов кортежей со страницы сопоставлений диапазонов индекса BRIN. Например:

test=# SELECT * FROM brin_revmap_data(get_raw_page('brinidx', 2)) LIMIT 5;
  pages
---------
 (6,137)
 (6,138)
 (6,139)
 (6,140)
 (6,141)

brin_page_items(page bytea, index oid) returns setof record

Функция brin_page_items возвращает данные, хранящиеся на странице данных BRIN. Например:

test=# SELECT * FROM brin_page_items(get_raw_page('brinidx', 5),
                                     'brinidx')
       ORDER BY blknum, attnum LIMIT 6;
 itemoffset | blknum | attnum | allnulls | hasnulls | placeholder |    value
------------+--------+--------+----------+----------+-------------+--------------
        137 |      0 |      1 | t        | f        | f           |
        137 |      0 |      2 | f        | f        | f           | {1 .. 88}
        138 |      4 |      1 | t        | f        | f           |
        138 |      4 |      2 | f        | f        | f           | {89 .. 176}
        139 |      8 |      1 | t        | f        | f           |
        139 |      8 |      2 | f        | f        | f           | {177 .. 264}

Возвращаемые столбцы соответствуют полям в структурах BrinMemTuple и BrinValues. Подробную информацию см. в src/include/access/brin_tuple.h.

Функции для индексов GIN

gin_metapage_info(page bytea) returns record

Функция gin_metapage_info возвращает информацию о метастранице индекса GIN. Например:

test=# SELECT * FROM gin_metapage_info(get_raw_page('gin_index', 0));
-[ RECORD 1 ]----+-----------
pending_head     | 4294967295
pending_tail     | 4294967295
tail_free_size   | 0
n_pending_pages  | 0
n_pending_tuples | 0
n_total_pages    | 7
n_entry_pages    | 6
n_data_pages     | 0
n_entries        | 693
version          | 2

gin_page_opaque_info(page bytea) returns record

Функция gin_page_opaque_info возвращает информацию о непрозрачной области индекса GIN, например, тип страницы. Например:

test=# SELECT * FROM gin_page_opaque_info(get_raw_page('gin_index', 2));
 rightlink | maxoff |         flags
-----------+--------+------------------------
         5 |      0 | {data,leaf,compressed}
(1 row)

gin_leafpage_items(page bytea) returns setof record

Функция gin_leafpage_items возвращает информацию о данных, хранящихся на листовой странице GIN. Например:

test=# SELECT first_tid, nbytes, tids[0:5] AS some_tids
        FROM gin_leafpage_items(get_raw_page('gin_test_idx', 2));
 first_tid | nbytes |                        some_tids
-----------+--------+----------------------------------------------------------
 (8,41)    |    244 | {"(8,41)","(8,43)","(8,44)","(8,45)","(8,46)"}
 (10,45)   |    248 | {"(10,45)","(10,46)","(10,47)","(10,48)","(10,49)"}
 (12,52)   |    248 | {"(12,52)","(12,53)","(12,54)","(12,55)","(12,56)"}
 (14,59)   |    320 | {"(14,59)","(14,60)","(14,61)","(14,62)","(14,63)"}
 (167,16)  |    376 | {"(167,16)","(167,17)","(167,18)","(167,19)","(167,20)"}
 (170,30)  |    376 | {"(170,30)","(170,31)","(170,32)","(170,33)","(170,34)"}
 (173,44)  |    197 | {"(173,44)","(173,45)","(173,46)","(173,47)","(173,48)"}
(7 rows)

Функции для индексов GiST

gist_page_opaque_info(page bytea) returns record

Функция gist_page_opaque_info возвращает информацию из непрозрачной области страницы индекса GiST, например, значения NSN, rightlink и тип страницы. Например:

test=# SELECT * FROM gist_page_opaque_info(get_raw_page('test_gist_idx', 2));
 lsn | nsn | rightlink | flags
-----+-----+-----------+--------
 0/1 | 0/0 |         1 | {leaf}
(1 row)

gist_page_items(page bytea, index_oid regclass) returns setof record

Функция gist_page_items возвращает информацию о данных, хранящихся на странице индекса GiST. Например:

test=# SELECT * FROM gist_page_items(get_raw_page('test_gist_idx', 0), 'test_gist_idx');
 itemoffset |   ctid    | itemlen | dead |       keys
------------+-----------+---------+------+-------------------
          1 | (1,65535) |      40 | f    | (p)=((166,166))
          2 | (2,65535) |      40 | f    | (p)=((332,332))
          3 | (3,65535) |      40 | f    | (p)=((498,498))
          4 | (4,65535) |      40 | f    | (p)=((664,664))
          5 | (5,65535) |      40 | f    | (p)=((830,830))
          6 | (6,65535) |      40 | f    | (p)=((996,996))
          7 | (7,65535) |      40 | f    | (p)=((1000,1000))
(7 rows)

gist_page_items_bytea(page bytea) returns setof record

Функция схожа с gist_page_items, но возвращает данные ключа в виде исходного двоичного объекта bytea. Так как она не пытается декодировать ключ, ей не требуется знать, какой индекс задействован. Например:

test=# SELECT * FROM gist_page_items_bytea(get_raw_page('test_gist_idx', 0));
 itemoffset |   ctid    | itemlen | dead |                                      key_data
------------+-----------+---------+------+-----------------------------------------​-------------------------------------------
          1 | (1,65535) |      40 | f    | \x00000100ffff28000000000000c0644000000000​00c06440000000000000f03f000000000000f03f
          2 | (2,65535) |      40 | f    | \x00000200ffff28000000000000c0744000000000​00c074400000000000e064400000000000e06440
          3 | (3,65535) |      40 | f    | \x00000300ffff28000000000000207f4000000000​00207f400000000000d074400000000000d07440
          4 | (4,65535) |      40 | f    | \x00000400ffff28000000000000c0844000000000​00c084400000000000307f400000000000307f40
          5 | (5,65535) |      40 | f    | \x00000500ffff28000000000000f0894000000000​00f089400000000000c884400000000000c88440
          6 | (6,65535) |      40 | f    | \x00000600ffff28000000000000208f4000000000​00208f400000000000f889400000000000f88940
          7 | (7,65535) |      40 | f    | \x00000700ffff28000000000000408f4000000000​00408f400000000000288f400000000000288f40
(7 rows)

Функции для хеш-индексов

hash_page_type(page bytea) returns text

Функция hash_page_type возвращает тип страницы для заданной страницы хеш-индекса. Например:

test=# SELECT hash_page_type(get_raw_page('con_hash_index', 0));
 hash_page_type
----------------
 metapage

hash_page_stats(page bytea) returns setof record

Функция hash_page_stats возвращает информацию о странице ячейки или переполнения хеш-индекса. Например:

test=# SELECT * FROM hash_page_stats(get_raw_page('con_hash_index', 1));
-[ RECORD 1 ]---+-----------
live_items      | 407
dead_items      | 0
page_size       | 8192
free_size       | 8
hasho_prevblkno | 4096
hasho_nextblkno | 8474
hasho_bucket    | 0
hasho_flag      | 66
hasho_page_id   | 65408

hash_page_items(page bytea) returns setof record

Функция hash_page_items возвращает информацию о данных, хранящихся на странице ячейки или переполнения хеш-индекса. Например:

test=# SELECT * FROM hash_page_items(get_raw_page('con_hash_index', 1)) LIMIT 5;
 itemoffset |   ctid    |    data
------------+-----------+------------
          1 | (899,77)  | 1053474816
          2 | (897,29)  | 1053474816
          3 | (894,207) | 1053474816
          4 | (892,159) | 1053474816
          5 | (890,111) | 1053474816

hash_bitmap_info(index oid, blkno bigint) returns record

Функция hash_bitmap_info показывает состояние бита в странице битовой карты для определенной страницы переполнения хеш-индекса. Например:

test=# SELECT * FROM hash_bitmap_info('con_hash_index', 2052);
 bitmapblkno | bitmapbit | bitstatus
-------------+-----------+-----------
          65 |         3 | t

hash_metapage_info(page bytea) returns record

Функция hash_metapage_info возвращает информацию, хранящуюся в метастранице хеш-индекса. Например:

test=# SELECT magic, version, ntuples, ffactor, bsize, bmsize, bmshift,
test-#     maxbucket, highmask, lowmask, ovflpoint, firstfree, nmaps, procid,
test-#     regexp_replace(spares::text, '(,0)*}', '}') as spares,
test-#     regexp_replace(mapp::text, '(,0)*}', '}') as mapp
test-# FROM hash_metapage_info(get_raw_page('con_hash_index', 0));
-[ RECORD 1 ]-------------------------------------------------​------------------------------
magic     | 105121344
version   | 4
ntuples   | 500500
ffactor   | 40
bsize     | 8152
bmsize    | 4096
bmshift   | 15
maxbucket | 12512
highmask  | 16383
lowmask   | 8191
ovflpoint | 28
firstfree | 1204
nmaps     | 1
procid    | 450
spares    | {0,0,0,0,0,0,1,1,1,1,1,1,1,1,3,4,4,4,45,55,58,59,​508,567,628,704,1193,1202,1204}
mapp      | {65}

passwordcheck

Модуль passwordcheck проверяет пароли пользователей, задаваемые командами CREATE ROLE или ALTER ROLE. Если пароль признается слишком слабым, он не принимается, и команда завершается ошибкой.

Чтобы загрузить этот модуль, добавьте строку '$libdir/passwordcheck' в переменную shared_preload_libraries в qhb.conf, а затем перезапустите сервер.

Вы можете приспособить этот модуль к своим нуждам, изменив исходный код. Например, для проверки паролей можно использовать библиотеку CrackLib — для этого нужно только раскомментировать две строки в Makefile и пересобрать модуль. (Мы не можем включить CrackLib по умолчанию из-за лицензии.) Без CrackLib этот модуль проверяет надежность пароля по простым правилам, которые вы можете модифицировать или расширить по своему усмотрению.

Внимание!
Чтобы незашифрованные пароли не передавались по сети, не записывались в журнал сервера и не стали каким-либо иным образом известны администратору базы данных, QHB позволяет пользователю передавать предварительно зашифрованные пароли. Многие клиентские программы могут воспользоваться этой функциональной возможностью и шифровать пароль, прежде чем передавать его серверу.
Это ограничивает полезность модуля passwordcheck, так как в этом случае он может лишь попытаться угадать пароль. По этой причине не рекомендуется использовать passwordcheck, если требуется высокий уровень безопасности. Более безопасно будет применить внешний метод проверки подлинности, например GSSAPI, чем полагаться на пароли в базе данных.
Как вариант, можно модифицировать passwordcheck, чтобы предварительно зашифрованные пароли не принимались, но если вынудить пользователей задавать пароли открытым текстом, это повлечет свои риски безопасности.

pg_buffercache

Модуль pg_buffercache предоставляет возможность проверить, что происходит в общем кэше буферов в реальном времени.

Этот модуль предоставляет функцию на C pg_buffercache_pages, возвращающую набор записей плюс представление pg_buffercache, которое является удобной оберткой этой функции.

По умолчанию его использование разрешено только суперпользователям и членам роли pg_monitor. Дать доступ другим можно с помощью команды GRANT.

Представление pg_buffercache

Определения столбцов, содержащихся в представлении, показаны в Таблице 15.

Таблица 15. Столбцы pg_buffercache

Тип столбца | Описание
bufferid integer | ID, в диапазоне 1..shared_buffers
relfilenode oid (ссылается на pg_class.relfilenode) | Номер файлового узла для отношения
reltablespace oid (ссылается на pg_tablespace.oid) | OID табличного пространства, содержащего отношение
reldatabase oid (ссылается на pg_database.oid) | OID базы данных, содержащей отношение
relforknumber smallint | Номер ветви в отношении; см. common/relpath.h
relblocknumber bigint | Номер страницы в отношении
isdirty boolean | Страница загрязнена?
usagecount smallint | Счетчик обращений по часовой стрелке
pinning_backends integer | Количество обслуживающих процессов, закрепивших этот буфер

Для каждого буфера в общем кэше выдается одна строка. Для неиспользуемых буферов все поля равны NULL, за исключением bufferid. Общие системные каталоги показываются как относящиеся к базе данных номер ноль.

Поскольку кэш используется совместно всеми базами данных, обычно в нем находятся и страницы отношений, не принадлежащих текущей базе данных. Это означает, что для некоторых строк при соединении с pg_class не будет соответствующих строк, или даже могут образоваться некорректные соединения. Если вы пытаетесь выполнить соединение с pg_class, имеет смысл ограничить соединение строками, в которых reldatabase содержит OID текущей базы данных или ноль.

Поскольку для копирования данных состояния буфера, которые будет отображать представление, блокировки в менеджере буферов не устанавливаются, обращения к представлению pg_buffercache оказывает меньшее влияние на нормальную работу буферов, но при этом не обеспечивает согласованный набор результатов для всех буферов. Однако внутри каждого отдельного буфера согласованность информации гарантируется.

Пример вывода

regression=# SELECT n.nspname, c.relname, count(*) AS buffers
             FROM pg_buffercache b JOIN pg_class c
             ON b.relfilenode = pg_relation_filenode(c.oid) AND
                b.reldatabase IN (0, (SELECT oid FROM pg_database
                                      WHERE datname = current_database()))
             JOIN pg_namespace n ON n.oid = c.relnamespace
             GROUP BY n.nspname, c.relname
             ORDER BY 3 DESC
             LIMIT 10;

  nspname   |        relname         | buffers
------------+------------------------+---------
 public     | delete_test_table      |     593
 public     | delete_test_table_pkey |     494
 pg_catalog | pg_attribute           |     472
 public     | quad_poly_tbl          |     353
 public     | tenk2                  |     349
 public     | tenk1                  |     349
 public     | gin_test_idx           |     306
 pg_catalog | pg_largeobject         |     206
 public     | gin_test_tbl           |     188
 public     | spgist_text_tbl        |     182
(10 rows)

Авторы

Марк Кирквуд (markir@paradise.net.nz)

Предложения по дизайну: Нейл Конвей (neilc@samurai.com)

Советы по отладке: Том Лейн (tgl@sss.pgh.pa.us)

pgcrypto

Модуль pgcrypto предоставляет криптографические функции для QHB.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Стандартные функции хеширования

digest()

digest(data text, type text) returns bytea
digest(data bytea, type text) returns bytea

Вычисляет двойной хеш указанных данных (параметр data). Параметр type выбирает используемый алгоритм. Стандартные алгоритмы: md5, sha1, sha224, sha256, sha384 и sha512. Если модуль pgcrypto собирался с OpenSSL, будут доступны и другие алгоритмы, как описано в Таблице 19.

Если вам нужен дайджест в виде шестнадцатеричной строки, примените к результату encode(). Например:

CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS $$
    SELECT encode(digest($1, 'sha1'), 'hex')
$$ LANGUAGE SQL STRICT IMMUTABLE;

hmac()

hmac(data text, key text, type text) returns bytea
hmac(data bytea, key bytea, type text) returns bytea

Вычисляет код аутентификации сообщения на основе хеша для данных data с ключом key. Параметр type имеет то же значение, что и для digest().

Эта функция похожа на digest(), но с ней хеш можно перерассчитать, только зная ключ. Это предотвращает сценарий подмены данных и изменения хеша (для соответствия).

Если размер ключа больше размера блока хеша, он сначала хешируется, а уже результат используется в качестве ключа.

Функции хеширования пароля

Функции crypt() и gen_salt() разработаны специально для хеширования паролей. Функция crypt() выполняет хеширование, а gen_salt() подготавливает параметры алгоритма для нее.

Алгоритмы в crypt() отличаются от обычных алгоритмов хеширования MD5 или SHA1 в следующих аспектах:

Они медленные. Поскольку объем данных очень мал, это единственный способ усложнить перебор паролей.

Они используют случайное значение, называемое солью, чтобы у пользователей с одинаковыми паролями зашифрованные пароли были разными. Это также является дополнительной защитой от инвертирования алгоритма.

Они включают в результат тип алгоритма, вследствие чего могут сосуществовать пароли, хешированные разными алгоритмами.

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

В Таблице 16 перечислены алгоритмы, поддерживаемые функцией crypt().

Таблица 16. Алгоритмы, поддерживаемые crypt()

Алгоритм | Макс. длина пароля | Адаптивный? | Размер соли (бит) | Длина вывода | Описание
bf | 72 | да | 128 | 60 | На основе Blowfish, вариант 2a
md5 | без ограничений | нет | 48 | 34 | crypt на основе MD5
xdes | 8 | да | 24 | 20 | Расширенный DES
des | 8 | нет | 12 | 13 | Оригинальный crypt из UNIX

crypt()

crypt(password text, salt text) returns text

Вычисляет хеш пароля (password) в стиле crypt(3). При сохранении нового пароля следует вызвать gen_salt(), чтобы сгенерировать новое значение соли. Для проверки пароля нужно передать сохраненное значение хеша в параметре salt и проверить, соответствует ли результат сохраненному значению.

Пример установки нового пароля:

UPDATE ... SET pswhash = crypt('new password', gen_salt('md5'));

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

SELECT (pswhash = crypt('entered password', pswhash)) AS pswmatch FROM ... ;

Этот запрос возвращает true, если введенный пароль верен.

gen_salt()

gen_salt(type text [, iter_count integer ]) returns text

Генерирует новую случайную строку соли для использования в crypt(). Строка соли также сообщает crypt(), какой алгоритм применять.

Параметр type задает алгоритм хеширования. Допустимые типы: des, xdes, md5 и bf.

Параметр iter_count позволяет пользователю задать счетчик итераций для алгоритма, у которого он есть. Чем выше это число, тем больше времени уйдет на хеширование пароля и, как следствие, тем больше времени понадобится, чтобы его взломать. Хотя при слишком большим значением время вычисления хеша может вырасти до нескольких лет — что довольно непрактично. Если параметр iter_count опущен, используется количество итераций по умолчанию. Допустимые значения для iter_count зависят от алгоритма, как показано в Таблице 17.

Таблица 17. Счетчики итераций для crypt()

Алгоритм | По умолчанию | Минимум | Максимум
xdes | 725 | 1 | 16777215
bf | 6 | 4 | 31

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

При выборе подходящего количества итераций учтите, что оригинальный алгоритм DES crypt был разработан так, чтобы выдавать 4 хеша в секунду на оборудовании того времени. Скорость менее 4 хешей в секунду, скорее всего, снизит удобство применения. А скорость больше, чем 100 хешей в секунду, вероятно, будет слишком высокой.

В Таблице 18 дан обзор относительной скорости различных алгоритмов хеширования. В таблице показано, сколько времени займет попытка перебрать все комбинации символов в 8-символьном пароле, в предположении, что пароль содержит только строчные буквы, либо строчные и прописные буквы, а также цифры. В записях crypt-bf числа после косой черты представляют собой значение параметра iter_count функции gen_salt.

Таблица 18. Скорости алгоритмов хеширования

Алгоритм | Хешей/сек. | Для [a-z] | Для [A-Za-z0-9] | Длительность относительно хеша md5
crypt-bf/8 | 1792 | 4 года | 3927 лет | 100 000
crypt-bf/7 | 3648 | 2 года | 1929 лет | 50 000
crypt-bf/6 | 7168 | 1 год | 982 года | 25 000
crypt-bf/5 | 13504 | 188 дней | 521 год | 12 500
crypt-md5 | 171584 | 15 дней | 41 год | 1000
crypt-des | 23221568 | 157,5 минут | 108 дней | 7
sha1 | 37774272 | 90 минут | 68 дней | 4
md5 (хеш) | 150085504 | 22,5 минуты | 17 дней | 1

Примечания:

Для расчетов использовался процессор Intel Mobile Core i3.

Показатели алгоритмов crypt-des и crypt-md5 взяты из вывода теста программы John the Ripper v1.6.38.

Показатели хеша md5 получены программой mdcrack 1.2.

Показатели sha1 получены программой lcrack-20031130-beta.

Показатели crypt-bf получены при помощи простой программы, обрабатывающей в цикле 1000 8-символьных паролей. Таким способом можно показать скорость с разным количеством итераций. Для справки: john -test показывает 13 506 циклов/с для crypt-bf/5. (Это очень малое различие в результатах согласуется с тем фактом, что реализация crypt-bf в pgcrypto та же, что применяется в программе John the Ripper.)

Обратите внимание, что вариант «перебрать все комбинации» не особо приближен к реальности. Обычно взлом паролей производится с помощью словарей, содержащих как обычные слова, так и их различные видоизменения. Поэтому даже некоторым образом похожие на слова пароли можно подобрать быстрее, чем за вышеуказанное время, тогда как 6-символьный несловесный пароль может избежать взлома. А может и не избежать.

Функции шифрования на основе PGP

Описанные здесь функции реализуют часть стандарта OpenPGP (RFC 4880), касающуюся шифрования. Они поддерживают шифрование как с симметричным, так и с открытым ключом.

Зашифрованное сообщение PGP состоит из 2 частей, или пакетов:

Пакет, содержащий сеансовый ключ — либо симметричный, либо открытый (в зашифрованном виде).

Пакет, содержащий данные, зашифрованные сеансовым ключом.

При шифровании с симметричным ключом (т. е. паролем):

Заданный пароль хешируется по алгоритму String2Key (S2K). Этот алгоритм отчасти схож с алгоритмами crypt() — нарочно замедлен и добавляет случайную соль — но создает двоичный ключ полной длины.

Если требуется отдельный сеансовый ключ, будет сгенерирован новый случайный ключ. В противном случае в качестве сеансового будет использован непосредственно ключ S2K.

Если используется непосредственно ключ S2K, то в пакет сеансового ключа будут помещены только параметры S2K. В противном случае сеансовый ключ будет зашифрован ключом S2K и помещен в пакет сеансового ключа.

При шифровании с открытым ключом:

Генерируется новый случайный ключ.

Он зашифровывается открытым ключом и помещается в пакет сеансового ключа.

В любом случае данные, подлежащие шифрованию, обрабатываются следующим образом:

Необязательная обработка данных: сжатие, перекодировка в UTF-8 и/или преобразование окончаний строк.

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

В конце добавляется хеш SHA1 случайного префикса и данных.

Все это шифруется сеансовым ключом и помещается в пакет данных.

pgp_sym_encrypt()

pgp_sym_encrypt(data text, psw text [, options text ]) returns bytea
pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) returns bytea

Шифрует данные (data) симметричным ключом PGP psw. Параметр options может содержать криптографические параметры, описанные ниже.

Pgp_sym_decrypt()

pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns text
pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea

Дешифрует сообщение, зашифрованное симметричным ключом PGP.

Дешифровать данные bytea функцией pgp_sym_decrypt запрещено. Это сделано для того, чтобы избежать вывода некорректных символьных данных. Дешифровать изначально текстовые данные с помощью pgp_sym_decrypt_bytea разрешается.

Параметр options может содержать криптографические параметры, описанные ниже.

pgp_pub_encrypt()

pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea
pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea

Зашифровывает данные (data) открытым ключом PGP key. Если передать этой функции закрытый ключ, она выдаст ошибку.

Параметр options может содержать криптографические параметры, описанные ниже.

pgp_pub_decrypt()

pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) returns text
pgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, options text ]]) returns bytea

Дешифрует сообщение, зашифрованное открытым ключом. В key следует передавать секретный ключ, соответствующий открытому ключу, использованному при шифровании. Если секретный ключ защищен паролем, этот пароль нужно передать в параметре psw. Если пароля нет, но вы хотите задать криптографические параметры, следует передать пустой пароль.

Дешифровать данные bytea функцией pgp_pub_decrypt запрещено. Это сделано для того, чтобы избежать вывода некорректных символьных данных. Дешифровать изначально текстовые данные с помощью pgp_pub_decrypt_bytea разрешается.

Параметр options может содержать криптографические параметры, описанные ниже.

pgp_key_id()

pgp_key_id(bytea) returns text

Функция pgp_key_id извлекает ID ключа из открытого или секретного ключа PGP. Либо выдает ID ключа, который использовался для шифрования данных, если ей передается зашифрованное сообщение.

Она может возвращать 2 специальных ID ключа:

SYMKEY

Сообщение зашифровано симметричным ключом.

ANYKEY

Сообщение зашифровано открытым ключом, но ID ключа был удален. Это означает, что вам надо будет перепробовать все ваши секретные ключи, чтобы подобрать тот, который дешифрует это сообщение. Сам pgcrypto не создает такие сообщения.

Обратите внимание, что у разных ключей может быть одинаковый ID. Это редкое, но обычное явление. При этом клиентское приложение должно пытаться дешифровать данные с каждым ключом, чтобы найти подходящий — как в случае с ANYKEY.

armor(), dearmor()

armor(data bytea [ , keys text[], values text[] ]) returns text
dearmor(data text) returns bytea

Эти функции преобразуют двоичные данные в/из формата PGP «ASCII-armor», который по сути представляет собой кодировку Base64 с контрольными суммами и дополнительным форматированием.

Если задаются массивы keys и values, для каждой пары ключ/значение в формате Armor добавляется заголовок Armor. Оба массива должны быть одномерными и иметь одинаковую длину. Ключи и значения могут содержать только символы ASCII.

pgp_armor_headers

pgp_armor_headers(data text, key out text, value out text) returns setof record

Функция pgp_armor_headers() извлекает заголовки Armor из данных (data). Возвращаемое значение представляет собой набор строк с двумя столбцами, key (ключ) и value (значение). Если ключи или значения содержат какие-либо символы, отличные от ASCII, они воспринимаются как UTF-8.

Параметры функций PGP

Имена параметрам подобны таковым в GnuPG. Значение параметра следует задавать после знака равенства; разделяются параметры запятыми. Например:

pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')

Все эти параметры, кроме convert-crlf, применяются только к функциям шифрования. Функции дешифрования получают параметры из данных PGP.

Вероятно, самые интересные параметры — это compress-algo и unicode-mode. Остальные должны иметь приемлемые значения по умолчанию.

cipher-algo

Выбирает алгоритм шифрования.

Значения: bf, aes128, aes192, aes256 (только OpenSSL: 3des, cast5)
По умолчанию: aes128
Применим к: pgp_sym_encrypt, pgp_pub_encrypt

compress-algo

Выбирает алгоритм сжатия. Доступен, только если QHB был собран с zlib.

Значения:
0 - без сжатия
1 - сжатие ZIP
2 - сжатие ZLIB (= ZIP плюс метаданные и контрольные суммы блоков)
По умолчанию: 0
Применим к: pgp_sym_encrypt, pgp_pub_encrypt

compress-level

Определяет уровень сжатия. При более высоких уровнях на выходе получается меньший объем, но сам процесс замедляется. Значение 0 выключает сжатие.

Значения: 0, 1-9
По умолчанию: 6
Применим к: pgp_sym_encrypt, pgp_pub_encrypt

convert-crlf

Определяет, преобразовывать ли \n в \r\n при шифровании и \r\n в \n при дешифровании. В RFC 4880 указано, что текстовые данные должны храниться с переводами строк в виде \r\n. Используйте этот параметр, чтобы добиться поведения, полностью соответствующего RFC.

Значения: 0, 1
По умолчанию: 0
Применим к: pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt

disable-mdc

Не защищать данные хешем SHA-1. Единственная веская причина использовать этот параметр — добиться совместимости с древними программами PGP, вышедшими до того, как в RFC 4880 была добавлена защита пакетов с SHA-1. Недавние программы с gnupg.org и pgp.com прекрасно ее поддерживают.

Значения: 0, 1
По умолчанию: 0
Применим к: pgp_sym_encrypt, pgp_pub_encrypt

sess-key

Использовать отдельные сеансовый ключ. Для шифрования с открытым ключом всегда используется отдельный сеансовый ключ; этот параметр предназначен для шифрования с симметричным ключом, которое по умолчанию использует непосредственно ключ S2K.

Значения: 0, 1
По умолчанию: 0
Применим к: pgp_sym_encrypt

s2k-mode

Выбирает режим алгоритма S2K.

Значения:
0 - Без соли. Опасно!
1 - С солью, но с фиксированным числом итераций.
3 - С переменным числом итераций.
По умолчанию: 3
Применим к: pgp_sym_encrypt

s2k-count

Выбирает число итераций для алгоритма S2K. Значение должно быть от 1024 до 65011712 включительно.

По умолчанию: Случайное значение от 65536 до 253952
Применим к: pgp_sym_encrypt, только с s2k-mode=3

s2k-digest-algo

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

Значения: md5, sha1
По умолчанию: sha1
Применим к: pgp_sym_encrypt

s2k-cipher-algo

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

Значения: bf, aes, aes128, aes192, aes256
По умолчанию: используется cipher-algo
Применим к: pgp_sym_encrypt

unicode-mode

Определяет, преобразовывать ли текстовые данные из внутренней кодировки базы данных в UTF-8 и обратно. Если кодировка базы уже UTF-8, преобразование не производится, но сообщение будет помечено как UTF-8. Без этого параметра этого не произойдет.

Значения: 0, 1
По умолчанию: 0
Применим к: pgp_sym_encrypt, pgp_pub_encrypt

Генерирование ключей PGP с помощью GnuPG

Генерирование нового ключа:

gpg --gen-key

Предпочитаемый тип ключей — «DSA and Elgamal».

Для шифрования RSA необходимо создать в качестве главного либо DSA, либо RSA ключ только для подписания, а затем добавить подключ для шифрования RSA, выполнив команду gpg --edit-key.

Вывод списка ключей:

gpg --list-secret-keys

Экспорт открытого ключа в формате ASCII Armor:

gpg -a --export KEYID > public.key

Экспорт секретного ключа в формате ASCII Armor:

gpg -a --export-secret-keys KEYID > secret.key

Прежде чем передать эти ключи функциям PGP, необходимо применить к ним функцию dearmor(). Либо, если есть возможность обработать двоичные данные, можно убрать -a из этой команды.

Дополнительную информацию см. в руководстве man gpg, The GNU Privacy Handbook (Руководство GNU по обеспечению конфиденциальности) и в другой документации на сайте https://www.gnupg.org/.

Ограничения кода PGP

Нет поддержки подписывания. Это также означает, что принадлежность подключа шифрования главному ключу не проверяется.

Нет поддержки использования ключа шифрования в качестве главного ключа. Поскольку такая практика в целом не приветствуется, это не должно быть проблемой.

Нет поддержки нескольких подключей. Это может показаться проблемой, так как подобная практика распространена. С другой стороны, не стоит использовать обычные ключи GPG/PGP с pgcrypto — следует создавать новые, поскольку их сценарий использования отличается.

Низкоуровневые функции шифрования

Эти функции выполняют только шифрование данных; у них нет расширенных возможностей шифрования PGP. Поэтому с ними связано несколько существенных проблем:

Они используют ключ пользователя непосредственно в качестве ключа шифрования.

Они не обеспечивают проверку целостности для выявления изменений в зашифрованных данных.

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

Они не обрабатывают текст.

Поэтому с введением шифрования PGP использование низкоуровневых функций шифрования не приветствуется.

encrypt(data bytea, key bytea, type text) returns bytea
decrypt(data bytea, key bytea, type text) returns bytea

encrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
decrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea

Шифрование/дешифрование данных, применяя метод шифрования, заданный параметром type. Синтаксис строки type:

алгоритм [ - режим ] [ /pad: дозаполнение ]

где допустимый алгоритм:

bf — Blowfish

aes — AES (Rijndael-128, -192 or -256)

допустимый режим:

cbc — следующий блок зависит от предыдущего (по умолчанию)

ecb — каждый блок шифруется отдельно (только для тестирования)

и допустимое дозаполнение:

pkcs — данные могут быть любой длины (по умолчанию)

none — размер данных должен быть кратным размеру блока шифра

Так что, например, эти строки равнозначны:

encrypt(data, 'fooz', 'bf')
encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')

Для функций encrypt_iv и decrypt_iv параметр iv является начальным значением для режима CBC; для ECB он игнорируется. Если размер значения не равен ровно размеру блока, оно обрезается или дополняется нулями. В функциях без этого параметра оно по умолчанию заполняется нулями.

Функции получения случайных данных

gen_random_bytes(count integer) returns bytea

Возвращает заданное количество (count) криптографически стойких случайных байтов. За один вызов можно извлечь не более 1024 байтов. Это ограничение предотвращает исчерпание пула генератора случайных чисел.

gen_random_uuid() returns uuid

Возвращает UUID версии 4 (случайный). (В данном модуле эту функцию можно счесть устаревшей, поскольку теперь такая функция включена в ядро QHB.)

Примечания

Конфигурирование

Модуль pgcrypto настраивается в соответствии с данными, полученными в главном скрипте configure QHB. На его конфигурацию влияют аргументы --with-zlib и --with-ssl=openssl.

При компиляции с zlib шифрующие функции PGP способны сжимать данные перед шифрованием.

При компиляции с OpenSSL будут доступны дополнительные алгоритмы. Кроме того, функции шифрования с открытым ключом будут работать быстрее, поскольку OpenSSL содержит более оптимизированные функции для работы с большими числами (BIGNUM).

Таблица 19. Обзор функциональности с OpenSSL и без него

Функциональность | Встроенная | С OpenSSL
MD5 | да | да
SHA1 | да | да
SHA224/256/384/512 | да | да
Другие алгоритмы выборки сообщений | нет | да (Примечание 1)
Blowfish | да | да
AES | да | да
DES/3DES/CAST5 | нет | да
Низкоуровневое шифрование | да | да
Симметричное шифрование PGP | да | да
Шифрование PGP с открытым ключом | да | да

Чтобы использовать более старые шифры, такие как DES или Blowfish, при компиляции с OpenSSL версии 3.0.0 и новее, нужно активировать поставщика ранней версии в файле конфигурации openssl.cnf.

Примечание:

Автоматически выбирается любой алгоритм выборки сообщений, который поддерживает OpenSSL. Это невозможно с шифрами, которые должны поддерживаться явно.

Обработка NULL

В соответствии со стандартом SQL, все функции возвращают NULL, если любой из аргументов — NULL. Это может угрожать безопасности при небрежном применении.

Ограничения безопасности

Все функции pgcrypto выполняются внутри сервера базы данных. Это означает, что все данные и пароли передаются между pgcrypto и клиентскими приложениями открытым текстом. Поэтому вы должны:

Подключаться локально или использовать подключения SSL.

Доверять и системе, и администратору баз данных.

Если это невозможно, лучше провести шифрование внутри клиентского приложения.

Эта реализация не противостоит атакам по сторонним каналам. Например, время, требующееся для выполнения функции дешифрования pgcrypto, будет разным для разного шифротекста заданного размера.

Полезное чтение

https://www.gnupg.org/gph/en/manual.html

The GNU Privacy Handbook (Руководство GNU по обеспечению конфиденциальности).

https://www.openwall.com/crypt/

Описывает алгоритм crypt-blowfish.

https://www.iusmentis.com/security/passphrasefaq/

Как выбрать хороший пароль.

https://theworld.com/~reinhold/diceware.html

Интересный способ выбора паролей.

http://www.interhack.net/people/cmcurtin/snake-oil-faq.html

Описывает хорошую и плохую криптографию.

Техническая справка

https://tools.ietf.org/html/rfc4880

Формат сообщений OpenPGP.

https://tools.ietf.org/html/rfc1321

Алгоритм выборки сообщений MD5.

https://tools.ietf.org/html/rfc2104

HMAC: Хеширование по ключу для аутентификации сообщений.

https://www.usenix.org/legacy/events/usenix99/provos.html

Сравнение алгоритмов crypt-des, crypt-md5 и bcrypt.

https://en.wikipedia.org/wiki/Fortuna_(PRNG)

Описание Fortuna CSPRNG.

https://jlcooke.ca/random/

Драйвер /dev/random для Linux на базе Fortuna, написанный Жан-Люком Куком.

Автор

Марко Крин (markokr@gmail.com)

Модуль pgcrypto использует код из следующих источников:

Алгоритм | Автор | Источник исходного кода
Шифрование DES | Дэвид Вуррен и др. | FreeBSD, libcrypt
Шифрование MD5 | Пол-Хеннинг Камп | FreeBSD, libcrypt
Шифрование Blowfish | Solar Designer | www.openwall.com
Шифр Blowfish | Саймон Тэтем | PuTTY
Шифр Rijndael | Брайан Глэдмен | OpenBSD, sys/crypto
Хеш MD5 и SHA1 | Проект WIDE | KAME, kame/sys/crypto
SHA256/384/512 | Аарон Д. Гиффорд | OpenBSD, sys/crypto
Математика BIGNUM | Майкл Дж. Фромбергер | dartmouth.edu/~sting/sw/imath

pg_freespacemap

Модуль pg_freespacemap обеспечивает возможность исследовать карту свободного пространства (free space map, FSM). Он предоставляет функцию pg_freespace, или, точнее, две перегруженных функции. Эти функции показывают значение, записанное в карте свободного пространства для данной страницы либо для всех страниц в отношении.

По умолчанию его использование разрешено только суперпользователям и членам роли pg_stat_scan_tables. Дать доступ всем остальным можно с помощью команды GRANT.

Функции

pg_freespace(rel regclass IN, blkno bigint IN) returns int2

Возвращает объем свободного пространства на странице отношения, заданного параметром blkno, согласно FSM.

pg_freespace(rel regclass IN, blkno OUT bigint, avail OUT int2)

Показывает объем свободного пространства на каждой странице отношения, согласно FSM. Возвращается набор кортежей (blkno bigint, avail int2), по одному кортежу для каждой страницы в отношении.

Значения, хранимые в карте свободного пространства, не являются точными. Они округляются до 1/256 BLCKSZ (до 32 байтов со значением BLCKSZ по умолчанию) и не в полной мере поддерживаются в актуальном состоянии при каждом добавлении и изменении кортежей.

Для индексов отслеживаются полностью неиспользованные страницы, а не свободное пространство в страницах. Как следствие, эти значения не имеют смысла, а лишь показывают, занята страница или свободна.

Пример вывода

postgres=# SELECT * FROM pg_freespace('foo');
 blkno | avail
-------+-------
     0 |     0
     1 |     0
     2 |     0
     3 |    32
     4 |   704
     5 |   704
     6 |   704
     7 |  1216
     8 |   704
     9 |   704
    10 |   704
    11 |   704
    12 |   704
    13 |   704
    14 |   704
    15 |   704
    16 |   704
    17 |   704
    18 |   704
    19 |  3648
(20 rows)

postgres=# SELECT * FROM pg_freespace('foo', 7);
 pg_freespace
--------------
         1216
(1 row)

Автор

Исходную версию разработал Марк Кирквуд (markir@paradise.net.nz). Для версии с новой реализацией FSM код адаптировал Хейкки Линнакангас (heikki@enterprisedb.com)

pg_prewarm

Модуль pg_prewarm предоставляет оптимальный способ загружать данные отношений в кэш буферов операционной системы или в кэш буферов QHB. Предварительную загрузку можно выполнить вручную при помощи функции pg_prewarm или автоматически, добавив pg_prewarm в shared_preload_libraries. В последнем случае система запустит фоновый процесс, который будет периодически записывать содержимое общих буферов в файл autoprewarm.blocks, а после перезапуска сервера будет подгружать эти блоки в память, используя 2 фоновых процесса.

Функции

pg_prewarm(regclass, mode text default 'buffer', fork text default 'main',
           first_block int8 default null,
           last_block int8 default null) RETURNS int8

Первый аргумент задает отношение, которое будет предварительно загружено. Второй аргумент задает метод этой загрузки, из описанных ниже; третий задает ветвь отношения для загрузки, обычно main. Четвертый аргумент задает номер первого загружаемого блока (NULL принимается как синоним нуля). Пятый аргумент задает номер последнего загружаемого блока (NULL означает загрузку до последнего блока отношения включительно). Возвращаемое значение — количество предварительно загруженных блоков.

В этой функции доступны три метода предварительной загрузки. При prefetch выдаются асинхронные запросы предвыборки данных операционной системы, если они поддерживаются, либо происходит ошибка. При read считывается заданный диапазон блоков; в отличие от prefetch это происходит синхронно и поддерживается на всех платформах и сборках, но может быть медленнее. При buffer запрошенный диапазон блоков считывается в кэш буферов базы данных.

Обратите внимание, что с любым из этих методов попытка предварительно загрузить больше блоков, чем может поместиться в кэше, — в кэше ОС методом prefetch или read или в кэше QHB методом buffer — скорее всего приведет к тому, что блоки с меньшими номерами будут вытеснены из кэша при чтении последующих блоков. Кроме того, предварительно загруженные данные не имеют никакой специальной защиты от вытеснения из кэша, в связи с чем возможна ситуация, когда из-за другой системной активности только что загруженные блоки будут вытеснены вскоре после чтения, и, наоборот, при предварительной загрузке из кэша могут быть вытеснены другие данные. По этим причинам предварительная загрузка обычно наиболее полезна при запуске сервера, когда кэши в основном пусты.

autoprewarm_start_worker() RETURNS void

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

autoprewarm_dump_now() RETURNS int8

Обновить autoprewarm.blocks немедленно. Это может быть полезно, если рабочий процесс автозагрузки не работает, но вы хотите, чтобы он сработал после следующего перезапуска. Возвращаемое значение — количество записей, внесенных в autoprewarm.blocks.

Параметры конфигурации

pg_prewarm.autoprewarm (boolean)

Определяет, должен ли сервер запускать рабочий процесс автозагрузки. По умолчанию от включен. Этот параметр можно задать только при запуске сервера.

pg_prewarm.autoprewarm_interval (int)

Это интервал между обновлениями файла autoprewarm.blocks. Значение по умолчанию — 300 секунд. При значении 0 файл будет сохраняться не периодически, а только при отключении сервера.

Автор

Роберт Хаас (rhaas@postgresql.org)

pgrowlocks

Модуль pgrowlocks предоставляет функцию, показывающую информацию о блокировке строк для заданной таблицы.

По умолчанию его использование разрешено суперпользователям, членам роли pg_stat_scan_tables и пользователям с правом SELECT в заданной таблице.

Обзор

pgrowlocks(text) returns setof record

В параметре передается имя таблицы. Возвращаемый результат представляет собой набор записей, в котором каждая строка соответствует заблокированной строке в таблице. Столбцы вывода перечислены в Таблице 20.

Таблица 20. Столбцы вывода pgrowlocks

Параметр | Тип | Описание
locked_row | tid | Идентификатор кортежа (TID) блокированной строки
locker | xid | Идентификатор блокировочной транзакции или идентификатор мультитранзакции, если это мультитранзакция
multi | boolean | True, если блокиратор — мультитранзакция
xids | xid[] | Идентификаторы блокирующих транзакций (больше одного, если это мультитранзакция)
modes | text[] | Режим блокирования (больше одного, если это мультитранзакция), массив со значениями Key Share, Share, For No Key Update, No Key Update, For Update, Update.
pids | integer[] | Идентификаторы блокирующих обслуживающих процессов (больше одного, если это мультитранзакция)

Функция pgrowlocks запрашивает блокировку AccessShareLock для целевой таблицы и считывает строки одну за другой для сбора информации о блокировке строк. Для большой таблицы это происходит небыстро. Обратите внимание, что:

Если таблица заблокирована в режиме ACCESS EXCLUSIVE, функция pgrowlocks будет блокироваться.

Функция pgrowlocks не гарантирует выдачу внутренне согласованных снимков. В ходе ее выполнения могут быть установлены новые блокировки строк или сняты старые.

Функция pgrowlocks не показывает содержимое заблокированных строк. Если вы хотите одновременно взглянуть на содержимое строк, можно написать что-то вроде этого:

SELECT * FROM accounts AS a, pgrowlocks('accounts') AS p
  WHERE p.locked_row = a.ctid;

Однако учтите, что такой запрос будет очень неэффективным.

Пример вывода

=# SELECT * FROM pgrowlocks('t1');
 locked_row | locker | multi | xids  |     modes      |  pids
------------+--------+-------+-------+----------------+--------
 (0,1)      |    609 | f     | {609} | {"For Share"}  | {3161}
 (0,2)      |    609 | f     | {609} | {"For Share"}  | {3161}
 (0,3)      |    607 | f     | {607} | {"For Update"} | {3107}
 (0,4)      |    607 | f     | {607} | {"For Update"} | {3107}
(4 rows)

Автор

Тацуо Исии

pg_stat_statements

Модуль pg_stat_statements предоставляет возможность отслеживать статистику планирования и выполнения сервером всех операторов SQL.

Этот модуль следует загружать посредством добавления pg_stat_statements в shared_preload_libraries в файле qhb.conf, так как ему требуется дополнительная общая память. Это означает, что для добавления или удаления этого модуля необходимо перезапустить сервер. Кроме того, для функционирования этого модуля требуется включить вычисление идентификаторов запросов, что происходит автоматически, если задать в параметре compute_query_id значение auto или on или загрузить сторонний модуль, вычисляющий идентификаторы запросов.

Когда модуль pg_stat_statements активен, он отслеживает статистику по всем базам данных на сервере. Для доступа к этой статистике и ее обработки модуль предоставляет представления pg_stat_statements и pg_stat_statements_info, а также вспомогательные функции pg_stat_statements_reset и pg_stat_statements. Эти объекты не доступны глобально, но их можно установить в определенной базе данных посредством команды CREATE EXTENSION pg_stat_statements.

Представление pg_stat_statements

Статистика, собираемая модулем, выдается через представление с именем pg_stat_statements. Это представление содержит по одной строке для каждой отдельной комбинации ID базы данных, ID пользователя, ID запроса и указания, относится ли данный оператор к верхнему уровню или нет (в количестве, не превышающем максимальное число отдельных операторов, которые может отслеживать модуль). Столбцы представления показаны в Таблице 20.

Таблица 20. Столбцы pg_stat_statements

Тип столбца | Описание
userid oid (ссылается на pg_authid.oid) | OID пользователя, выполнявшего оператор
dbid oid (ссылается на pg_database.oid) | OID базы данных, в которой выполнялся оператор
toplevel bool | True, если этот запрос выполнялся как оператор верхнего уровня (всегда true, если для параметра pg_stat_statements.track установлено значение top)
queryid bigint | Хеш-код для идентификации одинаковых нормализованных запросов
query text | Текст, представляющий оператор
plans bigint | Число операций планирования этого оператора (если включен параметр pg_stat_statements.track_planning; иначе 0)
total_plan_time double precision | Общее время, затраченное на планирование этого оператора, в миллисекундах (если включен параметр pg_stat_statements.track_planning; иначе 0)
min_plan_time double precision | Минимальное время, затраченное на планирование этого оператора, в миллисекундах (если включен параметр pg_stat_statements.track_planning; иначе 0)
max_plan_time double precision | Максимальное время, затраченное на планирование этого оператора, в миллисекундах (если включен параметр pg_stat_statements.track_planning; иначе 0)
mean_plan_time double precision | Среднее время, затраченное на планирование этого оператора, в миллисекундах (если включен параметр pg_stat_statements.track_planning; иначе 0)
stddev_plan_time double precision | Стандартное отклонение времени, затраченного на планирование этого оператора, в миллисекундах (если включен параметр pg_stat_statements.track_planning; иначе 0)
calls bigint | Число выполнений этого оператора
total_exec_time double precision | Общее время, затраченное на выполнение этого оператора, в миллисекундах
min_exec_time double precision | Минимальное время, затраченное на выполнение этого оператора, в миллисекундах
max_exec_time double precision | Максимальное время, затраченное на выполнение этого оператора, в миллисекундах
mean_exec_time double precision | Среднее время, затраченное на выполнение этого оператора, в миллисекундах
stddev_exec_time double precision | Стандартное отклонение времени, затраченного на выполнение этого оператора, в миллисекундах
rows bigint | Общее количество строк, полученных или затронутых этим оператором
shared_blks_hit bigint | Общее количество попаданий в разделяемый кэш блоков для этого оператора
shared_blks_read bigint | Общее количество прочтений разделяемых блоков для этого оператора
shared_blks_dirtied bigint | Общее количество разделяемых блоков, «загрязненных» этим оператором
shared_blks_written bigint | Общее количество разделяемых блоков, записанных этим оператором
local_blks_hit bigint | Общее количество попаданий в локальный кэш блоков для этого оператора
local_blks_read bigint | Общее количество прочтений локальных блоков для этого оператора
local_blks_dirtied bigint | Общее количество локальных блоков, «загрязненных» этим оператором
local_blks_written bigint | Общее количество локальных блоков, записанных этим оператором
temp_blks_read bigint | Общее количество прочтений временных блоков для этого оператора
temp_blks_written bigint | Общее количество временных блоков, записанных этим оператором
blk_read_time double precision | Общее время, затраченное оператором на чтение блоков, в миллисекундах (если включен track_io_timing; иначе 0)
blk_write_time double precision | Общее время, затраченное оператором на запись блоков, в миллисекундах (если включен track_io_timing; иначе 0)
wal_records bigint | Общее количество записей WAL, сгенерированных этим оператором
wal_fpi bigint | Общее количество образов полных страниц WAL, сгенерированных этим оператором
wal_bytes numeric | Общий объем WAL, сгенерированный этим оператором, в байтах

Из соображений безопасности только суперпользователям и членам роли pg_read_all_stats разрешено видеть текст SQL queryid запросов, выполняемых другими пользователями. Однако другие пользователи могут видеть статистику, если это представление было установлено в их базу данных.

Планируемые запросы (то есть SELECT, INSERT, UPDATE и DELETE) объединяются в одну запись в pg_stat_statements, когда они имеют идентичные структуры запросов согласно внутреннему вычислению хеша. Обычно в таком случае два запроса буду считаться равными, если они семантически равнозначны, не считая значений литеральных констант, находящихся в запросе. Однако служебные команды (то есть все остальные команды) сравниваются строго по текстовым строкам запросов.

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

Когда значение константы игнорируется в целях сравнения запроса с другими запросами, эта константа заменяется в выводе pg_stat_statements символом параметра, например $1. В остальном этот вывод содержит текст первого запроса с определенным значением хеша queryid, связанным с записью в pg_stat_statements.

В некоторых случаях запросы с визуально различными тексами могут быть объединены в одну запись pg_stat_statements. Обычно это происходит только для семантически равнозначных запросов, но есть небольшой шанс, что из-за наложений хеша несвязанные запросы окажутся объединенными в одной записи. (Однако это невозможно для запросов, принадлежащих разным пользователям или базам данных.)

Поскольку значение хеша queryid вычисляется по представлениям запроса на стадии после синтаксического анализа, возможна и обратная ситуация: запросы с идентичным текстом могут оказаться в разных записях, если в результате каких-либо факторов, например разных настроек search_path, у них будет разное значение.

Потребители статистики pg_stat_statements могут пожелать использовать в качестве более стабильного и надежного идентификатора для каждой записи не текст запроса, а queryid (возможно, в сочетании с dbid и userid). Однако важно понимать, что стабильность значения хеша queryid гарантируется с ограничениями. Так как этот идентификатор получается из дерева запроса после анализа, его значение будет, помимо прочего, зависеть от внутренних идентификаторов объектов, находящихся в этом представлении. С этим связано несколько неинтуитивных последствий. Например, pg_stat_statements будет считать два одинаковых внешне запроса разными, если они обращаются к таблице, которая была удалена и воссоздана между выполнением этих запросов. Процесс хеширования также чувствителен к различиям в машинной архитектуре и другим особенностям платформы. Более того, опасно предполагать, что queryid будет оставаться неизменным при обновлении основных версий QHB.

На практике выходит, что значения queryid можно считать надежными и сравнимыми только до тех пор, пока версия базового сервера и детали метаданных каталога остаются неизменными. Можно ожидать, что два сервера, участвующих в репликации на основе воспроизведения физического WAL, будут иметь одинаковые значения queryid для одного. Однако схемы с логической репликацией не гарантируют идентичности реплик во всех значимых деталях, поэтому queryid не будет полезным идентификатором для накопления показателей стоимости по всему набору логических реплик. В случае сомнений рекомендуется провести прямое тестирование.

Символы параметров, используемые для замены констант в представляющих запросы текстах, начинаются с номера, следующего за наибольшим параметром $n в исходном текста запроса, или с $1, если параметров нет. Стоит отметить, что в некоторых случаях на эту нумерацию могут влиять скрытые символы параметров. Например, PL/pgSQL применяет такие символы для добавления в запросы значений локальных переменных функции, поэтому оператор PL/pgSQL вида SELECT i + 1 INTO j будет представлен в тексте как SELECT i + $2.

Тексты, представляющие запросы, сохраняются во внешнем файле на диске и не занимают общую память. Поэтому даже очень громоздкие тексты запросов можно успешно сохранить. Однако если во внешнем файле накапливается много длинных текстов запросов, он может вырасти до неуправляемого размера. В таком случае в качестве метода исправления этой проблемы pg_stat_statements может решить стереть тексты запросов, в результате чего во всех существующих записях в представлении pg_stat_statements в полях query окажутся значения NULL, хотя статистика, связанная с каждым queryid, сохранится. Если это происходит, возможно, стоит уменьшить pg_stat_statements.max, чтобы предотвратить повторение таких ситуаций.

Показатели plans и calls не всегда должны совпадать, потому что статистика планирования и выполнения обновляется в конце соответствующей фазы и только при успешном завершении операции. Например, если оператор был успешно запланирован, но во время фазы выполнения произошла ошибка, изменится только статистика планирования. Если же планирование пропускается по причине использования кэшированного плана, изменится только статистика выполнения.

Представление pg_stat_statements_info

Статистика самого модуля pg_stat_statements отслеживается и выдается через представление с именем pg_stat_statements_info. Это представление содержит только одну строку. Столбцы представления показаны в Таблице 21.

Таблица 21. Столбцы pg_stat_statements_info

Тип столбца | Описание
dealloc bigint | Сколько всего раз в pg_stat_statements отбрасывались записи о наиболее редко выполняемых операторах, поскольку было выявлено больше различных операторов, чем задано в pg_stat_statements.max
stats_reset timestamp with time zone | Время последнего сброса всех статистических данных в представлении pg_stat_statements

Функции

pg_stat_statements_reset(userid Oid, dbid Oid, queryid bigint) returns void

Функция pg_stat_statements_reset сбрасывает всю статистику, собранную к этому времени модулем pg_stat_statements для заданного пользователя (userid), базы данных (dbid) и запроса (queryid). В случае отсутствия одного из параметров для него по умолчанию используется значение 0(неприменимо) и очищается статистика, соответствующая другим параметрам. Если никакой параметр не задан или все параметры имеют значение 0(неприменимо), очищается вся статистика. Если сбрасывается вся статистика в представлении pg_stat_statements, вместе с ней также очищается статистика в представлении pg_stat_statements_info view. По умолчанию эту функцию могут выполнять только суперпользователи. Другим пользователям можно дать к ней доступ с помощью команды GRANT.

pg_stat_statements(showtext boolean) returns setof record

Представление pg_stat_statements определено на базе функции, которая также называется pg_stat_statements. Клиенты могут вызвать функцию pg_stat_statements напрямую, а также, указав showtext := false, получить результат без текста запроса (то есть выходной аргумент (OUT), соответствующий столбцу представления query, вернет column NULL). Эта возможность предназначена для поддержки внешних инструментов, для которых может быть желательно избежать издержек, связанных с постоянным получением текстов запросов неопределенной длины. Такие инструменты могут кэшировать текст первого запроса, который они выявят для каждой записи самостоятельно, поскольку именно это и делает сам pg_stat_statements, а затем запрашивать тексты запросов только при необходимости. Так как сервер сохраняет тексты запросов в файле, этот подход может сократить объем физического ввода/вывода, порождаемого постоянной проверкой данных pg_stat_statements.

Параметры конфигурации

pg_stat_statements.max (integer)

Параметр pg_stat_statements.max задает максимальное количество операторов, отслеживаемых модулем (т. е. максимальное количество строк в представлении pg_stat_statements). Если выявляется больше различных операторов, чем заданное число, информация о наиболее редко выполняемых операторах сбрасывается. В представлении pg_stat_statements_info можно увидеть, сколько раз это происходило. Значение по умолчанию — 5000. Этот параметр можно задать только при запуске сервера.

pg_stat_statements.track (enum)

Параметр pg_stat_statements.track определяет, какие операторы будут подсчитываться модулем. Со значением top отслеживаются операторы верхнего уровня (те, которые выполняются непосредственно клиентами), со значением all также отслеживаются вложенные операторы (например, операторы, вызываемые внутри функций), а со значением none сбор статистики по операторам выключается. Значение по умолчанию — top. Изменять этот параметр могут только суперпользователи.

pg_stat_statements.track_utility (boolean)

Параметр pg_stat_statements.track_utility определяет, будет ли модуль отслеживать служебные команды. Служебными командами являются все команды, отличные от SELECT, INSERT, UPDATE и DELETE. Значение по умолчанию — on (включен). Изменять этот параметр могут только суперпользователи.

pg_stat_statements.track_planning (boolean)

Параметр pg_stat_statements.track_planning определяет, будет ли модуль отслеживать операции планирования и их длительность. Включение этого параметра может вызвать значительное снижение производительности, особенно когда операторы с идентичной структурой запроса выполняются во множестве одновременных подключений, которые в итоге пытаются одновременно изменить одни и те же записи в pg_stat_statements. Значение по умолчанию — off (выключен). Изменять этот параметр могут только суперпользователи.

pg_stat_statements.save (boolean)

Параметр pg_stat_statements.save указывает, должна ли статистика операторов сохраняться после перезагрузок сервера. При значении off (выключен) статистика не сохраняется при остановке сервера и не перезагружается при его запуске. Значение по умолчанию — on (включен). Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Этому модулю требуется дополнительная общая память в объеме, пропорциональном pg_stat_statements.max. Обратите внимание, что эта память будет занята при загрузке модуля, даже если pg_stat_statements.track имеет значение none.

Эти параметры следует задавать в qhb.conf. Обычно использование может выглядеть так:

# qhb.conf
shared_preload_libraries = 'pg_stat_statements'

compute_query_id = on
pg_stat_statements.max = 10000
pg_stat_statements.track = all

Пример вывода

bench=# SELECT pg_stat_statements_reset();

$ pgbench -i bench
$ pgbench -c10 -t300 bench

bench=# \x
bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit /
               nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
          FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-[ RECORD 1 ]---+--------------------------------------------------​------------------
query           | UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2
calls           | 3000
total_exec_time | 25565.855387
rows            | 3000
hit_percent     | 100.0000000000000000
-[ RECORD 2 ]---+--------------------------------------------------​------------------
query           | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
calls           | 3000
total_exec_time | 20756.669379
rows            | 3000
hit_percent     | 100.0000000000000000
-[ RECORD 3 ]---+--------------------------------------------------​------------------
query           | copy pgbench_accounts from stdin
calls           | 1
total_exec_time | 291.865911
rows            | 100000
hit_percent     | 100.0000000000000000
-[ RECORD 4 ]---+--------------------------------------------------​------------------
query           | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2
calls           | 3000
total_exec_time | 271.232977
rows            | 3000
hit_percent     | 98.8454011741682975
-[ RECORD 5 ]---+--------------------------------------------------​------------------
query           | alter table pgbench_accounts add primary key (aid)
calls           | 1
total_exec_time | 160.588563
rows            | 0
hit_percent     | 100.0000000000000000

bench=# SELECT pg_stat_statements_reset(0,0,s.queryid) FROM pg_stat_statements AS s
            WHERE s.query = 'UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2';

bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit /
               nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
          FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-[ RECORD 1 ]---+--------------------------------------------------​------------------
query           | UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2
calls           | 3000
total_exec_time | 20756.669379
rows            | 3000
hit_percent     | 100.0000000000000000
-[ RECORD 2 ]---+--------------------------------------------------​------------------
query           | copy pgbench_accounts from stdin
calls           | 1
total_exec_time | 291.865911
rows            | 100000
hit_percent     | 100.0000000000000000
-[ RECORD 3 ]---+--------------------------------------------------​------------------
query           | UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2
calls           | 3000
total_exec_time | 271.232977
rows            | 3000
hit_percent     | 98.8454011741682975
-[ RECORD 4 ]---+--------------------------------------------------​------------------
query           | alter table pgbench_accounts add primary key (aid)
calls           | 1
total_exec_time | 160.588563
rows            | 0
hit_percent     | 100.0000000000000000
-[ RECORD 5 ]---+--------------------------------------------------​------------------
query           | vacuum analyze pgbench_accounts
calls           | 1
total_exec_time | 136.448116
rows            | 0
hit_percent     | 99.9201915403032721

bench=# SELECT pg_stat_statements_reset(0,0,0);

bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit /
               nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
          FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-[ RECORD 1 ]---+--------------------------------------------------​---------------------------
query           | SELECT pg_stat_statements_reset(0,0,0)
calls           | 1
total_exec_time | 0.189497
rows            | 1
hit_percent     |
-[ RECORD 2 ]---+--------------------------------------------------​---------------------------
query           | SELECT query, calls, total_exec_time, rows, $1 * shared_blks_hit /          +
                |                nullif(shared_blks_hit + shared_blks_read, $2) AS hit_percent+
                |           FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT $3
calls           | 0
total_exec_time | 0
rows            | 0
hit_percent     |

Авторы

Такахиро Итагаки (itagaki.takahiro@oss.ntt.co.jp). Нормализацию запросов добавил Питер Гейган (peter@2ndquadrant.com).

pgstattuple

Модуль pgstattuple предоставляет различные функции для получения статистики на уровне кортежей.

Поскольку эти функции возвращают подробную информацию на уровне страницы, доступ к ним по умолчанию ограничен. По умолчанию право EXECUTE для них имеет только роль pg_stat_scan_tables. Разумеется, суперпользователи могут обойти это ограничение. После установки этого расширения пользователи могут с помощью команд GRANT поменять права доступа к этим функциям и разрешить их выполнение другим пользователям. Однако вместо этого предпочтительнее будет добавить этих пользователей в роль pg_stat_scan_tables.

Функции

pgstattuple(regclass) returns record

Функция pgstattuple возвращает физическую длину отношения, процент «мертвых» кортежей и другую информацию. Это может помочь пользователям принять решение о необходимости очистки. В аргументе передается имя целевого отношения (при необходимости дополненное схемой) или его OID. Например:

test=> SELECT * FROM pgstattuple('pg_catalog.pg_proc');
-[ RECORD 1 ]------+-------
table_len          | 458752
tuple_count        | 1470
tuple_len          | 438896
tuple_percent      | 95.67
dead_tuple_count   | 11
dead_tuple_len     | 3157
dead_tuple_percent | 0.69
free_space         | 8932
free_percent       | 1.95

Выходные столбцы описаны в Таблице 23.

Таблица 23. Выходные столбцы pgstattuple

Столбец | Тип | Описание
table_len | bigint | Физическая длина отношения в байтах
tuple_count | bigint | Количество «живых» кортежей
tuple_len | bigint | Общая длина «живых» кортежей в байтах
tuple_percent | float8 | Процент «живых» кортежей
dead_tuple_count | bigint | Количество «мертвых» кортежей
dead_tuple_len | bigint | Общая длина «мертвых» кортежей в байтах
dead_tuple_percent | float8 | Процент «мертвых» кортежей
free_space | bigint | Общий объем свободного пространства в байтах
free_percent | float8 | Процент свободного пространства

Примечание
Значение table_len всегда будет больше суммы tuple_len, dead_tuple_len и free_space. Разница объясняется фиксированными издержками на страницу, внутристраничной таблицей указателей на кортежи и пропусками, добавляемыми для корректного выравнивания кортежей.

Функция pgstattuple получает блокировку отношения только для чтения. Поэтому ее результаты не отражают мгновенный снимок; на них будут влиять параллельные изменения.

Функция pgstattuple считает кортеж «мертвым», если HeapTupleSatisfiesDirty возвращает false.

pgstattuple(text) returns record

Равнозначна функции pgstattuple(regclass) за исключением того, что целевое отношение задается в текстовом виде. Эта функция пока что оставлена для обратной совместимости и в последующих выпусках будет считаться устаревшей.

pgstatindex(regclass) returns record

Функция pgstatindex возвращает запись, показывающую информацию об индексе B-дерева. Например:

test=> SELECT * FROM pgstatindex('pg_cast_oid_index');
-[ RECORD 1 ]------+------
version            | 2
tree_level         | 0
index_size         | 16384
root_block_no      | 1
internal_pages     | 0
leaf_pages         | 1
empty_pages        | 0
deleted_pages      | 0
avg_leaf_density   | 54.27
leaf_fragmentation | 0

Выходные столбцы:

Столбец | Тип | Описание
version | integer | Номер версии B-дерева
tree_level | integer | Уровень корневой страницы в дереве
index_size | bigint | Общий размер индекса в байтах
root_block_no | bigint | Расположение корневой страницы (0, если таковой нет)
internal_pages | bigint | Количество «внутренних» страниц (верхнего уровня)
leaf_pages | bigint | Количество страниц на уровне листьев
empty_pages | bigint | Количество пустых страниц
deleted_pages | bigint | Количество удаленных страниц
avg_leaf_density | float8 | Средняя плотность страниц на уровне листьев
leaf_fragmentation | float8 | Фрагментация страниц на уровне листьев

Сообщаемый размер индекса (index_size) обычно равен сумме internal_pages + leaf_pages + empty_pages + deleted_pages плюс одна страница, поскольку в него также включается метастраница индекса.

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

pgstatindex(text) returns record

Равнозначна функции pgstatindex(regclass) за исключением того, что целевой индекс задается в текстовом виде. Эта функция пока что оставлена для обратной совместимости и в последующих выпусках будет считаться устаревшей.

pgstatginindex(regclass) returns record

Функция pgstatginindex возвращает запись, показывающую информацию об индексе GIN. Например:

test=> SELECT * FROM pgstatginindex('test_gin_index');
-[ RECORD 1 ]--+--
version        | 1
pending_pages  | 0
pending_tuples | 0

Выходные столбцы:

Столбец | Тип | Описание
version | integer | Номер версии GIN
pending_pages | integer | Количество страниц в списке ждущих обработки
pending_tuples | bigint | Количество кортежей в списке ждущих обработки

pgstathashindex(regclass) returns record

Функция pgstathashindex возвращает запись, показывающую информацию о хеш-индексе. Например:

test=> SELECT * FROM pgstathashindex('con_hash_index');
-[ RECORD 1 ]--+-----------------
version        | 4
bucket_pages   | 33081
overflow_pages | 0
bitmap_pages   | 1
unused_pages   | 32455
live_items     | 10204006
dead_items     | 0
free_percent   | 61.8005949100872

Выходные столбцы:

Столбец | Тип | Описание
version | integer | Номер версии HASH
bucket_pages | bigint | Количество страниц с ячейками хеша
overflow_pages | bigint | Количество страниц переполнения
bitmap_pages | bigint | Количество страниц битовой карты
unused_pages | bigint | Количество неиспользованных страниц
live_items | bigint | Количество «живых» кортежей
dead_tuples | bigint | Количество «мертвых» кортежей
free_percent | float | Процент свободного пространства

pg_relpages(regclass) returns bigint

Функция pg_relpages возвращает количество страниц в отношении.

pg_relpages(text) returns bigint

Равнозначна pg_relpages(regclass) за исключением того, что целевое отношение задается в текстовом виде. Эта функция пока что оставлена для обратной совместимости и в последующих выпусках будет считаться устаревшей.

pgstattuple_approx(regclass) returns record

Функция pgstattuple_approx представляет собой более быструю альтернативу pgstattuple, возвращающую приблизительные результаты В аргументе передается имя или OID целевого отношения. Например:

test=> SELECT * FROM pgstattuple_approx('pg_catalog.pg_proc'::regclass);
-[ RECORD 1 ]--------+-------
table_len            | 573440
scanned_percent      | 2
approx_tuple_count   | 2740
approx_tuple_len     | 561210
approx_tuple_percent | 97.87
dead_tuple_count     | 0
dead_tuple_len       | 0
dead_tuple_percent   | 0
approx_free_space    | 11996
approx_free_percent  | 2.09

Выходные столбцы описаны в Таблице 24.

В то время как pgstattuple всегда производит полное сканирование таблицы и возвращает точное количество «живых» и «мертвых» кортежей (и их размер), а также точный объем свободного пространства, pgstattuple_approx пытается избежать полного сканирования таблицы и возвращает точную статистику только по «мертвым» кортежам, а количество и размер «живых» кортежей и объем свободного пространства выдает приблизительные.

pgstattuple_approx делает это, пропуская страницы, в которых, согласно карте видимости, есть только видимые кортежи (если для страницы установлен соответствующий бит карты видимости, предполагается, что она не содержит «мертвых» кортежей). Для таких страниц эта функция узнает объем свободного пространства из карты свободного пространства и предполагает, что остальное пространство на странице занято «живыми» кортежами.

На страницах, которые нельзя пропустить, она сканирует каждый кортеж, записывая его наличие и размер в соответствующих счетчиках и подсчитывая свободное пространство на странице. В конце она оценивает общее количество «живых» кортежей, исходя из количества просканированных страниц и кортежей (тем же способом, каким VACUUM рассчитывает значение pg_class.reltuples).

Таблица 24. Выходные столбцы pgstattuple_approx

Столбец | Тип | Описание
table_len | bigint | Физическая длина отношения в байтах (точная)
scanned_percent | float8 | Просканированный процент таблицы
approx_tuple_count | bigint | Количество «живых» кортежей (примерное)
approx_tuple_len | bigint | Общая длина «живых» кортежей в байтах (примерная)
approx_tuple_percent | float8 | Процент «живых» кортежей
dead_tuple_count | bigint | Количество «мертвых» кортежей (точное)
dead_tuple_len | bigint | Общая длина «мертвых» кортежей в байтах (точная)
dead_tuple_percent | float8 | Процент «мертвых» кортежей
approx_free_space | bigint | Общий объем свободного пространства в байтах (примерный)
approx_free_percent | float8 | Процент свободного пространства

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

Авторы

Тацуо Исии, Сатоши Нагаясу и Абхиджит Менон-Сен

pg_surgery

Модуль pg_surgery предоставляет различные функции для проведения операций с поврежденными отношениями. Эти функции по своему замыслу небезопасны, и их применение может повредить вашу базу данных (или усугубить имеющиеся повреждения). Например, эти функции с легкостью могут быть использованы, чтобы нарушить соответствие таблицы и ее индексов, повредить ограничения UNIQUE или FOREIGN KEY или даже сделать видимыми кортежи, чтение которых вызовет аварийный сбой сервера базы данных. Эти функции следует использовать с большой осторожностью и только в крайнем случае.

Функции

heap_force_kill(regclass, tid[]) returns void

Функция heap_force_kill помечает «использованные» указатели строк как «мертвые», не проверяя кортежи. Эта функция предназначена для принудительного удаления кортежей, к которым нельзя обратиться. Например:

test=> SELECT * FROM t1 where ctid = '(0, 1)';
ERROR:  could not access status of transaction 4007513275
DETAIL:  Could not open file "pg_xact/0EED": No such file or directory.

test=# SELECT heap_force_kill('t1'::regclass, ARRAY['(0, 1)']::tid[]);
 heap_force_kill
-----------------

(1 row)

test=# SELECT * FROM t1 where ctid = '(0, 1)';
(0 rows)

heap_force_freeze(regclass, tid[]) returns void

Функция heap_force_freeze помечает кортежи как замороженные, не проверяя их данные. Эта функция предназначена для получения доступа к кортежам, которые оказались недоступными из-за повреждения информации о видимости или не дают успешно очистить таблицу из-за повреждения этой информации. Например:

test=> VACUUM t1;
ERROR:  found xmin 507 from before relfrozenxid 515
CONTEXT:  while scanning block 0 of relation "public.t1"

test=# SELECT ctid FROM t1 WHRER xmin = 507;
 ctid
-------
 (0,3)
(1 row)

test=# SELECT heap_force_freeze('t1'::regclass, ARRAY['(0, 3)']::tid[]);
 heap_force_freeze
-------------------

(1 row)

test=# SELECT ctid FROM t1 WHERE xmin = 2;
 ctid
-------
 (0,3)
(1 row)

Авторы

Ашутош Шарма (ashu.coek88@gmail.com)

pg_trgm

Модуль pg_trgm предоставляет функции и операторы для определения схожести буквенно-цифрового текста на основе соответствия триграмм, а также классы операторов индексов, поддерживающие быстрый поиск схожих строк.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Общее представление о триграммах (или триграфах)

Триграмма — это группа из трех последовательных символов, взятых из одной строки. Схожесть двух строк можно измерить, подсчитав количество триграмм, которые есть в обеих. Эта простая идея оказывается очень эффективной для измерения схожести слов на многих естественных языках.

Примечание
Извлекая триграммы из строки, pg_trgm игнорирует символы, не относящиеся к словам (не буквенно-цифровые). При определении набора триграмм, содержащихся в строке, считается, что перед каждым словом находятся два пробела, а после — один. Например, из строки «cat» выделяется набор триграмм: « c», « ca», «cat» и «at ». Набор триграмм из строки «foo|bar»: « f», « fo», «foo», «oo », « b», « ba», «bar» и «ar ».

Функции и операторы

Функции, предоставляемые модулем pg_trgm, перечислены в Таблице 25, а операторы — в Таблице 26.

Таблица 25. Функции pg_trgm

Функция
Описание

similarity ( text, text ) → real
Возвращает число, показывающее, насколько схожи два аргумента. Диапазон результата — от нуля (показывающего, что две строки полностью различны) до единицы (показывающей, что две строки идентичны).

show_trgm ( text ) → text[]
Возвращает массива всех триграмм в заданной строке. (На практике это редко бывает полезно, разве что для отладки.)

word_similarity ( text, text ) → real
Возвращает число, показывающее наибольшую степень схожести между набором триграмм в первой строке и любой непрерывной последовательностью упорядоченного набора триграмм во второй строке. Подробнее об этом рассказывается ниже.

strict_word_similarity ( text, text ) → real
Похожа на word_similarity, но подгоняет границы последовательности к границам слов. Поскольку триграммы не пересекают слова, эта функция фактически возвращает наибольшую степень схожести между первой строкой и любой непрерывной последовательностью слов во второй строке.

show_limit () → real
Возвращает текущий порог схожести, используемый оператором %. Это значение задает минимальную схожесть между двумя словами, при которой они считаются достаточно схожими, чтобы, к примеру, одно было ошибочным написанием другого (Устаревшая функция; используйте вместо нее SHOW pg_trgm.similarity_threshold.)

set_limit ( real ) → real
Задает текущий порог схожести, используемый оператором %. Порог должен быть в диапазоне от 0 до 1 (по умолчанию 0.3). Возвращает то же значение, что было передано на вход. (Устаревшая функция; используйте вместо нее SET pg_trgm.similarity_threshold.)

Рассмотрим следующий пример:

# SELECT word_similarity('word', 'two words');
 word_similarity
-----------------
             0.8
(1 row)

Набор триграмм в первой строке: {" w"," wo","wor","ord","rd "}. Во второй строке упорядоченный набор триграмм: {" t"," tw","two","wo "," w"," wo","wor","ord","rds","ds "}. Наиболее схожая последовательность в упорядоченном наборе триграмм: {" w"," wo","wor","ord"}, а коэффициент схожести равен 0.8.

Эта функция возвращает значение, которое можно примерно воспринимать как наивысшую схожесть между первой строкой и любой подстрокой второй строки. Однако эта функция не добавляет пробелы к границам последовательности. Поэтому число дополнительных символов, находящихся во второй строке, не рассматривается, разве что для несоответствующих границ слов.

В то же время strict_word_similarity выбирает последовательность слов во второй строке. В показанном выше примере strict_word_similarity выбрала бы последовательность из одного слова 'words', которой соответствует набор триграмм {" w"," wo","wor","ord","rds","ds "}.

# SELECT strict_word_similarity('word', 'two words'), similarity('word', 'words');
 strict_word_similarity | similarity
------------------------+------------
               0.571429 |   0.571429
(1 row)

Таким образом, функция strict_word_similarity полезна для выявления схожести целых слов, тогда как word_similarity больше подходит для выявления схожести частей слов.

Таблица 26. Операторы pg_trgm

Оператор
Описание

text % text → boolean
Возвращает true, если схожесть его аргументов выше текущего порога, заданного параметром pg_trgm.similarity_threshold.

text <% text → boolean
Возвращает true, если схожесть между набором триграмм в первом аргументе и непрерывной последовательностью упорядоченного набора триграмм во втором аргументе выше текущего порога схожести слов, заданного параметром pg_trgm.word_similarity_threshold.

text %> text → boolean
Коммутатор оператора <%.

text <<% text → boolean
Возвращает true, если в его втором аргументе имеется непрерывная последовательность упорядоченного набора триграмм, соответствующего границам слов, и его схожесть с набором триграмм первого аргумента выше текущего порога схожести строго слов, заданного параметром pg_trgm.strict_word_similarity_threshold.

text %>> text → boolean
Коммутатор оператора <<%.

text <-> text → real
Возвращает «расстояние» между аргументами, то есть один минус значение similarity().

text <<-> text → real
Возвращает «расстояние» между аргументами, то есть один минус значение word_similarity().

text <->> text → real
Коммутатор оператора <<->.

text <<<-> text → real
Возвращает «расстояние» между аргументами, то есть один минус значение strict_word_similarity().

text <->>> text → real
Коммутатор оператора <<<->.

Параметры GUC

pg_trgm.similarity_threshold (real)

Задает текущий порог схожести, используемый оператором %. Порог должен быть в диапазоне от 0 до 1 (по умолчанию 0.3).

pg_trgm.word_similarity_threshold (real)

Задает текущий порог схожести слов, используемый операторами <% и %>. Порог должен быть в диапазоне от 0 до 1 (по умолчанию 0.6).

pg_trgm.strict_word_similarity_threshold (real)

Задает текущий порог схожести строго слов, используемый операторами <<% и %>>. Порог должен быть в диапазоне от 0 до 1 (по умолчанию 0.5).

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

Модуль pg_trgm предоставляет классы операторов индексов GiST и GIN, которые позволяют создавать индекс по текстовым столбцам для очень быстрого поиска по схожести. Эти типы индексов поддерживают вышеописанные операторы схожести и дополнительно поддерживают поиск по индексу на основе триграмм для запросов с LIKE, ILIKE, ~, ~* и =. Операторы неравенства не поддерживаются. Обратите внимание, что для операторов равенства эти индексы могут быть не так эффективны, как обычные индексы B-деревья.

Пример:

CREATE TABLE test_trgm (t text);
CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops);

или

CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);

Класс операторов GiST gist_trgm_ops аппроксимирует набор триграмм в виде сигнатуры битовой карты. Его необязательный целочисленный параметр siglen определяет длину сигнатуры в байтах. Значение по умолчанию — 12 байтов. Допустима длина сигнатуры в пределах от 1 до 2024 байтов. При увеличении размера сигнатур поиск работает точнее (сканируется меньшая область индекса и меньше страниц кучи), но сам индекс становится больше.

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

CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops(siglen=32));

На этот момент у вас будет индекс по столбцу t, который можно использовать для поиска по схожести. Типичный запрос:

SELECT t, similarity(t, 'слово') AS sml
  FROM test_trgm
  WHERE t % 'слово'
  ORDER BY sml DESC, t;

Он возвратит все значения в текстовом столбце, которые достаточно схожи со строкой слово, отсортированные от наиболее к наименее схожим. Благодаря использованию индекса эта операция будет быстрой даже с очень большими наборами данных.

Другой вариант предыдущего запроса:

SELECT t, t <-> 'слово' AS dist
  FROM test_trgm
  ORDER BY dist LIMIT 10;

Он может быть довольно эффективно выполнен с индексами GiST, но не GIN. Обычно он выигрышнее первого варианта, когда требуется получить лишь небольшое число близких совпадений.

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

SELECT t, word_similarity('слово', t) AS sml
  FROM test_trgm
  WHERE 'слово' <% t
  ORDER BY sml DESC, t;

и

SELECT t, strict_word_similarity('слово', t) AS sml
  FROM test_trgm
  WHERE 'слово' <<% t
  ORDER BY sml DESC, t;

Он возвратит все значения в текстовом столбце, для которых существует непрерывная последовательность в упорядоченном наборе триграмм, достаточно схожая с набором триграмм строки слово, отсортированные от наиболее к наименее схожим. Благодаря использованию индекса эта операция будет быстрой даже с очень большими наборами данных.

Другие возможные варианты предыдущих запросов:

SELECT t, 'слово' <<-> t AS dist
  FROM test_trgm
  ORDER BY dist LIMIT 10;

и

SELECT t, 'слово' <<<-> t AS dist
  FROM test_trgm
  ORDER BY dist LIMIT 10;

Они могут быть довольно эффективно выполнены с индексами GiST, но не GIN.

Эти типы индексов также поддерживают поиск с операторами LIKE и ILIKE, например:

SELECT * FROM test_trgm WHERE t LIKE '%foo%bar';

При таком поиске по индексу сначала из искомой строки извлекаются триграммы, а затем они ищутся в индексе. Чем больше триграмм оказывается в искомой строке, тем более эффективен поиск по индексу. В отличие от поиска по B-дереву, искомой строке не обязательно быть привязанной к левому краю.

Кроме того, эти типы индексов поддерживают индексный поиск по совпадению регулярных выражений (операторы ~ и ~*), например:

SELECT * FROM test_trgm WHERE t ~ '(foo|bar)';

При таком поиске по индексу сначала из регулярного выражения извлекаются триграммы, а затем они ищутся в индексе. Чем больше триграмм можно извлечь из регулярного выражения, тем более эффективен поиск по индексу. В отличие от поиска по B-дереву, искомой строке не обязательно быть привязанной к левому краю.

Относительно поиска с LIKE или по регулярному выражению, имейте в виду, что при отсутствии триграмм в шаблоне поиск сводится к полному сканированию индекса.

Выбор между индексами GiST и GIN зависит от относительных характеристик производительности GiST и GIN, которые здесь не рассматриваются.

Интеграция с текстовым поиском

Сопоставление триграмм — очень полезный инструмент при использовании в сочетании с полнотекстовым индексом. В частности, это может помочь распознать слова, написанные неправильно, которые нельзя будет сопоставить непосредственно механизмом полнотекстового поиска.

Первый этап — создание вспомогательной таблицы, содержащей все уникальные слова в документах:

CREATE TABLE words AS SELECT word FROM
        ts_stat('SELECT to_tsvector(''simple'', bodytext) FROM documents');

где documents — это таблица с текстовым полем bodytext, по которому нам нужно выполнить поиск. Причина использования с функцией to_tsvector конфигурации simple вместо конфигурации для определенного языка состоит в том, что нам нужен список исходных (необработанных стеммером) слов.

Затем создадим индекс триграмм по столбцу со словами:

CREATE INDEX words_idx ON words USING GIN (word gin_trgm_ops);

Теперь можно использовать запрос SELECT, подобный показанному в предыдущем примере, чтобы предлагать варианты исправлений слов, введенных пользователем с ошибками. Будет полезно дополнительно проверить, что выбранные слова также имеют длину, схожую с длиной неправильно написанного слова.

Примечание
Поскольку таблица words была создана как отдельная статическая таблица, ее нужно периодически пересоздавать, чтобы она достаточно хорошо соответствовала коллекции документов. Постоянно поддерживать ее в полностью актуальном состоянии обычно нет необходимости.

Ссылки

Сайт разработки GiST http://www.sai.msu.su/~megera/postgres/gist/

Сайт разработки Tsearch2 http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/

Авторы

Олег Бартунов (oleg@sai.msu.su), Москва, Московский Государственный Университет им. М. В. Ломоносова, Россия

Федор Сигаев (teodor@sigaev.ru), Москва, ООО «Дельта-Софт», Россия

Александр Коротков (a.korotkov@postgrespro.ru), Москва, Postgres Professional, Россия

Документация: Кристофер Кингс-Линн

Разработку этого модуля спонсировало ООО «Дельта-Софт», г. Москва, Россия.

pg_visibility

Модуль pg_visibility обеспечивает возможность исследовать для таблицы карту видимости (visibility map, VM) и информацию о видимости на уровне страниц. Кроме того, он предоставляет функции для проверки целостности карты видимости и ее принудительного пересоздания.

Для хранения информации о видимости на уровне страниц используется три различных бита. Бит полной видимости в карте видимости означает, что все кортежи в соответствующей странице отношения являются видимыми для всех текущих и будущих транзакций. Бит полной заморозки в карте видимости означает, что все кортежи в этой странице являются замороженными; то есть никакой будущей операции очистки не понадобится модифицировать эту страницу, пока в ней не будет добавлен, изменен, удален или заблокирован кортеж. Бит PD_ALL_VISIBLE в заголовке страницы имеет то же значение, что и бит полной видимости в карте видимости, но он хранится в самой странице данных, а не в отдельной структуре данных. В большинстве случаев эти два бита будут согласованы, но бит полной видимости в странице иногда может быть задан, тогда как в карте видимости он сбрасывается при восстановлении после сбоя. Полученные значения также могут расходиться вследствие изменения, происходящего в промежутке между проверкой pg_visibility карты видимости и страницы данных. Кроме того, эти биты могут различаться из-за событий, приводящих к повреждению данных.

Функции, показывающие информацию о битах PD_ALL_VISIBLE, куда более затратны, нежели те, что обращаются только к карте видимости, так как они должны читать блоки данных отношения, а не только карту видимости (которая гораздо меньше). Функции, которые проверяют блоки данных отношения, тоже затратны.

Функции

pg_visibility_map(relation regclass, blkno bigint, all_visible OUT boolean, all_frozen OUT boolean) returns record
Возвращает биты полной видимости и полной заморозки в карте видимости для указанного блока указанного отношения.

pg_visibility(relation regclass, blkno bigint, all_visible OUT boolean, all_frozen OUT boolean, pd_all_visible OUT boolean) returns record
Возвращает биты полной видимости и полной заморозки в карте видимости для указанного блока указанного отношения, а также бит PD_ALL_VISIBLE этого блока.

pg_visibility_map(relation regclass, blkno OUT bigint, all_visible OUT boolean, all_frozen OUT boolean) returns setof record
Возвращает биты полной видимости и полной заморозки в карте видимости для всех блоков указанного отношения.

pg_visibility(relation regclass, blkno OUT bigint, all_visible OUT boolean, all_frozen OUT boolean, pd_all_visible OUT boolean) returns setof record
Возвращает биты полной видимости и полной заморозки в карте видимости для всех блоков указанного отношения, а также бит PD_ALL_VISIBLE каждого блока.

pg_visibility_map_summary(relation regclass, all_visible OUT bigint, all_frozen OUT bigint) returns record
Возвращает количество полностью видимых страниц и полностью замороженных страниц в отношении, согласно карте видимости.

pg_check_frozen(relation regclass, t_ctid OUT tid) returns setof tid
Возвращает идентификаторы TID незамороженных кортежей, хранящихся в страницах, помеченных как полностью замороженные в карте видимости. Если эти функции возвращают непустое множество TID, карта видимости повреждена.

pg_check_visible(relation regclass, t_ctid OUT tid) returns setof tid
Возвращает идентификаторы TID не полностью видимых кортежей, хранящихся в страницах, помеченных как полностью видимые в карте видимости. Если эти функции возвращают непустое множество TID, карта видимости повреждена.

pg_truncate_visibility_map(relation regclass) returns void
Усекает карту видимости для указанного отношения. Эта функция полезна, если вы полагаете, что карта видимости для этого отношения повреждена, и хотите принудительно ее восстановить. Первая же команда VACUUM, выполненная с заданным отношением после выполнения этой функции, просканирует все страницы в этом отношении и воссоздаст карту видимости. (Пока это не произойдет, запросы будут обрабатывать карту видимости как содержащую одни нули.)

По умолчанию эти функции разрешено выполнять только суперпользователям и членам роли pg_stat_scan_tables, за исключением pg_truncate_visibility_map(relation regclass), которую могут выполнять только суперпользователи.

postgres_fdw

Модуль postgres_fdw предоставляет обертку сторонних данных postgres_fdw, которую можно использовать для обращения к данным, хранящимся на внешних серверах QHB.

Функциональность, обеспечиваемая этим модулем, во многом пересекается с функциональностью старого модуля dblink. Однако postgres_fdw предоставляет более прозрачный и стандартизированный синтаксис для обращения к удаленным таблицам и во многих случаях может дать лучшую производительность.

Для подготовки к удаленному обращению через postgres_fdw:

Установите расширение postgres_fdw с помощью команды CREATE EXTENSION.

Создайте объект стороннего сервера с помощью команды CREATE SERVER, чтобы он представлял удаленную базу данных, к которой вы хотите подключиться (по одному для каждой). Задайте свойства подключения, кроме user и password, в параметрах объекта сервера.

Создайте сопоставление пользователей с помощью команды CREATE USER MAPPING для каждого пользователя базы данных, которому нужен доступ к какому-либо удаленному серверу. Укажите имя и пароль удаленного пользователя в параметрах user и password сопоставления.

Создайте стороннюю таблицу с помощью команды CREATE FOREIGN TABLE или IMPORT FOREIGN SCHEMA для каждой удаленной таблицы, к которой вы хотите обращаться. Столбцы сторонней таблицы должны соответствовать столбцам ссылочной удаленной таблицы. Однако можно использовать имена таблиц и/или столбцов, отличные от удаленных, если указать правильные удаленные имена в параметрах объекта сторонней таблицы.

Теперь для обращения к данным, хранящимся в нижележащей удаленной таблице, вам нужно только выполнять SELECT из сторонней таблицы. Вы также можете изменять удаленную таблицу, используя INSERT, UPDATE, DELETE или TRUNCATE. (Естественно, удаленный пользователь, которого вы указали в сопоставлении, должен иметь необходимые права для этого.)

Обратите внимание, что при чтении или изменении удаленной таблицы указание параметра ONLY в командах SELECT, UPDATE, DELETE или TRUNCATE не действует.

Обратите внимание, что в настоящее время в postgres_fdw не поддерживаются операторы INSERT с предложением ON CONFLICT DO UPDATE. Однако предложение ON CONFLICT DO NOTHING поддерживается при опущенном указании для извлечения уникального индекса. Также обратите внимание, что postgres_fdw поддерживает перемещение строк, вызываемое командами UPDATE, выполненными для партиционированных таблиц, однако в настоящее время это не работает в случаях, когда удаленная партиция, выбранная для добавления перемещаемой строки, также является целевой партицией для UPDATE и будет изменена в другой части той же команды.

Как правило рекомендуется объявлять столбцы сторонней таблицы с точно такими же типами данных и правилами сортировки (если применимо), как у ссылочных столбцов удаленной таблицы. Хотя postgres_fdw в настоящее время довольно лоялен к преобразованиям типов данных при необходимости, но когда типы или правила сортировки не совпадают, возможны неожиданные семантические аномалии из-за того, что удаленный сервер обрабатывает условия запроса не так, как локальный.

Обратите внимание, что стороннюю таблицу можно объявить с меньшим количеством или с другим порядком столбцов, чем в нижележащей удаленной таблице. Сопоставление столбцов удаленной таблицы производится по имени, а не по позиции.

Параметры обертки сторонних данных для postgres_fdw

Параметры подключения

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

user, password и sslpassword (их следует задавать в сопоставлениях пользователей или использовать файл служб)

client_encoding (автоматически принимается равной кодировке локального сервера)

fallback_application_name (значение всегда postgres_fdw)

sslkey и sslcert — могут находиться в свойствах соединения и/или сопоставления пользователя. Если задаются и там, и там, сопоставление пользователя переопределяет свойства соединения.

Создавать или изменять сопоставления пользователей с установленным параметром sslcert или sslkey могут только суперпользователи.

Подключаться к сторонним серверам без аутентификации по паролю могут только суперпользователи, поэтому в сопоставлениях для обычных пользователей всегда нужно задавать пароль (параметр password).

Суперпользователь может переопределить эту проверку на уровне сопоставления пользователя, установив параметр password_required 'false', например:

ALTER USER MAPPING FOR some_non_superuser SERVER loopback_nopw
OPTIONS (ADD password_required 'false');

Чтобы не позволить недоверенным пользователям злоупотреблять правами аутентификации пользователя, от имени которого работает сервер qhb, чтобы повысить свои права до суперпользователя, изменять этот параметр в сопоставлении пользователя может только суперпользователь.

Необходимо позаботиться о том, чтобы через установленное сопоставление пользователь не мог подключиться к другой базе как суперпользователь, воспользовавшись уязвимостями CVE-2007-3278 и CVE-2007-6601. Не задавайте password_required=false для роли public. Не забывайте, что сопоставляемый пользователь имеет возможность использовать любые клиентские сертификаты, файлы .pgpass, .pg_service.conf и т. п. в домашнем каталоге системного пользователя, от имени которого работает сервер qhb. Они также могут использовать любое отношение доверия, установленное режимами аутентификации, например peer или ident.

Параметры имени объекта

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

schema_name
Этот параметр, который можно указывать для сторонней таблицы, задает имя схемы для обращения к этой таблице на удаленном сервере. Если этот параметр опущен, используется схема сторонней таблицы.

table_name
Этот параметр, который можно указывать для сторонней таблицы, задает имя таблицы для обращения к этой таблице на удаленном сервере. Если этот параметр опущен, используется имя сторонней таблицы.

column_name
Этот параметр, который можно указывать для столбца сторонней таблицы, задает имя столбца для обращения к этому столбцу на удаленном сервере. Если этот параметр опущен, используется исходное имя столбца.

Параметры оценки стоимости

Обертка postgres_fdw получает удаленные данные, выполняя запросы на удаленных серверах, поэтому в идеале оценочная стоимость сканирования сторонней таблицы должна равняться стоимости выполнения на удаленном сервере плюс некоторые издержки на обмен данными. Самый надежный способ получить такую оценку — запросить стоимость у удаленного сервера, а затем добавить издержки — но для простых запросов может быть невыгодно передавать дополнительный запрос, чтобы просто получить оценку стоимости. Поэтому postgres_fdw предоставляет следующие параметры, позволяющие управлять вычислением оценки стоимости:

use_remote_estimate
Этот параметр, который можно указывать для сторонней таблицы или стороннего сервера, определяет, будет ли postgres_fdw выполнять удаленно команды EXPLAIN для получения оценок стоимости. Параметр, заданный для сторонней таблицы, переопределяет параметр сервера, но только для этой таблицы. Значение по умолчанию — false (выключено).

fdw_startup_cost
Этот параметр, который можно указывать для стороннего сервера, является значением с плавающей запятой, которое добавляется к оценочной стоимости запуска для любого сканирования сторонней таблицы на этом сервере. Он отображает дополнительные издержки на установление подключения, разбор и планирование запроса на удаленной стороне и т. п. Значение по умолчанию — 100.

fdw_tuple_cost
Этот параметр, который можно указывать для стороннего сервера, является значением с плавающей запятой, которое используется как дополнительная стоимость чтения одного кортежа при сканировании сторонней таблицы на этом сервере. Он отображает дополнительные издержки на передачу данных между серверами. Это число можно увеличить или уменьшить, чтобы отразить большую или меньшую сетевую задержку при взаимодействии с удаленным сервером. Значение по умолчанию — 0.01.

Когда параметр use_remote_estimate равен true (включено), postgres_fdw получает количество строк и оценки стоимости с удаленного сервера, а затем добавляет к оценке стоимости fdw_startup_cost и fdw_tuple_cost. Когда параметр use_remote_estimate равен false, postgres_fdw рассчитывает количество строк и оценку стоимости локально, а затем добавляет к оценке стоимости fdw_startup_cost и fdw_tuple_cost. Скорее всего, эта локальная оценка будет достаточно точной только при наличии локальных копий статистики удаленных таблиц. Обновить локальную статистику можно, выполнив для сторонней таблицы команду ANALYZE; при этом будет просканирована удаленная таблица, а затем по результату будет вычислена и сохранена статистика как для локальной таблицы. Локальное хранение статистики может быть полезно для сокращения издержек планирования запросов для удаленной таблицы — но если удаленная таблица часто меняется, локальная статистика быстро устареет.

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

По умолчанию на удаленном сервере будут обрабатываться только предложения WHERE, использующие встроенные операторы и функции. Предложения, содержащие вызовы не встроенных функций, проверяются локально после извлечения строк. Если же такие функции доступны на удаленном сервере и можно рассчитывать, что они дадут те же результаты, что и локально, производительность можно увеличить, передавая и такие предложения WHERE для удаленного выполнения. Этим поведением позволяет управлять следующий параметр:

extensions
Этот параметр является списком имен расширений QHB через запятую, которые установлены и имеют совместимые версии как на локальном, так и на удаленном сервере. Относящиеся к перечисленным расширениям и при этом неизменяемые (immutable) функции и операторы будут считаться поддающимися передаче удаленному серверу. Этот параметр можно задать только для сторонних серверов, но не для таблиц.

При использовании параметра extensions пользователь сам отвечает за то, чтобы перечисленные расширения существовали и вели себя одинаково на локальном и удаленном серверах. Иначе удаленные запросы могут выдавать ошибки или неожиданное поведение.

fetch_size
Этот параметр задает число строк, которое должна получить postgres_fdw в каждой операции выборки. Его можно задать для сторонней таблицы или стороннего сервера. Параметр, указанный для таблицы, переопределяет параметр сервера. Значение по умолчанию — 100.

batch_size
Этот параметр задает число строк, которое должна вставлять postgres_fdw в каждой операции вставки. Его можно задать для сторонней таблицы или стороннего сервера. Параметр, указанный для таблицы, переопределяет параметр сервера. Значение по умолчанию — 1.

Обратите внимание, что фактическое число строк, вставляемых postgres_fdw в одной операции, зависит от числа столбцов и предоставленного значения batch_size. Вставка пакета выполняется в одном запросе, а протокол libpq (который postgres_fdw использует для подключения к удаленному серверу) ограничивает количество параметров одного запроса числом 65535. Когда «число столбцов * batch_size» превышает этот предел, batch_size корректируется во избежание ошибки.

Параметры асинхронного выполнения

Модуль postgres_fdw поддерживает асинхронное выполнение, когда для улучшения производительности несколько частей узла Append выполняются одновременно, а не последовательно. Таким выполнением управляет следующий параметр:

async_capable
Этот параметр определяет, будет ли postgres_fdw допускать одновременное сканирование сторонних таблиц для асинхронного выполнения. Его можно задать для сторонней таблицы или стороннего сервера. Параметр, определенный на уровне таблицы, переопределяет параметр уровня сервера. Значение по умолчанию — false (запрещено).

Для гарантии того, что возвращаемые со стороннего сервера данные являются согласованными, postgres_fdw открывает только одно соединение для каждого стороннего сервера и выполняет все запросы к нему последовательно, даже если задействовано несколько сторонних таблиц (несколько соединений будет открыто, только если для этих таблиц используются разные сопоставления пользователей). Возможно, в таком случае для увеличения производительности стоит выключить этот параметр, чтобы убрать издержки, связанные с асинхронным выполнением запросов.

Асинхронное выполнение применяется, даже когда узел Append содержит как подпланы, выполняемые синхронно, так и подпланы, выполняемые асинхронно. В таких случаях, если асинхронные подпланы обрабатываются postgres_fdw, кортежи этих подпланов не возвращаются, пока хотя бы один синхронный подплан не вернет все кортежи, так как этот подплан выполняется, пока асинхронные подпланы ожидают результатов асинхронных запросов, переданных сторонним серверам. Это поведение может измениться в будущих версиях.

Параметры изменения данных

По умолчанию все сторонние таблицы, доступные через обертку postgres_fdw, считаются изменяемыми. Это можно переопределить с помощью следующего параметра:

updatable
Этот параметр, определяет, будет ли postgres_fdw допускать изменения в сторонних таблицах посредством команд INSERT, UPDATE и DELETE. Его можно задать для сторонней таблицы или стороннего сервера. Параметр, определенный на уровне таблицы, переопределяет параметр уровня сервера. Значение по умолчанию — true (изменения разрешены).

Разумеется, если удаленная таблица на самом деле не изменяемая, все равно произойдет ошибка. Использование этого параметра в первую очередь позволяет выдать ошибку локально, не посылая запрос удаленному серверу. Однако обратите внимание, что представление information_schema будет показывать, что сторонняя таблица postgres_fdw является изменяемой (или нет), согласно значению этого параметра, не проверяя это на удаленном сервере.

Параметры опустошения таблиц

По умолчанию все сторонние таблицы, доступные через обертку postgres_fdw, считаются опустошаемыми. Это можно переопределить с помощью следующего параметра:

truncatable Этот параметр, определяет, будет ли postgres_fdw допускать опустошение сторонних таблиц посредством команды TRUNCATE. Его можно задать для сторонней таблицы или стороннего сервера. Параметр, определенный на уровне таблицы, переопределяет параметр уровня сервера. Значение по умолчанию — true (опустошение разрешено).

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

Параметры импорта

Обертка postgres_fdw способна импортировать определения сторонних таблиц с помощью команды IMPORT FOREIGN SCHEMA. Эта команда создает на локальном сервере определения сторонних таблиц, которые соответствуют таблицам или представлениям, находящимся на удаленном сервере. Если импортируемые удаленные таблицы содержат столбцы пользовательских типов данных, на локальном сервере должны быть совместимые типы с теми же именами.

Поведение операции импорта можно настроить посредством следующих параметров (задаваемых в команде IMPORT FOREIGN SCHEMA):

import_collate
Этот параметр определяет, будут ли в определениях сторонних таблиц, импортируемых со стороннего сервера, включаться правила сортировки столбцов (параметр COLLATE). Значение по умолчанию — true (включено). Вам может понадобиться выключить его, если на удаленном сервере набор имен правил сортировки отличается от локального, что скорее всего произойдет, если удаленный сервер работает в другой операционной системе. Однако при отключении крайне велик риск, что правила сортировки в столбцах импортируемых таблиц не совпадут с правилами для нижележащих данных, что приведет к аномальному поведению запросов.

Даже когда этот параметр равен true, импортировать столбцы с правилом сортировки, выбираемом на удаленном сервере по умолчанию, может быть рискованно. Эти столбцы будут импортироваться со свойством COLLATE "default", в результате чего будет применяться правило сортировки, выбираемое по умолчанию на локальном сервере, которое может отличаться от выбираемого на удаленном.

import_default
Этот параметр определяет, будут ли в определениях сторонних таблиц, импортируемых со стороннего сервера, включаться заданные для столбцов выражения по умолчанию (DEFAULT). Значение по умолчанию — false (выключено). Если вы включите этот параметр, опасайтесь выражений по умолчанию, которые могут вычисляться на локальном сервере не так, как на удаленном; к примеру, nextval() является частым источником проблем. Если в импортируемом выражении по умолчанию используются функции или операторы, несуществующие локально, команда IMPORT в целом завершится неудачно.

import_generated
Этот параметр определяет, будут ли в определениях сторонних таблиц, импортируемых со стороннего сервера, включаться заданные для столбцов генерирующие выражения (GENERATED). Значение по умолчанию — true (включено). Если в импортируемом генерирующем выражении используются функции или операторы, несуществующие локально, команда IMPORT в целом завершится неудачно.

import_not_null
Этот параметр определяет, будут ли в определениях сторонних таблиц, импортируемых со стороннего сервера, включаться заданные для столбцов ограничения NOT NULL. Значение по умолчанию — true (включено).

Обратите внимание, что ограничения, отличные от NOT NULL, из удаленных таблиц никогда импортироваться не будут. Хотя QHB и поддерживает проверочные ограничения для сторонних таблиц, средства для их автоматического импорта отсутствуют из-за риска, что выражение ограничения на локальном и удаленном серверах может вычисляться по-разному. Любая такая несогласованность в поведении проверочных ограничений может привести к сложно выявляемым ошибкам в оптимизации запросов. Поэтому если вы хотите импортировать проверочные ограничения, вам следует сделать это вручную и нужно будет внимательно проверить семантику каждого. Более подробную информацию об интерпретации проверочных ограничений для сторонних таблиц см. в CREATE FOREIGN TABLE.

Таблицы или сторонние таблицы, являющиеся партициями некоторой другой таблицы, импортируются, только когда они явно указаны в предложении LIMIT TO. В противном случае они автоматически исключаются из команды IMPORT FOREIGN SCHEMA. Поскольку все данные могут быть доступны через корневую партиционированную таблицу в иерархии партиционирования, должно быть достаточно импортировать только партиционированные таблицы, чтобы получить доступ ко всем данным, не создавая лишние объекты.

Параметры управления соединением

По умолчанию все соединения, устанавливаемые postgres_fdw со сторонними серверами, остаются открытыми в локальном сеансе для повторного использования.

keep_connections
Этот параметр определяет, будет ли postgres_fdw оставлять открытыми соединения со сторонним сервером, чтобы последующие запросы могли повторно их использовать. Этот параметр можно задать только для стороннего сервера. Значение по умолчанию — on (включено). При значении off все соединения к этому стороннему серверу будут закрываться в конце каждой транзакции.

Функции

postgres_fdw_get_connections(OUT server_name text, OUT valid boolean) returns setof record

Эта функция возвращает имена всех сторонних серверов, с которыми postgres_fdw установила соединения в локальном сеансе. Также она возвращает признак допустимого соединения. Признак false возвращается, если это соединение к стороннему серверу используется в текущей локальной транзакции, но определение стороннего сервера или сопоставление пользователя были изменены или удалены (обратите внимание, что если определение сервера удалено, имя сервера в записи недопустимого соединения будет NULL). Такое недопустимое соединение будет закрыто в конце этой транзакции. Во всех остальных случаях возвращается true (допустимое соединение). Если открытых соединений нет, никакая запись не возвращается. Пример использования этой функции:

postgres=# SELECT * FROM postgres_fdw_get_connections() ORDER BY 1;
 server_name | valid
-------------+-------
 loopback1   | t
 loopback2   | f

postgres_fdw_disconnect(server_name text) returns boolean

Эта функция закрывает открытые соединения, установленные postgres_fdw со сторонним сервером с заданным именем в локальном сеансе. Обратите внимание, что с данным сервером может быть установлено несколько соединений, использующих разные сопоставления пользователей. Если какие-либо соединения используются в текущей локальной транзакции, они не разрываются, о чем выдаются предупреждающие сообщения. Эта функция возвращает true, если закрывает хотя бы одно соединение; в противном случае возвращается false. Если сторонний сервер с заданным именем не найден, выдается ошибка. Пример использования этой функции:

postgres=# SELECT postgres_fdw_disconnect('loopback1');
 postgres_fdw_disconnect
-------------------------
 t

postgres_fdw_disconnect_all() returns boolean

Эта функция закрывает все открытые соединения, установленные postgres_fdw со сторонними серверами в локальном сеансе. Если какие-либо соединения используются в текущей локальной транзакции, они не разрываются, о чем выдаются предупреждающие сообщения. Эта функция возвращает true, если закрывает хотя бы одно соединение; в противном случае возвращается false. Пример использования этой функции:

postgres=# SELECT postgres_fdw_disconnect_all();
 postgres_fdw_disconnect_all
-----------------------------
 t

Управление соединением

Модуль postgres_fdw устанавливает соединение со сторонним сервером при первом запросе, использующем стороннюю таблицу, связанную со сторонним сервером. По умолчанию это соединение сохраняется и повторно используется для последующих запросов в том же сеансе. Этим поведением можно управлять с помощью параметра стороннего сервера keep_connections. Если для обращения к стороннему серверу задействуется несколько пользователей (сопоставлений пользователей), для каждого сопоставления пользователей устанавливается отдельное соединение.

При изменении или удалении определения стороннего сервера или сопоставления пользователя, связанные с ними соединения закрываются. Но обратите внимание, что если какие-либо соединения используются в текущей локальной транзакции, они сохраняются до конца этой транзакции. Когда закрытые соединения понадобятся для будущих запросов, использующих сторонние таблицы, они будут установлены заново.

Установленное соединение со сторонним сервером по умолчанию сохраняется до конца локального или соответствующего удаленного сеанса. Чтобы явно закрыть соединение, можно отключить параметр стороннего сервера keep_connections или воспользоваться функциями postgres_fdw_disconnect и postgres_fdw_disconnect_all. Например, эти функции полезны тем, что закрывают уже ненужные соединения, тем самым освобождая подключения на стороннем сервере.

Управление транзакциями

В процессе выполнения запроса, содержащего ссылки на какие-либо удаленные таблицы на стороннем сервере, postgres_fdw открывает транзакцию на удаленном сервере, если еще не была открыта такая транзакция, соответствующая текущей локальной транзакции. Удаленная транзакция фиксируется или прерывается, когда фиксируется или прерывается локальная транзакция. Подобным образом (созданием соответствующих удаленных точек) реализуется и управление точками сохранения.

Для удаленной транзакции используется уровень изоляции SERIALIZABLE, когда локальная транзакция открыта с уровнем SERIALIZABLE; в противном случае применяется уровень REPEATABLE READ. Такой выбор гарантирует, что если запрос сканирует несколько таблиц на удаленном сервере, он будет получать согласованные со снимком результаты для всех сканирований. Как следствие, последовательные запросы в рамках одной транзакции будут видеть одинаковые данные удаленного сервера, даже если на нем происходят параллельные изменения, вызванные другими действиями. Это поведение в любом случае ожидаемо, если для локальной транзакции используется уровень изоляции SERIALIZABLE или REPEATABLE READ, но для локальной транзакции с уровнем READ COMMITTED оно может быть неожиданным. В будущих выпусках QHB эти правила могут быть изменены.

Обратите внимание, что подготовку удаленной транзакции для двухфазной фиксации postgres_fdw в настоящее время не поддерживает.

Оптимизация удаленных запросов

Обертка postgres_fdw пытается оптимизировать удаленные запросы, чтобы уменьшить объем данных, пересылаемых сторонними серверами. Для этого она передает на выполнение удаленному серверу предложения WHERE и не получает столбцы таблицы, которые не требуются для текущего запроса. Чтобы снизить риск неправильного выполнения запросов, предложения WHERE передаются удаленному серверу, только если в них используются типы данных, операторы и функции, встроенные в ядро или принадлежащие расширениям, перечисленным в параметре extensions удаленного сервера. Операторы и функции в таких предложениях также должны быть неизменяемыми (IMMUTABLE). Для запросов UPDATE или DELETE postgres_fdw пытается оптимизировать выполнение, передавая удаленному серверу весь запрос, если в нем нет предложений WHERE, которые нельзя передать удаленному серверу, для запроса не выполняется локальное соединение, в целевой таблице отсутствуют локальные триггеры BEFORE или AFTER уровня строки или хранимые генерируемые столбцы, а в родительских представлениях нет ограничения CHECK OPTION. Кроме того, в запросах UPDATE выражения, присваиваемые целевым столбцам, должны использовать только встроенные типы данных и неизменяемые (IMMUTABLE) операторы и функции, чтобы снизить риск неправильного выполнения запроса.

Когда postgres_fdw обнаруживает соединение сторонних таблиц на одном стороннем сервере, она передает все это соединение стороннему серверу, если только по какой-то причине не решит, что будет эффективнее выбирать строки из каждой таблицы по отдельности, или если задействованные ссылки на таблицы относятся к разным сопоставлениям пользователей. При передаче предложений JOIN она принимает те же меры предосторожности, что и описанные выше для предложений WHERE.

Запрос, фактически передаваемый стороннему серверу для выполнения, можно проверить с помощью команды EXPLAIN VERBOSE.

Среда удаленного выполнения запросов

В удаленных сеансах, установленных postgres_fdw, в параметре search_path задается только pg_catalog, поэтому без указания схемы видны только встроенные объекты. Это не проблема для запросов, генерируемых самой postgres_fdw, поскольку она всегда добавляет такое указание. Однако это может представлять опасность для функций, которые выполняются на удаленном сервере при срабатывании триггеров или правил для удаленных таблиц. К примеру, если удаленная таблица на самом деле является представлением, все функции, используемые в этом представлении, будут выполняться с таким ограниченным путем поиска. Поэтому в таких функциях рекомендуется дополнять все имена схемой или добавлять параметры SET search_path (см. CREATE FUNCTION), чтобы установить ожидаемую ими среду пути поиска.

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

TimeZone — UTC

DateStyle — ISO

IntervalStyle — postgres

extra_float_digits — 3

С ними проблемы менее вероятны, чем с search_path, но если они возникнут, их можно решить, задав нужные значения в параметре SET.

Это поведение не рекомендуется переопределять, меняя значения этих параметров на уровне сеанса; скорее всего, это приведет к нарушениям в работе postgres_fdw.

Совместимость с разными версиями

Модуль postgres_fdw полагает, что неизменяемые встроенные функции и операторы могут безопасно передаваться на удаленный сервер для выполнения, если они фигурируют в предложении WHERE для сторонней таблицы. Как следствие, на выполнение может быть отправлена встроенная функция, добавленная в более новой версии, чем на удаленном сервере, что в результате приведет к ошибке «функция не существует» или ей подобной. Отказы такого типа можно предотвратить, переписав запрос, например, поместив ссылку на стороннюю таблицу во вложенный SELECT с OFFSET 0 в качестве защиты от оптимизации и применив проблематичную функцию или оператор снаружи этого вложенного SELECT.

Примеры

Ниже приведен пример создания сторонней таблицы при помощи postgres_fdw. Сначала установите расширение:

CREATE EXTENSION postgres_fdw;

Затем создайте сторонний сервер командой CREATE SERVER. В данном примере мы хотим подключиться к серверу QHB на хосте 192.83.123.89, порт 5432. База данных, с которой устанавливается соединение, на удаленном сервере называется foreign_db:

CREATE SERVER foreign_server
        FOREIGN DATA WRAPPER postgres_fdw
        OPTIONS (host '192.83.123.89', port '5432', dbname 'foreign_db');

Также для определения роли, которая будет задействована на удаленном сервере, требуется сопоставление пользователей, задаваемое командой CREATE USER MAPPING:

CREATE USER MAPPING FOR local_user
        SERVER foreign_server
        OPTIONS (user 'foreign_user', password 'password');

Теперь можно создать стороннюю таблицу с помощью команды CREATE FOREIGN TABLE. В данном примере мы ходим обратиться к таблице, которая на удаленном сервере называется some_schema.some_table. Ее локальным именем будет foreign_table:

CREATE FOREIGN TABLE foreign_table (
        id integer NOT NULL,
        data text
)
        SERVER foreign_server
        OPTIONS (schema_name 'some_schema', table_name 'some_table');

Важно, чтобы типы данных и другие свойства столбцов, объявленных в CREATE FOREIGN TABLE, соответствовали фактической удаленной таблице. Также должны соответствовать имена столбцов, если только вы не добавите параметры column_name для отдельных столбцов, показывающие их имена в удаленной таблице. Во многих случаях использовать IMPORT FOREIGN SCHEMA предпочтительнее, чем конструировать определения сторонних таблиц вручную.

Автор

Шигеру Ханада (shigeru.hanada@gmail.com)

seg

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

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Обоснование

Геометрия измерений обычно более сложна, чем точка в числовом континууме. Как правило, измерение представляет собой отрезок этого континуума с несколько размытыми границами. Эти измерения выводятся в виде отрезков вследствие неопределенности и случайности, а также того, что измеряемое значение может по своей природе быть интервалом, указывающим на некое условие, например, диапазон температур стабильности белка.

Исходя только из здравого смысла, кажется более удобным хранить такие данные в виде интервалов, а не пары чисел. На практике это оказывается еще эффективнее в большинстве приложений.

Если и далее рассуждать здраво, размытость границ предполагает, что использование традиционных числовых типов данных приводит к определенной потере информации. Рассмотрим такой пример: ваш инструмент выдает 6.50, и вы вводите это значение в базу данных. Что вы получите, выбрав его из базы? Смотрите:

test=> SELECT 6.50 :: float8 AS "pH";
 pH
---
6.5
(1 row)

В мире измерений 6.50 — это не то же самое, что 6.5. Разница между этими измерениями иногда может быть критической. Экспериментаторы обычно записывают (и публикуют) цифры, в которых уверены. Запись 6.50 на самом деле представляет собой нечеткий интервал, содержащийся внутри большего и еще более нечеткого интервала 6.5, и единственное, что у них может быть общего, — это их центральные точки. Мы определенно не хотим, чтобы такие разные элементы данных выглядели одинаково.

Вывод? Удобно иметь специальный тип данных, в котором можно сохранить границы интервала с произвольной переменной точностью. (Здесь под переменной точностью подразумевается, что для каждого элемента данных она записывается индивидуально.)

Проверьте это:

test=> SELECT '6.25 .. 6.50'::seg AS "pH";
          pH
------------
6.25 .. 6.50
(1 row)

Синтаксис

Внешнее представление интервала образуется одним или двумя числами с плавающей запятой, соединенными оператором диапазона (.. или ...). Как вариант, интервал можно задать центральной точкой плюс/минус отклонение. Также этот тип позволяет сохранить дополнительные индикаторы достоверности (<, > или ~). (Однако индикаторы достоверности игнорируются всеми встроенными операторами.) В Таблице 27 дается обзор допустимых представлений; в Таблице 28 приводятся некоторые примеры.

В Таблице 27 символы x, y и delta обозначают числа с плавающей запятой. Перед значениями x и y, но не delta, может быть добавлен индикатор достоверности.

Таблица 27. Внешние представления seg

Представление | Описание
x | Одно значение (интервал нулевой длины)
x .. y | Интервал от x до y
x (+-) delta | Интервал от x - delta до x + delta
x .. | Открытый интервал с нижней границей x
.. x | Открытый интервал с верхней границей x

Таблица 28. Примеры допустимых вводимых значений seg

Пример | Что делает
5.0 | Создает сегмент нулевой длины (или точку, если хотите).
~5.0 | Создает сегмент нулевой длины и записывает ~ в данные. Знак ~ игнорируется при операциях с seg, но сохраняется как комментарий.
<5.0 | Создает точку с координатой 5.0. Знак < игнорируется, но сохраняется как комментарий.
>5.0 | Создает точку с координатой 5.0. Знак > игнорируется, но сохраняется как комментарий.
5(+-)0.3 | Создает интервал 4.7 .. 5.3. Обратите внимание, что запись (+-) не сохраняется.
50 .. | Все, что больше или равно 50.
.. 0 | Все, что меньше или равно 0.
1.5e-2 .. 2E-2 | Создает интервал 0.015 .. 0.02.
1 ... 2 | То же, что и 1...2, или 1 .. 2, или 1..2 (пробелы вокруг оператора диапазона игнорируются).

Поскольку оператор «...» широко используется в источниках данных, он принимается в качестве альтернативного написания оператора «..». К сожалению, это порождает неоднозначность при разборе: неясно, какая верхняя граница подразумевается в записи 0...23 — 23 или 0.23. Для разрешения этой неоднозначности во входных числах seg перед десятичной точкой всегда должна быть как минимум одна цифра.

В целях контроля корректности seg не принимает интервалы с нижней границей, превышающей верхнюю, например 5 .. 2.

Точность

Значения seg хранятся внутри как пары 32-битных чисел с плавающей запятой. Это значит, что числа с более чем 7 значащими цифрами будут усекаться.

Числа, содержащие 7 значащих цифр и меньше, сохраняют изначальную точность. То есть, если запрос возвращает 0.00, вы можете быть уверены, что конечные нули не являются артефактами форматирования: они отражают точность исходных данных. Количество ведущих нулей не влияет на точность: значение 0.0067 считается имеющим только 2 значащие цифры.

Применение

Модуль seg включает класс операторов индекса GiST для значений seg. Операторы, поддерживаемые этим классом операторов, перечислены в Таблице 29.

Таблица 29. Операторы seg для GiST

Оператор | Описание
seg << seg → boolean | Первый seg полностью находится левее второго? [a, b] << [c, d] — true, если b < c.
seg >> seg → boolean | Первый seg полностью находится правее второго? [a, b] >> [c, d] — true, если a > d.
seg &< seg → boolean | Первый seg не простирается правее второго? [a, b] &< [c, d] — true, если b <= d.
seg &> seg → boolean | Первый seg не простирается левее второго? [a, b] &> [c, d] — true, если a >= c.
seg = seg → boolean | Два интервала seg равны?
seg && seg → boolean | Два интервала seg пересекаются?
seg @> seg → boolean | Первый интервал seg содержит второй?
seg <@ seg → boolean | Первый интервал seg содержится во втором?

Помимо вышеописанных операторов для типа seg доступны обычные операторы сравнения, показанные в таблице Функции и операторы сравнения. Эти операторы сначала сравнивают (a) с (c), и если они равны, сравнивают (b) с (d). В большинстве случаев это приводит к адекватному упорядочиванию, что полезно, если нужно применить ORDER BY с этим типом.

Примечания

Примеры использования можно увидеть в регрессионном тесте sql/seg.sql.

Механизм, преобразующий (+-) в обычные диапазоны, не вполне точно определяет число значащих цифр для границ. К примеру, он добавляет дополнительную цифру к нижней границе, если результирующий интервал включает степень десяти:

postgres=> SELECT '10(+-)1'::seg AS seg;
      seg
---------
9.0 .. 11             -- должно быть: 9 .. 11

Производительность индекса R-дерева может значительно зависеть от начального порядка вводимых значений. Может быть очень полезно отсортировать входную таблицу по столбцу seg; пример можно найти в скрипте sort-segments.pl.

Благодарности

Оригинальный автор: Джин Селков мл. (selkovjr@mcs.anl.gov), Аргоннская национальная лаборатория, Отдел математики и компьютерных наук.

Я благодарен в первую очередь профессору Джо Геллерштейну (https://dsf.berkeley.edu/jmh/) за пояснение сути GiST (http://gist.cs.berkeley.edu/). Я также признателен всем разработчикам Postgres в настоящем и прошлом за возможность создать мой собственный мир и спокойно жить в нем. Еще я хотел бы выразить признательность Аргоннской лаборатории и Министерству энергетики США за годы постоянной поддержки моих исследований в области баз данных.

sepgsql

sepgsql — это загружаемый модуль, который поддерживает мандатное управление доступом (Mandatory access control, MAC) с использованием меток, работающее на основе политик безопасности SELinux.

Предупреждение
Текущая реализация имеет существенные ограничения и применяет мандатное управление доступом не для всех действий. См. подраздел Ограничения.

Обзор

Этот модуль интегрируется в SELinux, чтобы обеспечить дополнительный уровень проверок безопасности, превышающий те, что предоставляются обычными средствами QHB. С точки зрения SELinux, этот модуль позволяет QHB выполнять роль менеджера объектов в пространстве пользователя. Обращение к каждой таблице или функции, инициируемое запросом DML, будет проверено согласно системной политике безопасности. Эта проверка дополняет обычную проверку разрешений SQL, осуществляемую QHB.

SELinux принимает решения о разрешении доступа на основе меток безопасности, представляемых строками вида system_u:object_r:sepgsql_table_t:s0. В каждом решении учитываются две метки: метка субъекта, пытающегося выполнить действие, и метка объекта, над которым должна совершиться эта операция. Поскольку эти метки могут применяться к объекту любого вида, решения о разрешении доступа к объектам внутри базы данных могут подчиняться (и с этим модулем подчиняются) тем же общим критериям, которые применяются к объектам любого другого типа, например, к файлам. Эта конструкция призвана дать возможность организовать централизованную политику безопасности для защиты информационных активов, не зависящую от того, как именно хранятся эти активы.

Назначить метку безопасности объекту базы данных позволяет команда SECURITY LABEL.

Установка

Модуль sepgsql можно использовать только в Linux 2.6.28 или новее с включенным SELinux. На остальных платформах он недоступен. Также вам понадобится libselinux 2.1.10 или новее и selinux-policy 3.9.13 или новее (хотя в некоторых дистрибутивах можно бэкпортировать необходимые правила к политике старой версии).

Команда sestatus позволяет проверить состояние SELinux. Типичное ее отображение выглядит так:

$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /selinux
Current mode:                   enforcing
Mode from config file:          enforcing
Policy version:                 24
Policy from config file:        targeted

Если SELinux выключен или не установлен, его необходимо настроить, прежде чем устанавливать этот модуль.

Чтобы собрать этот модуль, добавьте параметр --with-selinux в команду QHB configure. Убедитесь в том, что на момент сборки установлен RPM-пакет libselinux-devel.

Для использования этого модуля следует добавить sepgsql в параметр shared_preload_libraries в qhb.conf. Этот модуль не будет корректно работать если загрузить его каким-либо иным способом. Когда он загрузится, нужно выполнить sepgsql.sql в каждой базе данных. Этот скрипт установит функции, необходимые для управления метками безопасности, и назначит начальные метки безопасности.

Ниже приведен пример, показывающий, как инициализировать новый кластер баз данных с установленными функциями и метками безопасности sepgsql. Измените показанные пути в соответствии размещением вашей инсталляции:

$ export PGDATA=/path/to/data/directory
$ initdb
$ vi $PGDATA/qhb.conf
  изменить
    #shared_preload_libraries = ''                # (после изменения требуется перезапуск)
  на
    shared_preload_libraries = 'sepgsql'          # (после изменения требуется перезапуск)
$ for DBNAME in template0 template1 postgres; do
    postgres --single -F -c exit_on_error=true $DBNAME \
      </usr/local/pgsql/share/contrib/sepgsql.sql >/dev/null
  done

Пожалуйста, обратите внимание, что вы можете увидеть следующие уведомления, в зависимости от конкретных установленных версий libselinux и selinux-policy:

/etc/selinux/targeted/contexts/sepgsql_contexts:  line 33 has invalid object type db_blobs
/etc/selinux/targeted/contexts/sepgsql_contexts:  line 36 has invalid object type db_language
/etc/selinux/targeted/contexts/sepgsql_contexts:  line 37 has invalid object type db_language
/etc/selinux/targeted/contexts/sepgsql_contexts:  line 38 has invalid object type db_language
/etc/selinux/targeted/contexts/sepgsql_contexts:  line 39 has invalid object type db_language
/etc/selinux/targeted/contexts/sepgsql_contexts:  line 40 has invalid object type db_language

Эти сообщения безвредны и их следует игнорировать.

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

Регрессионные тесты

Учитывая природу SELinux, для проведения регрессионных тестов sepgsql требуется несколько дополнительных этапов конфигурирования, некоторые из которых должен выполнять root. Эти регрессионные тесты не будут запускаться обычной командой make check или make installcheck; необходимо настроить конфигурацию, а затем вызвать тестовый скрипт вручную. Тесты должны запускаться в каталоге contrib/sepgsql сконфигурированного дерева сборки QHB. Несмотря на то, что им требуется дерево сборки, эти тесты рассчитаны на выполнение на установленном сервере, то есть они сравнимы с make installcheck, но не с make check.

Сначала установите sepgsql в рабочую базу данных согласно инструкциям, приведенным в подразделе Установка. Обратите внимание, что для этого текущий пользователь операционной системы должен иметь возможность подключаться к базе данных как суперпользователь без аутентификации по паролю.

На втором этапе соберите и установите пакет политики для регрессионного теста. Политика sepgsql-regtest представляет собой политику особого назначения, которая предоставляет набор правил, включаемых во время регрессионных тестов. Ее нужно собрать из исходного файла политики sepgsql-regtest.te, что осуществляется с помощью команды make со скриптом Makefile, предоставляемым SELinux. Вам понадобится найти подходящий Makefile в своей системе; путь, показанный ниже, является всего лишь примером. (Этот Makefile обычно поставляется в пакете RPM selinux-policy-devel или selinux-policy.) Собрав пакет политики, установите его с помощью команды semodule, которая загружает предоставленные пакеты в ядро. Если пакет установлен корректно, команда semodule -l должна показать sepgsql-regtest в списке доступных пакетов политик:

$ cd .../contrib/sepgsql
$ make -f /usr/share/selinux/devel/Makefile
$ sudo semodule -u sepgsql-regtest.pp
$ sudo semodule -l | grep sepgsql
sepgsql-regtest 1.07

На третьем этапе включите параметр sepgsql_regression_test_mode. По соображениям безопасности правила в sepgsql-regtest по умолчанию выключены; параметр sepgsql_regression_test_mode включает правила, необходимые для запуска регрессионных тестов. Включить этот параметр можно командой setsebool:

$ sudo setsebool sepgsql_regression_test_mode on
$ getsebool sepgsql_regression_test_mode
sepgsql_regression_test_mode --> on

На четвертом этапе убедитесь, что ваша оболочка работает в домене unconfined_t:

$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

Если необходимо сменить рабочий домен, подробную информацию об этом см. в подразделе Внешние ресурсы.

Наконец запустите скрипт регрессионного теста:

$ ./test_sepgsql

Этот скрипт попытается проверить, все ли этапы конфигурирования были выполнены корректно, а затем запустит регрессионные тесты для модуля sepgsql.

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

$ sudo setsebool sepgsql_regression_test_mode off

Возможно, более предпочтительным будет полностью удалить политику sepgsql-regtest:

$ sudo semodule -r sepgsql-regtest

Параметры GUC

sepgsql.permissive (boolean)

Этот параметр позволяет sepgsql работать в разрешительном режиме, независимо от настроек системы. Значение по умолчанию — off (выключено). Этот параметр можно задать только в файле qhb.conf или в командной строке сервера.

Когда этот параметр включен, sepgsql функционирует в разрешительном режиме, даже если SELinux в целом работает в ограничительном режиме. Этот параметр полезен в первую очередь для тестирования.

sepgsql.debug_audit (boolean)

Этот параметр включает вывод сообщений аудита, независимо от настроек системной политики. Значение по умолчанию — off, означающее, что сообщения будут выводиться согласно настройкам системы.

Политика безопасности SELinux также содержит правила, определяющие, будут ли протоколироваться в журнал определенные обращения. По умолчанию фиксируются нарушения доступа, а успешный доступ — нет.

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

Функциональные возможности

Управляемые классы объектов

Модель безопасности SELinux описывает все правила управления доступом как отношения между сущностью субъекта (обычно это клиент базы данных) и сущностью объекта (например, объектом базы данных), каждая из которых определяется меткой безопасности. Если предпринимается попытка доступа к непомеченному объекту, он обрабатывается как объект с меткой unlabeled_t.

В настоящее время sepgsql позволяет назначать метки безопасности схемам, таблицам, столбцам, последовательностям, представлениям и функциям. При использовании sepgsql метки безопасности автоматически назначаются поддерживаемым объектом базы данных в момент создания. Такая метка называется меткой безопасности по умолчанию и устанавливается согласно системной политике безопасности, которая принимает на вход метку создателя, метку, назначенную родительскому объекту нового объекта и, при необходимости, имя создаваемого объекта.

Новый объект базы данных обычно наследует метку безопасности родительского объекта, за исключением случаев, когда в политике безопасности установлены специальные правила, известные как правила перехода типов, — тогда может быть назначена другая метка. Для схем родительским объектом является текущая база данных; для таблиц, последовательностей, представлений и функций — схемы, содержащие эти объекты; для столбцов — содержащие их таблицы.

Разрешения для DML

Для всех ссылочных целевых таблиц проверяются разрешения db_table:select, db_table:insert, db_table:update или db_table:delete, в зависимости от вида оператора; кроме того, для всех таблиц, содержащих столбцы, фигурирующие в предложении WHERE или RETURNING, или служащих источником данных для UPDATE и т. п., также проверяется разрешение db_table:select.

Для всех ссылочных столбцов также проверяются разрешения уровня столбцов. Разрешение db_column:select проверяется не только для столбцов, которые считываются оператором SELECT, но и для тех, к которым обращаются другие операторы DML; для столбцов, изменяемых оператором UPDATE или INSERT, также проверяется разрешение db_column:update или db_column:insert соответственно.

Например, рассмотрим запрос:

UPDATE t1 SET x = 2, y = func1(y) WHERE z = 100;

В данном примере разрешение db_column:update будет проверяться для столбца t1.x, так как он изменяется, db_column:{select update} — для t1.y, так как он и изменяется, и считывается, а db_column:select — для t1.z, так как он только считывается. Также на уровне таблицы будет проверяться разрешение db_table:{select update}.

Для последовательностей проверяется разрешение db_sequence:get_value при обращении к объекту последовательности с помощью оператора SELECT; однако обратите внимание, что в настоящее время разрешения на выполнение связанных функций, например lastval(), не проверяются.

Для представлений будет проверяться разрешение db_view:expand, а затем все остальные требуемые разрешения для объектов, развернутых из этого представления, по отдельности.

Для функций будет проверяться разрешение db_procedure:{execute}, когда пользователь попытается выполнить функцию в составе запроса, или при вызове по быстрому пути. Если эта функция является доверенной процедурой, также проверяется разрешение db_procedure:{entrypoint}, чтобы определить, может ли эта функция выступать точкой входа в доверенную процедуру.

Для обращения к любому объекту схемы необходимо разрешение db_schema:search для содержащей его схемы. Когда имя ссылочного объекта не дополняется схемой, схемы, для которых это разрешение отсутствует, не будут просматриваться (то же самое происходит, если у пользователя нет права USAGE для этой схемы). Если имеется явное указание схемы, но у пользователя нет требуемого разрешения для именованной схемы, произойдет ошибка.

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

Система прав базы данных по умолчанию позволяет суперпользователям этой базы изменять системные каталоги с помощью команд DML и обращаться к TOAST-таблицам или модифицировать их. Когда модуль sepgsql активен, эти операции запрещаются.

Разрешения для DDL

SELinux определяет несколько разрешений для управления стандартными операциями для каждого типа объекта, в частности, созданием, изменением определения, удалением и сменой метки безопасности. Кроме того, для некоторых типов объектов имеются специальные разрешения, управляющие их специфическими операциями, в частности добавлением или удалением элементов в определенной схеме.

Для создания нового объекта базы данных требуется разрешение create. SELinux выдает или отказывает в выдаче этого разрешения в зависимости от метки безопасности клиента и предполагаемой метки безопасности нового объекта. В некоторых случаях требуются дополнительные разрешения:

Для CREATE DATABASE дополнительно требуется разрешение getattr в исходной или шаблонной базе данных.

Для создания объекта схемы дополнительно требуется разрешение add_name в родительской схеме.

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

Для создания функции с атрибутом LEAKPROOF дополнительно требуется разрешение install. (Это разрешение также проверяется, когда атрибут LEAKPROOF устанавливается для существующей функции.)

Когда выполняется команда DROP, для удаляемого объекта будет проверяться разрешение drop. Также разрешения будут проверяться для объектов, удаляемых неявно посредством указания CASCADE. Для удаления объектов, содержащихся в определенной схеме (таблиц, представлений, последовательностей и процедур) дополнительно требуется разрешение remove_name в этой схеме.

Когда выполняется команда ALTER, для каждого типа модифицируемого объекта будет проверяться разрешение setattr, за исключением подчиненных объектов, таких как индексы или триггеры таблицы, для которых вместо этого проверяется разрешение родительского объекта. В некоторых случаях требуются дополнительные разрешения:

Для перемещения объекта в новую схему дополнительно требуется разрешение remove_name в старой схеме и add_name в новой.

Для установки атрибута LEAKPROOF для функции требуется разрешение install.

Для использования SECURITY LABEL на объекте дополнительно требуется разрешение relabelfrom для этого объекта в связке со старой меткой безопасности и relabelto для этого же объекта в связке с новой меткой безопасности. (В тех случаях, когда установлено несколько поставщиков меток и пользователь пытается задать метку безопасности, неподконтрольную SELinux, должно проверяться только разрешение setattr. В настоящее время этого не происходит из-за ограничений реализации.)

Доверенные процедуры

Доверенные процедуры схожи с функциями, определяющими контекст безопасности, или командами setuid. SELinux предоставляет возможность запускать доверенный код с меткой безопасности, отличной от метки клиента, обычно с целью предоставления тщательно контролируемого доступа к важным данным (при этом, к примеру, могут отбрасываться строки или может быть снижена точность хранимых значений). Будет ли функция действовать как доверенная процедура, определяется ее меткой безопасности и политикой безопасности операционной системы. Например:

postgres=# CREATE TABLE customer (
               cid     int primary key,
               cname   text,
               credit  text
           );
CREATE TABLE
postgres=# SECURITY LABEL ON COLUMN customer.credit
               IS 'system_u:object_r:sepgsql_secret_table_t:s0';
SECURITY LABEL
postgres=# CREATE FUNCTION show_credit(int) RETURNS text
             AS 'SELECT regexp_replace(credit, ''-[0-9]+$'', ''-xxxx'', ''g'')
                        FROM customer WHERE cid = $1'
           LANGUAGE sql;
CREATE FUNCTION
postgres=# SECURITY LABEL ON FUNCTION show_credit(int)
               IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
SECURITY LABEL

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

postgres=# SELECT * FROM customer;
ERROR:  SELinux: security policy violation
postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
 cid | cname  |     show_credit
-----+--------+---------------------
   1 | taro   | 1111-2222-3333-xxxx
   2 | hanako | 5555-6666-7777-xxxx
(2 rows)

В данном случае обычный пользователь не может обращаться к customer.credit напрямую, но доверенная процедура show_credit позволяет ему получить номера кредитных карт клиентов, в которых скрыты некоторые цифры.

Динамические переключения домена

Функциональную возможность динамического переключения между доменами SELinux можно использовать для перевода метки безопасности клиентского процесса, клиентский домен, в новый контекст, если это допускается политикой безопасности. Для этого клиентскому домену нужно разрешение setcurrent, а также разрешение dyntransition для перехода из старого в новый домен.

Динамические переключения домена следует тщательно продумывать, поскольку они позволяют пользователям менять свои метки, а значит и права, по собственному желанию, а не согласно требованиям системы (как в случае с доверенными процедурами). Поэтому разрешение dyntransition считается безопасным, только когда используется для перехода в домен с меньшим набором прав, чем исходный. Например:

regression=# select sepgsql_getcon();
                    sepgsql_getcon
-------------------------------------------------------
 unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
(1 row)

regression=# SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0-s0:c1.c4');
 sepgsql_setcon
----------------
 t
(1 row)

regression=# SELECT sepgsql_setcon('unconfined_u:unconfined_r:unconfined_t:s0-s0:c1.c1023');
ERROR:  SELinux: security policy violation

В этом примере нам позволили переключиться из более широкого диапазона MCS c1.c1023 в более узкий c1.c4, но в обратном переключении было отказано.

Комбинация динамического переключение домена и доверенной процедуры открывает интересный сценарий использования, который подходит жизненному циклу типичных процессов менеджера пула соединений. Даже если менеджеру пула соединений не разрешено запускать большинство команд SQL, ему можно разрешить сменить метку безопасности клиента с помощью функции sepgsql_setcon() из доверенной процедуры; для авторизации этого запроса наверняка понадобится удостоверение защиты. После этого сеанс получит права целевого пользователя, а не пользователя пула соединений. Позднее пользователь пула соединений может отменить смену метки безопасности, снова воспользовавшись функцией sepgsql_setcon() с аргументом NULL, также вызванной из доверенной процедуры с требуемыми проверками разрешений. Смысл здесь в том, что только у этой доверенной процедуры есть разрешение менять действующую метку безопасности и только тогда, когда получает правильные подтверждения авторизации. Естественно, чтобы такая операция была безопасной, хранилище удостоверений защиты (таблица, определение процедуры или что-то другое) должно быть защищено от несанкционированного доступа.

Прочее

Выполнение команды LOAD полностью запрещено, так как любой загруженный модуль может легко обойти установки политики безопасности.

Функции sepgsql

В Таблице 30 перечислены доступные функции.

Таблица 30. Функции sepgsql

Функция | Описание
sepgsql_getcon () → text | Возвращает клиентский домен, текущую метку безопасности клиента.
sepgsql_setcon ( text ) → boolean | Переключает клиентский домен текущего сеанса в новый домен, если это допускает политика безопасности. Также принимает в аргументе NULL в качестве запроса на переход в исходный домен клиента.
sepgsql_mcstrans_in ( text ) → text | Переводит заданный диапазон MLS/MCS из полной записи в низкоуровневый формат, если работает демон mcstrans.
sepgsql_mcstrans_out ( text ) → text | Переводит заданный диапазон MLS/MCS из низкоуровневого формата в полную запись, если работает демон mcstrans.
sepgsql_restorecon ( text ) → boolean | Устанавливает начальные метки безопасности для всех объектов в текущей базе данных. В аргументе может передаваться NULL или имя файла спецификации для использования вместо стандартного системного.

Ограничения

Разрешения для языка определения данных (Data Definition Language, DDL)

Из-за ограничений реализации для некоторых операций DDL разрешения не проверяются.

Разрешения для языка управления данными (Data Control Language, DCL)

Из-за ограничений реализации для некоторых операций DCL разрешения не проверяются.

Управление доступом на уровне строк

QHB поддерживает доступ на уровне строк, а sepgsql — нет.

Скрытые каналы

Модуль sepgsql не пытается скрыть существование определенного объекта, даже если пользователю не разрешено к нему обращаться. Например, можно сделать логический вывод о существовании невидимого объекта по конфликтам первичного ключа, нарушениям внешних ключей и т. д., даже если нельзя получить содержимое этого объекта. Существование совершенно секретной таблицы невозможно скрыть; можно лишь надеяться утаить ее содержимое.

Внешние ресурсы

SE-PostgreSQL Introduction, Введение в SE-PostgreSQL

На этой вики-странице представлен краткий обзор и описание архитектуры и проекта безопасности, администрирования и ожидаемых в будущем возможностей расширения SE-PostgreSQL.

SELinux User's and Administrator's Guide, Руководство пользователя и администратора SELinux

В этом документе представлен широкий спектр знаний по администрированию SELinux в операционных системах. В первую очередь он ориентирован на системы Red Hat, но не ограничивается только ими.

Fedora SELinux FAQ, ЧаВо по SELinux в ОС Fedora

В этом документе даются ответы на часто задаваемые вопросы по SELinux. В первую очередь он ориентирован на ОС Fedora, но не ограничивается только ей.

Автор

КайГай Кохэй (kaigai@ak.jp.nec.com)

spi

Модуль spi предоставляет несколько рабочих примеров использования Интерфейса программирования сервера (Server Programming Interface, SPI) и триггеров. Хотя эти функции имеют некоторую ценность и сами по себе, они еще более полезны в качестве образцов, которые можно модифицировать под собственные нужды. Эти функции достаточно общие, чтобы применяться с любой таблицей, но при создании триггера следует явно указать имена таблицы и полей (как описано ниже).

Каждая описанная ниже группа функций представлена в виде отдельно устанавливаемого расширения.

refint — функции для реализации ссылочной целостности

Функции check_primary_key() и check_foreign_key() используются для проверки ограничений внешних ключей. (Эта функциональность уже давно вытеснена встроенным механизмом внешних ключей, но этот модуль все еще полезен в качестве примера.)

Функция check_primary_key() проверяет ссылающуюся таблицу. Чтобы воспользоваться ей, создайте с помощью этой функции триггер BEFORE INSERT OR UPDATE для таблицы, ссылающейся на другую. Задайте в аргументах триггера: имена столбцов ссылающейся таблицы, образующих внешний ключ, имя ссылочной таблицы и имена столбцов в ней, образующих первичный/уникальный ключ. Чтобы управлять несколькими внешними ключами, создайте триггер для каждой такой ссылки.

Функция check_foreign_key() проверяет ссылочную таблицу. Чтобы воспользоваться ей, создайте с помощью этой функции триггер BEFORE DELETE OR UPDATE для таблицы, на которую ссылаются другие таблицы. Задайте в аргументах триггера: количество ссылающихся таблиц, для которых функция должна выполнить проверки, действие в случае обнаружения ссылающегося ключа (cascade — удалить ссылающуюся строку, restrict — прервать транзакцию, если ссылающийся ключ существует, setnull — установить в полях ссылающихся ключей значения NULL), имена столбцов ссылочной таблицы, образующих первичный/уникальный ключ, а затем имя ссылающейся таблицы и имена ее столбцов (повторить для стольких ссылающихся таблиц, сколько их указано в первом аргументе). Обратите внимание, что столбцы первичного/уникального ключа должны быть помечены NOT NULL и по ним должен быть создан уникальный индекс.

Примеры приведены в refint.example.

autoinc — функции для автоувеличения полей

Функция autoinc() является триггером, сохраняющим следующее значение последовательности в целочисленном поле. Это некоторым образом пересекается со встроенной функциональностью столбцов типа serial, но все же не одно и то же: autoinc() будет переопределять попытки вставить другое значение поля при добавлении строк, а также при необходимости может использоваться для увеличения значения поля при изменениях.

Чтобы воспользоваться этой функцией, создайте с ее помощью триггер BEFORE INSERT (или BEFORE INSERT OR UPDATE). Задайте в двух аргументах триггера имя целочисленного столбца, подлежащего изменению, и имя объекта последовательности, который будет поставлять значения. (На самом деле можно задать любое количество пар таких имен, если хотите изменять несколько автоувеличивающихся столбцов.)

Пример приведен в autoinc.example.

insert_username — функции для отслеживания пользователя, меняющего таблицу

Функция insert_username() является триггером, сохраняющим имя текущего пользователя в текстовом поле. Это может быть полезно для отслеживания пользователя, последним изменившего конкретную строку в таблице.

Чтобы воспользоваться этой функцией, создайте с ее помощью триггер BEFORE INSERT и/или UPDATE. Задайте в единственном аргументе триггера имя текстового столбца, подлежащего изменению.

Пример приведен в insert_username.example.

moddatetime — функции для отслеживания времени последнего изменения

Функция moddatetime() является триггером, сохраняющим текущее время в поле типа timestamp. Это может быть полезно для отслеживания времени последнего изменения конкретной строки в таблице.

Чтобы воспользоваться этой функцией, создайте с ее помощью триггер BEFORE UPDATE. Задайте в единственном аргументе триггера имя столбца, подлежащего изменению. Столбец должен иметь тип timestamp или timestamp with time zone.

Пример приведен в moddatetime.example.

sslinfo

Модуль sslinfo предоставляет информацию о SSL-сертификате, который был предоставлен текущим клиентом при подключении к QHB. Этот модуль бесполезен (большинство функций возвратят NULL), если для текущего подключения не используется SSL.

Часть информации, доступной благодаря этому модулю, также можно получить через встроенное системное представление pg_stat_ssl.

Это расширение не будет собираться, если установка была произведена без ключа --with-ssl=openssl.

Предоставляемые функции

ssl_is_used() returns boolean

Возвращает true, если текущее подключение к серверу использует SSL, и false в противном случае.

ssl_version() returns text

Возвращает имя протокола, используемого для SSL-подключения (например, TLSv1.0, TLSv1.1, TLSv1.2 или TLSv1.3).

ssl_cipher() returns text

Возвращает имя шифра, используемого для SSL-подключения (например, DHE-RSA-AES256-SHA).

ssl_client_cert_present() returns boolean

Возвращает true, если текущий клиент предоставил серверу действительный клиентский SSL-сертификат, и false в противном случае. (В зависимости от своей конфигурации, сервер может требовать, а может и не требовать предоставления клиентского сертификата.)

ssl_client_serial() returns numeric

Возвращает серийный номер текущего клиентского сертификата. Сочетание серийного номера сертификата с выдавшим его издателем (центром сертификации, ЦС) однозначно гарантирует идентификацию сертификата (но не его владелец — владелец должен регулярно менять свои ключи и получать новые сертификаты в ЦС).

Поэтому, если вы используете собственный ЦС и серверу разрешено принимать сертификаты только от этого ЦС, серийный номер будет наиболее надежным (хотя и не очень запоминающимся) средством идентификации пользователя.

ssl_client_dn() returns text

Возвращает полное имя субъекта текущего клиентского сертификата, преобразуя символьные данные в текущую кодировку базы данных. Предполагается, что если в именах сертификатов используются символы вне ASCII, база данных тоже может представить эти символы. Если в базе данных используется кодировка SQL_ASCII, символы вне ASCII в имени будут представлены последовательностями UTF-8.

Результат выглядит так: /CN=Somebody /C=Some country/O=Some organization.

ssl_issuer_dn() returns text

Возвращает полное имя издателя текущего клиентского сертификата, преобразуя символьные данные в текущую кодировку базы данных. Преобразования кодировки осуществляются так же, как и в ssl_client_dn.

Сочетание возвращаемого значения этой функции с серийным номером сертификата однозначно идентифицирует сертификат.

Фактически эта функция полезна, только если в установленном на сервере файле ЦС содержатся сертификаты нескольких доверенных ЦС или если этот ЦС выдает сертификаты для промежуточных центров сертификации.

ssl_client_dn_field(fieldname text) returns text

Эта функция возвращает значение заданного поля в субъекте сертификата или NULL, если это поле отсутствует. Имена полей являются строковыми константами, которые преобразуются в идентификаторы объектов ASN1, используя базу данных объектов OpenSSL. Принимаются следующие значения:

commonName (alias CN)
surname (alias SN)
name
givenName (alias GN)
countryName (alias C)
localityName (alias L)
stateOrProvinceName (alias ST)
organizationName (alias O)
organizationalUnitName (alias OU)
title
description
initials
postalCode
streetAddress
generationQualifier
description
dnQualifier
x500UniqueIdentifier
pseudonym
role
emailAddress

Все эти поля являются необязательными, за исключением commonName. Какие из них будут включены в сертификат, а какие нет, полностью зависит от политики ЦС. Однако значение этих полей строго определено стандартами X.500 и X.509, поэтому им нельзя присваивать произвольное значение.

ssl_issuer_field(fieldname text) returns text

То же, что и ssl_client_dn_field, но для издателя, а не для субъекта сертификата.

ssl_extension_info() returns setof record

Предоставляет информацию о расширениях клиентского сертификата: имя расширения, значение расширения и является ли это расширение критическим.

Автор

Виктор Вагнер (vitus@cryptocom.ru), ООО «Криптоком»

Дмитрий Воронин (carriingfate92@yandex.ru)

Адрес электронной почты группы разработчиков OpenSSL в ООО «Криптоком»: (openssl@cryptocom.ru)

tablefunc

Модуль tablefunc содержит ряд функций, возвращающих таблицы (то есть множества строк). Эти функции полезны и сами по себе, и как примеры написания функций на C, возвращающих множество строк.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Предоставляемые функции

Функции, предоставляемые модулем tablefunc, перечислены в Таблице 31.

Таблица 31. Функции tablefunc

Функция | Описание
normal_rand ( numvals integer, mean float8, stddev float8 ) → setof float8 | Выдает набор случайных значений с нормальным распределением.
crosstab ( sql text ) → setof record | Выдает «повернутую таблицу», содержащую имена строк плюс N столбцов значений, где N определяется типом строк, заданным в вызывающем запросе.
crosstabN ( sql text ) → setof table_crosstab_N | Выдает «повернутую таблицу», содержащую имена строк плюс N столбцов значений. Функции crosstab2, crosstab3 и crosstab4 предопределены, но можно создать дополнительные функции crosstabN, как описано ниже.
crosstab ( source_sql text, category_sql text ) → setof record | Выдает «повернутую таблицу» со столбцами значений, заданными вторым запросом.
crosstab ( sql text, N integer ) → setof record | Устаревшая версия crosstab(text). Параметр N теперь игнорируется, поскольку число столбцов всегда определяется вызывающим запросом.
connectby ( relname text, keyid_fld text, parent_keyid_fld text [, orderby_fld text ], start_with text, max_depth integer [, branch_delim text ] ) → setof record | Выдает представление иерархической древовидной структуры.

normal_rand

normal_rand(int numvals, float8 mean, float8 stddev) returns setof float8

Функция normal_rand выдает набор случайных значений с нормальным распределением (распределение Гаусса).

Параметр numvals представляет собой количество значений, возвращаемых этой функцией. Параметр mean — это среднее (математическое ожидание) нормального распределения значений, а параметр stddev — стандартное отклонение нормального распределения значений.

Например, этот вызов запрашивает 1000 значений со средним 5 и стандартным отклонением 3:

test=# SELECT * FROM normal_rand(1000, 5, 3);
     normal_rand
----------------------
     1.56556322244898
     9.10040991424657
     5.36957140345079
   -0.369151492880995
    0.283600703686639
       .
       .
       .
     4.82992125404908
     9.71308014517282
     2.49639286969028
(1000 rows)

crosstab(text)

crosstab(text sql)
crosstab(text sql, int N)

Функция crosstab используется для создания «повернутых» отображений, в которых данные записываются вдоль строк, а не сверху вниз. Например, у нас могут быть данные вроде таких:

row1    val11
row1    val12
row1    val13
...
row2    val21
row2    val22
row2    val23
...

которые мы хотим отобразить как

row1    val11   val12   val13   ...
row2    val21   val22   val23   ...
...

Функция crosstab принимает в текстовом параметре запрос SQL, выдающий исходные данные, отформатированные первым способом, и выдает таблицу, отформатированную вторым способом.

Параметр sql представляет собой оператор SQL, выдающий исходный набор данных. Этот оператор должен возвращать один столбец row_name, один столбец category и один столбец value. Параметр N является устаревшим и игнорируется, если передается при вызове (раньше он должен был соответствовать числу выходных столбцов значений, но теперь это число определяется вызывающим запросом).

Например, приведенный запрос может выдавать результат вроде этого:

 row_name    cat    value
----------+-------+-------
  row1      cat1    val1
  row1      cat2    val2
  row1      cat3    val3
  row1      cat4    val4
  row2      cat1    val5
  row2      cat2    val6
  row2      cat3    val7
  row2      cat4    val8

Функция crosstab объявлена как возвращающая setof record, поэтому фактические имена и типы выходных столбцов должны определяться в предложении FROM вызывающего оператора SELECT, например так:

SELECT * FROM crosstab('...') AS ct(row_name text, category_1 text, category_2 text);

В этом примере выдается приблизительно такой результат:

           <== value  columns  ==>
 row_name   category_1   category_2
----------+------------+------------
  row1        val1         val2
  row2        val5         val6

Предложение FROM должно определять выводимые данные как один столбец row_name (с тем же типом данных, что и у первого результирующего столбца запроса SQL), за которым следуют N столбцов значений (все с тем же типом данных, что и третий результирующий столбец запроса SQL). Можно задать любое желаемое количество выходных столбцов значений, и имена выходных столбцов также определяете вы сами.

Функция crosstab выдает одну выходную строку для каждой последовательной группы входных строк с одним значением row_name. Она заполняет выходные столбцы значений слева направо полями value из этих строк. Если в группе оказывается меньше строк, чем выходных столбцов значений, лишние столбцы заполняются значениями NULL; если же строк оказывается больше, лишние строки игнорируются.

На практике в запросе SQL всегда следует указывать ORDER BY 1,2, чтобы входные строки были отсортированы должным образом, то есть чтобы значения с одинаковым row_name собирались вместе и правильно упорядочивались в строке. Заметьте, что сама crosstab не обращает никакого внимания на второй столбец в результате запроса; он присутствует только для упорядочивания, чтобы определять порядок, в котором значения третьего столбца будут находиться в строке.

Вот полный пример:

CREATE TABLE ct(id SERIAL, rowid TEXT, attribute TEXT, value TEXT);
INSERT INTO ct(rowid, attribute, value) VALUES('test1','att1','val1');
INSERT INTO ct(rowid, attribute, value) VALUES('test1','att2','val2');
INSERT INTO ct(rowid, attribute, value) VALUES('test1','att3','val3');
INSERT INTO ct(rowid, attribute, value) VALUES('test1','att4','val4');
INSERT INTO ct(rowid, attribute, value) VALUES('test2','att1','val5');
INSERT INTO ct(rowid, attribute, value) VALUES('test2','att2','val6');
INSERT INTO ct(rowid, attribute, value) VALUES('test2','att3','val7');
INSERT INTO ct(rowid, attribute, value) VALUES('test2','att4','val8');

SELECT *
FROM crosstab(
  'select rowid, attribute, value
   from ct
   where attribute = ''att2'' or attribute = ''att3''
   order by 1,2')
AS ct(row_name text, category_1 text, category_2 text, category_3 text);

 row_name | category_1 | category_2 | category_3
----------+------------+------------+------------
 test1    | val2       | val3       |
 test2    | val6       | val7       |
(2 rows)

Можно избежать необходимости постоянно писать предложение FROM для определения выходных столбцов, собрав функцию crosstab под себя и зашив в ее определение желаемый тип выходной строки. Это описывается в следующем подразделе. Еще один вариант — включить требуемое предложение FROM в определение представления.

Примечание
Также изучите команду \crosstabview в psql, предоставляющую функциональность, схожую с crosstab().

crosstabN(text)

crosstabN(text sql)

Функции crosstabN являются примерами того, как можно создать собственные обертки универсальной функции crosstab, чтобы не приходилось писать имена и типы столбцов в вызывающем запросе SELECT. Модуль tablefunc содержит функции crosstab2, crosstab3 и crosstab4, у которых типы выходных строк определяются как

CREATE TYPE tablefunc_crosstab_N AS (
    row_name TEXT,
    category_1 TEXT,
    category_2 TEXT,
        .
        .
        .
    category_N TEXT
);

Таким образом, эти функции могут применяться непосредственно, когда входной запрос выдает столбцы row_name и value типа text и хочется получить 2, 3 или 4 выходных столбца значений. В остальном эти функции ведут себя точно так же, как описанная выше универсальная функция crosstab.

Так, пример, приведенный в предыдущем подразделе, сработает и в таком виде:

SELECT *
FROM crosstab3(
  'select rowid, attribute, value
   from ct
   where attribute = ''att2'' or attribute = ''att3''
   order by 1,2');

Эти функции предоставлены в основном в демонстрационных целях. Вы можете создать свои типы возвращаемых данных и функции на базе нижележащей функции crosstab(). Существует два способа это сделать:

Создать составной тип, описывающий желаемые выходные столбцы, подобно тому, как это делается в примерах в contrib/tablefunc/tablefunc--1.0.sql. Затем определить уникальное имя для функции, принимающей один параметр text и возвращающей setof имя_вашего_типа, но связать его с той же нижележащей функцией crosstab на C. Например, если ваши исходные данные выдают имена строк типа text и значения типа float8, и вы хотите получить 5 столбцов значений:

CREATE TYPE my_crosstab_float8_5_cols AS (
    my_row_name text,
    my_category_1 float8,
    my_category_2 float8,
    my_category_3 float8,
    my_category_4 float8,
    my_category_5 float8
);

CREATE OR REPLACE FUNCTION crosstab_float8_5_cols(text)
    RETURNS setof my_crosstab_float8_5_cols
    AS '$libdir/tablefunc','crosstab' LANGUAGE C STABLE STRICT;

Использовать выходные параметры (OUT), чтобы явно определить возвращаемый тип. Тот же пример можно реализовать и таким способом:

CREATE OR REPLACE FUNCTION crosstab_float8_5_cols(
    IN text,
    OUT my_row_name text,
    OUT my_category_1 float8,
    OUT my_category_2 float8,
    OUT my_category_3 float8,
    OUT my_category_4 float8,
    OUT my_category_5 float8)
  RETURNS setof record
  AS '$libdir/tablefunc','crosstab' LANGUAGE C STABLE STRICT;

crosstab(text, text)

crosstab(text source_sql, text category_sql)

Главное ограничение формы crosstab с одним параметром состоит в том, что она интерпретирует все значения в группе одинаково, вставляя каждое значение в первый свободный столбец. Если вы хотите, чтобы столбцы значений соответствовали определенным категориям данных, а некоторые группы могли содержать данные не для всех категорий, этот подход не будет нормально работать. Форма crosstab с двумя параметрами решает эту задачу, предоставляя явный список категорий, соответствующих выходным столбцам.

Параметр source_sql — это оператор SQL, выдающий исходный набор данных. Этот оператор должен возвращать один столбец row_name, один столбец category и один столбец value. Также он может выдавать один и более «дополнительных» столбцов. Столбец row_name должен быть первым. Столбцы category и value должны быть последними двумя и именно в таком порядке. Все столбцы между row_name и category воспринимаются «дополнительные». Ожидается, что «дополнительные» столбцы будут содержать одинаковые значения для всех строк с одним значением row_name.

Например, source_sql может выдавать набор данных вроде этого:

SELECT row_name, extra_col, cat, value FROM foo ORDER BY 1;

 row_name    extra_col   cat    value
----------+------------+-----+---------
  row1         extra1    cat1    val1
  row1         extra1    cat2    val2
  row1         extra1    cat4    val4
  row2         extra2    cat1    val5
  row2         extra2    cat2    val6
  row2         extra2    cat3    val7
  row2         extra2    cat4    val8

Параметр category_sql — это оператор SQL, выдающий набор категорий. Этот оператор должен возвращать только один столбец. Он должен выдать как минимум одну строку, иначе произойдет ошибка. Кроме того, он не должен выдавать повторяющиеся значения, так как это тоже вызовет ошибку. Например, category_sql может выглядеть так:

SELECT DISTINCT cat FROM foo ORDER BY 1;
    cat
  -------
    cat1
    cat2
    cat3
    cat4

Функция crosstab объявлена как возвращающая тип setof record, поэтому фактические имена и типы выходных столбцов должны определяться в предложении FROM вызывающего оператора SELECT, например:

SELECT * FROM crosstab('...', '...')
    AS ct(row_name text, extra text, cat1 text, cat2 text, cat3 text, cat4 text);

При этом будет получен примерно такой результат:

                  <==  value  columns   ==>
row_name   extra   cat1   cat2   cat3   cat4
---------+-------+------+------+------+------
  row1     extra1  val1   val2          val4
  row2     extra2  val5   val6   val7   val8

В предложении FROM должно определяться требуемое количество выходных столбцов надлежащих типов данных. Если в результате запроса source_sql содержится N столбцов, первые N-2 из них должны соответствовать первым N-2 выходным столбцам. Оставшиеся выходные столбцы должны иметь тип последнего столбца результата запроса source_sql и их должно быть ровно столько, сколько строк содержится в результате запроса category_sql.

Функция crosstab выдает по одной выходной строке для каждой последовательной группы входных строк с одинаковым значением row_name. Выходной столбец row_name плюс все «дополнительные» столбцы копируются из первой строки группы. Выходные столбцы значений заполняются содержимым полей value из строк с соответствующими значениями category. Если значение поля category не соответствует ни одному выходному значению запроса category_sql, содержимое поля value в этой строке игнорируется. Выходные столбцы, для которых соответствующая категория не представлена ни в одной входной строке группы, заполняются значениями NULL.

На практике в запросе source_sql всегда следует указывать ORDER BY 1, чтобы все значения с одинаковым row_name гарантированно выводились вместе. При этом порядок категорий внутри группы не важен. Кроме того, крайне важно, чтобы порядок выходных данных запроса category_sql соответствовал заданному порядку выходных столбцов.

Ниже приводится два законченных примера:

create table sales(year int, month int, qty int);
insert into sales values(2007, 1, 1000);
insert into sales values(2007, 2, 1500);
insert into sales values(2007, 7, 500);
insert into sales values(2007, 11, 1500);
insert into sales values(2007, 12, 2000);
insert into sales values(2008, 1, 1000);

select * from crosstab(
  'select year, month, qty from sales order by 1',
  'select m from generate_series(1,12) m'
) as (
  year int,
  "Jan" int,
  "Feb" int,
  "Mar" int,
  "Apr" int,
  "May" int,
  "Jun" int,
  "Jul" int,
  "Aug" int,
  "Sep" int,
  "Oct" int,
  "Nov" int,
  "Dec" int
);
 year | Jan  | Feb  | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov  | Dec
------+------+------+-----+-----+-----+-----+-----+-----+-----+-----+------+------
 2007 | 1000 | 1500 |     |     |     |     | 500 |     |     |     | 1500 | 2000
 2008 | 1000 |      |     |     |     |     |     |     |     |     |      |
(2 rows)

CREATE TABLE cth(rowid text, rowdt timestamp, attribute text, val text);
INSERT INTO cth VALUES('test1','01 March 2003','temperature','42');
INSERT INTO cth VALUES('test1','01 March 2003','test_result','PASS');
INSERT INTO cth VALUES('test1','01 March 2003','volts','2.6987');
INSERT INTO cth VALUES('test2','02 March 2003','temperature','53');
INSERT INTO cth VALUES('test2','02 March 2003','test_result','FAIL');
INSERT INTO cth VALUES('test2','02 March 2003','test_startdate','01 March 2003');
INSERT INTO cth VALUES('test2','02 March 2003','volts','3.1234');

SELECT * FROM crosstab
(
  'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
  'SELECT DISTINCT attribute FROM cth ORDER BY 1'
)
AS
(
       rowid text,
       rowdt timestamp,
       temperature int4,
       test_result text,
       test_startdate timestamp,
       volts float8
);
 rowid |          rowdt           | temperature | test_result |      test_startdate      | volts
-------+--------------------------+-------------+-------------+--------------------------+--------
 test1 | Sat Mar 01 00:00:00 2003 |          42 | PASS        |                          | 2.6987
 test2 | Sun Mar 02 00:00:00 2003 |          53 | FAIL        | Sat Mar 01 00:00:00 2003 | 3.1234
(2 rows)

Чтобы не выписывать в каждом запросе имена и типы результирующих столбцов, можно создать предопределенные функции. См. примеры в предыдущем подразделе. Нижележащая функция на C для этой формы crosstab называется crosstab_hash.

connectby

connectby(text relname, text keyid_fld, text parent_keyid_fld
          [, text orderby_fld ], text start_with, int max_depth
          [, text branch_delim ])

Функция connectby выдает отображение хранящихся в таблице данных иерархической структуры. Эта таблица должна содержать поле ключа, однозначно идентифицирующее строки, и поле ключа родителя, ссылающееся на родителя каждой строки (если он есть). Функция connectby может отобразить вложенное дерево, начиная с любой строки.

Параметры функции описаны в Таблице 29.

Таблица 32. Параметры connectby

Параметр | Описание
relname | Имя исходного отношения
keyid_fld | Имя поля ключа
parent_keyid_fld | Имя поля ключа родителя
orderby_fld | Имя поля, по которому сортируются потомки (необязательно)
start_with | Значение ключа начальной строки
max_depth | Максимальная глубина, на которую можно погрузиться, или ноль для неограниченной глубины
branch_delim | Строка, разделяющая ключи в выводе ветви (необязательно)

Поля ключа и ключа родителя могут быть любого типа, но он у них должен быть одинаковым. Обратите внимание, что значение start_with должно задаваться текстовой строкой, независимо от типа поля ключа.

Функция connectby объявлена как возвращающая setof record, поэтому фактические имена и типы выходных столбцов должны определяться в предложении FROM вызывающего оператора SELECT, например:

SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'pos', 'row2', 0, '~')
    AS t(keyid text, parent_keyid text, level int, branch text, pos int);

Первые два выходных столбца используются для вывода ключа текущей строки и ключа ее родительской строки; их тип должен соответствовать типу поля ключа таблицы. В третьем выходном столбце выводится глубина в дереве, и он должен иметь тип integer. Если передается параметр branch_delim, в следующем выходном столбце отображаются ветви, и он должен иметь тип text. Наконец, если передается параметр orderby_fld, в последнем выходном столбце выводится порядковый номер, и он должен иметь тип integer.

В выходном столбце «ветви» показывается путь, приведший ключи к текущей строке. Ключи разделяются заданной строкой branch_delim. Если выводить ветви не требуется, опустите параметр branch_delim и столбец ветви в списке выходных столбцов.

Если порядок потомков одного родителя важен, добавьте параметр orderby_fld для указания поля, по которому будут упорядочены потомки. Это поле может иметь любой сортируемый тип данных. Последним в списке выходных столбцов должен идти целочисленный столбец с порядковыми номерами, если и только если передается параметр orderby_fld.

Параметры, представляющие имена таблицы и полей, копируются как есть в запросы SQL, которые connectby генерирует внутри. Поэтому их следует заключить в кавычки, если они содержат буквы в разном регистре или специальные символы. Также может понадобиться дополнить имя таблицы схемой.

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

Важно, чтобы строка branch_delim не содержалась в значениях ключа, иначе connectby может некорректно сообщить об ошибке бесконечной вложенности. Обратите внимание, что если branch_delim не передается, для выявления рекурсии используется символ по умолчанию ~.

Пример:

CREATE TABLE connectby_tree(keyid text, parent_keyid text, pos int);

INSERT INTO connectby_tree VALUES('row1',NULL, 0);
INSERT INTO connectby_tree VALUES('row2','row1', 0);
INSERT INTO connectby_tree VALUES('row3','row1', 0);
INSERT INTO connectby_tree VALUES('row4','row2', 1);
INSERT INTO connectby_tree VALUES('row5','row2', 0);
INSERT INTO connectby_tree VALUES('row6','row4', 0);
INSERT INTO connectby_tree VALUES('row7','row3', 0);
INSERT INTO connectby_tree VALUES('row8','row6', 0);
INSERT INTO connectby_tree VALUES('row9','row5', 0);

-- с ветвями без orderby_fld (порядок результатов не гарантируется)
SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'row2', 0, '~')
 AS t(keyid text, parent_keyid text, level int, branch text);
 keyid | parent_keyid | level |       branch
-------+--------------+-------+---------------------
 row2  |              |     0 | row2
 row4  | row2         |     1 | row2~row4
 row6  | row4         |     2 | row2~row4~row6
 row8  | row6         |     3 | row2~row4~row6~row8
 row5  | row2         |     1 | row2~row5
 row9  | row5         |     2 | row2~row5~row9
(6 rows)

-- без ветвей и без orderby_fld (порядок результатов не гарантируется)
SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'row2', 0)
 AS t(keyid text, parent_keyid text, level int);
 keyid | parent_keyid | level
-------+--------------+-------
 row2  |              |     0
 row4  | row2         |     1
 row6  | row4         |     2
 row8  | row6         |     3
 row5  | row2         |     1
 row9  | row5         |     2
(6 rows)

-- с ветвями и с orderby_fld (обратите внимание, что row5 идет перед row4)
SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'pos', 'row2', 0, '~')
 AS t(keyid text, parent_keyid text, level int, branch text, pos int);
 keyid | parent_keyid | level |       branch        | pos
-------+--------------+-------+---------------------+-----
 row2  |              |     0 | row2                |   1
 row5  | row2         |     1 | row2~row5           |   2
 row9  | row5         |     2 | row2~row5~row9      |   3
 row4  | row2         |     1 | row2~row4           |   4
 row6  | row4         |     2 | row2~row4~row6      |   5
 row8  | row6         |     3 | row2~row4~row6~row8 |   6
(6 rows)

-- без ветвей, с orderby_fld (обратите внимание, что row5 идет перед row4)
SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'pos', 'row2', 0)
 AS t(keyid text, parent_keyid text, level int, pos int);
 keyid | parent_keyid | level | pos
-------+--------------+-------+-----
 row2  |              |     0 |   1
 row5  | row2         |     1 |   2
 row9  | row5         |     2 |   3
 row4  | row2         |     1 |   4
 row6  | row4         |     2 |   5
 row8  | row6         |     3 |   6
(6 rows)

Автор

Джо Конвэй

tcn

Модуль tcn предоставляет триггерную функцию, уведомляющую блоки прослушивания об изменениях в любой таблице, к которой она привязана. Она должна использоваться в качестве триггера AFTER вида FOR EACH ROW.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Этой функции в операторе CREATE TRIGGER может передаваться только один параметр, и он является необязательным. Если этот параметр присутствует, он будет использован для указания имени канала для уведомлений. Если он отсутствует, именем канала будет tcn.

Информационное наполнение уведомлений состоит из имени таблицы, буквы, обозначающей тип выполняемой операции, и пар имя столбца/значение для столбцов первичного ключа. Каждая часть отделяется от следующей запятой. Для упрощения синтаксического анализа сообщения регулярными выражениями имена таблицы и столбцов всегда заключаются в кавычки, а значения данных — в апострофы. Внутренние кавычки и апострофы дублируются.

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

test=# create table tcndata
test-#   (
test(#     a int not null,
test(#     b date not null,
test(#     c text,
test(#     primary key (a, b)
test(#   );
CREATE TABLE
test=# create trigger tcndata_tcn_trigger
test-#   after insert or update or delete on tcndata
test-#   for each row execute function triggered_change_notification();
CREATE TRIGGER
test=# listen tcn;
LISTEN
test=# insert into tcndata values (1, date '2012-12-22', 'one'),
test-#                            (1, date '2012-12-23', 'another'),
test-#                            (2, date '2012-12-23', 'two');
INSERT 0 3
Asynchronous notification "tcn" with payload ""tcndata",I,"a"='1',"b"='2012-12-22'" received from server process with PID 22770.
Asynchronous notification "tcn" with payload ""tcndata",I,"a"='1',"b"='2012-12-23'" received from server process with PID 22770.
Asynchronous notification "tcn" with payload ""tcndata",I,"a"='2',"b"='2012-12-23'" received from server process with PID 22770.
test=# update tcndata set c = 'uno' where a = 1;
UPDATE 2
Asynchronous notification "tcn" with payload ""tcndata",U,"a"='1',"b"='2012-12-22'" received from server process with PID 22770.
Asynchronous notification "tcn" with payload ""tcndata",U,"a"='1',"b"='2012-12-23'" received from server process with PID 22770.
test=# delete from tcndata where a = 1 and b = date '2012-12-22';
DELETE 1
Asynchronous notification "tcn" with payload ""tcndata",D,"a"='1',"b"='2012-12-22'" received from server process with PID 22770.

test_decoding

test_decoding представляет собой пример плагина вывода логического декодирования. Он не делает ничего особенно полезного, но может служить отправной точкой для разработки собственного плагина вывода.

test_decoding получает WAL через механизм логического декодирования и дешифрует его в текстовые представления выполняемых операций.

Типичный вывод этого плагина, используемого через интерфейс логического декодирования SQL, может выглядеть так:

postgres=# SELECT * FROM pg_logical_slot_get_changes('test_slot', NULL, NULL, 'include-xids', '0');
   lsn     | xid |                       data
-----------+-----+--------------------------------------------------
 0/16D30F8 | 691 | BEGIN
 0/16D32A0 | 691 | table public.data: INSERT: id[int4]:2 data[text]:'arg'
 0/16D32A0 | 691 | table public.data: INSERT: id[int4]:3 data[text]:'demo'
 0/16D32A0 | 691 | COMMIT
 0/16D32D8 | 692 | BEGIN
 0/16D3398 | 692 | table public.data: DELETE: id[int4]:2
 0/16D3398 | 692 | table public.data: DELETE: id[int4]:3
 0/16D3398 | 692 | COMMIT
(8 rows)

Также можно получить информацию об изменениях в текущей транзакции, и типичный вывод может выглядеть так:

postgres[33712]=#* SELECT * FROM pg_logical_slot_get_changes('test_slot', NULL, NULL, 'stream-changes', '1');
    lsn    | xid |                       data
-----------+-----+--------------------------------------------------
 0/16B21F8 | 503 | opening a streamed block for transaction TXN 503
 0/16B21F8 | 503 | streaming change for TXN 503
 0/16B2300 | 503 | streaming change for TXN 503
 0/16B2408 | 503 | streaming change for TXN 503
 0/16BEBA0 | 503 | closing a streamed block for transaction TXN 503
 0/16B21F8 | 503 | opening a streamed block for transaction TXN 503
 0/16BECA8 | 503 | streaming change for TXN 503
 0/16BEDB0 | 503 | streaming change for TXN 503
 0/16BEEB8 | 503 | streaming change for TXN 503
 0/16BEBA0 | 503 | closing a streamed block for transaction TXN 503
(10 rows)

tsm_system_rows

Модуль tsm_system_rows предоставляет метод взятия выборки из таблицы SYSTEM_ROWS, который можно использовать в предложении TABLESAMPLE команды SELECT.

Этот метод взятия выборки из таблицы принимает один целочисленный аргумент, задающий максимальное количество строк для прочтения. Результирующая выборка всегда будет содержать ровно столько строк, если только в таблице не окажется недостаточно строк — в таком случае выбирается вся таблица.

Как и встроенный метод взятия выборки SYSTEM, SYSTEM_ROWS производит взятие выборки на уровне блоков, так что выборка не будет полностью случайной, а может подвергаться эффектам кластеризации, особенно если запрашивается небольшое количество строк.

SYSTEM_ROWS не поддерживает предложение REPEATABLE.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Примеры

Ниже приведен пример отбора выборки из таблицы с помощью SYSTEM_ROWS. Сначала установим расширение:

CREATE EXTENSION tsm_system_rows;

Затем его можно использовать в команде SELECT, например так:

SELECT * FROM my_table TABLESAMPLE SYSTEM_ROWS(100);

Эта команда возвратит выборку из 100 строк из таблицы my_table (а если в таблице не окажется 100 видимых строк, будут возвращены все строки).

tsm_system_time

Модуль tsm_system_time предоставляет метод взятия выборки из таблицы SYSTEM_TIME, который можно использовать в предложении TABLESAMPLE команды SELECT.

Этот метод взятия выборки из таблицы принимает один целочисленный аргумент, задающий максимальное время в миллисекундах, которое отводится на чтение таблицы. Это обеспечивает возможность непосредственно управлять продолжительностью выполнения запроса за счет того, что размер выборки становится трудно предсказать. Результирующая выборка будет содержать столько строк, сколько удастся прочитать за заданное время, если только раньше не будет прочитана вся таблица.

Как и встроенный метод взятия выборки SYSTEM, SYSTEM_TIME производит взятие выборки на уровне блоков, так что выборка не будет полностью случайной, а может подвергаться эффектам кластеризации, особенно если запрашивается небольшое количество строк.

SYSTEM_TIME не поддерживает предложение REPEATABLE.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Примеры

Ниже приведен пример отбора выборки из таблицы с помощью SYSTEM_TIME. Сначала установим расширение:

CREATE EXTENSION tsm_system_time;

Затем его можно использовать в команде SELECT, например так:

SELECT * FROM my_table TABLESAMPLE SYSTEM_TIME(1000);

Эта команда возвратит такой объем выборки из my_table, какой удастся прочитать за 1 секунду (1000 миллисекунд). Конечно, если за 1 секунду можно прочитать всю таблицу, будут возвращены все ее строки.

unaccent

Модуль unaccent представляет собой словарь текстового поиска, убирающий надбуквенные (диакритические) знаки из лексем. Это фильтрующий словарь, что означает, что его выходные данные всегда передаются следующему словарю (если таковой имеется), что отличается от обычного поведения словарей. Это позволяет выполнять полнотекстовый поиск без учета знаков ударения (диакритик).

Текущую реализацию unaccent нельзя использовать в качестве нормализующего словаря для словаря thesaurus.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Конфигурация

Словарь unaccent принимает следующие параметры:

Параметр RULES задает базовое имя файла, содержащего список правил преобразования. Этот файл должен храниться в каталоге $SHAREDIR/tsearch_data/ (где под $SHAREDIR подразумевается каталог с общими данными установки QHB). Имя файла должно заканчиваться расширением .rules (которое не нужно указывать в параметре RULES).

Файл правил имеет следующий формат:

Каждая строка представляет одно правило преобразования, состоящее из символа с диакритикой и следующим за ним символом без диакритики. Первый символ преобразуется во второй. Например,

À        A
Á        A
Â        A
Ã        A
Ä        A
Å        A
Æ        AE

Эти два символа должны разделяться пробелом, а любые начальные или конечные пробелы в строке игнорируются.

Как вариант, если в строке задан всего один символ, вхождения этого символа удаляются; это полезно в языках, где диакритики представляются отдельными символами.

Фактически «символом» может быть любая строка, не содержащая пробелов, поэтому, помимо удаления диакритик, словари unaccent можно использовать и для других видов замены подстрок.

Как и другие файлы конфигурации текстового поиска в QHB, файл правил должен храниться в кодировке UTF-8. При загрузке данные из него будут автоматически преобразованы в кодировку текущей базы данных. Все строки, содержащие непереводимые символы, молча игнорируются, поэтому файлы правил могут содержать правила, неприменимые в текущей кодировке.

Более полный экземпляр правил, непосредственно пригодный для большинства европейских языков, можно найти в файле unaccent.rules, который при установке модуля unaccent помещается в каталог $SHAREDIR/tsearch_data/. Этот файл правил преобразует символы с диакритиками в те же символы без диакритик, а также разворачивает лигатуры в равнозначные последовательности простых символов (например, Æ в AE).

Применение

При установке расширения unaccent создается шаблон текстового поиска unaccent и словарь unaccent на его основе. Для словаря unaccent по умолчанию задается параметр RULES='unaccent', благодаря чему его можно сразу использовать со стандартным файлом unaccent.rules. При желании этот параметр можно изменить, например так:

mydb=# ALTER TEXT SEARCH DICTIONARY unaccent (RULES='my_rules');

или создать новые словари на базе этого шаблона.

Чтобы протестировать этот словарь можно попробовать следующее:

mydb=# select ts_lexize('unaccent','Hôtel');
 ts_lexize
-----------
 {Hotel}
(1 row)

Приведенный ниже пример показывает, как добавить словарь unaccent в конфигурацию текстового поиска:

mydb=# CREATE TEXT SEARCH CONFIGURATION fr ( COPY = french );
mydb=# ALTER TEXT SEARCH CONFIGURATION fr
        ALTER MAPPING FOR hword, hword_part, word
        WITH unaccent, french_stem;
mydb=# select to_tsvector('fr','Hôtels de la Mer');
    to_tsvector
-------------------
 'hotel':1 'mer':4
(1 row)

mydb=# select to_tsvector('fr','Hôtel de la Mer') @@ to_tsquery('fr','Hotels');
 ?column?
----------
 t
(1 row)

mydb=# select ts_headline('fr','Hôtel de la Mer',to_tsquery('fr','Hotels'));
      ts_headline
------------------------
 <b>Hôtel</b> de la Mer
(1 row)

Функции

Функция unaccent() удаляет надбуквенные (диакритические) знаки из заданной строки. По сути это обертка вокруг словарей типа unaccent, но ее можно использовать и вне обычного контекста текстового поиска.

unaccent([словарь regdictionary, ] строка text) returns text

Если аргумент словарь опущен, применяется словарь текстового поиска с именем unaccent, находящийся в той же схеме, что и сама функция unaccent().

Например:

SELECT unaccent('unaccent', 'Hôtel');
SELECT unaccent('Hôtel');

uuid-ossp

Модуль uuid-ossp предоставляет функции для генерирования универсальных уникальных идентификаторов (UUID) по одному из нескольких стандартных алгоритмов. Также в нем есть функции, выдающие определенные специальные константы UUID. Этот модуль необходим только в случае специальных требований module за пределами функциональности, доступной в ядре QHB. Встроенные в ядро способы генерирования UUID см. в разделе Функции генерирования UUID.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.

Функции uuid-ossp

В Таблице 33 показаны имеющиеся в модуле функции для генерирования UUID. В соответствующих стандартах ITU-T Rec. X.667, ISO/IEC 9834-8:2005 и RFC 4122 указаны четыре алгоритма для генерирования UUID, обозначаемые номерами версий 1, 3, 4 и 5. (Алгоритма версии 2 не существует.) Каждый из этих алгоритмов предназначен для различных групп приложений.

Таблица 33. Функции для генерирования UUID

Функция
Описание

uuid_generate_v1 () → uuid
Генерирует UUID версии 1. Этот UUID включает в себя MAC-адрес компьютера и метку времени. Обратите внимание, что UUID такого типа раскрывают реквизиты компьютера, создавшего идентификатор, и время создания, что может быть неприемлемым для определенных приложений, где требуется повышенная безопасность данных.

uuid_generate_v1mc () → uuid
Генерирует UUID версии 1, но вместо реального MAC-адреса компьютера использует случайный групповой MAC-адрес.

uuid_generate_v3 ( namespace uuid, name text ) → uuid
Генерирует UUID версии 3 в заданном пространстве имен, используя указанное имя. Пространство имен должно задаваться одной из специальных констант, которые выдаются функциями uuid_ns_*(), перечисленными в *Таблице 31. (Хотя теоретически это может быть любой UUID.) Имя задает идентификатор в выбранном пространстве имен.
Например:
SELECT uuid_generate_v3(uuid_ns_url(), 'http://www.postgresql.org');
Из параметра name будет получен MD5-хеш, так что из сгенерированного UUID нельзя будет извлечь открытый текст. В генерировании UUID таким методом нет элемента случайности или зависимости от среды, поэтому его можно воспроизвести.

uuid_generate_v4 () → uuid
Генерирует UUID версии 4, который полностью выводится из случайных чисел.

uuid_generate_v5 ( namespace uuid, name text ) → uuid
Генерирует UUID версии 5, который похож на версию 3, но хеширование проводится по алгоритму SHA-1. Версия 5 предпочтительнее версии 3, так как SHA-1 считается более безопасным, чем MD5.

Таблица 34. Функции, возвращающие константы UUID

Функция | Описание
uuid_nil () → uuid | Возвращает «нулевую» константу UUID, которая не считается настоящим UUID.
uuid_ns_dns () → uuid | Возвращает константу, обозначающую пространство имен DNS для UUID.
uuid_ns_url () → uuid | Возвращает константу, обозначающую пространство имен URL для UUID.
uuid_ns_oid () → uuid | Возвращает константу, обозначающую пространство имен идентификаторов объектов ISO (OID, ISO object identifier) для UUID. (Здесь имеются в виду OID ASN.1, которые не имеют отношения к OID, используемым в QHB.)
uuid_ns_x500 () → uuid | Возвращает константу, обозначающую пространство имен с различаемыми именами (DN, distinguished name) X.500 для UUID.

Сборка uuid-ossp

Исторически этот модуль зависел от библиотеки OSSP UUID, что отразилось в его имени. Хотя библиотеку OSSP UUID все еще можно найти по адресу http://www.ossp.org/pkg/lib/uuid/, она плохо поддерживается и ее становится все сложнее портировать на более новые платформы. Поэтому теперь на некоторых платформах модуль uuid-ossp можно собирать без библиотеки OSSP. Во FreeBSD, NetBSD и некоторых других платформах на базе BSD подходящие функции создания UUID включены в корневую библиотеку libc. В Linux, macOS и некоторых других платформах подходящие функции предоставляются библиотекой libuuid, которая изначально пришла из проекта e2fsprogs (хотя в современном Linux она считается частью util-linux-g). Вызывая configure, укажите ключ --with-uuid=bsd, чтобы использовать функции BSD, или --with-uuid=e2fs, чтобы использовать libuuid из e2fsprogs, или --with-uuid=ossp, чтобы использовать библиотеку OSSP UUID. На конкретной платформе может быть установлено сразу несколько библиотек, поэтому configure не выбирает библиотеку автоматически.

Автор

Питер Эйзентраут (peter_e@gmx.net)

xml2

Модуль xml2 предоставляет функциональные возможности для выполнения запросов XPath и преобразований XSLT.

Уведомление о депрекации

В QHB функциональность, связанная с XML, основана на стандарте SQL/XML и находится в ядре сервера. Эта функциональность охватывает проверку синтаксиса XML и запросы XPath, что как раз и делает этот модуль, но он имеет совершенно несовместимый API. Этот модуль планируется удалить в будущей версии QHB в пользу более нового стандартного API, поэтому мы рекомендуем вам попробовать перевести свои приложения на новый API.

Описание функций

Функции, предоставляемые этим модулем, описаны в Таблице 35. Эти функции обеспечивают выполнение простого разбора XML и запросов XPath.

Таблица 35. Функции xml2

Функция
Описание

xml_valid ( document text ) → boolean
Разбирает переданный документ и возвращает true, если это правильно сформированный XML. (Примечание: это псевдоним стандартной функции QHB xml_is_well_formed(). Имя name xml_valid() технически некорректно, поскольку понятия допустимости (valid) и правильности формата (well-formed) в XML имеют разное значение.)

xpath_string ( document text, query text ) → text
Анализирует запрос XPath для предоставленного документа и приводит результат к типу text.

xpath_number ( document text, query text ) → real
Анализирует запрос XPath для предоставленного документа и приводит результат к типу real.

xpath_bool ( document text, query text ) → boolean
Анализирует запрос XPath для предоставленного документа и приводит результат к типу boolean.

xpath_nodeset ( document text, query text, toptag text, itemtag text ) → text
Анализирует запрос для документа и обрамляет результат XML-тегами. Если результат содержит несколько значений, выходные данные будут выглядеть так:
<toptag>
<itemtag>Value 1 which could be an XML fragment</itemtag>
<itemtag>Value 2....</itemtag>
</toptag>
Если toptag или itemtag — пустая строка, соответствующий тег опускается.

xpath_nodeset ( document text, query text, itemtag text ) → text
Подобна xpath_nodeset(document, query, toptag, itemtag), но выводит результат без toptag.

xpath_nodeset ( document text, query text ) → text
Подобна xpath_nodeset(document, query, toptag, itemtag), но выводит результат без обоих тегов.

xpath_list ( document text, query text, separator text ) → text
Анализирует запрос для документа и возвращает несколько значений, вставляя между ними заданный разделитель (separator), например: Значение 1,Значение 2,Значение 3, если separator равен «,».

xpath_list ( document text, query text ) → text
Это обертка предыдущей функции, устанавливающая в качестве разделителя знак «,».

xpath_table

xpath_table(text key, text document, text relation, text xpaths, text criteria) returns setof record

xpath_table — это табличная функция, обрабатывающая набор запросов XPath для каждого из набора документов и возвращающая результаты в виде таблицы. Поле первичного ключа из таблицы оригинального документа возвращается в виде первого столбца результата, поэтому результирующий набор можно сразу использовать в соединениях. Параметры функции описаны в Таблице 36.

Таблица 36. Параметры xpath_table

Параметр | Описание
key | имя «ключевого» поля — это просто поле, содержимым которого будет заполнен первый столбец выходной таблицы, т. е. оно указывает на запись, из которой была получена каждая выходная строка (см. примечание о нескольких значениях ниже)
document | имя поля, содержащего XML-документ
relation | имя таблицы (или представления), содержащей документы
xpaths | одно или несколько выражений XPath, разделенных символом |
criteria | содержимое предложения WHERE. Этот параметр нельзя опустить, поэтому если хотите обработать все строки в отношении, напишите true или 1=1

Эти параметры (за исключением строк XPath) просто подставляются в обычный оператор SQL SELECT, так что у вас есть некоторая гибкость — оператор выглядит так:

SELECT <key>, <document> FROM <relation> WHERE <criteria>

поэтому в этих параметрах можно передать всё, что будет допустимо в этих позициях. Этот SELECT должен возвращать в результате ровно два столбца (что он и будет делать, если только вы не попытаетесь перечислить несколько полей в параметрах key или document). Учтите, что при таком примитивном подходе вам обязательно нужно проверять все значения, получаемые от пользователя, во избежание атак с внедрением SQL-кода.

Эта функция предназначена для использования в выражении FROM, с предложением AS, задающим выходные столбцы; например:

SELECT * FROM
xpath_table('article_id',
            'article_xml',
            'articles',
            '/article/author|/article/pages|/article/title',
            'date_entered > ''2003-01-01'' ')
AS t(article_id integer, author text, page_count integer, title text);

Предложение AS определяет имена и типы столбцов в выходной таблице. Первым идет «ключевое» поле, а за ним поля, соответствующие запросам XPath. Если запросов XPath больше, чем результирующих столбцов, лишние запросы будут игнорироваться. Если же результирующих столбцов больше, чем запросов XPath, лишние столбцы будут заполнены значением NULL.

Обратите внимание, что в этом примере результирующий столбец page_count определен как целочисленный. Эта функция внутри имеет дело со стоковыми представлениями, поэтому когда вы указываете, что в выходных данных хотите получить целое число, она берет строковое представление результата XPath и использует функции ввода QHB, чтобы преобразовать его в целое число (или в тот тип, который указан в предложении AS). Если она не сможет это сделать, произойдет ошибка — например, если результат пустой, — поэтому если вы считаете, что с данными могут возникнуть проблемы, возможно, стоит просто оставить для такого столбца тип text.

Вызывающий оператор SELECT необязательно должен быть простым SELECT * — он может обращаться к выходным столбцам по именам или соединять их с другими таблицами. Эта функция выдает виртуальную таблицу, с которой вы можете выполнять любые операции, какие пожелаете (например, агрегацию, соединение, сортировку данных и т. д.). Поэтому возможен и такой запрос:

SELECT t.title, p.fullname, p.email
FROM xpath_table('article_id', 'article_xml', 'articles',
                 '/article/title|/article/author/@id',
                 'xpath_string(article_xml,''/article/@date'') > ''2003-03-20'' ')
       AS t(article_id integer, title text, author_id integer),
     tblPeopleInfo AS p
WHERE t.author_id = p.person_id;

в качестве более сложного примера. Разумеется, для удобства весь этот запрос можно завернуть в представление.

Результаты с несколькими значениями

Функция xpath_table предполагает, что результаты каждого запроса XPath могут состоять из нескольких значений, поэтому количество возвращенных этой функцией строк может не совпадать с количеством входных документов. Первая возвращенная строка содержит первый результат каждого запроса, вторая — второй результат и т. д. Если один запросов возвращает меньше значений, чем другие, вместо недостающих значений будет возвращаться NULL.

В некоторых случаях пользователь будет знать, что данный запрос XPath возвратить только один результат (возможно, уникальный идентификатор документа), — если он используется рядом с запросом XPath, возвращающим несколько результатов, результат с одним значением появится только в первой строке результата. Чтобы решить эту проблему, можно использовать поле ключа и соединить результат с более простым запросом XPath. Например:

CREATE TABLE test (
    id int PRIMARY KEY,
    xml text
);

INSERT INTO test VALUES (1, '<doc num="C1">
<line num="L1"><a>1</a><b>2</b><c>3</c></line>
<line num="L2"><a>11</a><b>22</b><c>33</c></line>
</doc>');

INSERT INTO test VALUES (2, '<doc num="C2">
<line num="L1"><a>111</a><b>222</b><c>333</c></line>
<line num="L2"><a>111</a><b>222</b><c>333</c></line>
</doc>');

SELECT * FROM
  xpath_table('id','xml','test',
              '/doc/@num|/doc/line/@num|/doc/line/a|/doc/line/b|/doc/line/c',
              'true')
  AS t(id int, doc_num varchar(10), line_num varchar(10), val1 int, val2 int, val3 int)
WHERE id = 1 ORDER BY doc_num, line_num

 id | doc_num | line_num | val1 | val2 | val3
----+---------+----------+------+------+------
  1 | C1      | L1       |    1 |    2 |    3
  1 |         | L2       |   11 |   22 |   33

Чтобы получить doc_num в каждой строке, можно вызвать xpath_table дважды и соединить результаты:

SELECT t.*,i.doc_num FROM
  xpath_table('id', 'xml', 'test',
              '/doc/line/@num|/doc/line/a|/doc/line/b|/doc/line/c',
              'true')
    AS t(id int, line_num varchar(10), val1 int, val2 int, val3 int),
  xpath_table('id', 'xml', 'test', '/doc/@num', 'true')
    AS i(id int, doc_num varchar(10))
WHERE i.id=t.id AND i.id=1
ORDER BY doc_num, line_num;

 id | line_num | val1 | val2 | val3 | doc_num
----+----------+------+------+------+---------
  1 | L1       |    1 |    2 |    3 | C1
  1 | L2       |   11 |   22 |   33 | C1
(2 rows)

Функции XSLT

Если установлена libxslt, доступны следующие функции:

xslt_process

xslt_process(text document, text stylesheet, text paramlist) returns text

Эта функция применяет стилевое оформление XSL к документу и возвращает результат преобразования. Параметр paramlist — это список присваиваний значений параметрам, которые будут использоваться в преобразовании, заданных в форме a=1,b=2. Обратите внимание, что разбор параметров выполнен очень просто: значения параметров не могут содержать запятые!

Также есть версия xslt_process с двумя параметрами, которая не передает преобразованию никакие параметры.

Автор

Джон Грей (jgray@azuli.co.uk)

Разработку этого модуля спонсировала компания Torchbox Ltd. (www.torchbox.com). Этот модуль выпускается под той же лицензией BSD, что и PostgreSQL.

Клиентские приложения

В этом разделе рассматриваются клиентские приложения QHB, размещенные в contrib. Их можно запускать откуда угодно, независимо от того, где находится сервер базы данных. Информацию о клиентских приложениях, являющихся частью ядра дистрибутива QHB, см. в разделе Клиентские приложения QHB.

oid2name — разрешить OID и номера файловых узлов в каталоге данных QHB

vacuumlo — удалить потерянные большие объекты из базы данных QHB

oid2name

oid2name — разрешить OID и номера файловых узлов в каталоге данных QHB

Синтаксис

oid2name [параметр...]

Описание

oid2name — это программа-утилита, помогающая администраторам изучать файловую структуру QHB. Чтобы использовать ее, необходимо понимать файловую структуру базы данных, описанную в главе Физическое хранилище базы данных.

Примечание
Название «oid2name» является историческим и на самом деле вводит в заблуждение, поскольку при ее использовании вы по большей части будете иметь дело с номерами файловых узлов таблиц (которые представляют собой имена файлов, видимые в каталогах баз данных). Необходимо точно понимать разницу между OID таблиц и номерами файловых узлов таблиц!

Программа oid2name подключается к целевой базе данных и извлекает информацию об OID, номерах файловых узлов и/или именах таблиц. С ее помощью можно также просмотреть OID базы данных или табличного пространства.

Параметры

oid2name принимает следующие аргументы командной строки:

-f файловый_узел
--filenode=файловый_узел
Показать информацию о таблице, к которой относится файловый_узел.

-i
--indexes
Включить в распечатку индексы и последовательности.

-o oid
--oid=oid
Показать информацию о таблице с OID, равным oid.

-q
--quiet
Не выводить заголовки (полезно для скриптов).

-s
--tablespaces
Показать OID табличных пространств.

-S
--system-objects
Включить в распечатку системные объекты (те, что находятся в схемах information_schema, pg_toast и pg_catalog).

-t шаблон_имен_таблиц
--table=шаблон_имен_таблиц
Показать информацию о таблицах, соответствующих шаблону_имен_таблиц.

-V
--version
Вывести версию oid2name и завершиться.

-x
--extended
Отобразить более подробную информацию о каждом показываемом объекте: имя табличного пространства, имя схемы и OID.

-?
--help
Показать справку об аргументах командной строки oid2name и завершиться.

Кроме того, в качестве параметров подключения oid2name принимает следующие аргументы командной строки:

-d база_данных
--dbname=база_данных
База данных для подключения.

-h хост
--host=хост
Имя хост-компьютера, на котором работает сервер баз данных.

-H хост
Имя хост-компьютера, на котором работает сервер баз данных. Этот параметр считается устаревшим.

-p порт
--port=порт
Порт сервера базы данных.

-U имя_пользователя
--username=имя_пользователя
Имя пользователя, под которым производится подключение.

Для отображения информации об определенных таблицах выберите их с аргументом -o, -f и/или -t. Параметр -o принимает OID, -f — файловый узел, а -t — имя таблицы (на самом деле это шаблон LIKE, поэтому в нем можно задать что-то вроде foo%). Эти аргументы можно использовать в любом количестве; в распечатку будут включены все объекты, соответствующие любым из этих указаний. Но учтите, что эти параметры будут выбирать только объекты в базе данных, заданной ключом -d.

Если вы не задали -o, -f или -t, но передали -d, будут перечислены все таблицы в базе данных с именем, заданным в -d. В этом режиме набором выводимых сведений управляют параметры -S и -i.

Если вы не задали и аргумент -d, будет показан список OID баз данных. Как вариант, можно передать аргумент -s, чтобы получить список табличных пространств.

Переменные среды

PGHOST
PGPORT
PGUSER
Параметры подключения по умолчанию.

Переменная среды PG_COLOR указывает, использовать ли цвет в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Примечания

Программе oid2name требуется работающий сервер с исправными системными каталогами. Таким образом, в ситуациях восстановления после катастрофического повреждения базы данных ее использование ограничено.

Примеры

$ # Что вообще есть на этом сервере базы данных?
$ oid2name
All databases:
    Oid  Database Name  Tablespace
----------------------------------
  17228       alvherre  pg_default
  17255     regression  pg_default
  17227      template0  pg_default
      1      template1  pg_default

$ oid2name -s
All tablespaces:
     Oid  Tablespace Name
-------------------------
    1663       pg_default
    1664        pg_global
  155151         fastdisk
  155152          bigdisk

$ # Ладно, давайте взглянем на базу данных alvherre
$ cd $PGDATA/base/17228

$ # Получим первые 10 объектов БД (отсортированных по размеру) в табличном пространстве по умолчанию
$ ls -lS * | head -10
-rw-------  1 alvherre alvherre 136536064 sep 14 09:51 155173
-rw-------  1 alvherre alvherre  17965056 sep 14 09:51 1155291
-rw-------  1 alvherre alvherre   1204224 sep 14 09:51 16717
-rw-------  1 alvherre alvherre    581632 sep  6 17:51 1255
-rw-------  1 alvherre alvherre    237568 sep 14 09:50 16674
-rw-------  1 alvherre alvherre    212992 sep 14 09:51 1249
-rw-------  1 alvherre alvherre    204800 sep 14 09:51 16684
-rw-------  1 alvherre alvherre    196608 sep 14 09:50 16700
-rw-------  1 alvherre alvherre    163840 sep 14 09:50 16699
-rw-------  1 alvherre alvherre    122880 sep  6 17:51 16751

$ # Поинтересуемся, что за файл 155173...
$ oid2name -d alvherre -f 155173
From database "alvherre":
  Filenode  Table Name
----------------------
    155173    accounts

$ # Можно запросить информацию о нескольких объектах
$ oid2name -d alvherre -f 155173 -f 1155291
From database "alvherre":
  Filenode     Table Name
-------------------------
    155173       accounts
   1155291  accounts_pkey

$ # Можно добавить другие параметры и получить более подробную информацию с -x
$ oid2name -d alvherre -t accounts -f 1155291 -x
From database "alvherre":
  Filenode     Table Name      Oid  Schema  Tablespace
------------------------------------------------------
    155173       accounts   155173  public  pg_default
   1155291  accounts_pkey  1155291  public  pg_default

$ # Показать объем, который занимает на диске каждый объект БД
$ du [0-9]* |
> while read SIZE FILENODE
> do
>   echo "$SIZE       `oid2name -q -d alvherre -i -f $FILENODE`"
> done
16            1155287  branches_pkey
16            1155289  tellers_pkey
17561            1155291  accounts_pkey
...

$ # То же самое, но отсортированное по размеру
$ du [0-9]* | sort -rn | while read SIZE FN
> do
>   echo "$SIZE   `oid2name -q -d alvherre -f $FN`"
> done
133466             155173    accounts
17561            1155291  accounts_pkey
1177              16717  pg_proc_proname_args_nsp_index
...

$ # Если хотите просмотреть содержимое табличных пространств, воспользуйтесь каталогом pg_tblspc
$ cd $PGDATA/pg_tblspc
$ oid2name -s
All tablespaces:
     Oid  Tablespace Name
-------------------------
    1663       pg_default
    1664        pg_global
  155151         fastdisk
  155152          bigdisk

$ # Объекты каких баз данных находятся в табличном пространстве «fastdisk»?
$ ls -d 155151/*
155151/17228/  155151/PG_VERSION

$ # И что это еще за база данных 17228?
$ oid2name
All databases:
    Oid  Database Name  Tablespace
----------------------------------
  17228       alvherre  pg_default
  17255     regression  pg_default
  17227      template0  pg_default
      1      template1  pg_default

$ # Давайте посмотрим, какие объекты этой базы данных находятся в этом табличном пространстве.
$ cd 155151/17228
$ ls -l
total 0
-rw-------  1 postgres postgres 0 sep 13 23:20 155156

$ # Ладно, это довольно маленькая таблица... и что это за таблица?
$ oid2name -d alvherre -f 155156
From database "alvherre":
  Filenode  Table Name
----------------------
    155156         foo

Автор

Б. Палмер (bpalmer@crimelabs.net)

vacuumlo

vacuumlo — удалить потерянные большие объекты из базы данных QHB

Синтаксис

vacuumlo [параметр...] имя_бд...

Описание

vacuumlo — это простая программа-утилита, которая удалит все потерянные «большие» объекты из базы данных QHB. Потерянным большим объектом (БО) считается любой БО, OID не фигурирует ни в одном столбце данных oid или lo в базе данных.

Если вы пользуетесь этой утилитой, вас также может заинтересовать триггер lo_manage в модуле lo. lo_manage полезен тем, что пытается предотвратить образование потерянных БО в принципе.

Обрабатываются все базы данных, перечисленные в командной строке.

Параметры

vacuumlo принимает следующие аргументы командной строки:

-l предел
--limit=предел
Удалять в одной транзакции не более заданного предела больших объектов (количество по умолчанию — 1000). Поскольку сервер запрашивает блокировку для каждого удаляемого БО, удаление слишком большого количества БО в одной транзакции чревато превышением лимита max_locks_per_transaction. Если вы хотите удалить все в одной транзакции, установите этот предел равным нулю.

-n
--dry-run
Не удалять ничего, просто показать предполагаемые операции.

-v
--verbose
Выводить множество сообщений о прогрессе.

-V
--version
Вывести версию vacuumlo и завершиться.

-?
--help
Показать справку об аргументах командной строки vacuumlo и завершиться.

Кроме того, в качестве параметров подключения vacuumlo принимает следующие аргументы командной строки:

-h хост
--host=хост
Имя хост-компьютера, на котором работает сервер баз данных.

-p порт
--port=порт
Порт сервера базы данных.

-U имя_пользователя
--username=имя_пользователя
Имя пользователя, под которым производится подключение.

-w
--no-password
Не запрашивать ввод пароля. Если серверу требуется аутентификация по паролю, и пароль недоступен с помощью иных средств, таких как файл .pgpass, попытка подключения завершится неудачно. Этот параметр может быть полезен в пакетных заданиях и скриптах, где нет пользователя, чтобы ввести пароль.

-W
--password
Принудительно запрашивать пароль перед подключением к базе данных.

Это несущественный параметр, так как vacuumlo автоматически запросит пароль, если сервер требует аутентификацию по паролю. Однако чтобы выяснить это, vacuumlo потребуется дополнительная попытка подключения к серверу. В некоторых случаях имеет смысл ввести -W, чтобы исключить эту лишнюю попытку.

Переменные среды

PGHOST
PGPORT
PGUSER
Параметры подключения по умолчанию.

Переменная среды PG_COLOR указывает, использовать ли цвет в диагностических сообщениях. Возможные значения: always (всегда), auto (автоматически) и never (никогда).

Примечания

Утилита vacuumlo работает следующим образом: сначала vacuumlo конструирует временную таблицу, содержащую все OID больших объектов в выбранной базе данных. Затем она сканирует все столбцы в базе данных, имеющие тип oid или lo, и удаляет соответствующие записи из временной таблицы. (Примечание: рассматриваются только типы именно с этими именами; в частности, домены на их базе не рассматриваются.) Оставшиеся записи во временной таблице указывают на потерянные БО, которые и удаляются.

Автор

Питер Маунт (peter@retep.org.uk)

Сторонние расширения

PostGIS (Spatial and Geographic Objects for PostgreSQL)

Patroni (Управление высокодоступными кластерами)

В данном разделе представлены внешние расширения проверенные на совместимость с QHB.

PostGIS (Spatial and Geographic Objects for PostgreSQL)

Данное расширение вы также можете установить для QHB. На текущий момент поддерживаются пакеты для Centos 7,8 а также для Alt Сервер 9 (поддержка ограничена).

Для установки PostGIS вам потребуется установленный QHB с инициализированным кластером БД. Для получения информации по установке и настройке QHB обратитесь в соответствующий раздел документации (см. Краткая инструкция по начальной загрузке и установке). )

Примечание
Более подробную информацию о расширении PostGIS, а также документацию к нему можно найти на официальном сайте продукта.

Установка последней версии пакета PostGIS_QHB

Информация актуальная для версий QHB 1.4.x и 1.5.0 Для ОС отличных от Centos 7 пакет доступен по запросу, для получения обратитесь на qhb.support@quantom.info.

Centos 7

Подключите EPEL и PostgreSQL YUM repository

sudo yum install epel-release \
   https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/\
pgdg-redhat-repo-latest.noarch.rpm

sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm

Требуемые версии зависимых пакетов будут скачаны оттуда.

Установите репозиторий и пакет:

sudo yum-config-manager --add-repo https://repo.quantom.info/qhb//std-1/centos/7/x86_64/qhb.repo

sudo yum install postgis31_14_qhb

Установка расширения PostGIS на кластере QHB

После установки пакета расширения возможно выполнить установку расширения, для используемой БД.

Для установки расширения подключитесь к БД любимым инструментом и выполните:

create extension postgis;

Установка ранних версий PostGIS

Информация актуальная для версий QHB ниже 1.4.x

Установка PostGIS на Centos 7/8

Подключите EPEL и PostgreSQL YUM repository

sudo yum install epel-release \
   https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/\
pgdg-redhat-repo-latest.noarch.rpm

для Centos 7.

Или

sudo dnf install epel-release \
   https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/\
pgdg-redhat-repo-latest.noarch.rpm

для Centos 8.

Также, для Centos 8 выполните:

sudo dnf config-manager --set-enabled powertools

Установите, если ранее не был установлен, репозиторий программного продукта СУБД «Квант-Гибрид» командой:

sudo rpm --import https://repo.quantom.info/qhb//keys/RPM-GPG-KEY-qhb

и далее, для CentOS 7:

sudo yum-config-manager --add-repo https://repo.quantom.info/qhb//std-1/centos/7/x86_64/qhb.repo

или для CentOS 8:

sudo dnf config-manager --add-repo https://repo.quantom.info/qhb//std-1/centos/8/x86_64/qhb.repo

Установите PostGIS доступной версии командой, например:

для CentOS 7:

sudo yum install https://repo.quantom.info/qhb//std-1/centos/7/x86_64/postgis30_12-3.0.4-1.rhel7.x86_64.rpm

или

sudo yum install https://repo.quantom.info/qhb//std-1/centos/7/x86_64/postgis31_12-3.1.4-1.rhel7.x86_64.rpm

для CentOS 8 выполните один из вариантов, в зависимости от нужной версии PostGIS:

sudo dnf install https://repo.quantom.info/qhb//std-1/centos/8/x86_64/postgis30_12-3.0.4-1.rhel8.x86_64.rpm

или

sudo dnf install https://repo.quantom.info/qhb//std-1/centos/8/x86_64/postgis31_12-3.1.4-1.rhel8.x86_64.rpm

Установка PostGIS на Fedora 33/34

Установите, если ранее не был установлен, репозиторий программного продукта СУБД «Квант-Гибрид» и репозиторий с зависимыми пакетами для PostGIS:

sudo rpm --import https://repo.quantom.info/qhb//keys/RPM-GPG-KEY-qhb

и далее, для Fedora 33:

sudo dnf config-manager --add-repo https://repo.quantom.info/qhb/std-1/fedora/33/x86_64/qhb.repo
sudo dnf install https://download.postgresql.org/pub/repos/yum/reporpms/F-33-x86_64/pgdg-fedora-repo-latest.noarch.rpm

или для Fedora 34:

sudo dnf config-manager --add-repo https://repo.quantom.info/qhb/std-1/fedora/34/x86_64/qhb.repo
sudo dnf install https://download.postgresql.org/pub/repos/yum/reporpms/F-34-x86_64/pgdg-fedora-repo-latest.noarch.rpm

Скачайте и установите PostGIS доступной версии командой, например:

для Fedora 33:

sudo dnf install https://repo.quantom.info/qhb//std-1/fedora/33/x86_64/postgis30_12-3.0.4-1.f33.x86_64.rpm

или

sudo dnf install https://repo.quantom.info/qhb//std-1/fedora/33/x86_64/postgis31_12-3.1.4-1.f33.x86_64.rpm

для Fedora 34 выполните один из вариантов, в зависимости от нужной версии PostGIS:

sudo dnf install https://repo.quantom.info/qhb//std-1/fedora/34/x86_64/postgis30_12-3.0.4-1.f34.x86_64.rpm

или

sudo dnf install https://repo.quantom.info/qhb//std-1/fedora/34/x86_64/postgis31_12-3.1.4-1.f34.x86_64.rpm

Установка PostGIS на Альт Сервер 9

Скачайте пакет PostGIS c репозитория СУБД «Квант-Гибрид» командой, например:

wget -c -r -l1 -A postgresql12-postgis-3.1.1-alt0.p9.1.x86_64.rpm -nd -np https://repo.quantom.info/qhb//std-1/centos/8/x86_64/

Установите PostGIS:

apt-get install postgresql12-postgis-3.1.1-alt0.p9.1.x86_64.rpm

Patroni

Общее описание

Patroni — это Python-приложение для создания высокодоступных кластеров QHB на основе потоковой репликации. Оно используется такими компаниями как Red Hat, IBM Compose, Zalando и многими другими. С его помощью можно преобразовать систему из ведущего и ведомых узлов (primary — replica) в высокодоступный кластер с поддержкой автоматического контролируемого (switchover) и аварийного (failover) переключения. Patroni позволяет легко добавлять новые реплики в существующий кластер, поддерживает динамическое изменение конфигурации QHB одновременно на всех узлах кластера и множество других возможностей, таких как синхронная репликация, настраиваемые действия при переключении узлов, REST API, возможность запуска пользовательских команд для создания реплики.

Одна из возможных комбинаций стека технологий для организации высокодоступных кластеров:

QHB в качестве СУБД;

Patroni в качестве решения для кластеризации;

etcd или consul в качестве распределенного хранилища для Patroni.

Установка компонентов

etcd

Пример установки на CentOS 7: yum install etcd.

Также возможна установка через пакеты или исходный код по документации.

Рекомендуется устанавливать etcd на отдельных машинах, где не будет находится Patroni и QHB, так как для etcd очень важна нагрузка на диски. Для штатной работы кластера etcd минимальное количество нод - три. Для разработки и тестирования можно использовать одну ноду.

Consul

В связи с ограничениями Hashicorp на данный момент установка возможна только с использованием VPN.

Установка через пакеты с ресурса https://pkgs.org/download/consul, либо по документации.

Серверы Consul — это место, где хранятся и реплицируются данные Patroni. Поэтому рекомендованное минимальное количество нод - 3, для разработки и тестирования можно использовать 1 ноду. Для продуктивных высоконагруженных системы следует разворачивать сервисы Consul на отдельных от Patroni и QHB машинах.

Клиенты Consul также является членом системы и может подключаться к кластеру серверов Consul для получения информации об инфраструктуре. Клиента можно расположить на сервере с Patroni и QHB для подключения к кластеру серверов Consul через локального клиента Consul.

QHB

Установка через пакеты из репозитория QHB.

Не требуется создание и запуск сервиса QHB, а также инициализация кластера базы данных. Все управление кластером СУБД осуществляется через Patroni.

Patroni

Для работы Patroni требуется установить Python версии не ниже 3.6.

Для CentOS 8 установка Python происходит так:

dnf update -y
dnf install python3 -y
alternatives --set python /usr/bin/python3

Для работы Patroni требуется пакет psycopg2-binary:

python3.6 -m pip install -U pip
python3.6 -m pip install -U setuptools
pip3 install psycopg2-binary

Patroni требуется установить на тех же машинах, где и сервера QHB.

Установить Patroni можно через пакет из репозитория QHB:

dnf install patroni-qhb2.1.0-3.rhel7.x86_64 (для Centos 7);

dnf install patroni-qhb2.1.0-3.rhel8.x86_64 (для Centos 8).

Можно скачать rpm-пакет по следующим ссылкам и установить через утилиту rpm.

Для Centos 7:

https://repo.quantom.info/qhb/std-1/centos/7/x86_64/patroni-qhb2.1.0-3.rhel7.x86_64.rpm

rpm -i patroni-qhb2.1.0-3.rhel7.x86_64.rpm

Для Centos 8:

https://repo.quantom.info/qhb/std-1/centos/8/x86_64/patroni-qhb2.1.0-3.rhel8.x86_64.rpm
rpm -i patroni-qhb2.1.0-3.rhel8.x86_64.rpm

При необходимости, выполните установку недостающих необходимых пакетов, например для CentOS 7:

yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
yum install https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/p/python36-PyYAML-3.13-1.el7.x86_64.rpm
yum install https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/p/python36-psutil-5.6.7-1.el7.x86_64.rpm
yum install https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/p/python36-click-6.7-8.el7.noarch.rpm
yum install https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/p/python36-six-1.14.0-3.el7.noarch.rpm
yum install https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/p/python36-dateutil-2.4.2-5.el7.noarch.rpm
yum install https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/p/python36-prettytable-0.7.2-19.el7.noarch.rpm
yum install https://download.postgresql.org/pub/repos/yum/common/redhat/rhel-7-x86_64/python3-cdiff-1.0-1.rhel7.noarch.rpm
yum install https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/p/python3-psycopg2-2.7.7-2.el7.x86_64.rpm
yum install https://download.postgresql.org/pub/repos/yum/common/redhat/rhel-7-x86_64/python3-ydiff-1.2-10.rhel7.noarch.rpm

Для работы с базой etcd или consul, требуется установить зависимости для Patroni:

pip3 install patroni[etcd]

или

pip3 install patroni[consul]

Проверьте версию patroni:

patroni --version

Результат: patroni 2.1.0

Настройка

etcd

Выполняется независимо от Patroni и QHB, может быть выполнена предварительно (подробнее в официальной документации etcd). Для конфигурации каждого узла etcd, необходимо внести необходимые для запуска параметры в файл /etc/etcd/etcd.conf Также предварительно на каждом из узлов необходимо создать директорию под данные etcd, например /var/lib/etcd/default.etcd Ниже представлен пример минимальной конфигурации узлов на 3 машинах с адресами: X.X.X.101, X.X.X.102, X.X.X.103.

Для первого узла:

ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="http://X.X.X.101:2380"
ETCD_LISTEN_CLIENT_URLS="http://X.X.X.101:2379,http://127.0.0.1:2379"
ETCD_NAME="etcd0"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://X.X.X.101:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://X.X.X.101:2379"
ETCD_INITIAL_CLUSTER="etcd0=http://X.X.X.101:2380,etcd1=http://X.X.X.102:2380,etcd3=http://X.X.X.103:2380"
ETCD_INITIAL_CLUSTER_TOKEN="qhb_token"

Для второго узла:

ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="http://X.X.X.102:2380"
ETCD_LISTEN_CLIENT_URLS="http://X.X.X.102:2379,http://127.0.0.1:2379"
ETCD_NAME="etcd1"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://X.X.X.102:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://X.X.X..102:2379"
ETCD_INITIAL_CLUSTER="etcd0=http://X.X.X.101:2380,etcd1=http://X.X.X.102:2380,etcd3=http://X.X.X.103:2380"
ETCD_INITIAL_CLUSTER_TOKEN="qhb_token"

Для третьего узла:

ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="http://X.X.X.103:2380"
ETCD_LISTEN_CLIENT_URLS="http://X.X.X.103:2379,http://127.0.0.1:2379"
ETCD_NAME="etcd2"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://X.X.X.103:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://X.X.X.103:2379"
ETCD_INITIAL_CLUSTER="etcd0=http://X.X.X.101:2380,etcd1=http://X.X.X.102:2380,etcd3=http://X.X.X.103:2380"
ETCD_INITIAL_CLUSTER_TOKEN="qhb_token"

Базу данных etcd при пересоздании кластера нужно переинициализировать, удаляя данные в каталоге /var/lib/etcd/default.etcd.

Создайте сервис etcd на каждом из узлов для удобного управления кластером:

[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
WorkingDirectory=/var/lib/etcd/
EnvironmentFile=-/etc/etcd/etcd.conf
User=etcd
# set GOMAXPROCS to number of processors
ExecStart=/bin/bash -c "GOMAXPROCS=$(nproc) /usr/bin/etcd --name=\"${ETCD_NAME}\" --data-dir=\"${ETCD_DATA_DIR}\" --listen-client-urls=\"${ETCD_LISTEN_CLIENT_URLS}\""
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Consul

Выполняется независимо от Patroni и QHB, может быть выполнена предварительно (подробнее в официальной документации consul). Для конфигурации каждого узла кластера серверов consul, необходимо внести необходимые для запуска параметры в файл /etc/consul.d/config.json.

Ниже представлен пример минимальной конфигурации узлов на 3 машинах. Адреса и доменные имена:

X.X.X.101, qhb-srv-01.quantom.local;

X.X.X.102, qhb-srv-02.quantom.local;

X.X.X.103, qhb-srv-03.quantom.local.

Для первого узла кластера серверов consul (для второго и третьего узла изменить bind_addr и client_addr):

{
     "bind_addr": "X.X.X.101",
     "bootstrap_expect": 3,
     "client_addr": "X.X.X.101",
     "datacenter": "dc1",
     "data_dir": "/opt/consul",
     "domain": "consul",
     "enable_script_checks": true,
     "dns_config": {
         "enable_truncate": true,
         "only_passing": true
     },
     "enable_syslog": true,
     "encrypt": "v6+HtK6JQ6kX2XgYkSFQM9KFXF1YeGyFHcRo6hWZbjI=",
     "leave_on_terminate": true,
     "log_level": "INFO",
     "rejoin_after_leave": true,
     "retry_join": [
         "qhb-srv-01.quantom.local",
         "qhb-srv-02.quantom.local",
         "qhb-srv-03.quantom.local"
     ],
     "server": true,
     "start_join": [
         "qhb-srv-01.quantom.local",
         "qhb-srv-02.quantom.local",
         "qhb-srv-03.quantom.local"
     ],
    "ui_config": { "enabled": true }
}

Для подключения к кластеру серверов consul можно создать клиента consul (например, разместить на серверах с Patroni). При такой конфигурации Patroni подключается к локальному клиенту consul, который обращается ко всему кластеру серверов consul.

Для конфигурации клиента consul, необходимо внести необходимые для запуска параметры в файл /etc/consul.d/config.json.

Пример минимальной конфигурации клиента:

{
     "bind_addr": "X.X.X.104",
     "client_addr": "X.X.X.104",
     "datacenter": "dc1",
     "node_name": "client01",
     "data_dir": "/opt/consul",
     "domain": "consul",
     "enable_script_checks": true,
     "dns_config": {
         "enable_truncate": true,
         "only_passing": true
     },
     "enable_syslog": true,
     "encrypt": "v6+HtK6JQ6kX2XgYkSFQM9KFXF1YeGyFHcRo6hWZbjI=",
     "leave_on_terminate": true,
     "log_level": "INFO",
     "rejoin_after_leave": true,
     "retry_join": [
         "qhb-srv-01.quantom.local",
         "qhb-srv-02.quantom.local",
         "qhb-srv-03.quantom.local"
     ],
     "server": false,
     "start_join": [
         "qhb-srv-01.quantom.local",
         "qhb-srv-02.quantom.local",
         "qhb-srv-03.quantom.local"
     ],
    "ui_config": { "enabled": true }
}

Каждую конфигурацию необходимо проверить на валидность:

consul validate /etc/consul.d/config.json

Создайте сервис consul на каждом из узлов для удобного управления кластером:

[Unit]
Description=Consul Service Discovery Agent
Documentation=https://www.consul.io/
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=consul
Group=consul
ExecStart=/usr/bin/consul agent \
    -node=qhb-srv-01.quantom.local \
    -config-dir=/etc/consul.d
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGINT
TimeoutStopSec=5
Restart=on-failure
SyslogIdentifier=consul

[Install]
WantedBy=multi-user.target

Patroni

На каждом хосте с Patroni необходимо создать директорию под кластер баз данных QHB и раздать необходимые права пользователю qhb, например /mnt/patroni:

mkdir -p /mnt/patroni
chown qhb:qhb /mnt/patroni
chmod 700 /mnt/patroni

Создайте конфигурационный файл с расширением .yml на каждом хосте с Patroni. Подробнее, о параметрах конфигурации, в документации.

Кроме основных параметров конфигурации, указанных в документации Patroni, в релизе patroni-qhb2.1.0-3 добавлены дополнительные параметры для управления QHB:

#QHB parameters
  hba_file: qhb_hba.conf
  ident_file: qhb_ident.conf
  database: qhb
  config_base_name: qhb

Ниже представлен пример минимальной конфигурации кластера Patroni на 2 хостах с адресами: X.X.X.104, X.X.X.105.

В примере указаны параметры подключения к etcd, для работы с consul - раскомментируйте и добавьте необходимые строки

qhb0.yml для первого узла кластера Patroni:

scope: cluster
#namespace: /service/
name: qhb0

restapi:
  listen: 127.0.0.1:8008
  connect_address: 127.0.0.1:8008

etcd:
#Use "hosts" to provide multiple endpoints
  hosts:
  - X.X.X.101:2379
  - X.X.X.102:2379
  - X.X.X.103:2379

#consul:
  #host: X.X.X.104:8500

bootstrap:
  # this section will be written into etcd/Consul:/<namespace>/<scope>/config after initializing new cluster
  # and all other cluster members will use it as a `global configuration`
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    maximum_lag_on_failover: 1048576
    postgresql:
      use_pg_rewind: true
  # some desired options for 'initdb'
  initdb:  # Note: It needs to be a list (some options need values, others are switches)
  - encoding: UTF8
  - data-checksums

  pg_hba:  # Add following lines to qhb_hba.conf after running 'initdb'
  - host replication replicator 127.0.0.1/32 md5
  - host replication replicator X.X.X.0/24 md5
  - host all all 0.0.0.0/0 md5

  # Some additional users users which needs to be created after initializing new cluster
  users:
    admin:
      password: admin
      options:
        - createrole
        - createdb
postgresql:
  listen: 127.0.0.1,X.X.X.104,::1:5432
  connect_address: X.X.X.104:5432
  data_dir: /mnt/patroni
  bin_dir: /usr/local/qhb/bin
  pgpass: /tmp/pgpass0
  authentication:
    replication:
      username: replicator
      password: rep-pass
    superuser:
      username: qhb
      password: qhb
    rewind:
      username: rewind_user
      password: rewind_password
  parameters:
    unix_socket_directories: '.'
  # QHB parameters
  hba_file: qhb_hba.conf
  ident_file: qhb_ident.conf
  database: qhb
  config_base_name: qhb

tags:
  nofailover: false
  noloadbalance: false
  clonefrom: false

qhb1.yml для второго узла кластера Patroni:

scope: cluster
#namespace: /service/
name: qhb1

restapi:
  listen: 127.0.0.1:8008
  connect_address: 127.0.0.1:8008

etcd:
#Use "hosts" to provide multiple endpoints
  hosts:
  - X.X.X.101:2379
  - X.X.X.102:2379
  - X.X.X.103:2379

#consul:
  #host: X.X.X.105:8500

bootstrap:
  # this section will be written into etcd/Consul:/<namespace>/<scope>/config after initializing new cluster
  # and all other cluster members will use it as a `global configuration`
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    maximum_lag_on_failover: 1048576
    postgresql:
      use_pg_rewind: true
  # some desired options for 'initdb'
  initdb:  # Note: It needs to be a list (some options need values, others are switches)
  - encoding: UTF8
  - data-checksums

  pg_hba:  # Add following lines to qhb_hba.conf after running 'initdb'
  - host replication replicator 127.0.0.1/32 md5
  - host replication replicator X.X.X.0/24 md5
  - host all all 0.0.0.0/0 md5

  # Some additional users users which needs to be created after initializing new cluster
  users:
    admin:
      password: admin
      options:
        - createrole
        - createdb
postgresql:
  listen: 127.0.0.1,X.X.X.105,::1:5432
  connect_address: X.X.X.105:5432
  data_dir: /mnt/patroni
  bin_dir: /usr/local/qhb/bin
  pgpass: /tmp/pgpass0
  authentication:
    replication:
      username: replicator
      password: rep-pass
    superuser:
      username: qhb
      password: qhb
    rewind:
      username: rewind_user
      password: rewind_password
  parameters:
    unix_socket_directories: '.'
  # QHB parameters
  hba_file: qhb_hba.conf
  ident_file: qhb_ident.conf
  database: qhb
  config_base_name: qhb

tags:
  nofailover: false
  noloadbalance: false
  clonefrom: false

Создайте сервис patroni на каждом из узлов для удобного управления кластером Patroni:

# This is an example systemd config file for Patroni
# You can copy it to "/etc/systemd/system/patroni.service",
[Unit]
Description=Runners to orchestrate a high-availability PostgreSQL
After=syslog.target network.target
[Service]
Type=simple
User=qhb
Group=qhb
# Read in configuration file if it exists, otherwise proceed
EnvironmentFile=-/etc/patroni_env.conf
WorkingDirectory=~
# Where to send early-startup messages from the server
# This is normally controlled by the global default set by systemd
#StandardOutput=syslog
# Pre-commands to start watchdog device
# Uncomment if watchdog is part of your patroni setup
#ExecStartPre=-/usr/bin/sudo /sbin/modprobe softdog
#ExecStartPre=-/usr/bin/sudo /bin/chown postgres /dev/watchdog
# Start the patroni process
ExecStart=/usr/bin/patroni /opt/qhb0.yml
# Send HUP to reload from patroni.yml
ExecReload=/bin/kill -s HUP $MAINPID
# only kill the patroni process, not it's children, so it will gracefully stop postgres
KillMode=process
# Give a reasonable amount of time for the server to start up/shut down
TimeoutSec=30
# Do not restart the service if it crashes, we want to manually inspect database on failure
Restart=no
[Install]
WantedBy=multi-user.target

Запуск компонентов

etcd

Предварительно убедитесь, что у вас добавлены необходимые правила в firewall. Для запуска etcd через сервис, на всех узлах необходимо выполнить systemctl start etcd команда на всех узлах завершится, когда узлы найдут друг друга, т.е. когда команда будет выполнена на втором и третьем хосте. При пересоздании кластера нужно предварительно очистить базы etcd.

Состав кластера etcd можно посмотреть командой etcdctl member list.

Consul

Предварительно убедитесь, что у вас добавлены необходимые правила в firewall.

Для запуска etcd через сервис, на всех узлах серверов и клиентах необходимо выполнить systemctl start consul.

По адресу http(s) любого участника кластера, например: http://X.X.X.101:8500 , можно посмотреть состав кластера (сервера и клиенты) и текущего лидера. Подробнее в официальной документации Consul.

Patroni

Предварительно убедитесь, что у вас добавлены необходимые правила в firewall.

Запуск необходимо производить только после запуска распределенного хранилища - etcd/consul.

Для запуска etcd через сервис, на всех узлах необходимо выполнить systemctl start patroni.

Состояние и состав кластера Patroni можно посмотреть на любом узле, например patronictl -c /opt/qhb0.yml list.

При штатной работе вы увидите State - running у всех участников кластера.

Работа, переключение

При остановке работы СУБД на хосте 104, СУБД на хосте 105 становится лидером.

При включении в работу СУБД на хосте 104, она становится репликой.

Если останавливается работа мастера на хосте 105, мастером становится хост 104.

См. также

Документация по etcd

Документация по Consul

Документация по Patroni

Поддерживаемые платформы

Платформа (то есть комбинация архитектуры процессора и операционной системы) считается поддерживаемой сообществом разработчиков QHB, если код адаптирован для работы на этой платформе и в настоящее время успешно собирается и проходит на ней регрессионные тесты.

В настоящее время тестирование совместимости в основном выполняется автоматически силами сообщества разработчиков QHB.

Если вы заинтересованы в использовании QHB на платформе, еще не представленной в данном разделе, но уверены, что код на ней работает или может работать, пожалуйста, сообщите нам по адресу qhb.support@quantom.info.

В текущей версии QHB 1.5.0 поддерживаются процессоры следующих архитектур:

x86_64

Сервер QHB собирается и будет работать в следующих операционных системах:

Linux
Возможна работа на всех последних дистрибутивах, но пока бинарные пакеты собираются только для операционных систем, перечисленных на странице загрузки.

Если вы столкнулись с проблемами установки на поддерживаемой платформе, пожалуйста, сообщите о них по адресу qhb.support@quantom.info.

Поддержка прикладных платформ

Прикладная платформа — это программное обеспечение, предназначенное для проектирования, разработки, выполнения и модернизации программных компонентов, автоматизирующих деятельность различных конкретных предприятий.

Поддержка решений 1С

2B: Поддержка решений 1С

Поддерживается платформа «1С:Предприятие», начиная с версии 8.3.18.

Дополнительные типы данных для совместимости с Microsoft SQL Server

В версии QHB 1.2.0 добавлены типы данных mchar и mvarchar, являющиеся аналогами типов char и varchar, но имеющие ряд особенностей поведения, повторяющих поведение типов nchar и nvarchar в Microsoft SQL Server.

Отличия от стандартных типов char и varchar в QHB:

сравнение без учета регистра, в том числе, если один из аргументов mchar/mvarchar, а второй нет (для сравнения с учетом регистра вводятся специальные версии операторов сравнения: &<, &<=, &=, &>= и &>).

mchar фиксированной длины дополняется пробелами справа, но при сравнениях эти пробелы игнорируются;

данные типа mchar/mvarchar хранятся в кодировке utf-16 (для пользователя это не важно);

для сравнения и приведения к верхнему/нижнему регистру используется ICU, поэтому результат не зависит от операционной системы;

функция получения подстроки substr(str, pos [, length]), а не substring.

Отличия от поведения nchar и nvarchar в Microsoft SQL Server:

mchar и mvarchar без указания длины трактуются как строки неопределенной неограниченной длины (похоже на nvarchar(max)); mchar неопределенной длины не дополняется пробелами справа (но явно заданные пробелы все-таки хранятся);

функция вычисления длины строки length(string), а не len, как в Microsoft SQL Server.

Для типов данных mchar и mvarchar поддерживаются индексы B-деревья и хеш-индексы.

fulleq — дополнительный оператор равенства для совместимости с Microsoft SQL Server

Для совместимости с Microsoft SQL Server в версии QHB 1.2.0 добавлен оператор fulleq (полное равенство) ==.

Оператор fulleq (==) отличается от обычного равенства (=) тем, что возвращает TRUE при сравнении двух значений NULL (обычное равенство возвращает NULL согласно стандарту ANSI SQL). Такое поведение принято в СУБД семейства Microsoft SQL.

Оператор == определен для следующих типов данных:

bool

bytea

char

mchar

varchar

mvarchar

name

int2

int4

int8

int2vector

text

oid

xid

cid

oidvector

float4

float8

macaddr

inet

cidr

date

time

timestamp

timestamptz

interval

timetz

Быстрое усечение временных таблиц

В QHB операция TRUNCATE транзакционная: ее можно откатить. Из-за этого она более дорогая, чем в других СУБД.

В версии 1.2.0 добавлена функция fasttruncate, которая очищает таблицу быстрее и предотвращает разрастание системного каталога pg_class. Пока ее можно применять только ко временным таблицам, благодаря чему очистка не вызывает конфликта с другим пользователем.

Внимание!
Функция fasttruncate является внетранзакционной: при откате транзакции очистка таблицы не будет отменена!

Пример использования:

SELECT fasttruncate ('pg_temp.tmp3')

Немедленное обновление статистики отдельных таблиц

Для сильно меняющихся таблиц, например временных, стандартные средства сбора статистики могут быть неэффективны. В версии QHB 1.2.0 появилась возможность немедленного обновления статистики таблицы после ее изменения, т. н. немедленный анализ (online analyze).

Сбор статистики вызывается после фиксации транзакции, модифицировавшей таблицу, при выполнении определенных условий. Настройка этого поведения осуществляется с помощью конфигурационных параметров, перечисленных ниже.

Конфигурационные параметры немедленного обновления статистики

online_analyze.enable (boolean)

Включение/выключение функции немедленного анализа.

По умолчанию, параметр устанавливается в значение false.

online_analyze.verbose (boolean)

Включение/выключение возможности записи в лог при сборе статистики (аналогично команде SQL ANALYZE VERBOSE).

По умолчанию, параметр устанавливается в значение false.

online_analyze.table_type (enum)

Указывает тип таблиц, для которых следует применять немедленный анализ. Возможные варианты значений: all (все), persistent (постоянные), temporary (временные), none (никакие).

По умолчанию, параметр устанавливается в значение temporary.

online_analyze.exclude_tables (string)

Указывает список таблиц, исключаемых из немедленного анализа. Например, "имя_таблицы_1, имя_таблицы_2 ..."**.

По умолчанию, параметр устанавливается в пустое значение ('').

online_analyze.include_tables (string)

Указывает список таблиц, подлежащих немедленному анализу (этот параметр перекрывает online_analyze.exclude_tables).

По умолчанию, параметр устанавливается в пустое значение ('').

Примечание
Значения этих двух параметров записываются в виде списка имен таблиц через запятую.

online_analyze.local_tracking (boolean)

Включение/выключение хранения статистики временных таблиц в локальном кэше.

По умолчанию, параметр устанавливается в значение false

online_analyze.capacity_threshold (integer)

Указывает максимальное число временных таблиц, сохраняемых в локальном кэше.

По умолчанию, параметр устанавливается в значение 100000.

Примечание
По умолчанию используется системная статистика, но имеет смысл хранить ее в локальном кэше процесса, обслуживающего сессию.

online_analyze.scale_factor (floating point)

Указывает процент измененных строк от всей таблицы, при котором начинается немедленный анализ.

По умолчанию, параметр устанавливается в значение 0.1.

online_analyze.threshold (integer)

Указывает минимальное число измененных строк, после которого может начаться немедленный анализ.

По умолчанию, параметр устанавливается в значение 50.

online_analyze.lower_limit (integer)

Указывает минимальное число строк в таблице, при котором может начаться немедленный анализ.

По умолчанию, параметр устанавливается в значение 0.

online_analyze.min_interval (integer)

Минимальный интервал времени между вызовами команды ANALYZE для конкретной таблицы (в миллисекундах).

По умолчанию, параметр устанавливается в значение 10000.

Поддержка указаний для планировщика, позволяющих выключать или подключать определенные индексы при выполнении запроса

В версии QHB 1.2.0 добавлены конфигурационные параметры для управления использованием индексов. С их помощью можно просить планировщик не использовать определенные индексы. Если вы хотите, чтобы конкретный индекс использовался, этих параметров недостаточно, одновременно с ними можно использовать параметр enable_seqscan и другие.

Эти новые параметры относятся к настройкам планирования. Как и для других настроек планирования, основным сценарием использования является изменение этих параметров на уровне транзакции, перед выполнением конкретного запроса. Технически возможно использование параметров на уровне базы данных или сервера, но если вам понадобилось выключить использование индекса на уровне базы данных, имеет смысл рассмотреть его удаление.

Конфигурационные параметры для управления использованием индексов

plantuner.disable_index (string)

Указывает список индексов, которые нельзя использовать.

plantuner.enable_index (string)

Указывает список индексов, которые все-таки можно использовать (этот параметр перекрывает plantuner.disable_index).

plantuner.only_index (string)

Указывает точный список индексов, которые можно использовать (этот параметр перекрывает plantuner.disable_index и plantuner.enable_index).

Примечание
Значения этих трех параметров записываются в виде списка имен индексов через запятую.

Разрешение имени индекса происходит для конкретного пользователя, даже если параметр задан на уровне сервера. Если индекс не найден по имени, например, когда индекс вообще не существует, он игнорируется.

plantuner.fix_empty_table (boolean)

Этот параметр меняет оценку планировщиком совсем пустых таблиц (только что созданных или опустошенных при помощи команды TRUNCATE). Если значение равно true, то QHB планирует в предположении, что эти таблицы так и будут оставаться пустыми. При значении false QHB по умолчанию будет считать, что таблицы содержат некоторое количество записей (обычно 20).

Особенности использования QHB

По сравнению с привычными настройками, параметрами или функциями, используемыми в схожих СУБД, существуют изменения, которые были внесены командой разработчиков QHB.

В разделе Изменения в настройках по умолчанию описаны те настройки, которые изначально, при разметке нового кластера QHB, будут иметь значение, отличное от привычного. В разделе Устаревшие функции и методы описаны функции и методы, от которых в QHB было решено отказаться по причине их устарелости.

Изменения в настройках по умолчанию

Параметр restart_after_crash получил значение по умолчанию off (выключен).

Устаревшие функции и методы

На данный момент устаревшие функции и методы отсутствуют.

Ограничения QHB

В Таблице 1 описаны различные жесткие ограничения QHB. Однако практические ограничения, такие как предел производительности или доступного дискового пространства, могут проявиться раньше, чем будут достигнуты абсолютные жесткие ограничения.

Таблица 1. Ограничения QHB

Параметр | Верхний предел | Комментарий
размер базы данных | без ограничений |
количество баз данных | 4 294 950 911 |
отношения в базе данных | 1 431 650 303 |
размер отношения | 32 ТБ | со значением BLCKSZ по умолчанию, равным 8192 байта
строк в таблице | ограничено количеством кортежей, которое может уместиться на 4 294 967 295 страниц |
столбцов в таблице | 1600 | дополнительно ограничено размером кортежа, который может уместиться на одной странице; см примечание ниже
размер поля | 1 ГБ |
длина идентификатора | 63 байта | можно увеличить, перекомпилировав QHB
индексов на таблицу | без ограничений | ограничен максимальным количеством отношений в базе данных
столбцов на индекс | 32 | можно увеличить, перекомпилировав QHB
ключи раздела | 32 | можно увеличить, перекомпилировав QHB

Максимальное количество столбцов таблицы дополнительно уменьшается, поскольку сохраняемый кортеж должен умещаться на одной странице кучи размером 8192 байта. Например, без учета заголовка кортеж, состоящий из 1600 столбцов int, будет занимать 6400 байт и может храниться на странице кучи, однако кортеж из 1600 столбцов bigint будет занимать 12 800 байт и, следовательно, на странице не поместится. Поля переменной длины таких типов, как text, varchar и char, могут хранить свои значения отдельно, в таблице TOAST, когда эти значения достаточно велики для этого. При этом внутри кортежа в куче таблицы должен остаться только 18-байтовый указатель. Для более коротких полей переменной длины используется 4-байтовый или 1-байтовый заголовок, а значение сохраняется внутри кортежа кучи.

Столбцы, которые были удалены из таблицы, также влияют на максимальное количество столбцов. Более того, хотя значения удаленных столбцов для вновь создаваемых кортежей внутренне помечаются как NULL в заведенной специально для них битовой карте кортежа, эта карта тоже занимает место.

Страница загрузки

Бинарные дистрибутивы для поддерживаемых платформ можно получить в репозитории QHB.

QHB для CentOS 7 и 8

QHB для ROSA Enterprise Linux Server (RELS) 7

QHB для Альт 8 СП

QHB для Альт Сервер 9

QHB для Fedora 33, 34 и 35

QHB для Debian 9 и Astra Linux Special Edition «Смоленск» 1.6

QHB в виде docker контейнера

Обновление уже установленных пакетов

QHB для CentOS 7 и 8

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

Установите репозиторий программного продукта СУБД «Квант-Гибрид» командой:

rpm --import https://repo.quantom.info/qhb//keys/RPM-GPG-KEY-qhb

и далее, для CentOS 7:

yum-config-manager --add-repo https://repo.quantom.info/qhb//std-1/centos/7/x86_64/qhb.repo

или для CentOS 8:

dnf config-manager --add-repo https://repo.quantom.info/qhb//std-1/centos/8/x86_64/qhb.repo

Установите бинарные пакеты командой:

yum install qhb-core [qhb-contrib] [qcp] [qdl] [qdlm] [qbackup] [metricsd]

Например:

yum install qhb-core

В результате установки пакета ядра QHB будет создан пользователь qhb. Ядро QHB, по умолчанию, установится в каталог /usr/local/qhb.

Многие утилиты QHB устанавливаются в каталог /usr/local/qhb/bin. Если этот каталог указать в переменной окружения $PATH, это облегчит их запуск.

QHB для ROSA Enterprise Linux Server (RELS) 7

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

Установите репозиторий программного продукта СУБД «Квант-Гибрид» командой:

rpm --import https://repo.quantom.info/qhb//keys/RPM-GPG-KEY-qhb

и далее:

yum-config-manager --add-repo https://repo.quantom.info/qhb//std-1/centos/7/x86_64/qhb.repo

Установите бинарные пакеты командой:

yum install qhb-core [qhb-contrib] [qcp] [qdl] [qdlm] [qbackup] [metricsd]

Например:

yum install qhb-core

В результате установки пакета ядра QHB будет создан пользователь qhb. Ядро QHB, по умолчанию, установится в каталог /usr/local/qhb.

Многие утилиты QHB устанавливаются в каталог /usr/local/qhb/bin. Если этот каталог указать в переменной окружения $PATH, это облегчит их запуск.

QHB для Альт 8 СП

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

СУБД QHB поддерживает ОС Альт 8 СП только версии ядра 5.10.

Для установки потребуются дополнительные библиотеки libicu и libgostcrypto. Также, возможно, потребуется установка или обновление библиотеки glibc до версии 2.34 или выше, заранее проверьте версию вашей библиотеки.

Скачайте библиотеку (подходит версия 60.3-2) в любую локальную папку:

wget -c http://mirror.centos.org/centos/8/BaseOS/x86_64/os/Packages/libicu-60.3-2.el8_1.x86_64.rpm

Скачайте библиотеку libgostcrypto:

wget -c -r -l1 -A '*.rpm' -nd -np https://repo.quantom.info/qhb/std-1/centos/8/x86_64/libgostcrypto.0.6-0.6.1-1.x86_64.rpm

Внимание СУБД QHB для ОС Альт 8 СП поставляется на дисках. Для получения обратитесь qhb.support@quantom.info.

Скопируйте с диска все бинарные пакеты в локальную папку и установите командой, например:

apt-get install *.rpm

Вы можете установить не все пакеты QHB, а только необходимые вам. В этом случае вы можете перечислить в команде нужные вам пакеты rpm через пробел. Выбирайте последние версии из rpm пакетов, которые скачались.

Библиотека libicu и пакет qhb-core - обязательны.

В результате установки пакета ядра QHB будет создан пользователь qhb, ядро СУБД QHB, по умолчанию, будет установлено в каталог /usr/local/qhb. Многие утилиты QHB устанавливаются в каталог /usr/local/qhb/bin. Если этот каталог указать в переменной окружения $PATH, это облегчит их запуск.

QHB для Альт Сервер 9

Обратите внимание, что для СУБД «Квант-Гибрид» версии 1.5.0 на сервере под управлением OS Alt Server 9, должна быть установлена библиотека GLIBC версии не ниже 2.28.

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

Для установки потребуется дополнительная библиотека libicu. Также, возможно, потребуется установка или обновление библиотеки glibc.

Скачайте библиотеку (подходит версия 60.3-2) в любую локальную папку:

wget -c http://mirror.centos.org/centos/8/BaseOS/x86_64/os/Packages/libicu-60.3-2.el8_1.x86_64.rpm

Скачайте в эту же папку пакеты программного продукта СУБД «Квант-Гибрид» из репозитория командой:

wget -c -r -l1 -A '*.rpm' -nd -np https://repo.quantom.info/qhb//std-1/centos/8/x86_64/

Обновите описания пакетов:

apt-get update

Находясь в этой же локальной папке, установите бинарные пакеты командой, например:

apt-get install *.rpm

Вы можете установить не все пакеты QHB, а только необходимые вам. В этом случае вы можете перечислить в команде нужные вам пакеты rpm через пробел. Выбирайте последние версии из rpm пакетов, которые скачались.

Библиотека libicu и пакет qhb-core - обязательны.

В результате установки пакета ядра QHB будет создан пользователь qhb, ядро QHB, по умолчанию, установится в каталог /usr/local/qhb. Многие утилиты QHB устанавливаются в каталог /usr/local/qhb/bin. Если этот каталог указать в переменной окружения $PATH, это облегчит их запуск.

QHB для Fedora 33, 34 и 35

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

Установите репозиторий программного продукта СУБД «Квант-Гибрид» командой:

rpm --import https://repo.quantom.info/qhb//keys/RPM-GPG-KEY-qhb

и далее, для Fedora 33:

dnf config-manager --add-repo https://repo.quantom.info/qhb//std-1/fedora/33/x86_64/qhb.repo

для Fedora 34:

dnf config-manager --add-repo https://repo.quantom.info/qhb//std-1/fedora/34/x86_64/qhb.repo

или для Fedora 35:

dnf config-manager --add-repo https://repo.quantom.info/qhb//std-1/fedora/35/x86_64/qhb.repo

Установите бинарные пакеты командой:

dnf install qhb-core [qhb-contrib] [qcp] [qdl] [qdlm] [qbackup] [metricsd]

Например:

dnf install qhb-core

В результате установки пакета ядра QHB будет создан пользователь qhb. Ядро QHB, по умолчанию, установится в каталог /usr/local/qhb.

Многие утилиты QHB устанавливаются в каталог /usr/local/qhb/bin. Если этот каталог указать в переменной окружения $PATH, это облегчит их запуск.

QHB для Debian 9 и Astra Linux Special Edition «Смоленск» 1.6

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

Для подключения к репозиторию выполните следующую команду:

apt install gnupg2 apt-transport-https wget

Обратите внимание, что для исключения из OS Astra Linux отозванных сертификатов безопасности следует выполнить очистку согласно инструкции по прекращению использования нежелательного сертификата.

Для корректной работы репозитория СУБД «Квант-Гибрид» в файле конфигурации репозиториев /etc/apt/sources.list должна быть добавлена запись

deb [arch=amd64] https://repo.quantom.info/qhb/std-1/debian stretch main

Для Astra требуется установка корневых сертификатов. Установите их с помощью команды:

apt-get install ca-certificates

Установите репозиторий программного продукта СУБД «Квант-Гибрид» командой:

wget -qO - https://repo.quantom.info/qhb//keys/RPM-GPG-KEY-qhb --no-check-certificate | apt-key add -

Далее выполните команды:

echo 'deb https://repo.quantom.info/qhb//std-1/debian stretch main' >> /etc/apt/sources.list

и

apt update

Установите бинарные пакеты командой:

apt install qhb-core [qhb-contrib] [qcp] [qdl] [qdlm] [qbackup] [metricsd]

Например:

apt install qhb-core

В результате установки пакета ядра QHB будет создан пользователь qhb. Ядро QHB, по умолчанию, установится в каталог /usr/local/qhb.

Многие утилиты QHB устанавливаются в каталог /usr/local/qhb/bin. Если этот каталог указать в переменной окружения $PATH, это облегчит их запуск.

QHB в виде docker контейнера

Предлагается конфигурация: сервер СУБД «Квант-Гибрид» с расширениями и пул соединений QCP.

Получение Dockerfile и конфигураций

Создайте локальный каталог и разверните в нём скрипты docker из репозитория СУБД «Квант-Гибрид»:

wget https://repo.quantom.info/qhb//std-1/docker/qhb-docker-image.tar.gz -O - | tar -xz

Создание образов

Внимание!
Потребуется docker-compose. Здесь и далее, запускать docker-compose следует из-под обычного пользователя (не root). Возможно, для этого надо будет выполнить рекомендации по настройке docker.

Как правило, не требуется вручную создавать образ, т.к. он автоматически создаётся при первом запуске. Однако это можно сделать и явным образом:

docker-compose build

Замечание.
Если возникают проблемы с построением образа, связанные с недоступностью репозитория centos, нужно проверить сетевые разрешения. Иногда, перед построением, просто помогает выполнение таких команд:

sudo firewall-cmd --zone=public --add-masquerade --permanent
sudo firewall-cmd --reload

Запуск контейнера

Для запуска контейнеров СУБД «Квант-Гибрид» и QCP с настройками по умолчанию, выполните в текущем каталоге:

docker-compose up

Прервать выполнение можно с помощью Ctrl+C, или же выполнив из другой консоли в текущем каталоге:

docker-compose down

Для запуска контейнеров в режиме "демона", выполните:

docker-compose up -d

При первом запуске база данных будет автоматически инициализирована.

Посмотреть логи можно с помощью команды:

docker-compose logs

По умолчанию, база данных сохраняется на диске в каталоге qhb/pgdata.

Остановка контейнеров

Если контейнеры были запущены посредством docker-compose up, то для остановки достаточно нажать Ctrl+C. Если же контейнеры были запущены в режиме «демона», т. е. посредством docker-compose up -d, то для их остановки выполните (из любой консоли) в текущем каталоге: docker-compose down. Эту команду также можно выполнять и при остановленных контейнерах.

Настройка QCP

По умолчанию сервис QCP выключен. Для его запуска необходимо в файле docker-compose.yaml убрать (закомментировать) запись

entrypoint: ["echo", "Service qcp disabled"]

из раздела services: -> qcp:, а также настроить сервис, в частности, параметры подключения к серверу.

Настройка QCP осуществляется посредством редактирования файла ./qcp/config.yaml.

Обязательно замените в разделе servers:

%USER% - на имя пользователя, от имени которого запускается docker-compose, и

%DATABASE% - на имя базы данных, в простейшем случае база создается с именем, совпадающим с именем пользователя.

В дальнейшем, если вы создадите другую базу данных, этот параметр может потребоваться изменить. По умолчанию сервис QCP работает на порту 8080, при необходимости переопределите этот параметр. Обратите внимание, что настройки применяются только при запуске.

Отредактируйте файл docker-compose.yaml следующим образом:

В разделе services: -> qhb: -> volumes: замените

- ./qhb/pgdata:/qhb-data/

на

- /path/to/pgdata:/qhb-data/

где /path/to/pgdata — абсолютный путь до каталога базы данных с существующей базой данных.

В разделе services: -> qhb: -> environment: замените - USER на - USER=USERNAME

Запустите контейнер с помощью команды

docker-compose -e USER="USERNAME" up

где USERNAME — имя пользователя, владеющего каталогом /path/to/pgdata.

Пример отредактированного файла docker-compose.yaml:

version: "3.3"
services:
  qhb:
    build: qhb
    image: qhb
    network_mode: "host"
    environment:
      - USER=qhb
    volumes:
      - ./qhb/pgdata:/qhb-data/

  qcp:
    build: qcp
    image: qcp
    network_mode: "host"
    entrypoint: ["echo", "Service qcp disabled"]  # Comment this line if will use QCP
    volumes:
      - ./qcp/config.yaml:/config.yaml:ro

Обновление уже установленных пакетов

Внимание! Следует заметить, что при обновлении версии QHB, необходимо позаботиться о миграции имеющихся баз данных, поскольку при смене версии может также меняться формат файлов базы данных. Обратитесь к разделу Установка с обновлением. О том, что в новой версии QHB происходит изменение формата файлов базы данных, можно узнать в разделе документации Замечания к релизу.

Если QHB уже установлен, его обновления можно устанавливать с помощью пакетных менеджеров, имеющихся в ваших дистрибутивах операционных систем.

В любом случае, рекомендуется сделать бэкап исполняемых файлов СУБД, перед началом процедуры апгрейда. И сохранить резервную копию до момента окончания апгрейда.

Как правило, для этого достаточно выполнить команду:

(на примере Fedora)

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

dnf update

В этом случае обновятся все пакеты.

Или, с указанием конкретных пакетов:

dnf update qhb-core qhb-contrib qcp qdl qdlm qbackup metricsd

Иногда, для обновления метаданных полезно обновить информацию о пакетах:

dnf clean all

Краткие инструкции

Предоставляются краткие инструкции по различным задачам при использовании QHB.

Они не глубоко раскрывают различные тонкости и параметры, но предоставляют собой примерные, ознакомительные и наиболее употребительные сценарии.

Краткая инструкция по начальной загрузке и установке

Краткая инструкция по обновлению

Краткая инструкция по миграции кластера баз данных в QHB

Запуск и остановка сервера с помощью сервиса systemd

Пример настройки синхронной реплики

Инструкция по использованию QSS в режиме хранения пользовательского ключа на USB-носителе

Инструкция по использованию QSS в режиме хранения пользовательского ключа на крипто-токене

Инструкция по установке и использованию QCP

Работа с 1С

Инструкция по установке QHB под 1С

Конфигурирование QHB под 1С

Резервное копирование QHB под 1С

Пошаговая инструкция по начальной загрузке, установке и запуску

Описывается сценарий установки СУБД «Квант-Гибрид» в первый раз или установки и обновления при начальной инициализации кластера базы данных, если есть возможность произвести резервное копирование и восстановление данных во вновь создаваемый кластер.

Внимание!
В случаях, когда

необходимо обновление или миграция (бинарная) с других СУБД, или с младших версий СУБД «Квант-Гибрид»;

каталог базы данных уже существует и не может быть создан заново;

необходимо сохранить данные уже используемой базы данных в бинарном виде

обратитесь к разделу документации Краткая инструкция по обновлению.
Также настоятельно рекомендуется использовать процедуры Резервного копирования и восстановления.

Загрузка и установка бинарных пакетов

Загрузка и установка бинарных пакетов производится из репозитория СУБД «Квант-Гибрид». Обратитесь к разделу документации Поддерживаемые платформы для уточнения, какие именно платформы поддерживаются СУБД «Квант-Гибрид».

Примечание
При установке команды должны выполняться от пользователя с правами суперпользователя. Если пользователь не обладает правами суперпользователя, выполните в терминале следующую команду (понадобится ввести пароль суперпользователя):

su -

или временно повысьте привилегии пользователя до суперпользователя командой

sudo -i

Для Centos 7 и 8

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

Установите репозиторий программного продукта СУБД «Квант-Гибрид» командой:

rpm --import https://repo.quantom.info/qhb//keys/RPM-GPG-KEY-qhb

и далее, для CentOS 7:

yum-config-manager --add-repo https://repo.quantom.info/qhb//std-1/centos/7/x86_64/qhb.repo

или для CentOS 8:

dnf config-manager --add-repo https://repo.quantom.info/qhb//std-1/centos/8/x86_64/qhb.repo

Установите бинарные пакеты командой:

yum install qhb-core [qhb-contrib] [qcp] [qdl] [qdlm] [qbackup] [metricsd]

Например:

yum install qhb-core

В результате установки пакета ядра QHB будет создан пользователь qhb. Ядро QHB, по умолчанию, установится в каталог /usr/local/qhb.

Многие утилиты QHB устанавливаются в каталог /usr/local/qhb/bin. Если этот каталог указать в переменной окружения $PATH, это облегчит их запуск.

Смотрите дополнительно: Состав поставки, Страница загрузки

Для ROSA Enterprise Linux Server (RELS) 7

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

Установите репозиторий программного продукта СУБД «Квант-Гибрид» командой:

rpm --import https://repo.quantom.info/qhb//keys/RPM-GPG-KEY-qhb

и далее:

yum-config-manager --add-repo https://repo.quantom.info/qhb//std-1/centos/7/x86_64/qhb.repo

Установите бинарные пакеты командой:

yum install qhb-core [qhb-contrib] [qcp] [qdl] [qdlm] [qbackup] [metricsd]

Например:

yum install qhb-core

В результате установки пакета ядра QHB будет создан пользователь qhb. Ядро QHB, по умолчанию, установится в каталог /usr/local/qhb.

Многие утилиты QHB устанавливаются в каталог /usr/local/qhb/bin. Если этот каталог указать в переменной окружения $PATH, это облегчит их запуск.

Смотрите дополнительно: Состав поставки, Страница загрузки

Для Альт 8 СП

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

СУБД QHB поддерживает ОС Альт 8 СП только версии ядра 5.10.

Для установки потребуются дополнительные библиотеки libicu и libgostcrypto. Также, возможно, потребуется установка или обновление библиотеки glibc до версии 2.34 или выше, заранее проверьте версию вашей библиотеки.

Скачайте библиотеку (подходит версия 60.3-2) в любую локальную папку:

wget -c http://mirror.centos.org/centos/8/BaseOS/x86_64/os/Packages/libicu-60.3-2.el8_1.x86_64.rpm

Скачайте библиотеку libgostcrypto:

wget -c -r -l1 -A '*.rpm' -nd -np https://repo.quantom.info/qhb/std-1/centos/8/x86_64/libgostcrypto.0.6-0.6.1-1.x86_64.rpm

Внимание СУБД QHB для ОС Альт 8 СП поставляется на дисках. Для получения обратитесь qhb.support@quantom.info.

Скопируйте с диска все бинарные пакеты в локальную папку и установите командой, например:

apt-get install *.rpm

Вы можете установить не все пакеты QHB, а только необходимые вам. В этом случае вы можете перечислить в команде нужные вам пакеты rpm через пробел. Выбирайте последние версии из rpm пакетов, которые скачались.

Библиотека libicu и пакет qhb-core - обязательны.

В результате установки пакета ядра QHB будет создан пользователь qhb, ядро СУБД QHB, по умолчанию, будет установлено в каталог /usr/local/qhb. Многие утилиты QHB устанавливаются в каталог /usr/local/qhb/bin. Если этот каталог указать в переменной окружения $PATH, это облегчит их запуск.

Смотрите дополнительно: Состав поставки, Страница загрузки

Для Альт Сервер 9

Обратите внимание, что для СУБД «Квант-Гибрид» версии 1.5.0 на сервере под управлением OS Alt Server 9, должна быть установлена библиотека GLIBC версии не ниже 2.28.

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

Для установки потребуется дополнительная библиотека libicu. Также, возможно, потребуется установка или обновление библиотеки glibc.

Скачайте библиотеку (подходит версия 60.3-2) в любую локальную папку:

wget -c http://mirror.centos.org/centos/8/BaseOS/x86_64/os/Packages/libicu-60.3-2.el8_1.x86_64.rpm

Скачайте в эту же папку пакеты программного продукта СУБД «Квант-Гибрид» из репозитория командой:

wget -c -r -l1 -A '*.rpm' -nd -np https://repo.quantom.info/qhb//std-1/centos/8/x86_64/

Обновите описания пакетов:

apt-get update

Находясь в этой же локальной папке, установите бинарные пакеты командой, например:

apt-get install *.rpm

Вы можете установить не все пакеты QHB, а только необходимые вам. В этом случае вы можете перечислить в команде нужные вам пакеты rpm через пробел. Выбирайте последние версии из rpm пакетов, которые скачались.

Библиотека libicu и пакет qhb-core - обязательны.

В результате установки пакета ядра QHB будет создан пользователь qhb, ядро QHB, по умолчанию, установится в каталог /usr/local/qhb. Многие утилиты QHB устанавливаются в каталог /usr/local/qhb/bin. Если этот каталог указать в переменной окружения $PATH, это облегчит их запуск.

Смотрите дополнительно: Состав поставки, Страница загрузки

Для Fedora 33, 34 и 35

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

Установите репозиторий программного продукта СУБД «Квант-Гибрид» командой:

rpm --import https://repo.quantom.info/qhb//keys/RPM-GPG-KEY-qhb

и далее, для Fedora 33:

dnf config-manager --add-repo https://repo.quantom.info/qhb//std-1/fedora/33/x86_64/qhb.repo

для Fedora 34:

dnf config-manager --add-repo https://repo.quantom.info/qhb//std-1/fedora/34/x86_64/qhb.repo

или для Fedora 35:

dnf config-manager --add-repo https://repo.quantom.info/qhb//std-1/fedora/35/x86_64/qhb.repo

Установите бинарные пакеты командой:

dnf install qhb-core [qhb-contrib] [qcp] [qdl] [qdlm] [qbackup] [metricsd]

Например:

dnf install qhb-core

В результате установки пакета ядра QHB будет создан пользователь qhb. Ядро QHB, по умолчанию, установится в каталог /usr/local/qhb.

Многие утилиты QHB устанавливаются в каталог /usr/local/qhb/bin. Если этот каталог указать в переменной окружения $PATH, это облегчит их запуск.

Смотрите дополнительно: Состав поставки, Страница загрузки

Для Debian и Astra

Обратите внимание, что команды должны выполняться от пользователя с правами суперпользователя.

Для подключения к репозиторию выполните следующую команду:

apt install gnupg2 apt-transport-https wget

Обратите внимание, что для исключения из OS Astra Linux отозванных сертификатов безопасности следует выполнить очистку согласно инструкции по прекращению использования нежелательного сертификата.

Для корректной работы репозитория СУБД «Квант-Гибрид» в файле конфигурации репозиториев /etc/apt/sources.list должна быть добавлена запись

deb [arch=amd64] https://repo.quantom.info/qhb/std-1/debian stretch main

Для Astra требуется установка корневых сертификатов. Установите их с помощью команды:

apt-get install ca-certificates

Установите репозиторий программного продукта СУБД «Квант-Гибрид» командой:

wget -qO - https://repo.quantom.info/qhb//keys/RPM-GPG-KEY-qhb --no-check-certificate | apt-key add -

Далее выполните команды:

echo 'deb https://repo.quantom.info/qhb//std-1/debian stretch main' >> /etc/apt/sources.list

и

apt update

Установите бинарные пакеты командой:

apt install qhb-core [qhb-contrib] [qcp] [qdl] [qdlm] [qbackup] [metricsd]

Например:

apt install qhb-core

В результате установки пакета ядра QHB будет создан пользователь qhb. Ядро QHB, по умолчанию, установится в каталог /usr/local/qhb.

Многие утилиты QHB устанавливаются в каталог /usr/local/qhb/bin. Если этот каталог указать в переменной окружения $PATH, это облегчит их запуск.

Смотрите дополнительно: Состав поставки, Страница загрузки

В виде docker контейнера

Предлагается конфигурация: сервер СУБД «Квант-Гибрид» с расширениями и пул соединений QCP.

Получение Dockerfile и конфигураций

Создайте локальный каталог и разверните в нём скрипты docker из репозитория СУБД «Квант-Гибрид»:

wget https://repo.quantom.info/qhb//std-1/docker/qhb-docker-image.tar.gz -O - | tar -xz

Создание образов

Внимание!
Потребуется docker-compose. Здесь и далее, запускать docker-compose следует из-под обычного пользователя (не root). Возможно, для этого надо будет выполнить рекомендации по настройке docker.

Как правило, не требуется вручную создавать образ, т.к. он автоматически создаётся при первом запуске. Однако это можно сделать и явным образом:

docker-compose build

Замечание.
Если возникают проблемы с построением образа, связанные с недоступностью репозитория centos, нужно проверить сетевые разрешения. Иногда, перед построением, просто помогает выполнение таких команд:

sudo firewall-cmd --zone=public --add-masquerade --permanent
sudo firewall-cmd --reload

Запуск контейнера

Для запуска контейнеров СУБД «Квант-Гибрид» и QCP с настройками по умолчанию, выполните в текущем каталоге:

docker-compose up

Прервать выполнение можно с помощью Ctrl+C, или же выполнив из другой консоли в текущем каталоге:

docker-compose down

Для запуска контейнеров в режиме "демона", выполните:

docker-compose up -d

При первом запуске база данных будет автоматически инициализирована.

Посмотреть логи можно с помощью команды:

docker-compose logs

По умолчанию, база данных сохраняется на диске в каталоге qhb/pgdata.

Остановка контейнеров

Если контейнеры были запущены посредством docker-compose up, то для остановки достаточно нажать Ctrl+C. Если же контейнеры были запущены в режиме «демона», т. е. посредством docker-compose up -d, то для их остановки выполните (из любой консоли) в текущем каталоге: docker-compose down. Эту команду также можно выполнять и при остановленных контейнерах.

Настройка QCP

По умолчанию сервис QCP выключен. Для его запуска необходимо в файле docker-compose.yaml убрать (закомментировать) запись

entrypoint: ["echo", "Service qcp disabled"]

из раздела services: -> qcp:, а также настроить сервис, в частности, параметры подключения к серверу.

Настройка QCP осуществляется посредством редактирования файла ./qcp/config.yaml.

Обязательно замените в разделе servers:

%USER% - на имя пользователя, от имени которого запускается docker-compose, и

%DATABASE% - на имя базы данных, в простейшем случае база создается с именем, совпадающим с именем пользователя.

В дальнейшем, если вы создадите другую базу данных, этот параметр может потребоваться изменить. По умолчанию сервис QCP работает на порту 8080, при необходимости переопределите этот параметр. Обратите внимание, что настройки применяются только при запуске.

Отредактируйте файл docker-compose.yaml следующим образом:

В разделе services: -> qhb: -> volumes: замените

- ./qhb/pgdata:/qhb-data/

на

- /path/to/pgdata:/qhb-data/

где /path/to/pgdata — абсолютный путь до каталога базы данных с существующей базой данных.

В разделе services: -> qhb: -> environment: замените - USER на - USER=USERNAME

Запустите контейнер с помощью команды

docker-compose -e USER="USERNAME" up

где USERNAME — имя пользователя, владеющего каталогом /path/to/pgdata.

Пример отредактированного файла docker-compose.yaml:

version: "3.3"
services:
  qhb:
    build: qhb
    image: qhb
    network_mode: "host"
    environment:
      - USER=qhb
    volumes:
      - ./qhb/pgdata:/qhb-data/

  qcp:
    build: qcp
    image: qcp
    network_mode: "host"
    entrypoint: ["echo", "Service qcp disabled"]  # Comment this line if will use QCP
    volumes:
      - ./qcp/config.yaml:/config.yaml:ro

В данном сценарии уже подготовлены и при запуске будут проделаны шаги по инициализации кластера, запуску сервера и созданию базы данных.

Поэтому следующие шаги в данной инструкции могут быть пропущены. Может оказаться полезным раздел Доступ к базе данных.

Инициализация кластера базы данных

Создайте каталог для размещения базы данных командой

sudo mkdir <путь/имя_каталога>

Например:

sudo mkdir /var/lib/qhb/data

Разрешите доступ к нему пользователю qhb командой

sudo chown qhb <путь/имя_каталога>

В нашем случае:

sudo chown qhb /var/lib/qhb/data

Далее рекомендуется переключиться на пользователя qhb и выполнять команды от его имени. В ином случае необходимо использовать sudo -u qhb для всех последующих команд, если вы работаете от пользователя с правами sudo.

Инициализируйте кластер базы данных при помощи утилиты initdb или qhb_bootstrap.

<путь_к_бинарным_утилитам QHB>/initdb -D <путь/имя_каталога> -U qhb

В нашем случае:

/usr/local/qhb/bin/initdb -D /var/lib/qhb/data -U qhb

или, если установлена переменная окружения $PATH:

initdb -D /var/lib/qhb/data -U qhb

В дальнейшем данный каталог будет использоваться при запуске и остановке сервера QHB. Его можно указать в переменной окружения $PGDATA, что облегчит запуск следующих утилит — можно будет не указывать каталог в параметре -D.

Смотрите дополнительно: Создание кластера базы данных, initdb, qhb_bootstrap

Запуск и остановка сервера

Рекомендуется переключиться на пользователя qhb и выполнять команды от его имени. В ином случае необходимо использовать sudo -u qhb для всех последующих команд, если вы работаете от пользователя с правами sudo.

Для запуска сервера используйте команду:

<путь_к_бинарным_утилитам QHB>/qhb_ctl -D <путь/имя_каталога> start

В нашем случае:

/usr/local/qhb/bin/qhb_ctl -D /var/lib/qhb/data start

Или, если установлены переменные окружения $PGDATA и $PATH:

qhb_ctl start

Для остановки сервера используйте команду:

<путь_к_бинарным_утилитам QHB>/qhb_ctl -D <путь/имя_каталога> stop

В нашем случае:

/usr/local/qhb/bin/qhb_ctl -D /var/lib/qhb/data stop

Или, если установлена переменная окружения $PGDATA:

qhb_ctl stop

Смотрите дополнительно: Запуск сервера базы данных, Завершение работы сервера

Запуск и остановка сервера с помощью сервиса systemd

Замечание.
Описывается запуск QHB сервиса через подсистему инициализации и управления службами systemd. Уточните, что она установлена в вашем дистрибутиве Linux.

При установке из пакета rpm, имеется возможность использовать сервис systemd для автоматического запуска сервера БД при старте системы. Для этого, необходимо уточнить предварительно установленный скрипт запуска сервиса. Если необходимо поменять расположение каталога базы данных, задаваемое по умолчанию, выполните:

sudo systemctl edit qhb

Далее, в открывшемся редакторе (файл изначально пуст) укажите нужный путь:

[Service]
Environment=PGDATA=/opt/qhb/data

Сохраните изменения и завершите работу редактора.

Перестройте сервис и, если необходимо, разрешите ему запускаться при перезагрузке ОС:

sudo systemctl daemon-reload
sudo systemctl enable qhb

В дальнейшем, после начальной настройки (см. Начало работы), запускать сервис нужно будет командой:

sudo systemctl start qhb

А останавливать командой:

sudo systemctl stop qhb

Создание базы данных

Первый тест, чтобы увидеть, можете ли вы получить доступ к серверу базы данных - попытаться создать базу данных. Работающий сервер QHB может управлять многими базами данных. Как правило, отдельная база данных используется для каждого проекта или для каждого пользователя.

Возможно, ваш администратор уже создал базу данных для вашего использования. В этом случае вы можете пропустить этот шаг и перейти к следующему разделу.

Чтобы создать новую базу данных, в этом примере с именем mydb, вы используете следующую команду:

createdb mydb -h localhost

Если в ответ на такой запуск нет ошибки, то этот шаг был успешным, и вы можете пропустить оставшуюся часть этого раздела.

Если вы видите сообщение, похожее на:

createdb: command not found

тогда QHB не был установлен должным образом. Либо он вообще не был установлен, либо путь поиска вашей оболочки не был настроен на его включение. Попробуйте вместо этого вызвать команду с абсолютным путем:

/usr/local/qhb/bin/createdb mydb

Ваш путь может быть другим. Обратитесь к вашему администратору или ознакомьтесь с инструкциями по установке, чтобы исправить ситуацию.

Другой ответ может быть таким:

createdb: could not connect to database qhb: could not connect to server: No such file or directory
        Is the server running locally and accepting
        connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

Это означает, что сервер не был запущен или не был запущен там, где ожидал createdb. Снова, проверьте инструкции по установке или проконсультируйтесь с администратором.

Другой ответ может быть таким:

createdb: could not connect to database qhb: FATAL:  role "joe" does not exist

где упоминается ваше имя пользователя. Это произойдет, если администратор не создал для вас учетную запись пользователя QHB. (Учетные записи пользователей QHB отличаются от учетных записей пользователей операционной системы). Если вы являетесь администратором, обратитесь к главе Роли в базе данных за помощью в создании учетных записей. Вам нужно стать пользователем операционной системы, под которой был установлен QHB (обычно qhb), чтобы создать первую учетную запись пользователя. Возможно также, что вам было присвоено имя пользователя QHB, отличающееся от имени пользователя вашей операционной системы; в этом случае вам нужно использовать ключ -U или установить окружение PGUSER, чтобы указать свое имя пользователя в QHB.

Если у вас есть учетная запись пользователя, но у нее нет прав, необходимых для создания базы данных, вы увидите следующее:

createdb: database creation failed: ERROR:  permission denied to create database

Не каждый пользователь имеет право создавать новые базы данных. Если QHB отказывается создавать базы данных для вас, ваш администратор должен предоставить вам разрешение на создание баз данных. Обратитесь к вашему администратору, если это произойдет. Если вы установили QHB самостоятельно, вы должны войти в систему в целях данного руководства под учетной записью пользователя, с которой вы запустили сервер¹.

Вы также можете создавать базы данных с другими именами. QHB позволяет создавать любое количество баз данных. Имена баз данных должны иметь алфавитный первый символ и иметь длину не более 63 байтов. Удобный выбор - создать базу данных с тем же именем, что и ваше текущее имя пользователя. Многие инструменты предполагают, что имя базы данных используется как имя по умолчанию, поэтому оно поможет вам сэкономить время при наборе текста. Чтобы создать эту базу данных, просто введите:

createdb

Если вы больше не хотите использовать свою базу данных, вы можете удалить ее. Например, если вы являетесь владельцем (создателем) базы данных mydb, вы можете уничтожить ее, используя следующую команду:

dropdb mydb

(Для этой команды имя базы данных по умолчанию не совпадает с именем учетной записи пользователя. Вы всегда должны указывать его.) Это действие физически удаляет все файлы, связанные с базой данных, и не может быть отменено, так что это должно быть сделано только с большим количеством предусмотрительности.

Больше информации по командам createdb и dropdb можно найти в соответствующих разделах.

¹

В качестве объяснения того, почему это работает: имена пользователей QHB отделены от учетных записей пользователей операционной системы. Когда вы подключаетесь к базе данных, вы можете выбрать, какое имя пользователя QHB будет подключаться; если вы этого не сделаете, по умолчанию будет использоваться то же имя, что и ваша текущая учетная запись операционной системы. Как это бывает, всегда будет учетная запись пользователя QHB, имя которой совпадает с именем пользователя операционной системы, запустившего сервер, и также бывает, что этот пользователь всегда имеет разрешение на создание баз данных. Вместо входа в систему под этим пользователем вы также можете указать опцию -U везде, чтобы выбрать имя пользователя QHB для подключения.

Доступ к базе данных

Создав базу данных, вы можете получить к ней доступ одним из следующих способов:

С помощью интерактивного терминала QHB, называемого psql, который позволяет в интерактивном режиме вводить, редактировать и выполнять команды SQL.

С использованием существующего графического инструмента внешнего интерфейса, например, pgAdmin, DBeaver или офисного пакета с поддержкой ODBC или JDBC. Эти возможности не рассматриваются в этом руководстве.

Написав собственное приложение с использованием одной из нескольких доступных языковых привязок.

Возможно, вы захотите запустить psql, чтобы попробовать примеры из этого урока. Его можно запустить для базы данных mydb, набрав команду:

$ psql -h localhost -d mydb

Если вы не укажете имя базы данных, по умолчанию будет использоваться имя вашей учетной записи. Мы уже сталкивались с этой схемой в предыдущем разделе, при работе с createdb.

Внутри psql вы увидите следующее сообщение:

psql - Interactive terminal qhb (1.2.0)
Enter "\help" in order to get help

mydb(username)=#

Это будет означать, что вы являетесь суперпользователем базы данных, что наиболее вероятно, если вы установили экземпляр QHB самостоятельно. Суперпользователь имеет неограниченный доступ к базе данных; впрочем, для целей урока это не имеет значения.

Если вы столкнулись с проблемами при запуске psql, вернитесь к предыдущему разделу. Диагностика createdb и psql схожи, и если первый сработал, последний также должен сработать.

Последняя строка, напечатанная psql является приглашением и указывает, что psql ожидает ввод, то есть вы можете вводить запросы SQL в рабочее пространство. Попробуйте эти команды:

mydb(username)=# select version();
version
----------
QHB 1.2.0
(1 строка)

mydb(username)=# SELECT current_date;
    date
------------
 2020-10-07

mydb(username)=# SELECT 2 + 2;
 ?column?
----------
4
(1 строка)

Программа psql имеет ряд внутренних команд, которые не являются командами SQL. Они начинаются с символа обратной косой черты "\". Например, чтобы выйти из psql, введите:

mydb(username)=# \q

и psql завершит работу и вернёт вас в командную оболочку.

Все возможности psql описаны в соответствующем разделе: psql.

Краткая инструкция по обновлению

Описывается базовый сценарий обновления QHB с помощью утилиты qhb_upgrade. Более подробную информацию см. в главе qhb_upgrade.

В случае если ваш сервер уже остановлен, первый шаг можно пропустить.

Для остановки сервера базы данных введите:

qhb_ctl -D $PGDATA stop

или, если он запускался через сервис:

sudo systemctl stop qhb

Примечание.
Файлы конфигурации и данных, используемые СУБД, обычно хранятся в одном каталоге, называемом PGDATA (аналогично имени переменной среды). В наших примерах мы будем использовать /opt/qhb/data.old как путь к данным старого каталога кластера базы данных и /opt/qhb/data как путь к данным нового каталога кластера базы данных. Каталог /usr/local/qhb.old/bin — это место хранения исполняемых файлов старой версии QHB, а каталог /usr/local/qhb/bin — это место хранения исполняемых файлов новой версии QHB.

Переместите старый кластер:

sudo mv /opt/qhb/data /opt/qhb/data.old

Переместите текущий установочный каталог QHB, чтобы он не мешал новой установке QHB:

sudo mv /usr/local/qhb /usr/local/qhb.old

Установите новые пакеты QHB командой от пользователя с правами суперпользователя:

yum [ install | upgrade ] <путь до каталога c пакетами/названия пакетов>

Примечание.
В зависимости от ОС команда для установки может меняться. Если вы устанавливаете пакеты из репозитория QHB, можно воспользоваться краткой инструкцией по начальной загрузке и установке.

Вновь создайте папку для базы данных:

sudo mkdir -p /opt/qhb/data

Разрешите пользователю qhb доступ к ней:

sudo chown -R qhb /opt/qhb/data

Далее рекомендуется переключиться на пользователя qhb и выполнять команды от его имени. В ином случае необходимо использовать sudo -u qhb для всех последующих команд, если вы работаете от пользователя с правами sudo.

Инициализируйте кластер базы данных командой:

initdb -D /opt/qhb/data

Выполните обновление QHB через утилиту qhb_upgrade:

qhb_upgrade \
  --old-bindir=/usr/local/qhb.old/bin/ \
  --new-bindir=/usr/local/qhb/bin/ \
  --old-datadir=/opt/qhb/data.old \
  --new-datadir=/opt/qhb/data --verbose

Примечание.
В случае ошибки обновления необходимо убедиться, что пользователь qhb имеет права доступа ко всем указанным каталогам.
Также пользователь qhb должен обладать правами на запись в той директории, из которой происходит запуск qhb_upgrade.

Ожидаемый результат:

Upgrade Complete

Для старта сервера базы данных введите:

qhb_ctl -D /opt/qhb/data start

или

sudo systemctl start qhb

Миграция кластера баз данных в QHB

В настоящее время поддерживается миграция в QHB только начиная с версии PostgreSQL 12. Если используется более ранняя версия PostgreSQL, необходимо сначала выполнить обновление до версии 12. Также поддерживается миграция со старых версий QHB. Данная инструкция охватывает только основные моменты миграции, на которые нужно обратить внимание.

В данном примере рассматривается вариант миграции с PostgreSQL версии 12 на QHB текущей версии. Более подробно процедура миграции описана в разделе qhb_upgrade.

Подготовка к миграции

Пример исходных данных

Данные PostgreSQL
Каталог с запускаемыми файлами: /usr/pgsql-12/bin
Каталог с данными: /var/lib/pgsql/12/data/
Суперпользователь: postgres
Порт: 5432
Служба: postgresql-12

Данные QHB
Каталог с запускаемыми файлами: /usr/local/qhb/bin
Каталог с данными: /opt/qhb/data
Суперпользователь: qhb
Порт: 5432
Служба: qhb

Если необходимо, чтобы сервисы postgresql-12 и qhb работали одновременно (например, в целях тестирования работоспособности QHB), нужно изменить номер порта для QHB, т. е. изменить значение параметра port в файле параметров qhb.conf, например, на 5433. Для обновления через qhb_upgrade это не является обязательным, так как сервисы запускаются по очереди.

Языковые настройки

Заранее уточните значения параметров с языковыми настройками нового кластера баз данных QHB (locale, lc-collate, lc-ctype — соответственно локаль, порядок сортировки строк, классификация символов). Как правило, эти параметры имеют одинаковое значение и должны соответствовать настройкам исходного кластера баз данных PostgreSQL. Выяснить значения этих настроек в PostgreSQL можно через команду \l в psql, выведя список баз данных с их параметрами. В данном примере значения параметров одинаковы и равны en_US.UTF-8.

Переименование в кластере баз PostgreSQL суперпользователя postgres в qhb

Утилита qhb_upgrade должна работать от имени одного и того же пользователя как с PostgreSQL, так и с QHB. Рекомендуется использовать в качестве такого пользователя qhb. Так как суперпользователь qhb по умолчанию отсутствует в базе PostgreSQL, переименуем пользователя postgres в qhb. Для этого нужно зайти в PostgreSQL от имени другого суперпользователя. Если такого пользователя не существует, можно его создать, а после переименования пользователя postgres удалить.

Зайдем от имени суперпользователя postgres:

/usr/pgsql-12/bin/psql -d postgres -U postgres

Создадим нового суперпользователя temp_su, зайдем от его имени, переименуем пользователя postgres в qhb, подключимся под qhb и удалим уже ненужного пользователя temp_su:

CREATE ROLE temp_su LOGIN SUPERUSER;
\c postgres temp_su
ALTER USER postgres RENAME TO qhb;
\c postgres qhb
DROP ROLE temp_su;

Создание тестовой базы данных и таблицы в ней

Для того чтобы наглядно удостовериться в успешном переносе данных, особенно при тестовом прогоне, можно создать в PostgreSQL отдельную базу данных и таблицу в ней, заполнив ее хотя бы одной строкой.

CREATE DATABASE test;
\c test
CREATE TABLE test (id int, txt text);
INSERT INTO test VALUES(1, "test1");

Остановка работы сервисов и смена владельца каталога PostgreSQL

Остановка сервисов postgres-12 и qhb (если сервис qhb был запущен):

sudo systemctl stop postgresql-12
sudo systemctl stop qhb

Для того чтобы утилита qhb_upgrade нормально отработала от имени системного пользователя qhb, поменяем владельца для каталога данных PostgreSQL на qhb. Для верхних каталогов необходимо выдать на время права, позволяющие другим пользователям заходить в них и читать данные.

sudo chmod o+rx /var/lib/pgsql
sudo chmod o+rx /var/lib/pgsql/12
sudo chown -R qhb:qhb /var/lib/pgsql/12/data

Инициализация кластера баз данных QHB

Далее рекомендуется переключиться на системного пользователя qhb и выполнять последующие команды от его имени:

sudo su - qhb

Очистка каталога данных QHB

Здесь предполагается, что каталог /opt/qhb/data является корневым каталогом данных QHB, выбранном при установке и настройке базы данных. Если каталог не является к этому моменту пустым, необходимо его полностью очистить. В противном случае миграция данных не запустится.

rm -rf /opt/qhb/data/*

Инициализация нового кластера

По умолчанию в кластере баз данных создается суперпользователь с именем пользователя операционной системы, запускавшего установку, т. е. qhb.

/usr/local/qhb/bin/qhb_bootstrap \
--locale=en_US.UTF-8 \
--lc-collate=en_US.UTF-8 \
--lc-ctype=en_US.UTF-8 \
--encoding=UTF8 \
--data-dir /opt/qhb/data

Выполнение проверки возможности миграции базы с PostgreSQL на QHB

Запускать утилиту qhb_upgrade лучше из отдельного временного каталога, например /tmp/qhb_upgrade. В каталоге будут сохраняться логи работы утилиты, в частности, файл pg_upgrade_internal.log, в который записываются результаты проверки возможности миграции или сообщения об ошибках при выполнении миграции. Желательно сделать этот каталог недоступным для других пользователей.

mkdir /tmp/qhb_upgrade
chmod 700 /tmp/qhb_upgrade
cd /tmp/qhb_upgrade

Внимание!
Версия qhb_upgrade при миграции из PostgreSQL в QHB должна быть не меньше 1.3.1. Проверка выполняется через запуск /usr/local/qhb/bin/qhb_upgrade --version.

Следующая команда выполняет проверку возможности миграции, для этого используется параметр --check.

/usr/local/qhb/bin/qhb_upgrade \
--new-bindir=/usr/local/qhb/bin \
--new-datadir=/opt/qhb/data \
--old-bindir=/usr/pgsql-12/bin \
--old-datadir=/var/lib/pgsql/12/data \
-U qhb -v --check

Результаты проверки выведутся на экран, а также в файл pg_upgrade_internal.log. В случае успешного завершения проверки на экран предпоследней строкой выведется сообщение *Clusters are compatible* (кластеры совместимы).

Выполнение миграции

Для выполнения миграции в предыдущей команде необходимо убрать параметр --check.

/usr/local/qhb/bin/qhb_upgrade \
--new-bindir=/usr/local/qhb/bin \
--new-datadir=/opt/qhb/data \
--old-bindir=/usr/pgsql-12/bin \
--old-datadir=/var/lib/pgsql/12/data \
-U qhb -v

В случае успешной миграции на экран предпоследней строкой выведется Upgrade Complete (обновление завершено). Также результаты работы утилиты будут зафиксированы в логе pg_upgrade_internal.log.

Возврат исходных прав для каталога

В общем случае в каталоге /var/lib/pgsql могут быть расположены несколько кластеров баз данных. Вернем исходные права доступа для верхних каталогов и вернем прежнего владельца для каталога с данными PostgreSQL /var/lib/pgsql/12/data. Это может понадобиться в случае отмены миграции и возврата к использованию баз PostgreSQL.

sudo chmod o-rx /var/lib/pgsql
sudo chmod o-rx /var/lib/pgsql/12
sudo chown -R postgres:postgres /var/lib/pgsql/12/data

Проверка переноса данных

Предварительно должны быть отредактированы все настройки в соответствующих файлах настроек (qhb.conf, qhb_hba.conf и qhb_ident.conf).

Запуск QHB и проверка перенесенных данных:

sudo systemctl start qhb
/usr/local/qhb/bin/psql -U qhb -d test -c "select * from test"

Запуск и остановка сервера с помощью сервиса systemd

Замечание.
Описывается запуск QHB сервиса через подсистему инициализации и управления службами systemd. Уточните, что она установлена в вашем дистрибутиве Linux.

При установке из пакета rpm, имеется возможность использовать сервис systemd для автоматического запуска сервера БД при старте системы. Для этого, необходимо уточнить предварительно установленный скрипт запуска сервиса. Если необходимо поменять расположение каталога базы данных, задаваемое по умолчанию, выполните:

sudo systemctl edit qhb

Далее, в открывшемся редакторе (файл изначально пуст) укажите нужный путь:

[Service]
Environment=PGDATA=/opt/qhb/data

Сохраните изменения и завершите работу редактора.

Перестройте сервис и, если необходимо, разрешите ему запускаться при перезагрузке ОС:

sudo systemctl daemon-reload
sudo systemctl enable qhb

В дальнейшем, после начальной настройки (см. Начало работы), запускать сервис нужно будет командой:

sudo systemctl start qhb

А останавливать командой:

sudo systemctl stop qhb

Пример настройки синхронной реплики

Внимание!
В данном разделе приводится конечный пример настройки кластера баз данных с синхронной репликацией. Обратите внимание, что здесь приводятся некоторые параметры (например, ip адреса хостов или каталоги баз данных), которые могут не соответствовать вашим.

Начальные условия

Операционная система на серверах — CentOS 7.

172.31.101.70 — IP сервера, назначенного основным (мастер).

172.31.101.71 — IP резервного сервера (реплика).

СУБД установлена из RPM пакетов (см. Страница загрузки. QHB для Centos 7 и 8).

Каталог размещения БД — PG_DATA = /u01/qhb/db.

/usr/local/qhb/ — путь установки QHB по умолчанию.

Сервисы systemd сконфигурированы с учетом вышеперечисленного.

Настройка основного сервера

Создаем пользователя QHB для репликации:

create user repluser replication password 'repluser';

Устанавливаем необходимые параметры в файле конфигурации vi /u01/qhb/db/qhb.conf:

listen_addresses = '*'
wal_level = hot_standby
max_wal_senders = 2
max_replication_slots = 2
hot_standby = on
hot_standby_feedback = on
logging_collector = on
log_directory = './log'
synchronous_commit = on
synchronous_standby_names = '*'

Устанавливаем параметры доступа к базе данных с машины резервного сервера в файле конфигурации

vi /u01/qhb/db/qhb_hba.conf

host replication repluser 172.31.101.70/32 md5
host replication repluser 172.31.101.71/32 md5

Внимание!
Существующую запись host replication не изменяем.

Производим рестарт сервиса СУБД

systemctl restart qhb && systemctl status qhb

Настройка резервного сервера

Останавливаем сервис СУБД на сервере реплики

systemctl stop qhb && systemctl status qhb

Удаляем директорию с данными базы данных

rm -rf /u01/qhb/db/* # Полное удаление данных

Внимание!
Удаляя директорию, вы должны полностью осознавать необратимость данного действия и тот факт, что все данные, файлы конфигурации и логи будут утеряны без возможности восстановления.

В случае если ситуация позволяет, выполняем восстановление через бэкап.

Если объем данных в базе данных большой, то рекомендуется воспользоваться копией данных, при ее наличии.

Восстановление через утилиту бэкапирования запускается от системного пользователя QHB:

sudo -u qhb \#
/usr/local/qhb/bin/qhb_basebackup \#Утилита бэкапа
-h 172.31.101.70 \ # IP адрес мастер сервера
-p 5432 \ # Порт по умолчанию; рекомендуется сменить, если конфигурация была изменена
-U repluser \ # Пользователь, созданный ранее на основном сервере, с правами репликации
-D /u01/qhb/db/ \ # Директория размещения СУБД
-Fp -Xs -P -R # Набор ключей, необходимых для репликации

Для лучшего понимания следует ознакомиться с функционированием ключей -Fp, -Xs, -P и -R данной утилиты, описанных в разделе Параметры qhb_basebackup.

Если копия делается при помощи qhb_basebackup, параметры подключения для реплики будут указаны в файле qhb.auto.conf.
Следует удостовериться в корректности заданной строки подключения.

Если копия базы данных была развернута через файл (или иной корректный способ, не указанный в данной инструкции), то до старта необходимо скорректировать несколько параметров:

запуск сервера СУБД в резервном режиме. Для этого требуется создать файл standby.signal в директории кластера базы данных (рядом с qhb.conf).

sudo -u qhb touch /u01/qhb/db/standby.signal
ls -la standby.signal
#-rw------- 1 qhb qhb     0 Nov 25 15:34 standby.signal

в файле конфигурации qhb.conf необходимо указать строку подключения к основному серверу:

primary_conninfo = 'user=repluser password=repluser host=172.31.101.70 port=5432 sslmode=disable sslcompression=0 gssencmode=disable krbsrvname=postgres target_session_attrs=any'

Примечание
Строка подключения длинная, и некоторые консольные редакторы могут применить опцию word-wrap, нарушая визуальную целостность строки.

В обоих случаях параметры СУБД в файле конфигурации будут скопированы с мастера, и следует изменить некоторые значения на соответствующие реплике.

Параметры на реплике указываются следующие:

vi /u01/qhb/db/qhb.conf

listen_addresses = '*'
logging_collector = on
log_directory = 'log' # Удостоверьтесь в существовании директории и наличии необходимых прав.

Теперь и только теперь можно запустить СУБД на втором сервере, реплике.

systemctl start qhb && systemctl status qhb

Проверка

Проверяем на мастере:

/usr/local/qhb/bin/psql -x -c "select * from pg_stat_replication"

Должен высветится один слот:

pid              | 15566
usesysid         | 16385
usename          | repluser
application_name | walreceiver
client_addr      | 172.31.101.71
client_hostname  |
client_port      | 40516
backend_start    | 2020-11-25 09:45:22.249802-03
backend_xmin     |
state            | streaming
sent_lsn         | 0/30001F8
write_lsn        | 0/30001F8
flush_lsn        | 0/30001F8
replay_lsn       | 0/30001F8
write_lag        |
flush_lag        |
replay_lag       |
sync_priority    | 1
sync_state       | sync
                   ^^^^^^^
reply_time       | 2020-11-25 09:47:22.710523-03

Причем sync_state должен быть sync (репликация синхронная).

Создадим таблицу на мастере:

CREATE TABLE rtest (vname VARCHAR(40));

Заполним случайными значениями на миллион строк:

insert into rtest select substr(md5(random()::text), 0, 40) from generate_series(1,1000000);

На ведомом сервере проверяем чтение из таблицы:

qhb=# select count(*) from rtest;
  count
---------
 1000000
(1 row)

Дополнительные настройки

В некоторых вариантах рекомендовано прописать явное указание слота репликации.

На реплике в файл qhb.auto.conf добавить параметр primary_slot_name = 'standby_slot2_qhb'.

На мастере выполнить команду SQL:

SELECT pg_create_physical_replication_slot('standby_slot2_qhb');

Запрос по активным слотам репликации должен показывать на мастере нашу реплику

SELECT * FROM pg_replication_slots WHERE active=true;

См. также

Внешнее руководство с инструкциями и пояснениями, qhb_basebackup

Инструкция по использованию QSS в режиме хранения пользовательского ключа на USB носителе

В данной инструкции представлены рекомендации по использованию модуля безопасного хранения QHB (Quantum Secure Storage, QSS), а также сценарий инициализации QSS c помощью утилиты qss_mgr и последующий запуск сервера базы данных. Более подробную информацию см. в главе Модуль безопасного хранения QSS.

Для начала работы с QSS требуется установить QHB на ваш компьютер и инициализировать кластер базы данных (обычно путь к данным кластера экспортируется в переменную среды PGDATA). Если вы устанавливаете пакеты из репозитория QHB, можно воспользоваться краткой инструкцией по начальной загрузке и установке.

Сервер базы данных должен остаться выключенным.

Подключите USB-носители к компьютеру для генерации мастер-ключа и ключа пользователя, который имеет право запускать сервер базы данных (далее Админ)

Примечание.
USB-носитель должен всегда подключаться по одному и тому же пути (здесь используется путь /mtn/flash для флеш-накопителя Админа и /mtn/flash_master для флеш-накопителя с мастер-ключом). Не следует копировать мастер-ключ на компьютер. USB-носитель с мастер-ключом (или сам файл мастер-ключа) нужен только при инициализации QSS, а потом должен храниться в защищенном месте.

Выполните генерацию мастер-ключа (данную операцию необязательно производить на сервере c QHB, выполнение возможно на любой другой машине):

head -c 32 /dev/random > /mnt/flash_master/master.bin

Выполните генерацию пользовательского ключа:

head -c 32 /dev/random > /mnt/flash/user1.bin

Добавьте ключи в QSS с помощью утилиты qss_mgr (здесь и далее не указаны пути до места хранения исполняемых файлов QHB). Данная командная строка требует установленной переменной среды PGDATA. В случае если PGDATA не установлена, для указания пути до папки базы данных следует использовать параметр -d либо --data-dir:

qss_mgr init --module=/usr/lib64/librtpkcs11ecp.so \
-m fs \
-k \
/mnt/flash/user1.bin  \
/mnt/flash_master/master.bin

Для добавления в QSS ключа другого Админа необходимо кроме флеш-накопителя добавляемого Админа подключить флеш-накопитель уже добавленного Админа.

Примечание.
Если владельцев ключа несколько, USB-носители должны монтироваться по уникальным путям, например /mnt/flash2, /mnt/flash3 и т. п.

Для добавления в QSS дополнительного ключа введите:

qss_mgr add
-m fs \
-n 0 \ # Номер ключа уже добавленного Админа (при инициализации добавляется ключ с номером 0)
/mnt/flash2/user2.bin

Включите шифрование путем добавления значения qss_mode = 1 в файл qhb.conf:

echo qss_mode = 1 >> $PGDATA/qhb.conf

Для запуска сервера базы данных в режиме работы модуля безопасного хранения QSS порядок действий будет следующий:

Подключить USB-носитель Админа

Выполнить запуск сервера базы данных с помощью утилиты qhb_ctl от имени пользователя QHB:

qhb_ctl start

Отключить USB-носитель

Инструкция по использованию QSS в режиме хранения пользовательского ключа на крипто-токене

В данной инструкции представлены рекомендации по использованию модуля безопасного хранения QHB (Quantum Secure Storage, QSS), а также сценарий инициализации QSS c помощью утилит qss_mgr и qss_pinpad. Кроме того, здесь представлена информация об использовании Рутокена как средства безопасной двухфакторной аутентификации пользователей, генерации и защищенного хранения ключей шифрования. Более подробную информацию см. в главе Модуль безопасного хранения QSS.

В QHB допускается использование токенов, поддерживающих аппаратное шифрование с использованием алгоритма Магма ГОСТ 34.12-2018 (ГОСТ 28147-89) и поддерживающих стандарт PKCS#11. Шифрование проверялось на следующих токенах: Рутокен ЭЦП 2.0, Рутокен ЭЦП 2.0 сертифицированный ФСБ, Рутокен ЭЦП 2.0 3000, сертифицированный ФСБ (более подробную информацию о токенах можно найти на официальном сайте www.rutoken.ru или по ссылке rutoken-ecp).

Для начала работы с QSS требуется установить QHB на ваш компьютер и инициализировать кластер базы данных (обычно путь к данным кластера экспортируется в переменную среды PGDATA). Если вы устанавливаете пакеты из репозитория QHB, можно воспользоваться краткой инструкцией по начальной загрузке и установке).

Также для работы требуется установленный модуль PKCS#12. Для Рутокена его можно скачать по ссылке библиотека PKCS. Токен устанавливается по пути /usr/lib64/librtpkcs11ecp.so или /usr/lib/librtpkcs11ecp.so, в зависимости от операционной системы.

Инициализации ключей

Сервер базы данных должен остаться выключенным.

Подключите USB-носитель к компьютеру для генерации мастер ключа.

Примечание.
USB-носитель должен всегда подключаться по одному и тому же пути (здесь используется путь /mtn/flash_master для флеш-накопителя с мастер-ключом). Не следует копировать мастер-ключ на компьютер. USB-носитель с мастер-ключом (или сам файл мастер-ключа) нужен только при инициализации QSS, а потом, как и носитель с токеном, должен храниться в защищенном месте.

Выполните генерацию мастер-ключа (данную операцию необязательно производить на сервере c QHB, выполнение возможно на любой другой машине):

head -c 32 /dev/random > /mnt/flash_master/master.bin

Пользовательский ключ (далее токен) генерируется внешними программно-аппаратными средствами или утилитой magma_key_gen, которая поставляется в пакете qhb-contrib.

Проверить токен можно с помощью утилиты из пакета opensc. Если у вас нет данного пакета, его можно скачать, выполнив на терминале соответствующую команду.

Для CentOS/Fedora:

sudo dnf install opensc

для Debian/Astra:

sudo apt-get install opensc

Для проверки ключа введите на своем терминале:

pkcs11-tool --module /usr/lib64/librtpkcs11ecp.so -T

Пример вывода:

Available slots:
Slot 0 (0x0): Aktiv Rutoken ECP 00 00
  token label        : test_label
  token manufacturer : Aktiv Co.
  token model        : Rutoken ECP
  token flags        : login required, rng, SO PIN to be changed, token initialized, PIN initialized, user PIN to be changed
  hardware version   : 20.5
  firmware version   : 23.2
  serial num         : 3c4c6444
  pin min/max        : 6/32

serial num — это уникальный серийный номер токена, который требуется для добавления токена в QSS.

Также необходимо проверить список ключей на токене. Для этого введите на своем терминале:

pkcs11-tool --module /usr/lib64/librtpkcs11ecp.so -Ol

Пример вывода:

Using slot 0 with a present token (0x0)
Logging in to "test_label".
WARNING: user PIN to be changed
Please enter User PIN:
Secret Key Object; unknown key algorithm 50
  label:
  ID:         3132333435
  Usage:      encrypt, decrypt, verify

Запись unknown key algorithm 50 означает, что используется тип ключа Магма. ID отображен в шестнадцатеричном виде, в данном случае это строка «3132333435», что соответствует строке «12345». Этот ID также нужен для добавления токена в QSS. В строке Usage должно быть указано encrypt, decrypt.

Описание инициализации QSS

В отдельном терминале запустите утилиту qss_pinpad, она требуется для ввода пин-кода токена. Запуск должен производиться от пользователя, который будет добавлять ключи в QSS.

Подключите USB-носители с мастер-ключом и пользовательским ключом (токеном).

Добавьте ключи в QSS с помощью утилиты qss_mgr (здесь и далее не указаны пути до места хранения исполняемых файлов QHB). Данная командная строка требует установленной переменной среды PGDATA. В случае если PGDATA не установлена, для указания пути до папки базы данных следует использовать параметр -d либо --data-dir:

qss_mgr init \
--module=/usr/lib64/librtpkcs11ecp.so \
-m pkcs11 \
--token-serial-number 3c4c6444 \
--token-key-id 12345 \
/mnt/flash_master/master.bin

Введите пин-код токена, запрос которого будет выдан в терминале с qss_pinpad в процессе работы.

Отключите USB-носитель с мастер-ключом и токен.

Примечание.
Токеном должен «владеть» пользователь, имеющий право запускать сервер (далее Админ). Если таких Админов несколько, то у каждого должен быть свой токен.

Далее рассмотрим пример добавления в QSS дополнительного ключа (если Админ один, можете пропустить этот пункт):

В отдельном терминале запустите qss_pinpad.

Подключите токен нового Админа и токен уже зарегистрированного Админа.

Для добавления в QSS дополнительного ключа введите:

qss_mgr add
-m pkcs11 \
--token-serial-number 3c4c6444 \
--token-key-id 3132333435 \
-n 0 \ # номер ключа уже добавленного Админа (при инициализации добавляется ключ с номером 0)

Включение режима работы сервера с QSS

Для использования QSS при работы с вашей базой данных включите шифрование путем добавления значения qss_mode = 1 в файл qhb.conf:

echo qss_mode = 1 >> $PGDATA/qhb.conf

Запуск сервера

Для запуска сервера базы данных в режиме работы модуля безопасного хранения QSS (вариант хранения пользовательского ключа на крипто-токене) порядок действий будет следующий:

В отдельном терминале запустить qss_pinpad (утилита будет использоваться для ввода пин-кода токена).

Подключить USB-носитель Админа с токеном.

Выполнить запуск сервера базы данных с помощью утилиты qhb_ctl от имени пользователя QHB:

qhb_ctl start

Ввести пин-код в терминале с qss_pinpad.

Отключить USB-носитель (токен).

Инструкция по установке и использованию QCP

В данной инструкции представлены простые шаги по установке пулера соединений QCP, а также базовая настройка сервиса systemd. Более подробную информацию см. в главе Асинхронный пул соединений QCP.

Чтобы начать использовать QCP, необходимо установить поставляемый пакет в систему. Если вы устанавливаете пакеты из репозитория QHB, можно воспользоваться краткой инструкцией по начальной загрузке и установке. После подключения к репозиторию установить пакет, например для Centos 7, можно следующим образом:

sudo yum install qcp

После установки будет создан файл с примером конфигурации в /etc/qcp/config-example.yaml. Его необходимо скопировать в /etc/qcp/config.yaml и отредактировать под свои нужды, выставив необходимы параметры подключения к СУБД.

Управление сервисом осуществляется через поставляемый юнит systemd.

Перед первым запуском, после установки QCP, необходимо выполнить перезагрузку демона systemd, для того чтобы изменения вступили в силу:

systemctl daemon-reload

Запуск QCP:

sudo systemctl start qcp

Остановка QCP:

sudo systemctl stop qcp

Для того чтобы QCP автоматически запускался при старте системы, выполните следующую команду:

sudo systemctl enable qcp

Работа с 1С

Инструкция по установке QHB под 1С

Конфигурирование QHB под 1С

Резервное копирование QHB под 1С

Инструкция по установке QHB под 1С

Первым шагом является установка QHB. После его установки необходимо инициализировать кластер баз данных с нужными параметрами:

sudo -u qhb /usr/local/qhb/bin/qhb_bootstrap --locale=ru_RU.UTF-8 --lc-collate=ru_RU.UTF-8 --lc-ctype=ru_RU.UTF-8 --encoding=UTF8 -D /u01/1CBase/ -U qhb

Далее необходимо создать базу данных, после чего подключить ее к 1С. Сделать это можно двумя путями: через консоль администрирования серверов 1С или через приложение 1С. И в том, и в другом случае необходимо указывать информацию в полях для добавления информационной базы в соответствии с вашей конфигурацией и инфраструктурой. В поле «Тип СУБД» нужно выбрать PostgreSQL.

В качестве примера указания параметров информационной базы можно разобрать добавление информационной базы через приложение 1C.

Наименование столбца | Необходимые данные
Кластер серверов 1С:Предприятия | Наименование кластера серверов
Имя информационной базы в кластере | Наименование созданной базы
Защищенное соединение | Выбор параметра зависит от вашей конфигурации; по умолчанию «Выключено»
Тип СУБД | PostgreSQL
Сервер баз данных | IPv6 адрес
Имя базы данных | Наименование базы данных
Пользователь базы данных | Имя пользователя с правами для создания базы на сервере базы данных
Пароль пользователя | Пароль пользователя с правами для создания базы на сервере базы данных
Смещение дат | Выбор параметра зависит от вашей конфигурации; по умолчанию 0

При указании флага Создать базу данных в случае ее отсутствия будет создана новая база данных, без необходимости предварительного создания экземпляра базы данных.

Внимание!
В версии QHB 1.3.0 и 1.3.1 внесены некоторые изменения. Для корректной работы необходимо на каждом экземпляре базы данных выполнить следующую команду:

UPDATE pg_catalog.pg_operator SET oprcanhash = false WHERE oid in (7200, 7201);

Конфигурирование QHB под 1С

Пример настроек в конфигурации QHB:

Важно!!!
Стоит ответственно подойти к конфигурации базы данных для различных пакетов 1С. Конфигурация ниже является исключительно примером и может отличаться от той конфигурации, что необходима вам. В случае, если вы используете уже готовую конфигурацию от другой системы, нельзя дать гарантии, что эта конфигурация заработает.

#------------------------------------------------------------------------------
# 1C SUPPORT OPTIONS
#------------------------------------------------------------------------------
# Specially fo 1C tuning
# Desingned for server
# 8 Cores
# RAM 64GB
#------------------------------------------------------------------------------
# Estimated user count
max_connections = 525

# Memory
shared_buffers = 16GB
temp_buffers = 128MB

# Can be changed regarding on workload
work_mem = 256MB
maintenance_work_mem = 256MB
effective_cache_size = 48GB

# WAL and friends
wal_level = minimal
commit_delay = 1000
max_wal_size = 4GB
min_wal_size = 2GB
checkpoint_timeout = 15min

# Autovacuum stuff
autovacuum_max_workers = 4
vacuum_cost_limit = 400
autovacuum_naptime = 20s
# Can be tuned individually for some heavy tables
autovacuum_vacuum_scale_factor = 0.01
autovacuum_analyze_scale_factor = 0.025

# Transaction, Query and Plan tuning section
max_locks_per_transaction = 256
seq_page_cost = 1.0
random_page_cost = 2.5
from_collapse_limit = 8
join_collapse_limit = 8

huge_pages = off
row_security = off

# OS interaction related stuff
max_files_per_process = 10000
max_parallel_maintenance_workers = 6
max_parallel_workers_per_gather = 0
synchronous_commit = off

# Special options
escape_string_warning = off
standard_conforming_strings = off
online_analyze.enable = on
plantuner.fix_empty_table = on
online_analyze.verbose = off
online_analyze.min_interval = 10000
online_analyze.capacity_threshold = 99999
online_analyze.table_type = 'temporary'

Важно!!!
Для системы 1С необходимо установить максимальный размер файлов, разрешенный для хранения в базе данных, равным 495 МБ.

Резервное копирование QHB под 1С

Для резервного копирования QHB используется инструмент резервного копирования qbackup. Обратите внимание, что 1С позволяет загружать в базу данных файлы большого размера, и в таком случае резервное копирование данных утилитами qhb_dump или qhb_dumpall не может быть выполнено. В таких ситуациях следует применять альтернативные варианты резервного копирования. При миграции на QHB следует предварительно выгрузить файлы больше 500 МБ средствами 1С.

Важно!!!
Необходимо периодически не только делать копии данных, но и архивировать логи. Также нужно убедиться в том, что логи сохраняются в отдельный архив. Обратитесь к статьям по практике создания резервных копий.

Сокращения

Это список сокращений, обычно используемых в документации QHB и в обсуждениях, касающихся QHB.

Сокращение | Полное название
ANSI | American National Standards Institute, Американский национальный институт стандартов
API | Application Programming Interface, Интерфейс прикладного программирования
ASCII | American Standard Code for Information Interchange, Американский стандартный код для обмена информацией
BKI | Backend Interface, Внутренний интерфейс
CA | Certificate Authority, Центр сертификации
CIDR | Classless Inter-Domain Routing, Бесклассовая адресация
CPAN | Comprehensive Perl Archive Network, Обширный сетевой архив ресурсов для языка PERL
CRL | Certificate Revocation List, Список отозванных сертификатов
CSV | Comma Separated Values, Значения, разделенные запятыми
CTE | Common Table Expression, Общее табличное выражение
CVE | Common Vulnerabilities and Exposures, Общий перечень уязвимостей и рисков
DBA | Database Administrator, Администратор базы данных
DBI | Database Interface (Perl), Интерфейс базы данных (Perl)
СУБД | Система управления базами данных (англ. Database Management System, сокр. DBMS)
DDL | Data Definition Language, Язык описания данных, включающий такие команды SQL, как CREATE TABLE, ALTER USER
DML | Data Manipulation Language, Язык управления данными, включающий такие команды SQL, как INSERT, UPDATE, DELETE
DST | Daylight Saving Time, Летнее время
ESQL | Embedded SQL, Встроенный SQL
FAQ | Frequently Asked Questions, Часто задаваемые вопросы
FSM | Free Space Map, Карта свободного пространства
GEQO | Genetic Query Optimizer, Генетический оптимизатор запросов
GIN | Generalized Inverted Index, Обобщенный инвертированный индекс
GiST | Generalized Search Tree, Обобщенное дерево поиска
Git | Git
GMT | Greenwich Mean Time, Среднее время по Гринвичу
GSSAPI | Generic Security Services Application Programming Interface, Общий программный интерфейс сервисов безопасности
GUC | Grand Unified Configuration, Главная унифицированная конфигурация, подсистема QHB, которая управляет конфигурацией сервера
HBA | Host-Based Authentication, Аутентификация по имени узла
HOT | Heap Only Tuple, функция, убирающая лишние записи индекса и позволяющая повторно использовать пространство, занимаемое удаленными или устаревшими из-за обновления кортежами, без необходимости очистки всей таблицы
IEC, МЭК | International Electrotechnical Commission, Международная электротехническая комиссия
IEEE | Institute of Electrical and Electronics Engineers, Институт инженеров по электротехнике и радиоэлектронике
IPC | Inter-Process Communication, Межпроцессное взаимодействие
ISO, ИСО | International Organization for Standardization, Международная организация по стандартизации
ISSN | International Standard Serial Number, Международный стандартный сериальный номер
JDBC | Java Database Connectivity, Соединение с базами данных на Java
JIT | Just-in-Time compilation, Динамическая компиляция
JSON | JavaScript Object Notation, Нотация объектов JavaScript
LDAP | Lightweight Directory Access Protocol, Облегченный протокол доступа к каталогам
LSN | Log Sequence Number, Регистрационный номер транзакции в журнале, см. pg_lsn и Внутреннее устройство WAL
MSVC | Microsoft Visual C
MVCC | Multi-Version Concurrency Control, Многоверсионное управление параллельным доступом
NLS | National Language Support, Поддержка национальных языков
ODBC | Open Database Connectivity, Открытый интерфейс взаимодействия с базами данных
OID | Object Identifier, Идентификатор объекта
OLAP | Online Analytical Processing, Оперативная аналитическая обработка данных
OLTP | Online Transaction Processing, Оперативная обработка транзакций
ORDBMS | Object-Relational Database Management System, Объектно-реляционная система управления базами данных
РАМ | Pluggable Authentication Modules, Подключаемые модули аутентификации
PGXS | Инфраструктура сборки расширений СУБД «Квант-Гибрид»
PID | Process Identifier, Идентификатор процесса
PITR | Point-In-Time Recovery, Восстановление на момент времени (Непрерывное архивирование)
PL | Procedural Languages, Процедурные языки (на стороне сервера)
POSIX | Portable Operating System Interface, Интерфейс переносимой операционной системы
QHB | QuantumHybridBase, СУБД «Квант-Гибрид»
RDBMS | Relational Database Management System, Реляционная система управления базами данных
RFC | Request For Comments, Рабочее предложение
SGML | Standard Generalized Markup Language, Стандартный обобщенный язык разметки
SPI | Server Programming Interface, Интерфейс программирования сервера
SP-GiST | Space-Partitioned Generalized Search Tree, Обобщенное дерево поиска с разбиением пространства
SQL | Structured Query Language, Язык структурированных запросов
SRF | Set-Returning Function, Функция, возвращающая множество
SSH | Secure Shell, Безопасная оболочка
SSL | Secure Sockets Layer, Уровень защищенных сокетов
SSPI | Security Support Provider Interface, Интерфейс поставщика поддержки безопасности
SYSV | Unix System V
TCP/IP | Transmission Control Protocol (TCP) / Internet Protocol (IP), Протокол управления передачей / Межсетевой протокол
TID | Tuple Identifier, Идентификатор кортежа
TLS | Transport Layer Security, Протокол защиты транспортного уровня
TOAST | The Oversized-Attribute Storage Technique, Методика хранения сверхбольших атрибутов
TPC | Transaction Processing Performance Council, Совет по оценке производительности обработки транзакций
URL | Uniform Resource Locator, Унифицированный указатель ресурса
UTC | Coordinated Universal Time, Всемирное координированное время
UTF | Unicode Transformation Format, Формат преобразования Юникода
UTF8 | Unicode Transformation Format, 8-bit, Формат преобразования Юникода, 8-бит
UUID | Universally Unique Identifier, Универсально уникальный идентификатор
WAL | Write-Ahead Log, Журнал упреждающей записи
XID | Transaction Identifier, Идентификатор транзакции
XML | Extensible Markup Language, Расширяемый язык разметки

Глоссарий

Ниже представлен перечень терминов с описанием их значений в контексте QHB и реляционных баз данных в целом.

ACID

Atomicity (Атомарность), Consistency (Согласованность), Isolation (Изолированность) и Durability (Прочность). Этот набор свойств транзакций в базе данных призван гарантировать корректность операций при параллельном выполнении и даже в случае ошибок, при отключении питания и т. п.

DELETE

Команда SQL, удаляющая строки из данной таблицы или отношения.

Подробную информацию см. в описании команды DELETE.

GRANT

Команда SQL, дающая пользователю или роли доступ к определенным объектам в базе данных.

Подробную информацию см. в описании команды GRANT.

INSERT

Команда SQL, добавляющая в таблицу новые данные.

Подробную информацию см. в описании команды INSERT.

NULL

Понятие несуществования, которое является основным положением теории реляционных баз данных. Оно обозначает отсутствие какого-либо определенного значения.

Postmaster (процесс)

Самый первый процесс экземпляра СУБД. Он запускает другие вспомогательные процессы и управляет ими, а также создает обслуживающие процессы по требованию.

Подробную информацию см. в разделе Запуск сервера базы данных.

REVOKE

Команда, лишающая указанный список ролей доступа к определенному набору объектов базы данных.

Подробную информацию см. в описании команды REVOKE.

ROLLBACK

Команда, отменяющая все операции, выполненные с начала транзакции.

Подробную информацию см. в описании команды ROLLBACK.

SELECT

Команда SQL, запрашивающая данные из базы. Обычно команды SELECT не должны никаким образом изменять базу данных, однако нельзя исключить возможность, что побочным действием функций, вызванных в каком-либо запросе, станет изменение данных.

Подробную информацию см. в описании команды SELECT.

TOAST

Механизм, который обеспечивает хранение больших атрибутов строк таблицы, разделяя их и размещая в дополнительной таблице, называемой TOAST-таблицей. У каждого отношения с большими атрибутами имеется собственная TOAST-таблица.

Подробную информацию см. в подразделе TOAST.

UPDATE

Команда SQL, изменяющая уже существующие строки определенной таблицы. Эта команда не может добавлять или удалять строки.

Подробную информацию см. в описании команды UPDATE.

WAL

См. Журнал упреждающей записи.

Автовакуум (процесс)

Ряд фоновых процессов, регулярно выполняющих операции очистки и анализа.

Подробную информацию см. в подразделе Процесс «Автовакуум».

Агрегатная функция (подпрограмма)

Функция, которая объединяет (агрегирует) множество входных значений, например, подсчитывая их количество, складывая их или вычисляя среднее, в результате чего на выходе получается одно значение.

Подробную информацию см. в подразделе Агрегатные функции.

См. также Оконная функция (подпрограмма).

Анализ (операция)

Процесс сбора статистики по данным в таблицах и других отношениях, помогающий планировщику запросов принимать решения, касающиеся способов выполнения запросов.

(Не путайте этот термин с указанием ANALYZE команды EXPLAIN.)

Подробную информацию см. в описании команды ANALYZE.

Аналитическая функция

См. Оконная функция (подпрограмма).

Архивирование WAL (процесс)

Процесс сохранения копий файлов WAL в целях создания резервных дубликатов данных или для поддержания актуального состояния реплик.

Подробную информацию см. в подразделе Непрерывное архивирование и восстановление на момент времени (PITR).

Атомарность

Свойство транзакции, которое заключается в том, что все ее операции выполняются вместе, либо не выполняются вовсе. Кроме того, в случае сбоя системы в процессе выполнения транзакции никаких частичных изменений после восстановления не появится. Это одно из свойств ACID.

Атомарный

Применительно к данным означает, что элемент данных нельзя разделить на меньшие части.

Применительно к транзакциям в базах данных см. атомарность.

Атрибут

Элемент с определенным именем и типом данных, содержащийся в кортеже.

База данных

Именованный набор локальных объектов SQL.

Подробную информацию см. в подразделе Обзор раздела Управление базами данных.

Блокировка

Механизм, позволяющий некоторому процессу ограничивать или запрещать параллельный доступ к ресурсу.

Ветвь

Отдельный набор файлов-сегментов, в которых хранится отношение. В основной ветви находятся собственно данные отношения. Помимо нее существуют две дополнительные ветви для метаинформации: карта свободного пространства и карта видимости. У нежурналируемых отношений также имеется ветвь инициализации.

Внешний ключ

Вид ограничения, определяемый для одного или нескольких столбцов в таблице и требующий, чтобы значения в этих столбцах указывали на ноль или одну строку в другой (или, в редких случаях, той же) таблице.

Временная таблица

Таблица, существующая либо в рамках сеанса, либо в рамках транзакции, в соответствии с выбранным при ее создании режимом. Такие таблицы не являются журналируемыми, и их содержимое не видно в других сеансах. Временные таблицы часто применяются для хранения промежуточных данных в ходе многошаговой операции.

Подробную информацию см. в описании команды CREATE TABLE.

Главный сервер

См. Основной (сервер).

Журнал упреждающей записи

Журнал, в котором отслеживаются изменения в кластере баз данных, производимые при выполнении операций пользователями и самой системой. Он содержит множество отдельных записей WAL, вносимых последовательно в файлы WAL.

Журналируемость

Таблица считается журналируемой, если вносимые в нее изменения отсылаются в WAL. По умолчанию все обычные таблицы являются журналируемыми. Таблицу можно сделать нежурналируемой либо во время создания, либо с помощью команды ALTER TABLE.

Записывание WAL (процесс)

Процесс, осуществляющий перенос записей WAL из общей памяти в файлы WAL.

Подробную информацию см. в разделе Журнал упреждающей записи (WAL).

Запись

См. Кортеж.

Запись WAL

Низкоуровневое описание отдельного изменения данных. Оно содержит достаточно информации для повторного выполнения (воспроизведения) этого изменения в случае, если при сбое системы оно не зафиксировалось. В записях WAL используется непечатаемый двоичный формат.

Подробную информацию см. в разделе Внутреннее устройство WAL.

Запись журнала

Устаревшее название записи WAL.

Запрос

Указание, передаваемое клиентом обслуживающему процессу, в результате выполнения которого обычно либо выдаются результаты, либо меняются данные в базе.

Идентификатор транзакции

Числовой уникальный идентификатор, последовательно назначаемый каждой транзакции при первой операции, меняющей данные в базе. Часто он сокращенно обозначается xid. Когда эти идентификаторы хранятся на диске, каждый занимает всего 32 бита, поэтому создать их можно только примерно для 4 миллиардов пишущих транзакций. Чтобы система могла работать дольше, дополнительно используются эпохи, размер которых также составляет 32 бита. Когда счетчик транзакций достигает максимального значения xid, он сбрасывается до 3 (меньшие значения зарезервированы) и значение эпохи увеличивается на 1. В некоторых контекстах значения эпохи и xid рассматриваются вместе как одна 64-битная величина.

Подробную информацию см. в разделе Типы идентификаторов объектов.

Изолированность

Свойство, которое заключается в том, что результаты транзакции не видны для параллельных транзакций, пока она не будет зафиксирована. Это одно из свойств ACID.

Подробную информацию см. в разделе Изоляция транзакций.

Индекс (отношение)

Отношение, содержащее производную информацию от исходных данных таблицы или материализованного представления. Его внутренняя структура обеспечивает быструю выборку и извлечение исходных данных.

Подробную информацию см. в описании команды CREATE INDEX.

Карта видимости (ветвь)

Хранимая структура, содержащая метаинформацию о каждой странице данных в основной ветви таблицы. Для записи о каждой странице в ней отводится два бита: первый (all-visible, полностью видимая) показывает, что все кортежи в странице видны всем транзакциям, а второй (all-frozen, полностью замороженная) — что все кортежи в странице помечены как замороженные.

Карта свободного пространства (ветвь)

Хранимая структура, содержащая метаинформацию о каждой странице данных в основной ветви таблицы. В ней указывается, какой объем доступен для новых кортежей в каждой странице, при этом сама она специально оптимизирована для эффективного поиска свободного места для нового кортежа заданного размера.

Подробную информацию см. в разделе Карта свободного пространства.

Каталог

В стандарте SQL этот термин обозначает то, что в терминологии QHB называется базой данных.

(Не путайте это понятие с понятием системного каталога).

Подробную информацию см. в подразделе Обзор раздела Управление базами данных.

Каталог данных

Главный каталог в файловой системе сервера, содержащий все файлы данных и подкаталоги, связанные с кластером баз данных (за исключением табличных пространств и, возможно, WAL). Обычно на каталог данных ссылается переменная окружения PGDATA.

Каталог данных в совокупности со всеми дополнительными табличными пространствами образует хранилище данных кластера.

Подробную информацию см. в разделе Структура файлов базы данных.

Класс (устаревшее)

См. Отношение.

Кластер баз данных

Совокупность баз данных и глобальных объектов SQL, а также их общих статических и динамических метаданных. Иногда также называется просто кластером.

В QHB термин кластер иногда обозначает также экземпляр СУБД. (Не путайте этот термин с командой SQL CLUSTER.)

Клиент (процесс)

Любой процесс, возможно, удаленный, который запускает сеанс путем подключения к экземпляру СУБД для взаимодействия с базой данных.

Ключ

Средство идентификации строки в таблице или другом отношении по значениям, содержащимся в одном или нескольких атрибутах этого отношения.

Конкурентный доступ

Концепция, определяющая возможность одновременного выполнения нескольких независимых операций в одной базе данных. В QHB конкурентный доступ реализуется механизмом многоверсионного управления конкурентным доступом.

Контрольная точка

Точка в последовательности записей WAL, в которой гарантируется, что в файлы данных кучи и индекса попала вся информация из общей памяти, измененная до этой контрольной точки; для обозначения этой точки в WAL записывается и сбрасывается запись контрольной точки.

Контрольной точкой также называется процесс выполнения всех действий, необходимых для достижения контрольной точки в приведенном выше определении. Этот процесс запускается при выполнении предопределенных условий, например по истечении заданного времени или после помещения в журнал некоторого объема записей; также ее может вызвать пользователь командой CHECKPOINT.

Подробную информацию см. в разделе Конфигурация WAL.

Кортеж

Упорядоченный набор атрибутов. Порядок атрибутов может определяться таблицей (или другим отношением), в которой содержится кортеж. В этом случае кортеж часто называют строкой таблицы. Он также может определяться структурой результирующего множества; такие кортежи иногда называют записями.

Куча

Содержит значения атрибутов строк (то есть непосредственно данные) отношения. Куча размещается в одном или нескольких файловых сегментах в основной ветви отношения.

Материализованное представление (отношение)

Отношение, которое определяется оператором SELECT (подобно обычному представлению), но при этом содержит данные как обычная таблица. Его содержимое нельзя изменить операторами INSERT, UPDATE и DELETE.

Подробную информацию см. в описании команды CREATE MATERIALIZED VIEW.

Материализованность

Свойство данных, означающее, что они были вычислены заранее и сохранены для последующего использования, а не вычисляются каждый раз «на лету».

Этот термин используется применительно к материализованным представлениям и означает, что данные, полученные из запроса представления, сохраняются на диске отдельно от источника этих данных.

Этот термин также используется применительно к некоторым многоэтапным запросам и означает, что данные, полученные на некотором этапе, сохраняются в памяти (а могут быть также вытеснены на диск), благодаря чему они могут быть многократно прочитаны на следующем этапе.

Многоверсионное управление конкурентным доступом (MVCC)

Механизм, позволяющий нескольким транзакциям читать и записывать одни и те же строки и при этом не ждать друг друга. В QHB для реализации MVCC создаются копии (версии) кортежей, когда их данные меняются; после того, как все транзакции, которым были видны старые версии, завершаются, эти версии удаляются.

Мусорный объем

Объем в страницах данных, который не занимают текущие версии строк таблицы, в том числе неиспользуемый (свободный) объем и объем, занимаемый старыми версиями строк.

Нежурналируемость

Свойство некоторых отношений: изменения в них не отражаются в журнале WAL. Для таких отношений отсутствует возможность репликации и восстановления после сбоя.

Нежурналируемые таблицы в основном применяются для хранения промежуточных рабочих данных, которыми совместно пользуются различные процессы.

Все временные таблицы являются нежурналируемыми.

Обертка сторонних данных

Способ представления данных, находящихся не в локальной базе данных, таким образом, что они отображаются как содержащиеся в локальных таблицах. Обертка сторонних данных позволяет определить сторонний сервер и сторонние таблицы.

Подробную информацию см. в описании команды CREATE FOREIGN DATA WRAPPER.

Область данных

См. Каталог данных.

Обслуживающий процесс (backend)

Процесс экземпляра СУБД, который реализует сеанс клиента и обрабатывает его запросы.

(Не путайте это понятие со схожими понятиями фоновый рабочий процесс и фоновый процесс записи.)

Общая память

Область ОЗУ, совместно используемая процессами, относящимися к одному экземпляру СУБД. В нее отображаются фрагменты файлов базы данных, а также она служит временным хранилищем для записей WAL и содержит дополнительную общую информацию. Обратите внимание, что общая память относится ко всему экземпляру, а не к отдельной базе данных в нем.

Самая крупная часть общей памяти носит название общие буферы, и в нее в виде страниц отображаются фрагменты файлов данных. Страница, измененная в буфере, но еще не сохраненная в файловой системе, называется «грязной».

Подробную информацию см. в подразделе Память.

Объект SQL

Любой объект, который может быть создан командой CREATE. Большинство объектов относятся к одной базе данных, поэтому они обычно называются локальными объектами.

Большинство локальных объектов принадлежат некоторой схеме в соответствующей базе данных, как например отношения (любых видов), подпрограммы (любых видов), типы данных и т. д. Имена таких объектов одного вида в отдельной схеме должны быть уникальными.

Кроме того, существуют локальные объекты, не принадлежащие схемам, например расширения, приведения типов данных и обертки сторонних данных. Имена таких объектов одного вида должны быть уникальными в базе данных.

Другие виды объектов, например роли, табличные пространства, источники репликации, подписки логической репликации, а также сами базы данных не являются локальными объектами SQL, так как они существуют вне какой-либо отдельной базы данных; они называются глобальными объектами. Имена таких объектов должны быть уникальными во всем кластере баз данных.

Подробную информацию см. в подразделе Обзор раздела Управление базами данных.

Ограничение

Условие, определяющее допустимость значений данных в таблице или атрибутов в домене.

Подробную информацию см. в разделе Ограничения.

Ограничение уникальности

Вид ограничения, определенного для отношения, который ограничивает значения, допустимые в одном или нескольких столбцах таким образом, чтобы каждое значение или набор значений могли присутствовать в отношении только один раз — то есть ни в какой другой строке отношения не может быть значений, равных данным.

Так как значения NULL не считаются равными друг другу, то наличие в отношении нескольких строк со значениями NULL не нарушает ограничение уникальности.

Оконная функция (подпрограмма)

Вид функций, используемых в запросах, который применяется к партиции результирующего множества запроса; результат такой функции зависит от значений в строках одной партиции или рамки.

В качестве оконных могут использоваться и агрегатные функции, но оконные функции способны, например, определять ранг для каждой строки в партиции. Оконные функции также называют аналитическими функциями.

Подробную информацию см. в разделе Оконные функции.

Оптимизатор

См. Планировщик запросов.

Основной (сервер)

Когда две и более базы данных связываются вместе посредством репликации, сервер, считающийся достоверным источником информации, называется основным или главным.

Отношение

Общий термин, который охватывает все объекты в базе данных, имеющие имя и упорядоченный список атрибутов. Отношениями являются таблицы, последовательности, представления, сторонние таблицы, материализованные представления, составные типы и индексы.

В более общем смысле отношением является набор кортежей; например, результат выполнения запроса тоже отношение.

В QHB название класс является устаревшим синонимом отношения.

Очистка (VACUUM)

Процесс удаления устаревших версий кортежей из таблиц или материализованных представлений и другая тесно связанная с ним обработка данных, потребность в которой продиктована реализацией MVCC в QHB. Очистку можно запустить с помощью команды VACUUM, но она также может осуществляться автоматически процессами автовакуума.

Подробную информацию см. в разделе Регулярная очистка.

Параллельный запрос

Возможность распределять выполнение частей запроса между параллельными процессами, используя возможности серверов с несколькими ЦП.

Партиционированная таблица (отношение)

Отношение, которое по смыслу не отличается от таблицы, но его содержимое хранится распределенно, в нескольких партициях.

Партиция

Одно из нескольких отдельных (не пересекающихся) подмножеств большего множества.

Применительно к партиционированной таблице: одна из таблиц, содержащих часть данных партиционированной таблицы, которая называется родительской. Партиция сама по себе является таблицей, поэтому запросы могут обращаться к ней напрямую; при этом партиция иногда тоже может быть партиционированной таблицей, позволяя создавать иерархии.

Применительно к оконным функциям в запросе партиция соответствует заданному пользователем критерию, определяющему, какие из смежных строк результирующего множества запроса может обрабатывать такая функция.

Первичный ключ

Особый вид ограничения уникальности, определяемого для таблицы или другого отношения, который дополнительно гарантирует, что все атрибуты в первичном ключе не имеют значений NULL. Как можно понять из названия, в таблице может быть только один первичный ключ, хотя в ней может быть несколько ограничений уникальности, также не допускающих значения NULL в атрибутах.

Переработка

См. Файл WAL.

Планировщик запросов

Компонент QHB, предназначенный для определения (планирования) наиболее эффективного способа выполнения запросов. Также называется оптимизатором запросов, оптимизатором или просто планировщиком.

Подключение

Установленная линия передачи данных между клиентским процессом и обслуживающим процессом, обычно сетевая, в рамках которой реализуется сеанс. Иногда этот термин употребляется как синоним сеанса.

Подробную информацию см. в разделе Подключения и аутентификация.

Подпрограмма

Определенный набор инструкций, сохраненный в СУБД, который может выполняться по запросу. Подпрограммы могут быть написаны на самых разных языках программирования. Понятие подпрограмм включает функции (в том числе функции, возвращающие множества, и триггерные функции), агрегатные функции и процедуры.

QHB уже содержит большое количество подпрограмм, но при этом поддерживает и добавление пользовательских подпрограмм.

Пользователь

Роль с правом LOGIN.

Последовательность (отношение)

Вид отношения, позволяющий генерировать значения. Как правило, генерируются последовательные неповторяющиеся числа. Последовательности часто используются для генерирования идентификаторов в первичном ключе.

Представление

Отношение, которое определяется оператором SELECT, но само по себе не хранится. При каждом обращении к представлению его определение подставляется в запрос, как если бы пользователь просто ввел его в качестве подзапроса вместо имени представления.

Подробную информацию см. в описании команды CREATE VIEW.

Приведение

Преобразование элемента данных из его текущего типа в другой тип данных.

Подробную информацию см. в описании команды CREATE CAST.

Проверочное ограничение

Вид ограничения, определяемый для отношения и ограничивающий множество значений, допустимых в одном или нескольких атрибутах. Проверочное ограничение может обращаться к любому атрибуту в той же строке отношения, но не может обращаться к другим строкам этого же отношения или к другим отношениям.

Подробную информацию см. в разделе Ограничения.

Протоколирование (процесс)

В активном состоянии этот процесс записывает сообщения о событиях в базе данных в свой текущий файл журнала. Данный файл сменяется новым, когда выполняется некоторый критерий (ограничение по времени или объему). Также этот процесс называется syslogger.

Подробную информацию см. в разделе Регистрация ошибок и протоколирование.

Процедура (подпрограмма)

Вид подпрограммы, характеризующийся отсутствием возвращаемых значений. Кроме того, процедуры могут создавать операторы управления транзакциями, например COMMIT и ROLLBACK. Вызываются процедуры командой CALL.

Подробную информацию см. в описании команды CREATE PROCEDURE.

Процесс контрольных точек (checkpointer)

Специальный процесс, обеспечивающий выполнение процедуры контрольной точки.

Прочность

Гарантия того, что после фиксирования транзакции произведенные ей изменения не будут потеряны даже в случае системной ошибки или краха сервера. Это одно из свойств ACID.

Расширение

Дополнительный программный модуль, который может быть установлен в экземпляре СУБД для расширения его функциональности.

Подробную информацию см. в разделе Упаковка связанных объектов в расширение.

Резервный сервер

См. Реплика (сервер).

Результирующее множество

Отношение, передаваемое из обслуживающего процесса клиенту после выполнения команды SQL, обычно SELECT, но если указать предложение RETURNING, это также могут быть команды INSERT, UPDATE или DELETE.

То, что результирующее множество является отношением, означает, что один запрос может фигурировать в определении другого запроса, превращаясь в подзапрос.

Реплика (сервер)

База данных, связанная с основной базой и содержащая копию некоторых или всех данных последней. Прежде всего такие базы создаются для расширения возможностей доступа к этим данным, а также для обеспечения доступности данных в случае потери основного сервера.

Репликация

Процесс переноса информации с одного сервера на другой называется репликацией. Она может реализовываться как физическая репликация, когда с одного сервера на другой точно копируются все изменения на уровне файлов, или как логическая репликация, когда изменения в определенном подмножестве данных передаются с помощью представления на более высоком уровне.

Роль

Набор прав доступа к объектам экземпляра СУБД. Принадлежность к роли тоже может считаться правом, которым можно наделять другие роли. Это часто используется для удобства распределения прав или когда нужно дать одинаковые права множеству пользователей.

Подробную информацию см. в описании команды CREATE ROLE.

Сбор статистики (процесс)

Этот процесс собирает статистическую информацию о действиях экземпляра СУБД.

Подробную информацию см. в разделе Сборщик статистики.

Сеанс

Состояние, в котором клиент может взаимодействовать с обслуживающим процессом, используя установленное подключение.

Сегмент

См. Файловый сегмент.

Сегмент WAL

См. Файл WAL.

Сервер

Компьютер, на котором работают экземпляры QHB. Термин сервер может обозначать как настоящее аппаратное оборудование, так и контейнер или виртуальную машину.

Иногда сервером также называют экземпляр СУБД или сетевой узел.

Сервер баз данных

См. Экземпляр СУБД.

Сетевой узел (хост)

Компьютер, взаимодействующий с другими компьютерами по сети. Иногда так называют сервер. Также это название применимо к компьютеру, на котором работают клиентские процессы.

Системный каталог

Набор таблиц, которые описывают структуру всех объектов SQL экземпляра СУБД. Системный каталог располагается в схеме pg_catalog. Эти таблицы содержат данные во внутреннем представлении и, как правило, не считаются полезными для изучения пользователями. В более понятном виде часть этой информации доступна в ряде удобных для пользователя представлений, также находящихся в схеме pg_catalog. Кроме того, имеется схема information_schema, содержащая дополнительные таблицы и представления, из которых можно получить отчасти ту же, но также и некоторую другую информацию, предоставляемую в соответствии с требованиями стандарта SQL.

Подробную информацию см. в разделе Схемы.

Согласованность

Свойство базы, суть которого состоит в том, что ее данные всегда удовлетворяют ограничениям целостности. Транзакциям позволяется временно нарушать такие ограничения до их фиксирования, но если к моменту завершения транзакции эти нарушения не устраняются, транзакция автоматически откатывается. Это одно из свойств ACID.

Соединение (join)

Операция (и ключевое слово SQL), выполняемая в запросах для объединения данных из нескольких отношений.

Сопоставление пользователей

Трансляция учетных записей локальной базы данных в учетные записи удаленной системы данных, определяемая оберткой сторонних данных.

Подробную информацию см. в описании команды CREATE USER MAPPING.

Ссылочная целостность

Средство ограничения данных в каком-либо отношении по внешнему ключу таким образом, чтобы им обязательно соответствовали данные в другом отношении.

Стандарт SQL

Совокупность документов, определяющих язык SQL.

Столбец

Атрибут, относящийся к таблице или представлению.

Сторонний сервер

Именованная совокупность сторонних таблиц, для обращения к которым используется одна обертка сторонних данных, и которые также имеют общие параметры конфигурации.

Подробную информацию см. в описании команды CREATE SERVER.

Сторонняя таблица (отношение)

Отношение, строки и столбцы которого представляются сходными с таковыми в обычной таблице, но запросы к нему проходят через обертку сторонних данных, которая в ответ возвращает результирующие множества, структурированные согласно определению сторонних таблиц.

Подробную информацию см. в описании команды CREATE FOREIGN TABLE.

Страница данных

Основная структура, в которой хранятся данные отношений. Все страницы имеют одинаковый размер. Страницы данных обычно хранятся на диске, каждая в определенном файле, и могут быть прочитаны в общие буферы, где могут быть изменены, став в результате грязными. При записи на диск они вновь становятся чистыми. Новые страницы, изначально существующие только в памяти, также считаются грязными, пока не будут записаны на диск.

Строка таблицы

См. Кортеж.

Схема

Схема — это пространство имен объектов SQL, принадлежащих одной базе данных. Каждый отдельный объект SQL должен располагаться ровно в одной схеме.

Все системные объекты SQL располагаются в схеме pg_catalog.

В более общем смысле термин схема означает совокупность всех описаний данных (определений таблиц, ограничений, комментариев и т. д.) в определенной базе или в ее подмножестве.

Подробную информацию см. в разделе Схемы.

Таблица

Совокупность кортежей, имеющих общую структуру данных (одинаковое количество атрибутов в том же порядке, имеющих одинаковые имена и типы). Таблица является наиболее распространенным видом отношения в QHB.

Подробную информацию см. в описании команды CREATE TABLE.

Табличное пространство

Именованное расположение в файловой системе сервера. Все объекты SQL, для которых требуется хранилище за рамками их определений в системном каталоге, должны располагаться в некотором табличном пространстве. Изначально в кластере баз данных есть только одно табличное пространство, которое называется pg_default и по умолчанию содержит все объекты SQL.

Подробную информацию см. в разделе Табличные пространства.

Точка сохранения

Специальная отметка в последовательности операций транзакции. Изменения данных, произведенные после этого момента времени, можно отменить, вернув данные в состояние на момент сохранения.

Подробную информацию см. в описании команды SAVEPOINT.

Транзакции/сек (TPS)

Среднее количество транзакций, выполняющихся за секунду, подсчитанное по всем активным сеансам за время наблюдения. Эта величина используется для оценки производительности экземпляра СУБД.

Транзакция

Совокупность команд, которые должны выполниться как одна атомарная команда: они завершаются успешно либо отменяются все вместе, а результаты их выполнения не видны в других сеансах до завершения транзакции и, возможно, даже позже (в зависимости от уровня изоляции).

Подробную информацию см. в разделе Изоляция транзакций.

Триггер

Функция, которую можно определить для выполнения какой-либо определенной операции (INSERT, UPDATE, DELETE, TRUNCATE) с некоторым отношением. Триггер выполняется в рамках той же транзакции, что и вызвавший его оператор, и если в функции триггера произойдет ошибка, этот оператор тоже не будет выполнен успешно.

Подробную информацию см. в описании команды CREATE TRIGGER.

Файл WAL

Также называется сегментом WAL или файлом сегмента WAL. Представляет собой один из последовательно нумеруемых файлов, служащих хранилищем WAL. Все эти файлы имеют одинаковый предопределенный размер и записываются по порядку. В файле WAL перемежаются изменения, производимые в нескольких параллельных сеансах. В случае сбоя системы эти файлы также по порядку считываются и все записанные в них изменения воспроизводятся, в результате чего восстанавливается состояние системы до сбоя.

Каждый файл WAL может быть освобожден после того, как во время контрольной точки все изменения из него будут переписаны в соответствующие файлы данных. Освобожденный файл может быть удален или переименован (это называется переработкой) так, чтобы его можно было использовать в будущем.

Подробную информацию см. в разделе Внутреннее устройство WAL.

Файл журнала

Файлы журналов содержат предназначенные для человека текстовые сообщения о событиях. Например, в журнале могут фиксироваться ошибки входа, длительные запросы и т. д.

Подробную информацию см. в разделе Обслуживание файла журнала.

Файловый сегмент

Физический файл, содержащий данные для определенного отношения. Размер сегментов ограничивается параметром конфигурации (обычно это 1 гигабайт), поэтому отношения большего объема разбиваются на несколько сегментов.

Подробную информацию см. в разделе Структура файлов базы данных.

(Не путайте это понятие с подобным понятием сегмент WAL.)

Фиксирование

Акт успешного завершения транзакции в базе данных, обеспечивающий прочность и видимость ее результатов для других транзакций.

Подробную информацию см. в описании команды COMMIT.

Фоновая запись (процесс)

Процесс, записывающий «грязные» страницы данных из общей памяти в файловую систему. Он периодически запускается, но лишь на короткое время, чтобы вызываемые им затратные операции ввода/вывода распределялись во времени с целью исключения значительных пиков нагрузки, блокирующих другие процессы.

Подробную информацию см. в подразделе Фоновая запись.

Фоновый рабочий процесс

Процесс внутри экземпляра СУБД, выполняющий системный или пользовательский код. Обеспечивает инфраструктуру для различной функциональности QHB, в частности, для логической репликации и параллельных запросов. Кроме того, отдельные рабочие процессы могут создаваться расширениями.

Подробную информацию см. в главе Фоновые рабочие процессы.

Функция (подпрограмма)

Вид подпрограммы, который получает ноль или более аргументов, выдает ноль или более выходных значений и может выполняться только в рамках одной транзакции. Функции могут вызываться в ходе выполнения запросов, например, SELECT. Некоторые функции могут возвращать множества; они называются функциями, возвращающими множества.

Функции также вызываются при срабатывании триггеров.

Подробную информацию см. в описании команды CREATE FUNCTION.

Экземпляр СУБД

Группа обслуживающих и вспомогательных процессов, использующих общую область разделяемой памяти. Экземпляром СУБД управляет один процесс postmaster, а этот экземпляр управляет ровно одним кластером баз данных со всеми его базами. На одном сервере могут работать несколько экземпляров СУБД, если их TCP-порты не конфликтуют.

Конкретный экземпляр реализует все функциональные возможности СУБД: читает и записывает файлы, работает с общей памятью, обеспечивает свойства ACID, принимает подключения клиентских процессов, проверяет права доступа, выполняет восстановление после сбоя, осуществляет репликацию и т. д.

Элемент данных

Внутреннее представление одного значения некоторого типа данных SQL.

Эпоха

См. Идентификатор транзакции.

Поддержка цветового оформления

Большинство программ в пакете QHB могут выводить в консоль цветной текст. В этом приложении описывается, как это настраивается.

Когда используется цветовое оформление

Для использования цветного текста следует установить переменную среды PG_COLOR в одно из следующих значений:

always — цвет используется всегда.

auto — цвет используется, если стандартный поток вывода сообщений об ошибках связан с устройством терминала.

В ином случае цвет не используется.

Конфигурация цветового оформления

Фактически используемые цвета конфигурируются с помощью переменной среды PG_COLORS (обратите внимание на множественное число — colors). Значение переменной представляет собой список пар ключ=значение, разделенных двоеточиями. Ключи указывают, для чего будет применяться цвет, а значения задают цвет в формате SGR (Select Graphic Rendition), который интерпретируется терминалом.

В настоящее время применяются следующие ключи:

error
выделяет текст «ошибки» в соответствующих сообщениях

warning
выделяет текст «предупреждения» в соответствующих сообщениях

locus выделяет в сообщениях информацию о расположении (например, имя программы и имя файла)

Значение по умолчанию: error=01;31:warning=01;35:locus=01 (01;31 = красный жирным шрифтом, 01;35 = пурпурный жирным шрифтом, 01 = жирный шрифт, цвет по умолчанию).

Подсказка
Этот формат указания цветов также применяется и в других программных продуктах, например, в GCC, GNU coreutils и GNU grep.

Лицензирование QHB версии 1.5.0

Получение файла лицензии

Клиент получает лицензионный файл который необходим для использования QHB. Вместе с файлом лицензии клиент получает номер лицензии, по которому его можно идентифицировать. Данный номер также необходим для обращения в техническую поддержку.

Применение лицензионных параметров

При запуске QHB ожидает наличия лицензионного файла license в каталоге, куда он установлен, например /usr/local/qhb/license.

Для проверки лицензии требуется произвести идентификацию аппаратуры хоста пользователя, которая выполняется пакетом qhb-serial. Данная утилита также необходима для запуска QHB и утилит, использование которых предусматривает наличие лицензионного файла.

Примечание
Утилита qhb-serial может не требоваться для запуска таких утилит как qcp и qdl, если в лицензионном файле идентификация аппаратуры хоста не заполняется.

По проверке соответствия параметров лицензирования, указанных в лицензионном файле текущей инсталляции QHB, выводится сообщение:

первоначально при выводе версии печатается краткая информация о сроке действия лицензии;

далее в файл журнала предзаписи выводятся найденные ошибки загрузки и валидации лицензии.

Запрос лицензионных параметров во время работы

Для удобства администратора QHB предоставляется SQL-функция, которая позволяет получить детали текущей используемой лицензии.

Допустимые значения:

start_date          Дата начала действия лицензии

end_date            Дата окончания действия лицензии

release             Текущий релиз

product_code        Название продукта

Например:

# select qhb_license_info('product_code');
-- QHB

# select qhb_license_info('release');
-- 1.3

При невозможности обработать указанный параметр функция qhb_license_info возвращает пустую строку в виде значения типа text.

Возвращаемые данные соответствуют значениям, указанным в самой лицензии (лицензионный файл), и могут отличаться от значений, соответствующих сборке QHB.

Действия клиента в случае ошибки загрузки лицензии

При обычной проверке лицензии (при запуске QHB) могут возникать два типа ошибок:

ошибки загрузки лицензии;

ошибки валидации лицензии относительно текущей сборки/релиза QHB.

В случае ошибки загрузки лицензии следует удостовериться, что:

лицензия расположена по ожидаемому продуктом пути: в каталоге установки, например /usr/local/qhb/license;

файл лицензии не был изменен после получения от коммерческого отдела (файл подписан криптографически, и любые правки делают его недействительным);

у пользователя QHB есть права на чтение лицензионного файла.

В случае соблюдения этих условий и повторного возникновения ошибки следует обратиться в техническую поддержку.

Действия клиента в случае ошибки валидации лицензии

В случае ошибки валидации лицензии детальная информация об ошибке доступна в начале файла журнала QHB, сформированного при запуске.

Сообщение включает в себя результат валидации (каждый некорректный параметр выводится на новой строке) и краткое указание для дальнейших действий, локализованное для текущих настроек ОС (LC_ALL=ru/en).

Лицензирование QDL и QCP

При запуске данные утилиты ожидают наличие лицензионного файла qdl-license или qcp-license (аналогично QHB) в директории местонахождения исполняемого файла.

Сообщения об ошибках загрузки и валидации лицензии выводятся в лог соответствующих утилит (в зависимости от настроек это могут быть стандартные потоки ввода-вывода либо файл журнала).

Возможности запросить лицензионные параметры для утилит во время их работы в данном релизе не предусмотрено.

В случае ошибки валидации лицензии детальная информация об ошибке доступна в виде результата валидации (каждый некорректный параметр выводится на новой строке) и краткого указания для дальнейших действий (вывод зависит от текущих настроек локали).

Замечания к релизу QHB версии 1.5.0

Добавлено

Расширение QHB Part_Import, частичный импорт файлов данных из «внешней» БД.

Переменные уровня сессии - qhb-variables

Qbackup: Возможность валидировать резервные копии (команда validate).

Qbackup: Возможность удаления WAL-файлов при удалении резервной копии.

Поддержка отслеживания статуса выполнения запросов с помощью qhb_running_queries() и qhb_dump_qep(<query_id>).

Экспериментальная поддержка работы планировщика заданий QSched.

Поддержка дистрибутива Fedora 35.

Поддержка дистрибутива Альт 8 СП.

QDL: Новые типы данных

QDL: API для загрузки данных без использования CSV

Улучшено

Обновлено ядро QHB, теперь оно основано на PG 14.

Qbackup: Производительность сжатия резервных копий.

Qbayes: добавлены параметры, ограничивающие "коммутативный взрыв".

Qbayes: добавлен тип discrete_integer (перечислимый тип данных) в описание модели.

Устранена уязвимость в процессе сброса WAL - урегулирована параметром wal_after_error_sleep_delay.

QDL: Убрано ограничение в 8Кб на размер данных

Исправлено

Добавлены mock файлы описания расширений для 2B для исправления сценариев платформы «1С:Предприятие».

Удалено

Пользовательская интерактивная утилита для работы из командной строки qsql удалена. Рекомендовано использовать утилиту psql.

Замечания к релизу QHB версии 1.4.0

Добавлено

Поддержка дистрибутива Fedora 34.

Расширение QHB Qbayes, методы статистического анализа на основе условных вероятностей Байеса.

Модуль QHB Disk Cache Dump, предоставляющий механизм сохранения и восстановления содержимого дискового кэша.

Qbackup: Поддержка удаленного резервного копирования с использованием протокола репликации.

Qbackup: Подкоманда replica для создания реплики сервера.

Qbackup: Опции работы с паролями (диалоговый режим).

Qbackup: Обновлена структура каталога.

Qbackup: Поддержка табличных пространств.

Qbackup: Поддержка CDC (Change Data Capture, захват изменения данных) для инкрементальных резервных копий.

Qbackup: Возможность быстрой контрольной точки перед резервным копированием.

Qbackup: Опция, позволяющая базировать новые инкрементальные резервные копии на последних удачно завершившихся копиях.

Qbackup: Возможность указать комментарий для резервной копии при ее создании.

Qbackup: Расширены возможности команды remove.

QDL: Механизм совместимости с CDC.

QDL: Поддержка получения данных из stdin.

QCP: Поддержка списка stand_by серверов для автоматического переключения на резервный сервер в случае потери связи с текущим сервером БД.

Улучшено

QBackup: В 2 раза ускорено восстановление для инкрементальных резервных копий.

QDL: Значительно улучшена производительность многопоточного режима.

QDL: Улучшены сообщения об ошибках разбора CSV.

QSS: Повышение безопасности. Функционирование в отдельном процессе.

QSS: Унификация администрирования пользователей и управление ключами.

Исправлено

QBackup: Исправлено отображение информации в некоторых ошибках.

QBackup: Возможность удаления информации о резервных копиях без директории каталога.

QDL: Из файла конфигурации удалено поле encoding (было помечено как устаревшее в релизе 1.2.0).

Замечания к релизу QHB версии 1.3.1

Добавлено

Поддержка дистрибутива ROSA Enterprise Linux Server (RELS) 7.

Исправлено

Исправлены ошибки расширения Qbim, возникающие в некоторых ситуациях при изменении версий объекта.

qhb_upgrade: убрана ошибочная проверка на количество пользователей в обновляемом кластере.

QCP: Добавлено отображение параметра application_name входящих клиентов в режиме Debug.

Исправления для повышения стабильности 2B.

Другие минорные исправления.

Замечания к релизу QHB версии 1.3.0

Добавлено

Поддержка таблиц с параметром HOLDMEM = ONLY.

Множество новых метрик в metricsd.

Утилита magma_key_gen для генерации пользовательских ключей QSS на крипто-токенах, поддерживающих аппаратное шифрование по ГОСТ 34.12-2018 и ГОСТ 34.13-2018.

QDL: Поддержка типа UUID.

QDL: Широкая поддержка специальных значений для типов date и timestamp: Epoch, Infinity, -Infinity.

QBackup: Механизмы поддержки и актуализации предыдущих версий каталога.

QBackup: В каталог добавлена информация о версии базы данных и каталога версий.

QCP: Отмена текущего запроса на сервере при отключении клиента.

QCP: «Проброс» запроса на отмену текущего запроса от клиента к конечному серверу

QCP: Автоматическая отмена всех текущих запросов при остановке QCP.

QCP: Отправка heartbeat-сообщений неактивным подключениям к БД.

QCP: Клиенты могут подключаться к QCP через сокеты домена Unix.

Улучшено

Оптимизирована работа с памятью в Tarq.

Локализация в qhb_upgrade.

MChar в 2B научился работать с like escape.

QDL: Зафиксирован формат записи для типов float, double precision, numeric.

QDL: Увеличена производительность генератора значений NULL.

QDL: Увеличена производительность анализатора значений с плавающей запятой и numeric.

QDL: Уменьшен размер исполняемого файла за счет удаления легаси-кода.

QDL: Упрощен генератор скрипта для create_table, уменьшена вероятность ошибок времени исполнения.

QCP: Более гибкая настройка логирования.

QCP: Более подробные сообщения об ошибках, возникающих в процессе работы.

Исправлено

Утечки в вакууме при использовании Tarq.

Параметры настройки rbytea.

Ошибка неправильного параметра auth в qhb_bootstrap под названием scram_sha_256.

В 2B удалена рекурсия из MChar like.

shared_buffers_partitions: правильная обработка параметра.

Запуск qhb_upgrade от пользователя root (запрещен).

Ошибка при инициализации кластера с помощью qhb_bootstrap при указании флага --waldir.

QBackup: Ошибка повторяющегося идентификатора копии при быстром последовательном создании нескольких резервных копий.

QBackup: Ошибка несовпадения директории каталога копий с метаинформацией каталога при ручном перемещении файла каталога.

QBackup: Пути в метаинформации каталога теперь всегда абсолютные.

QBackup: Ошибки во время резервного копирования теперь всегда отражаются в статусе копии.

QCP: qcp, и qcp-ctrl при ошибках теперь возвращают ненулевой код завершения

Замечания к релизу QHB версии 1.2.0

Добавлено

QHB 2B: Расширения QHB для поддержки 1С. Поддерживается платформа «1С:Предприятие», начиная с версии 8.3.18.

Утилита qhb_upgrade, обеспечивающая миграцию базы данных из предыдущих версий QHB и PostgreSQL версии 12.

Поддержка дистрибутивов Fedora 33.

Улучшено

Проверена работоспособность имеющихся пакетов в дистрибутиве Альт Сервер 9.

Замечания к релизу QHB версии 1.1.0

QHB Plan Cache

Экспериментальная функция выключена по умолчанию, добавлена для проверки стабильности и работоспособности.

Новая функция

QHB Bootstrap

Система инициализации базы, функциональный аналог initdb полностью переделан для учета особенностей QHB.

Новая функция

QSS

Модуль безопасного хранения (шифрования с поддержкой криптоалгоритма «Кузнечик»).

QSS пополнился функциями qhb_get_qss_tables и qhb_get_qss_indexes.

Улучшился вывод в случае ошибки.

Добавился новый режим работы с параллельным набором ключей, qss_recrypt с командой сбора зашифрованных таблиц и их индексов.

Добавлена поддержка base64-encoding ключей, «Кузнечика» в CTR режиме.

Добавлена поддержка rbytea.

Добавлена утилита QSS Pinpad.

Добавлена поддержка RuToken.

QSQL

Пользовательская интерактивная утилита для работы из командной строки.

Поддержка мета-команд \password и \include.

Append Only Storage

Данное хранилище позволяет только добавлять записи, но делает это с максимальной скоростью, т. к. не выполняется полноценного MVCC анализа.

Предназначено для таблиц, с которыми не производятся модификации, например журналов, данных с датчиков и т.п.

Новая функция

QBackup

Добавлена новая подсистема резервного копирования и восстановления с поддержкой каталога, архивации резервных копий и сохранения «разности» вместо полной резервной копии.

QDL

Добавлено

Параллельный режим работы QDL.

Пользовательская документация.

Демонстрационный файл конфигурации в пакете RPM.

Изменено

Для того чтобы создать базу, больше не нужен результат работы с СУБД.

Стабилизирована поддержка основных типов.

Переделана работа со страницами и кортежами для улучшения стабильности.

Стандартизировано API записи во всех компонентах QDL.

Исправлено

Ряд системных функций, работа которых приводила к записи некорректных размеров кортежей в мета-информацию страницы.

Утилиты

qhb_archivecleanup: обновлены зависимости и добавлена русская локализация.

qhb_checksum: добавлен режим параллельной работы и нормальный вывод прогресс-бара с флагом --verbose.

qhb_checksum: теперь корректно работает с файлами больших размеров.

Реализована утилита createdb.

Реализована утилита dropdb.

Метрики

Подсистема сбора и предоставления для внешнего отображения системных метрик. Предоставление пока только в grafana.

Метрики добавлены в буфер-менеджер TARQ.

grafana больше не инициализирует время выполнения под каждый вызов with_grafana.

Перещелкивание «часов» буфера назад при переводе системных часов.

Прочие улучшения

Оптимизирован буфер-менеджер TARQ.

Из релизной сборки удалены debug-символы.

Исправлены параметры сборки, приводившие к снижению производительности.

Реализация чекпоинтера на Rust.

Убраны многие раздражающие ограничения и устранены критические ошибки.