mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Improve speed of timestamp/time/date output functions.
It seems that sprintf(), at least in glibc's version, is unreasonably slow compared to hand-rolled code for printing integers. Replacing most uses of sprintf() in the datetime.c output functions with special-purpose code turns out to give more than a 2X speedup in COPY of a table with a single timestamp column; which is pretty impressive considering all the other logic in that code path. David Rowley and Andres Freund, reviewed by Peter Geoghegan and myself
This commit is contained in:
		@@ -43,8 +43,12 @@ static int DecodeTime(char *str, int fmask, int range,
 | 
				
			|||||||
static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);
 | 
					static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);
 | 
				
			||||||
static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
 | 
					static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
 | 
				
			||||||
		   struct pg_tm * tm);
 | 
							   struct pg_tm * tm);
 | 
				
			||||||
static void TrimTrailingZeros(char *str);
 | 
					
 | 
				
			||||||
static void AppendSeconds(char *cp, int sec, fsec_t fsec,
 | 
					#ifndef HAVE_INT64_TIMESTAMP
 | 
				
			||||||
 | 
					static char *TrimTrailingZeros(char *str);
 | 
				
			||||||
 | 
					#endif   /* HAVE_INT64_TIMESTAMP */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static char *AppendSeconds(char *cp, int sec, fsec_t fsec,
 | 
				
			||||||
			  int precision, bool fillzeros);
 | 
								  int precision, bool fillzeros);
 | 
				
			||||||
static void AdjustFractSeconds(double frac, struct pg_tm * tm, fsec_t *fsec,
 | 
					static void AdjustFractSeconds(double frac, struct pg_tm * tm, fsec_t *fsec,
 | 
				
			||||||
				   int scale);
 | 
									   int scale);
 | 
				
			||||||
@@ -398,57 +402,121 @@ GetCurrentTimeUsec(struct pg_tm * tm, fsec_t *fsec, int *tzp)
 | 
				
			|||||||
/* TrimTrailingZeros()
 | 
					/* TrimTrailingZeros()
 | 
				
			||||||
 * ... resulting from printing numbers with full precision.
 | 
					 * ... resulting from printing numbers with full precision.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns a pointer to the new end of string.  No NUL terminator is put
 | 
				
			||||||
 | 
					 * there; callers are responsible for NUL terminating str themselves.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * Before Postgres 8.4, this always left at least 2 fractional digits,
 | 
					 * Before Postgres 8.4, this always left at least 2 fractional digits,
 | 
				
			||||||
 * but conversations on the lists suggest this isn't desired
 | 
					 * but conversations on the lists suggest this isn't desired
 | 
				
			||||||
 * since showing '0.10' is misleading with values of precision(1).
 | 
					 * since showing '0.10' is misleading with values of precision(1).
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void
 | 
					#ifndef HAVE_INT64_TIMESTAMP
 | 
				
			||||||
 | 
					static char *
 | 
				
			||||||
TrimTrailingZeros(char *str)
 | 
					TrimTrailingZeros(char *str)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int			len = strlen(str);
 | 
						int			len = strlen(str);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (len > 1 && *(str + len - 1) == '0' && *(str + len - 2) != '.')
 | 
						while (len > 1 && *(str + len - 1) == '0' && *(str + len - 2) != '.')
 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		len--;
 | 
							len--;
 | 
				
			||||||
		*(str + len) = '\0';
 | 
						return str + len;
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					#endif   /* HAVE_INT64_TIMESTAMP */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Append sections and fractional seconds (if any) at *cp.
 | 
					 * Append seconds and fractional seconds (if any) at *cp.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * precision is the max number of fraction digits, fillzeros says to
 | 
					 * precision is the max number of fraction digits, fillzeros says to
 | 
				
			||||||
 * pad to two integral-seconds digits.
 | 
					 * pad to two integral-seconds digits.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns a pointer to the new end of string.  No NUL terminator is put
 | 
				
			||||||
 | 
					 * there; callers are responsible for NUL terminating str themselves.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * Note that any sign is stripped from the input seconds values.
 | 
					 * Note that any sign is stripped from the input seconds values.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void
 | 
					static char *
 | 
				
			||||||
AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
 | 
					AppendSeconds(char *cp, int sec, fsec_t fsec, int precision, bool fillzeros)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						Assert(precision >= 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HAVE_INT64_TIMESTAMP
 | 
				
			||||||
 | 
						/* fsec_t is just an int32 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fillzeros)
 | 
				
			||||||
 | 
							cp = pg_ltostr_zeropad(cp, Abs(sec), 2);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							cp = pg_ltostr(cp, Abs(sec));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fsec != 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int32		value = Abs(fsec);
 | 
				
			||||||
 | 
							char	   *end = &cp[precision + 1];
 | 
				
			||||||
 | 
							bool		gotnonzero = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							*cp++ = '.';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Append the fractional seconds part.  Note that we don't want any
 | 
				
			||||||
 | 
							 * trailing zeros here, so since we're building the number in reverse
 | 
				
			||||||
 | 
							 * we'll skip appending zeros until we've output a non-zero digit.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							while (precision--)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								int32		oldval = value;
 | 
				
			||||||
 | 
								int32		remainder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								value /= 10;
 | 
				
			||||||
 | 
								remainder = oldval - value * 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* check if we got a non-zero */
 | 
				
			||||||
 | 
								if (remainder)
 | 
				
			||||||
 | 
									gotnonzero = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (gotnonzero)
 | 
				
			||||||
 | 
									cp[precision] = '0' + remainder;
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									end = &cp[precision];
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * If we still have a non-zero value then precision must have not been
 | 
				
			||||||
 | 
							 * enough to print the number.  We punt the problem to pg_ltostr(),
 | 
				
			||||||
 | 
							 * which will generate a correct answer in the minimum valid width.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (value)
 | 
				
			||||||
 | 
								return pg_ltostr(cp, Abs(fsec));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return end;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return cp;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						/* fsec_t is a double */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (fsec == 0)
 | 
						if (fsec == 0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (fillzeros)
 | 
							if (fillzeros)
 | 
				
			||||||
			sprintf(cp, "%02d", abs(sec));
 | 
								return pg_ltostr_zeropad(cp, Abs(sec), 2);
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			sprintf(cp, "%d", abs(sec));
 | 
								return pg_ltostr(cp, Abs(sec));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
#ifdef HAVE_INT64_TIMESTAMP
 | 
					 | 
				
			||||||
		if (fillzeros)
 | 
					 | 
				
			||||||
			sprintf(cp, "%02d.%0*d", abs(sec), precision, (int) Abs(fsec));
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			sprintf(cp, "%d.%0*d", abs(sec), precision, (int) Abs(fsec));
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
		if (fillzeros)
 | 
							if (fillzeros)
 | 
				
			||||||
			sprintf(cp, "%0*.*f", precision + 3, precision, fabs(sec + fsec));
 | 
								sprintf(cp, "%0*.*f", precision + 3, precision, fabs(sec + fsec));
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			sprintf(cp, "%.*f", precision, fabs(sec + fsec));
 | 
								sprintf(cp, "%.*f", precision, fabs(sec + fsec));
 | 
				
			||||||
#endif
 | 
							return TrimTrailingZeros(cp);
 | 
				
			||||||
		TrimTrailingZeros(cp);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					#endif   /* HAVE_INT64_TIMESTAMP */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Variant of above that's specialized to timestamp case */
 | 
					
 | 
				
			||||||
static void
 | 
					/*
 | 
				
			||||||
 | 
					 * Variant of above that's specialized to timestamp case.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns a pointer to the new end of string.  No NUL terminator is put
 | 
				
			||||||
 | 
					 * there; callers are responsible for NUL terminating str themselves.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static char *
 | 
				
			||||||
AppendTimestampSeconds(char *cp, struct pg_tm * tm, fsec_t fsec)
 | 
					AppendTimestampSeconds(char *cp, struct pg_tm * tm, fsec_t fsec)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
@@ -459,7 +527,7 @@ AppendTimestampSeconds(char *cp, struct pg_tm * tm, fsec_t fsec)
 | 
				
			|||||||
	if (tm->tm_year <= 0)
 | 
						if (tm->tm_year <= 0)
 | 
				
			||||||
		fsec = 0;
 | 
							fsec = 0;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	AppendSeconds(cp, tm->tm_sec, fsec, MAX_TIMESTAMP_PRECISION, true);
 | 
						return AppendSeconds(cp, tm->tm_sec, fsec, MAX_TIMESTAMP_PRECISION, true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
@@ -3831,9 +3899,12 @@ datebsearch(const char *key, const datetkn *base, int nel)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* EncodeTimezone()
 | 
					/* EncodeTimezone()
 | 
				
			||||||
 *		Append representation of a numeric timezone offset to str.
 | 
					 *		Copies representation of a numeric timezone offset to str.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns a pointer to the new end of string.  No NUL terminator is put
 | 
				
			||||||
 | 
					 * there; callers are responsible for NUL terminating str themselves.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void
 | 
					static char *
 | 
				
			||||||
EncodeTimezone(char *str, int tz, int style)
 | 
					EncodeTimezone(char *str, int tz, int style)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int			hour,
 | 
						int			hour,
 | 
				
			||||||
@@ -3846,16 +3917,26 @@ EncodeTimezone(char *str, int tz, int style)
 | 
				
			|||||||
	hour = min / MINS_PER_HOUR;
 | 
						hour = min / MINS_PER_HOUR;
 | 
				
			||||||
	min -= hour * MINS_PER_HOUR;
 | 
						min -= hour * MINS_PER_HOUR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	str += strlen(str);
 | 
					 | 
				
			||||||
	/* TZ is negated compared to sign we wish to display ... */
 | 
						/* TZ is negated compared to sign we wish to display ... */
 | 
				
			||||||
	*str++ = (tz <= 0 ? '+' : '-');
 | 
						*str++ = (tz <= 0 ? '+' : '-');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (sec != 0)
 | 
						if (sec != 0)
 | 
				
			||||||
		sprintf(str, "%02d:%02d:%02d", hour, min, sec);
 | 
						{
 | 
				
			||||||
 | 
							str = pg_ltostr_zeropad(str, hour, 2);
 | 
				
			||||||
 | 
							*str++ = ':';
 | 
				
			||||||
 | 
							str = pg_ltostr_zeropad(str, min, 2);
 | 
				
			||||||
 | 
							*str++ = ':';
 | 
				
			||||||
 | 
							str = pg_ltostr_zeropad(str, sec, 2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	else if (min != 0 || style == USE_XSD_DATES)
 | 
						else if (min != 0 || style == USE_XSD_DATES)
 | 
				
			||||||
		sprintf(str, "%02d:%02d", hour, min);
 | 
						{
 | 
				
			||||||
 | 
							str = pg_ltostr_zeropad(str, hour, 2);
 | 
				
			||||||
 | 
							*str++ = ':';
 | 
				
			||||||
 | 
							str = pg_ltostr_zeropad(str, min, 2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		sprintf(str, "%02d", hour);
 | 
							str = pg_ltostr_zeropad(str, hour, 2);
 | 
				
			||||||
 | 
						return str;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* EncodeDateOnly()
 | 
					/* EncodeDateOnly()
 | 
				
			||||||
@@ -3871,48 +3952,70 @@ EncodeDateOnly(struct pg_tm * tm, int style, char *str)
 | 
				
			|||||||
		case USE_ISO_DATES:
 | 
							case USE_ISO_DATES:
 | 
				
			||||||
		case USE_XSD_DATES:
 | 
							case USE_XSD_DATES:
 | 
				
			||||||
			/* compatible with ISO date formats */
 | 
								/* compatible with ISO date formats */
 | 
				
			||||||
			if (tm->tm_year > 0)
 | 
								str = pg_ltostr_zeropad(str,
 | 
				
			||||||
				sprintf(str, "%04d-%02d-%02d",
 | 
										(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
 | 
				
			||||||
						tm->tm_year, tm->tm_mon, tm->tm_mday);
 | 
								*str++ = '-';
 | 
				
			||||||
			else
 | 
								str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
 | 
				
			||||||
				sprintf(str, "%04d-%02d-%02d %s",
 | 
								*str++ = '-';
 | 
				
			||||||
						-(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
 | 
								str = pg_ltostr_zeropad(str, tm->tm_mday, 2);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case USE_SQL_DATES:
 | 
							case USE_SQL_DATES:
 | 
				
			||||||
			/* compatible with Oracle/Ingres date formats */
 | 
								/* compatible with Oracle/Ingres date formats */
 | 
				
			||||||
			if (DateOrder == DATEORDER_DMY)
 | 
								if (DateOrder == DATEORDER_DMY)
 | 
				
			||||||
				sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
 | 
								{
 | 
				
			||||||
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mday, 2);
 | 
				
			||||||
 | 
									*str++ = '/';
 | 
				
			||||||
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
				sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
 | 
								{
 | 
				
			||||||
			if (tm->tm_year > 0)
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
 | 
				
			||||||
				sprintf(str + 5, "/%04d", tm->tm_year);
 | 
									*str++ = '/';
 | 
				
			||||||
			else
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mday, 2);
 | 
				
			||||||
				sprintf(str + 5, "/%04d %s", -(tm->tm_year - 1), "BC");
 | 
								}
 | 
				
			||||||
 | 
								*str++ = '/';
 | 
				
			||||||
 | 
								str = pg_ltostr_zeropad(str,
 | 
				
			||||||
 | 
										(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case USE_GERMAN_DATES:
 | 
							case USE_GERMAN_DATES:
 | 
				
			||||||
			/* German-style date format */
 | 
								/* German-style date format */
 | 
				
			||||||
			sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
 | 
								str = pg_ltostr_zeropad(str, tm->tm_mday, 2);
 | 
				
			||||||
			if (tm->tm_year > 0)
 | 
								*str++ = '.';
 | 
				
			||||||
				sprintf(str + 5, ".%04d", tm->tm_year);
 | 
								str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
 | 
				
			||||||
			else
 | 
								*str++ = '.';
 | 
				
			||||||
				sprintf(str + 5, ".%04d %s", -(tm->tm_year - 1), "BC");
 | 
								str = pg_ltostr_zeropad(str,
 | 
				
			||||||
 | 
										(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case USE_POSTGRES_DATES:
 | 
							case USE_POSTGRES_DATES:
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			/* traditional date-only style for Postgres */
 | 
								/* traditional date-only style for Postgres */
 | 
				
			||||||
			if (DateOrder == DATEORDER_DMY)
 | 
								if (DateOrder == DATEORDER_DMY)
 | 
				
			||||||
				sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
 | 
								{
 | 
				
			||||||
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mday, 2);
 | 
				
			||||||
 | 
									*str++ = '-';
 | 
				
			||||||
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
				sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
 | 
								{
 | 
				
			||||||
			if (tm->tm_year > 0)
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
 | 
				
			||||||
				sprintf(str + 5, "-%04d", tm->tm_year);
 | 
									*str++ = '-';
 | 
				
			||||||
			else
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mday, 2);
 | 
				
			||||||
				sprintf(str + 5, "-%04d %s", -(tm->tm_year - 1), "BC");
 | 
								}
 | 
				
			||||||
 | 
								*str++ = '-';
 | 
				
			||||||
 | 
								str = pg_ltostr_zeropad(str,
 | 
				
			||||||
 | 
										(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (tm->tm_year <= 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							memcpy(str, " BC", 3);	/* Don't copy NUL */
 | 
				
			||||||
 | 
							str += 3;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*str = '\0';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -3927,13 +4030,14 @@ EncodeDateOnly(struct pg_tm * tm, int style, char *str)
 | 
				
			|||||||
void
 | 
					void
 | 
				
			||||||
EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, int style, char *str)
 | 
					EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, int style, char *str)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	sprintf(str, "%02d:%02d:", tm->tm_hour, tm->tm_min);
 | 
						str = pg_ltostr_zeropad(str, tm->tm_hour, 2);
 | 
				
			||||||
	str += strlen(str);
 | 
						*str++ = ':';
 | 
				
			||||||
 | 
						str = pg_ltostr_zeropad(str, tm->tm_min, 2);
 | 
				
			||||||
	AppendSeconds(str, tm->tm_sec, fsec, MAX_TIME_PRECISION, true);
 | 
						*str++ = ':';
 | 
				
			||||||
 | 
						str = AppendSeconds(str, tm->tm_sec, fsec, MAX_TIME_PRECISION, true);
 | 
				
			||||||
	if (print_tz)
 | 
						if (print_tz)
 | 
				
			||||||
		EncodeTimezone(str, tz, style);
 | 
							str = EncodeTimezone(str, tz, style);
 | 
				
			||||||
 | 
						*str = '\0';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -3971,106 +4075,129 @@ EncodeDateTime(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, const char
 | 
				
			|||||||
		case USE_ISO_DATES:
 | 
							case USE_ISO_DATES:
 | 
				
			||||||
		case USE_XSD_DATES:
 | 
							case USE_XSD_DATES:
 | 
				
			||||||
			/* Compatible with ISO-8601 date formats */
 | 
								/* Compatible with ISO-8601 date formats */
 | 
				
			||||||
 | 
								str = pg_ltostr_zeropad(str,
 | 
				
			||||||
			if (style == USE_ISO_DATES)
 | 
										(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
 | 
				
			||||||
				sprintf(str, "%04d-%02d-%02d %02d:%02d:",
 | 
								*str++ = '-';
 | 
				
			||||||
						(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
 | 
								str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
 | 
				
			||||||
						tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
 | 
								*str++ = '-';
 | 
				
			||||||
			else
 | 
								str = pg_ltostr_zeropad(str, tm->tm_mday, 2);
 | 
				
			||||||
				sprintf(str, "%04d-%02d-%02dT%02d:%02d:",
 | 
								*str++ = (style == USE_ISO_DATES) ? ' ' : 'T';
 | 
				
			||||||
						(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
 | 
								str = pg_ltostr_zeropad(str, tm->tm_hour, 2);
 | 
				
			||||||
						tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
 | 
								*str++ = ':';
 | 
				
			||||||
 | 
								str = pg_ltostr_zeropad(str, tm->tm_min, 2);
 | 
				
			||||||
			AppendTimestampSeconds(str + strlen(str), tm, fsec);
 | 
								*str++ = ':';
 | 
				
			||||||
 | 
								str = AppendTimestampSeconds(str, tm, fsec);
 | 
				
			||||||
			if (print_tz)
 | 
								if (print_tz)
 | 
				
			||||||
				EncodeTimezone(str, tz, style);
 | 
									str = EncodeTimezone(str, tz, style);
 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (tm->tm_year <= 0)
 | 
					 | 
				
			||||||
				sprintf(str + strlen(str), " BC");
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case USE_SQL_DATES:
 | 
							case USE_SQL_DATES:
 | 
				
			||||||
			/* Compatible with Oracle/Ingres date formats */
 | 
								/* Compatible with Oracle/Ingres date formats */
 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (DateOrder == DATEORDER_DMY)
 | 
								if (DateOrder == DATEORDER_DMY)
 | 
				
			||||||
				sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
 | 
								{
 | 
				
			||||||
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mday, 2);
 | 
				
			||||||
 | 
									*str++ = '/';
 | 
				
			||||||
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
				sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
 | 
								{
 | 
				
			||||||
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
 | 
				
			||||||
			sprintf(str + 5, "/%04d %02d:%02d:",
 | 
									*str++ = '/';
 | 
				
			||||||
					(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mday, 2);
 | 
				
			||||||
					tm->tm_hour, tm->tm_min);
 | 
								}
 | 
				
			||||||
 | 
								*str++ = '/';
 | 
				
			||||||
			AppendTimestampSeconds(str + strlen(str), tm, fsec);
 | 
								str = pg_ltostr_zeropad(str,
 | 
				
			||||||
 | 
										(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
 | 
				
			||||||
 | 
								*str++ = ' ';
 | 
				
			||||||
 | 
								str = pg_ltostr_zeropad(str, tm->tm_hour, 2);
 | 
				
			||||||
 | 
								*str++ = ':';
 | 
				
			||||||
 | 
								str = pg_ltostr_zeropad(str, tm->tm_min, 2);
 | 
				
			||||||
 | 
								*str++ = ':';
 | 
				
			||||||
 | 
								str = AppendTimestampSeconds(str, tm, fsec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/*
 | 
								/*
 | 
				
			||||||
			 * Note: the uses of %.*s in this function would be risky if the
 | 
								 * Note: the uses of %.*s in this function would be risky if the
 | 
				
			||||||
			 * timezone names ever contain non-ASCII characters.  However, all
 | 
								 * timezone names ever contain non-ASCII characters.  However, all
 | 
				
			||||||
			 * TZ abbreviations in the Olson database are plain ASCII.
 | 
								 * TZ abbreviations in the IANA database are plain ASCII.
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (print_tz)
 | 
								if (print_tz)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				if (tzn)
 | 
									if (tzn)
 | 
				
			||||||
					sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
 | 
									{
 | 
				
			||||||
 | 
										sprintf(str, " %.*s", MAXTZLEN, tzn);
 | 
				
			||||||
 | 
										str += strlen(str);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				else
 | 
									else
 | 
				
			||||||
					EncodeTimezone(str, tz, style);
 | 
										str = EncodeTimezone(str, tz, style);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (tm->tm_year <= 0)
 | 
					 | 
				
			||||||
				sprintf(str + strlen(str), " BC");
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case USE_GERMAN_DATES:
 | 
							case USE_GERMAN_DATES:
 | 
				
			||||||
			/* German variant on European style */
 | 
								/* German variant on European style */
 | 
				
			||||||
 | 
								str = pg_ltostr_zeropad(str, tm->tm_mday, 2);
 | 
				
			||||||
			sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
 | 
								*str++ = '.';
 | 
				
			||||||
 | 
								str = pg_ltostr_zeropad(str, tm->tm_mon, 2);
 | 
				
			||||||
			sprintf(str + 5, ".%04d %02d:%02d:",
 | 
								*str++ = '.';
 | 
				
			||||||
					(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1),
 | 
								str = pg_ltostr_zeropad(str,
 | 
				
			||||||
					tm->tm_hour, tm->tm_min);
 | 
										(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
 | 
				
			||||||
 | 
								*str++ = ' ';
 | 
				
			||||||
			AppendTimestampSeconds(str + strlen(str), tm, fsec);
 | 
								str = pg_ltostr_zeropad(str, tm->tm_hour, 2);
 | 
				
			||||||
 | 
								*str++ = ':';
 | 
				
			||||||
 | 
								str = pg_ltostr_zeropad(str, tm->tm_min, 2);
 | 
				
			||||||
 | 
								*str++ = ':';
 | 
				
			||||||
 | 
								str = AppendTimestampSeconds(str, tm, fsec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (print_tz)
 | 
								if (print_tz)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				if (tzn)
 | 
									if (tzn)
 | 
				
			||||||
					sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
 | 
									{
 | 
				
			||||||
 | 
										sprintf(str, " %.*s", MAXTZLEN, tzn);
 | 
				
			||||||
 | 
										str += strlen(str);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				else
 | 
									else
 | 
				
			||||||
					EncodeTimezone(str, tz, style);
 | 
										str = EncodeTimezone(str, tz, style);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (tm->tm_year <= 0)
 | 
					 | 
				
			||||||
				sprintf(str + strlen(str), " BC");
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case USE_POSTGRES_DATES:
 | 
							case USE_POSTGRES_DATES:
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			/* Backward-compatible with traditional Postgres abstime dates */
 | 
								/* Backward-compatible with traditional Postgres abstime dates */
 | 
				
			||||||
 | 
					 | 
				
			||||||
			day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
 | 
								day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
 | 
				
			||||||
			tm->tm_wday = j2day(day);
 | 
								tm->tm_wday = j2day(day);
 | 
				
			||||||
 | 
					 | 
				
			||||||
			memcpy(str, days[tm->tm_wday], 3);
 | 
								memcpy(str, days[tm->tm_wday], 3);
 | 
				
			||||||
			strcpy(str + 3, " ");
 | 
								str += 3;
 | 
				
			||||||
 | 
								*str++ = ' ';
 | 
				
			||||||
			if (DateOrder == DATEORDER_DMY)
 | 
								if (DateOrder == DATEORDER_DMY)
 | 
				
			||||||
				sprintf(str + 4, "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
 | 
								{
 | 
				
			||||||
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mday, 2);
 | 
				
			||||||
 | 
									*str++ = ' ';
 | 
				
			||||||
 | 
									memcpy(str, months[tm->tm_mon - 1], 3);
 | 
				
			||||||
 | 
									str += 3;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
				sprintf(str + 4, "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
 | 
								{
 | 
				
			||||||
 | 
									memcpy(str, months[tm->tm_mon - 1], 3);
 | 
				
			||||||
			sprintf(str + 10, " %02d:%02d:", tm->tm_hour, tm->tm_min);
 | 
									str += 3;
 | 
				
			||||||
 | 
									*str++ = ' ';
 | 
				
			||||||
			AppendTimestampSeconds(str + strlen(str), tm, fsec);
 | 
									str = pg_ltostr_zeropad(str, tm->tm_mday, 2);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			sprintf(str + strlen(str), " %04d",
 | 
								*str++ = ' ';
 | 
				
			||||||
					(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1));
 | 
								str = pg_ltostr_zeropad(str, tm->tm_hour, 2);
 | 
				
			||||||
 | 
								*str++ = ':';
 | 
				
			||||||
 | 
								str = pg_ltostr_zeropad(str, tm->tm_min, 2);
 | 
				
			||||||
 | 
								*str++ = ':';
 | 
				
			||||||
 | 
								str = AppendTimestampSeconds(str, tm, fsec);
 | 
				
			||||||
 | 
								*str++ = ' ';
 | 
				
			||||||
 | 
								str = pg_ltostr_zeropad(str,
 | 
				
			||||||
 | 
										(tm->tm_year > 0) ? tm->tm_year : -(tm->tm_year - 1), 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (print_tz)
 | 
								if (print_tz)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				if (tzn)
 | 
									if (tzn)
 | 
				
			||||||
					sprintf(str + strlen(str), " %.*s", MAXTZLEN, tzn);
 | 
									{
 | 
				
			||||||
 | 
										sprintf(str, " %.*s", MAXTZLEN, tzn);
 | 
				
			||||||
 | 
										str += strlen(str);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				else
 | 
									else
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					/*
 | 
										/*
 | 
				
			||||||
@@ -4079,15 +4206,19 @@ EncodeDateTime(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, const char
 | 
				
			|||||||
					 * avoid formatting something which would be rejected by
 | 
										 * avoid formatting something which would be rejected by
 | 
				
			||||||
					 * the date/time parser later. - thomas 2001-10-19
 | 
										 * the date/time parser later. - thomas 2001-10-19
 | 
				
			||||||
					 */
 | 
										 */
 | 
				
			||||||
					sprintf(str + strlen(str), " ");
 | 
										*str++ = ' ';
 | 
				
			||||||
					EncodeTimezone(str, tz, style);
 | 
										str = EncodeTimezone(str, tz, style);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (tm->tm_year <= 0)
 | 
					 | 
				
			||||||
				sprintf(str + strlen(str), " BC");
 | 
					 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (tm->tm_year <= 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							memcpy(str, " BC", 3);	/* Don't copy NUL */
 | 
				
			||||||
 | 
							str += 3;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*str = '\0';
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -4242,7 +4373,8 @@ EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str)
 | 
				
			|||||||
							day_sign, abs(mday),
 | 
												day_sign, abs(mday),
 | 
				
			||||||
							sec_sign, abs(hour), abs(min));
 | 
												sec_sign, abs(hour), abs(min));
 | 
				
			||||||
					cp += strlen(cp);
 | 
										cp += strlen(cp);
 | 
				
			||||||
					AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
 | 
										cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
 | 
				
			||||||
 | 
										*cp = '\0';
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				else if (has_year_month)
 | 
									else if (has_year_month)
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
@@ -4252,13 +4384,15 @@ EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str)
 | 
				
			|||||||
				{
 | 
									{
 | 
				
			||||||
					sprintf(cp, "%d %d:%02d:", mday, hour, min);
 | 
										sprintf(cp, "%d %d:%02d:", mday, hour, min);
 | 
				
			||||||
					cp += strlen(cp);
 | 
										cp += strlen(cp);
 | 
				
			||||||
					AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
 | 
										cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
 | 
				
			||||||
 | 
										*cp = '\0';
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				else
 | 
									else
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					sprintf(cp, "%d:%02d:", hour, min);
 | 
										sprintf(cp, "%d:%02d:", hour, min);
 | 
				
			||||||
					cp += strlen(cp);
 | 
										cp += strlen(cp);
 | 
				
			||||||
					AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
 | 
										cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
 | 
				
			||||||
 | 
										*cp = '\0';
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
@@ -4284,8 +4418,7 @@ EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str)
 | 
				
			|||||||
			{
 | 
								{
 | 
				
			||||||
				if (sec < 0 || fsec < 0)
 | 
									if (sec < 0 || fsec < 0)
 | 
				
			||||||
					*cp++ = '-';
 | 
										*cp++ = '-';
 | 
				
			||||||
				AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
 | 
									cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
 | 
				
			||||||
				cp += strlen(cp);
 | 
					 | 
				
			||||||
				*cp++ = 'S';
 | 
									*cp++ = 'S';
 | 
				
			||||||
				*cp++ = '\0';
 | 
									*cp++ = '\0';
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -4311,7 +4444,8 @@ EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str)
 | 
				
			|||||||
						(minus ? "-" : (is_before ? "+" : "")),
 | 
											(minus ? "-" : (is_before ? "+" : "")),
 | 
				
			||||||
						abs(hour), abs(min));
 | 
											abs(hour), abs(min));
 | 
				
			||||||
				cp += strlen(cp);
 | 
									cp += strlen(cp);
 | 
				
			||||||
				AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
 | 
									cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, true);
 | 
				
			||||||
 | 
									*cp = '\0';
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -4337,8 +4471,7 @@ EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str)
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
				else if (is_before)
 | 
									else if (is_before)
 | 
				
			||||||
					*cp++ = '-';
 | 
										*cp++ = '-';
 | 
				
			||||||
				AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
 | 
									cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false);
 | 
				
			||||||
				cp += strlen(cp);
 | 
					 | 
				
			||||||
				sprintf(cp, " sec%s",
 | 
									sprintf(cp, " sec%s",
 | 
				
			||||||
						(abs(sec) != 1 || fsec != 0) ? "s" : "");
 | 
											(abs(sec) != 1 || fsec != 0) ? "s" : "");
 | 
				
			||||||
				is_zero = FALSE;
 | 
									is_zero = FALSE;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -227,3 +227,164 @@ pg_lltoa(int64 value, char *a)
 | 
				
			|||||||
		*a-- = swap;
 | 
							*a-- = swap;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * pg_ltostr_zeropad
 | 
				
			||||||
 | 
					 *		Converts 'value' into a decimal string representation stored at 'str'.
 | 
				
			||||||
 | 
					 *		'minwidth' specifies the minimum width of the result; any extra space
 | 
				
			||||||
 | 
					 *		is filled up by prefixing the number with zeros.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns the ending address of the string result (the last character written
 | 
				
			||||||
 | 
					 * plus 1).  Note that no NUL terminator is written.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The intended use-case for this function is to build strings that contain
 | 
				
			||||||
 | 
					 * multiple individual numbers, for example:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	str = pg_ltostr_zeropad(str, hours, 2);
 | 
				
			||||||
 | 
					 *	*str++ = ':';
 | 
				
			||||||
 | 
					 *	str = pg_ltostr_zeropad(str, mins, 2);
 | 
				
			||||||
 | 
					 *	*str++ = ':';
 | 
				
			||||||
 | 
					 *	str = pg_ltostr_zeropad(str, secs, 2);
 | 
				
			||||||
 | 
					 *	*str = '\0';
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note: Caller must ensure that 'str' points to enough memory to hold the
 | 
				
			||||||
 | 
					 * result.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					char *
 | 
				
			||||||
 | 
					pg_ltostr_zeropad(char *str, int32 value, int32 minwidth)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char	   *start = str;
 | 
				
			||||||
 | 
						char	   *end = &str[minwidth];
 | 
				
			||||||
 | 
						int32		num = value;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Assert(minwidth > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Handle negative numbers in a special way.  We can't just write a '-'
 | 
				
			||||||
 | 
						 * prefix and reverse the sign as that would overflow for INT32_MIN.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (num < 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							*start++ = '-';
 | 
				
			||||||
 | 
							minwidth--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * Build the number starting at the last digit.  Here remainder will
 | 
				
			||||||
 | 
							 * be a negative number, so we must reverse the sign before adding '0'
 | 
				
			||||||
 | 
							 * in order to get the correct ASCII digit.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							while (minwidth--)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								int32		oldval = num;
 | 
				
			||||||
 | 
								int32		remainder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								num /= 10;
 | 
				
			||||||
 | 
								remainder = oldval - num * 10;
 | 
				
			||||||
 | 
								start[minwidth] = '0' - remainder;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/* Build the number starting at the last digit */
 | 
				
			||||||
 | 
							while (minwidth--)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								int32		oldval = num;
 | 
				
			||||||
 | 
								int32		remainder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								num /= 10;
 | 
				
			||||||
 | 
								remainder = oldval - num * 10;
 | 
				
			||||||
 | 
								start[minwidth] = '0' + remainder;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * If minwidth was not high enough to fit the number then num won't have
 | 
				
			||||||
 | 
						 * been divided down to zero.  We punt the problem to pg_ltostr(), which
 | 
				
			||||||
 | 
						 * will generate a correct answer in the minimum valid width.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (num != 0)
 | 
				
			||||||
 | 
							return pg_ltostr(str, value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Otherwise, return last output character + 1 */
 | 
				
			||||||
 | 
						return end;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * pg_ltostr
 | 
				
			||||||
 | 
					 *		Converts 'value' into a decimal string representation stored at 'str'.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Returns the ending address of the string result (the last character written
 | 
				
			||||||
 | 
					 * plus 1).  Note that no NUL terminator is written.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The intended use-case for this function is to build strings that contain
 | 
				
			||||||
 | 
					 * multiple individual numbers, for example:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	str = pg_ltostr(str, a);
 | 
				
			||||||
 | 
					 *	*str++ = ' ';
 | 
				
			||||||
 | 
					 *	str = pg_ltostr(str, b);
 | 
				
			||||||
 | 
					 *	*str = '\0';
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note: Caller must ensure that 'str' points to enough memory to hold the
 | 
				
			||||||
 | 
					 * result.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					char *
 | 
				
			||||||
 | 
					pg_ltostr(char *str, int32 value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char	   *start;
 | 
				
			||||||
 | 
						char	   *end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Handle negative numbers in a special way.  We can't just write a '-'
 | 
				
			||||||
 | 
						 * prefix and reverse the sign as that would overflow for INT32_MIN.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (value < 0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							*str++ = '-';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Mark the position we must reverse the string from. */
 | 
				
			||||||
 | 
							start = str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Compute the result string backwards. */
 | 
				
			||||||
 | 
							do
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								int32		oldval = value;
 | 
				
			||||||
 | 
								int32		remainder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								value /= 10;
 | 
				
			||||||
 | 
								remainder = oldval - value * 10;
 | 
				
			||||||
 | 
								/* As above, we expect remainder to be negative. */
 | 
				
			||||||
 | 
								*str++ = '0' - remainder;
 | 
				
			||||||
 | 
							} while (value != 0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/* Mark the position we must reverse the string from. */
 | 
				
			||||||
 | 
							start = str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Compute the result string backwards. */
 | 
				
			||||||
 | 
							do
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								int32		oldval = value;
 | 
				
			||||||
 | 
								int32		remainder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								value /= 10;
 | 
				
			||||||
 | 
								remainder = oldval - value * 10;
 | 
				
			||||||
 | 
								*str++ = '0' + remainder;
 | 
				
			||||||
 | 
							} while (value != 0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Remember the end+1 and back up 'str' to the last character. */
 | 
				
			||||||
 | 
						end = str--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Reverse string. */
 | 
				
			||||||
 | 
						while (start < str)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							char		swap = *start;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							*start++ = *str;
 | 
				
			||||||
 | 
							*str-- = swap;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return end;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -290,6 +290,8 @@ extern int32 pg_atoi(const char *s, int size, int c);
 | 
				
			|||||||
extern void pg_itoa(int16 i, char *a);
 | 
					extern void pg_itoa(int16 i, char *a);
 | 
				
			||||||
extern void pg_ltoa(int32 l, char *a);
 | 
					extern void pg_ltoa(int32 l, char *a);
 | 
				
			||||||
extern void pg_lltoa(int64 ll, char *a);
 | 
					extern void pg_lltoa(int64 ll, char *a);
 | 
				
			||||||
 | 
					extern char *pg_ltostr_zeropad(char *str, int32 value, int32 minwidth);
 | 
				
			||||||
 | 
					extern char *pg_ltostr(char *str, int32 value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 *		Per-opclass comparison functions for new btrees.  These are
 | 
					 *		Per-opclass comparison functions for new btrees.  These are
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user