33.6. pgtypes 库

Pgtypes库映射PostgreSQL数据库类型到C等值, 它可用于C程序。没有PostgreSQL服务器的帮助下, 它也提供一些函数在C中用这些类型做基本的运算。 请看下面的例子:

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);
free(out);

33.6.1. 数值类型

数值类型提供任意精度的计算。 参见第 8.1 节 获取PostgreSQL服务器等价类型。 由于任意精度这一变量需要能够扩展和动态收缩。 那就是你只能在堆上创建数值变量的原因, 通过PGTYPESnumeric_newPGTYPESnumeric_free函数。 和十进制类型类似但精确度有限,可以在栈中创建也可以在堆上创建。

下列函数用于处理数值类型:

PGTYPESnumeric_new

请求一个新分配的数值变量的指针。

numeric *PGTYPESnumeric_new(void);

PGTYPESnumeric_free

任意数值类型释放所有内存。

void PGTYPESnumeric_free(numeric *var);

PGTYPESnumeric_from_asc

从字符串标号解析数值类型。

numeric *PGTYPESnumeric_from_asc(char *str, char **endptr);

有效格式比如:-2, .794, +3.44, 592.49E07或者 -32.84e-4。 如果值解析不成功,返回一个有效指针,否则空指针。 此刻ECPG总是解析完整的字符串, 所以目前不支持存储在*endptr第一无效字符的地址。 你可以安全地设置endptr为空。

PGTYPESnumeric_to_asc

返回一个指向字符串的指针, 该字符串是通过malloc包 含数值类型num的字符串表示形式分配的。

char *PGTYPESnumeric_to_asc(numeric *num, int dscale);

如果必要的话,数值类型的值将带有dscale小数位数舍入。

PGTYPESnumeric_add

添加两个数值变量到三分之一。

int PGTYPESnumeric_add(numeric *var1, numeric *var2, numeric *result);

函数添加变量var1var2到结果变量result中。 函数成功时返回0,错误情况下返回-1。

PGTYPESnumeric_sub

减去两个数值变量并且返回三分之一结果。

int PGTYPESnumeric_sub(numeric *var1, numeric *var2, numeric *result);

函数从变量var1中减去变量var2。 操作的结果被存储在变量result中。 函数成功时返回0,并且错误的情况下返回-1。

PGTYPESnumeric_mul

两个数值变量相乘,并且返回三分之一结果。

int PGTYPESnumeric_mul(numeric *var1, numeric *var2, numeric *result);

函数将变量var1var2相乘。操作的结果被存储在变量result中。 函数成功时返回0,并且错误的情况下返回-1。

PGTYPESnumeric_div

两个数值变量相除并且返回三分之一结果。

int PGTYPESnumeric_div(numeric *var1, numeric *var2, numeric *result);

函数将变量var1除以变量var2。 操作的结果被存储在变量result中。 函数成功时返回0并且错误的情况下返回-1。

PGTYPESnumeric_cmp

比较两个数值变量。

int PGTYPESnumeric_cmp(numeric *var1, numeric *var2)

这个函数比较两个数值变量。在错误的情况下,返回INT_MAX。 成功,函数返回三个可能结果之一:

  • 如果var1大于var2,则返回1。

  • 如果var1小于var2,则返回-1。

  • 如果var1等于var2,则返回0。

PGTYPESnumeric_from_int

转换一个int变量到数值变量。

int PGTYPESnumeric_from_int(signed int int_val, numeric *var);

这个函数接受有符号整型变量并将其存储在数值变量var中, 成功时,则返回0。在失败的情况下,返回-1。

PGTYPESnumeric_from_long

转换长整型变量到数值变量。

int PGTYPESnumeric_from_long(signed long int long_val, numeric *var);

这个函数接受有符号长整型变量并将其存储在数值变量var中,成功时, 则返回0。在失败的情况下,返回-1。

PGTYPESnumeric_copy

拷贝一个数值变量为另一个变量。

int PGTYPESnumeric_copy(numeric *src, numeric *dst);

这个函数拷贝变量的值,这个变量是src指向dst指向的变量, 成功时返回0,错误的情况下返回-1。

PGTYPESnumeric_from_double

将double类型的变量转换成数值类型的。

int  PGTYPESnumeric_from_double(double d, numeric *dst);

这个函数接受double变量并将其结果存储在dst指向的变量中, 成功时,则返回0。在失败的情况下,返回-1。

PGTYPESnumeric_to_double

将数值类型变量转换成double类型的。

int PGTYPESnumeric_to_double(numeric *nv, double *dp)

这个函数从变量中转换数值类型的值, 这个变量是nv指向的dp指向的double变量, 成功时返回0,错误的情况下返回-1,包括溢出。溢出的时候, 全局变量errno将额外设置PGTYPES_NUM_OVERFLOW

PGTYPESnumeric_to_int

将数值类型变量转化成整型。

int PGTYPESnumeric_to_int(numeric *nv, int *ip);

这个函数从变量中转换数值类型的值,这个变量是nv指向的ip指向的整型变量, 成功时返回0,错误的情况下返回-1,包括溢出。溢出的时候, 全局变量errno将额外设置PGTYPES_NUM_OVERFLOW

PGTYPESnumeric_to_long

将数值类型的变量转换成long类型。

int PGTYPESnumeric_to_long(numeric *nv, long *lp);

这个函数从变量中转换数值类型的值,这个变量是nv指向的lp指向的长整型变量, 成功时返回0,错误的情况下返回-1,包括溢出。溢出的时候, 全局变量errno将额外设置PGTYPES_NUM_OVERFLOW

PGTYPESnumeric_to_decimal

将数值类型的变量转换成十进制类型。

int PGTYPESnumeric_to_decimal(numeric *src, decimal *dst);

这个函数从变量中转换数值类型的值,这个变量是src指向的 dst指向的十进制变量, 成功时返回0,错误的情况下返回-1,包括溢出。溢出的时候, 全局变量errno将额外设置PGTYPES_NUM_OVERFLOW

PGTYPESnumeric_from_decimal

将十进制类型的变量转换成数值类型。

int PGTYPESnumeric_from_decimal(decimal *src, numeric *dst);

这个函数从变量中转换十进制值,这个变量是src指向的dst指向的数值变量, 成功时返回0,错误的情况下返回-1,包括溢出。 由于十进制类型作为数值类型的有限版本实现的,不会发生这种转换溢出。

33.6.2. 日期类型

C中的日期类型允许你的程序处理SQL类型的数据。参见第 8.5 节 获得PostgreSQL服务器的等价类型。

下面的函数可以适用于日期类型:

PGTYPESdate_from_timestamp

从一个时间戳中提取日期部分。

date PGTYPESdate_from_timestamp(timestamp dt);

这个函数接受一个时间戳作为其唯一的参数,并且从时间戳返回提取日期部分。

PGTYPESdate_from_asc

从文本表示解析日期。

date PGTYPESdate_from_asc(char *str, char **endptr);

函数接收C char*字符串str和指向C char*字符串endptr的指针。 此刻ECPG总是解析完整的字符串, 所以目前不支持存储在*endptr中的第一无效字符的地址。 你可以安全地设置endptr无效。

注意,函数始终假定MDY格式化日期目前在ECPG还没有改变。

表 33-2显示了允许输入格式。

表 33-2. PGTYPESdate_from_asc有效输入格式

输入结果
January 8, 1999January 8, 1999
1999-01-08January 8, 1999
1/8/1999January 8, 1999
1/18/1999January 18, 1999
01/02/03February 1, 2003
1999-Jan-08January 8, 1999
Jan-08-1999January 8, 1999
08-Jan-1999January 8, 1999
99-Jan-08January 8, 1999
08-Jan-99January 8, 1999
08-Jan-06January 8, 2006
Jan-08-99January 8, 1999
19990108ISO 8601; January 8, 1999
990108ISO 8601; January 8, 1999
1999.008year and day of year
J2451187Julian day
January 8, 99 BCyear 99 before the Common Era
PGTYPESdate_to_asc

返回一个数据变量的文本表示。

char *PGTYPESdate_to_asc(date dDate);

函数接收日期dDate作为它唯一参数。 输出数据的形式1999-01-18,即YYYY-MM-DD格式。

PGTYPESdate_julmdy

从一个日期型的变量中提取一天、本月和一年的值。

void PGTYPESdate_julmdy(date d, int *mdy);

函数接收日期d和一个指向3个整型值mdy数组的指针。 变量名称显示顺序:mdy[0]设置为包含的几个月份, mdy[1]设置为一天的值,mdy[2]包含一年的值。

PGTYPESdate_mdyjul

从指定日期的年、月、日的3个整型值数组中创建一个日期值。

void PGTYPESdate_mdyjul(int *mdy, date *jdate);

函数接收3个整型(mdy)的数组作为第一个参数, 第二个参数是指向保留运算结果的日期型变量的指针。

PGTYPESdate_dayofweek

返回表示日期值的一个星期数。

int PGTYPESdate_dayofweek(date d);

函数接收日期变量d作为其唯一的参数, 并返回一个整数,表示这个日期的本周的一天。

  • 0 - 星期日

  • 1 - 星期一

  • 2 - 星期二

  • 3 - 星期三

  • 4 - 星期四

  • 5 – 星期五

  • 6 – 星期六

PGTYPESdate_today

得到当前日期。

void PGTYPESdate_today(date *d);

函数接收指向日期变量(d)的一个指针,它设置当前的日期。

PGTYPESdate_fmt_asc

将日期类型变量转换成使用格式掩码的文本表示形式。

int PGTYPESdate_fmt_asc(date dDate, char *fmtstring, char *outbuf);

函数接收一个转换(dDate)日期, 格式掩码(fmtstring)以及保持日期(outbuf)文本表示形式的字符串。

成功的时候返回0,如果产生错误则返回负数。

下列是你可以使用的字段分类符:

  • dd - 某月的天数。

  • mm -某年的月数。

  • yy - 2位数的年数。

  • yyyy -4位数的年数。

  • ddd - 某天的名字(缩略)。

  • mmm - 某月份名字(缩略)。

所有其它的字符按1:1复制到输出字符串中。

表 33-3表示一些可能的格式。 这将让你知道如何使用这些功能。所有输出行基于相同的日期:1959年11月23号。

表 33-3. PGTYPESdate_fmt_asc有效输入格式

格式结果
mmddyy112359
ddmmyy231159
yymmdd591123
yy/mm/dd59/11/23
yy mm dd59 11 23
yy.mm.dd59.11.23
.mm.yyyy.dd..11.1959.23.
mmm. dd, yyyyNov. 23, 1959
mmm dd yyyyNov 23 1959
yyyy dd mm1959 23 11
ddd, mmm. dd, yyyyMon, Nov. 23, 1959
(ddd) mmm. dd, yyyy(Mon) Nov. 23, 1959
PGTYPESdate_defmt_asc

使用格式掩码转换C char*到日期类型的值。

int PGTYPESdate_defmt_asc(date *d, char *fmt, char *str);

函数接收一个指向保持操作(d)结果的日期值的指针, 解析日期(fmt)的格式掩码以及包含日期(str)文本表示的C char*字符串。 希望文本表示匹配格式掩码。但是你不需要字符串1:1映射到格式掩码。 这个函数仅分析相继顺序, 并且查找yy或者yyyy显示年的位置, mm显示月的位置,dd显示一天的位置。

表 33-4表明一些可能的格式。 这将让你知道如果使用这个函数。

表 33-4. rdefmtdate有效输入格式

格式字符串结果
ddmmyy21-2-541954-02-21
ddmmyy2-12-541954-12-02
ddmmyy201119541954-11-20
ddmmyy1304641964-04-13
mmm.dd.yyyyMAR-12-19671967-03-12
yy/mm/dd1954, February 3rd1954-02-03
mmm.dd.yyyy0412691969-04-12
yy/mm/dd在2525年,7月28号人类仍存活。2525-07-28
dd-mm-yy2525年7月28号2525-07-28
mmm.dd.yyyy9/14/581958-09-14
yy/mm/dd47/03/291947-03-29
mmm.dd.yyyyoct 28 19751975-10-28
mmddyyNov 14th, 19851985-11-14

33.6.3. 时间戳类型

C中时间戳类型允许你的程序处理SQL类型时间戳数据。 参见第 8.5 节 获取关于PostgreSQL服务器的等价类型。

下面的函数可以用于时间戳类型。

PGTYPEStimestamp_from_asc

将文本表示的时间戳解析成一个时间戳变量。

timestamp PGTYPEStimestamp_from_asc(char *str, char **endptr);

函数接收一个解析(str)字符串和指向C char*(endptr)指针。 此刻ECPG总是解析完整字符串, 因此它目前不支持存储*endptr中第一无效字符地址。 你可以安全地设置endptr为空。

成功时函数返回解析的时间戳,产生错误时返回PGTYPESInvalidTimestamp, 并且设置errnoPGTYPES_TS_BAD_TIMESTAMP。 参见PGTYPESInvalidTimestamp获取这个值的重要注释。

一般情况下,输入的字符串可以包含一个所允许日期规范、 一个空格字符和允许的时间规范的任意组合。请注意,ECPG不支持时区。 它可以解析它们,但不适用于任何计算比如PostgreSQL服务器。 时区说明符默认是省略的。

表 33-5包含输入字符串的一些例子。

表 33-5. PGTYPEStimestamp_from_asc有效输入格式

输入结果
1999-01-08 04:05:061999-01-08 04:05:06
January 8 04:05:06 1999 PST1999-01-08 04:05:06
1999-Jan-08 04:05:06.789-81999-01-08 04:05:06.789 (忽略时区说明符)
J2451187 04:05-08:001999-01-08 04:05:00 (忽略时区说明符)
PGTYPEStimestamp_to_asc

将日期转换成C char*字符串。

char *PGTYPEStimestamp_to_asc(timestamp tstamp);

函数接收时间戳tstamp作为其唯一的参数 并返回一个包含时间戳的文本表示的分配的字符串。

PGTYPEStimestamp_current

获取当前时间戳。

void PGTYPEStimestamp_current(timestamp *ts);

该函数获取当前时间戳,并且将它保存到ts指向 的时间戳变量中。

PGTYPEStimestamp_fmt_asc

使用格式掩码将时间戳变量转换为C char*。

int PGTYPEStimestamp_fmt_asc(timestamp *ts, char *output, int str_len, char *fmtstr);

这个函数接受一个指向时间戳转换为它的第一个参数(ts)的指针, 一个指向缓冲输出(output), 最大长度已为输出缓冲区(str_len)分配, 并且为转换(fmtstr)设置掩码格式的指针。

一旦成功,该函数返回0,如果产生错误,则返回负值。

你可以为格式掩码使用以下的格式分类符。 格式分类符是和libcstrftime 函数中使用的相同的。 任何非格式分类符将被复制到输出缓冲区。

  • %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 -作为十进制数(1-31)按当月的一天替换; 单位数前面有空格。

  • %F - 等同于%Y-%m-%d

  • %G - 以每年作为一个世纪的十进制数替换。 今年是包含一周的大部分中的一个(星期一作为一周的第一天)。

  • %g - 由%G中的同一年替换, 但作为一个没有世纪(00-99)的十进制数。

  • %H - 作为十进制数(00-23)按小时(24小时)进行替换。

  • %h -等同于%b

  • %I - 作为十进制数(01-12)按小时(12小时)进行替换。

  • %j - 作为十进制数(001-366)按一年的一天来替换。

  • %k - 作为十进制数(0-23)按小时(24小时)替换; 单位数前面有空格。

  • %l - 作为十进制数(1-12)按小时(12小时)替换; 单位数前面有空格。

  • %M - 作为十进制数(00-59)按分钟来替换。

  • %m - 作为十进制数(01-12)按月替换。

  • %n - 通过换行符替换。

  • %O* - 等同于%E*

  • %p - 由合适的"午前""午后"的国家表示形式进行替换。

  • %R - 等同于%H:%M

  • %r - 等同于%I:%M:%S%p

  • %S - 作为十进制数(00-60)按秒进行替换。

  • %s -通过Epoch, UTC以来的秒数替换。

  • %T - 等同于%H:%M:%S

  • %t -通过制表符替换。

  • %U -按照十进制数(00-53)一年中的 周数取代(星期日作为一周的第一天)。

  • %u - 按照十进制数(1-7)工 作日取代(星期一作为一周的第一天)。

  • %V - 通过十进制数(01-53)一年 中的周数取代(星期一作为一周的第一天)。 如果在新的一年中包含一月一日的工作日有四天以上, 那么它是第1周;否则它是去年的最后一周,并且下一周是第1周。

  • %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

从另外一个中减去一个时间戳,并且将结果保存在interval类型的变量中。

int PGTYPEStimestamp_sub(timestamp *ts1, timestamp *ts2, interval *iv);

该函数将减去时间戳变量,这个变量是ts2 指向的ts1指向的时间戳的变量, 并将结果存储在iv指向的时间戳变量中。

成功时,函数返回0。如果发生错误则返回一个负值。

PGTYPEStimestamp_defmt_asc

从使用格式掩码的文本表示中分析一个时间戳值。

int PGTYPEStimestamp_defmt_asc(char *str, char *fmt, timestamp *d);

这个函数接受变量str时间戳的文本表示 以及格式掩码中使用的变量fmt。 结果将存储在d指向的变量中。

如果格式掩码fmt是空, 该函数将回落到缺省格式掩码%Y-%m-%d%H:%M:%S

这是PGTYPEStimestamp_fmt_asc的反向函数。 参见文档找出可能的格式掩码项。

PGTYPEStimestamp_add_interval

增加interval变量到timestamp变量。

int PGTYPEStimestamp_add_interval(timestamp *tin, interval *span, timestamp *tout);

这个函数接受一个指向timestamp变量tin的指针, 一个指向interval变量span的指针。 它增加interval到timestamp, 并且将结果timestamp保存在tout指向的变量中。

成功时,这个函数返回0,如果产生错误,则返回一个负数。

PGTYPEStimestamp_sub_interval

从一个timestamp变量中减去interval变量。

int PGTYPEStimestamp_sub_interval(timestamp *tin, interval *span, timestamp *tout);

这个函数减去interval变量,这个变量是span 指向的tin指向的timestamp变量, 并且将结果存储在tout指向的变量中。

成功时,这个函数返回0。当产生错误的时候,返回负数。

33.6.4. 区间类型

C中区间类型允许你的程序处理SQL类型区间的数据。 参见第 8.5 节获取 PostgreSQL服务器的等价类型。

下面的函数可以用于区间类型:

PGTYPESinterval_new

返回一个已分配的区间变量的指针。

interval *PGTYPESinterval_new(void);

PGTYPESinterval_free

释放已经分配区间变量的内存。

void PGTYPESinterval_new(interval *intvl);

PGTYPESinterval_from_asc

解析文本表示的区间。

interval *PGTYPESinterval_from_asc(char *str, char **endptr);

该函数解析输入的字符串str并返回分配区间变量的指针。 此刻ECPG总是解析完整的字符串,所以目 前不支持存储在*endptr中的第一无效字符的地址。 你可以安全地设置endptr为空。

PGTYPESinterval_to_asc

将类型区间的变量转换成它的文本表示。

char *PGTYPESinterval_to_asc(interval *span);

该函数将转换span指向C char*的区间变量, 输出看起来像这样的例子:@ 1 day 12 hours 59 mins 10 secs

PGTYPESinterval_copy

复制区间类型的变量。

int PGTYPESinterval_copy(interval *intvlsrc, interval *intvldest);

该函数复制intvlsrc指向intvldest指向的区间变量,注意, 你需要在目标变量前分配内存。

33.6.5. 十进制类型

decimal类型和numeric类型是类似的。然而, 它仅仅是一个30位数的最大精度。相反, numeric类型只能在堆上创建,decimal类型可以在栈或堆上创建 (通过函数PGTYPESdecimal_newPGTYPESdecimal_free)。 第 33.15 节中描述的 Informix兼容模式还有很多处 理decimal类型的其他函数。

下面的函数可以用于decimal类型,不仅包含在libcompat库中。

PGTYPESdecimal_new

请求一个新分配的decimal变量的指针。

decimal *PGTYPESdecimal_new(void);

PGTYPESdecimal_free

任意decimal类型,释放所有内存。

void PGTYPESdecimal_free(decimal *var);

33.6.6. pgtypeslib的errno值

PGTYPES_NUM_BAD_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

无效的timestamp字符串被传递给PGTYPEStimestamp_from_asc函数, 或者无效timestamp值被传递给PGTYPEStimestamp_to_asc函数。

PGTYPES_TS_ERR_EINFTIME

在环境中遇到的无限timestamp值不能处理它。

33.6.7. pgtypeslib的特殊常量

PGTYPESInvalidTimestamp

代表一个无效的时间戳的timestamp类型的值。 这是通过函数PGTYPEStimestamp_from_asc返回解析错误。 请注意,由于该timestamp数据类型的内部表示, 同时PGTYPESInvalidTimestamp也是一个有效的timestamp。 它设置1899-12-31 23:59:59。为了检测错误, 确保你的应用每次调用PGTYPEStimestamp_from_asc后 不仅测试PGTYPESInvalidTimestamp也能检测errno != 0