Hi, > Andy Fan <[email protected]> writes: >> You understood me correctly and I thought we should maintain one version >> two years ago, so I tried to implement this idea today. The first issue I >> want to talk about now how to define the function protocol in SQL, take >> int4out for example: > >> master: cstring int4out(integer); >> New protocol: void int4out(integer, internal). and the internal is >> StringInfo acutally. > >> The direct impaction would be: > >> master support: >> postgres=# select int4out(8); >> int4out >> --------- >> 8 >> (1 row) > >> After our change, user could not invoke any {type}out function anymore >> in SQL since it takes 'internal' as an agrument. I am not sure if people >> would write SQL like this, but it'd be good to have a talk about this. > > I think you missed the point of what I said two years ago: you will > never be able to remove the existing output function API, nor the > per-datatype functions that implement that API. Even if we were > willing to convert every last one of the in-core callers and callees, > doing that would break too much non-core code. So the above example > is never going to stop working.
OK, I thought David supported this and you didn't object it, so I planned to try with this way. > We can consider implementing a new datatype output API alongside the > existing one. But it'd likely not be callable from SQL, so the > question of SQL compatibility is moot. > > My own guess is that even if we built a new API, only a fairly small > number of datatypes would find it worth the trouble to support. > The potential win seems clear for, say, textout: there's really no > computation to do, only data copying, so halving the amount of > copying is attractive. But I bet you won't measure much percentage > improvement for numeric_out or point_out. Is this similar with the soluation I called as print function at [1] """ My high level proposal is define a type specific print function like: oidprint(Datum datum, StringInfo buf) textprint(Datum datum, StringInfo buf) """ Then we can have benefits like compatibility, incremental development (start from common used/potential win data type). But as David said "what would be the point of having both versions?" at [2], actually I was persuaded by this, I thought David's method paies more effort to current patch now and save the maintain effort in future. (To be honest, I am not good at the trade off...) So looks we have 3 soluation for now IIUC. (1) Maintaining one copy of output function (David's proposal). (2) Add a new type of API for some specific data types like above. (3) Andres's method, acutally I can't follow well now. >From Andres: > FWIW, I've experimented fixing this overhead before, and what I did was to > pass an optional context via the fcinfo, and output / send functions could use > memory allocated via that optional context object, rather than doing it > allocating in CurrentMemoryContext. For the send functions that looks > reasonably clean, given that it already deals with a stringinfo. For out > functions it's a bit uglier, but still somewhat acceptable. Puting optional context via the fcinfo looks novel to me (I have zero experience to use fcinfo utility.). Then I'm not sure how to use the optional context, Will it be a MemoryContext or a StringInfo? If MemoryContext, then how to avoid the memory copy in the printtup sistuation or this method has different target. > This line of thought suggests that maybe some special-purpose hack > would be a better answer than defining a new datatype API. It's hard > to tell without some concrete performance numbers, which are sadly > lacking in this thread. Does the data in [3] helpful? Quote the message there: "The attached is PoC of this idea, not matter which method are adopted (rewrite all the outfunction or a optional print function), I think the benefit will be similar. In the blew test case, it shows us 10%+ improvements. (0.134ms vs 0.110ms) create table demo as select oid as oid1, relname::text as text1, relam, relname::text as text2 from pg_class; pgbench: select * from demo;" I re-attached the patch there, just rebased with the latest master. [1] https://www.postgresql.org/message-id/87wmjzfz0h.fsf%40163.com [2] https://www.postgresql.org/message-id/CAApHDvqHthJb6baDhgTE5T4RLW6nEX%3Dr239EYmpjfg%3DWq5CqQA%40mail.gmail.com [3] https://www.postgresql.org/message-id/87v7zihaf1.fsf%40163.com -- Best Regards Andy Fan
>From f4b88d3533008c2d7a23f6b907d4d1adac6dad02 Mon Sep 17 00:00:00 2001 From: Andy Fan <[email protected]> Date: Wed, 11 Sep 2024 12:25:52 +0800 Subject: [PATCH v20260912 3/4] add unlikely hint for enlargeStringInfo. enlargeStringInfo has a noticeable ratio in perf peport with a "select * from pg_class" workload). So add a unlikely hint in enlargeStringinfo to avoid some overhead. --- src/common/stringinfo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/stringinfo.c b/src/common/stringinfo.c index ae39540e468..a725ef132d7 100644 --- a/src/common/stringinfo.c +++ b/src/common/stringinfo.c @@ -345,7 +345,7 @@ enlargeStringInfo(StringInfo str, int needed) * Guard against out-of-range "needed" values. Without this, we can get * an overflow or infinite loop in the following. */ - if (needed < 0) /* should not happen */ + if (unlikely(needed < 0)) /* should not happen */ { #ifndef FRONTEND elog(ERROR, "invalid string enlargement request size: %d", needed); @@ -354,7 +354,7 @@ enlargeStringInfo(StringInfo str, int needed) exit(EXIT_FAILURE); #endif } - if (((Size) needed) >= (MaxAllocSize - (Size) str->len)) + if (unlikely(((Size) needed) >= (MaxAllocSize - (Size) str->len))) { #ifndef FRONTEND ereport(ERROR, -- 2.43.0
>From 7e125bab012fa05cfc8637ab13a3c84bb2939daa Mon Sep 17 00:00:00 2001 From: Andy Fan <[email protected]> Date: Wed, 11 Sep 2024 12:19:57 +0800 Subject: [PATCH v20260912 1/4] Refactor float8out_internval for better performance Some users like cube, geo needs calls float8out_internval to get a string, and then copy them into its own StringInfo. In this commit, we would let the user provide a buffer to float8out_internal so that it can put the data to buffer directly. This commit also reuse the existing string length to avoid another strlen call in appendStringInfoString. --- contrib/cube/cube.c | 17 +++++++++++++++-- src/backend/utils/adt/float.c | 14 ++++++++------ src/backend/utils/adt/geo_ops.c | 34 ++++++++++++++++++++++----------- src/include/catalog/pg_type.h | 1 + src/include/utils/float.h | 2 +- 5 files changed, 48 insertions(+), 20 deletions(-) diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c index 77263ab277f..a143690092b 100644 --- a/contrib/cube/cube.c +++ b/contrib/cube/cube.c @@ -12,6 +12,7 @@ #include "access/gist.h" #include "access/stratnum.h" +#include "catalog/pg_type.h" #include "cubedata.h" #include "libpq/pqformat.h" #include "utils/array.h" @@ -299,26 +300,38 @@ cube_out(PG_FUNCTION_ARGS) StringInfoData buf; int dim = DIM(cube); int i; + int str_len; initStringInfo(&buf); appendStringInfoChar(&buf, '('); + + /* 3 for ", " and 1 for '\0'. */ + enlargeStringInfo(&buf, (MAXFLOAT8LEN + 4) * dim); for (i = 0; i < dim; i++) { if (i > 0) appendStringInfoString(&buf, ", "); - appendStringInfoString(&buf, float8out_internal(LL_COORD(cube, i))); + float8out_internal(LL_COORD(cube, i), buf.data + buf.len, &str_len); + buf.len += str_len; + buf.data[buf.len] = '\0'; } appendStringInfoChar(&buf, ')'); if (!cube_is_point_internal(cube)) { appendStringInfoString(&buf, ",("); + + /* 3 for ", " and 1 for '\0'. */ + enlargeStringInfo(&buf, (MAXFLOAT8LEN + 4) * dim); for (i = 0; i < dim; i++) { if (i > 0) appendStringInfoString(&buf, ", "); - appendStringInfoString(&buf, float8out_internal(UR_COORD(cube, i))); + + float8out_internal(UR_COORD(cube, i), buf.data + buf.len, &str_len); + buf.len += str_len; + buf.data[buf.len] = '\0'; } appendStringInfoChar(&buf, ')'); } diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 362c29ab803..13ceade129e 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -571,22 +571,24 @@ float8out(PG_FUNCTION_ARGS) * float8out_internal - guts of float8out() * * This is exposed for use by functions that want a reasonably - * platform-independent way of outputting doubles. - * The result is always palloc'd. + * platform-independent way of outputting doubles, output the + * string length to *len; */ char * -float8out_internal(double num) +float8out_internal(double num, char *ascii, int *len) { - char *ascii = (char *) palloc(32); int ndig = DBL_DIG + extra_float_digits; + if (ascii == NULL) + ascii = (char *) palloc(MAXFLOAT8LEN); + if (extra_float_digits > 0) { - double_to_shortest_decimal_buf(num, ascii); + *len = double_to_shortest_decimal_buf(num, ascii); return ascii; } - (void) pg_strfromd(ascii, 32, ndig, num); + *len = pg_strfromd(ascii, 32, ndig, num); return ascii; } diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index cc5ce013d0f..212faf08a50 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -29,6 +29,7 @@ #include <float.h> #include <ctype.h> +#include "catalog/pg_type.h" #include "libpq/pqformat.h" #include "miscadmin.h" #include "nodes/miscnodes.h" @@ -203,10 +204,12 @@ single_decode(char *num, float8 *x, char **endptr_p, static void single_encode(float8 x, StringInfo str) { - char *xstr = float8out_internal(x); + int str_len; + enlargeStringInfo(str, MAXFLOAT8LEN + 1); + float8out_internal(x, str->data + str->len, &str_len); - appendStringInfoString(str, xstr); - pfree(xstr); + str->len += str_len; + str->data[str->len] = '\0'; } /* single_encode() */ static bool @@ -255,12 +258,20 @@ fail: static void pair_encode(float8 x, float8 y, StringInfo str) { - char *xstr = float8out_internal(x); - char *ystr = float8out_internal(y); + int data_len; + /* the additional 2 is for ',' and '\0' */ + enlargeStringInfo(str, MAXFLOAT8LEN * 2 + 2); - appendStringInfo(str, "%s,%s", xstr, ystr); - pfree(xstr); - pfree(ystr); + float8out_internal(x, str->data + str->len, &data_len); + str->len += data_len; + + str->data[str->len] = ','; + str->len++; + + float8out_internal(y, str->data + str->len, &data_len); + str->len += data_len; + + str->data[str->len] = '\0'; } static bool @@ -1041,9 +1052,10 @@ Datum line_out(PG_FUNCTION_ARGS) { LINE *line = PG_GETARG_LINE_P(0); - char *astr = float8out_internal(line->A); - char *bstr = float8out_internal(line->B); - char *cstr = float8out_internal(line->C); + int datalen; + char *astr = float8out_internal(line->A, NULL, &datalen); + char *bstr = float8out_internal(line->B, NULL, &datalen); + char *cstr = float8out_internal(line->C, NULL, &datalen); PG_RETURN_CSTRING(psprintf("%c%s%c%s%c%s%c", LDELIM_L, astr, DELIM, bstr, DELIM, cstr, RDELIM_L)); diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 74183ec5a2e..b3858188131 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -348,6 +348,7 @@ MAKE_SYSCACHE(TYPENAMENSP, pg_type_typname_nsp_index, 64); #endif /* EXPOSE_TO_CLIENT_CODE */ +#define MAXFLOAT8LEN 32 extern ObjectAddress TypeShellMake(const char *typeName, Oid typeNamespace, diff --git a/src/include/utils/float.h b/src/include/utils/float.h index ffa743d6273..69661a7071f 100644 --- a/src/include/utils/float.h +++ b/src/include/utils/float.h @@ -43,7 +43,7 @@ extern float8 float8in_internal(char *num, char **endptr_p, extern float4 float4in_internal(char *num, char **endptr_p, const char *type_name, const char *orig_string, struct Node *escontext); -extern char *float8out_internal(float8 num); +extern char *float8out_internal(float8 num, char *ascii, int *len); extern int float4_cmp_internal(float4 a, float4 b); extern int float8_cmp_internal(float8 a, float8 b); -- 2.43.0
>From 8418f76079ddb589f9861a83059271b5eaceaf77 Mon Sep 17 00:00:00 2001 From: Andy Fan <[email protected]> Date: Wed, 11 Sep 2024 12:21:39 +0800 Subject: [PATCH v20260912 2/4] Continue to remove some unnecesary strlen calls sprintf return the number of characters printed (not including the trailing `\0'), so it is exactly same as strlen. so we can reuse that value and avoid a strlen call. --- src/backend/utils/adt/datetime.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 8f25c15fcfc..83c3c85305b 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -4717,6 +4717,7 @@ EncodeInterval(struct pg_itm *itm, int style, char *str) int fsec = itm->tm_usec; bool is_before = false; bool is_zero = true; + int data_len; /* * The sign of year and month are guaranteed to match, since they are @@ -4774,11 +4775,11 @@ EncodeInterval(struct pg_itm *itm, int style, char *str) char sec_sign = (hour < 0 || min < 0 || sec < 0 || fsec < 0) ? '-' : '+'; - sprintf(cp, "%c%d-%d %c%" PRId64 " %c%" PRId64 ":%02d:", + data_len = sprintf(cp, "%c%d-%d %c%" PRId64 " %c%" PRId64 ":%02d:", year_sign, abs(year), abs(mon), day_sign, i64abs(mday), sec_sign, i64abs(hour), abs(min)); - cp += strlen(cp); + cp += data_len; cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true); *cp = '\0'; } @@ -4788,16 +4789,16 @@ EncodeInterval(struct pg_itm *itm, int style, char *str) } else if (has_day) { - sprintf(cp, "%" PRId64 " %" PRId64 ":%02d:", + data_len = sprintf(cp, "%" PRId64 " %" PRId64 ":%02d:", mday, hour, min); - cp += strlen(cp); + cp += data_len; cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true); *cp = '\0'; } else { - sprintf(cp, "%" PRId64 ":%02d:", hour, min); - cp += strlen(cp); + data_len = sprintf(cp, "%" PRId64 ":%02d:", hour, min); + cp += data_len; cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true); *cp = '\0'; } -- 2.43.0
>From 03d9c947c960a8ac2a41ff35aebc3a1673446e13 Mon Sep 17 00:00:00 2001 From: Andy Fan <[email protected]> Date: Thu, 12 Sep 2024 10:03:57 +0000 Subject: [PATCH v20260912 4/4] Make printtup a bit faster (intermediate state). Currently the out function usually allocate its own memory and fill it with the cstring. After the printtup get the cstring, printtup computes it string length and copy it to its own StringInfo. So there are some wastage in this workflow. In the desired case, out function should take a StringInfo as a input and fill the data to StringInfo's buffer directly. Within this way, there is no extra memory allocate, memory copy and probably avoid the most strlen since the most of the outfunction can compute it easily. for example a). snprintf return the length encoded string, b). the varlena's header has a strlen. c). we know the start position before we encode a Datum and we know the end position after the Datum encoding, so the length would be similar as 'end_pos - start_pos'. Since we have 79 out functions to change, this patch just finish part of them by using a new print function and wish a review of it. If there are anything wrong, it is better know them earlier. --- src/backend/access/common/printtup.c | 80 ++++++++++++++++++-- src/backend/utils/adt/char.c | 32 ++++++++ src/backend/utils/adt/date.c | 74 ++++++++++++++++++- src/backend/utils/adt/datetime.c | 17 ++++- src/backend/utils/adt/float.c | 53 +++++++++++++- src/backend/utils/adt/int.c | 32 ++++++++ src/backend/utils/adt/int8.c | 16 ++++ src/backend/utils/adt/numeric.c | 68 +++++++++++++++-- src/backend/utils/adt/oid.c | 16 ++++ src/backend/utils/adt/timestamp.c | 106 ++++++++++++++++++++++++++- src/backend/utils/adt/varchar.c | 25 +++++++ src/backend/utils/adt/varlena.c | 16 ++++ src/include/catalog/pg_proc.dat | 83 ++++++++++++++++++++- src/include/lib/stringinfo.h | 19 +++++ src/include/utils/date.h | 2 +- src/include/utils/datetime.h | 8 +- 16 files changed, 618 insertions(+), 29 deletions(-) diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index 616bdafd395..860e67cfcc9 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -19,6 +19,7 @@ #include "libpq/pqformat.h" #include "libpq/protocol.h" #include "tcop/pquery.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/memdebug.h" #include "utils/memutils.h" @@ -50,6 +51,7 @@ typedef struct bool typisvarlena; /* is it varlena (ie possibly toastable)? */ int16 format; /* format code for this column */ FmgrInfo finfo; /* Precomputed call info for output fn */ + FmgrInfo p_finfo; /* Precomputed call info for print fn if any */ } PrinttupAttrInfo; typedef struct @@ -244,6 +246,47 @@ SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo, pq_endmessage_reuse(buf); } +static Oid +get_type_printfn_tmp(Oid type) +{ + switch(type) + { + case OIDOID: + return F_OIDPRINT; + case TEXTOID: + return F_TEXTPRINT; + case FLOAT4OID: + return F_FLOAT4PRINT; + case FLOAT8OID: + return F_FLOAT8PRINT; + case INT2OID: + return F_INT2PRINT; + case INT4OID: + return F_INT4PRINT; + case INT8OID: + return F_INT8PRINT; + case TIMEOID: + return F_TIMEPRINT; + case TIMETZOID: + return F_TIMETZPRINT; + case TIMESTAMPOID: + return F_TIMESTAMPPRINT; + case TIMESTAMPTZOID: + return F_TIMESTAMPTZPRINT; + case INTERVALOID: + return F_INTERVAL_PRINT; + case NUMERICOID: + return F_NUMERIC_PRINT; + case BPCHAROID: + return F_BPCHARPRINT; + case VARCHAROID: + return F_VARCHARPRINT; + case CHAROID: + return F_CHARPRINT; + } + return InvalidOid; +} + /* * Get the lookup info that printtup() needs */ @@ -275,10 +318,18 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs) thisState->format = format; if (format == 0) { - getTypeOutputInfo(attr->atttypid, - &thisState->typoutput, - &thisState->typisvarlena); - fmgr_info(thisState->typoutput, &thisState->finfo); + Oid print_fn = get_type_printfn_tmp(attr->atttypid); + if (print_fn != InvalidOid) + fmgr_info(print_fn, &thisState->p_finfo); + else + { + getTypeOutputInfo(attr->atttypid, + &thisState->typoutput, + &thisState->typisvarlena); + fmgr_info(thisState->typoutput, &thisState->finfo); + /* mark print function is invalid */ + thisState->p_finfo.fn_oid = InvalidOid; + } } else if (format == 1) { @@ -356,10 +407,23 @@ printtup(TupleTableSlot *slot, DestReceiver *self) if (thisState->format == 0) { /* Text output */ - char *outputstr; - - outputstr = OutputFunctionCall(&thisState->finfo, attr); - pq_sendcountedtext(buf, outputstr, strlen(outputstr)); + if (thisState->p_finfo.fn_oid) + { + /* + * Use print function if it is defined. + * + * XXX: we can remove this if statement once we refactor all + * the out function. + */ + FunctionCall2(&thisState->p_finfo, attr, PointerGetDatum(buf)); + } + else + { + char *outputstr; + + outputstr = OutputFunctionCall(&thisState->finfo, attr); + pq_sendcountedtext(buf, outputstr, strlen(outputstr)); + } } else { diff --git a/src/backend/utils/adt/char.c b/src/backend/utils/adt/char.c index 698863924ee..6d1d9403c2d 100644 --- a/src/backend/utils/adt/char.c +++ b/src/backend/utils/adt/char.c @@ -83,6 +83,38 @@ charout(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(result); } +Datum +charprint(PG_FUNCTION_ARGS) +{ + char ch = PG_GETARG_CHAR(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + char *result; + uint32 data_len; + + result = outStringReserveLen(buf, 5); + + if (IS_HIGHBIT_SET(ch)) + { + result[0] = '\\'; + result[1] = TOOCTAL(((unsigned char) ch) >> 6); + result[2] = TOOCTAL((((unsigned char) ch) >> 3) & 07); + result[3] = TOOCTAL(((unsigned char) ch) & 07); + result[4] = '\0'; + data_len = 4; + } + else + { + /* This produces acceptable results for 0x00 as well */ + result[0] = ch; + result[1] = '\0'; + data_len = 1; + } + + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + /* * charrecv - converts external binary format to char * diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index c3327440380..4d606e7888f 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -196,6 +196,31 @@ date_out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(result); } +Datum +date_print(PG_FUNCTION_ARGS) +{ + DateADT date = PG_GETARG_DATEADT(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + char *data; + uint32 data_len; + + struct pg_tm tt, + *tm = &tt; + + data = outStringReserveLen(buf, MAXDATELEN + 1); + + if (DATE_NOT_FINITE(date)) + data_len = EncodeSpecialDate(date, data); + else + { + j2date(date + POSTGRES_EPOCH_JDATE, + &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); + data_len = EncodeDateOnly(tm, DateStyle, data); + } + outStringCompletePhase(buf, data_len); + PG_RETURN_VOID(); +} + /* * date_recv - converts external binary format to date */ @@ -291,13 +316,21 @@ make_date(PG_FUNCTION_ARGS) /* * Convert reserved date values to string. */ -void +int EncodeSpecialDate(DateADT dt, char *str) { if (DATE_IS_NOBEGIN(dt)) + { strcpy(str, EARLY); + /* the return value can be computed at compiling time. */ + return strlen(EARLY); + } else if (DATE_IS_NOEND(dt)) + { strcpy(str, LATE); + /* the return value can be computed at compiling time. */ + return strlen(LATE); + } else /* shouldn't happen */ elog(ERROR, "invalid argument for EncodeSpecialDate"); } @@ -1603,6 +1636,25 @@ time_out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(result); } +Datum +time_print(PG_FUNCTION_ARGS) +{ + TimeADT time = PG_GETARG_TIMEADT(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + char *data; + uint32 data_len; + + data = outStringReserveLen(buf, MAXDATELEN + 1); + time2tm(time, tm, &fsec); + data_len = EncodeTimeOnly(tm, fsec, false, 0, DateStyle, data); + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + /* * time_recv - converts external binary format to time */ @@ -2417,6 +2469,26 @@ timetz_out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(result); } +Datum +timetz_print(PG_FUNCTION_ARGS) +{ + TimeTzADT *time = PG_GETARG_TIMETZADT_P(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + char *data; + uint32 data_len; + int tz; + + data = outStringReserveLen(buf, MAXDATELEN + 1); + timetz2tm(time, tm, &fsec, &tz); + data_len = EncodeTimeOnly(tm, fsec, true, tz, DateStyle, data); + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + /* * timetz_recv - converts external binary format to timetz */ diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 83c3c85305b..85da5780870 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -4346,9 +4346,10 @@ EncodeTimezone(char *str, int tz, int style) /* EncodeDateOnly() * Encode date as local time. */ -void +int EncodeDateOnly(struct pg_tm *tm, int style, char *str) { + char *start = str; Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR); switch (style) @@ -4420,6 +4421,7 @@ EncodeDateOnly(struct pg_tm *tm, int style, char *str) str += 3; } *str = '\0'; + return str - start; } @@ -4430,10 +4432,13 @@ EncodeDateOnly(struct pg_tm *tm, int style, char *str) * a time zone (the difference between time and timetz types), tz is the * numeric time zone offset, style is the date style, str is where to write the * output. + * + * returns the strlen of the encoded format. */ -void +int EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, char *str) { + char *start = str; str = pg_ultostr_zeropad(str, tm->tm_hour, 2); *str++ = ':'; str = pg_ultostr_zeropad(str, tm->tm_min, 2); @@ -4442,6 +4447,7 @@ EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, if (print_tz) str = EncodeTimezone(str, tz, style); *str = '\0'; + return str - start; } @@ -4460,11 +4466,14 @@ EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, * ISO - yyyy-mm-dd hh:mm:ss+/-tz * German - dd.mm.yyyy hh:mm:ss tz * XSD - yyyy-mm-ddThh:mm:ss.ss+/-tz + * + * return the strlen of the encoded data. */ -void +int EncodeDateTime(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str) { int day; + char *start = str; Assert(tm->tm_mon >= 1 && tm->tm_mon <= MONTHS_PER_YEAR); @@ -4624,6 +4633,8 @@ EncodeDateTime(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, const char str += 3; } *str = '\0'; + + return str - start; } diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 13ceade129e..76ad00c60d9 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -373,6 +373,32 @@ float4out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(ascii); } + +Datum +float4print(PG_FUNCTION_ARGS) +{ + float4 num = PG_GETARG_FLOAT4(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + int data_len; + char *ascii; + int ndig = FLT_DIG + extra_float_digits; + + ascii = outStringReserveLen(buf, 32); + + if (extra_float_digits > 0) + data_len = float_to_shortest_decimal_buf(num, ascii); + else + data_len = pg_strfromd(ascii, 32, ndig, num); + if (data_len == -1) + { + /* XXX, think more of this. */ + elog(ERROR, "failed on float4print"); + } + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + /* * float4recv - converts external binary format to float4 */ @@ -563,10 +589,35 @@ Datum float8out(PG_FUNCTION_ARGS) { float8 num = PG_GETARG_FLOAT8(0); + int len; - PG_RETURN_CSTRING(float8out_internal(num)); + PG_RETURN_CSTRING(float8out_internal(num, NULL, &len)); } +Datum +float8print(PG_FUNCTION_ARGS) +{ + float8 num = PG_GETARG_FLOAT8(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + int data_len; + char *ascii; + + ascii = outStringReserveLen(buf, 32); + + float8out_internal(num, ascii, &data_len); + + if (data_len == -1) + { + /* XXX, think more of this. */ + elog(ERROR, "failed on float8print"); + } + + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + + /* * float8out_internal - guts of float8out() * diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index 4c894a49d5d..5a121a46b94 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -80,6 +80,22 @@ int2out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(result); } + +Datum +int2print(PG_FUNCTION_ARGS) +{ + int16 arg1 = PG_GETARG_INT16(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + char *data; + uint32 data_len; + + data = outStringReserveLen(buf, 7); + data_len = pg_itoa(arg1, data); + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + /* * int2recv - converts external binary format to int2 */ @@ -333,6 +349,22 @@ int4out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(result); } +Datum +int4print(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + char *data; + uint32 data_len; + + data = outStringReserveLen(buf, 12); + data_len = pg_ltoa(arg1, data); + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + + /* * int4recv - converts external binary format to int4 */ diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c index 19bb30f2d0f..8580c273792 100644 --- a/src/backend/utils/adt/int8.c +++ b/src/backend/utils/adt/int8.c @@ -76,6 +76,22 @@ int8out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(result); } +Datum +int8print(PG_FUNCTION_ARGS) +{ + int64 arg1 = PG_GETARG_INT64(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + char *data; + uint32 data_len; + + data = outStringReserveLen(buf, MAXINT8LEN + 1); + data_len = pg_lltoa(arg1, data); + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + + /* * int8recv - converts external binary format to int8 */ diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index cb23dfe9b95..84719a79ae8 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -509,7 +509,7 @@ static bool set_var_from_non_decimal_integer_str(const char *str, static void set_var_from_num(Numeric num, NumericVar *dest); static void init_var_from_num(Numeric num, NumericVar *dest); static void set_var_from_var(const NumericVar *value, NumericVar *dest); -static char *get_str_from_var(const NumericVar *var); +static char *get_str_from_var(const NumericVar *var, StringInfo buf); static char *get_str_from_var_sci(const NumericVar *var, int rscale); static void numericvar_serialize(StringInfo buf, const NumericVar *var); @@ -820,11 +820,52 @@ numeric_out(PG_FUNCTION_ARGS) */ init_var_from_num(num, &x); - str = get_str_from_var(&x); + str = get_str_from_var(&x, NULL); PG_RETURN_CSTRING(str); } +Datum +numeric_print(PG_FUNCTION_ARGS) +{ + Numeric num = PG_GETARG_NUMERIC(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + + NumericVar x; + + /* + * Handle NaN and infinities + */ + if (NUMERIC_IS_SPECIAL(num)) + { + const char* special_str; + char *data; + uint32 data_len; + + if (NUMERIC_IS_PINF(num)) + special_str = "Infinity"; + else if (NUMERIC_IS_NINF(num)) + special_str = "-Infinity"; + else + special_str = "NaN"; + + data_len = strlen(special_str) + 1; + data = outStringReserveLen(buf, data_len); + memcpy(data, special_str, data_len); + outStringCompletePhase(buf, data_len); + PG_RETURN_VOID(); + } + + /* + * Get the number in the variable format. + */ + init_var_from_num(num, &x); + + (void) get_str_from_var(&x, buf); + + PG_RETURN_VOID(); +} + /* * numeric_is_nan() - * @@ -1027,7 +1068,7 @@ numeric_normalize(Numeric num) init_var_from_num(num, &x); - str = get_str_from_var(&x); + str = get_str_from_var(&x, NULL); /* If there's no decimal point, there's certainly nothing to remove. */ if (strchr(str, '.') != NULL) @@ -7251,7 +7292,7 @@ set_var_from_var(const NumericVar *value, NumericVar *dest) * Returns a palloc'd string. */ static char * -get_str_from_var(const NumericVar *var) +get_str_from_var(const NumericVar *var, StringInfo buf) { int dscale; char *str; @@ -7279,7 +7320,14 @@ get_str_from_var(const NumericVar *var) if (i <= 0) i = 1; - str = palloc(i + dscale + DEC_DIGITS + 2); + if (buf == NULL) + { + str = palloc(i + dscale + DEC_DIGITS + 2); + } + else + { + str = outStringReserveLen(buf, i + dscale + DEC_DIGITS + 2); + } cp = str; /* @@ -7378,6 +7426,12 @@ get_str_from_var(const NumericVar *var) * terminate the string and return it */ *cp = '\0'; + + if (buf != NULL) + { + uint32 data_len = cp - str; + outStringCompletePhase(buf, data_len); + } return str; } @@ -7451,7 +7505,7 @@ get_str_from_var_sci(const NumericVar *var, int rscale) power_ten_int(exponent, &tmp_var); div_var(var, &tmp_var, &tmp_var, rscale, true, true); - sig_out = get_str_from_var(&tmp_var); + sig_out = get_str_from_var(&tmp_var, NULL); free_var(&tmp_var); @@ -8004,7 +8058,7 @@ numericvar_to_double_no_overflow(const NumericVar *var) double val; char *endptr; - tmp = get_str_from_var(var); + tmp = get_str_from_var(var, NULL); /* unlike float8in, we ignore ERANGE from strtod */ val = strtod(tmp, &endptr); diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c index a3419728971..96df114eaf6 100644 --- a/src/backend/utils/adt/oid.c +++ b/src/backend/utils/adt/oid.c @@ -53,6 +53,22 @@ oidout(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(result); } +Datum +oidprint(PG_FUNCTION_ARGS) +{ + Oid o = PG_GETARG_OID(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + uint32 data_len; + char *data; + + /* 12 is the max length for an oid's text presentation. */ + data = outStringReserveLen(buf, 12); + data_len = pg_snprintf(data, 12, "%u", o); + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + /* * oidrecv - converts external binary format to oid */ diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 288d696be77..27b546deb74 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -87,7 +87,7 @@ static bool AdjustIntervalForTypmod(Interval *interval, int32 typmod, static TimestampTz timestamp2timestamptz(Timestamp timestamp); static Timestamp timestamptz2timestamp(TimestampTz timestamp); -static void EncodeSpecialInterval(const Interval *interval, char *str); +static int EncodeSpecialInterval(const Interval *interval, char *str); static void interval_um_internal(const Interval *interval, Interval *result); /* common code for timestamptypmodin and timestamptztypmodin */ @@ -244,6 +244,33 @@ timestamp_out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(result); } +Datum +timestamp_print(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + char *data; + uint32 data_len; + + data = outStringReserveLen(buf, MAXDATELEN + 1); + + if (TIMESTAMP_NOT_FINITE(timestamp)) + data_len = EncodeSpecialTimestamp(timestamp, data); + else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) + data_len = EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, data); + else + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + /* * timestamp_recv - converts external binary format to timestamp */ @@ -789,6 +816,36 @@ timestamptz_out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(result); } +Datum +timestamptz_print(PG_FUNCTION_ARGS) +{ + TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + int tz; + const char *tzn; + struct pg_tm tt, + *tm = &tt; + fsec_t fsec; + char *data; + uint32 data_len; + + data = outStringReserveLen(buf, MAXDATELEN + 1); + + if (TIMESTAMP_NOT_FINITE(timestamp)) + data_len = EncodeSpecialTimestamp(timestamp, data); + else if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) == 0) + data_len = EncodeDateTime(tm, fsec, true, tz, tzn, DateStyle, data); + else + ereport(ERROR, + (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), + errmsg("timestamp out of range"))); + + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + + /* * timestamptz_recv - converts external binary format to timestamptz */ @@ -983,6 +1040,35 @@ interval_out(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(result); } +Datum +interval_print(PG_FUNCTION_ARGS) +{ + Interval *span = PG_GETARG_INTERVAL_P(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + struct pg_itm tt, + *itm = &tt; + char *data; + uint32 data_len; + + data = outStringReserveLen(buf, MAXDATELEN + 1); + + if (INTERVAL_NOT_FINITE(span)) + data_len = EncodeSpecialInterval(span, data); + else + { + interval2itm(*span, itm); + EncodeInterval(itm, IntervalStyle, data); + /* + * XXX: making EncodeInterval returns a string len is error-prone for me. + * so call strlen directly on the result. + */ + data_len = strlen(data); + } + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + /* * interval_recv - converts external binary format to interval */ @@ -1577,26 +1663,40 @@ out_of_range: /* EncodeSpecialTimestamp() * Convert reserved timestamp data type to string. */ -void +int EncodeSpecialTimestamp(Timestamp dt, char *str) { if (TIMESTAMP_IS_NOBEGIN(dt)) + { strcpy(str, EARLY); + return strlen(EARLY); + } else if (TIMESTAMP_IS_NOEND(dt)) + { strcpy(str, LATE); + return strlen(LATE); + } else /* shouldn't happen */ elog(ERROR, "invalid argument for EncodeSpecialTimestamp"); } -static void +static int EncodeSpecialInterval(const Interval *interval, char *str) { if (INTERVAL_IS_NOBEGIN(interval)) + { strcpy(str, EARLY); + return strlen(EARLY); + } else if (INTERVAL_IS_NOEND(interval)) + { strcpy(str, LATE); + return strlen(LATE); + } else /* shouldn't happen */ elog(ERROR, "invalid argument for EncodeSpecialInterval"); + + return 0; } Datum diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c index a62e55eec19..e14c66999a8 100644 --- a/src/backend/utils/adt/varchar.c +++ b/src/backend/utils/adt/varchar.c @@ -223,6 +223,25 @@ bpcharout(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(TextDatumGetCString(txt)); } +Datum +bpcharprint(PG_FUNCTION_ARGS) +{ + Datum txt = PG_GETARG_DATUM(0); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + + /* XXX: improve here since we can put the cstring into buf directly. */ + char *data = TextDatumGetCString(txt); + uint32 data_len = strlen(data); + char *target; + + target = outStringReserveLen(buf, data_len); + memcpy(target, data, data_len); + outStringCompletePhase(buf, data_len); + + PG_RETURN_VOID(); +} + + /* * bpcharrecv - converts external binary format to bpchar */ @@ -520,6 +539,12 @@ varcharout(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(TextDatumGetCString(txt)); } +Datum +varcharprint(PG_FUNCTION_ARGS) +{ + return bpcharprint(fcinfo); +} + /* * varcharrecv - converts external binary format to varchar */ diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index c0ff51bd2fc..3fa6bff1182 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -293,6 +293,22 @@ textout(PG_FUNCTION_ARGS) PG_RETURN_CSTRING(TextDatumGetCString(txt)); } + +Datum +textprint(PG_FUNCTION_ARGS) +{ + text *txt = (text *) pg_detoast_datum((struct varlena *)PG_GETARG_POINTER(0)); + StringInfo buf = (StringInfo) PG_GETARG_POINTER(1); + uint32 text_len = VARSIZE(txt) - VARHDRSZ; + char *data; + + data = outStringReserveLen(buf, text_len); + memcpy(data, VARDATA(txt), text_len); + outStringCompletePhase(buf, text_len); + + PG_RETURN_VOID(); +} + /* * textrecv - converts external binary format to text */ diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index fa9ae79082b..938f5e2d585 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -4877,7 +4877,6 @@ { oid => '1799', descr => 'I/O', proname => 'oidout', prorettype => 'cstring', proargtypes => 'oid', prosrc => 'oidout' }, - { oid => '3058', descr => 'concatenate values', proname => 'concat', provariadic => 'any', proisstrict => 'f', provolatile => 's', prorettype => 'text', proargtypes => 'any', @@ -12769,4 +12768,86 @@ proname => 'hashoid8extended', prorettype => 'int8', proargtypes => 'oid8 int8', prosrc => 'hashoid8extended' }, +{ + oid => '9771', descr => 'I/O', + proname => 'oidprint', prorettype => 'void', proargtypes => 'oid internal', + prosrc => 'oidprint'}, +{ + oid => '8907', descr => 'I/O', + proname => 'textprint', prorettype => 'void', proargtypes => 'text internal', + prosrc => 'textprint' }, + +{ + oid => '9234', descr => 'I/O', + proname => 'float4print', prorettype => 'void', proargtypes => 'float4 internal', + prosrc => 'float4print' }, +{ + oid => '6313', descr => 'I/O', + proname => 'float8print', prorettype => 'void', proargtypes => 'float8 internal', + prosrc => 'float8print' }, + +{ + oid => '4099', descr => 'I/O', + proname => 'int2print', prorettype => 'void', proargtypes => 'int2 internal', + prosrc => 'int2print' }, +{ + oid => '4100', descr => 'I/O', + proname => 'int4print', prorettype => 'void', proargtypes => 'int4 internal', + prosrc => 'int4print' }, +{ + oid => '4551', descr => 'I/O', + proname => 'int8print', prorettype => 'void', proargtypes => 'int8 internal', + prosrc => 'int8print' }, + +{ + oid => '4552', descr => 'I/O', + proname => 'timeprint', prorettype => 'void', proargtypes => 'time internal', + prosrc => 'time_print' }, + +{ + oid => '4553', descr => 'I/O', + proname => 'timetzprint', prorettype => 'void', proargtypes => 'timetz internal', + prosrc => 'timetz_print' }, + +{ + oid => '4554', descr => 'I/O', + proname => 'dateprint', prorettype => 'void', proargtypes => 'date internal', + prosrc => 'date_print'}, + + +{ + oid => '4555', descr => 'I/O', + proname => 'timestampprint', prorettype => 'void', proargtypes => 'timestamp internal', + prosrc => 'timestamp_print'}, + +{ + oid => '4556', descr => 'I/O', + proname => 'timestamptzprint', prorettype => 'void', proargtypes => 'timestamptz internal', + prosrc => 'timestamptz_print'}, + +{ + oid => '4557', descr => 'I/O', + proname => 'interval_print', prorettype => 'void', proargtypes => 'interval internal', + prosrc => 'interval_print'}, + +{ + oid => '4558', descr => 'I/O', + proname => 'numeric_print', prorettype => 'void', proargtypes => 'numeric internal', + prosrc => 'numeric_print'}, + +{ + oid => '4559', descr => 'I/O', + proname => 'charprint', prorettype => 'void', proargtypes => 'char internal', + prosrc => 'charprint'}, + +{ + oid => '4560', descr => 'I/O', + proname => 'bpcharprint', prorettype => 'void', proargtypes => 'bpchar internal', + prosrc => 'bpcharprint'}, + +{ + oid => '4561', descr => 'I/O', + proname => 'varcharprint', prorettype => 'void', proargtypes => 'varchar internal', + prosrc => 'varcharprint'}, + ] diff --git a/src/include/lib/stringinfo.h b/src/include/lib/stringinfo.h index 079652c8ce4..6318fe8be5b 100644 --- a/src/include/lib/stringinfo.h +++ b/src/include/lib/stringinfo.h @@ -267,4 +267,23 @@ extern void enlargeStringInfo(StringInfo str, int needed); */ extern void destroyStringInfo(StringInfo str); +/* + * outString - The StringInfo used in type specific out function. + */ +static inline char * +outStringReserveLen(StringInfo buf, uint32 data_len) +{ + /* sizeof(uint32) is for storing the data_len itself. */ + enlargeStringInfo(buf, sizeof(uint32) + data_len); + return buf->data + buf->len + sizeof(uint32); +} + +/* define outStringCompletePhase as macro to avoid including pg_bswap.h */ +#define outStringCompletePhase(buf, data_len) \ +{ \ + *(uint32 *)(buf->data + buf->len) = pg_hton32(data_len); \ + buf->len += sizeof(uint32) + data_len; \ +} + + #endif /* STRINGINFO_H */ diff --git a/src/include/utils/date.h b/src/include/utils/date.h index 6063810891e..3a2fe34061e 100644 --- a/src/include/utils/date.h +++ b/src/include/utils/date.h @@ -111,7 +111,7 @@ extern DateADT timestamptz2date_safe(TimestampTz timestamp, Node *escontext); extern int32 date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2); extern int32 date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2); -extern void EncodeSpecialDate(DateADT dt, char *str); +extern int EncodeSpecialDate(DateADT dt, char *str); extern DateADT GetSQLCurrentDate(void); extern TimeTzADT *GetSQLCurrentTime(int32 typmod); extern TimeADT GetSQLLocalTime(int32 typmod); diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h index f77c6acd8b6..a6754195e0b 100644 --- a/src/include/utils/datetime.h +++ b/src/include/utils/datetime.h @@ -330,11 +330,11 @@ extern int DetermineTimeZoneAbbrevOffset(struct pg_tm *tm, const char *abbr, pg_ extern int DetermineTimeZoneAbbrevOffsetTS(TimestampTz ts, const char *abbr, pg_tz *tzp, int *isdst); -extern void EncodeDateOnly(struct pg_tm *tm, int style, char *str); -extern void EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, char *str); -extern void EncodeDateTime(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str); +extern int EncodeDateOnly(struct pg_tm *tm, int style, char *str); +extern int EncodeTimeOnly(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, int style, char *str); +extern int EncodeDateTime(struct pg_tm *tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str); extern void EncodeInterval(struct pg_itm *itm, int style, char *str); -extern void EncodeSpecialTimestamp(Timestamp dt, char *str); +extern int EncodeSpecialTimestamp(Timestamp dt, char *str); extern int ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc, struct pg_tm *tm); -- 2.43.0
