Несовместимости SQL между Greengage DB 6 и 7
- Партиционирование: параметры оптимизированных для добавления таблиц
- Партиционирование на основе списков по нескольким столбцам
- Партиционирование: ключи для ограничений уникальности
- Партиционирование: модификаторы EXCLUSIVE/INCLUSIVE для числовых и текстовых диапазонов
- Удаленные типы данных
- Удаленные расширения
- Прекращение поддержки Python 2
- Возвращаемое значение функций EXECUTE ON
- Триггеры на уровне операторов
- Пользовательские объекты, зависящие от удаленных объектов системного каталога
- Ресурсные группы
Эта статья описывает несовместимости на уровне SQL между Greengage DB 6 и 7 с примерами, показывающими, как выявить и устранить каждую несовместимость перед миграцией данных.
Партиционирование: параметры оптимизированных для добавления таблиц
Сценарий
Партиционированные таблицы в Greengage DB позволяют указывать разные типы хранения для отдельных партиций.
Партиции, оптимизированные для добавления (AO), также могут задавать дополнительные параметры хранения, такие как compresstype и compresslevel.
В Greengage DB 6, если для партиции не указан AO-параметр, применяется значение по умолчанию.
В Greengage DB 7, если AO-параметр не указан явно, он наследуется от родительской таблицы. Это изменение может повлиять на зависимые параметры и привести к неподдерживаемым конфигурациям хранения.
Следующий пример определяет партиционированную таблицу, где для родительской таблицы указан compresstype = zstd; для дочерней партиции compresstype не указан, но задан compresslevel = 0:
CREATE TABLE sales
(
id INT,
date DATE,
region TEXT,
amount DECIMAL(10, 2)
)
WITH (appendoptimized = true, compresstype = zstd, compresslevel = 1)
DISTRIBUTED BY (id)
PARTITION BY LIST (region)
(
PARTITION asia VALUES ('Asia')
WITH (appendoptimized = true, compresslevel = 0),
PARTITION europe VALUES ('Europe')
);
INSERT INTO sales(id, date, region, amount)
VALUES (1, '2024-01-10', 'Asia', 100.00),
(2, '2024-01-11', 'Europe', 200.00),
(3, '2024-01-12', 'Asia', 150.00),
(4, '2024-01-13', 'Europe', 300.00),
(5, '2024-01-14', 'Asia', 250.00);
В Greengage DB 6 партиция asia использует compresstype = none по умолчанию.
В Greengage DB 7 партиция asia унаследует compresstype = zstd от родительской таблицы.
В результате при создании таблицы может возникнуть следующая ошибка:
ERROR: compresstype "zstd" can't be used with compresslevel 0
Как выявить
Чтобы найти партиционированные таблицы, у которых дочерние партиции не задают явно параметры хранения, определенные в родительской таблице, выполните следующий запрос:
SELECT schemaname,
tablename,
partitionschemaname,
partitiontablename,
par.reloptions AS parent_options,
c.reloptions AS child_options,
po AS parent_option
FROM pg_catalog.pg_partitions p
JOIN pg_catalog.pg_class par ON par.oid = format('%s.%s', schemaname, tablename)::regclass
JOIN pg_catalog.pg_class c ON c.oid = format('%s.%s', partitionschemaname, partitiontablename)::regclass
JOIN UNNEST(par.reloptions) po ON TRUE
LEFT JOIN UNNEST(c.reloptions) co ON split_part(po, '=', 1) = split_part(co, '=', 1)
WHERE co IS NULL;
Запрос возвращает параметры родительской партиционированной таблицы, которые не определены для соответствующих дочерних партиций. Пример вывода:
-[ RECORD 1 ]-------+----------------------------------------------------
schemaname | public
tablename | sales
partitionschemaname | public
partitiontablename | sales_1_prt_asia
parent_options | {appendonly=true,compresstype=zstd,compresslevel=1}
child_options | {appendonly=true,compresslevel=0}
parent_option | compresstype=zstd
Пример вывода отображается в режиме расширенного форматирования, где каждый столбец выводится на отдельной строке.
Этот режим можно включить в psql с помощью метакоманды \x.
Как исправить
Создайте копию затронутой партиции с явно заданным compresstype = none:
CREATE TABLE sales_asia_copy
(
LIKE sales_1_prt_asia INCLUDING ALL
)
WITH (compresstype = none);
Скопируйте данные из исходной партиции:
INSERT INTO sales_asia_copy
SELECT *
FROM sales_1_prt_asia;
Замените исходную партицию обновленной копией:
ALTER TABLE sales
EXCHANGE PARTITION asia
WITH TABLE sales_asia_copy
WITH VALIDATION;
Удалите временную таблицу:
DROP TABLE sales_asia_copy;
Партиционирование на основе списков по нескольким столбцам
Сценарий
Начиная с Greengage DB 7 партиционирование на основе списков по нескольким столбцам больше не поддерживается, что приводит к следующей ошибке:
ERROR: cannot use "list" partition strategy with more than one column
В следующем примере используется ключ партиционирования на основе столбцов region и channel:
CREATE TABLE sales
(
id INT,
region TEXT,
channel TEXT,
amount DECIMAL(10, 2)
)
DISTRIBUTED BY (id)
PARTITION BY LIST (region, channel)
(
VALUES (('Asia', 'Online'), ('Asia', 'Retail')),
VALUES (('Europe', 'Online'), ('Europe', 'Retail'))
);
INSERT INTO sales (id, region, channel, amount)
VALUES (1, 'Asia', 'Online', 100.00),
(2, 'Asia', 'Retail', 120.00),
(3, 'Europe', 'Online', 180.00),
(4, 'Europe', 'Retail', 200.00),
(5, 'Asia', 'Online', 150.00);
Как выявить
Чтобы найти таблицы, использующие стратегию партиционирования LIST с более чем одним столбцом партиционирования, используйте следующий запрос:
SELECT n.nspname,
c.relname
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
JOIN pg_catalog.pg_partition p ON p.parrelid = c.oid
WHERE parkind = 'l'
AND parnatts > 1;
Пример вывода:
nspname | relname ---------+--------- public | sales (1 row)
Как исправить
Пересоздайте партиционированную таблицу с использованием структуры, поддерживаемой в Greengage DB 7.
В следующем примере в качестве ключа партиционирования используется один столбец-массив (region_channel):
CREATE TABLE sales
(
id INT,
region_channel TEXT[],
amount DECIMAL(10, 2)
)
DISTRIBUTED BY (id)
PARTITION BY LIST (region_channel)
(
VALUES ('{Asia,Online}', '{Asia,Retail}'),
VALUES ('{Europe,Online}', '{Europe,Retail}')
);
Вставьте данные, используя формат массива:
INSERT INTO sales (id, region_channel, amount)
VALUES (1, '{Asia,Online}', 100.00),
(2, '{Asia,Retail}', 120.00),
(3, '{Europe,Online}', 180.00),
(4, '{Europe,Retail}', 200.00),
(5, '{Asia,Online}', 150.00);
Партиционирование: ключи для ограничений уникальности
Сценарий
В Greengage DB 7 уникальный индекс для партиционированной таблицы должен включать все столбцы партиционирования. Если столбец партиционирования не включен в уникальный индекс, возникает следующая ошибка:
ERROR: unique constraint on partitioned table must include all partitioning keys
В следующей таблице столбец id используется как ключ распределения, а столбец region — как ключ партиционирования:
CREATE TABLE sales
(
id INT NOT NULL,
region TEXT,
amount DECIMAL(10, 2)
)
DISTRIBUTED BY (id)
PARTITION BY LIST (region)
(
VALUES ('Asia'),
VALUES ('Europe')
);
INSERT INTO sales (id, region, amount)
VALUES (1, 'Asia', 100.00),
(1, 'Europe', 150.00),
(2, 'Asia', 200.00);
Команда ниже создает уникальный индекс только по столбцу id, без учета ключа партиции.
В Greengage DB 6 она выполняется успешно, а в Greengage DB 7 приводит к ошибке:
CREATE UNIQUE INDEX sales_id_idx ON sales (id);
Как выявить
Чтобы найти уникальные индексы на партиционированных таблицах, которые не включают все столбцы партиционирования, выполните следующий запрос:
WITH partitions AS (SELECT DISTINCT n.nspname,
c.relname,
c.oid,
p.paratts
FROM pg_catalog.pg_partition p
JOIN pg_catalog.pg_class c ON (p.parrelid = c.oid)
JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace))
SELECT p.nspname,
p.relname,
ir.relname AS indexname
FROM pg_catalog.pg_index i
JOIN partitions p ON p.oid = i.indrelid
JOIN pg_catalog.pg_class ir ON ir.oid = i.indexrelid
WHERE (i.indisunique OR i.indisprimary)
AND NOT (p.paratts <@ i.indkey);
Пример вывода:
nspname | relname | indexname ---------+---------+-------------- public | sales | sales_id_idx (1 row)
Как исправить
Удалите индекс, который не включает все столбцы партиционирования:
DROP INDEX sales_id_idx;
Создайте новый индекс, включающий все столбцы партиционирования:
CREATE UNIQUE INDEX sales_id_region_idx ON sales (id, region);
Партиционирование: модификаторы EXCLUSIVE/INCLUSIVE для числовых и текстовых диапазонов
Сценарий
В Greengage DB 7 партиции на основе диапазонов не поддерживают START EXCLUSIVE и END INCLUSIVE для столбцов текстового и числовых типов (например, float4, float8, numeric).
Может возникнуть следующая ошибка:
ERROR: START EXCLUSIVE not supported for partition key data type: numeric HINT: Specify an inclusive START value and remove the EXCLUSIVE keyword
или:
ERROR: END INCLUSIVE not supported for partition key data type: numeric HINT: Specify an exclusive END value and remove the INCLUSIVE keyword
Следующий пример создает таблицу, где нижняя граница диапазона для столбца amount типа decimal задана как EXCLUSIVE:
CREATE TABLE sales
(
id INT,
date DATE,
amount DECIMAL(10, 2)
)
WITH (appendoptimized = true)
DISTRIBUTED BY (id)
PARTITION BY RANGE (amount)
(
START (0) EXCLUSIVE
END (1000)
EVERY (250)
);
Как выявить
Чтобы найти таблицы, использующие стратегию партиционирования RANGE с числовыми или текстовыми ключами партиционирования, выполните следующий запрос:
SELECT n.nspname,
c.relname,
t.typname,
pr.parchildrelid::regclass
FROM pg_catalog.pg_partition p
JOIN pg_catalog.pg_partition_rule pr ON p.oid = pr.paroid
JOIN pg_catalog.pg_class c ON (p.parrelid = c.oid)
JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid AND a.attnum = ANY (p.paratts)
JOIN pg_catalog.pg_type t ON t.oid = a.atttypid
WHERE t.typname IN ('text', 'float8', 'float4', 'numeric')
AND (NOT pr.parrangestartincl OR pr.parrangeendincl);
Пример вывода:
nspname | relname | typname | parchildrelid ---------+---------+---------+--------------- public | sales | numeric | sales_1_prt_1 (1 row)
Как исправить
Создайте таблицу заново, используя поддерживаемый тип ключа партиционирования или без границ EXCLUSIVE:
CREATE TABLE sales
(
id INT,
date DATE,
amount DECIMAL(10, 2)
)
WITH (appendoptimized = true)
DISTRIBUTED BY (id)
PARTITION BY RANGE (amount)
(
START (0)
END (1000)
EVERY (250)
);
Удаленные типы данных
Сценарий
Следующие типы данных были удалены в Greengage DB 7:
-
unknown -
reltime -
tinterval -
abstime
Использование этих типов в Greengage DB 7 приводит к следующим ошибкам:
ERROR: type "unknown" does not exist ERROR: type "reltime" does not exist ERROR: type "tinterval" does not exist ERROR: type "abstime" does not exist
Следующий пример создает таблицу со столбцами этих типов:
CREATE TABLE user_activity_log
(
event_id INT,
user_note unknown,
session_length reltime,
active_window tinterval,
created_at abstime
);
INSERT INTO user_activity_log
VALUES (1, 'login from mobile', '45 minutes',
tinterval('2024-01-10 08:00:00'::abstime, '2024-01-10 08:45:00'::abstime),
'2024-01-10 08:45:00'),
(2, 'password change', '5 minutes',
tinterval('2024-01-10 09:10:00'::abstime, '2024-01-10 09:15:00'::abstime),
'2024-01-10 09:15:00'),
(3, 'checkout completed', '12 minutes',
tinterval('2024-01-10 10:00:00'::abstime, '2024-01-10 10:12:00'::abstime),
'2024-01-10 10:12:00');
Как выявить
Создайте вспомогательную схему:
CREATE SCHEMA __ggmigrate_tmp;
Создайте функцию, которая рекурсивно находит все объекты, зависящие от заданных типов:
CREATE FUNCTION __ggmigrate_tmp.data_type_checks(base_oids REGTYPE[])
RETURNS TABLE
(
nspname NAME,
relname NAME,
attname NAME
)
AS
$$
DECLARE
result_oids REGTYPE[];
dependent_oids REGTYPE[];
BEGIN
dependent_oids = base_oids;
result_oids = base_oids;
WHILE array_length(dependent_oids, 1) IS NOT NULL
LOOP
dependent_oids := ARRAY(
SELECT t.oid
FROM (SELECT t.oid
FROM pg_catalog.pg_type t,
unnest(dependent_oids) AS x(oid)
WHERE typbasetype = x.oid
AND typtype = 'd'
UNION ALL
SELECT t.oid
FROM pg_catalog.pg_type t,
unnest(dependent_oids) AS x(oid)
WHERE typelem = x.oid
AND typtype = 'b'
UNION ALL
SELECT t.oid
FROM pg_catalog.pg_type t
JOIN pg_catalog.pg_class c ON t.oid = c.reltype
JOIN pg_catalog.pg_attribute a ON c.oid = a.attrelid
WHERE t.typtype = 'c'
AND NOT a.attisdropped
AND a.atttypid = ANY (dependent_oids)
UNION ALL
SELECT t.oid
FROM pg_catalog.pg_type t,
pg_catalog.pg_range r,
unnest(dependent_oids) AS x(oid)
WHERE t.typtype = 'r'
AND r.rngtypid = t.oid
AND r.rngsubtype = x.oid) AS t
);
result_oids := result_oids || dependent_oids;
END LOOP;
RETURN QUERY
SELECT n.nspname, c.relname, a.attname
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
JOIN pg_catalog.pg_attribute a ON c.oid = a.attrelid
WHERE NOT a.attisdropped
AND a.atttypid = ANY (result_oids)
AND c.relkind IN ('r', 'm', 'i')
AND n.nspname !~ '^pg_temp_'
AND n.nspname !~ '^pg_toast_temp_'
AND n.nspname NOT IN ('pg_catalog', 'information_schema');
END;
$$ LANGUAGE plpgsql;
Найдите все таблицы со столбцами, использующими удаленные типы данных:
SELECT *
FROM __ggmigrate_tmp.data_type_checks(
ARRAY [
'unknown'::regtype,
'reltime'::regtype,
'tinterval'::regtype,
'abstime'::regtype
]
);
Пример вывода:
nspname | relname | attname ---------+-------------------+---------------- public | user_activity_log | user_note public | user_activity_log | session_length public | user_activity_log | active_window public | user_activity_log | created_at (4 rows)
Как исправить
Преобразуйте каждый устаревший столбец в поддерживаемый тип:
ALTER TABLE user_activity_log
ALTER COLUMN user_note TYPE text;
ALTER TABLE user_activity_log
ALTER COLUMN session_length TYPE interval;
ALTER TABLE user_activity_log
ALTER COLUMN active_window TYPE text;
ALTER TABLE user_activity_log
ALTER COLUMN created_at TYPE timestamp;
Удаленные расширения
Сценарий
Следующие расширения были удалены в Greengage DB 7. Их функциональность теперь встроена в ядро:
-
gp_parallel_retrieve_cursor -
gp_array_agg -
gp_percentile_agg
Попытка установить их в Greengage DB 7 приводит к следующим ошибкам:
ERROR: could not open extension control file "/usr/local/gpdb/share/postgresql/extension/gp_parallel_retrieve_cursor.control": No such file or directory ERROR: could not open extension control file "/usr/local/gpdb/share/postgresql/extension/gp_array_agg.control": No such file or directory ERROR: could not open extension control file "/usr/local/gpdb/share/postgresql/extension/gp_percentile_agg.control": No such file or directory
В следующем примере эти расширения устанавливаются в Greengage DB 6, где они по-прежнему доступны:
CREATE EXTENSION gp_parallel_retrieve_cursor;
CREATE EXTENSION gp_array_agg;
CREATE EXTENSION gp_percentile_agg;
Как выявить
Проверьте, какие из удаленных расширений установлены:
SELECT extname,
extversion
FROM pg_catalog.pg_extension
WHERE extname IN ('gp_parallel_retrieve_cursor', 'gp_array_agg', 'gp_percentile_agg');
Пример вывода:
extname | extversion -----------------------------+------------ gp_parallel_retrieve_cursor | 1.0 gp_array_agg | 1.0.0 gp_percentile_agg | 1.0.0 (3 rows)
Как исправить
Удалите расширения перед миграцией:
DROP EXTENSION IF EXISTS gp_parallel_retrieve_cursor;
DROP EXTENSION IF EXISTS gp_array_agg;
DROP EXTENSION IF EXISTS gp_percentile_agg;
Прекращение поддержки Python 2
Сценарий
В Greengage DB 6 функции могли быть реализованы либо на Python 2 (plpython2u), либо на Python 3 (plpython3u).
Начиная с Greengage DB 7 для создания функций поддерживается только Python 3. Попытка использовать Python 2 приводит к следующей ошибке:
ERROR: could not open extension control file "/usr/local/gpdb/share/postgresql/extension/plpython2u.control": No such file or directory
В следующем примере регистрируется расширение plpython2u:
CREATE EXTENSION plpython2u;
Функция integer_division_demo() использует оператор /, который в Python 2 выполняет целочисленное деление, если оба операнда являются целыми числами:
CREATE OR REPLACE FUNCTION integer_division_demo(a int, b int)
RETURNS text
AS
$$
result = a / b
return str(result)
$$ LANGUAGE plpython2u;
В Python 3 / всегда возвращает число с плавающей точкой — вместо этого для целочисленного деления необходимо использовать //.
Вызовите функцию:
SELECT integer_division_demo(10, 3);
Пример вывода:
integer_division_demo ----------------------- 3 (1 row)
Как выявить
Чтобы найти функции plpython2u и просмотреть их определения, выполните следующий запрос:
SELECT n.nspname,
p.oid,
p.proname,
p.prosrc
FROM pg_catalog.pg_proc p
JOIN pg_namespace n ON n.oid = p.pronamespace
JOIN pg_language l ON l.oid = p.prolang
JOIN pg_pltemplate t ON t.tmplname = l.lanname
WHERE t.tmpllibrary = '$libdir/plpython2';
Пример вывода:
nspname | oid | proname | prosrc
---------+-------+------------------------+------------------------
public | 16461 | integer_division_demo | +
| | | result = a / b +
| | | return str(result)+
Получить определение функции можно с помощью pg_get_functiondef(func_oid).
Как исправить
Перед удалением расширения plpython2u убедитесь, что определения всех функций, реализованных на Python 2, были экспортированы и сохранены.
Удалите расширение plpython2u и все зависимые функции:
DROP EXTENSION plpython2u CASCADE;
Повторно подключитесь к базе данных, так как в рамках одной сессии Greengage DB все функции PL/Python должны использовать один и тот же вариант PL/Python (plpython2u или plpython3u):
\c demo gpadmin
Зарегистрируйте расширение plpython3u:
CREATE EXTENSION plpython3u;
Пересоздайте функцию на Python 3, заменив / на // для целочисленного деления:
CREATE OR REPLACE FUNCTION integer_division_demo(a int, b int)
RETURNS text
AS
$$
result = a // b
return str(result)
$$ LANGUAGE plpython3u;
Проверьте результат:
SELECT integer_division_demo(10, 3);
Пример вывода:
integer_division_demo ----------------------- 3 (1 row)
Возвращаемое значение функций EXECUTE ON
Сценарий
Начиная с Greengage DB 7 функции, определенные с атрибутом EXECUTE ON, должны возвращать SETOF.
Использование скалярного типа для возвращаемого значения приводит к следующей ошибке:
ERROR: EXECUTE ON ALL SEGMENTS is only supported for set-returning functions
Ниже приведен пример функции с EXECUTE ON ALL SEGMENTS, которая возвращает скалярное значение.
Это определение работает в Greengage DB 6, но не поддерживается в Greengage DB 7:
CREATE FUNCTION get_segment_ip()
RETURNS TEXT
AS
$$
BEGIN
RETURN inet_server_addr();
END;
$$
LANGUAGE plpgsql
NO SQL
EXECUTE ON ALL SEGMENTS;
В Greengage DB 6 вызов функции создает одну строку для каждого выполнения на сегменте:
SELECT get_segment_ip();
Пример вывода:
get_segment_ip ---------------- 192.168.1.40 192.168.1.30 192.168.1.40 192.168.1.30 (4 rows)
Как выявить
Чтобы найти пользовательские функции, задающие атрибут EXECUTE ON, но не возвращающие набор (SETOF), выполните следующий запрос:
SELECT n.nspname,
p.proname
FROM pg_catalog.pg_proc p
JOIN pg_catalog.pg_language l ON l.oid = p.prolang
JOIN pg_catalog.pg_type t ON t.oid = p.prorettype
JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
WHERE NOT proretset
AND proexeclocation IN ('s', 'm', 'i')
AND l.lanname NOT IN ('internal')
AND t.typname NOT IN ('record');
Пример вывода:
nspname | proname ---------+---------------- public | get_segment_ip (1 row)
Как исправить
Поскольку тип возвращаемого значения нельзя изменить напрямую, сначала удалите существующую функцию:
DROP FUNCTION get_segment_ip();
Затем пересоздайте ее с RETURNS SETOF и RETURN NEXT:
CREATE FUNCTION get_segment_ip()
RETURNS SETOF TEXT
AS
$$
BEGIN
RETURN NEXT inet_server_addr();
END;
$$
LANGUAGE plpgsql
NO SQL
EXECUTE ON ALL SEGMENTS;
Проверьте выполнение функции:
SELECT get_segment_ip();
Пример вывода:
get_segment_ip ---------------- 192.168.1.30 192.168.1.30 192.168.1.40 192.168.1.40 (4 rows)
Триггеры на уровне операторов
Для работы с триггерами на уровне операторов потребуется внешняя база данных PostgreSQL, которая будет выступать в роли стороннего сервера:
-
Используйте данный скрипт examples/foreign_tables/script_pg.sql для создания тестовой базы данных, таблиц и пользователя:
DROP DATABASE IF EXISTS customers; CREATE DATABASE customers; \c customers CREATE TABLE customers ( id INTEGER, name VARCHAR(50), email VARCHAR(100), address VARCHAR(255) ); INSERT INTO customers (id, name, email, address) VALUES (1,'John Doe','john.doe@example.com','123 Elm Street'), (2,'Jane Smith','jane.smith@example.com','456 Oak Street'), (3,'Bob Brown','bob.brown@example.com','789 Pine Street'), (4,'Rob Stuart','rob.stuart@example.com','119 Willow Street'); CREATE USER "user" WITH PASSWORD 'password'; GRANT ALL ON TABLE customers TO "user"; -
Выполните из Greengage DB скрипт для настройки доступа к внешнему экземпляру PostgreSQL:
\! PGPASSWORD=password psql -h postgres -U postgres -f examples/foreign_tables/script_pg.sql -
Установите расширение
postgres_fdw, которое позволяет обращаться к внешним таблицам:CREATE EXTENSION postgres_fdw; -
Создайте сторонний сервер, указывающий на экземпляр PostgreSQL:
CREATE SERVER postgres FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'postgres', port '5432', dbname 'customers'); -
Создайте сопоставление пользователя для аутентификации:
CREATE USER MAPPING FOR gpadmin SERVER postgres OPTIONS (user 'user', password 'password');
Сценарий
В Greengage DB 7 триггеры на уровне операторов не поддерживаются. Может возникнуть следующая ошибка:
ERROR: Triggers for statements are not yet supported
Создайте стороннюю таблицу, которая ссылается на таблицу PostgreSQL:
CREATE FOREIGN TABLE customers
(
id INTEGER,
name VARCHAR(50),
email VARCHAR(100),
address VARCHAR(255)
) SERVER postgres;
Создайте функцию триггера, выполняющую действие, например отправку уведомления:
CREATE OR REPLACE FUNCTION raise_notice_after_insert()
RETURNS TRIGGER
AS
$$
BEGIN
RAISE NOTICE 'Trigger fired';
RETURN NULL;
END
$$ LANGUAGE plpgsql;
Создайте триггер с FOR EACH STATEMENT для таблицы.
Этот тип триггера выполняется один раз для каждого SQL-запроса, независимо от того, сколько строк затронуто:
CREATE TRIGGER raise_notice_for_each_statement
AFTER INSERT
ON customers
FOR EACH STATEMENT
EXECUTE PROCEDURE raise_notice_after_insert();
Вставьте несколько строк. В Greengage DB 6 триггер уровня оператора срабатывает только один раз для всей вставки:
INSERT INTO customers (id, name, email, address)
VALUES (5, 'Alice Johnson', 'alice.johnson@example.com', '221 Maple Drive'),
(6, 'David Lee', 'david.lee@example.com', '333 Birch Lane'),
(7, 'Emily Wilson', 'emily.wilson@example.com', '444 Cedar Court');
Ожидаемый вывод:
NOTICE: Trigger fired INSERT 0 3
Как выявить
Для проверки существования триггеров на уровне операторов выполните запрос к системному каталогу:
SELECT n.nspname,
c.relname,
tgname
FROM pg_catalog.pg_trigger t
JOIN pg_catalog.pg_class c ON t.tgrelid = c.oid
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE tgtype & 1 = 0
AND NOT tgisinternal;
Пример вывода:
nspname | relname | tgname ---------+-----------+--------------------------------- public | customers | raise_notice_for_each_statement (1 row)
Как исправить
Удалите ранее добавленные строки:
DELETE
FROM customers
WHERE id IN (5, 6, 7);
Удалите существующий триггер уровня оператора:
DROP TRIGGER raise_notice_for_each_statement ON customers;
Пересоздайте триггер с уровнем FOR EACH ROW, чтобы он выполнялся для каждой затронутой строки:
CREATE TRIGGER raise_notice_for_each_row
AFTER INSERT
ON customers
FOR EACH ROW
EXECUTE PROCEDURE raise_notice_after_insert();
Повторно вставьте данные. Теперь триггер будет срабатывать отдельно для каждой добавленной строки:
INSERT INTO customers (id, name, email, address)
VALUES (5, 'Alice Johnson', 'alice.johnson@example.com', '221 Maple Drive'),
(6, 'David Lee', 'david.lee@example.com', '333 Birch Lane'),
(7, 'Emily Wilson', 'emily.wilson@example.com', '444 Cedar Court');
Ожидаемый вывод:
NOTICE: Trigger fired NOTICE: Trigger fired NOTICE: Trigger fired INSERT 0 3
Пользовательские объекты, зависящие от удаленных объектов системного каталога
Сценарий
В Greengage DB 7 удален или изменен ряд объектов системного каталога, включая таблицы, представления и отдельные столбцы. Ниже приведен список удаленных системных таблиц и представлений:
-
gp_toolkit.gp_size_of_partition_and_indexes_disk -
gp_toolkit.__gp_user_data_tables -
pg_catalog.pg_partition -
pg_catalog.pg_partition_columns -
pg_catalog.pg_partition_encoding -
pg_catalog.pg_partition_rule -
pg_catalog.pg_partitions -
pg_catalog.pg_partition_templates -
pg_catalog.pg_stat_partition_operations
Любые пользовательские объекты (включая представления и функции), ссылающиеся на эти объекты, перестанут работать после миграции. При восстановлении логического бэкапа создание представлений со ссылками на отсутствующие объекты завершится с ошибкой.
Например, следующее представление ссылается на удаленное представление pg_partitions:
CREATE VIEW table_partitions AS
SELECT schemaname,
tablename,
partitionname
FROM pg_partitions;
Как выявить
Чтобы получить список пользовательских представлений, которые ссылаются на системные объекты в своих определениях, выполните следующую команду:
WITH RECURSIVE system_objects AS
(SELECT
n.nspname,
c.relname,
format('%s.%s', n.nspname, c.relname) AS system_object
FROM pg_class c
JOIN pg_namespace n
ON c.relnamespace = n.oid WHERE nspname IN ('pg_catalog', 'gp_toolkit')
UNION ALL
SELECT
v.schemaname,
v.viewname,
c.system_object
FROM pg_views v
JOIN system_objects c ON v.definition ~* ('[^\.]\y' || c.relname || '\y')
WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'gp_toolkit'))
SELECT *
FROM system_objects
WHERE nspname NOT IN ('pg_catalog', 'information_schema', 'gp_toolkit');
Пример вывода:
nspname | relname | system_object ---------+------------------+-------------------------- public | table_partitions | pg_catalog.pg_partitions (1 row)
Как исправить
Удалите затронутое представление:
DROP VIEW table_partitions;
Изучите обновленную структуру системных таблиц в Greengage DB 7 и пересоздайте представление при необходимости.
Ресурсные группы
Сценарий
В Greengage DB 7 изменились некоторые параметры ресурсных групп, например:
-
CPU_RATE_LIMITбыл переименован вCPU_MAX_PERCENT. -
MEMORY_SHARED_QUOTA,MEMORY_SPILL_RATIOиMEMORY_AUDITORбыли удалены.
Пример ниже создает ресурсную группу с параметрами, используемыми в Greengage DB 6:
CREATE RESOURCE GROUP rg_analysts WITH (
CPU_RATE_LIMIT = 20,
MEMORY_LIMIT = 20,
CONCURRENCY = 30
);
Как исправить
Выполните gpbackup с параметром --without-globals при создании резервной копии данных старого кластера.
Параметр --without-globals гарантирует, что глобальные объекты, такие как ресурсные группы и табличные пространства, не будут выгружены во время резервного копирования.
Эти глобальные объекты необходимо заранее создать вручную в новом кластере перед восстановлением данных.