"CREATE SCHEMA foo ..." 从 PostgreSQL 中的 C 扩展查询(使用 SPI_execute_with_args)
"CREATE SCHEMA foo ..." query from C extension in PostgreSQL (using SPI_execute_with_args)
我正在尝试使用服务器编程接口 (SPI) 从为 PostgreSQL 构建的 C 扩展执行 SQL 查询。查询应该创建一个包含大量表的新模式。 (基本上它应该为用户设置一个工作区。)但是由于用户应该能够创建多个工作区,所以我在编写脚本时不知道模式名称。所以我需要一种在 运行 时间提供它的方法。但是我无法让它工作。
我正在尝试使用 SPI_execute_with_args
来执行此操作,因为 documentation 声明如下:
SPI_execute_with_args
executes a command that might include references
to externally supplied parameters. The command text refers to a
parameter as $n
, and the call specifies data types and values for each
such symbol. read_only
and count
have the same interpretation as in
SPI_execute
.
The main advantage of this routine compared to SPI_execute
is that
data values can be inserted into the command without tedious
quoting/escaping, and thus with much less risk of SQL-injection
attacks.
SQL 脚本如下所示(如果我手动将 </code> 替换为真实的模式名称并将其 运行 替换为普通脚本,则一切正常) :</p>
<pre><code>CREATE SCHEMA ;
ALTER SCHEMA OWNER TO some_user;
CREATE FUNCTION .foo() ...
CREATE TABLE .bar ...
...
但现在我想从 C 代码中 运行 它,并且由于文档缺少任何关于 SPI 的工作示例,我不得不 google 寻找任何可以进一步指导我的东西。我在 SO 上找到了 this example,函数如下所示:
...
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 ( + 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);
}
...
这可以正常工作(将 </code> 替换为作为参数输入的数字)。</p>
<p>但是当我开始修改它以使用我自己的查询时,一切都崩溃了。我什至不能只使用查询的第一行。</p>
<p>为了记录在案,我还尝试过 运行 一个简单的 <code>SELECT ''
查询并将其替换为各种变量。但除了这个例子之外别无他法。服务器崩溃,returns </code> 处的语法无效,或者只是 returns <code>
作为答案。
如果我是对的,那么 where 和 what 似乎很重要 </code> <strong>和</strong>。而且 SPI 不只是在 <code>
?
上执行 "find and replace"
我在测试各种变量类型时尝试了一些不同的 OID:s,例如:ANYOID
、CSTRINGOID
、CHAROID
、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, 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 row)
我不确定这是最好的方法,但即使不是,如果我需要更多地了解此 SPI 函数的工作原理,那就太好了它进一步进入项目。
有没有人对此有任何可行的例子或什么能把我推向正确的方向?
你的SELECT ''
里面的</code>在单引号里面,所以是字符串字面量,不是参数。</p>
<p>改为使用以下内容:</p>
<pre><code>SELECT
请注意,您可以使用参数,也可以使用相同类型的文字,但不能将参数用于标识符,例如 table 或列名。
如果您需要在这样的地方使用变量,则必须使用 snprintf
.
构造查询字符串
要避免 SQL 注入,请使用 utils/builtins.h
中的 quote_identifier
。
这是您的代码的固定版本:
#include "postgres.h"
#include "fmgr.h"
#include "catalog/pg_type.h"
#include "executor/spi.h"
#include "utils/builtins.h"
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(foobar);
Datum foobar(PG_FUNCTION_ARGS)
{
Datum arguments[1];
Oid argument_types[1] = { TEXTOID };
char *res;
bool isnull;
Datum result;
/* for when we don't want to use the SPI context */
MemoryContext context = CurrentMemoryContext;
arguments[0] = CStringGetTextDatum("some_text");
SPI_connect();
SPI_execute_with_args("SELECT ", 1, argument_types, arguments, NULL, false, 0);
result = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
res = MemoryContextStrdup(context, TextDatumGetCString(result));
SPI_finish();
PG_RETURN_TEXT_P(CStringGetTextDatum(res));
}
我正在尝试使用服务器编程接口 (SPI) 从为 PostgreSQL 构建的 C 扩展执行 SQL 查询。查询应该创建一个包含大量表的新模式。 (基本上它应该为用户设置一个工作区。)但是由于用户应该能够创建多个工作区,所以我在编写脚本时不知道模式名称。所以我需要一种在 运行 时间提供它的方法。但是我无法让它工作。
我正在尝试使用 SPI_execute_with_args
来执行此操作,因为 documentation 声明如下:
SPI_execute_with_args
executes a command that might include references to externally supplied parameters. The command text refers to a parameter as$n
, and the call specifies data types and values for each such symbol.read_only
andcount
have the same interpretation as inSPI_execute
.The main advantage of this routine compared to
SPI_execute
is that data values can be inserted into the command without tedious quoting/escaping, and thus with much less risk of SQL-injection attacks.
SQL 脚本如下所示(如果我手动将 </code> 替换为真实的模式名称并将其 运行 替换为普通脚本,则一切正常) :</p>
<pre><code>CREATE SCHEMA ;
ALTER SCHEMA OWNER TO some_user;
CREATE FUNCTION .foo() ...
CREATE TABLE .bar ...
...
但现在我想从 C 代码中 运行 它,并且由于文档缺少任何关于 SPI 的工作示例,我不得不 google 寻找任何可以进一步指导我的东西。我在 SO 上找到了 this example,函数如下所示:
... 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 ( + 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); } ...
这可以正常工作(将 </code> 替换为作为参数输入的数字)。</p>
<p>但是当我开始修改它以使用我自己的查询时,一切都崩溃了。我什至不能只使用查询的第一行。</p>
<p>为了记录在案,我还尝试过 运行 一个简单的 <code>SELECT ''
查询并将其替换为各种变量。但除了这个例子之外别无他法。服务器崩溃,returns </code> 处的语法无效,或者只是 returns <code>
作为答案。
如果我是对的,那么 where 和 what 似乎很重要 </code> <strong>和</strong>。而且 SPI 不只是在 <code>
?
我在测试各种变量类型时尝试了一些不同的 OID:s,例如:ANYOID
、CSTRINGOID
、CHAROID
、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, 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 row)
我不确定这是最好的方法,但即使不是,如果我需要更多地了解此 SPI 函数的工作原理,那就太好了它进一步进入项目。
有没有人对此有任何可行的例子或什么能把我推向正确的方向?
你的SELECT ''
里面的</code>在单引号里面,所以是字符串字面量,不是参数。</p>
<p>改为使用以下内容:</p>
<pre><code>SELECT
请注意,您可以使用参数,也可以使用相同类型的文字,但不能将参数用于标识符,例如 table 或列名。
如果您需要在这样的地方使用变量,则必须使用 snprintf
.
要避免 SQL 注入,请使用 utils/builtins.h
中的 quote_identifier
。
这是您的代码的固定版本:
#include "postgres.h"
#include "fmgr.h"
#include "catalog/pg_type.h"
#include "executor/spi.h"
#include "utils/builtins.h"
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(foobar);
Datum foobar(PG_FUNCTION_ARGS)
{
Datum arguments[1];
Oid argument_types[1] = { TEXTOID };
char *res;
bool isnull;
Datum result;
/* for when we don't want to use the SPI context */
MemoryContext context = CurrentMemoryContext;
arguments[0] = CStringGetTextDatum("some_text");
SPI_connect();
SPI_execute_with_args("SELECT ", 1, argument_types, arguments, NULL, false, 0);
result = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
res = MemoryContextStrdup(context, TextDatumGetCString(result));
SPI_finish();
PG_RETURN_TEXT_P(CStringGetTextDatum(res));
}