Гость
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Использование anyelement в функции / 13 сообщений из 13, страница 1 из 1
10.01.2020, 16:04
    #39912500
sdkmaster
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
Всем привет!

Кто подскажет, как присвоить значение колонки для типа данных anyelement?

Ситуация следующая:
  • есть простая таблица
  • на нее навешен триггер на апдейт
  • в триггерной функции мы вызываем функцию сравнения NEW и OLD
  • функция сравнения, должна вернуть строку в которой все поля NULL, кроме тех, что отличаются.
Код: plsql
1.
2.
3.
4.
create table public.firdata (
    ida integer,
    city text
);


Код: plsql
1.
2.
3.
create trigger tr_firdata
after update on public.firdata
for each row execute procedure arc.chacha();


Код: plsql
1.
create schema arc;


Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
create function arc.chacha() returns trigger as $$
declare
    rec record;
begin   
    if (tg_op = 'UPDATE') then
        rec = arc.maker(NEW, OLD);
        -- запишем значение rec в таблицу аудита...
        return NEW;
    end if;
end;
$$ language plpgsql;


Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
create function arc.maker(in NEW anyelement, in OLD anyelement, out RET anyelement) as $$
declare
begin
    -- pg_typeof(NEW) == pg_typeof(OLD) == pg_typeof(RET) == firdata - типы определяются верно.

    raise notice 'old.city = %', OLD.city; -- обращение по названию колонки работает корректно, выводится правильное значение

    RET.city = 'something'; -- попытка записать значение приводит к ошибке - 'ERROR:  "ret.city" is not a known variable'
end;
$$ language plpgsql;



Проблема в следующем - невозможно присвоить значение для столбца city переменной RET.
Постгрес ругается, что такого нет - вроде правильно, ведь указан тип anyelement. Но это полиморфный тип и в рантайме он известен - это тип public.firdata.
Как правильно записать значение в переменную RET ???
...
Рейтинг: 0 / 0
10.01.2020, 16:55
    #39912532
Guzya
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
Не уверен, что anyelement можно возвращать.
...
Рейтинг: 0 / 0
10.01.2020, 17:10
    #39912545
sdkmaster
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
Guzya
Не уверен, что anyelement можно возвращать.

Можно. Следующий код работает и возвращает все как надо.
Код: plsql
1.
2.
3.
4.
5.
6.
7.
create function arc.maker(in NEW anyelement, in OLD anyelement, out RET anyelement) as $$
declare
begin
    RET = NEW; -- работает, вызывающая функция получает запись RET со значениями равными значениям записи NEW
               -- но присвоить RET.city = 'something' не выходит
end;
$$ language plpgsql;
...
Рейтинг: 0 / 0
10.01.2020, 22:22
    #39912652
Guzya
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
А так
Код: sql
1.
2.
RET = NEW;
RET.city = 'something';
...
Рейтинг: 0 / 0
10.01.2020, 22:30
    #39912653
Troglodit
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
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;


Вот так не пробовали? Это из доки.
В вашем случае в (*) result.city:='test';

Единственное не понял, зачем вам anyelement, если в коде явно указан record. Почему использовать этот тип?
...
Рейтинг: 0 / 0
14.01.2020, 09:51
    #39913502
sdkmaster
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
Troglodit
Единственное не понял, зачем вам anyelement, если в коде явно указан record. Почему использовать этот тип?

Anyelement нужен, чтобы иметь одну функцию аудита для разных таблиц. Используя тип anyelement, я могу прокидывать в функцию arc.maker строки любой таблицы. Это удобно и этим надо пользоваться, а не плодить однотипные функции.
...
Рейтинг: 0 / 0
14.01.2020, 09:58
    #39913506
sdkmaster
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
Нашел временное решение:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
create function arc.maker(in NEW anyelement, in OLD anyelement, out RET anyelement) as $$
declare
    rec record;
begin
    rec = RET; -- rec получает тот же тип, что и RET, плюс все колонки изначально выставлены в NULL

    -- 
    -- заполняем, изменяем rec
    -- 

    RET = rec; -- присваиваем значение возращаемой переменной RET
end;
$$ language plpgsql;


Необходимо протестировать на производительность в реале.
Но пока никаких критических затыков не обнаружено.
...
Рейтинг: 0 / 0
14.01.2020, 11:42
    #39913557
entrypoint
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
sdkmaster,


Это точно работает ? Как Вы вызываете функцию ?
Переменные типа record похожи на переменные строкового типа, но они не имеют предопределённой структуры. Они приобретают фактическую структуру от строки, которая им присваивается командами SELECT или FOR . Структура переменной типа record может меняться каждый раз при присвоении значения
https://postgrespro.ru/docs/postgresql/9.5/plpgsql-declarations#plpgsql-declaration-records

а в Вашем случае значение переменной RET - не определено, значит не определено и значение переменной rec, что приведёт к ошибке
Код: sql
1.
2.
3.
4.
5.
ERROR:  record "ret" is not assigned yet
DETAIL:  The tuple structure of a not-yet-assigned record is indeterminate.
CONTEXT:  SQL statement "SELECT RET"
PL/pgSQL function inline_code_block line 6 at assignment
SQL-состояние: 55000 


в этом
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
DO $$
DECLARE 
	rec RECORD;
	RET RECORD;
BEGIN 	
    rec = RET; 
    RAISE NOTICE '%', RET; 	
END; 
$$ language plpgsql;


в этом
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
DO $$
DECLARE 
	rec RECORD;
	RET RECORD;
BEGIN 	
    SELECT rec INTO RET; 
    RAISE NOTICE '%', RET; 
END; 
$$ language plpgsql;


и в этом
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
DO $$
DECLARE 
	rec RECORD;
	RET ALIAS FOR rec;
BEGIN 	
    RAISE NOTICE '%', RET; 
END; 
$$ language plpgsql;


случаях
...
Рейтинг: 0 / 0
14.01.2020, 12:39
    #39913605
sdkmaster
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
entrypoint
Это точно работает ? Как Вы вызываете функцию ?

Все отлично работает :)

Вы упустили важный момент - функцию arc.maker дергает триггерная функция arc.chacha, которая передает записи NEW и OLD. А т.к. все anyelement в полиморфной функции должны быть одного(!) типа, то OUT переменная RET "наследует" тип IN переменных NEW и OLD. В первом сообщении описаны функции arc.chacha и arc.maker.

Я передаю RET как "out RET anyelement", а в ваших примерах вы везде определяете ее как record, отсюда и ошибка.
...
Рейтинг: 0 / 0
14.01.2020, 13:11
    #39913624
entrypoint
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
sdkmaster,
ОК )))))
...
Рейтинг: 0 / 0
14.01.2020, 13:47
    #39913640
entrypoint
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
sdkmaster,

sdkmaster
entrypoint
Это точно работает ? Как Вы вызываете функцию ?

Все отлично работает :)

Вы упустили важный момент - функцию arc.maker дергает триггерная функция arc.chacha, которая передает записи NEW и OLD. А т.к. все anyelement в полиморфной функции должны быть одного(!) типа, то OUT переменная RET "наследует" тип IN переменных NEW и OLD. В первом сообщении описаны функции arc.chacha и arc.maker.

Я передаю RET как "out RET anyelement", а в ваших примерах вы везде определяете ее как record, отсюда и ошибка.


всё же, если как у автора то,
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
create table public.firdata (
    ida integer,
    city text
);

create schema arc;

create function arc.maker(in NEW anyelement, in OLD anyelement, out RET anyelement) as $$
declare
    rec record;
begin
    rec = RET; 
    RET = rec; 
    RET.city = 'something'; -- !!!!!!!
end;
$$ language plpgsql;

, то
Код: sql
1.
2.
3.
4.
5.
ERROR:  "ret.city" is not a known variable
LINE 14:  RET.city = 'something';
          ^
SQL-состояние: 42601
Символ: 239 



а если так, то
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
create table public.firdata (
    ida integer,
    city text
);
INSERT INTO public.firdata VALUES(1,2);

create schema arc;

create function arc.maker(in NEW anyelement, in OLD anyelement, out RET anyelement) as $$
declare
    rec record;
begin
    rec = RET; 
    RET = rec; 
end;
$$ language plpgsql;

create function arc.chacha() returns trigger as $$
declare
    rec record;
begin   
    if (tg_op = 'UPDATE') then
        rec = arc.maker(NEW, OLD);
        -- запишем значение rec в таблицу аудита...
		INSERT INTO public.firdata (ida, city) VALUES(rec.ida, rec.city); -- !!!!!!!!!
        return NEW;
    end if;
end;
$$ language plpgsql;

create trigger tr_firdata
after update on public.firdata
for each row execute procedure arc.chacha();

UPDATE public.firdata SET city = 'gggg';

SELECT * FROM public.firdata;

, то нет значений

Код: sql
1.
Что я не так делаю ?!!!!!
...
Рейтинг: 0 / 0
14.01.2020, 14:15
    #39913660
entrypoint
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
Всё
Работает вот так

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
create table public.firdata (
    ida integer,
    city text
);
INSERT INTO public.firdata VALUES(1,2);

create schema arc;

create function arc.maker(in NEW anyelement, in OLD anyelement, out RET anyelement) as $$
begin
	SELECT NEW.ida, 'something' AS city INTO RET; -- !!!!!!
end;
$$ language plpgsql;

create function arc.chacha() returns trigger as $$
declare
    rec record;
begin   
    if (tg_op = 'UPDATE') then
        rec = arc.maker(NEW, OLD);
        -- запишем значение rec в таблицу аудита...
		INSERT INTO public.firdata (ida, city) VALUES(rec.ida, rec.city); -- !!!!!!!!!
        return NEW;
    end if;
end;
$$ language plpgsql;

create trigger tr_firdata
after update on public.firdata
for each row execute procedure arc.chacha();

UPDATE public.firdata SET city = 'gggg';

SELECT * FROM public.firdata;
...
Рейтинг: 0 / 0
14.01.2020, 19:08
    #39913907
Troglodit
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Использование anyelement в функции
sdkmaster
Troglodit
Единственное не понял, зачем вам anyelement, если в коде явно указан record. Почему использовать этот тип?

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

Все ваши входящие параметры и есть record.
Либо я может путаю, но в триггере NEW и OLD и есть record.
Так что мне кажется вы перемудрили.

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

NEW
Тип данных RECORD. Переменная содержит новую строку базы данных для команд INSERT/UPDATE в триггерах уровня строки. В триггерах уровня оператора и для команды DELETE эта переменная имеет значение null.

OLD
Тип данных RECORD. Переменная содержит старую строку базы данных для команд UPDATE/DELETE в триггерах уровня строки. В триггерах уровня оператора и для команды INSERT эта переменная имеет значение null.

Доку с про.
...
Рейтинг: 0 / 0
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Использование anyelement в функции / 13 сообщений из 13, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]