我是否应该在 Postgresql 的 C 函数中取消转义 bytea 字段,如果是这样 - 如何做到这一点



我为Postgresql编写了自己的C函数,该函数具有bytea参数。此函数定义如下

CREATE OR REPLACE FUNCTION putDoc(entity_type int, entity_id int, 
        doc_type text, doc_data bytea) RETURNS text
     AS 'proj_pg', 'call_putDoc'
     LANGUAGE C STRICT;

我的函数call_putDoc,写在 C 上,读取doc_data并将其数据传递给另一个函数,如 file_magic 以确定数据的 mime 类型,然后将数据传递给适当的文件转换器。

我从 php 脚本调用这个 postgresql 函数,它将文件内容加载到最后一个参数。所以,我应该用pg_escape_bytea传递文件内容。

当数据传递到call_putDoc C函数时,它的数据是否已经未转义,如果没有 - 如何取消转义它们?

编辑:正如我发现的,不,传递给C函数的数据并不是不可转义的。如何解脱它?

当涉及到为PostgreSQL编程C函数时,文档解释了一些基础知识,但对于其余部分,它通常归结为阅读PostgreSQL服务器的源代码。

值得庆幸的是,代码通常结构良好且易于阅读。我希望它有更多的文档评论。

用于导航源代码的一些重要工具是:

  • 一个好的 IDE;或
  • findgit grep命令。

在这种情况下,看了一眼之后,我认为您的bytea论点正在被解码 - 至少在第 9.2 页中,8.4 的行为可能(尽管不太可能(不同。服务器应该在调用您的函数之前自动执行此操作,我怀疑您在如何从 SQL 调用putDoc函数时存在编程错误。没有消息来源,很难说更多。

  • 尝试从psql调用它putDoc一些已验证已针对 8.4 服务器正确escape编码的示例数据
  • 尝试在 byteain 中设置断点,以确保在函数之前调用断点
  • 请按照以下步骤验证我所说的是否适用于 8.4。
  • 在函数中设置断点并使用 gdb 单步执行,在检查变量时使用 print 函数。有很多gdb教程会教你所需的breakbacktracecontstepnextprint等命令,所以我不会在这里重复所有这些。

至于出了什么问题:您可能会对数据进行双重编码 - 例如,鉴于您的评论,我想知道您是否base64编码数据并将其传递给Pgbytea_output设置为 escape .然后 Pg 会对其进行解码...为您提供一个包含字节base64编码的bytea表示形式的bytea,而不是原始字节本身。(编辑听起来可能不是基于评论(。

有关bytea的正确使用,请参阅:

  • http://www.postgresql.org/docs/current/static/runtime-config-client.html
  • http://www.postgresql.org/docs/current/static/datatype-binary.html

要说更多,我需要源代码。

这是我所做的:


源树中的快速find -name bytea*可查找src/include/utils/bytea.h 。那里的评论指出函数定义是utils/adt/varlena.c的 - 事实证明实际上是src/backend/util/adt/varlena.c.

bytea.h中,您还会注意到 bytea_output GUC 参数的定义,这是您在psqlSHOW bytea_outputSET bytea_output时看到的。

让我们看一个我们知道对bytea数据执行某些操作的函数,例如 bytea_substrvarlena.c 。它太短了,我将在这里包含它的声明之一:

Datum
bytea_substr(PG_FUNCTION_ARGS)
{
        PG_RETURN_BYTEA_P(bytea_substring(PG_GETARG_DATUM(0),
                    PG_GETARG_INT32(1),
                    PG_GETARG_INT32(2),
                    false));
}

许多公共函数都是私有实现的包装器,因此私有实现可以与具有不同参数的函数重用,也可以与其他私有代码重用。就是这样一种情况;您将看到真正的实现是 bytea_substring .以上所做的只是处理 SQL 函数调用接口。它根本不会弄乱包含bytea输入的Datum

在这种特殊情况下,真正的实现bytea_substring直接位于 SQL 接口包装器下方,因此请继续阅读 varlena.c

该实现似乎没有引用bytea_output GUC的,基本上只是在处理了一些边界案例后调用DatumGetByteaPSlice来完成工作。 git grep DatumGetByteaPSlice向我们展示了DatumGetByteaPSlicesrc/include/fmgr.h中,并且是一个定义为

#define DatumGetByteaPSlice(X,m,n)      ((bytea *) PG_DETOAST_DATUM_SLICE(X,m,n))

PG_DETOAST_DATUM_SLICE在哪里

#define PG_DETOAST_DATUM_SLICE(datum,f,c) 
            pg_detoast_datum_slice((struct varlena *) DatumGetPointer(datum), 
            (int32) (f), (int32) (c))

所以它只是解冻数据并返回一个内存切片。这让我想知道:解码是否在其他地方完成,作为函数调用接口的一部分?还是我错过了什么?

看看byteainbytea的输入函数,表明它肯定在解码数据。在该函数中设置断点,当您从 SQL 调用函数时,断点应该会跳闸,表明bytea数据确实正在解码。

例如,让我们看看当我们调用bytea_substr时,byteain是否被调用:

SELECT substring('1234'::bytea, 2,2);

如果您想知道如何将substring(bytea)变成对bytea_substrC调用,请查看映射src/catalog/pg_proc.h

我们将启动 psql 并获取后端的 pid:

$ psql -q regress
regress=# select pg_backend_pid();
 pg_backend_pid 
----------------
          18582
(1 row)

然后在另一个终端中使用 GDB 连接到该 PID,设置断点,并继续执行:

$ sudo -u postgres gdb -q -p 18582
Attaching to process 18582
... blah blah ...
(gdb) break bytea_substr
Breakpoint 1 at 0x6a9e40: file varlena.c, line 1845.
(gdb) cont
Continuing.

在第一个终端中,我们在 psql 中执行:

SELECT substring('1234'::bytea, 2,2);

。并注意它挂起而不返回结果。好。这是因为我们在 gdb 中触发了断点,正如您在第二个终端中看到的那样:

Breakpoint 1, bytea_substr (fcinfo=0x1265690) at varlena.c:1845
1845            PG_RETURN_BYTEA_P(bytea_substring(PG_GETARG_DATUM(0),
(gdb) 

使用 bt 命令的回溯不会在调用路径中显示bytea_substr,它都是 SQL 函数调用机制。因此,Pg 在将bytea传递给bytea_substr之前对其进行解码。

现在,您可以使用 quit 分离调试器。这不会退出 Pg 后端,只会分离并退出调试器。

相关内容

  • 没有找到相关文章

最新更新