Postgres C功能-通过和;返回数字

Postgres C功能-通过和;返回数字,c,postgresql,pointers,C,Postgresql,Pointers,我刚刚开始测试Postgres的外部C函数。当我传入一个数字并返回它时,函数工作正常。(示例) 样本函数 PG_FUNCTION_INFO_V1(numericTesting); Datum numericTesting(PG_FUNCTION_ARGS) { Numeric p = PG_GETARG_NUMERIC(0); PG_RETURN_NUMERIC(p); } PG_FUNCTION_INFO_V1(numericTesting); Datum num

我刚刚开始测试Postgres的外部C函数。当我传入一个数字并返回它时,函数工作正常。(示例)

样本函数

PG_FUNCTION_INFO_V1(numericTesting);
Datum
numericTesting(PG_FUNCTION_ARGS)
{
    Numeric       p = PG_GETARG_NUMERIC(0);
    PG_RETURN_NUMERIC(p);
}
PG_FUNCTION_INFO_V1(numericTesting);
Datum
numericTesting(PG_FUNCTION_ARGS)
{
    Numeric       p = PG_GETARG_NUMERIC(0);
    PG_RETURN_NUMERIC(p * .5);
}
但是,当我尝试对传入的变量执行任何数学函数时,它将不会编译。我明白了

错误:二进制操作数无效*

样本函数

PG_FUNCTION_INFO_V1(numericTesting);
Datum
numericTesting(PG_FUNCTION_ARGS)
{
    Numeric       p = PG_GETARG_NUMERIC(0);
    PG_RETURN_NUMERIC(p);
}
PG_FUNCTION_INFO_V1(numericTesting);
Datum
numericTesting(PG_FUNCTION_ARGS)
{
    Numeric       p = PG_GETARG_NUMERIC(0);
    PG_RETURN_NUMERIC(p * .5);
}

这是什么原因造成的?我猜数字数据类型需要一些函数来允许数学运算。我尝试使用:PG_RETURN_NUMERIC(DatumGetNumeric(p*.5)),但结果相同

*
的两个操作数必须具有算术类型(整数或浮点类型)。很有可能,
Numeric
是一个typedef,表示的不是简单的整数或浮点类型


不幸的是,我对Postgres API了解不够,无法提供更多帮助。希望有一个宏或函数可以将
数值
转换为算术类型,或对
数值
类型应用算术运算

Numeric
不是基元类型,因此不能直接对其执行算术运算。C没有运算符重载,因此无法为数值添加乘法运算符。您必须使用适当的函数调用来乘法数字

与编写Pg扩展函数时的大多数情况一样,阅读源代码并了解它在其他地方是如何完成的可能会有所帮助

在本例中,请查看
src/backend/utils/adt/numeric.c
。检查
Datum numeric\u mul(PG\u FUNCTION\u ARGS)
您将看到它使用
mul\u var(…)
来完成工作

不幸的是,
mul\u var
静态的
,因此不能在
numeric.c
之外使用。令人恼火和惊讶。必须有一种合理的方法来处理来自
C
NUMERIC
扩展函数,而无需使用spi/fmgr通过
SQL
操作符调用,正如您在注释中所示,您使用
DirectFunctionCall2
调用
NUMERIC\u mul
操作符

看起来可以直接从C调用的Numeric的公共内容在
src/include/utils/Numeric.h
中,所以让我们看看这里。哎呀,没什么,只是一些宏,用于在
Numeric
Datum
之间转换,还有一些助手
GETARG
RETURN
宏。看来通过SQL调用使用可能是唯一的方法

如果您确实发现自己无法通过用于Numeric的SQL接口使用
DirectFunctionCall2
,则可以使用
int4\u Numeric
从C整数为另一端创建一个数值参数


如果您找不到解决方案,请发布在pgsql通用邮件列表上,您将有更多的人体验C扩展和源代码。如果您这样做了,请链接回本文。

完全避免此问题的一种方法是使用数据类型强制。使用要强制值的类型声明SQL函数,例如

CREATE FUNCTION foo(float8) RETURNS float8 AS 'SELECT $i' LANGUAGE SQL;
提供给该函数的任何值都将强制为该值:

SELECT foo(12);
即使显式指定类型也可以:

SELECT foo(12::numeric);

您的C代码将收到一个
double
。(这要归功于汤姆·莱恩,看这个。)

你绝对是对的。我想知道到底是什么。我在上面做了一个编辑,但只有在传入两个Numeric类型的对象时才起作用。grep
Numeric\u to\u double\u no\u overflow
和/或
PGTYPESnumeric\u to\u double
的源代码。代码将显示如何执行从
struct
double
的转换。不是一个完整的答案,因为数据类型强制是我的解决方案(请参阅我的答案),但这至少是一个指针。此版本可以工作,但需要使用两个数字。我想,需要弄清楚如何从整数或浮点8实例化一个数值。PG_功能_信息_V1(数字测试);基准数值测试(PG_函数参数){Numeric p=PG_GETARG_Numeric(0);数值答案=DatumGetNumeric(DirectFunctionCall2(Numeric_mul,NumericGetDatum(p_纬度),NumericGetDatum(p_纬度));PG_返回数值(答案);}