PostgreSQL: C भाषा में एक्सटेंशन (फ़ंक्शन) का विकास

यह लेख कुछ साल पहले लिखा गया था, और यह नहीं पता था कि इसे कहाँ रखा जा सकता है, और फिर भूल गया।

पोस्टग्रेएसक्यूएल के लिए एक्सटेंशन (स्क्रिप्टेड) ​​भाषाओं की तुलना में एक्सटेंशन विकसित करते समय सी भाषा का उपयोग करने का अर्थ दो बिंदुओं: प्रदर्शन और कार्यक्षमता तक कम हो सकता है। लेकिन बस, सी में लिखा कोड बहुत तेजी से काम करेगा, उदाहरण के लिए, यदि फ़ंक्शन प्रत्येक रिकॉर्ड के लिए अनुरोध में एक लाख बार कहा जाता है। अधिक विशेष रूप से, कुछ PostgreSQL विशेषताएं C को छोड़कर सभी में नहीं की जा सकती हैं, उदाहरण के लिए, अन्य भाषाओं में, प्रकार (विशेषकर यदि आप किसी फ़ंक्शन से मान लौटाते हैं ) AnyELEMENT , ANYARRAY और विशेष रूप से महत्वपूर्ण VARIADIC समर्थित नहीं हैं

सरल सी फ़ंक्शन


उदाहरण के लिए, हम एक फ़ंक्शन लिखते हैं जो दो तर्क लेता है और उन्हें जोड़ता है। यह उदाहरण PostgreSQL के लिए प्रलेखन में वर्णित है, लेकिन हम इसे थोड़ा सुधारेंगे और इसे एकत्र करेंगे। फिर PostgreSQL में लोड करें और SQL फ़ंक्शन से कॉल लिखें।

#include "postgres.h"
#include "fmgr.h"
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(add_ab);
Datum add_ab(PG_FUNCTION_ARGS)
{
    int32 arg_a = PG_GETARG_INT32(0);
    int32 arg_b = PG_GETARG_INT32(1);
    PG_RETURN_INT32(arg_a + arg_b);
}

Add_func.c फ़ाइल को अधिक जटिल कार्यक्षमता विकसित करने के लिए टेम्पलेट के रूप में उपयोग किया जा सकता है। इसके अलावा, कोड के माध्यम से जाना

  • #include "postgresql.h": आपको हमेशा इस हेडर फ़ाइल को शामिल करना होगा, इसमें विभिन्न बुनियादी प्रकार और कार्य शामिल हैं।
  • #include "fmgr.h": हेडर फ़ाइल में विभिन्न PG_ * मैक्रोज़ होते हैं।
  • PG_MODULE_MAGIC: एक मैक्रो जो निर्धारित करता है कि हम संस्करण 8.2 से ऊपर PostgreSQL के लिए एक मॉड्यूल विकसित कर रहे हैं।
  • PG_FUNC_INFO_V1: PostgreSQL के भीतर एक मैक्रो डिफाइनिंग फ़ंक्शन कॉलिंग कन्वेंशन। यदि आप इसके बिना एक फ़ंक्शन घोषित करते हैं, तो संस्करण 0, अन्यथा संस्करण 1 पर कॉल करने पर एक समझौता होगा।
  • Datum: . , PostgreSQL. - VARIANT Microsoft. «» - . , .
  • add_ab(PG_FUNCTION_ARGS): . - . , .
  • int32 arg_a = PG_GETARG_INT32(0): ( ).
  • PG_RETURN_INT32(arg_a + arg_b): .

अब हमें इसे सही ढंग से संकलित करने और संकलित करने की आवश्यकता है। आउटपुट एक गतिशील रूप से लोड करने योग्य साझा पुस्तकालय (* .so) होगा। इसके लिए, इसे मेकफाइल के माध्यम से करना अधिक सुविधाजनक होगा प्रलेखन उन कुंजियों और रास्तों का वर्णन करता है जिन्हें पंजीकृत करने की आवश्यकता है, लेकिन हम PGXS का उपयोग करके एकत्र करेंगे इस विस्तार के डेवलपर्स, जिसका अर्थ है कि सभी आवश्यक के लिए एक वातावरण है -dev और -devel PostgreSQL के लिए संकुल आपके सिस्टम पर स्थापित किया जाना चाहिए

MODULES = add_func
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

हम SQL फ़ंक्शन बनाते हैं


अब हम एक SQL फ़ंक्शन लिखेंगे जिसे हमारे पहले से बनाए गए एक्सटेंशन लाइब्रेरी से बुलाया जाएगा।
CREATE FUNCTION add(int, int)
  RETURNS int
AS '/usr/lib/postgresql/9.4/lib/add_func', 'add_ab'
LANGUAGE C STRICT;

बस इतना ही! अब हम इस फ़ंक्शन का उपयोग कर सकते हैं

SELECT add(1, 2);

स्थापना को स्वचालित करें


अब हम स्थापना को थोड़ा स्वचालित करते हैं। यह बहुत उपयोगी होगा जब आपको पहले से पता न हो कि PostgreSQL का कौन सा संस्करण उपयोग किया जाता है, और किस तरीके से इसे स्थापित किया गया है। ऐसा करने के लिए, निम्न फ़ाइल बनाएं,

CREATE FUNCTION add(int, int)
  RETURNS int
AS 'MODULE_PATHNAME', 'add_ab'
LANGUAGE C STRICT;

और मेकफाइल में एक पंक्ति जोड़ें ,

MODULES = add_func
DATA_built = add_funcs.sql
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

थोड़ा सुधार करो


हम अपने उदाहरण में कुछ बदलाव करेंगे और कुछ सुधार करेंगे।

तर्क की जाँच


Add (int, int) फ़ंक्शन की SQL परिभाषा में याद रखें कि हमने STRICT कीवर्ड का उपयोग किया है ? इसका अर्थ है कि यदि कम से कम एक तर्क NULL है , तो फ़ंक्शन काम नहीं करेगा, और बस NULL वापस आ जाएगा यह SQL कथनों के व्यवहार के समान है, उदाहरण के लिए, यदि + ऑपरेटर में कम से कम एक NULL तर्क है , तो परिणाम NULL होगा

हम SQL एकत्रीकरण फ़ंक्शन योग () के समान हमारे फ़ंक्शन में एक तर्क जांच जोड़ते हैं , जो NULL मानों की उपेक्षा करता है और काम करना जारी रखता है। इसके लिए हमें करना होगा

  • सुनिश्चित करें कि फ़ंक्शन कार्य करता है, भले ही कोई तर्क NULL हो
  • यदि दोनों तर्क NULL हैं , तो NULL वापस करें

PG_FUNCTION_INFO_V1(add_ab_null);
Datum add_ab_null(PG_FUNCTION_ARGS)
{
    int32 not_null = 0;
    int32 sum = 0;
    if (!PG_ARGISNULL(0))
    {
        sum += PG_GETARG_INT32(0);
        not_null = 1;
    }
    if (!PG_ARGISNULL(1))
    {
        sum += PG_GETARG_INT32(1);
        not_null = 1;
    }
    if (not_null)
    {
        PG_RETURN_INT32(sum);
    }
    PG_RETURN_NULL();
}

और अब इसे देखें,

CREATE FUNCTION add(int, int)
 RETURNS int
AS '$libdir/add_func', 'add_ab_null'
LANGUAGE C;

SELECT add(NULL, NULL) AS must_be_null, add(NULL, 1) AS must_be_one;
-[ RECORD 1 ]+--
must_be_null |
must_be_one  | 1

और यहां बताया गया है कि मानक PostgreSQL टूल के साथ समान कैसे प्राप्त किया जा सकता है,
SELECT (CASE WHEN (a IS null) AND (b IS null)
(THEN null
ELSE coalesce(a, 0) + coalesce(b,0)
END)
FROM (SELECT 1::int AS a, null::int AS b) s;
-[ RECORD 1 ]
 case | 1

किसी फ़ंक्शन में कोई भी तर्क


जैसा कि आपने पहले ही देखा था, हमने तर्कों का मूल्य प्राप्त करने के लिए मैक्रोज़ का उपयोग किया। इसलिए, हम किसी भी तर्क को पारित कर सकते हैं, और फिर उनके मूल्यों को एक लूप में पढ़ सकते हैं,

if (!PG_ARGISNULL(i))
{
    sum += PG_GETARG_INT32(i);
    not_null = 1;
}

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

#include "utils/array.h"     //       
#include "catalog/pg_type.h" // INT4OID
PG_MODULE_MAGIC;
Datum add_int32_array(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(add_int32_array);
Datum add_int32_array(PG_FUNCTION_ARGS)
{
    //     .
    //          ,       int.
    ArrayType *input_array;
    int32 sum = 0;
    bool not_null = false;

    Datum *datums;
    bool *nulls;
    int count;
    int i;
    input_array = PG_GETARG_ARRAYTYPE_P(0); //     .  *_P  
                                            //        ,   INT32

    //          INT32 (INT4)
    Assert(ARR_ELEMTYPE(input_array) == INT4OID);

    //      
    if (ARR_NDIM(input_array) > 1)
        ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("1-dimensional array needed")));

    deconstruct_array(input_array, //  
                      INT4OID,     //  
                      4,           //    
                      true,        // int4   
                      'i',         //  'i'
                      &datums, &nulls, &count); //   

    for(i = 0; i < count; i++)
    {
        //     
        if (nulls[i])
            continue;

        // ,       
        sum += DatumGetInt32(datums[i]);
        not_null = true;
    }
    if (not_null)
        PG_RETURN_INT32(sum);

    PG_RETURN_NULL();
}

हमेशा की तरह, आइए कोड के माध्यम से चलते हैं,

  • पूर्णांक सरणियों के लिए कोई विशेष प्रकार नहीं है, इसलिए हम सामान्य प्रकार ArrayType का उपयोग करते हैं , जो किसी भी प्रकार के सरणी के लिए काम करता है
  • पहले तर्क के साथ सरणी को आरम्भ करने के लिए, हमने विशेष मैक्रो PG_GETARG_ARRAYTYPE_P का उपयोग किया
  • यह देखने के लिए भी जांच की जाती है कि क्या सरणी वास्तव में एक आयामी ARR_NDIM है
  • Int4 (= 23) के लिए OID प्रकार को INT4OID के रूप में परिभाषित किया गया है। अन्य प्रकार की परिभाषाएँ देखने के लिए, आप SQL का उपयोग कर सकते हैं,

    select oid, typlen, typbyval, typalign from pg_type
    where typname = 'int4';
    -[ RECORD 1 ]
    oid | 23
    typlen | 4
    typbyval | t
    typalign | i
    


अब हमें बस PostgreSQL को एक समारोह [एक] तर्क देने वाले फ़ंक्शन की घोषणा करके इसका उपयोग करना सिखाना होगा,]

CREATE OR REPLACE FUNCTION add_arr(int[]) RETURNS int
AS '$libdir/add_func', 'add_int32_array'
LANGUAGE C STRICT;

और जाँच करें

SELECT add_arr('{1,2,3,4,5,6,7,8,9}');
-[ RECORD 1 ]
add_arr | 45
SELECT add_arr(ARRAY[1,2,NULL]);
-[ RECORD 1 ]
add_arr | 3
SELECT add_arr(ARRAY[NULL::int]);
-[ RECORD 1 ]
add_arr |

अनुशंसाएँ


अब आइए एक्सटेंशन बनाने के लिए कुछ मुख्य सिफारिशों को संक्षेप में प्रस्तुत करें।

स्मृति के साथ काम करें


PostgreSQL DBMS के लेखक और निर्माता स्मृति के साथ काम करने और लीक को रोकने पर विशेष ध्यान देते हैं। यह DBMS और इसके एक्सटेंशन को विकसित करने के बुनियादी नियमों में से एक है। यह उनके अपने निर्णय पर आधारित है - स्मृति का संदर्भ।

पैलोक () और pfree () का उपयोग करना


PostgreSQL के सभी मेमोरी वर्क में नॉन-स्टैंडर्ड फ़ंक्शंस palloc () और pfree () का उपयोग शामिल है।

संरचनाओं का प्रारंभ


हमेशा नई घोषित संरचनाओं को प्रारंभ करें। उदाहरण के लिए, कॉल पेलोक () के बाद मेमसेट ()।

कनेक्टेड फ़ाइलें


आपके प्रत्येक एक्सटेंशन में कम से कम दो फाइलें शामिल होनी चाहिए: postgres.h और fmgr.h.

उपयोगी कड़ियाँ


PostgreSQL: सर्वर प्रोग्रामिंग इंटरफ़ेस

PostgreSQL: C PostgreSQL एक्सटेंशन नेटवर्क में उपयोगकर्ता परिभाषित कार्य


All Articles