2010-01-04 13 views
12

में EXECUTE का उपयोग कर जेनेरिक ट्रिगर से * मेरे पास पोस्टग्रेस "विभाजन" सुविधा का उपयोग करने वाली कई टेबल हैं। मैं प्रत्येक तालिका पर पंक्ति ट्रिगर के सामान्य से पहले एक सामान्य परिभाषित करना चाहता हूं जो 1) गतिशील रूप से विभाजन को मूल तालिका के विरुद्ध डालने चाहिए और 2) विभाजन के खिलाफ सम्मिलन को फिर से निष्पादित करना चाहिए।नया डालने। * पीएल/pgsql

कुछ की तरह:

CREATE OR REPLACE FUNCTION partition_insert_redirect() 
RETURNS trigger AS $BODY$ 
BEGIN 
    ... create the new partition and set up the redirect Rules ... 

    /* Redo the INSERT dynamically. The new RULE will redirect it to the child table */ 
    EXECUTE 'INSERT INTO ' || quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME) || 
      ' SELECT NEW.*' 
END 

लेकिन "नई" रिकॉर्ड निष्पादित एसक्यूएल अंदर दिखाई नहीं देता है। मैं इस काम को यथासंभव सरल कैसे बना सकता हूं?

एक विकल्प के रूप में, क्या मैं किसी भी तरह के नए रिकॉर्ड में फ़ील्ड पर फिर से शुरू कर सकता हूं?

मैं एक अस्थायी तालिका का उपयोग करने का सोचा है:

EXECUTE 'CREATE TEMPORARY TABLE new_row (LIKE ' || 
     quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME) || 
     ') ON COMMIT DROP'; 

INSERT INTO new_row SELECT NEW.*; 

EXECUTE 'INSERT INTO ' || quote_ident(TG_TABLE_SCHEMA) || '.' || quote_ident(TG_TABLE_NAME) || 
     ' SELECT * FROM new_row'; 
DROP TABLE new_row; 

लेकिन यह भी एक अस्थायी-मेज पर कैश की गई संदर्भ की वजह से काम नहीं करता है: Why do I get "relation with OID ##### does not exist" errors when accessing temporary tables in PL/PgSQL functions?

मैं Postgres उपयोग कर रहा हूँ 8.2 और मैं किसी अन्य संस्करण में नहीं बदल सकता।

संपादित करें:
रूप @alvherre ने कहा, यह शायद Postgres 8.4 निष्पादित ... का उपयोग वाक्य रचना के साथ किया जा सकता है। http://wiki.postgresql.org/wiki/PL/pgSQL_Dynamic_Triggers

+0

संबंधित बाद में सवाल समाधान के साथ Postgres 8.2 के लिए: http: // stackoverflow। कॉम/क्यू/751 9 044/9 3 9 860 –

+0

@ एरविन ब्रैंडस्टेटर: संबंधित प्रश्न में आपका समाधान नीचे दिए गए मेरे उत्तर में इस समस्या को हल करने के समान है, लेकिन इस मामले में, प्रत्येक बार तालिका में नया विभाजन जोड़ा जाने पर फ़ंक्शन को फिर से सम्मिलित किया जाना चाहिए या अन्यथा फ़ंक्शन अद्यतन विभाजन नियमों से अवगत नहीं होगा। –

उत्तर

1

मैं इस गतिशील रूप से एक समारोह है कि एक पैरामीटर के रूप नई पंक्ति को स्वीकार करता है संकलन के द्वारा काम करने के लिए प्राप्त करने के लिए प्रबंधित किया है: Postgres कार्यों बहुरूपी होते हैं

EXECUTE 'create or replace function partition_insert(r ' || TG_TABLE_NAME || ') RETURNS void AS $FUNC$' || 
      'BEGIN ' || 
       'insert into ' || TG_TABLE_NAME || ' SELECT r.*; ' || 
      'END $FUNC$ LANGUAGE plpgsql VOLATILE'; 
    PERFORM partition_insert(NEW); 

रूप में, यह इस ट्रिगर का उपयोग करता है प्रत्येक तालिका के लिए एक अलग समारोह उत्पन्न होगा।

बदसूरत क्लज होने के बावजूद, ऐसा लगता है कि यह काम करता है।

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

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

मैंने डेटाबेस को परिभाषित करते समय प्रत्येक आवश्यक तालिका-प्रकार पैरामीटर के लिए फ़ंक्शन के पूर्व-निर्माण स्टब संस्करणों द्वारा इसे हल किया।

+1

दिलचस्प चाल। कभी नहीं सुना। मेरा सुझाव है कि आप स्कीमा-टेबल नाम को अर्हता प्राप्त करें; ऐसा करने में असफल होने से यह दिलचस्प तरीकों से असफल हो जाता है जब आपके पास अलग-अलग स्कीमा पर एक ही नाम के साथ दो टेबल होते हैं। कैटलॉग को कम करने के लिए, संभवत: फ़ंक्शन पहले से मौजूद होने पर आप 'बनाना या स्थानांतरित करना' से बचना चाहते हैं। – alvherre

+0

@alvherre: आपने कभी इसके बारे में नहीं सुना क्योंकि मैंने इसे अभी बनाया है :) मुझे हर बार फ़ंक्शन को फिर से कंपाइल करने के लिए मजबूर होना पड़ता है क्योंकि मैंने क्रिएट नियम बदल दिया है ... INSERT INSTEAD नियमों पर और पुराने नियमों को कैश किया गया है कार्यक्रम। क्या यह सच है कि फ़ंक्शन को पुन: संकलित करने का एकमात्र तरीका बनाना या इसे पुनर्स्थापित करना है? –

+0

... और हाँ, मैं स्कीमा-नाम को अर्हता प्राप्त करता हूं। शोर को कम करने के लिए मैंने इसे उदाहरण से बाहर छोड़ा। –

19

पर एक उदाहरण देखें आप इसे 0 पास करने के लिए EXECUTE USING का उपयोग कर सकते हैं। आपका उदाहरण होगा

EXECUTE 'INSERT INTO ' || TG_RELID || '::regclass SELECT $1' USING NEW; 

(ध्यान दें कि मैं का उपयोग TG_RELID बजाय TG_TABLE_SCHEMA और TABLE_NAME क्योंकि यह उपयोग में आसान है, अगर गैरमानक है के साथ नगण्य की regclass लिए casted। लेकिन फिर भी, plpgsql वैसे भी गैरमानक है।)

+0

अरग - पोस्टग्रेज़ 8.4 में सटीक उपयोग नया है। मैंने आपको नोटिस नहीं दिया 8.2। – alvherre

+0

TG_RELID के बारे में अच्छी युक्ति। –

+2

यदि प्रतिबिंबित करने के बजाय तालिका नाम का उपयोग करना है, तो '$ 1 का चयन करें' होना चाहिए '$ 1. *' –

3

हां, आप निष्पादन का उपयोग कर सकते हैं ... 8.4 में उपयोग कर रहे हैं।उदाहरण के लिए:

EXECUTE 'INSERT INTO ' || table_name || ' SELECT $1.*' USING NEW;

कम संस्करणों में (मैं केवल 8.3 में परीक्षण किया है), तो आप उपयोग कर सकते हैं:

EXECUTE 'INSERT INTO ' || table_name || 
    ' SELECT (' || quote_literal(NEW) || '::' || TG_RELID::regclass || ').*'; 
संबंधित मुद्दे