psql_bfile. Составной тип bfile для доступа к внешнему файлу
Версия: 1.0.
В исходном дистрибутиве установлено по умолчанию: да.
Связанные компоненты: отсутствуют.
Функциональность доступна только для редакций Enterprise и Enterprise для ERP-систем.
Для облегчения миграции с СУБД Oracle, добавляются таблицы, типы данных и набор хранимых процедур, позволяющие получить доступ к содержимому файлов из внешнего хранилища по аналогии с функциональностью Oracle BFILEs.
Добавленные объекты позволяют: сохранить URL к контейнерам файлов (директорий файловой системы, корзины S3, базовый путь к файлам, получаемый по HTTP и т.п.) и параметры подключения к ним, сохранить местоположение файлов в данных контейнерах в произвольных таблицах БД, получить содержимое этих файлов.
psql_bfile располагается в папке contrib.
Для указания местоположения файла во внешнем хранилище добавляется тип данных bfile. В нем содержится наименование псевдонима каталога и наименование файла в этом каталоге. Длина псевдонима и длина наименования файла ограничены 255 символами.
Для него добавляются операторы преобразования:
text -> bfile– преобразует строку форматаdir_alias:file_nameв значение типаbfile;bfile -> text– преобразует значение типаbfileв строку форматаdir_alias:file_name.
Для хранения URL к внешнему хранилищу добавляется таблица bfile_directory. Длина псевдонима ограничена 255 символами, длина пути к каталогу ограничены 4096 символами:
| Тип столбца | Описание |
|---|---|
dir_alias text | Псевдоним каталога |
dir_url text | Путь к директории на хосте БД или URL каталога |
dir_options text[] | Дополнительные параметры, необходимые для обращения к контейнеру по его URL, в виде строк ключ=значение |
Для ограничения доступа к файловой системе хоста БД добавляются конфигурационные параметры (в /etc/pangolin-manager/postgres.yml (/pd_data/06/data/postgresql.conf), секция postgresql|parameters):
| Параметр | Описание |
|---|---|
psql_bfile.allowed_file_directories (string) | Содержит список директорий, на которые (на них самих или вложенных в них) можно ссылаться при добавлении записи в таблицу bfile_directory. По умолчанию содержит пустую строку |
psql_bfile.read_file_timeout | Содержит интервал в миллисекундах, в течении которого ожидается доступность файлового дескриптора для чтения в соответствующих методах |
Функции
Функции управления данными в таблице каталогов
| Функция | Описание |
|---|---|
bfile_directory_create ( dir_alias text, dir_url text [, dir_options text[]]) -> void | Создает каталог для заданных псевдонима и пути |
bfile_directory_read ( dir_alias text ) → record ( dir_alias text, dir_url text, dir_options text ) | Возвращает данные для указанного псевдонима |
bfile_directory_update ( dir_alias text, dir_url text [, dir_options text[]]) -> void | Заменяет данные указанного псевдонима. Длина пути каталогу ограничена 4096 символами |
bfile_directory_delete ( dir_alias text ) -> void | Удаляет запись с данными для указанного псевдонима |
bfile_directory_rename ( dir_alias text, new_dir_alias text ) -> void | Изменяет псевдоним каталога |
bfile_directory_getoptions( dir_alias text ) -> record( dir_options text[] ) | Возвращает значение опций, используемых для соединения с контейнером, связанных с указанным псевдонимом |
bfile_directory_setoptions( dir_alias text, dir_options text[] ) -> void | Заменяет значение опций, используемых для соединения с контейнером, связанных с указанным псевдонимом |
bfile_directory_geturl( dir_alias text ) -> record( dir_path text ) | Возвращает значение URL контейнера, связанного с указанным псевдонимом |
bfile_directory_seturl ( dir_alias text, dir_path text ) -> void | Заменяет значение URL контейнера, связанного с указанным псевдонимом |
Функции предоставления и отзыва прав доступа к каталогам
| Функция | Описание |
|---|---|
bfile_grant_directory ( role_name name, dir_alias text, priv_type text ) -> void | Предоставляет права доступа priv_type к каталогу dir_alias пользователю/роли role_name. Права, предоставленные групповой роли, наследуются всеми ее членами и их наследниками. Если указать псевдокаталог '*', пользователю будут предоставлены права на все каталоги в целом |
bfile_revoke_directory ( role_name name, dir_alias text, priv_type text ) -> void | Отзывает права доступа к каталогу dir_alias у пользователя/роли role_name. Если указать псевдокаталог '*', то у пользователя будут отозваны права на все каталоги в целом. Имеется ограничение: нет возможности отозвать право на отдельный каталог, если это право было выделено на все каталоги сразу |
bfile_has_directory_privilege ( role_name name, dir_alias text [, priv_type text]) -> bool | Проверяет доступ пользователя к каталогу. По умолчанию priv_text равен SELECT. Если указать псевдокаталог '*', то будут проверен доступ на все каталоги в целом |
bfile_cleanup_directory_roles ( ) -> void | Удаляет из таблицы bfile_directory_roles информацию о правах доступа к каталогам для удаленных пользователей/ролей |
Функции проверки на вменяемость локатора
| Функция | Описание |
|---|---|
bfilename ( dir_alias text, filename text ) -> bfile | Создает локатор файла с указанными псевдонимом каталог и именем файла |
bfile_fileexists ( file_loc bfile ) -> bool | Проверяет наличие файла во внешнем хранилище по его локатору |
bfile_filegetname ( file_loc bfile ) -> record ( dir_alias text, filename text ) | Возвращает псевдоним контейнера и имя файла по его локатору |
Функции для работы с файлами, связанными с локаторами
| Функция | Описание |
|---|---|
bfile_fileopen ( file_loc bfile ) -> int | Открывает файл для чтения по его локатору и возвращает его дескриптор. Открытый дескриптор существует в рамках сеанса (процесса), в котором был открыт, и не доступен из других сеансов (процессов) |
bfile_fileisopen ( file_handle int ) -> bool | Проверяет валидность дескриптора и его состояние, открыт для чтения или нет |
bfile_fileclose ( file_handle int ) -> void | Закрывает файл по его дескриптору |
bfile_filecloseall ( ) -> void | Закрывает все открытые в данном сеансе (процессе) файлы |
bfile_fileread ( file_handle int [, amount bigint [, offset bigint]]) -> record ( amount bigint, buffer bytea ) | Считывает amount байт со смещением offset из открытого файла с дескриптором handler и возвращает пару (количество прочитанных байт, прочитанные байты). По умолчанию параметр amount равен -1, а параметр offset равен 0. Значение amount не может быть больше 256 Мбайт. Если amount < 0, то будет прочитано до конца файла возможно большее количество байт (но не больше 256Мбайт) Если offset < 0, то смещение берется от конца файла |
bfile_filecompare ( file_handle1 int, file_handle2 int, amount bigint [, offset1 bigint [, offset2 bigint]]) -> int | Сравнивает содержимое двух открытых файлов. Количество сравниваемых байт задается в amount. В offset1, offset2 смещение соответственно в первом файле и во втором файле. По умолчанию offset1 = 0, и offset2 = 0. Значение amount не может быть больше 256 Мбайт. Если offset1 < 0 или offset2 < 0, то смещение берется от конца соответствующего файла. Возвращает - 0, если содержимое двух файлов равны, отрицательное значение, если содержимое первого файла стоит перед содержимым второго файла в лексикографическом порядке, положительное значение, если наоборот |
Функции для работы с содержимым файла непосредственно по его локатору
| Функция | Описание |
|---|---|
bfile_getlength ( file_loc bfile ) -> bigint | Возвращает длину файла для заданного значения file_loc |
bfile_read ( file_loc bfile [, amount bigint [, offset_ bigint]]) -> record ( amount bigint, buffer bytea ) | Считывает amount байт со смещением offseе из файла по его локатору file_loc и возвращает пару (количество прочитанных байт, прочитанные байты). По умолчанию параметр amount равен -1, а параметр offset равен 0. Значение amount не может быть больше 256 Мбайт. Если amount < 0, то будет прочитано до конца файла возможно большее количество байт (но не больше 256Мбайт). Если offset < 0 то смещение берется от конца файла |
bfile_compare ( file_loc1 bfile, file_loc2 bfile, amount bigint [, offset1 bigint [, offset2 bigint]]) -> int | Сравнивает содержимое двух файлов по их локатору. Количество сравниваемых байт задается в amount. В offset1, offset2 смещение соответственно в первом файле и во втором файле. По умолчанию offset1 = 0, и offset2 = 0. Значение amount не может быть больше 256 Мбайт. Если offset1 < 0 или offset2 < 0, то смещение берется от конца соответствующего файла. Возвращает - 0, если содержимое двух файлов равны, отрицательное значение, если содержимое первого файла стоит перед содержимым второго файла в лексикографическом порядке, положительное значение, если наоборот |
Представления
Получение списка всех каталогов
Для получения списка всех каталогов реализовано представление bfile_dba_directories:
| Тип столбца | Описание |
|---|---|
dir_alias text | Псевдоним каталога |
dir_url text | Путь к директории на хосте БД или URL каталога |
dir_options text[] | Дополнительные параметры, необходимые для обращения к контейнеру по его URL, в виде строк ключ=значение |
Получение списка доступных текущему пользователю каталогов
Для получения списка доступных текущему пользователю каталогов используется представление bfile_all_directories :
| Тип столбца | Описание |
|---|---|
dir_alias text | Псевдоним каталога |
dir_options text[] | Дополнительные параметры, необходимые для обращения к контейнеру по его URL, в виде строк ключ=значение |
dir_url text | Путь к директории на хосте БД или URL каталога |
Получение списка прав доступа к каталогам
Для хранения предоставленных прав доступа к каталогам добавлена таблица bfile_directory_rights:
| Тип столбца | Описание |
|---|---|
dir_alias text | Псевдоним каталога |
dir_acl aclitem[] | Список предоставленных прав доступа к каталогу |
Для всей таблицы можно предоставить права: INSERT, SELECT, UPDATE, DELETE. Для отдельной записи можно предоставить права: SELECT, UPDATE. Пользователю, создавшему запись (успешное выполнение функции bfile_directory_create), предоставляются права: UPDATE, SELECT.
Таблица соответствия API Oracle BFILE и API Pangolin psql_bfile
| Oracle BFILE | Pangolin psql_bfile | ||
|---|---|---|---|
| DIRECTORY Objects | |||
CREATE OR REPLACE DIRECTORY bfile_dir AS '/usr/home/scott'; | SELECT bfile_directory_create('bfile_dir', '/usr/home/bfile_dir'); SELECT bfile_directory_update('bfile_dir ', '/usr/home/bfile_dir'); | ||
DROP DIRECTORY bfile_dir | SELECT bfile_directory_delete('bfile_dir '); | ||
GRANT CREATE ANY DIRECTORY TO user_name | SELECT bfile_grant_directory('user_name', '*', 'INSERT,UPDATE'); | ||
GRANT DROP ANY DIRECTORY TO user_name | SELECT bfile_grant_directory('user_name', '*', 'DELETE'); | ||
REVOKE CREATE ANY DIRECTORY FROM user_name | SELECT bfile_revoke_directory('user_name', '*', 'INSERT,UPDATE'); | ||
REVOKE DROP ANY DIRECTORY FROM user_name | SELECT bfile_revoke_directory('user_name', '*', 'DELETE'); | ||
GRANT READ ON DIRECTORY bfile_dir TO user_name | SELECT bfile_grant_directory('user_name', 'bfile_dir', 'SELECT'); | ||
REVOKE READ ON DIRECTORY bfile_dir FROM user_name | SELECT bfile_revoke_directory('user_name', 'bfile_dir', 'SELECT'); | ||
SELECT * FROM ALL_DIRECTORIES(OWNER,DIRECTORY_NAM,DIRECTORY_PATH) | SELECT * FROM bfile_all_directories; | ||
SELECT * FROM DBA_DIRECTORIES(OWNER,DIRECTORY_NAM,DIRECTORY_PATH) | SELECT * FROM bfile_dba_directories; | ||
| BFILE Locators | |||
BFILE type | bfile type | ||
| BFILE APIs | |||
| Sanity Checking | |||
DBMS_LOB.FILEEXISTS ( file_loc IN BFILE) RETURN INTEGER; | bfile_fileexists ( file_loc bfile ) -> bool | ||
DBMS_LOB.FILEGETNAME ( file_loc IN BFILE, dir_alias OUT VARCHAR2, filename OUT VARCHAR2); | bfile_filegetname ( file_loc bfile ) -> record ( dir_alias text, filename text ) | ||
BFILENAME('directory','filename') | bfilename ( dir_alias text, filename text ) -> bfile | ||
| Open / Close | |||
DBMS_LOB.OPEN ( file_loc IN OUT NOCOPY BFILE, open_mode IN BINARY_INTEGER := file_readonly); DBMS_LOB.FILEOPEN ( file_loc IN OUT NOCOPY BFILE, open_mode IN BINARY_INTEGER := file_readonly); | bfile_fileopen ( file_loc bfile ) -> int | ||
DBMS_LOB.ISOPEN ( file_loc IN BFILE) RETURN INTEGER; DBMS_LOB.FILEISOPEN ( file_loc IN BFILE) RETURN INTEGER; | bfile_fileisopen ( file_handle int ) -> bool | ||
DBMS_LOB.CLOSE ( file_loc IN OUT NOCOPY BFILE); DBMS_LOB.FILECLOSE ( file_loc IN OUT NOCOPY BFILE); | bfile_fileclose ( file_handle int ) -> void | ||
DBMS_LOB.FILECLOSEALL; | bfile_filecloseall ( ) -> void | ||
| Read Operations | |||
DBMS_LOB.GETLENGTH ( file_loc IN BFILE) RETURN INTEGER; | bfile_getlength ( file_loc bfile ) -> bigint | ||
DBMS_LOB.READ ( file_loc IN BFILE, amount IN OUT NOCOPY INTEGER, offset IN INTEGER, buffer OUT RAW); | bfile_fileread ( file_handle int [, amount bigint [, offset bigint]]) -> record ( amount bigint, buffer bytea ) bfile_read ( file_loc bfile [, amount bigint [, offset_ bigint]]) -> record ( amount bigint, buffer bytea ) | ||
DBMS_LOB.SUBSTR ( file_loc IN BFILE, amount IN INTEGER := 32767, offset IN INTEGER := 1) RETURN RAW; | – | ||
DBMS_LOB.INSTR ( file_loc IN BFILE, pattern IN RAW, offset IN INTEGER := 1, nth IN INTEGER := 1) RETURN INTEGER; | – | ||
| Operations involving multiple locators | |||
BFILE := BFILE | – | ||
DBMS_LOB.LOADCLOBFROMFILE, DBMS_LOB.LOADBLOBFROMFILE | – | ||
DBMS_LOB.COMPARE ( lob_1 IN BFILE, lob_2 IN BFILE, amount IN INTEGER, offset_1 IN INTEGER := 1, offset_2 IN INTEGER := 1) RETURN INTEGER; | bfile_filecompare ( file_handle1 int, file_handle2 int, amount bigint [, offset1 bigint [, offset2 bigint]]) -> int bfile_compare ( file_loc1 bfile, file_loc2 bfile, amount bigint [, offset1 bigint [, offset2 bigint]]) -> int |
Ограничения
В psql_bfile версии 1.0 можно ссылаться только на файлы хоста экземпляра БД.
Пример использования
-
Создайте каталог для хранения данных
bfile:mkdir "/tmp/bfiles" -
Создайте файл с данными:
echo -n '123456789012345678901234567890' /tmp/bfiles/file1 -
Запустите скрипт:
-- Создание расширения psql_bfile:
CREATE EXTENSION psql_bfile;
-- Добавление допустимых к работе папок
ALTER SYSTEM SET psql_bfile.allowed_file_directories='/tmp';
-- Применение изменений в конфигурации
SELECT pg_reload_conf();
-- Создание каталога в базе данных для хранения bfile:
SELECT bfile_directory_create('BFILES', '/tmp/bfiles');
-- Создание таблицы bfile и добавление в нее одной записи, ссылающейся на этот файл:
CREATE TABLE bfile_table(id int, bf bfile);
INSERT INTO bfile_table VALUES (1, bfilename('BFILES', 'file1'));
-- Создание пользователя:
CREATE USER user1;
-- Предоставление прав user1 на схему public
GRANT ALL PRIVILEGES ON SCHEMA public TO user1;
-- Предоставление пользователю user1 прав на таблицу bfile_table:
GRANT ALL ON bfile_table TO user1;
-- Предоставление пользователю user1 права чтения файлов в BFILES:
SELECT bfile_grant_directory('user1', 'BFILES', 'SELECT');
-- Авторизация user1:
SET ROLE user1;
DO $$
DECLARE
v_bfile bfile;
v_buffer bytea;
v_length bigint;
v_handler int;
BEGIN
-- Открытие файла для чтения и записи:
SELECT bfile_fileopen(bf) INTO v_handler FROM bfile_table WHERE id = 1;
-- Считывание строки символов из файла в буфер и вывод ее длины и содержимого:
SELECT a_buffer FROM bfile_fileread(v_handler) INTO v_buffer;
RAISE NOTICE 'Buffer length: %', length(v_buffer);
RAISE NOTICE 'Buffer content: %', encode(v_buffer, 'escape');
-- Закрытие bfile:
PERFORM bfile_fileclose(v_handler);
END $$;
-- Проверка существования файла:
SELECT bfile_fileexists('BFILES:file1');
-- Получение и вывод длины файла:
SELECT bfile_getlength('BFILES:file1');
-- Проверка содержимого таблицы:
SELECT a_amount, encode(a_buffer, 'escape') FROM
(SELECT f.* FROM bfile_table, LATERAL bfile_read(bf) AS f) x;
-- Удаление таблицы, пользователя и расширения psql_bfile:
RESET ROLE;
DROP TABLE bfile_table;
REVOKE ALL PRIVILEGES ON SCHEMA public FROM user1;
DROP USER user1;
DROP EXTENSION psql_bfile; -
Скрипт выдает следующий результат:
CREATE EXTENSION
ALTER SYSTEM
pg_reload_conf
----------------
t
(1 row)
bfile_directory_create
------------------------
(1 row)
CREATE TABLE
INSERT 0 1
CREATE ROLE
GRANT
GRANT
bfile_grant_directory
-----------------------
(1 row)
SET
psql:example.sql:49: NOTICE: Buffer length: 30
psql:example.sql:49: NOTICE: Buffer content: 123456789012345678901234567890
DO
bfile_fileexists
------------------
t
(1 row)
bfile_getlength
-----------------
30
(1 row)
a_amount | encode
----------+--------------------------------
30 | 123456789012345678901234567890
(1 row)
RESET
DROP TABLE
REVOKE
DROP ROLE
DROP EXTENSION