Π­Ρ‚ΡŽΠ΄ ΠΏΠΎ рСализация бизнСс-Π»ΠΎΠ³ΠΈΠΊΠΈ Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ Ρ…Ρ€Π°Π½ΠΈΠΌΡ‹Ρ… Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ PostgreSQL

ΠŸΠΎΠ±ΡƒΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌ ΠΌΠΎΡ‚ΠΈΠ²ΠΎΠΌ ΠΊ написанию ΡΡ‚ΡŽΠ΄Π° послуТила ΡΡ‚Π°Ρ‚ΡŒΡ Β«Π’ ΠΊΠ°Ρ€Π°Π½Ρ‚ΠΈΠ½ Π½Π°Π³Ρ€ΡƒΠ·ΠΊΠ° выросла Π² 5 Ρ€Π°Π·, Π½ΠΎ ΠΌΡ‹ Π±Ρ‹Π»ΠΈ Π³ΠΎΡ‚ΠΎΠ²Ρ‹Β». Как Lingualeo ΠΏΠ΅Ρ€Π΅Π΅Ρ…Π°Π» Π½Π° PostgreSQL с 23 ΠΌΠ»Π½ ΡŽΠ·Π΅Ρ€ΠΎΠ². Π’Π°ΠΊ ΠΆΠ΅ показалось интСрСсной ΡΡ‚Π°Ρ‚ΡŒΡ опубликованная 4 Π³ΠΎΠ΄Π° Π½Π°Π·Π°Π΄ β€” РСализация бизнСс-Π»ΠΎΠ³ΠΈΠΊΠΈ Π² MySQL.

Показалось интСрСсным Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΠΎΠ΄Π½Π° ΠΈ Ρ‚Π° ΠΆΠ΅ ΠΌΡ‹ΡΠ»ΡŒ-"Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ бизнСс-Π»ΠΎΠ³ΠΈΠΊΡƒ Π² Π‘Π”".

Π­Ρ‚ΡŽΠ΄ ΠΏΠΎ рСализация бизнСс-Π»ΠΎΠ³ΠΈΠΊΠΈ Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ Ρ…Ρ€Π°Π½ΠΈΠΌΡ‹Ρ… Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ PostgreSQL

ΠΏΡ€ΠΈΡˆΠ»Π° Π² Π³ΠΎΠ»ΠΎΠ²Ρƒ Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΌΠ½Π΅ ΠΎΠ΄Π½ΠΎΠΌΡƒ.

Π’Π°ΠΊΠΆΠ΅ Π½Π° Π±ΡƒΠ΄ΡƒΡ‰Π΅Π΅ Ρ…ΠΎΡ‚Π΅Π»ΠΎΡΡŒ ΡΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ, для сСбя Π² ΠΏΠ΅Ρ€Π²ΡƒΡŽ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ, интСрСсныС Π½Π°Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ возникшиС ΠΏΠΎ Ρ…ΠΎΠ΄Ρƒ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ. ОсобСнно учитывая Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π½Π΅Π΄Π°Π²Π½ΠΎ Π±Ρ‹Π»ΠΎ принято стратСгичСскоС Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ ΠΎ смСнС Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρ‹ ΠΈ пСрСносС бизнСс-Π»ΠΎΠ³ΠΈΠΊΠΈ Π½Π° ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ backend. Π’Π°ΠΊ, Ρ‡Ρ‚ΠΎ всС, Ρ‡Ρ‚ΠΎ Π±Ρ‹Π»ΠΎ Π½Π°Ρ€Π°Π±ΠΎΡ‚Π°Π½ΠΎ, скоро Π½ΠΈΠΊΠΎΠΌΡƒ Π½Π΅ понадобится ΠΈ Π½ΠΈΠΊΠΎΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ Π½Π΅ интСрСсно.

ΠžΠΏΠΈΡΠ°Π½Π½Ρ‹Π΅ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ Π½Π΅ ΡΠ²Π»ΡΡŽΡ‚ΡΡ ΠΊΠ°ΠΊΠΈΠΌ Ρ‚ΠΎ ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ΠΌ ΠΈ ΠΈΡΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌ know how, всС ΠΏΠΎ классикС ΠΈ Π±Ρ‹Π»ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½ΠΎ Π½Π΅ΠΎΠ΄Π½ΠΎΠΊΡ€Π°Ρ‚Π½ΠΎ (я Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ ΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹ΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΠ» 20 Π»Π΅Ρ‚ Π½Π°Π·Π°Π΄ Π½Π° Oracle).ΠŸΡ€ΠΎΡΡ‚ΠΎ Ρ€Π΅ΡˆΠΈΠ» собрал всС Π² ΠΎΠ΄Π½ΠΎΠΌ мСстС. Π’Π΄Ρ€ΡƒΠ³ ΠΊΠΎΠΌΡƒ пригодится. Как ΠΏΠΎΠΊΠ°Π·Π°Π»Π° ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ° β€” довольно часто ΠΎΠ΄Π½Π° ΠΈ Ρ‚Π° ΠΆΠ΅ идСя ΠΏΡ€ΠΈΡ…ΠΎΠ΄ΠΈΡ‚ нСзависимо Ρ€Π°Π·Π½Ρ‹ΠΌ людям. Π”Π° ΠΈ для сСбя ΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ Π½Π° ΠΏΠ°ΠΌΡΡ‚ΡŒ, ΠΏΠΎΠ»Π΅Π·Π½ΠΎ.

ΠšΠΎΠ½Π΅Ρ‡Π½ΠΎ, ΠΆΠ΅ Π½ΠΈΡ‡Ρ‚ΠΎ Π² этом ΠΌΠΈΡ€Π΅ Π½Π΅ ΡΠΎΠ²Π΅Ρ€ΡˆΠ΅Π½Π½ΠΎ, ошибки ΠΈ ΠΎΠΏΠ΅Ρ‡Π°Ρ‚ΠΊΠΈ ΠΊ соТалСнию Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹. ΠšΡ€ΠΈΡ‚ΠΈΠΊΠ° ΠΈ замСчания всячСски ΠΏΡ€ΠΈΠ²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‚ΡΡ ΠΈ ΠΎΠΆΠΈΠ΄Π°ΡŽΡ‚ΡΡ.И Π΅Ρ‰Π΅ ΠΎΠ΄Π½Π° малСнькая Π΄Π΅Ρ‚Π°Π»ΡŒ β€” ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹Π΅ Π΄Π΅Ρ‚Π°Π»ΠΈ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΎΠΏΡƒΡ‰Π΅Π½Ρ‹. ВсС Ρ‚Π°ΠΊΠΈ всё ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΏΠΎΠΊΠ° Π² Ρ€Π΅Π°Π»ΡŒΠ½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‰Π΅ΠΌ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅. Π’Π°ΠΊ, Ρ‡Ρ‚ΠΎ ΡΡ‚Π°Ρ‚ΡŒΡ ΠΊΠ°ΠΊ ΡΡ‚ΡŽΠ΄ ΠΈ описаниС ΠΎΠ±Ρ‰Π΅ΠΉ ΠΊΠΎΠ½Ρ†Π΅ΠΏΡ†ΠΈΠΈ, Π½Π΅ Π±ΠΎΠ»Π΅Π΅ Ρ‚ΠΎΠ³ΠΎ. НадСюсь для понятия ΠΎΠ±Ρ‰Π΅ΠΉ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½Ρ‹, Π΄Π΅Ρ‚Π°Π»Π΅ΠΉ достаточно.

ΠžΠ±Ρ‰Π°Ρ идСя β€” «раздСляй ΠΈ властвуй, скрывай ΠΈ Π²Π»Π°Π΄Π΅ΠΉΒ»

ИдСя классичСская β€” ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Π°Ρ схСма для Ρ‚Π°Π±Π»ΠΈΡ†, ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Π°Ρ схСма для Ρ…Ρ€Π°Π½ΠΈΠΌΡ‹Ρ… Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ.
ΠšΠ»ΠΈΠ΅Π½Ρ‚ Π½Π΅ ΠΈΠΌΠ΅Π΅Ρ‚ доступа ΠΊ Π΄Π°Π½Π½Ρ‹ΠΌ Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ. ВсС, Ρ‡Ρ‚ΠΎ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ ΠΌΠΎΠΆΠ΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ β€” Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π²Ρ‹Π·Π²Π°Ρ‚ΡŒ Ρ…Ρ€Π°Π½ΠΈΠΌΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹ΠΉ ΠΎΡ‚Π²Π΅Ρ‚.

Π ΠΎΠ»ΠΈ

CREATE ROLE store;

CREATE ROLE sys_functions;

CREATE ROLE loc_audit_functions;

CREATE ROLE service_functions;

CREATE ROLE business_functions;

Π‘Ρ…Π΅ΠΌΡ‹

Π‘Ρ…Π΅ΠΌΠ° хранСния Ρ‚Π°Π±Π»ΠΈΡ†

Π¦Π΅Π»Π΅Π²Ρ‹Π΅ Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹, Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΡŽΡ‰ΠΈΠ΅ ΠΏΡ€Π΅Π΄ΠΌΠ΅Ρ‚Π½Ρ‹Π΅ сущности.

CREATE SCHEMA store AUTHORIZATION store ;

Π‘Ρ…Π΅ΠΌΠ° систСмных Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ

БистСмныС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ, Π² частности для логирования измСнСния Ρ‚Π°Π±Π»ΠΈΡ†.

CREATE SCHEMA sys_functions AUTHORIZATION sys_functions ;

Π‘Ρ…Π΅ΠΌΠ° локального Π°ΡƒΠ΄ΠΈΡ‚Π°

Π€ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΠΈ Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ для Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ локального Π°ΡƒΠ΄ΠΈΡ‚Π° выполнСния Ρ…Ρ€Π°Π½ΠΈΠΌΡ‹Ρ… Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ ΠΈ измСнСния Ρ†Π΅Π»Π΅Π²Ρ‹Ρ… Ρ‚Π°Π±Π»ΠΈΡ†.

CREATE SCHEMA loc_audit_functions AUTHORIZATION loc_audit_functions;

Π‘Ρ…Π΅ΠΌΠ° сСрвисных Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ

Π€ΡƒΠ½ΠΊΡ†ΠΈΠΈ для сСрвисных ΠΈ DML Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ.

CREATE SCHEMA service_functions AUTHORIZATION service_functions;

Π‘Ρ…Π΅ΠΌΠ° бизнСс Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ

Π€ΡƒΠ½ΠΊΡ†ΠΈΠΈ для ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹Ρ… бизнСс Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹Ρ… ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠΌ.

CREATE SCHEMA business_functions AUTHORIZATION business_functions;

ΠŸΡ€Π°Π²Π° доступа

Роль β€” DBA ΠΈΠΌΠ΅Π΅Ρ‚ ΠΏΠΎΠ»Π½Ρ‹ΠΉ доступ ΠΊΠΎ всСм схСмам (ΠΎΡ‚Π΄Π΅Π»Π΅Π½Π° ΠΎΡ‚ Ρ€ΠΎΠ»ΠΈ DB Owner).

CREATE ROLE dba_role;
GRANT store TO dba_role;
GRANT sys_functions TO dba_role;
GRANT loc_audit_functions TO dba_role;
GRANT service_functions TO dba_role;
GRANT business_functions TO dba_role;

Роль β€” USER ΠΈΠΌΠ΅Π΅Ρ‚ ΠΏΡ€ΠΈΠ²ΠΈΠ»Π΅Π³ΠΈΡŽ EXECUTE Π² схСмС business_functions.

CREATE ROLE user_role;

ΠŸΡ€ΠΈΠ²ΠΈΠ»Π΅Π³ΠΈΠΈ ΠΌΠ΅ΠΆΠ΄Ρƒ схСмами

GRANT
ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ всС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΡΠΎΠ·Π΄Π°ΡŽΡ‚ΡΡ с Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚ΠΎΠΌ SECURITY DEFINER Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠ° инструкция REVOKE EXECUTE ON ALL FUNCTION… FROM public;

REVOKE EXECUTE ON ALL FUNCTION IN SCHEMA sys_functions FROM public ; 
REVOKE EXECUTE ON ALL FUNCTION IN SCHEMA  loc_audit_functions  FROM public ; 
REVOKE EXECUTE ON ALL FUNCTION IN SCHEMA  service_functions FROM public ; 
REVOKE EXECUTE ON ALL FUNCTION IN SCHEMA  business_functions FROM public ; 

GRANT USAGE ON SCHEMA sys_functions TO dba_role ; 
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA sys_functions TO dba_role ;
GRANT USAGE ON SCHEMA loc_audit_functions  TO dba_role ; 
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA loc_audit_functions  TO dba_role ;
GRANT USAGE ON SCHEMA service_functions TO dba_role ; 
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA service_functions TO dba_role ;
GRANT USAGE ON SCHEMA business_functions TO dba_role ; 
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA business_functions TO dba_role ;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA business_functions TO user_role ;

GRANT ALL PRIVILEGES ON SCHEMA store TO GROUP business_functions ;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA store TO business_functions ;
GRANT USAGE ON ALL SEQUENCES IN SCHEMA store TO business_functions ;

Π˜Ρ‚Π°ΠΊ схСма Π‘Π” β€” Π³ΠΎΡ‚ΠΎΠ²Π°. МоТно ΠΏΡ€ΠΈΡΡ‚ΡƒΠΏΠ°Ρ‚ΡŒ ΠΊ наполнСнию Π΄Π°Π½Π½Ρ‹ΠΌΠΈ.

Π¦Π΅Π»Π΅Π²Ρ‹Π΅ Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹

Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ Ρ‚Π°Π±Π»ΠΈΡ† Ρ‚Ρ€ΠΈΠ²ΠΈΠ°Π»ΡŒΠ½ΠΎ. Никаких особСнностСй, Π·Π° ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ΠΌ Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ Π±Ρ‹Π»ΠΎ Ρ€Π΅ΡˆΠ΅Π½ΠΎ ΠΎΡ‚ΠΊΠ°Π·Π°Ρ‚ΡŒΡΡ ΠΎΡ‚ использования SERIAL ΠΈ Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ явно. Плюс, разумССтся максимальноС использованиС инструкции

COMMENT ON ...

ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ для всСх ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², Π±Π΅Π· ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ.

Π›ΠΎΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ Π°ΡƒΠ΄ΠΈΡ‚

Для вСдСния ΠΆΡƒΡ€Π½Π°Π»Π° выполнСния Ρ…Ρ€Π°Π½ΠΈΠΌΡ‹Ρ… Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ ΠΈ измСнСния Ρ†Π΅Π»Π΅Π²Ρ‹Ρ… Ρ‚Π°Π±Π»ΠΈΡ† ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Ρ‚Π°Π±Π»ΠΈΡ†Π° локального Π°ΡƒΠ΄ΠΈΡ‚Π°, Π²ΠΊΠ»ΡŽΡ‡Π°ΡŽΡ‰Π°Ρ Π² сСбя Π² Ρ‚ΠΎΠΌ числС Π΄Π΅Ρ‚Π°Π»ΠΈ клиСнтского соСдинСния, ΠΌΠ΅Ρ‚ΠΊΡƒ Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΠΎΠ³ΠΎ модуля, фактичСскиС значСния Π²Ρ…ΠΎΠ΄Π½Ρ‹Ρ… ΠΈ Π²Ρ‹Ρ…ΠΎΠ΄Π½Ρ‹Ρ… ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² Π² Π²ΠΈΠ΄Π΅ JSON.

БистСмныС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ

ΠŸΡ€Π΅Π΄Π½Π°Π·Π½Π°Ρ‡Π΅Π½Ρ‹ для логирования ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ Π² Ρ†Π΅Π»Π΅Π²Ρ‹Ρ… Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ…. ΠŸΡ€Π΅Π΄ΡΡ‚Π°Π²Π»ΡΡŽΡ‚ собой Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Π½Ρ‹Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ.

Π¨Π°Π±Π»ΠΎΠ½ — систСмная функция

---------------------------------------------------------
-- INSERT
CREATE OR REPLACE FUNCTION sys_functions.table_insert_log ()
RETURNS TRIGGER AS $$
BEGIN
  PERFORM loc_audit_functions.make_log( ' '||'table' , 'insert' , json_build_object('id', NEW.id)  );
  RETURN NULL ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER table_after_insert AFTER INSERT ON storage.table FOR EACH ROW EXECUTE PROCEDURE sys_functions.table_insert_log();

---------------------------------------------------------
-- UPDATE
CREATE OR REPLACE FUNCTION sys_functions.table_update_log ()
RETURNS TRIGGER AS $$
BEGIN
  IF OLD.column != NEW.column
  THEN
    PERFORM loc_audit_functions.make_log( ' '||'table' , 'update' , json_build_object('OLD.column', OLD.column , 'NEW.column' , NEW.column )  );
  END IF ;
  RETURN NULL ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER table_after_update AFTER UPDATE ON storage.table FOR EACH ROW EXECUTE PROCEDURE sys_functions.table_update_log ();

---------------------------------------------------------
-- DELETE
CREATE OR REPLACE FUNCTION sys_functions.table_delete_log ()
RETURNS TRIGGER AS $$
BEGIN
  PERFORM loc_audit_functions.make_log( ' '||'table' , 'delete' , json_build_object('id', OLD.id )  );
  RETURN NULL ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER table_after_delete AFTER DELETE ON storage.table FOR EACH ROW EXECUTE PROCEDURE sys_functions.table_delete_log ();

БСрвисныС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ

ΠŸΡ€Π΅Π΄Π½Π°Π·Π½Π°Ρ‡Π΅Π½Ρ‹ для Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ сСрвисных ΠΈ DML ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ Π½Π°Π΄ Ρ†Π΅Π»Π΅Π²Ρ‹ΠΌΠΈ Ρ‚Π°Π±Π»ΠΈΡ†Π°ΠΌΠΈ.

Π¨Π°Π±Π»ΠΎΠ½ — сСрвисная функция

--INSERT
--RETURN id OF NEW ROW
CREATE OR REPLACE FUNCTION service_functions.table_insert ( new_column store.table.column%TYPE )
RETURNS integer AS $$
DECLARE
  new_id integer ;
BEGIN
  -- Generate new id
  new_id = nextval('store.table.seq');

  -- Insert into table
  INSERT INTO store.table
  ( 
    id ,
    column
   )
  VALUES
  (
   new_id ,
   new_column
   );

RETURN new_id ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;

--DELETE
--RETURN ROW NUMBERS DELETED
CREATE OR REPLACE FUNCTION service_functions.table_delete ( current_id integer ) 
RETURNS integer AS $$
DECLARE
  rows_count integer  ;    
BEGIN
  DELETE FROM store.table WHERE id = current_id; 

  GET DIAGNOSTICS rows_count = ROW_COUNT;                                                                           

  RETURN rows_count ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;
 
-- UPDATE DETAILS
-- RETURN ROW NUMBERS UPDATED
CREATE OR REPLACE FUNCTION service_functions.table_update_column 
(
  current_id integer 
  ,new_column store.table.column%TYPE
) 
RETURNS integer AS $$
DECLARE
  rows_count integer  ; 
BEGIN
  UPDATE  store.table
  SET
    column = new_column
  WHERE id = current_id;

  GET DIAGNOSTICS rows_count = ROW_COUNT;                                                                           

  RETURN rows_count ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;

БизнСс Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ

ΠŸΡ€Π΅Π΄Π½Π°Π·Π½Π°Ρ‡Π΅Π½Ρ‹ для ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹Ρ… бизнСс Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ Π²Ρ‹Π·Ρ‹Π²Π°Π΅ΠΌΡ‹ΠΉ ΠΊΠ»ΠΈΠ΅Π½Ρ‚ΠΎΠΌ. Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°ΡŽΡ‚ всСгда β€” JSON. Для ΠΏΠ΅Ρ€Π΅Ρ…Π²Π°Ρ‚Π° ΠΈ логирования ошибок выполнСния, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π±Π»ΠΎΠΊ EXCEPTION.

Π¨Π°Π±Π»ΠΎΠ½ — бизнСс функция

CREATE OR REPLACE FUNCTION business_functions.business_function_template(
--Input parameters        
 )
RETURNS JSON AS $$
DECLARE
  ------------------------
  --for exception catching
  error_message text ;
  error_json json ;
  result json ;
  ------------------------ 
BEGIN
--LOGGING
  PERFORM loc_audit_functions.make_log
  (
    'business_function_template',
    'STARTED',
    json_build_object
    (
	--IN Parameters
    ) 
   );

  PERFORM business_functions.notice('business_function_template');            

  --START BUSINESS PART
  --END BUSINESS PART

  -- SUCCESFULLY RESULT
  PERFORM business_functions.notice('result');
  PERFORM business_functions.notice(result);

  PERFORM loc_audit_functions.make_log
  (
    'business_function_template',
    'FINISHED', 
    json_build_object( 'result',result )
  );

  RETURN result ;
----------------------------------------------------------------------------------------------------------
-- EXCEPTION CATCHING
EXCEPTION                        
  WHEN OTHERS THEN    
    PERFORM loc_audit_functions.make_log
    (
      'business_function_template',
      'STARTED',
      json_build_object
      (
	--IN Parameters	
      ) , TRUE );

     PERFORM loc_audit_functions.make_log
     (
       'business_function_template',
       ' ERROR',
       json_build_object('SQLSTATE',SQLSTATE ), TRUE 
     );

     PERFORM loc_audit_functions.make_log
     (
       'business_function_template',
       ' ERROR',
       json_build_object('SQLERRM',SQLERRM  ), TRUE 
      );

     GET STACKED DIAGNOSTICS error_message = RETURNED_SQLSTATE ;
     PERFORM loc_audit_functions.make_log
     (
      'business_function_template',
      ' ERROR-RETURNED_SQLSTATE',json_build_object('RETURNED_SQLSTATE',error_message  ), TRUE );

     GET STACKED DIAGNOSTICS error_message = COLUMN_NAME ;
     PERFORM loc_audit_functions.make_log
     (
       'business_function_template',
       ' ERROR-COLUMN_NAME',
       json_build_object('COLUMN_NAME',error_message  ), TRUE );

     GET STACKED DIAGNOSTICS error_message = CONSTRAINT_NAME ;
     PERFORM loc_audit_functions.make_log
     (
      'business_function_template',
      ' ERROR-CONSTRAINT_NAME',
      json_build_object('CONSTRAINT_NAME',error_message  ), TRUE );

     GET STACKED DIAGNOSTICS error_message = PG_DATATYPE_NAME ;
     PERFORM loc_audit_functions.make_log
     (
       'business_function_template',
       ' ERROR-PG_DATATYPE_NAME',
       json_build_object('PG_DATATYPE_NAME',error_message  ), TRUE );

     GET STACKED DIAGNOSTICS error_message = MESSAGE_TEXT ;
     PERFORM loc_audit_functions.make_log
     (
       'business_function_template',
       ' ERROR-MESSAGE_TEXT',json_build_object('MESSAGE_TEXT',error_message  ), TRUE );

     GET STACKED DIAGNOSTICS error_message = SCHEMA_NAME ;
     PERFORM loc_audit_functions.make_log
     (s
       'business_function_template',
       ' ERROR-SCHEMA_NAME',json_build_object('SCHEMA_NAME',error_message  ), TRUE );

     GET STACKED DIAGNOSTICS error_message = PG_EXCEPTION_DETAIL ;
     PERFORM loc_audit_functions.make_log
     (
      'business_function_template',
      ' ERROR-PG_EXCEPTION_DETAIL',
      json_build_object('PG_EXCEPTION_DETAIL',error_message  ), TRUE );

     GET STACKED DIAGNOSTICS error_message = PG_EXCEPTION_HINT ;
     PERFORM loc_audit_functions.make_log
     (
       'business_function_template',
       ' ERROR-PG_EXCEPTION_HINT',json_build_object('PG_EXCEPTION_HINT',error_message  ), TRUE );

     GET STACKED DIAGNOSTICS error_message = PG_EXCEPTION_CONTEXT ;
     PERFORM loc_audit_functions.make_log
     (
      'business_function_template',
      ' ERROR-PG_EXCEPTION_CONTEXT',json_build_object('PG_EXCEPTION_CONTEXT',error_message  ), TRUE );                                      

    RAISE WARNING 'ALARM: %' , SQLERRM ;

    SELECT json_build_object
    (
      'isError' , TRUE ,
      'errorMsg' , SQLERRM
     ) INTO error_json ;

  RETURN  error_json ;
END
$$ LANGUAGE plpgsql SECURITY DEFINER;

Π˜Ρ‚ΠΎΠ³

Для описания ΠΎΠ±Ρ‰Π΅ΠΉ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½Ρ‹, Π΄ΡƒΠΌΠ°ΡŽ Π²ΠΏΠΎΠ»Π½Π΅ достаточно. Если ΠΊΠΎΠ³ΠΎ заинтСрСсовали Π΄Π΅Ρ‚Π°Π»ΠΈ ΠΈ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹-ΠΏΠΈΡˆΠΈΡ‚Π΅ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ, с ΡƒΠ΄ΠΎΠ²ΠΎΠ»ΡŒΡΡ‚Π²ΠΈΠ΅ΠΌ дополню ΠΊΠ°Ρ€Ρ‚ΠΈΠ½Ρƒ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ ΡˆΡ‚Ρ€ΠΈΡ…Π°ΠΌΠΈ.

P.S.

Π›ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ простой ошибки — Ρ‚ΠΈΠΏ Π²Ρ…ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°

-[ RECORD 1 ]-
date_trunc      | 2020-08-19 13:15:46
id              | 1072
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          | STARTED
jsonb_pretty    | {
                |     "dko": {
                |         "id": 4,
                |         "type": "Type1",                                                                                                                                                                                            
                |         "title": "CREATED BY addKD",
                |         "Weight": 10,
                |         "Tr": "300",
                |         "reduction": 10,
                |         "isTrud": "TRUE",
                |         "description": "decription",
                |         "lowerTr": "100",
                |         "measurement": "measurement1",
                |         "methodology": "m1",                                                                                                                                                                                           
                |         "passportUrl": "files",
                |         "upperTr": "200",
                |         "weightingFactor": 100.123,
                |         "actualTrValue": null,
                |         "upperTrCalcNumber": "120"
                |     },
                |     "CardId": 3
                | }
-[ RECORD 2 ]-
date_trunc      | 2020-08-19 13:15:46
id              | 1073
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          |  ERROR
jsonb_pretty    | {
                |     "SQLSTATE": "22P02"
                | }
-[ RECORD 3 ]-
date_trunc      | 2020-08-19 13:15:46
id              | 1074
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          |  ERROR
jsonb_pretty    | {
                |     "SQLERRM": "invalid input syntax for type numeric: "null""
                | }
-[ RECORD 4 ]-
date_trunc      | 2020-08-19 13:15:46
id              | 1075
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          |  ERROR-RETURNED_SQLSTATE
jsonb_pretty    | {
                |     "RETURNED_SQLSTATE": "22P02"
                | }
-[ RECORD 5 ]-
date_trunc      | 2020-08-19 13:15:46
id              | 1076
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          |  ERROR-COLUMN_NAME
jsonb_pretty    | {
                |     "COLUMN_NAME": ""
                | }

-[ RECORD 6 ]-
date_trunc      | 2020-08-19 13:15:46
id              | 1077
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          |  ERROR-CONSTRAINT_NAME
jsonb_pretty    | {
                |     "CONSTRAINT_NAME": ""
                | }
-[ RECORD 7 ]-
date_trunc      | 2020-08-19 13:15:46
id              | 1078
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          |  ERROR-PG_DATATYPE_NAME
jsonb_pretty    | {
                |     "PG_DATATYPE_NAME": ""
                | }
-[ RECORD 8 ]-
date_trunc      | 2020-08-19 13:15:46
id              | 1079
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          |  ERROR-MESSAGE_TEXT
jsonb_pretty    | {
                |     "MESSAGE_TEXT": "invalid input syntax for type numeric: "null""
                | }
-[ RECORD 9 ]-
date_trunc      | 2020-08-19 13:15:46
id              | 1080
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          |  ERROR-SCHEMA_NAME
jsonb_pretty    | {
                |     "SCHEMA_NAME": ""
                | }
-[ RECORD 10 ]-
date_trunc      | 2020-08-19 13:15:46
id              | 1081
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          |  ERROR-PG_EXCEPTION_DETAIL
jsonb_pretty    | {
                |     "PG_EXCEPTION_DETAIL": ""
                | }
-[ RECORD 11 ]-
date_trunc      | 2020-08-19 13:15:46
id              | 1082
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          |  ERROR-PG_EXCEPTION_HINT
jsonb_pretty    | {
                |     "PG_EXCEPTION_HINT": ""
                | }
-[ RECORD 12 ]-
date_trunc      | 2020-08-19 13:15:46
id              | 1083
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          |  ERROR-PG_EXCEPTION_CONTEXT
jsonb_pretty    | {
usename         | emp1
log_module      | addKD
log_module_hash | 0b4c1529a89af3ddf6af3821dc790e8a
status          |  ERROR-MESSAGE_TEXT
jsonb_pretty    | {
                |     "MESSAGE_TEXT": "invalid input syntax for type numeric: "null""
                | }

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com

Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ