◆VC++ 2005環境での PostgreSQL - C言語関数開発(3)

[ 上へ ] [ PostgreSQL - C言語関数(番外編) ]

・8.2系

以下は

OS : Windows XP, Windows 2000       
DB : PostgreSQL 8.2  (Windowsバイナリ版)   
開発環境 : VC++ 2005        

で実施したものですが、多分8.2系すべてに同じことが言えると思います。 8.2系になると8.3系で修正した処理のほかにもやらなければいけないことが増えます。

まず、 ここで記述したインクルードパスを修正するところからはじめます。

C:\Program Files\PostgreSQL\8.2\include\server
C:\Program Files\PostgreSQL\8.2\include\server\port\win32
C:\Program Files\PostgreSQL\8.2\include\server\port\win32_msvc

とりあえずこれでビルドしてみます。

error C2065: '__func__' : 定義されていない識別子です。    
      d:\windows\vs2005\projects\cfunc\cfunc\cfunc.cpp	31    

とエラーがでます。行を特定すると

ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED)));

test_func内のこの行なのでelog.hのereportの定義を見てみます。

#define ereport(elevel, rest) \    
(errstart(elevel, __FILE__, __LINE__, PG_FUNCNAME_MACRO) ? \
(errfinish rest) : (void) 0)

さらに、elog.hのPG_FUNCNAME_MACROの定義は

#ifdef HAVE_FUNCNAME__FUNC
#define PG_FUNCNAME_MACRO __func__
#else
#ifdef HAVE_FUNCNAME__FUNCTION
#define PG_FUNCNAME_MACRO __FUNCTION__
#else
#define PG_FUNCNAME_MACRO NULL
#endif
#endif

ですが、HAVE_FUNCNAME__FUNCのスイッチが入っているので、PG_FUNCNAME_MACROは __func__ となります。これが未定義ということです。VC++ 2005では__FUNCTION__ なので HAVE_FUNCNAME__FUNC のスイッチを切って HAVE_FUNCNAME__FUNCTIONのスイッチを いれればよいのかもしれませんが、このスイッチがほかでも使われているとまずいので PG_MODULE_MAGICなどの時のように再定義します。

vc.hの最後のほうに以下のように再定義してください。

//vc.h///////////////////////////////////////////////////////////
・
・
//テキスト取得マクロ(text型で取得した文字列をCで扱えるように変換)
#define  GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, \
                                                            PointerGetDatum(textp)))
#define  GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, \
                                                           CStringGetDatum(cstrp)))

//ereportマクロの再定義(8.2系のみ)
//elog.h内の#define PG_FUNCNAME_MACRO __func__ で
//VC++ 2005では__func__の代わりが__FUNCION__のため
#ifdef PG_FUNCNAME_MACRO
#undef PG_FUNCNAME_MACRO
#endif /*PG_FUNCNAME_MACRO*/

#define PG_FUNCNAME_MACRO __FUNCTION__

#endif /* __VC_H__ */

これでビルドすると今度は

fatal error LNK1104: ファイル 'postgres.lib' を開くことができません。 CFunc

とライブラリファイルがないと言われます。 ここでライブラリファイルのパスを通してますが、
C:\Program Files\PostgreSQL\8.2 \lib
とインクルードファイルのように変更してもダメです。 ここでも少し書きましたがWindowsのバイナリ版 にはpostgres.libがもともとないのです。これを修正するには多くのスペースが必要なので 番外編としてこちらで解決してください。 そしてその後こちらに戻ってきてください。

・8.2系の修正・・・続き

番外編でpostgres.libを作成し、 #pragma comment(lib, xxxx)でリンクが完了しました。
何度目か分からないですが、再びビルドすると

error LNK2019: 未解決の外部シンボル _MemoryContextSwitchTo が
               関数 _test_func で参照されました。	    CFunc.obj

とリンカエラーになります。8.3系のときはpostgres.exe内に あったのでOKでしたが、8.2系の場合はどうやらないようです。(未確認)
ですが、palloc.hに定義があるので流用します。

//vc.h///////////////////////////////////////////////////////////
・
・
//テキスト取得マクロ(text型で取得した文字列をCで扱えるように変換)
#define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, \ 
                                                           PointerGetDatum(textp)))
#define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, \ 
                                                          CStringGetDatum(cstrp)))

//ereportマクロの再定義(8.2系のみ)
//elog.h内の#define PG_FUNCNAME_MACRO __func__ で    
//VC++ 2005では__func__の代わりが__FUNCION__のため
#ifdef PG_FUNCNAME_MACRO
#undef PG_FUNCNAME_MACRO
#endif /*PG_FUNCNAME_MACRO*/
#define PG_FUNCNAME_MACRO __FUNCTION__

//Visual C++での開発時に必要な関数の宣言(8.2系のみ)
static MemoryContext MemoryContextSwitchTo(MemoryContext context);

//MemoryContextSwitchTo関数定義(8.2系のみ)
//(VC++環境では__GNUC__マクロ未定義のためpalloc.h参照)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
{
    MemoryContext old = CurrentMemoryContext;    
    CurrentMemoryContext = context;    

    return old;    
}

#endif /* __VC_H__ */

最後にCFuncのプロパティページから ここで設定したビルド後のイベントを
copy $(TargetPath) C:\Progra~1\PostgreSQL\8.2\lib
に変更してください。

これで8.2系、8.3系でのC言語関数の開発はpostgres.h、funcapi.h(fmgr.h)、に加えて vc.hをインクルード しさえすれば、ドキュメントと同様の開発ができると思います。しかし、8.1系ではこれでもまだ、 エラーになります。ExtLinkのところでも少し書いて いますがerrcodeが再定義されてしまう問題が起きます。
もうすぐ8.4もリリースされますし気力も失せてきたので、もし、暇があれば対応策を考えてみたいと 思います。

最後に8.2、8.3系の両方に対応したvc.hと CFunc.cppの全コードを書いておきます。

//vc.h///////////////////////////////////////////////////////////////////
/*
PostgreSQL C言語関数をVisual C++での開発に必要な修正    
*/
//postgres.h, funcapi.h fmgr.hのインクルード後に    
//このヘッダファイルをインクルードする事
#ifndef __VC_H__
#define __VC_H__

//BUILDING_DLL使用不可
#ifdef BUILDING_DLL
#error Don't define BUILDING_DLL
#endif

//スイッチの定義
#ifndef VC_MODULE_BUILDING
#define VC_MODULE_BUILDING
#endif /* VC_MODULE_BUILDING */

////既存のPG_MODELE_MAGIC, PG_FUNCTION_INFO_V1をキャンセル
#undef PG_MODULE_MAGIC
#undef PG_FUNCTION_INFO_V1

//エクスポートスイッチ(VC_MODULE_BUILDINGによって__declspec(dllexport),
//__declspec(dllimport)を切り替える)
#if defined(_MSC_VER) || defined(__MINGW32__)
#if defined(VC_MODULE_BUILDING)
#define PG_EXPORT __declspec(dllexport)
#else
#define PG_EXPORT __declspec(dllimport)
#endif /* PG_MODULE_BUILDING */
#else
define PG_EXPORT
#endif /*_MSC_VER, __MINGW32__ */

//PG_MODULE_MAGICマクロ定義
#define PG_MODULE_MAGIC \    
PG_EXPORT const Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void); \
const Pg_magic_struct * \
PG_MAGIC_FUNCTION_NAME(void) \
{ \
    static const Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA; \
    return &Pg_magic_data; \
} \
extern int no_such_variable

//PG_FUNCTION_INFO_V1マクロ定義
#define PG_FUNCTION_INFO_V1(funcname) \
PG_EXPORT const Pg_finfo_record * CppConcat(pg_finfo_,funcname)(void); \
const Pg_finfo_record * \
CppConcat(pg_finfo_,funcname) (void) \
{ \
    static const Pg_finfo_record my_finfo = { 1 }; \
    return &my_finfo; \
} \
extern int no_such_variable

//ライブラリリンク
#pragma comment(lib, "postgres.lib")   //ここは番外編でパスを適宜修正する事

//Postgres内部関数textout使用のため
#ifndef BUILTINS_H
#include "utils/builtins.h"
#endif /* BUILTINS_H */

//テキスト取得マクロ(text型で取得した文字列をCで扱えるように変換)
#define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, \
                                                           PointerGetDatum(textp)))
#define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, \
                                                          CStringGetDatum(cstrp)))

//ereportマクロの再定義(8.2系のみ)
//elog.h内の#define PG_FUNCNAME_MACRO __func__ で    
//VC++ 2005では__func__の代わりが__FUNCION__のため
#ifdef PG_FUNCNAME_MACRO
#undef PG_FUNCNAME_MACRO
#endif /*PG_FUNCNAME_MACRO*/
#define PG_FUNCNAME_MACRO __FUNCTION__


//Visual C++での開発時に必要な関数の宣言(8.2系のみ)
static MemoryContext MemoryContextSwitchTo(MemoryContext context);

//MemoryContextSwitchTo関数定義(8.2系のみ)
//(VC++環境では__GNUC__マクロ未定義のためpalloc.h参照)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
{
    MemoryContext old = CurrentMemoryContext;    
    CurrentMemoryContext = context;    

    return old;    
}
#endif /* __VC_H__ */
//CFunc.cpp////////////////////////////////////////////////////////
#define _USE_32BIT_TIME_T

#include "postgres.h"
#include "funcapi.h" //fmgr.hはインクルード済み
#include "vc.h"

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif


PG_FUNCTION_INFO_V1(test_func);

PG_EXPORT Datum test_func(PG_FUNCTION_ARGS)    
{
    FuncCallContext  *pFcctx;    
    MemoryContext    oldmemoryctx;    
    TupleDesc        tupledesc;    
    AttInMetadata    *pAttinmeta;    
    Datum            result;

    if (SRF_IS_FIRSTCALL())    
    {
        pFcctx = SRF_FIRSTCALL_INIT();    
        oldmemoryctx = MemoryContextSwitchTo(pFcctx->multi_call_memory_ctx);    
        pFcctx->max_calls = PG_GETARG_UINT32(0);    

        if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
        {
            ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED)));    
        }

        pAttinmeta = TupleDescGetAttInMetadata(tupledesc);    
        pFcctx->attinmeta = pAttinmeta;    

        MemoryContextSwitchTo(oldmemoryctx);
    }

    pFcctx = SRF_PERCALL_SETUP();    

    if (pFcctx->call_cntr < pFcctx->max_calls)    
    {
        int       i;    
        char      *pSrc;    
        char      **ppValues = palloc(2 * sizeof(char *));
        HeapTuple htuple;    

        for (i=0; i<2; i++)    
        {
            ppValues[i] = palloc(16 * sizeof(char));    
            pSrc = GET_STR(PG_GETARG_TEXT_P(i+1));    

            if (strcpy_s(ppValues[i], 16, pSrc))    
                ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY)));    
        }

        htuple = BuildTupleFromCStrings(pFcctx->attinmeta, ppValues);    
        result = HeapTupleGetDatum(htuple);    

        SRF_RETURN_NEXT(pFcctx, result);    
    } else    
    {
        SRF_RETURN_DONE(pFcctx);
    }
}