本教程通过实现5个基于PostgreSQL的C语言自定义函数,使用PostgreSQL的numeric类型,实现了numeric加法、可变参数求和、带默认值的加法、数组求和、加权求和。目的是讲解如何在C语言自定义函数中使用PostgreSQL的内部类型、内置函数、可变参数、可变参数解析等内容。
一、核心概念说明
- PostgreSQL C函数:PostgreSQL底层由C实现,自定义C函数性能远高于PL/pgSQL,适合高频计算场景。
- Datum:PostgreSQL中表示数据的通用类型,所有数据在C函数中均以Datum传递。
- ArrayType:PostgreSQL数组的C语言表示类型,需通过
deconstruct_array解构为数组元素。 - DirectFunctionCall2:调用PostgreSQL内置函数的宏(
2表示2个参数),如numeric_add是内置的数值加法函数。
二、代码完整解析
先将源代码保存为numeric_add.c
#include "postgres.h"
#include "fmgr.h"
#include "utils/array.h"
#include "utils/fmgrprotos.h"
#include "catalog/pg_type.h"
#include "utils/lsyscache.h"
PG_MODULE_MAGIC;
/* 基本版本:numeric_add */
PG_FUNCTION_INFO_V1(my_numeric_add);
Datum
my_numeric_add(PG_FUNCTION_ARGS)
{
Datum num1;
Datum num2;
Datum result;
/* 检查参数是否为NULL */
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
PG_RETURN_NULL();
/* 获取Datum参数 */
num1 = PG_GETARG_DATUM(0);
num2 = PG_GETARG_DATUM(1);
/* 调用PostgreSQL内置的numeric_add函数 */
result = DirectFunctionCall2(numeric_add, num1, num2);
PG_RETURN_DATUM(result);
}
/* 高级版本:支持多个参数相加,使用PostgreSQL的聚合函数风格 */
PG_FUNCTION_INFO_V1(my_numeric_sum);
Datum
my_numeric_sum(PG_FUNCTION_ARGS)
{
int nargs = PG_NARGS();
Datum sum = (Datum) 0;
bool sum_isnull = true;
int i;
bool variadic = get_fn_expr_variadic(fcinfo->flinfo);
Datum *args_res;
bool *nulls_res;
Oid *types_res;
if(variadic)
{
ArrayType *array_in;
Oid element_type;
bool typbyval;
char typalign;
int16 typlen;
Assert(PG_NARGS() >= 1);
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
array_in = PG_GETARG_ARRAYTYPE_P(0);
element_type = ARR_ELEMTYPE(array_in);
get_typlenbyvalalign(element_type,
&typlen, &typbyval, &typalign);
deconstruct_array(array_in, element_type, typlen, typbyval,
typalign, &args_res, &nulls_res,
&nargs);
Datum total = args_res[0];
for(i=1; i<nargs; i++)
{
if (!nulls_res[i])
{
Datum current = args_res[i];
total = DirectFunctionCall2(numeric_add, total, current);
}
}
sum = total;
sum_isnull = false;
}
else {
/* 遍历所有参数 */
for (i = 0; i < nargs; i++)
{
if (!PG_ARGISNULL(i))
{
Datum current = PG_GETARG_DATUM(i);
if (sum_isnull)
{
/* 第一个非NULL值 */
sum = current;
sum_isnull = false;
}
else
{
/* 累加到当前和 */
sum = DirectFunctionCall2(numeric_add, sum, current);
}
}
}
}
if (sum_isnull)
PG_RETURN_NULL();
else
PG_RETURN_DATUM(sum);
}
/* 安全加法:处理NULL,返回默认值 */
PG_FUNCTION_INFO_V1(my_numeric_add_with_default);
Datum
my_numeric_add_with_default(PG_FUNCTION_ARGS)
{
Datum num1;
Datum num2;
Datum default_val;
Datum result;
bool num1_isnull = PG_ARGISNULL(0);
bool num2_isnull = PG_ARGISNULL(1);
bool default_isnull = PG_ARGISNULL(2);
/* 如果两个参数都为NULL,返回NULL或默认值 */
if (num1_isnull && num2_isnull)
{
if (default_isnull)
PG_RETURN_NULL();
else
{
default_val = PG_GETARG_DATUM(2);
PG_RETURN_DATUM(default_val);
}
}
/* 如果只有一个参数为NULL,用0或默认值替代 */
if (num1_isnull)
{
if (default_isnull)
num1 = DirectFunctionCall1(int4_numeric, Int32GetDatum(0));
else
num1 = PG_GETARG_DATUM(2);
}
else
{
num1 = PG_GETARG_DATUM(0);
}
if (num2_isnull)
{
if (default_isnull)
num2 = DirectFunctionCall1(int4_numeric, Int32GetDatum(0));
else
num2 = PG_GETARG_DATUM(2);
}
else
{
num2 = PG_GETARG_DATUM(1);
}
/* 执行加法运算 */
result = DirectFunctionCall2(numeric_add, num1, num2);
PG_RETURN_DATUM(result);
}
/* 批量加法:处理numeric数组相加 */
PG_FUNCTION_INFO_V1(my_numeric_array_sum);
Datum
my_numeric_array_sum(PG_FUNCTION_ARGS)
{
ArrayType *input_array;
Datum sum = (Datum) 0;
bool sum_isnull = true;
Datum *elems;
bool *nulls;
int nelems;
Oid elem_type;
int16 elem_width;
bool elem_byval;
char elem_align;
int i;
/* 检查参数是否为NULL */
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
input_array = PG_GETARG_ARRAYTYPE_P(0);
/* 验证数组元素类型是否为numeric */
elem_type = ARR_ELEMTYPE(input_array);
if (elem_type != NUMERICOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("输入数组元素类型必须为numeric")));
/* 获取数组信息并解构数组 */
get_typlenbyvalalign(elem_type, &elem_width, &elem_byval, &elem_align);
deconstruct_array(input_array, elem_type, elem_width,
elem_byval, elem_align,
&elems, &nulls, &nelems);
/* 遍历数组元素,计算总和 */
for (i = 0; i < nelems; i++)
{
if (!nulls[i])
{
Datum current = elems[i];
if (sum_isnull)
{
/* 第一个非NULL值 */
sum = current;
sum_isnull = false;
}
else
{
/* 累加到当前和 */
sum = DirectFunctionCall2(numeric_add, sum, current);
}
}
}
/* 释放数组内存 */
pfree(elems);
pfree(nulls);
if (sum_isnull)
PG_RETURN_NULL();
else
PG_RETURN_DATUM(sum);
}
/* 加权求和:两个数组对应元素相乘后求和 */
PG_FUNCTION_INFO_V1(my_numeric_weighted_sum);
Datum
my_numeric_weighted_sum(PG_FUNCTION_ARGS)
{
ArrayType *values_array;
ArrayType *weights_array;
Datum result = (Datum) 0;
bool result_isnull = true;
Datum return_null = (Datum) 0;
Datum *values;
Datum *weights;
bool *values_nulls;
bool *weights_nulls;
int nvalues;
int nweights;
Oid elem_type;
int16 elem_width;
bool elem_byval;
char elem_align;
int i;
/* 检查参数是否为NULL */
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
PG_RETURN_NULL();
values_array = PG_GETARG_ARRAYTYPE_P(0);
weights_array = PG_GETARG_ARRAYTYPE_P(1);
/* 验证数组元素类型是否为numeric */
elem_type = ARR_ELEMTYPE(values_array);
if (elem_type != NUMERICOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("数值数组元素类型必须为numeric")));
if (ARR_ELEMTYPE(weights_array) != NUMERICOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("权重数组元素类型必须为numeric")));
/* 检查数组长度 */
nvalues = ARR_DIMS(values_array)[0];
nweights = ARR_DIMS(weights_array)[0];
if (nvalues != nweights)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("数值数组和权重数组长度必须相同")));
/* 获取数组信息并解构数组 */
get_typlenbyvalalign(elem_type, &elem_width, &elem_byval, &elem_align);
deconstruct_array(values_array, elem_type, elem_width,
elem_byval, elem_align,
&values, &values_nulls, &nvalues);
deconstruct_array(weights_array, elem_type, elem_width,
elem_byval, elem_align,
&weights, &weights_nulls, &nweights);
/* 计算加权和 */
for (i = 0; i < nvalues; i++)
{
if (!values_nulls[i] && !weights_nulls[i])
{
Datum product = DirectFunctionCall2(numeric_mul, values[i], weights[i]);
if (result_isnull)
{
result = product;
result_isnull = false;
}
else
{
Datum new_result = DirectFunctionCall2(numeric_add, result, product);
/* 释放中间结果的内存 */
if (result != (Datum) 0)
pfree(DatumGetPointer(result));
pfree(DatumGetPointer(product));
result = new_result;
}
}
}
/* 释放数组内存 */
pfree(values);
pfree(values_nulls);
pfree(weights);
pfree(weights_nulls);
if (result_isnull)
PG_RETURN_NULL();
else
PG_RETURN_DATUM(result);
}
下面分函数解析核心逻辑:
2.1 基础加法函数(my_numeric_add)
#include "postgres.h"
#include "fmgr.h"
#include "utils/array.h"
#include "utils/fmgrprotos.h"
#include "catalog/pg_type.h"
#include "utils/lsyscache.h"
PG_MODULE_MAGIC; // 必须的模块标识,PostgreSQL强制要求
/* 基本版本:numeric_add */
PG_FUNCTION_INFO_V1(my_numeric_add); // 声明函数版本(V1是当前标准)
Datum
my_numeric_add(PG_FUNCTION_ARGS)
{
Datum num1;
Datum num2;
Datum result;
/* 检查参数是否为NULL */
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
PG_RETURN_NULL(); // 任意参数NULL则返回NULL
/* 获取Datum参数(0/1对应第1/2个参数) */
num1 = PG_GETARG_DATUM(0);
num2 = PG_GETARG_DATUM(1);
/* 调用PostgreSQL内置的numeric_add函数(2个参数) */
result = DirectFunctionCall2(numeric_add, num1, num2);
PG_RETURN_DATUM(result); // 返回计算结果
}
核心逻辑:
最基础的二元函数,检查参数NULL值 → 获取参数 → 调用内置加法 → 返回结果。
这个函数调用内置函数numeric_add实现numeric加法。根据PostgreSQL的编码规范,不能直接调用内置函数,根据使用场景,可使用:
DirectFunctionCallXXX系列函数CallerFInfoFunctionCallXXX系列函数OidFunctionCallXXX系列函数
如果内置函数不需要利用调用者函数的 flinfo 参数来初始化函数调用所需的 fcinfo 结构,可使用DirectFunctionCallXXX系列函数,否则需要使用CallerFInfoFunctionCallXXX或者OidFunctionCallXXX系列函数。CallerFInfoFunctionCallXXX和OidFunctionCallXXX的区别在于参数个数,CallerFInfoFunctionCallXXX只提供了支持1个或者2个参数的函数,如果参数个数超过两个,就只能使用OidFunctionCallXXX系列函数了。OidFunctionCallXXX的使用稍微麻烦些,需要先取得内置函数的OID,然后调用OidFunctionCallXXX系列函数。下面举个简单的例子:
char *value = "{1,2,3,4,5}";
Oid functionOid = fmgr_internal_function("array_in");
if(functionOid != InvalidOid) {
res = OidFunctionCall3(
functionOid,
CStringGetDatum(value),
Int32GetDatum(INT4OID),
Int32GetDatum(-1));
}
调用array_in需要三个参数,并且使用了调用者的fcinfo,因此需要使用OidFunctionCall3。fmgr_internal_function函数用于获取内置函数的Oid。
2.2 可变参数求和(my_numeric_sum)
这是最核心的可变参数函数,支持两种调用方式:
- 直接传多个numeric参数(如
my_numeric_sum(1,2,3)); - 传numeric数组(如
my_numeric_sum(VARIADIC ARRAY[1,2,3]))。
PG_FUNCTION_INFO_V1(my_numeric_sum);
Datum
my_numeric_sum(PG_FUNCTION_ARGS)
{
int nargs = PG_NARGS(); // 获取参数总数
Datum sum = (Datum) 0;
bool sum_isnull = true; // 标记总和是否为NULL
int i;
bool variadic = get_fn_expr_variadic(fcinfo->flinfo); // 判断是否为可变数组调用
Datum *args_res; // 存储数组解构后的参数
bool *nulls_res; // 存储参数是否为NULL的标记
Oid *types_res;
if(variadic) // 方式2:可变数组调用(VARIADIC)
{
ArrayType *array_in;
Oid element_type;
bool typbyval;
char typalign;
int16 typlen;
Assert(PG_NARGS() >= 1); // 断言:至少1个参数(数组)
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
array_in = PG_GETARG_ARRAYTYPE_P(0); // 获取数组参数
element_type = ARR_ELEMTYPE(array_in); // 获取数组元素类型
// 获取元素类型的内存属性(长度、是否传值、对齐方式)
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
// 解构数组为元素数组
deconstruct_array(array_in, element_type, typlen, typbyval,
typalign, &args_res, &nulls_res, &nargs);
// 数组元素累加
Datum total = args_res[0];
for(i=1; i<nargs; i++)
{
if (!nulls_res[i]) // 跳过NULL元素
{
Datum current = args_res[i];
total = DirectFunctionCall2(numeric_add, total, current);
}
}
sum = total;
sum_isnull = false;
}
else {
// 方式1:直接传多个参数
/* 遍历所有参数 */
for (i = 0; i < nargs; i++)
{
if (!PG_ARGISNULL(i)) // 跳过NULL参数
{
Datum current = PG_GETARG_DATUM(i);
if (sum_isnull)
{
/* 第一个非NULL值:初始化总和 */
sum = current;
sum_isnull = false;
}
else
{
/* 累加到当前和 */
sum = DirectFunctionCall2(numeric_add, sum, current);
}
}
}
}
if (sum_isnull)
PG_RETURN_NULL(); // 所有参数都是NULL则返回NULL
else
PG_RETURN_DATUM(sum);
}
核心逻辑:
get_fn_expr_variadic判断调用方式(普通参数/可变数组);- 数组调用时通过
deconstruct_array解构数组; - 遍历所有非NULL参数,调用
numeric_add累加。
2.3 带默认值的加法(my_numeric_add_with_default)
PG_FUNCTION_INFO_V1(my_numeric_add_with_default);
Datum
my_numeric_add_with_default(PG_FUNCTION_ARGS)
{
Datum num1;
Datum num2;
Datum default_val;
Datum result;
bool num1_isnull = PG_ARGISNULL(0); // 第1个参数是否NULL
bool num2_isnull = PG_ARGISNULL(1); // 第2个参数是否NULL
bool default_isnull = PG_ARGISNULL(2); // 第3个参数(默认值)是否NULL
/* 两个参数都为NULL:返回默认值或NULL */
if (num1_isnull && num2_isnull)
{
if (default_isnull)
PG_RETURN_NULL();
else
{
default_val = PG_GETARG_DATUM(2);
PG_RETURN_DATUM(default_val);
}
}
/* 单个参数NULL:用0或默认值替代 */
if (num1_isnull)
{
if (default_isnull)
num1 = DirectFunctionCall1(int4_numeric, Int32GetDatum(0)); // 0转numeric
else
num1 = PG_GETARG_DATUM(2);
}
else
{
num1 = PG_GETARG_DATUM(0);
}
if (num2_isnull)
{
if (default_isnull)
num2 = DirectFunctionCall1(int4_numeric, Int32GetDatum(0));
else
num2 = PG_GETARG_DATUM(2);
}
else
{
num2 = PG_GETARG_DATUM(1);
}
/* 执行加法 */
result = DirectFunctionCall2(numeric_add, num1, num2);
PG_RETURN_DATUM(result);
}
核心逻辑:
处理NULL参数的优雅方式,支持自定义默认值(无默认值时用0替代)。
2.4 数组求和(my_numeric_array_sum)
PG_FUNCTION_INFO_V1(my_numeric_array_sum);
Datum
my_numeric_array_sum(PG_FUNCTION_ARGS)
{
ArrayType *input_array;
Datum sum = (Datum) 0;
bool sum_isnull = true;
Datum *elems; // 数组元素
bool *nulls; // 元素NULL标记
int nelems; // 元素数量
Oid elem_type;
int16 elem_width;
bool elem_byval;
char elem_align;
int i;
/* 检查参数是否为NULL */
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
input_array = PG_GETARG_ARRAYTYPE_P(0); // 获取数组参数
/* 强制校验数组元素类型为numeric */
elem_type = ARR_ELEMTYPE(input_array);
if (elem_type != NUMERICOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("输入数组元素类型必须为numeric")));
/* 获取数组元素属性并解构 */
get_typlenbyvalalign(elem_type, &elem_width, &elem_byval, &elem_align);
deconstruct_array(input_array, elem_type, elem_width,
elem_byval, elem_align,
&elems, &nulls, &nelems);
/* 遍历累加 */
for (i = 0; i < nelems; i++)
{
if (!nulls[i])
{
Datum current = elems[i];
if (sum_isnull)
{
sum = current;
sum_isnull = false;
}
else
{
sum = DirectFunctionCall2(numeric_add, sum, current);
}
}
}
/* 释放数组内存(关键:避免内存泄漏) */
pfree(elems);
pfree(nulls);
if (sum_isnull)
PG_RETURN_NULL();
else
PG_RETURN_DATUM(sum);
}
核心逻辑:
- 强制校验数组元素类型为numeric;
- 解构数组后遍历累加;
- 手动释放数组内存(
pfree),避免内存泄漏。
2.5 加权求和(my_numeric_weighted_sum)
PG_FUNCTION_INFO_V1(my_numeric_weighted_sum);
Datum
my_numeric_weighted_sum(PG_FUNCTION_ARGS)
{
ArrayType *values_array; // 数值数组
ArrayType *weights_array; // 权重数组
Datum result = (Datum) 0;
bool result_isnull = true;
Datum *values;
Datum *weights;
bool *values_nulls;
bool *weights_nulls;
int nvalues;
int nweights;
Oid elem_type;
int16 elem_width;
bool elem_byval;
char elem_align;
int i;
/* 检查参数是否为NULL */
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
PG_RETURN_NULL();
values_array = PG_GETARG_ARRAYTYPE_P(0);
weights_array = PG_GETARG_ARRAYTYPE_P(1);
/* 校验数组元素类型 */
elem_type = ARR_ELEMTYPE(values_array);
if (elem_type != NUMERICOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("数值数组元素类型必须为numeric")));
if (ARR_ELEMTYPE(weights_array) != NUMERICOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("权重数组元素类型必须为numeric")));
/* 校验数组长度一致 */
nvalues = ARR_DIMS(values_array)[0];
nweights = ARR_DIMS(weights_array)[0];
if (nvalues != nweights)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("数值数组和权重数组长度必须相同")));
/* 解构两个数组 */
get_typlenbyvalalign(elem_type, &elem_width, &elem_byval, &elem_align);
deconstruct_array(values_array, elem_type, elem_width,
elem_byval, elem_align,
&values, &values_nulls, &nvalues);
deconstruct_array(weights_array, elem_type, elem_width,
elem_byval, elem_align,
&weights, &weights_nulls, &nweights);
/* 计算加权和:value[i] * weight[i] 累加 */
for (i = 0; i < nvalues; i++)
{
if (!values_nulls[i] && !weights_nulls[i])
{
// 元素相乘
Datum product = DirectFunctionCall2(numeric_mul, values[i], weights[i]);
if (result_isnull)
{
result = product;
result_isnull = false;
}
else
{
// 累加到结果
Datum new_result = DirectFunctionCall2(numeric_add, result, product);
/* 释放中间结果内存(避免泄漏) */
if (result != (Datum) 0)
pfree(DatumGetPointer(result));
pfree(DatumGetPointer(product));
result = new_result;
}
}
}
/* 释放数组内存 */
pfree(values);
pfree(values_nulls);
pfree(weights);
pfree(weights_nulls);
if (result_isnull)
PG_RETURN_NULL();
else
PG_RETURN_DATUM(result);
}
核心逻辑:
- 校验两个数组的类型和长度;
- 对应元素相乘后累加;
- 释放中间结果和数组内存,避免内存泄漏。
三、编译与安装
3.1 编译为共享库(CMakeLists.txt)
cmake_minimum_required(VERSION 3.25)
project(numeric_add C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_BUILD_TYPE debug)
list(APPEND flags "-fPIC")
find_program(PG_CONFIG pg_config REQUIRED)
execute_process(COMMAND ${PG_CONFIG} --includedir-server
OUTPUT_VARIABLE POSTGRESQL_INCLUDE_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${PG_CONFIG} --libdir
OUTPUT_VARIABLE POSTGRESQL_LIB_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${PG_CONFIG} --libs
OUTPUT_VARIABLE POSTGRESQL_LIBS
OUTPUT_STRIP_TRAILING_WHITESPACE)
add_library(numeric_add SHARED numeric_add.c)
# 设置生成的共享库名称,去掉lib前缀
set_target_properties(numeric_add PROPERTIES PREFIX "")
# 最终so文件名
set_target_properties(numeric_add PROPERTIES OUTPUT_NAME numeric_add)
# 安装目录
install(TARGETS numeric_add LIBRARY DESTINATION ${POSTGRESQL_LIB_DIR})
target_compile_options(numeric_add PUBLIC "-fPIC" "-g3" "-O0")
target_link_options(numeric_add PUBLIC "-shared")
target_include_directories(numeric_add PUBLIC ${POSTGRESQL_INCLUDE_DIR})
target_link_directories(numeric_add PUBLIC ${POSTGRESQL_LIB_DIR})
cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=1
make
3.2 在PostgreSQL中创建函数
- 连接PostgreSQL:
psql 依次创建5个函数(执行以下SQL):
-- 1. 基础加法函数 CREATE OR REPLACE FUNCTION my_numeric_add(numeric, numeric) RETURNS numeric AS '$libdir/numeric_functions' LANGUAGE C IMMUTABLE STRICT; -- 2. 可变参数求和函数(支持多参数/数组) CREATE OR REPLACE FUNCTION my_numeric_sum(VARIADIC numeric[]) RETURNS numeric AS '$libdir/numeric_functions' LANGUAGE C IMMUTABLE; -- 3. 带默认值的加法函数 CREATE OR REPLACE FUNCTION my_numeric_add_with_default(numeric, numeric, numeric) RETURNS numeric AS '$libdir/numeric_functions' LANGUAGE C IMMUTABLE; -- 4. 数组求和函数 CREATE OR REPLACE FUNCTION my_numeric_array_sum(numeric[]) RETURNS numeric AS '$libdir/numeric_functions' LANGUAGE C IMMUTABLE STRICT; -- 5. 加权求和函数 CREATE OR REPLACE FUNCTION my_numeric_weighted_sum(numeric[], numeric[]) RETURNS numeric AS '$libdir/numeric_functions' LANGUAGE C IMMUTABLE STRICT;$libdir:PostgreSQL扩展目录的简写;IMMUTABLE:函数结果仅依赖输入(无副作用),可优化性能;STRICT:任意参数NULL则返回NULL(部分函数手动处理NULL,故不加)。
四、测试验证
4.1 测试基础加法(my_numeric_add)
-- 正常调用
SELECT my_numeric_add(1.5::numeric, 2.5::numeric); -- 输出4.0
-- NULL参数
SELECT my_numeric_add(NULL, 2.5::numeric); -- 输出NULL
4.2 测试可变参数求和(my_numeric_sum)
-- 方式1:直接传多参数
SELECT my_numeric_sum(1::numeric, 2::numeric, 3::numeric); -- 输出6.0
-- 方式2:传数组(需VARIADIC关键字)
SELECT my_numeric_sum(VARIADIC ARRAY[1::numeric, 2::numeric, 3::numeric]); -- 输出6.0
-- 包含NULL参数
SELECT my_numeric_sum(1::numeric, NULL, 3::numeric); -- 输出4.0
4.3 测试带默认值的加法(my_numeric_add_with_default)
-- 正常调用
SELECT my_numeric_add_with_default(1.5::numeric, 2.5::numeric, 0::numeric); -- 输出4.0
-- 第一个参数NULL,用0替代
SELECT my_numeric_add_with_default(NULL, 2.5::numeric, NULL); -- 输出2.5
-- 两个参数NULL,返回默认值
SELECT my_numeric_add_with_default(NULL, NULL, 10::numeric); -- 输出10.0
4.4 测试数组求和(my_numeric_array_sum)
-- 正常数组
SELECT my_numeric_array_sum(ARRAY[1::numeric, 2::numeric, 3::numeric]); -- 输出6.0
-- 包含NULL的数组
SELECT my_numeric_array_sum(ARRAY[1::numeric, NULL, 3::numeric]); -- 输出4.0
-- 非numeric数组(报错)
SELECT my_numeric_array_sum(ARRAY[1,2,3]); -- 报错:元素类型必须为numeric
4.5 测试加权求和(my_numeric_weighted_sum)
-- 正常调用:(1*0.1)+(2*0.2)+(3*0.3) = 0.1+0.4+0.9 = 1.4
SELECT my_numeric_weighted_sum(ARRAY[1::numeric,2::numeric,3::numeric], ARRAY[0.1::numeric,0.2::numeric,0.3::numeric]); -- 输出1.4
-- 数组长度不一致(报错)
SELECT my_numeric_weighted_sum(ARRAY[1::numeric,2::numeric], ARRAY[0.1::numeric]); -- 报错:长度必须相同