我为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;或
find
和git grep
命令。
在这种情况下,看了一眼之后,我认为您的bytea
论点正在被解码 - 至少在第 9.2 页中,8.4 的行为可能(尽管不太可能(不同。服务器应该在调用您的函数之前自动执行此操作,我怀疑您在如何从 SQL 调用putDoc
函数时存在编程错误。没有消息来源,很难说更多。
- 尝试从
psql
调用它putDoc
一些已验证已针对 8.4 服务器正确escape
编码的示例数据 - 尝试在
byteain
中设置断点,以确保在函数之前调用断点 - 请按照以下步骤验证我所说的是否适用于 8.4。
- 在函数中设置断点并使用
gdb
单步执行,在检查变量时使用print
函数。有很多gdb教程会教你所需的break
,backtrace
,cont
,step
,next
,print
等命令,所以我不会在这里重复所有这些。
至于出了什么问题:您可能会对数据进行双重编码 - 例如,鉴于您的评论,我想知道您是否base64
编码数据并将其传递给Pg
,bytea_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 参数的定义,这是您在psql
中SHOW bytea_output
或SET bytea_output
时看到的。
让我们看一个我们知道对bytea
数据执行某些操作的函数,例如 bytea_substr
,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 函数调用接口。它根本不会弄乱包含bytea
输入的Datum
。
在这种特殊情况下,真正的实现bytea_substring
直接位于 SQL 接口包装器下方,因此请继续阅读 varlena.c
。
该实现似乎没有引用bytea_output
GUC的,基本上只是在处理了一些边界案例后调用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
数据确实正在解码。
例如,让我们看看当我们调用bytea_substr
时,byteain
是否被调用:
SELECT substring('1234'::bytea, 2,2);
如果您想知道如何将substring(bytea)
变成对bytea_substr
的C
调用,请查看映射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 后端,只会分离并退出调试器。