PostgreSQL
 sql >> डेटाबेस >  >> RDS >> PostgreSQL

PostgreSQL ट्रिगर और संग्रहीत कार्य मूल बातें

Meverynines से नोट:यह ब्लॉग मरणोपरांत प्रकाशित किया जा रहा है क्योंकि 16 जुलाई, 2018 को बेरेन्ड टोबर का निधन हो गया। हम PostgreSQL समुदाय में उनके योगदान का सम्मान करते हैं और अपने मित्र और अतिथि लेखक के लिए शांति की कामना करते हैं।

पिछले लेख में हमने पोस्टग्रेएसक्यूएल सीरियल स्यूडो-टाइप पर चर्चा की थी, जो बढ़ते हुए पूर्णांकों के साथ सिंथेटिक कुंजी मानों को पॉप्युलेट करने के लिए उपयोगी है। हमने देखा कि एक टेबल डेटा डेफिनिशन लैंग्वेज (डीडीएल) स्टेटमेंट में सीरियल डेटा टाइप कीवर्ड को नियोजित करना एक इंटीजर टाइप कॉलम डिक्लेरेशन के रूप में लागू किया जाता है, जो एक साधारण फंक्शन कॉल से प्राप्त डिफ़ॉल्ट मान के साथ, डेटाबेस इंसर्ट पर पॉप्युलेट होता है। डेटा हेरफेर भाषा (डीएमएल) गतिविधि के अभिन्न प्रतिक्रिया के हिस्से के रूप में कार्यात्मक कोड को लागू करने का यह स्वचालित व्यवहार पोस्टग्रेएसक्यूएल जैसे परिष्कृत रिलेशनल डेटाबेस मैनेजमेंट सिस्टम (आरडीबीएमएस) की एक शक्तिशाली विशेषता है। इस लेख में हम कस्टम कोड को स्वचालित रूप से लागू करने के लिए एक और अधिक सक्षम पहलू में आगे बढ़ते हैं, अर्थात् ट्रिगर्स और संग्रहीत कार्यों का उपयोग। परिचय

केस का उपयोग ट्रिगर और संग्रहीत कार्यों के लिए करें

आइए इस बारे में बात करें कि आप ट्रिगर और संग्रहीत कार्यों को समझने में निवेश क्यों करना चाहते हैं। डेटाबेस में ही डीएमएल कोड बनाकर, आप कई अलग-अलग अनुप्रयोगों में डेटा-संबंधित कोड के डुप्लिकेट कार्यान्वयन से बच सकते हैं जो डेटाबेस के साथ इंटरफेस करने के लिए बनाया जा सकता है। यह डेटा सत्यापन, डेटा सफाई, या अन्य कार्यक्षमता जैसे डेटा ऑडिटिंग (यानी, लॉगिंग परिवर्तन) या किसी भी कॉलिंग एप्लिकेशन से स्वतंत्र रूप से एक सारांश तालिका बनाए रखने के लिए डीएमएल कोड का लगातार निष्पादन सुनिश्चित करता है। ट्रिगर्स और संग्रहीत कार्यों का एक अन्य सामान्य उपयोग विचारों को लिखने योग्य बनाना है, अर्थात, जटिल दृश्यों पर इन्सर्ट और/या अपडेट को सक्षम करना या कुछ कॉलम डेटा को अनधिकृत संशोधन से बचाना है। इसके अतिरिक्त, एप्लिकेशन कोड के बजाय सर्वर पर संसाधित डेटा नेटवर्क को पार नहीं करता है, इसलिए डेटा के छिपने के जोखिम के साथ-साथ नेटवर्क की भीड़ में कमी का कुछ कम जोखिम होता है। इसके अलावा, PostgreSQL में संग्रहीत कार्यों को सत्र उपयोगकर्ता की तुलना में उच्च विशेषाधिकार स्तर पर कोड निष्पादित करने के लिए कॉन्फ़िगर किया जा सकता है, जो कुछ शक्तिशाली क्षमताओं को स्वीकार करता है। हम कुछ उदाहरण बाद में करेंगे।

द केस अगेंस्ट ट्रिगर्स एंड स्टोर्ड फंक्शन्स

पोस्टग्रेएसक्यूएल सामान्य मेलिंग सूची पर कमेंट्री की समीक्षा ने ट्रिगर्स और संग्रहीत कार्यों के उपयोग के प्रति प्रतिकूल कुछ राय प्रकट की, जिनका मैं यहां पूर्णता के लिए उल्लेख करता हूं और आपको और आपकी टीम को आपके कार्यान्वयन के लिए पेशेवरों और विपक्षों को तौलने के लिए प्रोत्साहित करता हूं।

आपत्तियों के बीच, उदाहरण के लिए, यह धारणा थी कि संग्रहीत कार्यों को बनाए रखना आसान नहीं है, इस प्रकार उन्हें प्रबंधित करने के लिए डेटाबेस प्रशासन में परिष्कृत कौशल और ज्ञान के साथ एक अनुभवी व्यक्ति की आवश्यकता होती है। कुछ सॉफ्टवेयर पेशेवरों ने बताया है कि डेटाबेस सिस्टम पर कॉर्पोरेट परिवर्तन नियंत्रण आमतौर पर एप्लिकेशन कोड की तुलना में अधिक जोरदार होते हैं, ताकि यदि डेटाबेस के भीतर व्यावसायिक नियम या अन्य तर्क लागू किए जाते हैं, तो आवश्यकताएँ विकसित होने पर परिवर्तन करना निषेधात्मक रूप से बोझिल है। एक अन्य दृष्टिकोण ट्रिगर्स को किसी अन्य क्रिया के अप्रत्याशित दुष्प्रभाव के रूप में मानता है और जैसे, अस्पष्ट हो सकता है, आसानी से छूट सकता है, डीबग करना मुश्किल हो सकता है, और बनाए रखने में निराशा होती है और इसलिए आमतौर पर अंतिम विकल्प होना चाहिए, न कि पहला।

इन आपत्तियों में कुछ योग्यता हो सकती है, लेकिन यदि आप इसके बारे में सोचते हैं, तो डेटा एक मूल्यवान संपत्ति है और इसलिए आप वास्तव में एक कुशल और अनुभवी व्यक्ति या टीम चाहते हैं जो किसी कॉर्पोरेट या सरकारी संगठन में आरडीबीएमएस के लिए जिम्मेदार हो, और इसी तरह, बदलें नियंत्रण बोर्ड रिकॉर्ड की सूचना प्रणाली के लिए स्थायी रखरखाव का एक सिद्ध घटक है, और एक व्यक्ति का दुष्प्रभाव दूसरे की शक्तिशाली सुविधा है, जो इस लेख के संतुलन के लिए अपनाया गया दृष्टिकोण है।

एक ट्रिगर घोषित करना

आइए नट और बोल्ट सीखने के बारे में जानें। ट्रिगर घोषित करने के लिए सामान्य डीडीएल सिंटैक्स में कई विकल्प उपलब्ध हैं, और सभी संभावित क्रमपरिवर्तनों का इलाज करने में एक महत्वपूर्ण समय लगेगा, इसलिए संक्षिप्तता के लिए हम उदाहरणों में उनमें से केवल एक न्यूनतम-आवश्यक उपसमुच्चय के बारे में बात करेंगे। इस संक्षिप्त सिंटैक्स का उपयोग करके अनुसरण करें:

CREATE TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }
    ON table_name
    FOR EACH ROW EXECUTE PROCEDURE function_name()

where event can be one of:

    INSERT
    UPDATE [ OF column_name [, ... ] ]
    DELETE
    TRUNCATE

नाम . के अलावा आवश्यक विन्यास योग्य तत्व कब . हैं , क्यों , कहां , और क्या , यानी, ट्रिगर कोड के लिए ट्रिगरिंग एक्शन (कब), विशिष्ट प्रकार के ट्रिगरिंग डीएमएल स्टेटमेंट (क्यों), एक्ट-ऑन टेबल या टेबल (कहां), और निष्पादित करने के लिए संग्रहीत फ़ंक्शन कोड के सापेक्ष ट्रिगर कोड को लागू करने का समय (क्या)।

फ़ंक्शन घोषित करना

उपरोक्त ट्रिगर घोषणा के लिए फ़ंक्शन नाम के विनिर्देश की आवश्यकता होती है, इसलिए तकनीकी रूप से ट्रिगर घोषणा DDL को तब तक निष्पादित नहीं किया जा सकता है जब तक कि ट्रिगर फ़ंक्शन को पहले परिभाषित नहीं किया जाता है। फ़ंक्शन घोषणा के लिए सामान्य डीडीएल सिंटैक्स में भी कई विकल्प होते हैं, इसलिए प्रबंधनीयता के लिए हम यहां अपने उद्देश्यों के लिए इस न्यूनतम पर्याप्त सिंटैक्स का उपयोग करेंगे:

CREATE [ OR REPLACE ] FUNCTION
    name () RETURNS TRIGGER
  { LANGUAGE lang_name
    | SECURITY DEFINER
    | SET configuration_parameter { TO value | = value | FROM CURRENT }
    | AS 'definition'
  }...

एक ट्रिगर फ़ंक्शन कोई पैरामीटर नहीं लेता है, और वापसी प्रकार TRIGGER होना चाहिए। नीचे दिए गए उदाहरणों में हम वैकल्पिक संशोधक के बारे में बात करेंगे।

ट्रिगर और कार्यों के लिए नामकरण योजना

सम्मानित कंप्यूटर वैज्ञानिक फिल कार्लटन को यह घोषित करने के लिए जिम्मेदार ठहराया गया है (यहां संक्षिप्त रूप में) कि चीजों का नामकरण सॉफ्टवेयर टीमों के लिए सबसे बड़ी चुनौतियों में से एक है। मैं यहां एक उपयोग में आसान ट्रिगर और संग्रहीत फ़ंक्शन नामकरण सम्मेलन प्रस्तुत करने जा रहा हूं जिसने मुझे अच्छी तरह से सेवा दी है और आपको इसे अपने स्वयं के आरडीबीएमएस परियोजनाओं के लिए अपनाने पर विचार करने के लिए प्रोत्साहित किया है। इस आलेख के उदाहरणों में नामकरण योजना संबंधित तालिका नाम का उपयोग करने के एक पैटर्न का पालन करती है जो एक संक्षिप्त नाम के साथ प्रत्ययित होता है जो घोषित ट्रिगर को दर्शाता है कब और क्यों विशेषताएँ:पहला प्रत्यय अक्षर या तो "बी", "ए", या "आई" ("पहले", "बाद" या "के बजाय") होगा, अगला "आई" का एक या अधिक होगा , "यू", "डी", या "टी" ("इन्सर्ट", "अपडेट", "डिलीट", या "ट्रंकेट" के लिए), और अंतिम अक्षर ट्रिगर के लिए सिर्फ एक "टी" है। (मैं नियमों के लिए एक समान नामकरण परंपरा का उपयोग करता हूं, और उस स्थिति में अंतिम अक्षर "r" है)। तो उदाहरण के लिए, "my_table" नामक तालिका के लिए विभिन्न न्यूनतम ट्रिगर घोषणा विशेषता संयोजन होंगे:

|-------------+-------------+-----------+---------------+-----------------|
|  TABLE NAME |  WHEN       |  WHY      |  TRIGGER NAME |  FUNCTION NAME  |
|-------------+-------------+-----------+---------------+-----------------|
|  my_table   |  BEFORE     |  INSERT   |  my_table_bit |  my_table_bit   |
|  my_table   |  BEFORE     |  UPDATE   |  my_table_but |  my_table_but   |
|  my_table   |  BEFORE     |  DELETE   |  my_table_bdt |  my_table_bdt   |
|  my_table   |  BEFORE     |  TRUNCATE |  my_table_btt |  my_table_btt   |
|  my_table   |  AFTER      |  INSERT   |  my_table_ait |  my_table_ait   |
|  my_table   |  AFTER      |  UPDATE   |  my_table_aut |  my_table_aut   |
|  my_table   |  AFTER      |  DELETE   |  my_table_adt |  my_table_adt   |
|  my_table   |  AFTER      |  TRUNCATE |  my_table_att |  my_table_att   |
|  my_table   |  INSTEAD OF |  INSERT   |  my_table_iit |  my_table_iit   |
|  my_table   |  INSTEAD OF |  UPDATE   |  my_table_iut |  my_table_iut   |
|  my_table   |  INSTEAD OF |  DELETE   |  my_table_idt |  my_table_idt   |
|  my_table   |  INSTEAD OF |  TRUNCATE |  my_table_itt |  my_table_itt   |
|-------------+-------------+-----------+---------------+-----------------|

ट्रिगर और संबंधित संग्रहीत फ़ंक्शन दोनों के लिए सटीक समान नाम का उपयोग किया जा सकता है, जो PostgreSQL में पूरी तरह से अनुमेय है क्योंकि RDBMS संबंधित उद्देश्यों से अलग-अलग ट्रिगर्स और संग्रहीत कार्यों का ट्रैक रखता है, और जिस संदर्भ में आइटम नाम का उपयोग किया जाता है वह बनाता है स्पष्ट करें कि नाम किस आइटम को संदर्भित करता है।

तो उदाहरण के लिए, ऊपर दी गई तालिका से पहली पंक्ति के परिदृश्य के अनुरूप एक ट्रिगर घोषणा को इस रूप में कार्यान्वित किया जाएगा

CREATE TRIGGER my_table_bit 
    BEFORE INSERT
    ON my_table
    FOR EACH ROW EXECUTE PROCEDURE my_table_bit();

उस स्थिति में जब कई क्यों . के साथ एक ट्रिगर घोषित किया जाता है विशेषताएँ, बस प्रत्यय को उचित रूप से विस्तृत करें, उदाहरण के लिए, सम्मिलित करें या अपडेट करें के लिए ट्रिगर, उपरोक्त बन जाएगा

CREATE TRIGGER my_table_biut 
    BEFORE INSERT OR UPDATE
    ON my_table
    FOR EACH ROW EXECUTE PROCEDURE my_table_biut();

मुझे पहले से ही कुछ कोड दिखाएं!

आइए इसे वास्तविक बनाएं। हम एक साधारण उदाहरण से शुरू करेंगे और फिर आगे की विशेषताओं को स्पष्ट करने के लिए उस पर विस्तार करेंगे। ट्रिगर डीडीएल स्टेटमेंट को पहले से मौजूद फ़ंक्शन की आवश्यकता होती है, जैसा कि उल्लेख किया गया है, और एक तालिका भी है जिस पर कार्य करना है, इसलिए पहले हमें काम करने के लिए एक टेबल की आवश्यकता है। उदाहरण के लिए मान लें कि हमें मूल खाता पहचान डेटा संग्रहीत करने की आवश्यकता है

CREATE TABLE person (
    login_name varchar(9) not null primary key,
    display_name text
);

कुछ डेटा अखंडता प्रवर्तन को उचित कॉलम डीडीएल के साथ नियंत्रित किया जा सकता है, जैसे इस मामले में एक आवश्यकता है कि लॉगिन_नाम मौजूद है और नौ वर्णों से अधिक लंबा नहीं होना चाहिए। एक NULL मान या login_name का बहुत लंबा मान डालने का प्रयास विफल हो जाता है और सार्थक त्रुटि संदेशों की रिपोर्ट करता है:

INSERT INTO person VALUES (NULL, 'Felonious Erroneous');
ERROR:  null value in column "login_name" violates not-null constraint
DETAIL:  Failing row contains (null, Felonious Erroneous).

INSERT INTO person VALUES ('atoolongusername', 'Felonious Erroneous');
ERROR:  value too long for type character varying(9)

अन्य प्रवर्तनों को चेक बाधाओं के साथ नियंत्रित किया जा सकता है, जैसे कि न्यूनतम लंबाई की आवश्यकता और कुछ वर्णों को अस्वीकार करना:

ALTER TABLE person 
    ADD CONSTRAINT PERSON_LOGIN_NAME_NON_NULL 
    CHECK (LENGTH(login_name) > 0);

ALTER TABLE person 
    ADD CONSTRAINT person_login_name_no_space 
    CHECK (POSITION(' ' IN login_name) = 0);

INSERT INTO person VALUES ('', 'Felonious Erroneous');
ERROR:  new row for relation "person" violates check constraint "person_login_name_non_null"
DETAIL:  Failing row contains (, Felonious Erroneous).

INSERT INTO person VALUES ('space man', 'Major Tom');
ERROR:  new row for relation "person" violates check constraint "person_login_name_no_space"
DETAIL:  Failing row contains (space man, Major Tom).

लेकिन ध्यान दें कि त्रुटि संदेश पहले की तरह पूरी तरह से सूचनात्मक नहीं है, केवल उतना ही संदेश देना जितना कि एक सार्थक व्याख्यात्मक पाठ संदेश के बजाय ट्रिगर नाम में एन्कोड किया गया है। इसके बजाय किसी संग्रहीत फ़ंक्शन में चेक तर्क को लागू करके, आप अधिक उपयोगी टेक्स्ट संदेश उत्सर्जित करने के लिए अपवाद का उपयोग कर सकते हैं। साथ ही, चेक बाधा अभिव्यक्तियों में उपश्रेणियाँ नहीं हो सकती हैं और न ही वर्तमान पंक्ति के स्तंभों के अलावा अन्य चरों का संदर्भ नहीं है और न ही अन्य डेटाबेस तालिकाएँ।

तो चलिए चेक बाधाओं को छोड़ देते हैं

ALTER TABLE PERSON DROP CONSTRAINT person_login_name_no_space;
ALTER TABLE PERSON DROP CONSTRAINT person_login_name_non_null;

और ट्रिगर और संग्रहीत कार्यों के साथ आगे बढ़ें।

मुझे कुछ और कोड दिखाएं

हमारे पास एक टेबल है। डीडीएल फ़ंक्शन पर चलते हुए, हम एक खाली-शरीर वाले फ़ंक्शन को परिभाषित करते हैं, जिसे हम बाद में विशिष्ट कोड के साथ भर सकते हैं:

CREATE OR REPLACE FUNCTION person_bit() 
    RETURNS TRIGGER
    SET SCHEMA 'public'
    LANGUAGE plpgsql
    SET search_path = public
    AS '
    BEGIN
    END;
    ';

यह हमें अंततः तालिका और फ़ंक्शन को जोड़ने वाले ट्रिगर डीडीएल तक पहुंचने की अनुमति देता है ताकि हम कुछ उदाहरण कर सकें:

CREATE TRIGGER person_bit 
    BEFORE INSERT ON person
    FOR EACH ROW EXECUTE PROCEDURE person_bit();

PostgreSQL विभिन्न भाषाओं में संग्रहीत कार्यों को लिखने की अनुमति देता है। इस मामले में और निम्नलिखित उदाहरणों में, हम पीएल/पीजीएसक्यूएल भाषा में कार्यों की रचना कर रहे हैं जो विशेष रूप से पोस्टग्रेएसक्यूएल के लिए डिज़ाइन किया गया है और पोस्टग्रेएसक्यूएल आरडीबीएमएस के सभी डेटा प्रकारों, ऑपरेटरों और कार्यों के उपयोग का समर्थन करता है। SET SCHEMA विकल्प स्कीमा खोज पथ सेट करता है जिसका उपयोग फ़ंक्शन निष्पादन की अवधि के लिए किया जाएगा। प्रत्येक फ़ंक्शन के लिए खोज पथ सेट करना एक अच्छा अभ्यास है, क्योंकि यह डेटाबेस ऑब्जेक्ट को स्कीमा नाम के साथ उपसर्ग करने से बचाता है और खोज पथ से संबंधित कुछ कमजोरियों से बचाता है।

उदाहरण 0 - डेटा सत्यापन

पहले उदाहरण के रूप में, आइए पहले की जाँचों को लागू करें, लेकिन अधिक मानव-अनुकूल संदेश के साथ।

CREATE OR REPLACE FUNCTION person_bit()
    RETURNS TRIGGER
    SET SCHEMA 'public'
    LANGUAGE plpgsql
    AS $$
    BEGIN
    IF LENGTH(NEW.login_name) = 0 THEN
        RAISE EXCEPTION 'Login name must not be empty.';
    END IF;

    IF POSITION(' ' IN NEW.login_name) > 0 THEN
        RAISE EXCEPTION 'Login name must not include white space.';
    END IF;
    RETURN NEW;
    END;
    $$;

"नया" क्वालीफायर सम्मिलित किए जाने वाले डेटा की पंक्ति का संदर्भ है। यह एक ट्रिगर फ़ंक्शन के भीतर उपलब्ध कई विशेष चरों में से एक है। हम नीचे कुछ अन्य का परिचय देंगे। ध्यान दें, PostgreSQL एकल उद्धरण चिह्नों के प्रतिस्थापन की अनुमति देता है जो अन्य सीमांककों के साथ फ़ंक्शन बॉडी को परिसीमित करता है, इस मामले में सीमांकक के रूप में दोहरे डॉलर के संकेतों का उपयोग करने के एक सामान्य सम्मेलन के बाद, क्योंकि फ़ंक्शन बॉडी में एकल उद्धरण वर्ण शामिल हैं। ट्रिगर फ़ंक्शंस को या तो नई पंक्ति को सम्मिलित करने के लिए या NULL को चुपचाप क्रिया को निरस्त करने के लिए वापस करके बाहर निकलना चाहिए।

वही डालने के प्रयास अपेक्षित रूप से विफल हो जाते हैं, लेकिन अब मैत्रीपूर्ण संदेश के साथ:

INSERT INTO person VALUES ('', 'Felonious Erroneous');
ERROR:  Login name must not be empty.

INSERT INTO person VALUES ('space man', 'Major Tom');
ERROR:  Login name must not include white space.

उदाहरण 1 - ऑडिट लॉगिंग

संग्रहीत कार्यों के साथ, हमारे पास व्यापक अक्षांश है कि आह्वान कोड क्या करता है, जिसमें अन्य तालिकाओं को संदर्भित करना शामिल है (जो चेक बाधाओं के साथ संभव नहीं है)। एक अधिक जटिल उदाहरण के रूप में हम एक ऑडिट तालिका के कार्यान्वयन के माध्यम से चलेंगे, अर्थात, एक अलग तालिका में, एक मुख्य तालिका में सम्मिलित, अद्यतन और हटाए जाने के रिकॉर्ड को बनाए रखना। ऑडिट तालिका में आम तौर पर मुख्य तालिका के समान गुण होते हैं, जिनका उपयोग परिवर्तित मूल्यों को रिकॉर्ड करने के लिए किया जाता है, साथ ही परिवर्तन करने के लिए निष्पादित ऑपरेशन को रिकॉर्ड करने के लिए अतिरिक्त विशेषताओं के साथ-साथ लेनदेन टाइमस्टैम्प, और उपयोगकर्ता का रिकॉर्ड बनाने के लिए उपयोग किया जाता है। परिवर्तन:

CREATE TABLE person_audit (
    login_name varchar(9) not null,
    display_name text,
    operation varchar,
    effective_at timestamp not null default now(),
    userid name not null default session_user
);

इस मामले में, ऑडिटिंग को लागू करना बहुत आसान है, हम ऑडिट टेबल इंसर्ट को प्रभावित करने के लिए डीएमएल को शामिल करने के लिए मौजूदा ट्रिगर फ़ंक्शन को संशोधित करते हैं, और फिर ट्रिगर को अपडेट के साथ-साथ इंसर्ट पर फिर से परिभाषित करते हैं। ध्यान दें कि हमने ट्रिगर फ़ंक्शन नाम प्रत्यय को "बायट" में नहीं बदलने के लिए चुना है, लेकिन यदि प्रारंभिक डिज़ाइन समय में ऑडिट कार्यक्षमता एक ज्ञात आवश्यकता थी, तो वह नाम इस्तेमाल किया जाएगा:

CREATE OR REPLACE FUNCTION person_bit()
    RETURNS TRIGGER
    SET SCHEMA 'public'
    LANGUAGE plpgsql
    AS $$
    BEGIN
    IF LENGTH(NEW.login_name) = 0 THEN
        RAISE EXCEPTION 'Login name must not be empty.';
    END IF;

    IF POSITION(' ' IN NEW.login_name) > 0 THEN
        RAISE EXCEPTION 'Login name must not include white space.';
    END IF;

    -- New code to record audits

    INSERT INTO person_audit (login_name, display_name, operation) 
        VALUES (NEW.login_name, NEW.display_name, TG_OP);

    RETURN NEW;
    END;
    $$;


DROP TRIGGER person_bit ON person;

CREATE TRIGGER person_biut 
    BEFORE INSERT OR UPDATE ON person
    FOR EACH ROW EXECUTE PROCEDURE person_bit();

ध्यान दें कि हमने एक और विशेष चर "TG_OP" पेश किया है, जिसे सिस्टम DML ऑपरेशन की पहचान करने के लिए सेट करता है, जिसने ट्रिगर को क्रमशः "TRUNCATE" के "INSERT", "UPDATE", "DELETE" के रूप में सक्रिय किया।

हमें इन्सर्ट और अपडेट से डिलीट को अलग से हैंडल करने की जरूरत है क्योंकि एट्रीब्यूट वेलिडेशन टेस्ट फालतू हैं और क्योंकि डिलीट से पहले में एंट्री पर नया स्पेशल वैल्यू परिभाषित नहीं है। ट्रिगर फ़ंक्शन और इसलिए संबंधित संग्रहीत फ़ंक्शन और ट्रिगर को परिभाषित करें:

CREATE OR REPLACE FUNCTION person_bdt()
    RETURNS TRIGGER
    SET SCHEMA 'public'
    LANGUAGE plpgsql
    AS $$
    BEGIN

    -- Record deletion in audit table

    INSERT INTO person_audit (login_name, display_name, operation) 
      VALUES (OLD.login_name, OLD.display_name, TG_OP);

    RETURN OLD;
    END;
    $$;
        
CREATE TRIGGER person_bdt 
    BEFORE DELETE ON person
    FOR EACH ROW EXECUTE PROCEDURE person_bdt();

उस पंक्ति के संदर्भ के रूप में OLD विशेष मान के उपयोग पर ध्यान दें, जो हटाए जाने वाली है, अर्थात, पंक्ति पहले के रूप में मौजूद है हटाना होता है।

हम कार्यक्षमता का परीक्षण करने के लिए कुछ इंसर्ट करते हैं और पुष्टि करते हैं कि ऑडिट टेबल में इंसर्ट का रिकॉर्ड शामिल है:

INSERT INTO person VALUES ('dfunny', 'Doug Funny');
INSERT INTO person VALUES ('pmayo', 'Patti Mayonnaise');

SELECT * FROM person;
 login_name |   display_name   
------------+------------------
 dfunny     | Doug Funny
 pmayo      | Patti Mayonnaise
(2 rows)

SELECT * FROM person_audit;
 login_name |   display_name   | operation |        effective_at        |  userid  
------------+------------------+-----------+----------------------------+----------
 dfunny     | Doug Funny       | INSERT    | 2018-05-26 18:48:07.6903   | postgres
 pmayo      | Patti Mayonnaise | INSERT    | 2018-05-26 18:48:07.698623 | postgres
(2 rows)

फिर हम एक पंक्ति में अपडेट करते हैं और पुष्टि करते हैं कि ऑडिट तालिका में डेटा रिकॉर्ड प्रदर्शन नामों में से किसी एक में मध्य नाम जोड़ने वाले परिवर्तन का रिकॉर्ड शामिल है:

UPDATE person SET display_name = 'Doug Yancey Funny' WHERE login_name = 'dfunny';

SELECT * FROM person;
 login_name |   display_name    
------------+-------------------
 pmayo      | Patti Mayonnaise
 dfunny     | Doug Yancey Funny
(2 rows)

SELECT * FROM person_audit ORDER BY effective_at;
 login_name |   display_name    | operation |        effective_at        |  userid  
------------+-------------------+-----------+----------------------------+----------
 dfunny     | Doug Funny        | INSERT    | 2018-05-26 18:48:07.6903   | postgres
 pmayo      | Patti Mayonnaise  | INSERT    | 2018-05-26 18:48:07.698623 | postgres
 dfunny     | Doug Yancey Funny | UPDATE    | 2018-05-26 18:48:07.707284 | postgres
(3 rows)

और अंत में हम डिलीट फंक्शनलिटी का प्रयोग करते हैं और पुष्टि करते हैं कि ऑडिट टेबल में वह रिकॉर्ड भी शामिल है:

DELETE FROM person WHERE login_name = 'pmayo';

SELECT * FROM person;
 login_name |   display_name    
------------+-------------------
 dfunny     | Doug Yancey Funny
(1 row)

SELECT * FROM person_audit ORDER BY effective_at;
 login_name |   display_name    | operation |        effective_at        |  userid  
------------+-------------------+-----------+----------------------------+----------
 dfunny     | Doug Funny        | INSERT    | 2018-05-27 08:13:22.747226 | postgres
 pmayo      | Patti Mayonnaise  | INSERT    | 2018-05-27 08:13:22.74839  | postgres
 dfunny     | Doug Yancey Funny | UPDATE    | 2018-05-27 08:13:22.749495 | postgres
 pmayo      | Patti Mayonnaise  | DELETE    | 2018-05-27 08:13:22.753425 | postgres
(4 rows)

उदाहरण 2 - व्युत्पन्न मान

आइए इसे एक कदम आगे बढ़ाते हैं और कल्पना करते हैं कि हम प्रत्येक पंक्ति के भीतर कुछ फ्री-फॉर्म टेक्स्ट दस्तावेज़ संग्रहीत करना चाहते हैं, एक सादा-पाठ स्वरूपित रेज़्यूमे या कॉन्फ़्रेंस पेपर या मनोरंजन चरित्र सार कहें, और हम शक्तिशाली पूर्ण-पाठ खोज के उपयोग का समर्थन करना चाहते हैं इन फ़्री-फ़ॉर्म टेक्स्ट दस्तावेज़ों पर PostgreSQL की क्षमताएँ।

हम पहले दस्तावेज़ के भंडारण और संबंधित पाठ खोज वेक्टर को मुख्य तालिका में समर्थन देने के लिए दो विशेषताएँ जोड़ते हैं। चूंकि टेक्स्ट सर्च वेक्टर प्रति पंक्ति के आधार पर व्युत्पन्न होता है, इसलिए इसे ऑडिट टेबल में स्टोर करने का कोई मतलब नहीं है, चाहे हम संबंधित ऑडिट टेबल में डॉक्यूमेंट स्टोरेज कॉलम जोड़ें:

ALTER TABLE person ADD COLUMN abstract TEXT;
ALTER TABLE person ADD COLUMN ts_abstract TSVECTOR;

ALTER TABLE person_audit ADD COLUMN abstract TEXT;

फिर हम इन नई विशेषताओं को संसाधित करने के लिए ट्रिगर फ़ंक्शन को संशोधित करते हैं। प्लेन-टेक्स्ट कॉलम को अन्य उपयोगकर्ता द्वारा दर्ज किए गए डेटा की तरह ही संभाला जाता है, लेकिन टेक्स्ट सर्च वेक्टर एक व्युत्पन्न मान है और इसलिए इसे एक फ़ंक्शन कॉल द्वारा नियंत्रित किया जाता है जो कुशल खोज के लिए दस्तावेज़ टेक्स्ट को एक tsvector डेटा प्रकार में कम कर देता है।

CREATE OR REPLACE FUNCTION person_bit()
    RETURNS TRIGGER
    LANGUAGE plpgsql
    SET SCHEMA 'public'
    AS $$
    BEGIN
    IF LENGTH(NEW.login_name) = 0 THEN
        RAISE EXCEPTION 'Login name must not be empty.';
    END IF;

    IF POSITION(' ' IN NEW.login_name) > 0 THEN
        RAISE EXCEPTION 'Login name must not include white space.';
    END IF;

    -- Modified audit code to include text abstract

    INSERT INTO person_audit (login_name, display_name, operation, abstract) 
        VALUES (NEW.login_name, NEW.display_name, TG_OP, NEW.abstract);

    -- New code to reduce text to text-search vector

    SELECT to_tsvector(NEW.abstract) INTO NEW.ts_abstract;

    RETURN NEW;
    END;
    $$;

एक परीक्षण के रूप में, हम विकिपीडिया से कुछ विवरण पाठ के साथ मौजूदा पंक्ति को अद्यतन करते हैं:

UPDATE person SET abstract = 'Doug is depicted as an introverted, quiet, insecure and gullible 11 (later 12) year old boy who wants to fit in with the crowd.' WHERE login_name = 'dfunny';

और फिर पुष्टि करें कि टेक्स्ट सर्च वेक्टर प्रोसेसिंग सफल रही:

SELECT login_name, ts_abstract  FROM person;
 login_name |                                                                                                                ts_abstract                                                                                                                
------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 dfunny     | '11':11 '12':13 'an':5 'and':9 'as':4 'boy':16 'crowd':24 'depicted':3 'doug':1 'fit':20 'gullible':10 'in':21 'insecure':8 'introverted':6 'is':2 'later':12 'old':15 'quiet':7 'the':23 'to':19 'wants':18 'who':17 'with':22 'year':14
(1 row)

उदाहरण 3 - ट्रिगर और दृश्य

उपरोक्त उदाहरण से व्युत्पन्न पाठ खोज वेक्टर मानव उपभोग के लिए अभिप्रेत नहीं है, अर्थात, यह उपयोगकर्ता द्वारा दर्ज नहीं है, और हम कभी भी अंतिम उपयोगकर्ता के लिए मूल्य प्रस्तुत करने की अपेक्षा नहीं करते हैं। यदि कोई उपयोगकर्ता ts_abstract कॉलम के लिए कोई मान डालने का प्रयास करता है, तो प्रदान की गई किसी भी चीज़ को त्याग दिया जाएगा और ट्रिगर फ़ंक्शन में आंतरिक रूप से व्युत्पन्न मान के साथ बदल दिया जाएगा, इसलिए हमारे पास खोज कॉर्पस को जहर देने से सुरक्षा है। कॉलम को पूरी तरह से छिपाने के लिए, हम एक संक्षिप्त दृश्य को परिभाषित कर सकते हैं जिसमें वह विशेषता शामिल नहीं है, लेकिन हमें अभी भी अंतर्निहित तालिका पर ट्रिगर गतिविधि का लाभ मिलता है:

CREATE VIEW abridged_person AS SELECT login_name, display_name, abstract FROM person;

एक साधारण दृश्य के लिए, PostgreSQL स्वचालित रूप से इसे लिखने योग्य बनाता है, इसलिए हमें डेटा को सफलतापूर्वक डालने या अपडेट करने के लिए कुछ और करने की आवश्यकता नहीं है। जब डीएमएल अंतर्निहित तालिका पर प्रभाव डालता है, तो ट्रिगर सक्रिय हो जाते हैं जैसे कि कथन सीधे तालिका पर लागू किया गया था, इसलिए हम अभी भी व्यक्ति तालिका के खोज वेक्टर कॉलम को पॉप्युलेट करने के साथ-साथ संलग्न करने वाली पृष्ठभूमि में दोनों पाठ खोज समर्थन प्राप्त करते हैं। जानकारी को ऑडिट टेबल में बदलें:

INSERT INTO abridged_person VALUES ('skeeter', 'Mosquito Valentine', 'Skeeter is Doug''s best friend. He is famous in both series for the honking sounds he frequently makes.');


SELECT login_name, ts_abstract FROM person WHERE login_name = 'skeeter';
 login_name |                                                                                   ts_abstract                                                                                    
------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 skeeter    | 'best':5 'both':11 'doug':3 'famous':9 'for':13 'frequently':18 'friend':6 'he':7,17 'honking':15 'in':10 'is':2,8 'makes':19 's':4 'series':12 'skeeter':1 'sounds':16 'the':14
(1 row)


SELECT login_name, display_name, operation, userid FROM person_audit ORDER BY effective_at;
 login_name |    display_name    | operation |  userid  
------------+--------------------+-----------+----------
 dfunny     | Doug Funny         | INSERT    | postgres
 pmayo      | Patti Mayonnaise   | INSERT    | postgres
 dfunny     | Doug Yancey Funny  | UPDATE    | postgres
 pmayo      | Patti Mayonnaise   | DELETE    | postgres
 dfunny     | Doug Yancey Funny  | UPDATE    | postgres
 skeeter    | Mosquito Valentine | INSERT    | postgres
(6 rows)

अधिक जटिल दृश्यों के लिए जो स्वचालित रूप से लिखने योग्य होने की आवश्यकताओं को पूरा नहीं करते हैं, या तो नियम प्रणाली या बजाय ट्रिगर लिखने और हटाने का समर्थन करने का काम कर सकते हैं।

उदाहरण 4 - सारांश मान

आइए आगे अलंकृत करें और उस परिदृश्य का इलाज करें जहां किसी प्रकार की लेनदेन तालिका है। यह काम किए गए घंटों का रिकॉर्ड हो सकता है, गोदाम या खुदरा स्टॉक की सूची में वृद्धि और कटौती, या शायद प्रत्येक व्यक्ति के लिए डेबिट और क्रेडिट के साथ एक चेक रजिस्टर हो सकता है:

CREATE TABLE transaction (
    login_name character varying(9) NOT NULL,
    post_date date,
    description character varying,
    debit money,
    credit money,
    FOREIGN KEY (login_name) REFERENCES person (login_name)
);

और मान लें कि लेन-देन के इतिहास को बनाए रखना महत्वपूर्ण है, व्यावसायिक नियम किसी भी लेन-देन के विवरण के बजाय आवेदन प्रसंस्करण में शुद्ध शेष राशि का उपयोग करते हैं। हर बार शेष राशि की आवश्यकता होने पर सभी लेन-देनों को जोड़कर शेष राशि को बार-बार पुनर्गणना करने से बचने के लिए, हम एक नया कॉलम जोड़कर और ट्रिगर और संग्रहीत फ़ंक्शन का उपयोग करके व्यक्ति तालिका में एक वर्तमान शेष राशि मान सकते हैं और बनाए रख सकते हैं। लेन-देन के रूप में शुद्ध शेष राशि डाली जाती है:

ALTER TABLE person ADD COLUMN balance MONEY DEFAULT 0;

CREATE FUNCTION transaction_bit() RETURNS trigger
    LANGUAGE plpgsql
    SET SCHEMA 'public'
    AS $$
    DECLARE
    newbalance money;
    BEGIN

    -- Update person account balance

    UPDATE person 
        SET balance = 
            balance + 
            COALESCE(NEW.debit, 0::money) - 
            COALESCE(NEW.credit, 0::money) 
        WHERE login_name = NEW.login_name
                RETURNING balance INTO newbalance;

    -- Data validation

    IF COALESCE(NEW.debit, 0::money) < 0::money THEN
        RAISE EXCEPTION 'Debit value must be non-negative';
    END IF;

    IF COALESCE(NEW.credit, 0::money) < 0::money THEN
        RAISE EXCEPTION 'Credit value must be non-negative';
    END IF;

    IF newbalance < 0::money THEN
        RAISE EXCEPTION 'Insufficient funds: %', NEW;
    END IF;

    RETURN NEW;
    END;
    $$;



CREATE TRIGGER transaction_bit 
      BEFORE INSERT ON transaction 
      FOR EACH ROW EXECUTE PROCEDURE transaction_bit();

डेबिट, क्रेडिट और बैलेंस मानों की गैर-नकारात्मकता को मान्य करने से पहले पहले संग्रहीत फ़ंक्शन में अपडेट करना अजीब लग सकता है, लेकिन डेटा सत्यापन के संदर्भ में ऑर्डर कोई फर्क नहीं पड़ता क्योंकि ट्रिगर फ़ंक्शन के शरीर को एक के रूप में निष्पादित किया जाता है। डेटाबेस लेनदेन, इसलिए यदि वे सत्यापन जांच विफल हो जाती है, तो अपवाद उठाए जाने पर संपूर्ण लेनदेन वापस ले लिया जाता है। पहले अद्यतन करने का लाभ यह है कि अद्यतन लेन-देन की अवधि के लिए प्रभावित पंक्ति को लॉक कर देता है और इसलिए उसी पंक्ति को अद्यतन करने का प्रयास करने वाला कोई अन्य सत्र तब तक अवरुद्ध रहता है जब तक कि वर्तमान लेनदेन पूरा नहीं हो जाता। आगे सत्यापन परीक्षण यह आश्वासन देता है कि परिणामी शेष राशि गैर-ऋणात्मक है, और अपवाद सूचना संदेश में एक चर शामिल हो सकता है, जो इस मामले में डिबगिंग के लिए आपत्तिजनक प्रयास की गई लेनदेन पंक्ति को वापस कर देगा।

यह प्रदर्शित करने के लिए कि यह वास्तव में काम करता है, यहां कुछ नमूना प्रविष्टियां और प्रत्येक चरण में अद्यतन शेष राशि दिखाने वाला चेक दिया गया है:

SELECT login_name, balance FROM person WHERE login_name = 'dfunny';
 login_name | balance 
------------+---------
 dfunny     |   $0.00
(1 row)

INSERT INTO transaction (login_name, post_date, description, credit, debit) VALUES ('dfunny', '2018-01-11', 'ACH CREDIT FROM: FINANCE AND ACCO ALLOTMENT : Direct Deposit', NULL, '$2,000.00');

SELECT login_name, balance FROM person WHERE login_name = 'dfunny';
 login_name |  balance  
------------+-----------
 dfunny     | $2,000.00
(1 row)
INSERT INTO transaction (login_name, post_date, description, credit, debit) VALUES ('dfunny', '2018-01-17', 'FOR:BGE PAYMENT ACH Withdrawal', '$2780.52', NULL);
ERROR:  Insufficient funds: (dfunny,2018-01-17,"FOR:BGE PAYMENT ACH Withdrawal",,"$2,780.52")

ध्यान दें कि अपर्याप्त धन पर उपरोक्त लेनदेन कैसे विफल हो जाता है, अर्थात, यह एक नकारात्मक शेष राशि उत्पन्न करेगा और सफलतापूर्वक वापस रोल करेगा। यह भी ध्यान दें कि हमने डिबगिंग के लिए त्रुटि संदेश में अतिरिक्त विवरण के रूप में नई विशेष चर के साथ पूरी पंक्ति लौटा दी है।

SELECT login_name, balance FROM person WHERE login_name = 'dfunny';
 login_name |  balance  
------------+-----------
 dfunny     | $2,000.00
(1 row)

INSERT INTO transaction (login_name, post_date, description, credit, debit) VALUES ('dfunny', '2018-01-17', 'FOR:BGE PAYMENT ACH Withdrawal', '$278.52', NULL);

SELECT login_name, balance FROM person WHERE login_name = 'dfunny';
 login_name |  balance  
------------+-----------
 dfunny     | $1,721.48
(1 row)

INSERT INTO transaction (login_name, post_date, description, credit, debit) VALUES ('dfunny', '2018-01-23', 'FOR: ANNE ARUNDEL ONLINE PMT ACH Withdrawal', '$35.29', NULL);

SELECT login_name, balance FROM person WHERE login_name = 'dfunny';
 login_name |  balance  
------------+-----------
 dfunny     | $1,686.19
(1 row)

उदाहरण 5 - ट्रिगर और दृश्य Redux

हालांकि, उपरोक्त कार्यान्वयन में एक समस्या है, और वह यह है कि कुछ भी दुर्भावनापूर्ण उपयोगकर्ता को पैसे छापने से नहीं रोकता है:

BEGIN;
UPDATE person SET balance = '1000000000.00';

SELECT login_name, balance FROM person WHERE login_name = 'dfunny';
 login_name |      balance      
------------+-------------------
 dfunny     | $1,000,000,000.00
(1 row)

ROLLBACK;

हमने अभी के लिए उपरोक्त चोरी को वापस ले लिया है और बैलेंस वैल्यू के अपडेट को रोकने के लिए एक ट्रिगर का उपयोग करके सुरक्षा में निर्माण करने का एक तरीका दिखाएंगे।

बैलेंस कॉलम को उजागर करने के लिए हम पहले संक्षिप्त दृश्य को पहले से बढ़ाते हैं:

CREATE OR REPLACE VIEW abridged_person AS
  SELECT login_name, display_name, abstract, balance FROM person;

यह स्पष्ट रूप से शेष राशि तक पढ़ने की अनुमति देता है, लेकिन यह अभी भी समस्या का समाधान नहीं करता है क्योंकि एकल तालिका पर आधारित इस तरह के सरल विचारों के लिए, PostgreSQL स्वचालित रूप से दृश्य को लिखने योग्य बनाता है:

BEGIN;
UPDATE abridged_person SET balance = '1000000000.00';
SELECT login_name, balance FROM abridged_person WHERE login_name = 'dfunny';
 login_name |      balance      
------------+-------------------
 dfunny     | $1,000,000,000.00
(1 row)

ROLLBACK;

We could use a rule, but to illustrate that triggers can be defined on views as well as tables, we will take the latter route and use an instead of update trigger on the view to block unwanted DML, preventing non-transactional changes to the balance value:

CREATE FUNCTION abridged_person_iut() RETURNS TRIGGER
    LANGUAGE plpgsql
    SET search_path TO public
    AS $$
    BEGIN

    -- Disallow non-transactional changes to balance

      NEW.balance = OLD.balance;
    RETURN NEW;
    END;
    $$;

CREATE TRIGGER abridged_person_iut
    INSTEAD OF UPDATE ON abridged_person
    FOR EACH ROW EXECUTE PROCEDURE abridged_person_iut();

The above instead of update trigger and stored procedure discards any attempted updates to the balance value and instead forces use of the value present in the database prior to the triggering update statement:

UPDATE abridged_person SET balance = '1000000000.00';

SELECT login_name, balance FROM abridged_person WHERE login_name = 'dfunny';
 login_name |  balance  
------------+-----------
 dfunny     | $1,686.19
(1 row)

which affords protection against un-auditable changes to the balance value.

आज श्वेतपत्र डाउनलोड करें क्लस्टरकंट्रोल के साथ पोस्टग्रेएसक्यूएल प्रबंधन और स्वचालन इस बारे में जानें कि पोस्टग्रेएसक्यूएल को तैनात करने, मॉनिटर करने, प्रबंधित करने और स्केल करने के लिए आपको क्या जानना चाहिए। श्वेतपत्र डाउनलोड करें

EXAMPLE 6 - Elevated Privileges

So far all the example code above has been executed at the database owner level by the postgres login role, so any of our anti-tampering efforts could be obviated… that’s just a fact of the database owner super-user privileges.

Our final example illustrates how triggers and stored functions can be used to allow the execution of code by a non-privileged user at a higher privilege than the logged in session user normally has by employing the SECURITY DEFINER attribute associated with stored functions.

First, we define a non-privileged login role, eve and confirm that upon instantiation there are no privileges:

CREATE USER eve;
\dp
                                  Access privileges
 Schema |      Name       | Type  | Access privileges | Column privileges | Policies 
--------+-----------------+-------+-------------------+-------------------+----------
 public | abridged_person | view  |                   |                   | 
 public | person          | table |                   |                   | 
 public | person_audit    | table |                   |                   | 
 public | transaction     | table |                   |                   | 
(4 rows)

We grant read, update, and create privileges on the abridged person view and read and create to the transaction table:

GRANT SELECT,INSERT, UPDATE ON abridged_person TO eve;
GRANT SELECT,INSERT ON transaction TO eve;
\dp
                                      Access privileges
 Schema |      Name       | Type  |     Access privileges     | Column privileges | Policies 
--------+-----------------+-------+---------------------------+-------------------+----------
 public | abridged_person | view  | postgres=arwdDxt/postgres+|                   | 
        |                 |       | eve=arw/postgres          |                   | 
 public | person          | table |                           |                   | 
 public | person_audit    | table |                           |                   | 
 public | transaction     | table | postgres=arwdDxt/postgres+|                   | 
        |                 |       | eve=ar/postgres           |                   | 
(4 rows)

By way of confirmation we see that eve is denied access to the person and person_audit tables:

SET SESSION AUTHORIZATION eve;

SELECT * FROM person;
ERROR:  permission denied for relation person

SELECT * from person_audit;
ERROR:  permission denied for relation person_audit

and that she does have appropriate read access to the abridged_person and transaction tables:

SELECT * FROM abridged_person;
 login_name |    display_name    |                                                            abstract                                                             |  balance  
------------+--------------------+---------------------------------------------------------------------------------------------------------------------------------+-----------
 skeeter    | Mosquito Valentine | Skeeter is Doug's best friend. He is famous in both series for the honking sounds he frequently makes.                          |     $0.00
 dfunny     | Doug Yancey Funny  | Doug is depicted as an introverted, quiet, insecure and gullible 11 (later 12) year old boy who wants to fit in with the crowd. | $1,686.19
(2 rows)

SELECT * FROM transaction;
 login_name | post_date  |                         description                          |   debit   | credit  
------------+------------+--------------------------------------------------------------+-----------+---------
 dfunny     | 2018-01-11 | ACH CREDIT FROM: FINANCE AND ACCO ALLOTMENT : Direct Deposit | $2,000.00 |        
 dfunny     | 2018-01-17 | FOR:BGE PAYMENT ACH Withdrawal                               |           | $278.52
 dfunny     | 2018-01-23 | FOR: ANNE ARUNDEL ONLINE PMT ACH Withdrawal                  |           |  $35.29
(3 rows)

However, even though she has write privilege on the transaction table, a transaction insert attempt fails due to lack of privilege on the person table.

SET SESSION AUTHORIZATION eve;

INSERT INTO transaction (login_name, post_date, description, credit, debit) VALUES ('dfunny', '2018-01-23', 'ACH CREDIT FROM: FINANCE AND ACCO ALLOTMENT : Direct Deposit', NULL, '$2,000.00');
ERROR:  permission denied for relation person
CONTEXT:  SQL statement "UPDATE person 
        SET balance = 
            balance + 
            COALESCE(NEW.debit, 0::money) - 
            COALESCE(NEW.credit, 0::money) 
        WHERE login_name = NEW.login_name"
PL/pgSQL function transaction_bit() line 6 at SQL statement

The error message context shows this hold up occurs when inside the trigger function DML to update the balance is invoked. The way around this need to deny Eve direct write access to the person table but still effect updates to the person balance in a controlled manner is to add the SECURITY DEFINER attribute to the stored function:

RESET SESSION AUTHORIZATION;
ALTER FUNCTION transaction_bit() SECURITY DEFINER;

SET SESSION AUTHORIZATION eve;

INSERT INTO transaction (login_name, post_date, description, credit, debit) VALUES ('dfunny', '2018-01-23', 'ACH CREDIT FROM: FINANCE AND ACCO ALLOTMENT : Direct Deposit', NULL, '$2,000.00');

SELECT * FROM transaction;
 login_name | post_date  |                         description                          |   debit   | credit  
------------+------------+--------------------------------------------------------------+-----------+---------
 dfunny     | 2018-01-11 | ACH CREDIT FROM: FINANCE AND ACCO ALLOTMENT : Direct Deposit | $2,000.00 |        
 dfunny     | 2018-01-17 | FOR:BGE PAYMENT ACH Withdrawal                               |           | $278.52
 dfunny     | 2018-01-23 | FOR: ANNE ARUNDEL ONLINE PMT ACH Withdrawal                  |           |  $35.29
 dfunny     | 2018-01-23 | ACH CREDIT FROM: FINANCE AND ACCO ALLOTMENT : Direct Deposit | $2,000.00 |        
(4 rows)

SELECT login_name, balance FROM abridged_person WHERE login_name = 'dfunny';
 login_name |  balance  
------------+-----------
 dfunny     | $3,686.19
(1 row)

Now the transaction insert succeeds because the stored function is executed with privilege level of its definer, i.e., the postgres user, which does have the appropriate write privilege on the person table.

निष्कर्ष

As lengthy as this article is, there’s still a lot more to say about triggers and stored functions. What we covered here is a basic introduction with a consideration of pros and cons of triggers and stored functions. We illustrated six use-case examples showing data validation, change logging, deriving values from inserted data, data hiding with simple updatable views, maintaining summary data in separate tables, and allowing safe invocation of code at elevated privilege. Look for a future article on using triggers and stored functions to prevent missing values in sequentially-incrementing (serial) columns.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. OSX 10.7 Lion में अपग्रेड करने के बाद Postgresql की मरम्मत

  2. PostgreSQL में ऐरे कैसे बनाएं

  3. PostgreSQL क्वेरी को दिन के हिसाब से गिनने/समूहित करने के लिए और बिना डेटा वाले दिनों को प्रदर्शित करने के लिए

  4. PostgreSQL पूर्ण पाठ खोज और ट्रिग्राम भ्रम

  5. कैसे IsFinite () PostgreSQL में काम करता है