"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> 作为答案。


如果我是对的,那么 wherewhat 似乎很重要 </code> <strong>和</strong>。而且 SPI 不只是在 <code>?

上执行 "find and replace"

我在测试各种变量类型时尝试了一些不同的 OID:s,例如:ANYOIDCSTRINGOIDCHAROIDREGNAMESPACEOIDTEXTOID 等。我尝试将变量作为纯字符数组和指向分配有 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));
}