在为 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
数据进行编码并将其传递给Pg
set bytea_output
to 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_output
SET bytea_output
psql
让我们看一下我们知道的一个函数,它对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_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
数据确实正在被解码。
例如,让我们看看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 后端,只会分离并退出调试器。