0

Server Programming Interface (SPI) を使用して、PostgreSQL 用に構築された C 拡張機能から SQL クエリを実行しようとしています。クエリは、非常に多数のテーブルを含む新しいスキーマを作成する必要があります。(基本的には、ユーザーが作業するためのワークスペースをセットアップする必要があります。)しかし、ユーザーは複数のワークスペースを作成できる必要があるため、スクリプトを作成するときにスキーマ名がわかりません。したがって、実行時にこれを提供する方法が必要です。しかし、私はそれを機能させることができません。

ドキュメントに次のように記載されてSPI_execute_with_argsいるため、これを使用してこれを実行しようとしています。

SPI_execute_with_args外部から提供されたパラメーターへの参照を含む可能性のあるコマンドを実行します。コマンド テキストはパラメータを として参照し$n、呼び出しはそのようなシンボルごとにデータ型と値を指定します。read_onlycountの解釈は同じ SPI_executeです。

と比較した場合のこのルーチンの主な利点は、SPI_execute面倒な引用符/エスケープなしでデータ値をコマンドに挿入できるため、SQL インジェクション攻撃のリスクがはるかに少ないことです。

SQL スクリプトは次のようになります ($1手動で実際のスキーマ名に置き換えて通常のスクリプトとして実行すると、すべて正常に機能します)。

CREATE SCHEMA $1;
ALTER SCHEMA $1 OWNER TO some_user;

CREATE FUNCTION $1.foo() ...

CREATE TABLE $1.bar ...
...

しかし、今は C コードから実行したいと思っています。ドキュメンテーションには SPI で動作する例が欠けているため、さらに詳しく説明できるものを見つけるためにグーグルで検索する必要がありました。そして、関数が次のようになる SO でこの例を見つけました。

...

Datum 
foo(PG_FUNCTION_ARGS)
{
    int ret;
    Datum args[1];
    Oid argtypes[1] = { INT4OID };
    Datum result;
    bool isnull;

    SPI_connect();

    args[0] = PG_GETARG_INT32(0);

    /* ensure expected result type by casting */
    ret = SPI_execute_with_args("SELECT ($1 + 10)::int", 
                                   1, argtypes, args, NULL,
                                   true, 1);

    Assert(SPI_processed == 1);

    result = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
    Assert(!isnull);

    SPI_finish();

    PG_RETURN_DATUM(result);
}

...

これは正常に機能します (パラメーターとして入力された数値に置き換え$1られます)。

しかし、自分のクエリで動作するように変更を開始するとすぐに、すべてが壊れます。クエリの最初の行だけで機能させることさえできません。

記録のために、単純なSELECT '$1'クエリを実行して、それをさまざまな変数に置き換えようとしました。しかし、例が機能する以外は何もありません。サーバーがクラッシュするか、無効な構文を返す$1か、単に$1答えとして返されます。


私が正しければ、どこ$1 置き換えたいかが重要なようです。$1そして、その SPI は?で「検索と置換」を行うだけではありません。

ANYOIDCSTRINGOIDCHAROID、などのさまざまな変数タイプをテストするときに、いくつかの異なる OID:s を試しました。そして、変数を純粋な char 配列として、または で割り当てられたテキスト ブロックへのポインターとして送信しようとしREGNAMESPACEOIDました。しかし成功せず…TEXTOIDSPI_palloc()palloc()

私が見つけた例とドキュメントからまとめたコード例:

PG_FUNCTION_INFO_V1(foobar);
Datum foobar(PG_FUNCTION_ARGS)
{
    Datum arguments[1];
    Oid argument_types[1] = { ANYOID };
    Datum result;
    bool isnull;
    arguments[0] = "some_text";

    SPI_connect();
    SPI_execute_with_args("SELECT '$1'", 1, argument_types, arguments, NULL, false, 0);
    result = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
    SPI_finish();

    PG_RETURN_DATUM(result);
}

このコードを実行すると、次の結果が得られます。

SELECT foobar();
 foobar
--------
 $1
(1 row)

これが最善の方法であるかどうかはわかりませんが、そうでない場合でも、プロジェクトでさらに必要になるため、この SPI 関数がどのように機能するかをもっと知っておくとよいでしょう。

誰かがこれに対する実際の例や、私を正しい方向に押しやる何かを持っていますか?

4

1 に答える 1