2

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

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_putDocC 函数时,它的数据是否已经转义,如果没有 - 如何对它们进行转义?

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

4

1 回答 1

7

在为 PostgreSQL 编写 C 函数时,文档解释了一些基础知识,但对于其余部分,通常是阅读 PostgreSQL 服务器的源代码。

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

一些用于浏览源代码的重要工具是:

  • 一个好的IDE;或者
  • find命令git grep

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

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

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

正确使用bytea见:

多说,我需要源代码。

这是我所做的:


find -name bytea\*在源代码树中快速定位src/include/utils/bytea.h. 那里的评论指出,函数定义在utils/adt/varlena.c- 结果实际上是src/backend/util/adt/varlena.c

bytea.h您还会注意到 GUC 参数的定义,这bytea_output是您在.SHOW bytea_outputSET bytea_outputpsql

让我们看一下我们知道的一个函数,它对bytea数据做一些事情,比如bytea_substr, in varlena.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函数调用接口。它根本不会与Datum包含bytea输入的内容混淆。

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

实现似乎没有参考bytea_outputGUC,基本上只是DatumGetByteaPSlice在处理一些边界情况后调用来完成工作。git grep DatumGetByteaPSlice向我们展示了DatumGetByteaPSlice在 中src/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))

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

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

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

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

如果您想知道如何substring(bytea)变成对 的C调用bytea_substr,请查看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 函数调用机制。所以 Pgbytea在将它传递给bytea_substr.

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

于 2012-08-26T21:31:55.927 回答