PostgreSQL SQL扩展 ---- C语言函数(二)

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核8GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: 可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)

四、编码规范

在我们转向更高级主题之前,我们将讨论一些PostgreSQL C语言函数的编码规则。虽然将非C语言编写的函数载入PostgreSQL是可能的,但这通常比较困难,因为诸如C++,FORTRAN,或者Pascal语言往往有着与C语言不同的调用约定。这意味着其他语言无法用C语言的方式在函数间传递参数和返回值。基于此,这里我们假设你的C语言函数是真真正正用C语言编写的。

编写、构建C函数的基本规则如下:

  • 使用 pg_config --includedir-server 检查你或者你用户的系统中是否安装了PostgreSQL服务端开发头文件。
  • 编译和链接可动态加载到PostgreSQL中的代码需要一些特定的标志。本文第五节会详细说明如何在特定的操作系统上完成此操作。
  • 切记要为你的共享库定义magic block”。
  • 分配内存时,请使用PostgreSQL的函数 pallocpfree 替代C语言库函数 mallocfree 。通过palloc 分配的内存在每个事务完成后会自动释放以预防内存泄露。
  • 使用结构前,一定要使用 memset 将结构的每个字节置0(或者在首次为它分配空间时使用 palloc0 )。因为即使给结构的每个字段都赋了值,也有可能仍旧存在为对齐而产生的含有垃圾值的填充字节(结构中的空洞)。不这样做,就很难支持hash索引或hash join,因为只能使用数据结构的有效位去计算hash值。有时规划器也会通过逐位比较来判断常量是否相等,因此如果逻辑上相等的值逐位比较却不相等,就会得到不想要的规划结果,从而产生错误的执行计划。
  • postgres.h中声明了一些PostgreSQL的内部类型,函数管理接口(PG_FUNCTION_ARGS......)则在 fmgr.h中,因此程序至少要包含这两个头文件。基于可移植性考虑,最好首先包含 postgres.h ,将其放在其他系统或用户头文件之前。包含 postgres.h 会自动包含 elog.hpalloc.h
  • 目标文件里的符号名必须唯一,不能与已经加载到PostgreSQL服务中的其他可执行符号名冲突。如果收到与此相关的错误消息,必须重命名相关的函数或变量。

五、编译链接动态加载函数

要想使用C语言编写的PostgreSQL扩展函数,必须使用特定的方法编译链接生成一个可被动态加载到服务器中的文件。准确的说,需要创建一个共享库。

本小节不会介绍C编译器、cc、链接器ld,相关内容请参阅你操作系统文档中的手册页。PostgreSQL的源码中也提供了几个可工作的示例,位于contrib目录。这些例子需要可用的PostgreSQL源码。

创建共享库与链接可执行文件类似:先将源码编译成目标文件,然后将目标文件链接到一起。目标文件要创建成位置无关代码(position-independent code (PIC)),从概念上讲,可执行文件可以将它们加载到内存中的任意位置。(适用于可执行文件的目标文件通常不用这种方式编译)。链接共享库时会使用一些特定的选项以与链接可执行文件区分开来。

接下来的示例中,我们假设你的源码名为foo.c,创建的共享库名为foo.so。除非另有说明,中间目标文件名为foo.o。共享库可包含不止一个目标文件,这里我们仅使用一个。

FreeBSD

创建PIC的编译选项是 -fPIC 。创建共享库的编译器选项是 -shared

gcc-fPIC-c foo.c
gcc-shared-o foo.so foo.o

Linux

创建PIC的编译选项是 -fPIC。创建共享库的编译选项是 -shared。下面是一个完整的示例:

cc-fPIC-c foo.c
cc-shared-o foo.so foo.o

macOS

假设已安装好开发工具,示例如下:

cc-c foo.c
cc-bundle-flat_namespace-undefined suppress -o foo.so foo.o

NetBSD

创建PIC的编译选项是 -fPIC。对于ELF系统,编译共享库的编译选项是 -shared 。对于早期的非ELF系统,使用 ld -Bshareable

gcc-fPIC-c foo.c
gcc-shared-o foo.so foo.o

OpenBSD

创建PIC的编译选项是 -fPIC。使用 ld -Bshareable 链接共享库。

gcc-fPIC-c foo.c
ld -Bshareable-o foo.so foo.o

Solaris

使用Sun编译器创建 PIC 的编译选项是 -KPIC ,使用GCC编译器的编译选项是 -fPIC 。链接共享库,可使用 -G 选项,对于GCC,也可以使用 -shared

cc-KPIC-c foo.c
cc-G-o foo.so foo.o

或者

gcc-fPIC-c foo.c
gcc-G-o foo.so foo.o

生成好的共享库文件可被加载到。给 CREATE FUNCTION 命令指定的文件名必须是共享库的名称而非中间目标文件的。 CREATE FUNCTION 命令可以省略系统标准的共享库扩展名(通常是 .so.sl),省略掉扩展名会有更好的可移植性。

六、组合类型参数

组合类型不具有类似C语言结构那样的固定布局。组合类型实例可以有空字段。此外,属于继承层次结构一部分的组合类型可以具有与同一继承层次结构的其他成员不同的字段。因此,PostgreSQL提供了一个函数接口,用于从C语言访问组合类型的字段。

Suppose we want to write a function to answer the query:

SELECT name, c_overpaid(emp,1500)AS overpaid
FROM emp
WHERE name ='Bill'OR name ='Sam';

使用版本-1调用约定,定义 c_overpaid 如下:

#include "postgres.h"#include "executor/executor.h"  /* for GetAttributeByName() */PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(c_overpaid);
Datumc_overpaid(PG_FUNCTION_ARGS)
{
HeapTupleHeadert=PG_GETARG_HEAPTUPLEHEADER(0);
int32limit=PG_GETARG_INT32(1);
boolisnull;
Datumsalary;
salary=GetAttributeByName(t, "salary", &isnull);
if (isnull)
PG_RETURN_BOOL(false);
/* Alternatively, we might prefer to do PG_RETURN_NULL() for null salary. */PG_RETURN_BOOL(DatumGetInt32(salary) >limit);
}

GetAttributeByNamePostgreSQL的系统函数,用于返回指定行的属性。它有三个参数:传递到函数中的HeapTupleHeader 类型, 所需属性的名称,以及告知该属性是否为空的返回参数。GetAttributeByName 返回一个 Datum 值,你可以使用相应的 DatumGetXXX() 函数将其转换到正确的数据类型。如果设置了null标志,则返回值没有意义;在尝试对结果执行任何操作之前,请始终检查null标志。

另外,GetAttributeByNum 通过列号而不是名字来选择目标属性。

以下命令声明供SQL调用的 c_overpaid 函数:

CREATE FUNCTION c_overpaid(emp,integer) RETURNS booleanAS'DIRECTORY/funcs','c_overpaid'    LANGUAGE C STRICT;

这里使用 STRICT 可不必检查输入参数是否为空。

七、返回行(组合类型)

从C语言函数返回一行或组合类型值,可以使用一组提供了宏和函数的特定API以隐藏构造组合数据类型的复杂性。使用这些API需要在源码中包含:

#include "funcapi.h"

构造组合类型(以后称为元组)有两种途径:从Datum值数组构造,或者从一个能被传递到元组列数据类型的输入转换函数的C字符串数组构造。不管用哪种方式,首先需要为元组结构获取或者构造一个 TupleDesc 描述符。当使用 Datums时,传递 TupleDescBlessTupleDesc,然后为每一行调用 heap_form_tuple 。当使用C字符串时,传递 TupleDescTupleDescGetAttInMetadata,然后为每一行调用 BuildTupleFromCStrings 。在返回元组集合的场景,在函数首次调用时可一次完成全部的设置步骤。

有几个辅助函数可用于设置所需的 TupleDesc。在大多数返回组合值的函数中建议如下调用:

TypeFuncClassget_call_result_type(FunctionCallInfofcinfo,
Oid*resultTypeId,
TupleDesc*resultTupleDesc)

将相同的 fcinfo 结构传递给调用函数本身(当然,这需要使用版本-1调用约定)。 resultTypeId 可以置空或者设置成一个局部变量地址用以接收函数的返回类型OID。 resultTupleDesc 可以是局部 TupleDesc 变量的地址。 TYPEFUNC_COMPOSITE 用于检查结果。这样做后, resultTupleDesc 已被需要的 TupleDesc 填充。(如果没有,可以报告错误function returning record called in context that cannot accept type record”。

获取TupleDesc比较旧的已经废弃的函数是:

TupleDescRelationNameGetTupleDesc(constchar*relname)

用于获取命名relation行类型的 TupleDesc

TupleDescTypeGetTupleDesc(Oidtypeoid, List*colaliases)

用于获取基于一个类型的OID的 TupleDesc 。可用于获取一个基类型或者组合类型的 TupleDesc 。无法用于返回 record 的函数,也无法解析多态类型。

一旦有了 TupleDesc ,调用

TupleDescBlessTupleDesc(TupleDesctupdesc)

如果计划使用Datums。

可使用:

AttInMetadata*TupleDescGetAttInMetadata(TupleDesctupdesc)

如果计划使用C字符串。如果写一个返回集合的函数,可以保存这些函数的结果到 FuncCallContext 结构 — 分别使用 tuple_descattinmeta 字段。

当采用Datums,使用

HeapTupleheap_form_tuple(TupleDesctupdesc, Datum*values, bool*isnull)

用给定的采用Datum方式的用户数据构造 HeapTuple

当采用C字符串方式,使用

HeapTupleBuildTupleFromCStrings(AttInMetadata*attinmeta, char**values)

用给定的采用C字符串方式的用户数据构造 HeapTuplevalues 是一个C字符串数组,每个数组表示返回行的一个属性。每一个C字符串都应该是属性数据类型输入函数所期望的形式。要给其中一个属性返回空值,需要将 values 数组中相应指针设置为 NULL 。每返回一行都需要调用此函数一次。

一旦从函数中构造好要返回的元组,它必须被转换成 Datum 。可使用

HeapTupleGetDatum(HeapTupletuple)

去转换 HeapTuple 到合法的Datum。如果函数返回单行,可直接返回些 Datum ,或在返回数据集的函数中做为当前行返回。

具体示例见下节。


相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
3月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS费用价格:MySQL、SQL Server、PostgreSQL和MariaDB引擎收费标准
阿里云RDS数据库支持MySQL、SQL Server、PostgreSQL、MariaDB,多种引擎优惠上线!MySQL倚天版88元/年,SQL Server 2核4G仅299元/年,PostgreSQL 227元/年起。高可用、可弹性伸缩,安全稳定。详情见官网活动页。
|
3月前
|
关系型数据库 分布式数据库 数据库
阿里云数据库收费价格:MySQL、PostgreSQL、SQL Server和MariaDB引擎费用整理
阿里云数据库提供多种类型,包括关系型与NoSQL,主流如PolarDB、RDS MySQL/PostgreSQL、Redis等。价格低至21元/月起,支持按需付费与优惠套餐,适用于各类应用场景。
|
3月前
|
存储 C语言
`scanf`是C语言中用于按格式读取标准输入的函数
`scanf`是C语言中用于按格式读取标准输入的函数,通过格式字符串解析输入并存入指定变量。需注意输入格式严格匹配,并建议检查返回值以确保读取成功,提升程序健壮性。
987 0
|
6月前
|
SQL 关系型数据库 MySQL
Go语言数据库编程:使用 `database/sql` 与 MySQL/PostgreSQL
Go语言通过`database/sql`标准库提供统一数据库操作接口,支持MySQL、PostgreSQL等多种数据库。本文介绍了驱动安装、连接数据库、基本增删改查操作、预处理语句、事务处理及错误管理等内容,涵盖实际开发中常用的技巧与注意事项,适合快速掌握Go语言数据库编程基础。
478 62
|
3月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS支持MySQL、SQL Server、PostgreSQL和MariaDB引擎
阿里云数据库RDS支持MySQL、SQL Server、PostgreSQL和MariaDB引擎,提供高性价比、稳定安全的云数据库服务,适用于多种行业与业务场景。
|
5月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
319 15
|
4月前
|
SQL 人工智能 数据挖掘
如何在`score`表中正确使用`COUNT`和`AVG`函数?SQL聚合函数COUNT与AVG使用指南
本文三桥君通过score表实例解析SQL聚合函数COUNT和AVG的常见用法。详解COUNT(studentNo)、COUNT(score)、COUNT()的区别,以及AVG函数对数值/字符型字段的不同处理,特别指出AVG()是无效语法。实战部分提供6个典型查询案例及结果,包含创建表、插入数据的完整SQL代码。产品专家三桥君强调正确理解函数特性(如空值处理、字段类型限制)对数据分析的重要性,帮助开发者避免常见误区,提升查询效率。
286 0
|
6月前
|
SQL 关系型数据库 PostgreSQL
CTE vs 子查询:深入拆解PostgreSQL复杂SQL的隐藏性能差异
本文深入探讨了PostgreSQL中CTE(公共表表达式)与子查询的选择对SQL性能的影响。通过分析两者底层机制,揭示CTE的物化特性及子查询的优化融合优势,并结合多场景案例对比执行效率。最终给出决策指南,帮助开发者根据数据量、引用次数和复杂度选择最优方案,同时提供高级优化技巧和版本演进建议,助力SQL性能调优。
585 1
|
10月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
386 1
一文彻底搞清楚C语言的函数
|
10月前
|
SQL 关系型数据库 OLAP
云原生数据仓库AnalyticDB PostgreSQL同一个SQL可以实现向量索引、全文索引GIN、普通索引BTREE混合查询,简化业务实现逻辑、提升查询性能
本文档介绍了如何在AnalyticDB for PostgreSQL中创建表、向量索引及混合检索的实现步骤。主要内容包括:创建`articles`表并设置向量存储格式,创建ANN向量索引,为表增加`username`和`time`列,建立BTREE索引和GIN全文检索索引,并展示了查询结果。参考文档提供了详细的SQL语句和配置说明。
289 2

热门文章

最新文章

推荐镜像

更多