我应该在Postgresql的C函数中unescape bytea字段,如果是这样 - 怎么做?

Pup*_*nij 2 c postgresql 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;
Run Code Online (Sandbox Code Playgroud)

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

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

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

编辑:正如我发现的那样,没有,传递给C函数的数据未被转义.如何取消它?

Cra*_*ger 7

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

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

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

  • 一个好的IDE; 要么
  • findgit grep命令.

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

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

至于什么是错的:你可能是双编码您的数据-例如,给你的意见,我想知道如果你base64的编码数据,并将其传递到Pgbytea_output设置为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您还会注意到的定义bytea_outputGUC参数,这是你所看到的,当你SHOW bytea_output还是SET bytea_outputpsql.

让我们来看看我们知道的与bytea数据有关的函数,比如bytea_substrin 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));
}
Run Code Online (Sandbox Code Playgroud)

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

真正的实现bytea_substring直接在这个部分情况下的SQL接口包装器下面,所以请继续阅读varlena.c.

实现似乎并不是指bytea_outputGUC,并且基本上只是DatumGetByteaPSlice在处理一些边界情况后调用它来完成工作.git grep DatumGetByteaPSlice向我们展示DatumGetByteaPSlicesrc/include/fmgr.h,并且是一个宏定义为:

#define DatumGetByteaPSlice(X,m,n)      ((bytea *) PG_DETOAST_DATUM_SLICE(X,m,n))
Run Code Online (Sandbox Code Playgroud)

这里PG_DETOAST_DATUM_SLICE

#define PG_DETOAST_DATUM_SLICE(datum,f,c) \
            pg_detoast_datum_slice((struct varlena *) DatumGetPointer(datum), \
            (int32) (f), (int32) (c))
Run Code Online (Sandbox Code Playgroud)

所以它只是取消了数据并返回一个内存片.这让我感到疑惑:解码是否在其他地方完成,作为函数调用接口的一部分?还是我错过了什么?

看一下byteain输入函数bytea,表明它确实解码了数据.在该函数中设置一个断点,当你从SQL调用函数时它应该跳转,表明bytea数据真的被解码了.

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

SELECT substring('1234'::bytea, 2,2);
Run Code Online (Sandbox Code Playgroud)

如果你想知道如何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)
Run Code Online (Sandbox Code Playgroud)

然后在另一个终端用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.
Run Code Online (Sandbox Code Playgroud)

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

SELECT substring('1234'::bytea, 2,2);
Run Code Online (Sandbox Code Playgroud)

...并注意它在没有返回结果的情况下挂起.好.那是因为我们在gdb中跳过断点,正如你在第二个终端中看到的那样:

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

使用该bt命令的回溯不会显示bytea_substr在调用路径中,它是所有SQL函数调用机制.所以Pg bytea在传递它之前解码它bytea_substr.

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