Server Programming Interface (SPI) を使用して、PostgreSQL 用に構築された C 拡張機能から SQL クエリを実行しようとしています。クエリは、非常に多数のテーブルを含む新しいスキーマを作成する必要があります。(基本的には、ユーザーが作業するためのワークスペースをセットアップする必要があります。)しかし、ユーザーは複数のワークスペースを作成できる必要があるため、スクリプトを作成するときにスキーマ名がわかりません。したがって、実行時にこれを提供する方法が必要です。しかし、私はそれを機能させることができません。
ドキュメントに次のように記載されてSPI_execute_with_args
いるため、これを使用してこれを実行しようとしています。
SPI_execute_with_args
外部から提供されたパラメーターへの参照を含む可能性のあるコマンドを実行します。コマンド テキストはパラメータを として参照し$n
、呼び出しはそのようなシンボルごとにデータ型と値を指定します。read_only
とcount
の解釈は同じ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 は?で「検索と置換」を行うだけではありません。
ANYOID
、CSTRINGOID
、CHAROID
、などのさまざまな変数タイプをテストするときに、いくつかの異なる OID:s を試しました。そして、変数を純粋な char 配列として、または で割り当てられたテキスト ブロックへのポインターとして送信しようとしREGNAMESPACEOID
ました。しかし成功せず…TEXTOID
SPI_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 関数がどのように機能するかをもっと知っておくとよいでしょう。
誰かがこれに対する実際の例や、私を正しい方向に押しやる何かを持っていますか?