# 35.9\. C-语言函数
用户定义的函数可以用 C 写(或者是与C兼容的语言,比如C++)。 这样的函数被编译进动态加载对象(共享库)并且由服务器根据需要加载。 动态加载的特性是"C 语言函数"和"内部函数"之间的区别—不过, 实际的编码习惯在两者之间实际上是一样的。因此, 标准的内部函数库为写用户定义C函数提供了大量最好的样例。
目前对 C 函数有两种调用约定。新的"版本-1"的调用约定是通过为该函数书写一个 `PG_FUNCTION_INFO_V1()`宏来标识的,像下面演示的那样。缺少这个宏表示一个 老风格的("版本-0")函数。两种风格里在`CREATE FUNCTION`里声明的都是`C`。 现在老风格的函数已经废弃了,主要是因为移植性原因和缺乏功能, 不过出于兼容性原因,系统仍然支持它。
## 35.9.1\. 动态加载
当用户定义的函数第一次被服务器会话调用时, 动态加载器才把可加载对象文件里的函数目标码加载进内存。 因此,用于用户定义 C 函数的`CREATE FUNCTION`必须为函数声明两个信息: 可加载对象文件名、在目标文件里调用的 C 函数名(连接符号)。 如果没有明确声明 C 函数名,那么就假设它与 SQL 函数名相同。
基于在`CREATE FUNCTION`命令中给出的名字, 下面的算法用于定位共享对象文件:
1. 如果名字是一个绝对路径,则加载给出的文件。
2. 如果名字以字符串`$libdir`开头, 那么该部分将被PostgreSQL库目录名代替, 该目录是在编译时确定的。
3. 如果名字不包含目录部分, 那么在配置参数[dynamic_library_path](#calibre_link-980). 声明的路径里查找。
4. 如果没有在路径里找到该文件,或者它包含一个非绝对目录部分, 那么动态加载器就会试图直接拿这个名字来加载, 这样几乎可以肯定是要失败的(依靠当前工作目录是不可靠的)。
如果这个顺序不管用,那么就给这个名字加上平台相关的共享库文件扩展名(通常是`.so`), 然后再重新按照上面的过程找一遍。如果还是失败,那么加载失败。
建议使用相对于`$libdir`的目录或者通过动态库路径定位共享库。 这样,如果新版本安装在一个不同的位置,那么就可以简化版本升级。 `$libdir`的实际目录位置可以 用`pg_config --pkglibdir`命令找到。
运行PostgreSQL服务器的用户ID 必须可以遍历路径到达想加载的文件。 一个常见的错误就是把该文件或者一个高层目 录的权限设置为postgres用户不可读和/或不能执行。
在任何情况下,在`CREATE FUNCTION`命令里给出的文件 名是在系统表里按照文本记录的, 因此,如果需要再次加载,那么会再次运行这个过程。
> **Note:** PostgreSQL不会自动编译 C 函数; 在使用`CREATE FUNCTION`命令之前你必须编译它。 参阅[Section 35.9.6](#calibre_link-926)获取更多信息。
为了确保不会错误加载共享库文件,从PostgreSQL 开始将检查那个文件的"magic block",这允许服务器以检查明显的不兼容性。 比如不同版本PostgreSQL的编译代码。 magic block需要被作为PostgreSQL 8.2。 为了包含"magic block", 请在包含了`fmgr.h`头文件之后, 将下面的内容写进一个(也只能是一个)模块的源代码文件中:
```
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
```
如果不打算兼容8.2 PostgreSQL之前的版本, `#ifdef`测试也可以省略。
动态加载对象文件在首次使用之后将一直滞留在内存中。 在同一个会话中的下一次调用将只需查找符号表的很小开销。 如果你想强制重新加载(比如重新编译之后), 可以重新开始一个新的会话。
动态加载文件也可以包含初始化函数和结束函数。 如果包含一个名为`_PG_init`的函数,那么该函数将在该文件被加载后立即执行, 该函数不能接受任何参数并且必须返回 void 。 如果包含一个名为`_PG_fini`的函数,那么该函数将在该文件即将被卸载前执行, 同样,该函数不能接受任何参数并且必须返回 void 。 需要注意的是`_PG_fini`仅在该文件即将被卸载前执行而不是在会话结束的时候执行。 目前,卸载被禁止并且将不会发生,但是这可能在将来改变。
## 35.9.2\. 基本类型的C语言函数
要知道如何写C语言函数,就必须知道PostgreSQL在 内部如何表现基本数据类型以及如何传入及传出函数。 PostgreSQL内部把基本类型当作"一块内存"看待。 定义在某种类型上的用户定义函数实际上定义了 PostgreSQL对该数据类型可能的操作。也就是说, PostgreSQL只是从磁盘读取和存储该数据 类型并使用你定义的函数来输入、处理、输出数据。
基本类型可以有下面三种内部形态(格式)之一:
* 传递数值,定长
* 传递引用,定长
* 传递引用,变长
传递数值的类型长度只能是1, 2, 4字节。如果`sizeof(Datum)` 在你的机器上是8的话,那么还有8字节。你要仔细定义你的类型, 确保它们在任何体系平台上都是相同尺寸(字节)。例如,`long` 是一个危险的类型, 因为在一些机器上它是4字节而在另外一些机器上是8字节, 而`int`在大多数Unix机器上都是4字节的。 在一个Unix机器上的`int4` 合理实现可能是:
```
/* 4-byte integer, passed by value */
typedef int int4;
```
实际PostgreSQL C代码调用此`int32`类型, 因为它是C中的惯例,`int``_XX_` 意味着`_XX_` _bits_。 注意因此C类型`int8`的大小是1字节。SQL类型`int8`被称为C中`int64`。参见 [Table 35-1](#calibre_link-981)。
另外,任何尺寸的定长类型都可以是传递引用型。例如, 下面是一个PostgreSQL类型的实现:
```
/* 16-byte structure, passed by reference */
typedef struct
{
double x, y;
} Point;
```
只能使用指向这些类型的指针在PostgreSQL函数里传入和传出数据。 要返回这样类型的值,用`palloc`分配正确数量的内存,填充这些内存, 然后返回一个指向它的指针。如果只是想返回和输入参数类型与数值都相同的数值, 可以忽略额外的`palloc`,只要返回指向输入数值的指针就行。
最后,所有变长类型同样也只能通过引用来传递。 所有变长类型必须以一个4字节的长度域开始,通过`SET_VARSIZE`设置, 没有直接设置这个字段! 所有存储在该类型中的数据必须放在紧接着长度域的存储空间里。 长度域是结构的全长,也就是说,包括长度域本身的长度。
另外一个重要的点是避免数据类型值中留下未初始化的位;比如,请注意任何对齐填充字节 溢出的零可能出现在结构体中。没有这些,你的数据类型的逻辑上等价常量可能被规划器看做是 不平等的,导致低效(虽然是不正确的)规划。
| **Warning** |
|:--- |
| _绝对不要_修改一个引用传递的输入值,否则很可能破坏磁盘上的数据。 因为指针很可能直接指向一个磁盘缓冲区。 这条规则的唯一例外在[Section 35.10](#calibre_link-836)里。 |
比如,我们可以用下面的方法定义一个`text`类型:
```
typedef struct {
int32 length;
char data[1];
} text;
```
显然,上面声明的数据域长度不足以存储任何可能的字符串。 因为在C中不可能声明变长结构,所以我们倚赖这样的知识: C编译器不会对数组下标进行范围检查。只需要分配足够的空间, 然后把数组当做已经声明为合适长度的变量访问。这是一个常用的技巧, 你可以在许多C教科书中读到。
当处理变长类型时,必须仔细分配正确的内存数量并正确设置长度域。 例如,如果想在一个`text`结构里存储40字节, 我们可能会使用像下面的代码片段:
```
#include "postgres.h"
...
char buffer[40]; /* our source data */
...
text *destination = (text *) palloc(VARHDRSZ + 40);
SET_VARSIZE(destination, VARHDRSZ + 40);
memcpy(destination->data, buffer, 40);
...
```
`VARHDRSZ`等价于`sizeof(int32)`, 但是我们认为用宏`VARHDRSZ`表示附加尺寸是用于变长类型的更好风格。 同时,该长度字段_必须_使用`SET_VARSIZE`宏设置, 而不是简单的赋值。
[Table 35-1](#calibre_link-981)列出了书写 使用PostgreSQL内置类型的 C 函数里需要知道的 SQL 类型与 C 类型的对应关系。"定义在" 列给出了需要包含以获取该类型定义的头文件。 实际定义可能在列表文件中包含的不同文件中。我们建议用户坚持定义的接口。 注意,你应该总是首先包括`postgres.h`, 因为它声明了许多你需要的东西。
**Table 35-1\. 与内建SQL类型等效的C类型**
| SQL Type | C Type | Defined In |
| --- | --- | --- |
| `abstime` | `AbsoluteTime` | `utils/nabstime.h` |
| `boolean` | `bool` | `postgres.h` (可能编译器内置) |
| `box` | `BOX*` | `utils/geo_decls.h` |
| `bytea` | `bytea*` | `postgres.h` |
| `"char"` | `char` | (编译器内置) |
| `character` | `BpChar*` | `postgres.h` |
| `cid` | `CommandId` | `postgres.h` |
| `date` | `DateADT` | `utils/date.h` |
| `smallint` (`int2`) | `int16` | `postgres.h` |
| `int2vector` | `int2vector*` | `postgres.h` |
| `integer` (`int4`) | `int32` | `postgres.h` |
| `real` (`float4`) | `float4*` | `postgres.h` |
| `double precision` (`float8`) | `float8*` | `postgres.h` |
| `interval` | `Interval*` | `datatype/timestamp.h` |
| `lseg` | `LSEG*` | `utils/geo_decls.h` |
| `name` | `Name` | `postgres.h` |
| `oid` | `Oid` | `postgres.h` |
| `oidvector` | `oidvector*` | `postgres.h` |
| `path` | `PATH*` | `utils/geo_decls.h` |
| `point` | `POINT*` | `utils/geo_decls.h` |
| `regproc` | `regproc` | `postgres.h` |
| `reltime` | `RelativeTime` | `utils/nabstime.h` |
| `text` | `text*` | `postgres.h` |
| `tid` | `ItemPointer` | `storage/itemptr.h` |
| `time` | `TimeADT` | `utils/date.h` |
| `time with time zone` | `TimeTzADT` | `utils/date.h` |
| `timestamp` | `Timestamp*` | `datatype/timestamp.h` |
| `tinterval` | `TimeInterval` | `utils/nabstime.h` |
| `varchar` | `VarChar*` | `postgres.h` |
| `xid` | `TransactionId` | `postgres.h` |
既然我们已经讨论了基本类型所有可能的结构, 我们便可以用实际的函数举一些例子。
## 35.9.3\. 版本-0调用约定
先提供现在已经不提倡了的"老风格"—因为比较容易迈出第一步。 在版本-0方法中,此风格 C 函数的参数和结果用普通 C 风格声明, 但是要小心使用上面显示的 SQL 数据类型的 C 表现形式。
下面是一些例子:
```
#include "postgres.h"
#include <string.h>
#include "utils/geo_decls.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
/* 传递数值 */
int
add_one(int arg)
{
return arg + 1;
}
/* 传递引用,定长 */
float8 *
add_one_float8(float8 *arg)
{
float8 *result = (float8 *) palloc(sizeof(float8));
*result = *arg + 1.0;
return result;
}
Point *
makepoint(Point *pointx, Point *pointy)
{
Point *new_point = (Point *) palloc(sizeof(Point));
new_point->x = pointx->x;
new_point->y = pointy->y;
return new_point;
}
/* 传递引用,变长*/
text *
copytext(text *t)
{
/*
* VARSIZE是结构以字节计的总长度。
*/
text *new_t = (text *) palloc(VARSIZE(t));
SET_VARSIZE(new_t, VARSIZE(t));
/*
* VARDATA是结构中一个指向数据区的指针。
*/
memcpy((void *) VARDATA(new_t), /* destination */
(void *) VARDATA(t), /* source */
VARSIZE(t) - VARHDRSZ); /* how many bytes */
return new_t;
}
text *
concat_text(text *arg1, text *arg2)
{
int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
text *new_text = (text *) palloc(new_text_size);
SET_VARSIZE(new_text, new_text_size);
memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ);
memcpy(VARDATA(new_text) + (VARSIZE(arg1) - VARHDRSZ),
VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ);
return new_text;
}
```
假设上面的代码放在`funcs.c`文件中并且编译成了共享目标, 我们可以用下面的命令为PostgreSQL定义这些函数:
```
CREATE FUNCTION add_one(integer) RETURNS integer
AS '_DIRECTORY_/funcs', 'add_one'
LANGUAGE C STRICT;
--注意:重载了名字为"add_one"的 SQL 函数
CREATE FUNCTION add_one(double precision) RETURNS double precision
AS '_DIRECTORY_/funcs', 'add_one_float8'
LANGUAGE C STRICT;
CREATE FUNCTION makepoint(point, point) RETURNS point
AS '_DIRECTORY_/funcs', 'makepoint'
LANGUAGE C STRICT;
CREATE FUNCTION copytext(text) RETURNS text
AS '_DIRECTORY_/funcs', 'copytext'
LANGUAGE C STRICT;
CREATE FUNCTION concat_text(text, text) RETURNS text
AS '_DIRECTORY_/funcs', 'concat_text'
LANGUAGE C STRICT;
```
这里的`_DIRECTORY_`代表共享库文件的目录, 比如包含本节示例代码的PostgreSQL教程目录。 更好的风格应该是将`_DIRECTORY_`加到搜索路径之后, 在`AS`子句里只使用`'funcs'`,不管怎样, 我们都可以省略和系统相关的共享库扩展, 通常是`.so`或者`.sl`。
请注意我们把函数声明为"strict"(严格),意思是说如果任何输入值为NULL, 那么系统应该自动假设一个NULL的结果。这样处理可以让我们避免在函数代码里面检查 NULL输入。如果不这样处理,我们就得明确检查NULL, 比如为每个传递引用的参数检查空指针。对于传值类型的参数,我们甚至没有办法检查!
尽管这种老调用风格用起来简单,但它却不太容易移植; 在一些系统上,用这种方法传递比`int`小的数据类型就会碰到困难。 而且,我们没有很好的返回NULL结果的办法,也没有除了把函数严格化以外的处理 NULL参数的方法。版本-1约定,下面要讲的新方法则解决了这些问题。
## 35.9.4\. 版本1调用约定
版本-1调用约定使用宏消除大多数传递参数和结果的复杂性。版本-1风格函数的C定义总是下面这样:
```
Datum funcname(PG_FUNCTION_ARGS)
```
另外,宏调用:
```
PG_FUNCTION_INFO_V1(funcname);
```
也必须出现在同一个源文件里(通常就可以写在函数自身前面)。 对那些`internal`语言函数而言,不需要调用这个宏, 因为PostgreSQL目前假设内部函数都是版本-1。不过,对于动态加载的函数, 它是必须的。
在版本-1 函数里,每个实际参数都是用一个对应该参数的数据类型的 `PG_GETARG_`_xxx_`()`宏抓取的, 用返回类型的`PG_RETURN_`_xxx_`()`宏返回结果。 `PG_GETARG_`_xxx_`()`接受要抓取的函数参数的编号 (从 0 开始)作为其参数。`PG_RETURN_`_xxx_`()` 接受实际要返回的数值为自身的参数。
下面是和上面一样的函数,但是使用版本-1风格编写的:
```
#include "postgres.h"
#include <string.h>
#include "fmgr.h"
#include "utils/geo_decls.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
/*传递数值*/
PG_FUNCTION_INFO_V1(add_one);
Datum
add_one(PG_FUNCTION_ARGS)
{
int32 arg = PG_GETARG_INT32(0);
PG_RETURN_INT32(arg + 1);
}
/*传递引用,定长*/
PG_FUNCTION_INFO_V1(add_one_float8);
Datum
add_one_float8(PG_FUNCTION_ARGS)
{
/*用于FLOAT8的宏,隐藏其传递引用的本质。*/
float8 arg = PG_GETARG_FLOAT8(0);
PG_RETURN_FLOAT8(arg + 1.0);
}
PG_FUNCTION_INFO_V1(makepoint);
Datum
makepoint(PG_FUNCTION_ARGS)
{
/* 这里,我们没有隐藏Point的传递引用的本质*/
Point *pointx = PG_GETARG_POINT_P(0);
Point *pointy = PG_GETARG_POINT_P(1);
Point *new_point = (Point *) palloc(sizeof(Point));
new_point->x = pointx->x;
new_point->y = pointy->y;
PG_RETURN_POINT_P(new_point);
}
/*传递引用,变长*/
PG_FUNCTION_INFO_V1(copytext);
Datum
copytext(PG_FUNCTION_ARGS)
{
text *t = PG_GETARG_TEXT_P(0);
/*
* VARSIZE是结构以字节计的总长度。
*/
text *new_t = (text *) palloc(VARSIZE(t));
SET_VARSIZE(new_t, VARSIZE(t));
/*
* VARDATA是结构中指向数据区的一个指针。
*/
memcpy((void *) VARDATA(new_t), /* 目的*/
(void *) VARDATA(t), /* 源 */
VARSIZE(t) - VARHDRSZ); /* 多少字节 */
PG_RETURN_TEXT_P(new_t);
}
PG_FUNCTION_INFO_V1(concat_text);
Datum
concat_text(PG_FUNCTION_ARGS)
{
text *arg1 = PG_GETARG_TEXT_P(0);
text *arg2 = PG_GETARG_TEXT_P(1);
int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ;
text *new_text = (text *) palloc(new_text_size);
SET_VARSIZE(new_text, new_text_size);
memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ);
memcpy(VARDATA(new_text) + (VARSIZE(arg1) - VARHDRSZ),
VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ);
PG_RETURN_TEXT_P(new_text);
}
```
用到的`CREATE FUNCTION`命令和版本-0等效命令一样。
猛一看,版本-1的编码好像只是无目的地蒙人。但是它的确给我们许多改进, 因为宏可以隐藏许多不必要的细节。一个例子在`add_one_float8`的编码里, 这里我们不再需要不停叮嘱自己`float8`是传递引用类型。 另外一个例子是用于变长类型的宏`GETARG`隐藏了抓取 "非常规"(压缩的或者超长的)值需要做的处理。
版本-1的函数另一个巨大的改进是对NULL输入和结果的处理。 宏`PG_ARGISNULL(`_n_`)`允许一个函数测试每个输入是否为NULL, 当然,这只是对那些没有声明为"strict"的函数有必要。 因为如果有`PG_GETARG_`_xxx_`()`宏, 输入参数是从零开始计算的。 请注意我们不应该执行`PG_GETARG_`_xxx_`()`, 除非有人声明了参数不是NULL。 要返回一个NULL结果,可以执行一个`PG_RETURN_NULL()`, 这样对严格的和不严格的函数都有效。
在新风格的接口中提供的其它选项是`PG_GETARG_`_xxx_`()`宏的两个变种。 第一个变体`PG_GETARG_`_xxx_`_COPY()` 保证返回一个指定参数的副本,该副本是可以安全地写入的。普通的宏有时候会返回一个指向物理存储在表中的某值的指针, 因此我们不能写入该指针。用`PG_GETARG_`_xxx_`_COPY()`宏保证获取一个可写的结果。 第二个变体由`PG_GETARG_`_xxx_`_SLICE()`宏组成, 它接受三个参数。第一个是参数的个数(与上同)。第二个和第三个是要返回的偏移量和数据段的长度。 偏移是从零开始计算的,一个负数的长度则要求返回该值的剩余长度的数据。 这些过程提供了访问大数据值的中一部分的更有效方法, 特别是数据的存储类型是"external"的时候。 一个字段的存储类型可以用`ALTER TABLE` `_tablename_` ALTER COLUMN `_colname_` SET STORAGE `_storagetype_`指定。 `_storagetype_`是`plain`,`external`, `extended`,或者`main`之一。
版本-1 的函数调用风格也令我们可能返回一"套"结果([Section 35.9.9](#calibre_link-929)) 并且实现触发器函数([Chapter 36](#calibre_link-460))和过程语言调用处理器 ([Chapter 51](#calibre_link-646))。 版本-1的代码也更容易移植,因为它没有违反C标准对函数调用协议的限制。 更多的细节请参阅源程序中的`src/backend/utils/fmgr/README`文件。
## 35.9.5\. 书写代码
在转到更深的话题之前,先要讨论一些PostgreSQL C语言函数的编码规则。 虽然可以用C以外的其它语言书写用于 PostgreSQL的共享函数, 但通常都很麻烦(当它可能的时候),因为其他语言, 比如C++, FORTRAN或者Pascal并不遵循C的调用习惯。 也就是说,其它语言在函数之间的传递参数和返回值的方式不一样。 因此假设你的C-编程语言函数是用C写的。
书写和编译C函数的基本规则如下:
* 使用`pg_config --includedir-server` 找出PostgreSQL服务器的头文件安装在你的系统上的 (或者你的用户正在运行)的位置。
* 把你的代码编译成可以动态装入PostgreSQL 的库文件总是需要一些特殊的标记。参阅[Section 35.9.6](#calibre_link-926)获取如何在你的平台上做这件事的详细说明。
* 按照[Section 35.9.1](#calibre_link-921)的指示为你的共享库定义一个"magic block"。
* 当分配内存时,用PostgreSQL的`palloc`和 `pfree`函数取代相应的C库函数 `malloc`和`free`。 用`palloc`分配的内存在每个事务结束时会自动释放,避免了内存泄露。
* 使用`memset`(或者在第一个位置分配`palloc0`)的你的结构字节总是零。 即使你给结构分配每个字段,可能有对齐填充(结构中含有孔)包含垃圾值。如果没有这一点, 很难支持散列索引和哈希连接,你必须只挑出你的数据结构中重要的位来计算一个散列。 规划器有时也依赖于通过位平等比较常数,所以如果逻辑等效值不是按位平等, 则你可能得到不良的规划结果。
* 大多数的PostgreSQL内部类型定义在`postgres.h`中, 而函数管理器接口(`PG_FUNCTION_ARGS`等等)都在`fmgr.h`中, 所以你至少应该包括这两个文件。出于移植性原因, 最好_先_包括`postgres.h`再包含其它系统或者用户头文件。 包含`postgres.h`将自动包含`elog.h`和`palloc.h`。
* 在目标文件里定义的符号一定不能相互冲突, 也不能和定义在PostgreSQL服务器可执行代码中的符号名字冲突。 如果你看到了与此相关的错误信息,那么必须重命名你的函数或者变量。
## 35.9.6\. 编译和链接动态加载的函数
在能够使用由 C 写的PostgreSQL扩展函数之前, 必须用一种特殊的方法编译和链接它们,这样才能生成可以被服务器动态加载的文件。 准确地说是需要创建一个_共享库_。
如果需要更多信息,那么你应该阅读操作系统的文档,特别是 C 编译器(`cc`) 和连接器(`ld`)的文档。另外,PostgreSQL 源代码里包含几个可以运行的例子,它们在`contrib`目录里。不过, 如果你依赖这些例子,那么你的模块将依赖于PostgreSQL源代码的可用性。
创建共享库和链接可执行文件类似:首先把源代码编译成目标文件,然后把目标文件链接起来。 目标文件需要创建成_位置无关码_(PIC), 也就是在可执行程序加载它们的时候, 它们可以被放在可执行程序内存里的任何地方(用于可执行文件的目标文件通常不是用这个方式编译的), 链接动态库的命令包含特殊标志,与链接可执行文件的命令是有区别的(至少理论上如此,不过现实未必)。
在下面的例子里,假设你的源程序代码在`foo.c`文件里,并且我们要创建 `foo.so`的共享库。中介的对象文件将叫做`foo.o` (除非另外注明)。虽然一个共享库可以包含多个对象文件,但是在这里只用一个。
FreeBSD
创建PIC的编译器标志是`-fpic`。创建共享库的链接器标志是`-shared`。
```
gcc -fpic -c foo.c
gcc -shared -o foo.so foo.o
```
上面方法适用于 3.0 版本的FreeBSD。
HP-UX
创建PIC的编译器标志是`+z` 。如果使用GCC 则是`-fpic`。创建共享库的链接器标志是`-b`。因此:
```
cc +z -c foo.c
```
或:
```
gcc -fpic -c foo.c
```
然后:
```
ld -b -o foo.sl foo.o
```
HP-UX使用`.sl`作为共享库扩展名,和其它大部分系统不同。
IRIX
PIC是缺省,不需要使用特殊的编译器选项。创建共享库的链接器标志是`-shared`。
```
cc -c foo.c
ld -shared -o foo.so foo.o
```
Linux
创建PIC的编译器标志是`-fpic`。在某些平台上如果`-fpic` 不工作则必须使用`-fPIC`。 参考 GCC 手册获取更多信息。创建共享库的编译器标志是`-shared`。一个完整的例子看起来像:
```
cc -fpic -c foo.c
cc -shared -o foo.so foo.o
```
Mac OS X
这里是一个例子。假设开发工具已经安装好了。
```
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编译器时创建PIC的编译器标志是`-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
```
Tru64 UNIX
PIC是缺省,不需要使用特殊的编译器选项。带特殊选项的`ld`用于链接:
```
cc -c foo.c
ld -shared -expect_unresolved '*' -o foo.so foo.o
```
用 GCC 代替系统编译器时的过程是一样的;不需要特殊的选项。
UnixWare
用 SCO 编译器时创建PIC的编译器标志是`-K PIC`; 用GCC编译器时创建PIC的编译器标志是 `-fpic`。链接共享库时 SCO 编译器用`-G`而GCC 使用`-shared`。
```
cc -K PIC -c foo.c
cc -G -o foo.so foo.o
```
或
```
gcc -fpic -c foo.c
gcc -shared -o foo.so foo.o
```
> **Tip:** 如果你觉得这些步骤实在太复杂,那么你应该考虑使用[GNU Libtool](http://www.gnu.org/software/libtool/),它把平台的差异隐藏在了一个统一的接口里。
生成的共享库文件然后就可以加载到PostgreSQL里面去了。 在给`CREATE FUNCTION`命令声明文件名的时候, 必须声明共享库文件的名字而不是中间目标文件的名字。 请注意你可以在`CREATE FUNCTION`命令上忽略系统标准的共享库扩展名(通常是 `.so`或`.sl`),并且出于最佳的兼容性考虑也应该忽略。
回头看看[Section 35.9.1](#calibre_link-921)获取有关服务器预期在哪里找到共享库的信息。
## 35.9.7\. 复合类型参数
复合类型不像 C 结构那样有固定的布局。复合类型的实例可能包含空(NULL)字段。另外, 一个属于继承层次一部分的复合类型可能和同一继承范畴的其它成员有不同的域/字段。 因此,PostgreSQL提供一个过程接口用于从C中访问复合类型。
假设为下面查询写一个函数:
```
SELECT name, c_overpaid(emp, 1500) AS overpaid
FROM emp
WHERE name = 'Bill' OR name = 'Sam';
```
使用调用约定版本0,可以这样定义`c_overpaid`:
```
#include "postgres.h"
#include "executor/executor.h" /* for GetAttributeByName() */
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
bool
c_overpaid(HeapTupleHeader t, /* the current row of emp */
int32 limit)
{
bool isnull;
int32 salary;
salary = DatumGetInt32(GetAttributeByName(t, "salary", &isnull));
if (isnull)
return false;
return salary > limit;
}
```
如果用版本-1则会写成下面这样:
```
#include "postgres.h"
#include "executor/executor.h" /* for GetAttributeByName() */
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
PG_FUNCTION_INFO_V1(c_overpaid);
Datum
c_overpaid(PG_FUNCTION_ARGS)
{
HeapTupleHeader t = PG_GETARG_HEAPTUPLEHEADER(0);
int32 limit = PG_GETARG_INT32(1);
bool isnull;
Datum salary;
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_NULL()用在null薪水上*/
PG_RETURN_BOOL(DatumGetInt32(salary) > limit);
}
```
`GetAttributeByName`是PostgreSQL系统函数, 用来返回当前记录的字段。它有三个参数:类型为`HeapTupleHeader`的传入函数的参数、你想要的字段名称、 一个确定字段是否为 NULL 的返回参数。`GetAttributeByName`函数返回一个 `Datum`值,你可以用对应的`DatumGet`_XXX_`()`宏把它转换成合适的数据类型。 请注意,如果设置了NULL标志,那么返回值是无意义的,在准备对结果做任何处理之前, 总是要先检查NULL标志。
还有一个`GetAttributeByNum`用字段编号而不是字段名选取目标字段。
下面的命令在SQL里声明`c_overpaid`函数:
```
CREATE FUNCTION c_overpaid(emp, integer) RETURNS boolean
AS '_DIRECTORY_/funcs', 'c_overpaid'
LANGUAGE C STRICT;
```
请注意使用`STRICT`后就不需要检查输入参数是否有NULL。
## 35.9.8\. 返回行(复合类型)
要从一个C语言函数里返回一个行或复合类型的数值, 可以使用一个特殊的API,它提供了许多宏和函数来消除大多数制作复合数据类型的复杂性。 要使用该API,源代码必须包含:
```
#include "funcapi.h"
```
制作一个复合类型数据值(也就是一个"行")有两种方法: 你可以从一个 Datum 值数组里制作,也可以从一个可以传递给该行的字段类型的输入转换函数的 C 字符串数组里制作。不管是哪种方式,你首先都需要为行结构获取或者制作一个 `TupleDesc`描述符。在使用 Datums 的时候,你给`BlessTupleDesc`传递这个`TupleDesc` 然后为每行调用`heap_form_tuple`。在使用C字符串的时候, 你给`TupleDescGetAttInMetadata` 传递`TupleDesc`,然后为每行调用`BuildTupleFromCStrings`。 如果是返回一个行集合的场合,所有设置步骤都可以在第一次调用该函数的时候一次性完成。
有几个便利函数可以用于设置所需要的`TupleDesc`。 在大多数返回复合类型给调用者的函数里建议的做法是这样的:
```
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo,
Oid *resultTypeId,
TupleDesc *resultTupleDesc)
```
把传递给调用函数自己的`fcinfo`传递给它(要求使用版本-1 的调用习惯)。 `resultTypeId`可以声明为`NULL`或者 接收函数的结果类型OID的局部变量地址(指针)。 `resultTupleDesc`应该是一个局部的`TupleDesc`变量地址(指针)。 检查结果是否`TYPEFUNC_COMPOSITE`;如是, `resultTupleDesc`就已经填充好需要的`TupleDesc`了。 如果不是,你可以报告一个类似"返回记录的函数在一个不接受记录的环境中被调用"的错误。
> **Tip:** `get_call_result_type`可以把一个多态的函数结果解析为实际类型; 因此它在返回多态的标量结果的函数里也很有用,而不仅仅是返回复合类型的函数里。 `resultTypeId`输出主要用于那些返回多态的标量类型的函数。
> **Note:** `get_call_result_type`有一个同胞弟兄`get_expr_result_type` 可以用于给一个用表达式树表示的函数调用解析输出, 它可以用于视图从函数本身外边判断结果类型的场合。 还有一个`get_func_result_type`可以用在只能拿到函数OID的场合。 不过,这些函数不能处理那些声明为返回`record`的函数, 并且`get_func_result_type`不能解析多态的类型, 因此你最好还是使用`get_call_result_type`。
旧的,现在已经废弃的获取`TupleDesc`的函数是:
```
TupleDesc RelationNameGetTupleDesc(const char *relname)
```
它可以从一个命名的关系里为行类型获取一个`TupleDesc`,还有:
```
TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases)
```
可以基于类型 OID 获取一个`TupleDesc`。 它可以用于给一个基本类型或者一个复合类型获取`TupleDesc`。 不过它不能处理返回`record`的函数,并且不能解析多态的类型。
一旦你有了一个`TupleDesc`,那么调用:
```
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
```
如果你想使用Datum,或者:
```
AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc)
```
如果你想使用C字符串。如果你在写一个返回集合的函数, 那么你可以把这些函数的结果保存在`FuncCallContext`结构里 (分别使用`tuple_desc`或者`attinmeta`字段)。
在使用Datum的时候,使用:
```
HeapTuple heap_form_tuple(TupleDesc tupdesc, Datum *values, bool *isnull)
```
制作一个`HeapTuple`,它把数据以Datum的形式交给用户。
当使用C字符串时,使用:
```
HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
```
制作一个`HeapTuple`,以C字符串的形式给出用户数据。 `values`是一个 C 字符串的数组,返回行的每个字段对应其中一个。 每个 C 字符串都应该是字段数据类型的输入函数预期的形式。 为了从其中一个字段中返回一个NULL, `values`数组中对应的指针应该设置为`NULL`。 这个函数将会需要为你返回的每个行调用一次。
一旦你制作了一个从你的函数中返回的行,那么该行必须转换成一个`Datum`。使用:
```
HeapTupleGetDatum(HeapTuple tuple)
```
把一个`HeapTuple`转换为一个有效的`Datum`。 如果你想只返回一行,那么这个 Datum 可以用于直接返回, 或者是它可以用作在一个返回集合的函数里的当前返回值。
例子在下面给出。
## 35.9.9\. 返回集合
还有一个特殊的API用于提供从C语言函数中返回集合(多行)。 一个返回集合的函数必须遵循版本-1的调用方式。 同样,源代码必须包含`funcapi.h`,就像上面说的那样。
一个返回集合的函数(SRF)通常为它返回的每个项都调用一次。 因此SRF必须保存足够的状态用于记住它正在做的事情以及在每次调用的时候返回下一个项。 表函数 API 提供了`FuncCallContext`结构用于帮助控制这个过程。 `fcinfo->flinfo->fn_extra` 用于保存一个跨越多次调用的指向`FuncCallContext`的指针。
```
typedef struct
{
/*
* 前面已经被调用的次数
* 初始的时候,call_cntr 被 SRF_FIRSTCALL_INIT() 置为 0,
*并且每次你调用 SRF_RETURN_NEXT() 的时候都递增
*/
uint32 call_cntr;
/*
* 可选的最大调用数量
* 这里的 max_calls 只是为了方便,设置它也是可选的。
* 如果没有设置,你必须提供可选的方法来知道函数何时结束。
*/
uint32 max_calls;
/*
* 指向结果槽位的可选指针
* 这个数据类型已经过时,只用于向下兼容。也就是那些使用已废弃的TupleDescGetSlot()的用户定义 SRF
*/
TupleTableSlot *slot;
/*
* 可选的指向用户提供的杂项环境信息的指针
* user_fctx 用做一个指向你自己的结构的指针,包含任意提供给你的函数的调用间的环境信息
*/
void *user_fctx;
/*
* 可选的指向包含属性类型输入元信息的结构数组的指针
* attinmeta 用于在返回行的时候(也就是说返回复合数据类型)
* 在只返回基本(也就是标量)数据类型的时候并不需要。
* 只有在你准备用 BuildTupleFromCStrings() 创建返回行的时候才需要它。
*/
AttInMetadata *attinmeta;
/*
* 用于必须在多次调用间存活的结构的内存环境
* multi_call_memory_ctx 是由 SRF_FIRSTCALL_INIT() 为你设置的,并且由 SRF_RETURN_DONE() 用于清理。
* 它是用于存放任何需要跨越多次调用 SRF 之间重复使用的内存。
*/
MemoryContext multi_call_memory_ctx;
/*
* 可选的指针,指向包含行描述的结构
* tuple_desc 用于返回行(也就是说复合数据类型)并且只是在你想使用 heap_form_tuple() 而不是 BuildTupleFromCStrings() 制作行的时候需要。
* 请注意这里存储的 TupleDesc 指针通常应该先用 BlessTupleDesc() 处理。
*/
TupleDesc tuple_desc;
} FuncCallContext;
```
一个SRF使用自动操作`FuncCallContext`结构 (可以通过`fn_extra`找到)的若干个函数和宏。使用:
```
SRF_IS_FIRSTCALL()
```
来判断你的函数是第一次调用还是后继的调用。只有在第一次调用的时候,使用:
```
SRF_FIRSTCALL_INIT()
```
初始化`FuncCallContext`。在每次函数调用时(包括第一次),使用:
```
SRF_PERCALL_SETUP()
```
为使用`FuncCallContext`做恰当的设置以及清理任何前面的轮回里面剩下的已返回的数据。
如果你的函数有数据要返回,使用:
```
SRF_RETURN_NEXT(funcctx, result)
```
返回给调用者(`result`必须是个`Datum`,要么是单个值, 要么是像前面介绍的那样准备的行)。 最后,如果你的函数结束了数据返回,使用:
```
SRF_RETURN_DONE(funcctx)
```
清理并结束SRF。
在SRF被调用时的内存环境是一个临时环境, 在调用之间将会被清理掉。 这意味着你不需要`pfree`所有你`palloc`的东西;它会自动消失的。 不过,如果你想分配任何跨越调用存在的数据结构, 那你就需要把它们放在其它什么地方。 被`multi_call_memory_ctx`引用的环境适合用于保存那些需要直到 SRF结束前都存活的数据。在大多数情况下, 这意味着你在第一次调用设置的时候应该切换到`multi_call_memory_ctx`。
一个完整的伪代码例子看起来像下面这样:
```
Datum
my_set_returning_function(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
Datum result;
_更多的声明_
if (SRF_IS_FIRSTCALL())
{
MemoryContext oldcontext;
funcctx = SRF_FIRSTCALL_INIT();
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* 这里放出现一次的设置代码: */
_用户代码_
_if 返回复合_
_制作 TupleDesc 以及可能还有 AttInMetadata_
_endif 返回复合_
_用户定义代码_
MemoryContextSwitchTo(oldcontext);
}
/* 每次都执行的设置代码在这里出现: */
_用户定义代码_
funcctx = SRF_PERCALL_SETUP();
_用户定义代码_
/* 这里只是用来测试是否完成的一个方法: */
if (funcctx->call_cntr < funcctx->max_calls)
{
/* 这里想返回另外一个条目: */
_用户代码_
_获取结果_
SRF_RETURN_NEXT(funcctx, result);
}
else
{
/* 这里完成返回条目的工作了,只需要清理就OK了: */
_用户代码_
SRF_RETURN_DONE(funcctx);
}
}
```
一个返回复合类型的完整SRF例子看起来像这样:
```
PG_FUNCTION_INFO_V1(retcomposite);
Datum
retcomposite(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
int call_cntr;
int max_calls;
TupleDesc tupdesc;
AttInMetadata *attinmeta;
/* 只是在第一次调用函数的时候干的事情 */
if (SRF_IS_FIRSTCALL())
{
MemoryContext oldcontext;
/*创建一个函数环境,用于在调用间保持住*/
funcctx = SRF_FIRSTCALL_INIT();
/* 切换到适合多次函数调用的内存环境 */
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
/* 要返回的行总数 */
funcctx->max_calls = PG_GETARG_UINT32(0);
/* 为了结果类型制作一个行描述 */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function returning record called in context "
"that cannot accept type record")));
/*
* 生成稍后从裸 C 字符串生成行的属性元数据
*/
attinmeta = TupleDescGetAttInMetadata(tupdesc);
funcctx->attinmeta = attinmeta;
MemoryContextSwitchTo(oldcontext);
}
/* 每次函数调用都要做的事情 */
funcctx = SRF_PERCALL_SETUP();
call_cntr = funcctx->call_cntr;
max_calls = funcctx->max_calls;
attinmeta = funcctx->attinmeta;
if (call_cntr < max_calls) /* 在还有需要发送的东西时继续处理 */
{
char **values;
HeapTuple tuple;
Datum result;
/*
* 准备一个数值数组用于版本的返回行
* 它应该是一个C字符串数组,稍后可以被合适的类型输入函数处理。
*/
values = (char **) palloc(3 * sizeof(char *));
values[0] = (char *) palloc(16 * sizeof(char));
values[1] = (char *) palloc(16 * sizeof(char));
values[2] = (char *) palloc(16 * sizeof(char));
snprintf(values[0], 16, "%d", 1 * PG_GETARG_INT32(1));
snprintf(values[1], 16, "%d", 2 * PG_GETARG_INT32(1));
snprintf(values[2], 16, "%d", 3 * PG_GETARG_INT32(1));
/* 制作一个行 */
tuple = BuildTupleFromCStrings(attinmeta, values);
/* 把行做成 datum */
result = HeapTupleGetDatum(tuple);
/* 清理(这些实际上并非必要) */
pfree(values[0]);
pfree(values[1]);
pfree(values[2]);
pfree(values);
SRF_RETURN_NEXT(funcctx, result);
}
else /* 在没有数据残留的时候干的事情 */
{
SRF_RETURN_DONE(funcctx);
}
}
```
在 SQL 里声明这个函数的一个方法是:
```
CREATE TYPE __retcomposite AS (f1 integer, f2 integer, f3 integer);
CREATE OR REPLACE FUNCTION retcomposite(integer, integer)
RETURNS SETOF __retcomposite
AS '_filename_', 'retcomposite'
LANGUAGE C IMMUTABLE STRICT;
```
另外一个方法是使用 OUT 参数:
```
CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer,
OUT f1 integer, OUT f2 integer, OUT f3 integer)
RETURNS SETOF record
AS '_filename_', 'retcomposite'
LANGUAGE C IMMUTABLE STRICT;
```
请注意在这个方法里,函数的输出类型实际上是匿名的`record`类型。
参阅源码发布包里的[contrib/tablefunc](#calibre_link-401) 获取更多有关返回集合的函数的例子。
## 35.9.10\. 多态参数和返回类型
C 语言函数可以声明为接受和返回多态的类型`anyelement`,`anyarray`, `anynonarray`, `anyenum`和`anyrange`。 参阅[Section 35.2.5](#calibre_link-909)获取有关多态函数的更详细解释。 如果函数参数或者返回类型定义为多态类型, 那么函数的作者就无法预先知道他将收到的参数,以及需要返回的数据。 在`fmgr.h`里有两个过程,可以让版本-1 的 C 函数知道它的参数的确切数据类型以及 它需要返回的数据类型。这两个过程叫`get_fn_expr_rettype(FmgrInfo *flinfo)`和 `get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)`。 它们返回结果或者参数的类型 OID,如果这些信息不可获取,则返回 InvalidOid 。 结构`flinfo`通常是以`fcinfo->flinfo`进行访问的。 参数`argnum`是以 0 为基的。 `get_call_result_type`也可以替代`get_fn_expr_rettype`。 还有`get_fn_expr_variadic`用于找出是否调用包含明确的`VARIADIC`关键字。 对于`VARIADIC "any"`函数是最有用的,正如下面所述。
比如,假设想写一个函数接受任意类型的一个元素,并且返回该类型的一个一维数组:
```
PG_FUNCTION_INFO_V1(make_array);
Datum
make_array(PG_FUNCTION_ARGS)
{
ArrayType *result;
Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
Datum element;
bool isnull;
int16 typlen;
bool typbyval;
char typalign;
int ndims;
int dims[MAXDIM];
int lbs[MAXDIM];
if (!OidIsValid(element_type))
elog(ERROR, "could not determine data type of input");
/* 获取提供的元素(要小心其为NULL的情况) */
isnull = PG_ARGISNULL(0);
if (isnull)
element = (Datum) 0;
else
element = PG_GETARG_DATUM(0);
/* 维数是1 */
ndims = 1;
/* 有1个元素 */
dims[0] = 1;
/* 数组下界是1 */
lbs[0] = 1;
/* 获取有关元素类型需要的信息 */
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
/* 然后制作数组 */
result = construct_md_array(&element, &isnull, ndims, dims, lbs,
element_type, typlen, typbyval, typalign);
PG_RETURN_ARRAYTYPE_P(result);
}
```
下面的命令用SQL声明`make_array`函数:
```
CREATE FUNCTION make_array(anyelement) RETURNS anyarray
AS '_DIRECTORY_/funcs', 'make_array'
LANGUAGE C IMMUTABLE;
```
有一个变种多态性,仅适用于C语言函数:他们可以声明采取类型 `"any"`的参数。(注意:这个类型名称必须是双引号, 因为它同时也是一个SQL的保留字)。类似于`anyelement`除了它并不限制不同`"any"` 参数是相同类型,也没有帮助确定该函数的结果类型。一个C语言的函数也可以声明最后的参数为`VARIADIC "any"`。 这将匹配一个或多个任意类型的实参(不一定是相同的类型)。 这些参数_不_被收集到一个数组中如发生正常的可变参数函数; 他们会分别被传递到函数中。`PG_NARGS()`宏和 上面描述的方法必须被用来确定实际参数数目 以及使用此功能时的类型。同时,这个函数的用户可能希望在函数调用中使用`VARIADIC`关键字, 以期望函数把数组元素看作单独的参数。函数本身必须实现 想要的操作,使用`get_fn_expr_variadic`之后 检测实际参数被标记为`VARIADIC`。
## 35.9.11\. 转换函数
一些函数的调用可以在规划中基于函数的属性特性被简化。比如, `int4mul(n, 1)`可简化为`n`。 为了定义函数-特定优化,写_transform function_并将其OID放入 基函数的`pg_proc`项的`protransform`字段中, 转换函数必须有SQL签名`protransform(internal) RETURNS internal`。 参数,其实`FuncExpr *`是代表调用基函数的一个虚拟节点。 如果表达式树的变换函数的研究证明简化的表达式树可以替代所有 可能的具体调用其表示建立并且返回简单的表达式。 否则,返回`NULL`指针(_不是_SQL null)。
我们不做任何保证, PostgreSQL不会调用这种情况下的主要函数以简化转换函数。 确保在简化的表达式以及实际调用主要函数之间的严格等价性。
当前,这个设施在SQL水平上不暴露给用户,出于安全考虑。因此只有实践中用于优化内置函数。
## 35.9.12\. 共享内存和LWLocks
插件可能保留 LWLocks 并在服务器启动时分配共享内存。 插件的共享库必须通过指定[shared_preload_libraries](#calibre_link-576)的方法预先加载。
```
void RequestAddinShmemSpace(int size)
```
共享内存可以通过在`_PG_init`函数中调用。
LWLocks通过调用进行预留:
```
void RequestAddinLWLocks(int n)
```
来自`_PG_init`。
为了避免可能的竞争条件,当连接并且初始化共享内存分配时, 每个后端应该使用LWLock `AddinShmemInitLock`,如下所示:
```
static mystruct *ptr = NULL;
if (!ptr)
{
bool found;
LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
ptr = ShmemInitStruct("my struct name", size, &found);
if (!found)
{
initialize contents of shmem area;
acquire any requested LWLocks using:
ptr->mylockid = LWLockAssign();
}
LWLockRelease(AddinShmemInitLock);
}
```
## 35.9.13\. 使用C++的可扩展性
尽管PostgreSQL后端以C写入,如果伴随这些准则, 在C++中写入扩展是可能的:
* 所有被后端访问的函数必须提供到后端的C接口; 这些C函数然后调用C++函数。比如,`extern C`联系
* 使用合适的存储单元分配方法释放内存。比如,使用`palloc()`分配大部分后端内存, 因此使用`pfree()`释放它。在这种情况下使用C++ `delete`将失败。
* 防止异常传播到C代码(使用捕获所有`extern C`函数的最高水平上的块)。 即使是C++代码没有明确地抛出异常,这是必要的,由于事件比如内存不足仍然可以抛出异常。 任何异常必须被捕获,并且将适当的错误传递给C接口。如果可能的话,编译C++ `-fno-exceptions`以完全消除异常;在这样的案例中, 你必须检查你的C++代码的错误,比如检查通过`new()`返回的NULL。
* 如果从C++代码中调用后端函数,确保C++调用堆栈中只包含纯旧的数据结构(POD)。 这是必要的因为后端错误产生一个遥远的`longjmp()`,不适当的展开与非-POD对象的C++ 调用堆栈。
总之,把C++代码放在与后端接口的`extern C`函数之后是最好的, 并且避免异常,内存以及调用堆栈泄露。
- 前言
- 何为PostgreSQL?
- PostgreSQL简史
- 格式约定
- 更多信息
- 臭虫汇报指导
- I. 教程
- Chapter 1. 从头开始
- 1.1. 安装
- 1.2. 体系基本概念
- 1.3. 创建一个数据库
- 1.4. 访问数据库
- Chapter 2. SQL语言
- 2.1. 介绍
- 2.2. 概念
- 2.3. 创建新表
- 2.4. 向表中添加行
- 2.5. 查询一个表
- 2.6. 在表间连接
- 2.7. 聚集函数
- 2.8. 更新
- 2.9. 删除
- Chapter 3. 高级特性
- 3.1. 介绍
- 3.2. 视图
- 3.3. 外键
- 3.4. 事务
- 3.5. 窗口函数
- 3.6. 继承
- 3.7. 结论
- II. SQL 语言
- Chapter 4. SQL语法
- 4.1. 词法结构
- 4.2. 值表达式
- 4.3. 调用函数
- Chapter 5. 数据定义
- 5.1. 表的基本概念
- 5.2. 缺省值
- 5.3. 约束
- 5.4. 系统字段
- 5.5. 修改表
- 5.6. 权限
- 5.7. 模式
- 5.8. 继承
- 5.9. 分区
- 5.10. 外部数据
- 5.11. 其它数据库对象
- 5.12. 依赖性跟踪
- Chapter 6. 数据操作
- 6.1. 插入数据
- 6.2. 更新数据
- 6.3. 删除数据
- Chapter 7. 查询
- 7.1. 概述
- 7.2. 表表达式
- 7.3. 选择列表
- 7.4. 组合查询
- 7.5. 行排序
- 7.6. LIMIT和OFFSET
- 7.7. VALUES列表
- 7.8. WITH 查询 (通用表表达式)
- Chapter 8. 数据类型
- 8.1. 数值类型
- 8.2. 货币类型
- 8.3. 字符类型
- 8.4. 二进制数据类型
- 8.5. 日期/时间类型
- 8.6. 布尔类型
- 8.7. 枚举类型
- 8.8. 几何类型
- 8.9. 网络地址类型
- 8.10. 位串类型
- 8.11. 文本搜索类型
- 8.12. UUID 类型
- 8.13. XML 类型
- 8.14. JSON 类型
- 8.15. Arrays
- 8.16. 复合类型
- 8.17. 范围类型
- 8.18. 对象标识符类型
- 8.19. 伪类型
- Chapter 9. 函数和操作符
- 9.1. 逻辑操作符
- 9.2. 比较操作符
- 9.3. 数学函数和操作符
- 9.4. 字符串函数和操作符
- 9.5. 二进制字符串函数和操作符
- 9.6. 位串函数和操作符
- 9.7. 模式匹配
- 9.8. 数据类型格式化函数
- 9.9. 时间/日期函数和操作符
- 9.10. 支持枚举函数
- 9.11. 几何函数和操作符
- 9.12. 网络地址函数和操作符
- 9.13. 文本检索函数和操作符
- 9.14. XML 函数
- 9.15. JSON 函数和操作符
- 9.16. 序列操作函数
- 9.17. 条件表达式
- 9.18. 数组函数和操作符
- 9.19. 范围函数和操作符
- 9.20. 聚集函数
- 9.21. 窗口函数
- 9.22. 子查询表达式
- 9.23. 行和数组比较
- 9.24. 返回集合的函数
- 9.25. 系统信息函数
- 9.26. 系统管理函数
- 9.27. 触发器函数
- 9.28. 事件触发函数
- Chapter 10. 类型转换
- 10.1. 概述
- 10.2. 操作符
- 10.3. 函数
- 10.4. 值存储
- 10.5. UNION, CASE 和相关构造
- Chapter 11. 索引
- 11.1. 介绍
- 11.2. 索引类型
- 11.3. 多字段索引
- 11.4. 索引和ORDER BY
- 11.5. 组合多个索引
- 11.6. 唯一索引
- 11.7. 表达式上的索引
- 11.8. 部分索引
- 11.9. 操作符类和操作符族
- 11.10. 索引和排序
- 11.11. 检查索引的使用
- Chapter 12. 全文检索
- 12.1. 介绍
- 12.2. 表和索引
- 12.3. 控制文本搜索
- 12.4. 附加功能
- 12.5. 解析器
- 12.6. 词典
- 12.7. 配置实例
- 12.8. 测试和调试文本搜索
- 12.9. GiST和GIN索引类型
- 12.10. psql支持
- 12.11. 限制
- 12.12. 来自8.3之前文本搜索的迁移
- Chapter 13. 并发控制
- 13.1. 介绍
- 13.2. 事务隔离
- 13.3. 明确锁定
- 13.4. 应用层数据完整性检查
- 13.5. 锁和索引
- Chapter 14. 性能提升技巧
- 14.1. 使用EXPLAIN
- 14.2. 规划器使用的统计信息
- 14.3. 用明确的JOIN控制规划器
- 14.4. 向数据库中添加记录
- 14.5. 非持久性设置
- III. 服务器管理
- Chapter 15. 源码安装
- 15.1. 简版
- 15.2. 要求
- 15.3. 获取源码
- 15.4. 安装过程
- 15.5. 安装后设置
- 15.6. 支持平台
- 15.7. 特定平台注意事项
- Chapter 16. Windows下用源代码安装
- 16.1. 用Visual C++或Microsoft Windows SDK编译
- 16.2. 用Visual C++或 Borland C++编译 libpq
- Chapter 17. 服务器设置和操作
- 17.1. PostgreSQL用户账户
- 17.2. 创建数据库集群
- 17.3. 启动数据库服务器
- 17.4. 管理内核资源
- 17.5. 关闭服务器
- 17.6. 升级一个 PostgreSQL 集群
- 17.7. 防止服务器欺骗
- 17.8. 加密选项
- 17.9. 用 SSL 进行安全的 TCP/IP 连接
- 17.10. 用SSH隧道进行安全 TCP/IP 连接
- 17.11. 在Windows上注册事件日志
- Chapter 18. 服务器配置
- 18.1. 设置参数
- 18.2. 文件位置
- 18.3. 连接和认证
- 18.4. 资源消耗
- 18.5. 预写式日志
- 18.6. 复制
- 18.7. 查询规划
- 18.8. 错误报告和日志
- 18.9. 运行时统计
- 18.10. 自动清理
- 18.11. 客户端连接缺省
- 18.12. 锁管理
- 18.13. 版本和平台兼容性
- 18.14. Error Handling
- 18.15. 预置选项
- 18.16. 自定义选项
- 18.17. 开发人员选项
- 18.18. 短选项
- Chapter 19. 用户认证
- 19.1. pg_hba.conf文件
- 19.2. 用户名映射
- 19.3. 认证方法
- 19.4. 用户认证
- Chapter 20. 数据库角色
- 20.1. 数据库角色
- 20.2. 角色属性
- 20.3. 角色成员
- 20.4. 函数和触发器安全
- Chapter 21. 管理数据库
- 21.1. 概述
- 21.2. 创建一个数据库
- 21.3. 模板数据库
- 21.4. 数据库配置
- 21.5. 删除数据库
- 21.6. 表空间
- Chapter 22. 区域
- 22.1. 区域支持
- 22.2. 排序规则支持
- 22.3. 字符集支持
- Chapter 23. 日常数据库维护工作
- 23.1. 日常清理
- 23.2. 经常重建索引
- 23.3. 日志文件维护
- Chapter 24. 备份与恢复
- 24.1. SQL转储
- 24.2. 文件系统级别备份
- 24.3. 在线备份以及即时恢复(PITR)
- Chapter 25. 高可用性与负载均衡,复制
- 25.1. 不同解决方案的比较
- 25.2. 日志传送备份服务器
- 25.3. 失效切换
- 25.4. 日志传送的替代方法
- 25.5. 热备
- Chapter 26. 恢复配置
- 26.1. 归档恢复设置
- 26.2. 恢复目标设置
- 26.3. 备用服务器设置
- Chapter 27. 监控数据库的活动
- 27.1. 标准Unix工具
- 27.2. 统计收集器
- 27.3. 查看锁
- 27.4. 动态跟踪
- Chapter 28. 监控磁盘使用情况
- 28.1. 判断磁盘的使用量
- 28.2. 磁盘满导致的失效
- Chapter 29. 可靠性和预写式日志
- 29.1. 可靠性
- 29.2. 预写式日志(WAL)
- 29.3. 异步提交
- 29.4. WAL 配置
- 29.5. WAL 内部
- Chapter 30. 回归测试
- 30.1. 运行测试
- 30.2. 测试评估
- 30.3. 平台相关的比较文件
- 30.4. 测试覆盖率检查
- IV. 客户端接口
- Chapter 31. libpq - C 库
- 31.1. 数据库连接控制函数
- 31.2. 连接状态函数
- 31.3. 命令执行函数
- 31.4. 异步命令处理
- 31.5. 逐行检索查询结果
- 31.6. 取消正在处理的查询
- 31.7. 捷径接口
- 31.8. 异步通知
- 31.9. 与COPY命令相关的函数
- 31.10. 控制函数
- 31.11. 各种函数
- 31.12. 注意信息处理
- 31.13. 事件系统
- 31.14. 环境变量
- 31.15. 口令文件
- 31.16. 连接服务的文件
- 31.17. LDAP查找连接参数
- 31.18. SSL 支持
- 31.19. 在多线程程序里的行为
- 31.20. 制作libpq程序
- 31.21. 例子程序
- Chapter 32. 大对象
- 32.1. 介绍
- 32.2. 实现特点
- 32.3. 客户端接口
- 32.4. 服务器端函数
- 32.5. 例子程序
- Chapter 33. ECPG - 在C中嵌入SQL
- 33.1. 概念
- 33.2. 管理数据库连接
- 33.3. 运行SQL命令
- 33.4. 使用宿主变量
- 33.5. 动态SQL
- 33.6. pgtypes 库
- 33.7. 使用描述符范围
- 33.8. 错误处理
- 33.9. 预处理器指令
- 33.10. 处理嵌入的SQL程序
- 33.11. 库函数
- 33.12. 大对象
- 33.13. C++应用程序
- 33.14. 嵌入的SQL命令
- ALLOCATE DESCRIPTOR
- CONNECT
- DEALLOCATE DESCRIPTOR
- DECLARE
- DESCRIBE
- DISCONNECT
- EXECUTE IMMEDIATE
- GET DESCRIPTOR
- OPEN
- PREPARE
- SET AUTOCOMMIT
- SET CONNECTION
- SET DESCRIPTOR
- TYPE
- VAR
- WHENEVER
- 33.15. Informix兼容模式
- 33.16. 内部
- Chapter 34. 信息模式
- 34.1. 关于这个模式
- 34.2. 数据类型
- 34.3. information_schema_catalog_name
- 34.4. administrable_role_authorizations
- 34.5. applicable_roles
- 34.6. attributes
- 34.7. character_sets
- 34.8. check_constraint_routine_usage
- 34.9. check_constraints
- 34.10. collations
- 34.11. collation_character_set_applicability
- 34.12. column_domain_usage
- 34.13. column_options
- 34.14. column_privileges
- 34.15. column_udt_usage
- 34.16. columns
- 34.17. constraint_column_usage
- 34.18. constraint_table_usage
- 34.19. data_type_privileges
- 34.20. domain_constraints
- 34.21. domain_udt_usage
- 34.22. domains
- 34.23. element_types
- 34.24. enabled_roles
- 34.25. foreign_data_wrapper_options
- 34.26. foreign_data_wrappers
- 34.27. foreign_server_options
- 34.28. foreign_servers
- 34.29. foreign_table_options
- 34.30. foreign_tables
- 34.31. key_column_usage
- 34.32. parameters
- 34.33. referential_constraints
- 34.34. role_column_grants
- 34.35. role_routine_grants
- 34.36. role_table_grants
- 34.37. role_udt_grants
- 34.38. role_usage_grants
- 34.39. routine_privileges
- 34.40. routines
- 34.41. schemata
- 34.42. sequences
- 34.43. sql_features
- 34.44. sql_implementation_info
- 34.45. sql_languages
- 34.46. sql_packages
- 34.47. sql_parts
- 34.48. sql_sizing
- 34.49. sql_sizing_profiles
- 34.50. table_constraints
- 34.51. table_privileges
- 34.52. tables
- 34.53. triggered_update_columns
- 34.54. triggers
- 34.55. udt_privileges
- 34.56. usage_privileges
- 34.57. user_defined_types
- 34.58. user_mapping_options
- 34.59. user_mappings
- 34.60. view_column_usage
- 34.61. view_routine_usage
- 34.62. view_table_usage
- 34.63. views
- V. 服务器端编程
- Chapter 35. 扩展SQL
- 35.1. 扩展性是如何实现的
- 35.2. PostgreSQL类型系统
- 35.3. 用户定义的函数
- 35.4. 查询语言(SQL)函数
- 35.5. 函数重载
- 35.6. 函数易失性范畴
- 35.7. 过程语言函数
- 35.8. 内部函数
- 35.9. C-语言函数
- 35.10. 用户定义聚集
- 35.11. 用户定义类型
- 35.12. 用户定义操作符
- 35.13. 操作符优化信息
- 35.14. 扩展索引接口
- 35.15. 包装相关对象到一个扩展
- 35.16. 扩展基础设施建设
- Chapter 36. 触发器
- 36.1. 触发器行为概述
- 36.2. 数据改变的可视性
- 36.3. 用C写触发器
- 36.4. 一个完整的触发器例子
- Chapter 37. 事件触发器
- 37.1. 事件触发器行为的概述
- 37.2. 事件触发器触发矩阵
- 37.3. 用C编写事件触发器函数
- 37.4. 一个完整的事件触发器的例子
- Chapter 38. 规则系统
- 38.1. 查询树
- 38.2. 视图和规则系统
- 38.3. 物化视图
- 38.4. 在 INSERT, UPDATE, 和 DELETE上的规则
- 38.5. 规则和权限
- 38.6. 规则和命令状态
- 38.7. 规则与触发器的比较
- Chapter 39. 过程语言
- 39.1. 安装过程语言
- Chapter 40. PL/pgSQL - SQL过程语言
- 40.1. 概述
- 40.2. PL/pgSQL的结构
- 40.3. 声明
- 40.4. 表达式
- 40.5. 基本语句
- 40.6. 控制结构
- 40.7. 游标
- 40.8. 错误和消息
- 40.9. 触发器过程
- 40.10. 在后台下的PL/pgSQL
- 40.11. 开发PL/pgSQL的一些提示
- 40.12. 从Oracle PL/SQL进行移植
- Chapter 41. PL/Tcl - Tcl 过程语言
- 41.1. 概述
- 41.2. PL/Tcl 函数和参数
- 41.3. PL/Tcl里的数据值
- 41.4. PL/Tcl里的全局量
- 41.5. 在PL/Tcl里访问数据库
- 41.6. PL/Tcl里的触发器过程
- 41.7. 模块和unknown的命令
- 41.8. Tcl 过程名字
- Chapter 42. PL/Perl - Perl 过程语言
- 42.1. PL/Perl 函数和参数
- 42.2. PL/Perl里的数据值
- 42.3. 内置函数
- 42.4. PL/Perl里的全局变量
- 42.5. 可信的和不可信的 PL/Perl
- 42.6. PL/Perl 触发器
- 42.7. 后台PL/Perl
- Chapter 43. PL/Python - Python 过程语言
- 43.1. Python 2 vs. Python 3
- 43.2. PL/Python Functions
- 43.3. Data Values
- 43.4. Sharing Data
- 43.5. Anonymous Code Blocks
- 43.6. Trigger Functions
- 43.7. Database Access
- 43.8. Explicit Subtransactions
- 43.9. Utility Functions
- 43.10. Environment Variables
- Chapter 44. 服务器编程接口
- 44.1. 接口函数
- SPI_connect
- SPI_finish
- SPI_push
- SPI_pop
- SPI_execute
- SPI_exec
- SPI_execute_with_args
- SPI_prepare
- SPI_prepare_cursor
- SPI_prepare_params
- SPI_getargcount
- SPI_getargtypeid
- SPI_is_cursor_plan
- SPI_execute_plan
- SPI_execute_plan_with_paramlist
- SPI_execp
- SPI_cursor_open
- SPI_cursor_open_with_args
- SPI_cursor_open_with_paramlist
- SPI_cursor_find
- SPI_cursor_fetch
- SPI_cursor_move
- SPI_scroll_cursor_fetch
- SPI_scroll_cursor_move
- SPI_cursor_close
- SPI_keepplan
- SPI_saveplan
- 44.2. 接口支持函数
- SPI_fname
- SPI_fnumber
- SPI_getvalue
- SPI_getbinval
- SPI_gettype
- SPI_gettypeid
- SPI_getrelname
- SPI_getnspname
- 44.3. 内存管理
- SPI_palloc
- SPI_repalloc
- SPI_pfree
- SPI_copytuple
- SPI_returntuple
- SPI_modifytuple
- SPI_freetuple
- SPI_freetuptable
- SPI_freeplan
- 44.4. 数据改变的可视性
- 44.5. 例子
- Chapter 45. 后台工作进程
- VI. 参考手册
- I. SQL 命令
- ABORT
- ALTER AGGREGATE
- ALTER COLLATION
- ALTER CONVERSION
- ALTER DATABASE
- ALTER DEFAULT PRIVILEGES
- ALTER DOMAIN
- ALTER EXTENSION
- ALTER EVENT TRIGGER
- ALTER FOREIGN DATA WRAPPER
- ALTER FOREIGN TABLE
- ALTER FUNCTION
- ALTER GROUP
- ALTER INDEX
- ALTER LANGUAGE
- ALTER LARGE OBJECT
- ALTER MATERIALIZED VIEW
- ALTER OPERATOR
- ALTER OPERATOR CLASS
- ALTER OPERATOR FAMILY
- ALTER ROLE
- ALTER RULE
- ALTER SCHEMA
- ALTER SEQUENCE
- ALTER SERVER
- ALTER TABLE
- ALTER TABLESPACE
- ALTER TEXT SEARCH CONFIGURATION
- ALTER TEXT SEARCH DICTIONARY
- ALTER TEXT SEARCH PARSER
- ALTER TEXT SEARCH TEMPLATE
- ALTER TRIGGER
- ALTER TYPE
- ALTER USER
- ALTER USER MAPPING
- ALTER VIEW
- ANALYZE
- BEGIN
- CHECKPOINT
- CLOSE
- CLUSTER
- COMMENT
- COMMIT
- COMMIT PREPARED
- COPY
- CREATE AGGREGATE
- CREATE CAST
- CREATE COLLATION
- CREATE CONVERSION
- CREATE DATABASE
- CREATE DOMAIN
- CREATE EXTENSION
- CREATE EVENT TRIGGER
- CREATE FOREIGN DATA WRAPPER
- CREATE FOREIGN TABLE
- CREATE FUNCTION
- CREATE GROUP
- CREATE INDEX
- CREATE LANGUAGE
- CREATE MATERIALIZED VIEW
- CREATE OPERATOR
- CREATE OPERATOR CLASS
- CREATE OPERATOR FAMILY
- CREATE ROLE
- CREATE RULE
- CREATE SCHEMA
- CREATE SEQUENCE
- CREATE SERVER
- CREATE TABLE
- CREATE TABLE AS
- CREATE TABLESPACE
- CREATE TEXT SEARCH CONFIGURATION
- CREATE TEXT SEARCH DICTIONARY
- CREATE TEXT SEARCH PARSER
- CREATE TEXT SEARCH TEMPLATE
- CREATE TRIGGER
- CREATE TYPE
- CREATE USER
- CREATE USER MAPPING
- CREATE VIEW
- DEALLOCATE
- DECLARE
- DELETE
- DISCARD
- DO
- DROP AGGREGATE
- DROP CAST
- DROP COLLATION
- DROP CONVERSION
- DROP DATABASE
- DROP DOMAIN
- DROP EXTENSION
- DROP EVENT TRIGGER
- DROP FOREIGN DATA WRAPPER
- DROP FOREIGN TABLE
- DROP FUNCTION
- DROP GROUP
- DROP INDEX
- DROP LANGUAGE
- DROP MATERIALIZED VIEW
- DROP OPERATOR
- DROP OPERATOR CLASS
- DROP OPERATOR FAMILY
- DROP OWNED
- DROP ROLE
- DROP RULE
- DROP SCHEMA
- DROP SEQUENCE
- DROP SERVER
- DROP TABLE
- DROP TABLESPACE
- DROP TEXT SEARCH CONFIGURATION
- DROP TEXT SEARCH DICTIONARY
- DROP TEXT SEARCH PARSER
- DROP TEXT SEARCH TEMPLATE
- DROP TRIGGER
- DROP TYPE
- DROP USER
- DROP USER MAPPING
- DROP VIEW
- END
- EXECUTE
- EXPLAIN
- FETCH
- GRANT
- INSERT
- LISTEN
- LOAD
- LOCK
- MOVE
- NOTIFY
- PREPARE
- PREPARE TRANSACTION
- REASSIGN OWNED
- REFRESH MATERIALIZED VIEW
- REINDEX
- RELEASE SAVEPOINT
- RESET
- REVOKE
- ROLLBACK
- ROLLBACK PREPARED
- ROLLBACK TO SAVEPOINT
- SAVEPOINT
- SECURITY LABEL
- SELECT
- SELECT INTO
- SET
- SET CONSTRAINTS
- SET ROLE
- SET SESSION AUTHORIZATION
- SET TRANSACTION
- SHOW
- START TRANSACTION
- TRUNCATE
- UNLISTEN
- UPDATE
- VACUUM
- VALUES
- II. PostgreSQL 客户端应用程序
- clusterdb
- createdb
- createlang
- createuser
- dropdb
- droplang
- dropuser
- ecpg
- pg_basebackup
- pg_config
- pg_dump
- pg_dumpall
- pg_isready
- pg_receivexlog
- pg_restore
- psql
- reindexdb
- vacuumdb
- III. PostgreSQL 服务器应用程序
- initdb
- pg_controldata
- pg_ctl
- pg_resetxlog
- postgres
- postmaster
- VII. 内部
- Chapter 46. PostgreSQL内部概述
- 46.1. 查询经过的路径
- 46.2. 连接是如何建立起来的
- 46.3. 分析器阶段
- 46.4. PostgreSQL规则系统
- 46.5. 规划器/优化器
- 46.6. 执行器
- Chapter 47. 系统表
- 47.1. 概述
- 47.2. pg_aggregate
- 47.3. pg_am
- 47.4. pg_amop
- 47.5. pg_amproc
- 47.6. pg_attrdef
- 47.7. pg_attribute
- 47.8. pg_authid
- 47.9. pg_auth_members
- 47.10. pg_cast
- 47.11. pg_class
- 47.12. pg_event_trigger
- 47.13. pg_constraint
- 47.14. pg_collation
- 47.15. pg_conversion
- 47.16. pg_database
- 47.17. pg_db_role_setting
- 47.18. pg_default_acl
- 47.19. pg_depend
- 47.20. pg_description
- 47.21. pg_enum
- 47.22. pg_extension
- 47.23. pg_foreign_data_wrapper
- 47.24. pg_foreign_server
- 47.25. pg_foreign_table
- 47.26. pg_index
- 47.27. pg_inherits
- 47.28. pg_language
- 47.29. pg_largeobject
- 47.30. pg_largeobject_metadata
- 47.31. pg_namespace
- 47.32. pg_opclass
- 47.33. pg_operator
- 47.34. pg_opfamily
- 47.35. pg_pltemplate
- 47.36. pg_proc
- 47.37. pg_range
- 47.38. pg_rewrite
- 47.39. pg_seclabel
- 47.40. pg_shdepend
- 47.41. pg_shdescription
- 47.42. pg_shseclabel
- 47.43. pg_statistic
- 47.44. pg_tablespace
- 47.45. pg_trigger
- 47.46. pg_ts_config
- 47.47. pg_ts_config_map
- 47.48. pg_ts_dict
- 47.49. pg_ts_parser
- 47.50. pg_ts_template
- 47.51. pg_type
- 47.52. pg_user_mapping
- 47.53. 系统视图
- 47.54. pg_available_extensions
- 47.55. pg_available_extension_versions
- 47.56. pg_cursors
- 47.57. pg_group
- 47.58. pg_indexes
- 47.59. pg_locks
- 47.60. pg_matviews
- 47.61. pg_prepared_statements
- 47.62. pg_prepared_xacts
- 47.63. pg_roles
- 47.64. pg_rules
- 47.65. pg_seclabels
- 47.66. pg_settings
- 47.67. pg_shadow
- 47.68. pg_stats
- 47.69. pg_tables
- 47.70. pg_timezone_abbrevs
- 47.71. pg_timezone_names
- 47.72. pg_user
- 47.73. pg_user_mappings
- 47.74. pg_views
- Chapter 48. 前/后端协议
- 48.1. 概要
- 48.2. 消息流
- 48.3. 流复制协议
- 48.4. 消息数据类型
- 48.5. 消息格式
- 48.6. 错误和通知消息字段
- 48.7. 自协议 2.0 以来的变化的概述
- Chapter 49. PostgreSQL 编码约定
- 49.1. 格式
- 49.2. 报告服务器里的错误
- 49.3. 错误消息风格指导
- Chapter 50. 本地语言支持
- 50.1. 寄语翻译家
- 50.2. 寄语程序员
- Chapter 51. 书写一个过程语言处理器
- Chapter 52. 写一个外数据包
- 52.1. 外数据封装函数
- 52.2. 外数据封装回调程序
- 52.3. 外数据封装辅助函数
- 52.4. 外数据封装查询规划
- Chapter 53. 基因查询优化器
- 53.1. 作为复杂优化问题的查询处理
- 53.2. 基因算法
- 53.3. PostgreSQL 里的基因查询优化(GEQO)
- 53.4. 进一步阅读
- Chapter 54. 索引访问方法接口定义
- 54.1. 索引的系统表记录
- 54.2. 索引访问方法函数
- 54.3. 索引扫描
- 54.4. 索引锁的考量
- 54.5. 索引唯一性检查
- 54.6. 索引开销估计函数
- Chapter 55. GiST索引
- 55.1. 介绍
- 55.2. 扩展性
- 55.3. 实现
- 55.4. 例
- Chapter 56. SP-GiST索引
- 56.1. 介绍
- 56.2. 扩展性
- 56.3. 实现
- 56.4. 例
- Chapter 57. GIN索引
- 57.1. 介绍
- 57.2. 扩展性
- 57.3. 实现
- 57.4. GIN提示与技巧
- 57.5. 限制
- 57.6. 例子
- Chapter 58. 数据库物理存储
- 58.1. 数据库文件布局
- 58.2. TOAST
- 58.3. 自由空间映射
- 58.4. 可见映射
- 58.5. 初始化分支
- 58.6. 数据库分页文件
- Chapter 59. BKI后端接口
- 59.1. BKI 文件格式
- 59.2. BKI 命令
- 59.3. 系统初始化的BKI文件的结构
- 59.4. 例子
- Chapter 60. 规划器如何使用统计信息
- 60.1. 行预期的例子
- VIII. 附录
- Appendix A. PostgreSQL 错误代码
- Appendix B. 日期/时间支持
- B.1. 日期/时间输入解析
- B.2. 日期/时间关键字
- B.3. 日期/时间配置文件
- B.4. 单位历史
- Appendix C. SQL关键字
- Appendix D. SQL兼容性
- D.1. 支持的特性
- D.2. 不支持的特性
- Appendix E. 版本说明
- E.1. 版本 9.3.1
- E.2. 版本 9.3
- E.3. 版本9.2.5
- E.4. 版本9.2.4
- E.5. 版本9.2.3
- E.6. 版本9.2.2
- E.7. 版本9.2.1
- E.8. 版本9.2
- E.9. 发布9.1.10
- E.10. 发布9.1.9
- E.11. 发布9.1.8
- E.12. 发布9.1.7
- E.13. 发布9.1.6
- E.14. 发布9.1.5
- E.15. 发布9.1.4
- E.16. 发布9.1.3
- E.17. 发布9.1.2
- E.18. 发布9.1.1
- E.19. 发布9.1
- E.20. 版本 9.0.14
- E.21. 版本 9.0.13
- E.22. 版本 9.0.12
- E.23. 版本 9.0.11
- E.24. 版本 9.0.10
- E.25. 版本 9.0.9
- E.26. 版本 9.0.8
- E.27. 版本 9.0.7
- E.28. 版本 9.0.6
- E.29. 版本 9.0.5
- E.30. 版本 9.0.4
- E.31. 版本 9.0.3
- E.32. 版本 9.0.2
- E.33. 版本 9.0.1
- E.34. 版本 9.0
- E.35. 发布8.4.18
- E.36. 发布8.4.17
- E.37. 发布8.4.16
- E.38. 发布8.4.15
- E.39. 发布8.4.14
- E.40. 发布8.4.13
- E.41. 发布8.4.12
- E.42. 发布8.4.11
- E.43. 发布8.4.10
- E.44. 发布8.4.9
- E.45. 发布8.4.8
- E.46. 发布8.4.7
- E.47. 发布8.4.6
- E.48. 发布8.4.5
- E.49. 发布8.4.4
- E.50. 发布8.4.3
- E.51. 发布8.4.2
- E.52. 发布8.4.1
- E.53. 发布8.4
- E.54. 发布8.3.23
- E.55. 发布8.3.22
- E.56. 发布8.3.21
- E.57. 发布8.3.20
- E.58. 发布8.3.19
- E.59. 发布8.3.18
- E.60. 发布8.3.17
- E.61. 发布8.3.16
- E.62. 发布8.3.15
- E.63. 发布8.3.14
- E.64. 发布8.3.13
- E.65. 发布8.3.12
- E.66. 发布8.3.11
- E.67. 发布8.3.10
- E.68. 发布8.3.9
- E.69. 发布8.3.8
- E.70. 发布8.3.7
- E.71. 发布8.3.6
- E.72. 发布8.3.5
- E.73. 发布8.3.4
- E.74. 发布8.3.3
- E.75. 发布8.3.2
- E.76. 发布8.3.1
- E.77. 发布8.3
- E.78. 版本 8.2.23
- E.79. 版本 8.2.22
- E.80. 版本 8.2.21
- E.81. 版本 8.2.20
- E.82. 版本 8.2.19
- E.83. 版本 8.2.18
- E.84. 版本 8.2.17
- E.85. 版本 8.2.16
- E.86. 版本 8.2.15
- E.87. 版本 8.2.14
- E.88. 版本 8.2.13
- E.89. 版本 8.2.12
- E.90. 版本 8.2.11
- E.91. 版本 8.2.10
- E.92. 版本 8.2.9
- E.93. 版本 8.2.8
- E.94. 版本 8.2.7
- E.95. 版本 8.2.6
- E.96. 版本 8.2.5
- E.97. 版本 8.2.4
- E.98. 版本 8.2.3
- E.99. 版本 8.2.2
- E.100. 版本 8.2.1
- E.101. 版本 8.2
- E.102. 版本 8.1.23
- E.103. 版本 8.1.22
- E.104. 版本 8.1.21
- E.105. 版本 8.1.20
- E.106. 版本 8.1.19
- E.107. 版本 8.1.18
- E.108. 版本 8.1.17
- E.109. 版本 8.1.16
- E.110. 版本 8.1.5
- E.111. 版本 8.1.14
- E.112. 版本 8.1.13
- E.113. 版本 8.1.12
- E.114. 版本 8.1.11
- E.115. 版本 8.1.10
- E.116. 版本 8.1.9
- E.117. 版本 8.1.8
- E.118. 版本 8.1.7
- E.119. 版本 8.1.6
- E.120. 版本 8.1.5
- E.121. 版本 8.1.4
- E.122. 版本 8.1.3
- E.123. 版本 8.1.2
- E.124. 版本 8.1.1
- E.125. 版本 8.1
- E.126. 版本 8.0.26
- E.127. 版本 8.0.25
- E.128. 版本 8.0.24
- E.129. 版本 8.0.23
- E.130. 版本 8.0.22
- E.131. 版本 8.0.21
- E.132. 版本 8.0.20
- E.133. 版本 8.0.19
- E.134. 版本 8.0.18
- E.135. 版本 8.0.17
- E.136. 版本 8.0.16
- E.137. 版本 8.0.15
- E.138. 版本 8.0.14
- E.139. 版本 8.0.13
- E.140. 版本 8.0.12
- E.141. 版本 8.0.11
- E.142. 版本 8.0.10
- E.143. 版本 8.0.9
- E.144. 版本 8.0.8
- E.145. 版本 8.0.7
- E.146. 版本 8.0.6
- E.147. 版本 8.0.5
- E.148. 版本 8.0.4
- E.149. 版本 8.0.3
- E.150. 版本 8.0.2
- E.151. 版本 8.0.1
- E.152. 版本 8.0.0
- E.153. 版本 7.4.30
- E.154. 版本 7.4.29
- E.155. 版本 7.4.28
- E.156. 版本 7.4.27
- E.157. 版本 7.4.26
- E.158. 版本 7.4.25
- E.159. 版本 7.4.24
- E.160. 版本 7.4.23
- E.161. 版本 7.4.22
- E.162. 版本 7.4.21
- E.163. 版本 7.4.20
- E.164. 版本 7.4.19
- E.165. 版本 7.4.18
- E.166. 版本 7.4.17
- E.167. 版本 7.4.16
- E.168. 版本 7.4.15
- E.169. 版本 7.4.14
- E.170. 版本 7.4.13
- E.171. 版本 7.4.12
- E.172. 版本 7.4.11
- E.173. 版本 7.4.10
- E.174. 版本 7.4.9
- E.175. 版本 7.4.8
- E.176. 版本 7.4.7
- E.177. 版本 7.4.6
- E.178. 版本 7.4.3
- E.179. 版本 7.4.4
- E.180. 版本 7.4.3
- E.181. 版本 7.4.2
- E.182. 版本 7.4.1
- E.183. 版本 7.4
- E.184. 版本 7.3.21
- E.185. 版本 7.3.20
- E.186. 版本 7.3.19
- E.187. 版本 7.3.18
- E.188. 版本 7.3.17
- E.189. 版本 7.3.16
- E.190. 版本 7.3.15
- E.191. 版本 7.3.14
- E.192. 版本 7.3.13
- E.193. 版本 7.3.12
- E.194. 版本 7.3.11
- E.195. 版本 7.3.10
- E.196. 版本 7.3.9
- E.197. 版本 7.3.8
- E.198. 版本 7.3.7
- E.199. 版本 7.3.6
- E.200. 版本 7.3.5
- E.201. 版本 7.3.4
- E.202. 版本 7.3.3
- E.203. 版本 7.3.2
- E.204. 版本 7.3.1
- E.205. 版本 7.3
- E.206. 版本 7.2.8
- E.207. 版本 7.2.7
- E.208. 版本 7.2.6
- E.209. 版本 7.2.5
- E.210. 版本 7.2.4
- E.211. 版本 7.2.3
- E.212. 版本 7.2.2
- E.213. 版本 7.2.1
- E.214. 版本 7.2
- E.215. 版本 7.1.3
- E.216. 版本 7.1.2
- E.217. 版本 7.1.1
- E.218. 版本 7.1
- E.219. 版本 7.0.3
- E.220. 版本 7.0.2
- E.221. 版本 7.0.1
- E.222. 版本 7.0
- E.223. 版本 6.5.3
- E.224. 版本 6.5.2
- E.225. 版本 6.5.1
- E.226. 版本 6.5
- E.227. 版本 6.4.2
- E.228. 版本 6.4.1
- E.229. 版本 6.4
- E.230. 版本 6.3.2
- E.231. 版本 6.3.1
- E.232. 版本 6.3
- E.233. 版本 6.2.1
- E.234. 版本 6.2
- E.235. 版本 6.1.1
- E.236. 版本 6.1
- E.237. 版本 6.0
- E.238. 版本 1.09
- E.239. 版本 1.02
- E.240. 版本 1.01
- E.241. 版本 1.0
- E.242. Postgres95 版本 0.03
- E.243. Postgres95 版本 0.02
- E.244. Postgres95 版本 0.01
- Appendix F. 额外提供的模块
- F.1. adminpack
- F.2. auth_delay
- F.3. auto_explain
- F.4. btree_gin
- F.5. btree_gist
- F.6. chkpass
- F.7. citext
- F.8. cube
- F.9. dblink
- dblink_connect
- dblink_connect_u
- dblink_disconnect
- dblink
- dblink_exec
- dblink_open
- dblink_fetch
- dblink_close
- dblink_get_connections
- dblink_error_message
- dblink_send_query
- dblink_is_busy
- dblink_get_notify
- dblink_get_result
- dblink_cancel_query
- dblink_get_pkey
- dblink_build_sql_insert
- dblink_build_sql_delete
- dblink_build_sql_update
- F.10. dict_int
- F.11. dict_xsyn
- F.12. dummy_seclabel
- F.13. earthdistance
- F.14. file_fdw
- F.15. fuzzystrmatch
- F.16. hstore
- F.17. intagg
- F.18. intarray
- F.19. isn
- F.20. lo
- F.21. ltree
- F.22. pageinspect
- F.23. passwordcheck
- F.24. pg_buffercache
- F.25. pgcrypto
- F.26. pg_freespacemap
- F.27. pgrowlocks
- F.28. pg_stat_statements
- F.29. pgstattuple
- F.30. pg_trgm
- F.31. postgres_fdw
- F.32. seg
- F.33. sepgsql
- F.34. spi
- F.35. sslinfo
- F.36. tablefunc
- F.37. tcn
- F.38. test_parser
- F.39. tsearch2
- F.40. unaccent
- F.41. uuid-ossp
- F.42. xml2
- Appendix G. 额外提供的程序
- G.1. 客户端应用程序
- oid2name
- pgbench
- vacuumlo
- G.2. 服务器端应用程序
- pg_archivecleanup
- pg_standby
- pg_test_fsync
- pg_test_timing
- pg_upgrade
- pg_xlogdump
- Appendix H. 外部项目
- H.1. 客户端接口
- H.2. 管理工具
- H.3. 过程语言
- H.4. 扩展
- Appendix I. 源代码库
- I.1. 获得源代码通过Git
- Appendix J. 文档
- J.1. DocBook
- J.2. 工具集
- J.3. 制作文档
- J.4. 文档写作
- J.5. 风格指导
- Appendix K. 首字母缩略词
- 参考书目
- Index