36.6. pgtypes 库
pgtypes 库将PostgreSQL数据库类型映射到 C 中等价的类型以便在 C 程序中使用。它还提供在 C 中对这些类型进行基本计算的函数,即不依赖PostgreSQL服务器进行计算。请看下面的例子:
EXEC SQL BEGIN DECLARE SECTION;
date date1;
timestamp ts1, tsout;
interval iv1;
char *out;
EXEC SQL END DECLARE SECTION;
PGTYPESdate_today(&date1);
EXEC SQL SELECT started, duration INTO :ts1, :iv1 FROM datetbl WHERE d=:date1;
PGTYPEStimestamp_add_interval(&ts1, &iv1, &tsout);
out = PGTYPEStimestamp_to_asc(&tsout);
printf("Started + duration: %s\n", out);
PGTYPESchar_free(out);
36.6.1. 字符串
PGTYPESnumeric_to_asc之类的一些函数返回一个新分配的字符串的指针。这些结果应该用PGTYPESchar_free而不是free释放(这只在Windows上很重要,因为Windows上的内存分配和释放有时候需要由同一个库完成)。
36.6.2. numeric类型
numeric类型用来完成对任意精度的计算。PostgreSQL服务器中等效的类型请见第 8.1 节。因为要用于任意精度,这种变量需要能够动态地扩展和收缩。这也是为什么你只能用PGTYPESnumeric_new和PGTYPESnumeric_free函数在堆上创建numeric变量。decimal类型与numeric类型相似但是在精度上有限制,decimal类型可以在堆上创建也可以在栈上创建。
下列函数可以用于numeric类型:
PGTYPESnumeric_new : 请求一个指向新分配的numeric变量的指针。
```synopsis
numeric *PGTYPESnumeric_new(void);
```
PGTYPESnumeric_free : 释放一个numeric类型,释放它所有的内存。
```synopsis
void PGTYPESnumeric_free(numeric *var);
```
PGTYPESnumeric_from_asc : 从字符串记号中解析一个numeric类型。
```synopsis
numeric *PGTYPESnumeric_from_asc(char *str, char **endptr);
```
例如,可用的格式是:
`-2`、
`.794`、
`+3.44`、
`592.49E07`或者
`-32.84e-4`。
如果值能被成功地解析,将返回一个有效的指针,否则返回 NULL 指针。目前 ECPG 总是解析整个字符串并且因此当前不支持把第一个非法字符的地址存储在`*endptr`中。你可以安全地把`endptr`设置为 NULL。
PGTYPESnumeric_to_asc : 返回由malloc分配的字符串的指针,它包含numeric类型num的字符串表达。
```synopsis
char *PGTYPESnumeric_to_asc(numeric *num, int dscale);
```
numeric值将被使用`dscale`小数位打印,必要时会圆整。结果必须用`PGTYPESchar_free()`释放。
PGTYPESnumeric_add : 把两个numeric变量相加放到第三个numeric变量中。
```synopsis
int PGTYPESnumeric_add(numeric *var1, numeric *var2, numeric *result);
```
该函数把变量`var1`和`var2`相加放到结果变量`result`中。成功时该函数返回 0,出错时返回 -1。
PGTYPESnumeric_sub : 把两个numeric变量相减并且把结果返回到第三个numeric变量。
```synopsis
int PGTYPESnumeric_sub(numeric *var1, numeric *var2, numeric *result);
```
该函数把变量`var2`从变量`var1`中减除。该操作的结果被存储在变量`result`中。成功时该函数返回 0,出错时返回 -1。
PGTYPESnumeric_mul : 把两个numeric变量相乘并且把结果返回到第三个numeric变量。
```synopsis
int PGTYPESnumeric_mul(numeric *var1, numeric *var2, numeric *result);
```
该函数把变量`var1`和`var2`相乘。该操作的结果被存储在变量`result`中。成功时该函数返回 0,出错时返回 -1。
PGTYPESnumeric_div : 把两个numeric变量相除并且把结果返回到第三个numeric变量。
```synopsis
int PGTYPESnumeric_div(numeric *var1, numeric *var2, numeric *result);
```
该函数用变量`var2`除变量`var1`。该操作的结果被存储在变量`result`中。成功时该函数返回 0,出错时返回 -1。
PGTYPESnumeric_cmp : 比较两个numeric变量。
```synopsis
int PGTYPESnumeric_cmp(numeric *var1, numeric *var2)
```
这个函数比较两个numeric变量。错误时会返回`INT_MAX`。成功时,该函数返回三种可能结果之一:
* `var1`大于`var2`则返回 1
* 如果`var1`小于`var2`则返回 -1
* 如果`var1`和`var2`相等则返回 0
PGTYPESnumeric_from_int : 把一个整数变量转换成一个numeric变量。
```synopsis
int PGTYPESnumeric_from_int(signed int int_val, numeric *var);
```
这个函数接受一个有符号整型变量并且把它存储在numeric变量`var`中。成功时返回 0,失败时返回 -1。
PGTYPESnumeric_from_long : 把一个长整型变量转换成一个numeric变量。
```synopsis
int PGTYPESnumeric_from_long(signed long int long_val, numeric *var);
```
这个函数接受一个有符号长整型变量并且把它存储在numeric变量`var`中。成功时返回 0,失败时返回 -1。
PGTYPESnumeric_copy : 把一个numeric变量复制到另一个中。
```synopsis
int PGTYPESnumeric_copy(numeric *src, numeric *dst);
```
这个函数把`src`指向的变量的值复制到`dst`指向的变量中。成功时该函数返回 0,出错时返回 -1。
PGTYPESnumeric_from_double : 把一个双精度类型的变量转换成一个numeric变量。
```synopsis
int PGTYPESnumeric_from_double(double d, numeric *dst);
```
这个函数接受一个双精度类型的变量并且把结果存储在`dst`指向的变量中。成功时该函数返回 0,出错时返回 -1。
PGTYPESnumeric_to_double : 将一个numeric类型的变量转换成双精度。
```synopsis
int PGTYPESnumeric_to_double(numeric *nv, double *dp)
```
这个函数将`nv`指向的变量中的numeric值转换成`dp`指向的双精度变量。成功时该函数返回 0,出错时返回 -1(包括溢出)。溢出时,全局变量`errno`将被额外地设置成`PGTYPES_NUM_OVERFLOW`。
PGTYPESnumeric_to_int : 将一个numeric类型的变量转换成整数。
```synopsis
int PGTYPESnumeric_to_int(numeric *nv, int *ip);
```
该函数将`nv`指向的变量的numeric值转换成`ip`指向的整数变量。成功时该函数返回 0,出错时返回 -1(包括溢出)。溢出时,全局变量`errno`将被额外地设置成`PGTYPES_NUM_OVERFLOW`。
PGTYPESnumeric_to_long : 将一个numeric类型的变量转换成长整型。
```synopsis
int PGTYPESnumeric_to_long(numeric *nv, long *lp);
```
该函数将`nv`指向的变量的numeric值转换成`ip`指向的长整型变量。成功时该函数返回 0,出错时返回 -1(包括溢出)。溢出时,全局变量`errno`将被额外地设置成`PGTYPES_NUM_OVERFLOW`。
PGTYPESnumeric_to_decimal : 将一个numeric类型的变量转换成decimal。
```synopsis
int PGTYPESnumeric_to_decimal(numeric *src, decimal *dst);
```
该函数将`nv`指向的变量的numeric值转换成`ip`指向的decimal变量。成功时该函数返回 0,出错时返回 -1(包括溢出)。溢出时,全局变量`errno`将被额外地设置成`PGTYPES_NUM_OVERFLOW`。
PGTYPESnumeric_from_decimal : 将一个decimal类型的变量转换成numeric。
```synopsis
int PGTYPESnumeric_from_decimal(decimal *src, numeric *dst);
```
该函数将`nv`指向的变量的decimal值转换成`ip`指向的numeric变量。成功时该函数返回 0,出错时返回 -1(包括溢出)。因为decimal类型被实现为numeric类型的一个有限的版本,在这个转换上不会发生溢出。
36.6.3. 日期类型
C 中的日期类型允许你的程序处理 SQL 日期类型的数据。PostgreSQL服务器的等效类型可见第 8.5 节。
下列函数可以被用于日期类型:
PGTYPESdate_from_timestamp : 从一个时间戳中抽取日期部分。
```synopsis
date PGTYPESdate_from_timestamp(timestamp dt);
```
该函数接收一个时间戳作为它的唯一参数并且从这个时间戳返回抽取的日期部分。
PGTYPESdate_from_asc : 从日期的文本表达解析一个日期。
```synopsis
date PGTYPESdate_from_asc(char *str, char **endptr);
```
该函数接收一个 C 的字符串`str`以及一个指向 C 字符串的指针`endptr`。当前 ECPG 总是解析完整的字符串并且因此当前不支持将第一个非法字符的地址存储在`*endptr`中。你可以安全地把`endptr`设置为 NULL。
注意该函数总是假定格式按照 MDY 格式化并且当前在 ECPG 中没有变体可以改变这种格式。
[表 36.2](ecpg-pgtypes.md#ECPG-PGTYPESDATE-FROM-ASC-TABLE "表 36.2. PGTYPESdate_from_asc的合法输入格式")展示了所有允许的输入格式。
****表 36.2. `PGTYPESdate_from_asc`的合法输入格式****
| 输入 | 结果 |
| --- | --- |
| `January 8, 1999` | `January 8, 1999` |
| `1999-01-08` | `January 8, 1999` |
| `1/8/1999` | `January 8, 1999` |
| `1/18/1999` | `January 18, 1999` |
| `01/02/03` | `February 1, 2003` |
| `1999-Jan-08` | `January 8, 1999` |
| `Jan-08-1999` | `January 8, 1999` |
| `08-Jan-1999` | `January 8, 1999` |
| `99-Jan-08` | `January 8, 1999` |
| `08-Jan-99` | `January 8, 1999` |
| `08-Jan-06` | `January 8, 2006` |
| `Jan-08-99` | `January 8, 1999` |
| `19990108` | `ISO 8601; January 8, 1999` |
| `990108` | `ISO 8601; January 8, 1999` |
| `1999.008` | `年以及积日` |
| `J2451187` | `儒略日` |
| `January 8, 99 BC` | `公元前 99 年` |
PGTYPESdate_to_asc : 返回一个日期变量的文本表达。
```synopsis
char *PGTYPESdate_to_asc(date dDate);
```
该函数接收日期`dDate`作为它的唯一参数。它将以形式`1999-01-18`输出该日期,即以`YYYY-MM-DD`格式输出。结果必须用`PGTYPESchar_free()`释放。
PGTYPESdate_julmdy : 从一个日期类型变量中抽取日、月和年的值。
```synopsis
void PGTYPESdate_julmdy(date d, int *mdy);
```
该函数接收日期`d`以及一个指向有 3 个整数值的数组`mdy`的指针。变量名就表明了顺序:`mdy[0]`将被设置为包含月份,`mdy[1]`将被设置为日的值,而`mdy[2]`将包含年。
PGTYPESdate_mdyjul : 从一个由 3 个整数构成的数组创建一个日期值,3 个整数分别指定日、月和年。
```synopsis
void PGTYPESdate_mdyjul(int *mdy, date *jdate);
```
这个函数接收 3 个整数(`mdy`)组成的数组作为其第一个参数,其第二个参数是一个指向日期类型变量的指针,它被用来保存操作的结果。
PGTYPESdate_dayofweek : 为一个日期值返回表示它是星期几的数字。
```synopsis
int PGTYPESdate_dayofweek(date d);
```
这个函数接收日期变量`d`作为它唯一的参数并且返回一个整数说明这个日期是星期几。
* 0 - 星期日
* 1 - 星期一
* 2 - 星期二
* 3 - 星期三
* 4 - 星期四
* 5 - 星期五
* 6 - 星期六
PGTYPESdate_today : 得到当前日期。
```synopsis
void PGTYPESdate_today(date *d);
```
该函数接收一个指向一个日期变量(`d`)的指针并且把该参数设置为当前日期。
PGTYPESdate_fmt_asc : 使用一个格式掩码将一个日期类型的变量转换成它的文本表达。
```synopsis
int PGTYPESdate_fmt_asc(date dDate, char *fmtstring, char *outbuf);
```
该函数接收要转换的日期(`dDate`)、格式掩码(`fmtstring`)以及将要保存日期的文本表达的字符串(`outbuf`)。
成功时,返回 0;如果发生错误,则返回一个负值。
下面是你可以使用的域指示符:
* `dd` - 一个月中的第几天。
* `mm` - 一年中的第几个月。
* `yy` - 两位数的年份。
* `yyyy` - 四位数的年份。
* `ddd` - 星期几的名称(简写)。
* `mmm` - 月份的名称(简写)。
所有其他字符会被原封不动地复制到输出字符串中。
[表 36.3](ecpg-pgtypes.md#ECPG-PGTYPESDATE-FMT-ASC-EXAMPLE-TABLE "表 36.3. PGTYPESdate_fmt_asc的合法输入格式")指出了一些可能的格式。这将给你一些线索如何使用这个函数。所有输出都是基于同一个日期:1959年11月23日。
****表 36.3. `PGTYPESdate_fmt_asc`的合法输入格式****
| 格式 | 结果 |
| --- | --- |
| `mmddyy` | `112359` |
| `ddmmyy` | `231159` |
| `yymmdd` | `591123` |
| `yy/mm/dd` | `59/11/23` |
| `yy mm dd` | `59 11 23` |
| `yy.mm.dd` | `59.11.23` |
| `.mm.yyyy.dd.` | `.11.1959.23.` |
| `mmm. dd, yyyy` | `Nov. 23, 1959` |
| `mmm dd yyyy` | `Nov 23 1959` |
| `yyyy dd mm` | `1959 23 11` |
| `ddd, mmm. dd, yyyy` | `Mon, Nov. 23, 1959` |
| `(ddd) mmm. dd, yyyy` | `(Mon) Nov. 23, 1959` |
PGTYPESdate_defmt_asc : 使用一个格式掩码把一个 C 的 char*子返回串转换成一个日期类型的值。
```synopsis
int PGTYPESdate_defmt_asc(date *d, char *fmt, char *str);
```
该函数接收一个用来保存操作结果的指向日期值的指针(`d`)、用于解析日期的格式掩码(`fmt`)以及包含日期文本表达的 C char* 串(`str`)。该函数期望文本表达匹配格式掩码。不过你不需要字符串和格式掩码的一一映射。该函数只分析相继顺序并且查找表示年份位置的文字`yy`或者`yyyy`、表示月份位置的`mm`以及表示日位置的`dd`。
[表 36.4](ecpg-pgtypes.md#ECPG-RDEFMTDATE-EXAMPLE-TABLE "表 36.4. rdefmtdate的合法输入格式")给出了一些可能的格式。这将给你一些线索如何使用这个函数。
****表 36.4. `rdefmtdate`的合法输入格式****
| 格式 | 字符串 | 结果 |
| --- | --- | --- |
| `ddmmyy` | `21-2-54` | `1954-02-21` |
| `ddmmyy` | `2-12-54` | `1954-12-02` |
| `ddmmyy` | `20111954` | `1954-11-20` |
| `ddmmyy` | `130464` | `1964-04-13` |
| `mmm.dd.yyyy` | `MAR-12-1967` | `1967-03-12` |
| `yy/mm/dd` | `1954, February 3rd` | `1954-02-03` |
| `mmm.dd.yyyy` | `041269` | `1969-04-12` |
| `yy/mm/dd` | `在 2525 年的七月二十八日,人类还将存在` | `2525-07-28` |
| `dd-mm-yy` | `也是 2525 年七月的二十八日` | `2525-07-28` |
| `mmm.dd.yyyy` | `9/14/58` | `1958-09-14` |
| `yy/mm/dd` | `47/03/29` | `1947-03-29` |
| `mmm.dd.yyyy` | `oct 28 1975` | `1975-10-28` |
| `mmddyy` | `Nov 14th, 1985` | `1985-11-14` |
36.6.4. 时间戳类型
C 中的时间戳类型允许你的程序处理 SQL 时间戳类型的数据。PostgreSQL服务器的等效类型可见第 8.5 节。
下列函数可用于时间戳类型:
PGTYPEStimestamp_from_asc : 从文本表达解析一个时间戳并放到一个时间戳变量中。
```synopsis
timestamp PGTYPEStimestamp_from_asc(char *str, char **endptr);
```
这个函数接收一个要解析的字符串(`str`)以及一个 C char* 的指针(`endptr`)。当前 ECPG 总是解析完整的字符串并且因此当前不支持将第一个非法字符的地址存储在`*endptr`中。你可以安全地把`endptr`设置为 NULL。
成功时该函数返回解析到的时间戳。错误时,会返回`PGTYPESInvalidTimestamp`并且`errno`会被设置为`PGTYPES_TS_BAD_TIMESTAMP`。关于这个值的重要提示请见[`PGTYPESInvalidTimestamp`](ecpg-pgtypes.md#PGTYPESINVALIDTIMESTAMP)。
通常,该输入字符串能够包含一个允许的日期说明、一个空格字符和一个允许的时间说明的任意组合。注意 ECPG 不支持时区。它能够解析时区但是不会应用任何计算(例如PostgreSQL服务器所作的事情)。时区指示符会被无声无息地丢弃。
[表 36.5](ecpg-pgtypes.md#ECPG-PGTYPESTIMESTAMP-FROM-ASC-EXAMPLE-TABLE "表 36.5. PGTYPEStimestamp_from_asc的合法输入格式")包含输入字符串的一些例子。
****表 36.5. `PGTYPEStimestamp_from_asc`的合法输入格式****
| 输入 | 结果 |
| --- | --- |
| `1999-01-08 04:05:06` | `1999-01-08 04:05:06` |
| `January 8 04:05:06 1999 PST` | `1999-01-08 04:05:06` |
| `1999-Jan-08 04:05:06.789-8` | `1999-01-08 04:05:06.789 (忽略了时区指示符)` |
| `J2451187 04:05-08:00` | `1999-01-08 04:05:00 (忽略了时区指示符)` |
PGTYPEStimestamp_to_asc : 将一个日期转换成一个 C char* 字符串。
```synopsis
char *PGTYPEStimestamp_to_asc(timestamp tstamp);
```
该函数接收时间戳`tstamp`作为它的唯一参数并且返回一个分配好的包含该时间戳文本表达的字符串。结果必须用`PGTYPESchar_free()`释放。
PGTYPEStimestamp_current : 检索当前的时间戳。
```synopsis
void PGTYPEStimestamp_current(timestamp *ts);
```
该函数检索当前的时间戳并且将它保存在`ts`指向的时间戳变量。
PGTYPEStimestamp_fmt_asc : 使用一个格式掩码将一个时间戳变量转换成一个 C char* 。
```synopsis
int PGTYPEStimestamp_fmt_asc(timestamp *ts, char *output, int str_len, char *fmtstr);
```
该函数接收一个指向时间戳的指针作为它的第一个参数(`ts`)、一个指向输出缓冲区的指针(`output`)、为输出缓冲区分配的最大长度(`str_len`)以及用于转换的格式掩码(`fmtstr`)。
成功时,该函数返回 0;如果有错误发生,则返回一个负值。
你可以为格式掩码使用下列格式指示符。格式指示符就是用在libc的`strftime`函数中的那一些。任何非格式指示符将被复制到输出缓冲区。
* `%A` - 被完整的星期几名称的本国表达所替换。
* `%a` - 被简写星期几名称的本国表达所替换。
* `%B` - 被完整的月份名称的本国表达所替换。
* `%b` - 被简写月份名称的本国表达所替换。
* `%C` - 被十进制数(年份/100)所替换,单一数字会被前置一个零。
* `%c` - 被时间和日期的本国表达所替换。
* `%D` - 等效于`%m/%d/%y`。
* `%d` - 被十进制数(01-31)的日所替换。
* `%E*` `%O*` - POSIX 区域扩展。序列
`%Ec`
`%EC`
`%Ex`
`%EX`
`%Ey`
`%EY`
`%Od`
`%Oe`
`%OH`
`%OI`
`%Om`
`%OM`
`%OS`
`%Ou`
`%OU`
`%OV`
`%Ow`
`%OW`
`%Oy`
被假定提供可供选择的表达。
此外还实现了`%OB`来表达可供选择的月份名称(单独使用,不用提过的日)。
* `%e` - 被十进制数(01-31)的日所替换,单一数字被前置一个空格。
* `%F` - 等效于`%Y-%m-%d`。
* `%G` - 被替换为一个带有世纪的十进制数年份。这个年份是包含这一周大部分的年份(星期一作为这一周的第一天)。
* `%g` - 被替换为与`%G`中相同的年份,但是作为一个不带世纪的十进制数(00-99)。
* `%H` - 被替换为一个十进制数的小时(24 小时制,00-23)。
* `%h` - 和`%b`相同。
* `%I` - 被替换为一个十进制数的小时(12 小时制,01-12)。
* `%j` - 被替换为一个十进制数的积日(001-366)。
* `%k` - 被替换为一个十进制数的小时(24 小时制,00-23),单一数字被前置一个空白。
* `%l` - 被替换为一个十进制数的小时(12 小时制,01-12),单一数字被前置一个空白。
* `%M` - 被替换为一个十进制数的分钟(00-59)。
* `%m` - 被替换为一个十进制数的月份(01-12)。
* `%n` - 被替换为一个新行。
* `%O*` - 和`%E*`相同。
* `%p` - 根据情况被替换为“午前”或“午后”的本国表达。
* `%R` - 等效于`%H:%M`。
* `%r` - 等效于`%I:%M:%S%p`。
* `%S` - 被替换为十进制数的秒(00-60)。
* `%s` - 被替换为从 UTC 新纪元以来的秒数。
* `%T` - 等效于`%H:%M:%S`
* `%t` - 被替换为一个制表符。
* `%U` - 被替换为十进制数的周数(周日作为一周的第一天,00-53)。
* `%u` - 被替换为十进制数的星期几(周一作为一周的第一天,1-7)。
* `%V` - 被替换为十进制数的周数(周一作为一周的第一天,01-53)。如果包含 1 月 1 日的周在新年中有 4 天或更多天,那么它是第一周。否则它是前一年的最后一周,并且下一周是第一周。
* `%v` - 等效于`%e-%b-%Y`。
* `%W` - 被替换为十进制数的周数(周一作为一周的第一天,00-53)。
* `%w` - 被替换为十进制数的星期几(0-6,周日作为一周的第一天)。
* `%X` - 被替换为时间的本国表达。
* `%x` - 被替换为日期的本国表达。
* `%Y` - 被替换为十进制数的带世纪的年份。
* `%y` - 被替换为十进制数的不带世纪的年份(00-99)。
* `%Z` - 被替换为时区名称。
* `%z` - 被替换为相对于 UTC 的时区偏移;一个前导的加号表示 UTC 东部,一个负号表示 UTC 西部,接着是分别有两个数字的小时和分钟并且它们之间没有定界符(RFC 822 日期头部的一般形式)。
* `%+` - 被替换为日期和时间的本国表达。
* `%-*` - GNU libc 扩展。在执行数值输出时不做任何填充。
* $_* - GNU libc 扩展。显式地指定用空格填充。
* `%0*` - GNU libc 扩展。显式地指定用零填充。
* `%%` - 被替换为`%`。
PGTYPEStimestamp_sub : 从一个时间戳中减去另一个时间戳并且把结果保存在一个区间类型的变量中。
```synopsis
int PGTYPEStimestamp_sub(timestamp *ts1, timestamp *ts2, interval *iv);
```
该函数将从`ts1`指向的时间戳变量中减去`ts2`指向的时间戳变量,并且将把结果存储在`iv`指向的区间变量中。
成功时,该函数返回 0;发生错误时则返回一个负值。
PGTYPEStimestamp_defmt_asc : 用一个格式掩码从时间戳的文本表达解析其值。
```synopsis
int PGTYPEStimestamp_defmt_asc(char *str, char *fmt, timestamp *d);
```
该函数接收一个放在变量`str`中的时间戳文本表达以及放在变量`fmt`中的要使用的格式掩码。结果将被存放在`d`指向的变量中。
如果格式掩码`fmt`是NULL,该函数将回退到使用默认的格式掩码`%Y-%m-%d %H:%M:%S`。
这是[`PGTYPEStimestamp_fmt_asc`](ecpg-pgtypes.md#PGTYPESTIMESTAMPFMTASC)的逆函数。可能的格式掩码项可以参考那个函数的文档。
PGTYPEStimestamp_add_interval : 把一个interval变量加到一个时间戳变量上。
```synopsis
int PGTYPEStimestamp_add_interval(timestamp *tin, interval *span, timestamp *tout);
```
该函数接收一个指向时间戳变量的指针`tin`以及一个指向interval变量的指针`span`。它把interval加到时间戳上,然后将结果时间戳保存在`tout`指向的变量中。
成功时该函数返回0,如果发生错误则返回一个负值。
PGTYPEStimestamp_sub_interval : 从一个时间戳变量中减去一个interval变量。
```synopsis
int PGTYPEStimestamp_sub_interval(timestamp *tin, interval *span, timestamp *tout);
```
该函数从`tin`指向的时间戳变量中减去`span`指向的interval变量,然后把结果保存在`tout`指向的变量中。
成功时该函数返回0,如果发生错误则返回一个负值。
36.6.5. 区间类型
C 中的区间类型允许你的程序处理 SQL 区间类型的数据。PostgreSQL服务器的等效类型可见第 8.5 节。
下列函数可以用于区间类型:
PGTYPESinterval_new : 返回一个指向新分配的区间变量的指针。
```synopsis
interval *PGTYPESinterval_new(void);
```
PGTYPESinterval_free : 释放先前分配的区间变量的内存。
```synopsis
void PGTYPESinterval_new(interval *intvl);
```
PGTYPESinterval_from_asc : 从文本表达解析一个区间。
```synopsis
interval *PGTYPESinterval_from_asc(char *str, char **endptr);
```
该函数解析输入字符串`str`并且返回一个已分配的区间变量的指针。目前 ECPG 总是解析整个字符串并且因此当前不支持把第一个非法字符的地址存储在`*endptr`中。你可以安全地把`endptr`设置为 NULL。
PGTYPESinterval_to_asc : 将一个区间类型的变量转换成它的文本表达。
```synopsis
char *PGTYPESinterval_to_asc(interval *span);
```
该函数将`span`指向的区间变量转换成一个 C char*。输出看起来像这个例子:
`@ 1 day 12 hours 59 mins 10 secs`。结果必须用`PGTYPESchar_free()`释放。
PGTYPESinterval_copy : 复制一个区间类型的变量。
```synopsis
int PGTYPESinterval_copy(interval *intvlsrc, interval *intvldest);
```
该函数将`intvlsrc`指向的区间变量复制到`intvldest`指向的区间变量。注意你需要现为目标变量分配好内存。
36.6.6. decimal类型
decimal类型和numeric类型相似。不过,它被限制为最大精度是 30 个有效位。与numeric类型只能在堆上创建相反,decimal类型既可以在栈上也可以在堆上创建(使用函数PGTYPESdecimal_new 和PGTYPESdecimal_free)。在第 36.15 节中描述的Informix兼容模式中有很多其它函数可以处理decimal类型。
下列函数可以被用于decimal类型并且不仅被包含于libcompat库中。
PGTYPESdecimal_new : 要求一个指向新分配的decimal变量的指针。
```synopsis
decimal *PGTYPESdecimal_new(void);
```
PGTYPESdecimal_free : 释放一个decimal类型,释放它的所有内存。
```synopsis
void PGTYPESdecimal_free(decimal *var);
```
36.6.7. pgtypeslib 的 errno 值
PGTYPES_NUM_BAD_NUMERIC : 一个参数应该包含一个numeric变量(或者指向一个numeric变量),但是实际上它的内存表达非法。
PGTYPES_NUM_OVERFLOW : 发生一次溢出。由于numeric类型可以处理几乎任何精度,将一个numeric变量转换成其他类型可能导致溢出。
PGTYPES_NUM_UNDERFLOW : 发生一次下溢。由于numeric类型可以处理几乎任何精度,将一个numeric变量转换成其他类型可能导致下溢。
PGTYPES_NUM_DIVIDE_ZERO : 尝试了一次除零。
PGTYPES_DATE_BAD_DATE : 一个非法的日期字符串被传给了PGTYPESdate_from_asc函数。
PGTYPES_DATE_ERR_EARGS : 非法参数被传给了PGTYPESdate_defmt_asc函数。
PGTYPES_DATE_ERR_ENOSHORTDATE : PGTYPESdate_defmt_asc函数在输入字符串中发现了一个非法记号。
PGTYPES_INTVL_BAD_INTERVAL : 一个非法的区间字符串被传给了PGTYPESinterval_from_asc函数,或者一个非法的区间值被传给了PGTYPESinterval_to_asc函数。
PGTYPES_DATE_ERR_ENOTDMY : 在PGTYPESdate_defmt_asc函数中有日/月/年不匹配的赋值。
PGTYPES_DATE_BAD_DAY : PGTYPESdate_defmt_asc函数发现了月中的一个非法日值。
PGTYPES_DATE_BAD_MONTH : PGTYPESdate_defmt_asc函数发现了一个非法的月值。
PGTYPES_TS_BAD_TIMESTAMP : 一个非法的时间戳字符串被传给了PGTYPEStimestamp_from_asc函数,或者一个非法的时间戳值被传给了PGTYPEStimestamp_to_asc函数。
PGTYPES_TS_ERR_EINFTIME : 在一个无法处理无限时间戳值的环境中遇到了这样一个值。
36.6.8. pgtypeslib 的特殊常量
PGTYPESInvalidTimestamp : 表示一个非法时间戳的时间戳类型值。在解析错误时,函数PGTYPEStimestamp_from_asc会返回这个值。注意由于timestamp数据类型的内部表达,PGTYPESInvalidTimestamp在同时也是一个合法的时间戳。它被设置为1899-12-31 23:59:59。为了检测到错误,确认你的应用在每次调用PGTYPEStimestamp_from_asc后不仅仅测试PGTYPESInvalidTimestamp,还应该测试errno != 0。
