1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-11 00:12:06 +03:00

Repair array subscript overrun identified by Yichen Xie. Reduce the

value of MAX_TIME_PRECISION in floating-point-timestamp-storage case
from 13 to 10, which is as much as time_out is actually willing to print.
(The alternative of increasing the number of digits we are willing to
print looks risky; we might find ourselves printing roundoff garbage.)
This commit is contained in:
Tom Lane
2003-01-29 01:09:03 +00:00
parent 8672494da5
commit 726b7f3b3c
4 changed files with 141 additions and 142 deletions

View File

@@ -1,5 +1,5 @@
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.106.2.2 2002/11/21 23:31:37 tgl Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.106.2.3 2003/01/29 01:09:03 tgl Exp $
--> -->
<chapter id="datatype"> <chapter id="datatype">
@@ -86,18 +86,18 @@ $Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.106.2.2 2002/11/21 23:31:
<entry>binary data</entry> <entry>binary data</entry>
</row> </row>
<row>
<entry><type>character(<replaceable>n</replaceable>)</type></entry>
<entry><type>char(<replaceable>n</replaceable>)</type></entry>
<entry>fixed-length character string</entry>
</row>
<row> <row>
<entry><type>character varying(<replaceable>n</replaceable>)</type></entry> <entry><type>character varying(<replaceable>n</replaceable>)</type></entry>
<entry><type>varchar(<replaceable>n</replaceable>)</type></entry> <entry><type>varchar(<replaceable>n</replaceable>)</type></entry>
<entry>variable-length character string</entry> <entry>variable-length character string</entry>
</row> </row>
<row>
<entry><type>character(<replaceable>n</replaceable>)</type></entry>
<entry><type>char(<replaceable>n</replaceable>)</type></entry>
<entry>fixed-length character string</entry>
</row>
<row> <row>
<entry><type>cidr</type></entry> <entry><type>cidr</type></entry>
<entry></entry> <entry></entry>
@@ -248,7 +248,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.106.2.2 2002/11/21 23:31:
The following types (or spellings thereof) are specified by The following types (or spellings thereof) are specified by
<acronym>SQL</acronym>: <type>bit</type>, <type>bit <acronym>SQL</acronym>: <type>bit</type>, <type>bit
varying</type>, <type>boolean</type>, <type>char</type>, varying</type>, <type>boolean</type>, <type>char</type>,
<type>character</type>, <type>character varying</type>, <type>character varying</type>, <type>character</type>,
<type>varchar</type>, <type>date</type>, <type>double <type>varchar</type>, <type>date</type>, <type>double
precision</type>, <type>integer</type>, <type>interval</type>, precision</type>, <type>integer</type>, <type>interval</type>,
<type>numeric</type>, <type>decimal</type>, <type>real</type>, <type>numeric</type>, <type>decimal</type>, <type>real</type>,
@@ -654,10 +654,11 @@ NUMERIC
</indexterm> </indexterm>
<para> <para>
The <type>serial</type> data types are not truly types, but are a The <type>serial</type> data type is not a true type, but merely
notational convenience for setting up unique identifier columns a notational convenience for setting up identifier columns
in tables. (similar to the <literal>AUTO_INCREMENT</literal> property
In the current implementation, specifying supported by some other databases). In the current
implementation, specifying
<programlisting> <programlisting>
CREATE TABLE <replaceable class="parameter">tablename</replaceable> ( CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
@@ -683,33 +684,50 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
not automatic. not automatic.
</para> </para>
<para>
To use a <type>serial</type> column to insert the next value of
the sequence into the table, specify that the <type>serial</type>
column should be assigned the default value. This can be done
either be excluding from the column from the list of columns in
the <command>INSERT</command> statement, or through the use of
the <literal>DEFAULT</literal> keyword.
</para>
<para> <para>
The type names <type>serial</type> and <type>serial4</type> are The type names <type>serial</type> and <type>serial4</type> are
equivalent: both create <type>integer</type> columns. The type equivalent: both create <type>integer</type> columns. The type
names <type>bigserial</type> and <type>serial8</type> work just names <type>bigserial</type> and <type>serial8</type> work just
the same way, except that they create a <type>bigint</type> the same way, except that they create a <type>bigint</type>
column. <type>bigserial</type> should be used if you anticipate column. <type>bigserial</type> should be used if you anticipate
the use of more than 2<superscript>31</> identifiers over the lifetime of the table. the use of more than 2<superscript>31</> identifiers over the
lifetime of the table.
</para> </para>
<para> <para>
The sequence created by a <type>serial</type> type is automatically The sequence created by a <type>serial</type> type is
dropped when automatically dropped when the owning column is dropped, and
the owning column is dropped, and cannot be dropped otherwise. cannot be dropped otherwise. (This was not true in
(This was not true in <productname>PostgreSQL</productname> releases <productname>PostgreSQL</productname> releases before 7.3. Note
before 7.3. Note that this automatic drop linkage will not occur for a that this automatic drop linkage will not occur for a sequence
sequence created by reloading a dump from a pre-7.3 database; the dump created by reloading a dump from a pre-7.3 database; the dump
file does not contain the information needed to establish the dependency file does not contain the information needed to establish the
link.) dependency link.) Furthermore, this dependency between sequence
and column is made only for the <type>serial</> column itself; if
any other columns reference the sequence (perhaps by manually
calling the <function>nextval()</>) function), they may be broken
if the sequence is removed. Using <type>serial</> columns in
fashion is considered bad form.
</para> </para>
<note><para> <note>
Prior to <productname>PostgreSQL</productname> 7.3, <type>serial</type> <para>
implied <literal>UNIQUE</literal>. This is no longer automatic. If Prior to <productname>PostgreSQL</> 7.3, <type>serial</type>
you wish a serial column to be <literal>UNIQUE</literal> or a implied <literal>UNIQUE</literal>. This is no longer automatic.
<literal>PRIMARY KEY</literal> it must now be specified, same as with If you wish a serial column to be <literal>UNIQUE</literal> or a
any other data type. <literal>PRIMARY KEY</literal> it must now be specified, just as
</para></note> with any other data type.
</para>
</note>
</sect2> </sect2>
</sect1> </sect1>
@@ -793,14 +811,14 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
</row> </row>
</thead> </thead>
<tbody> <tbody>
<row>
<entry><type>character(<replaceable>n</>)</type>, <type>char(<replaceable>n</>)</type></entry>
<entry>fixed-length, blank padded</entry>
</row>
<row> <row>
<entry><type>character varying(<replaceable>n</>)</type>, <type>varchar(<replaceable>n</>)</type></entry> <entry><type>character varying(<replaceable>n</>)</type>, <type>varchar(<replaceable>n</>)</type></entry>
<entry>variable-length with limit</entry> <entry>variable-length with limit</entry>
</row> </row>
<row>
<entry><type>character(<replaceable>n</>)</type>, <type>char(<replaceable>n</>)</type></entry>
<entry>fixed-length, blank padded</entry>
</row>
<row> <row>
<entry><type>text</type></entry> <entry><type>text</type></entry>
<entry>variable unlimited length</entry> <entry>variable unlimited length</entry>
@@ -817,29 +835,29 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
<para> <para>
<acronym>SQL</acronym> defines two primary character types: <acronym>SQL</acronym> defines two primary character types:
<type>character(<replaceable>n</>)</type> and <type>character <type>character varying(<replaceable>n</>)</type> and
varying(<replaceable>n</>)</type>, where <replaceable>n</> is a <type>character(<replaceable>n</>)</type>, where <replaceable>n</>
positive integer. Both of these types can store strings up to is a positive integer. Both of these types can store strings up to
<replaceable>n</> characters in length. An attempt to store a <replaceable>n</> characters in length. An attempt to store a
longer string into a column of these types will result in an longer string into a column of these types will result in an
error, unless the excess characters are all spaces, in which case error, unless the excess characters are all spaces, in which case
the string will be truncated to the maximum length. (This the string will be truncated to the maximum length. (This somewhat
somewhat bizarre exception is required by the bizarre exception is required by the <acronym>SQL</acronym>
<acronym>SQL</acronym> standard.) If the string to be stored is standard.) If the string to be stored is shorter than the declared
shorter than the declared length, values of type length, values of type <type>character</type> will be space-padded;
<type>character</type> will be space-padded; values of type values of type <type>character varying</type> will simply store the
<type>character varying</type> will simply store the shorter shorter
string. string.
</para> </para>
<note> <note>
<para> <para>
If one explicitly casts a value to If one explicitly casts a value to <type>character
<type>character(<replaceable>n</>)</type> or <type>character varying(<replaceable>n</>)</type> or
varying(<replaceable>n</>)</type>, then an overlength value will <type>character(<replaceable>n</>)</type>, then an over-length
be truncated to <replaceable>n</> characters without raising an value will be truncated to <replaceable>n</> characters without
error. (This too is required by the <acronym>SQL</acronym> raising an error. (This too is required by the
standard.) <acronym>SQL</acronym> standard.)
</para> </para>
</note> </note>
@@ -852,14 +870,14 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
</note> </note>
<para> <para>
The notations <type>char(<replaceable>n</>)</type> and The notations <type>varchar(<replaceable>n</>)</type> and
<type>varchar(<replaceable>n</>)</type> are aliases for <type>char(<replaceable>n</>)</type> are aliases for <type>character
<type>character(<replaceable>n</>)</type> and <type>character varying(<replaceable>n</>)</type> and
varying(<replaceable>n</>)</type>, <type>character(<replaceable>n</>)</type>, respectively.
respectively. <type>character</type> without length specifier is <type>character</type> without length specifier is equivalent to
equivalent to <type>character(1)</type>; if <type>character <type>character(1)</type>; if <type>character varying</type> is used
varying</type> is used without length specifier, the type accepts without length specifier, the type accepts strings of any size. The
strings of any size. The latter is a <productname>PostgreSQL</> extension. latter is a <productname>PostgreSQL</> extension.
</para> </para>
<para> <para>
@@ -943,19 +961,18 @@ SELECT b, char_length(b) FROM test2;
<para> <para>
There are two other fixed-length character types in There are two other fixed-length character types in
<productname>PostgreSQL</productname>, shown in <xref linkend="datatype-character-special-table">. <productname>PostgreSQL</productname>, shown in <xref
The <type>name</type> type linkend="datatype-character-special-table">. The <type>name</type>
exists <emphasis>only</emphasis> for storage of internal catalog type exists <emphasis>only</emphasis> for storage of internal
names and is not intended for use by the general user. Its length catalog names and is not intended for use by the general user. Its
is currently defined as 64 bytes (63 usable characters plus terminator) length is currently defined as 64 bytes (63 usable characters plus
but should be referenced using the constant terminator) but should be referenced using the constant
<symbol>NAMEDATALEN</symbol>. The length is set at compile time <symbol>NAMEDATALEN</symbol>. The length is set at compile time (and
(and is therefore adjustable for special uses); the default is therefore adjustable for special uses); the default maximum
maximum length may change in a future release. The type length may change in a future release. The type <type>"char"</type>
<type>"char"</type> (note the quotes) is different from (note the quotes) is different from <type>char(1)</type> in that it
<type>char(1)</type> in that it only uses one byte of storage. It only uses one byte of storage. It is internally used in the system
is internally used in the system catalogs as a poor-man's catalogs as a poor-man's enumeration type.
enumeration type.
</para> </para>
<table id="datatype-character-special-table"> <table id="datatype-character-special-table">
@@ -1280,8 +1297,7 @@ SELECT b, char_length(b) FROM test2;
fractional digits retained in the seconds field. By default, there fractional digits retained in the seconds field. By default, there
is no explicit bound on precision. The allowed range of is no explicit bound on precision. The allowed range of
<replaceable>p</replaceable> is from 0 to 6 for the <replaceable>p</replaceable> is from 0 to 6 for the
<type>timestamp</type> and <type>interval</type> types, 0 to 13 <type>timestamp</type> and <type>interval</type> types.
for the <type>time</type> types.
</para> </para>
<note> <note>
@@ -1297,6 +1313,12 @@ SELECT b, char_length(b) FROM test2;
</para> </para>
</note> </note>
<para>
For the <type>time</type> types, the allowed range of
<replaceable>p</replaceable> is from 0 to 6 when eight-byte integer
storage is used, or from 0 to 10 when floating-point storage is used.
</para>
<para> <para>
Time zones, and time-zone conventions, are influenced by Time zones, and time-zone conventions, are influenced by
political decisions, not just earth geometry. Time zones around the political decisions, not just earth geometry. Time zones around the
@@ -1468,7 +1490,7 @@ SELECT b, char_length(b) FROM test2;
<para> <para>
The <type>time</type> type can be specified as <type>time</type> or The <type>time</type> type can be specified as <type>time</type> or
as <type>time without time zone</type>. The optional precision as <type>time without time zone</type>. The optional precision
<replaceable>p</replaceable> should be between 0 and 13, and <replaceable>p</replaceable> should be between 0 and 6, and
defaults to the precision of the input time literal. defaults to the precision of the input time literal.
</para> </para>

View File

@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.73.2.2 2003/01/09 01:07:17 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.73.2.3 2003/01/29 01:09:03 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@@ -619,7 +619,7 @@ static void
AdjustTimeForTypmod(TimeADT *time, int32 typmod) AdjustTimeForTypmod(TimeADT *time, int32 typmod)
{ {
#ifdef HAVE_INT64_TIMESTAMP #ifdef HAVE_INT64_TIMESTAMP
static const int64 TimeScales[MAX_TIMESTAMP_PRECISION + 1] = { static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
INT64CONST(1000000), INT64CONST(1000000),
INT64CONST(100000), INT64CONST(100000),
INT64CONST(10000), INT64CONST(10000),
@@ -629,7 +629,7 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
INT64CONST(1) INT64CONST(1)
}; };
static const int64 TimeOffsets[MAX_TIMESTAMP_PRECISION + 1] = { static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = {
INT64CONST(500000), INT64CONST(500000),
INT64CONST(50000), INT64CONST(50000),
INT64CONST(5000), INT64CONST(5000),
@@ -640,14 +640,19 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
}; };
#else #else
static const double TimeScales[MAX_TIMESTAMP_PRECISION + 1] = { /* note MAX_TIME_PRECISION differs in this case */
static const double TimeScales[MAX_TIME_PRECISION + 1] = {
1, 1,
10, 10,
100, 100,
1000, 1000,
10000, 10000,
100000, 100000,
1000000 1000000,
10000000,
100000000,
1000000000,
10000000000
}; };
#endif #endif
@@ -656,7 +661,7 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
/* /*
* Note: this round-to-nearest code is not completely consistent * Note: this round-to-nearest code is not completely consistent
* about rounding values that are exactly halfway between integral * about rounding values that are exactly halfway between integral
* values. On most platforms, rint() will implement round-to-nearest, * values. On most platforms, rint() will implement round-to-nearest-even,
* but the integer code always rounds up (away from zero). Is it * but the integer code always rounds up (away from zero). Is it
* worth trying to be consistent? * worth trying to be consistent?
*/ */

View File

@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.96.2.2 2003/01/16 00:27:17 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.96.2.3 2003/01/29 01:09:03 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@@ -667,14 +667,13 @@ TrimTrailingZeros(char *str)
} }
#endif #endif
/* chop off trailing zeros... */ /* chop off trailing zeros... but leave at least 2 fractional digits */
while ((*(str + len - 1) == '0') while ((*(str + len - 1) == '0')
&& (*(str + len - 3) != '.')) && (*(str + len - 3) != '.'))
{ {
len--; len--;
*(str + len) = '\0'; *(str + len) = '\0';
} }
return;
} }
@@ -3145,33 +3144,22 @@ EncodeDateOnly(struct tm * tm, int style, char *str)
int int
EncodeTimeOnly(struct tm * tm, fsec_t fsec, int *tzp, int style, char *str) EncodeTimeOnly(struct tm * tm, fsec_t fsec, int *tzp, int style, char *str)
{ {
#ifndef HAVE_INT64_TIMESTAMP
fsec_t sec;
#endif
if ((tm->tm_hour < 0) || (tm->tm_hour > 24)) if ((tm->tm_hour < 0) || (tm->tm_hour > 24))
return -1; return -1;
#ifndef HAVE_INT64_TIMESTAMP
sec = (tm->tm_sec + fsec);
#endif
sprintf(str, "%02d:%02d", tm->tm_hour, tm->tm_min); sprintf(str, "%02d:%02d", tm->tm_hour, tm->tm_min);
/* /*
* If we have fractional seconds, then include a decimal point We will * Print fractional seconds if any. The field widths here should be
* do up to 6 fractional digits, and we have rounded any inputs to * at least equal to the larger of MAX_TIME_PRECISION and
* eliminate anything to the right of 6 digits anyway. If there are no * MAX_TIMESTAMP_PRECISION.
* fractional seconds, then do not bother printing a decimal point at
* all. - thomas 2001-09-29
*/ */
if (fsec != 0) if (fsec != 0)
{ {
#ifdef HAVE_INT64_TIMESTAMP #ifdef HAVE_INT64_TIMESTAMP
sprintf((str + strlen(str)), ":%02d", tm->tm_sec); sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
sprintf((str + strlen(str)), ".%06d", fsec);
#else #else
sprintf((str + strlen(str)), ":%013.10f", sec); sprintf((str + strlen(str)), ":%013.10f", tm->tm_sec + fsec);
#endif #endif
/* chop off trailing pairs of zeros... */ /* chop off trailing pairs of zeros... */
while ((strcmp((str + strlen(str) - 2), "00") == 0) while ((strcmp((str + strlen(str) - 2), "00") == 0)
@@ -3179,11 +3167,7 @@ EncodeTimeOnly(struct tm * tm, fsec_t fsec, int *tzp, int style, char *str)
*(str + strlen(str) - 2) = '\0'; *(str + strlen(str) - 2) = '\0';
} }
else else
#ifdef HAVE_INT64_TIMESTAMP
sprintf((str + strlen(str)), ":%02d", tm->tm_sec); sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
#else
sprintf((str + strlen(str)), ":%02.0f", sec);
#endif
if (tzp != NULL) if (tzp != NULL)
{ {
@@ -3217,20 +3201,12 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha
hour, hour,
min; min;
#ifndef HAVE_INT64_TIMESTAMP
fsec_t sec;
#endif
/* /*
* Why are we checking only the month field? Change this to an * Why are we checking only the month field? Change this to an
* assert... if ((tm->tm_mon < 1) || (tm->tm_mon > 12)) return -1; * assert... if ((tm->tm_mon < 1) || (tm->tm_mon > 12)) return -1;
*/ */
Assert((tm->tm_mon >= 1) && (tm->tm_mon <= 12)); Assert((tm->tm_mon >= 1) && (tm->tm_mon <= 12));
#ifndef HAVE_INT64_TIMESTAMP
sec = (tm->tm_sec + fsec);
#endif
switch (style) switch (style)
{ {
case USE_ISO_DATES: case USE_ISO_DATES:
@@ -3241,21 +3217,20 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha
tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min); tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
/* /*
* If we have fractional seconds, then include a decimal point * Print fractional seconds if any. The field widths here should
* We will do up to 6 fractional digits, and we have rounded * be at least equal to MAX_TIMESTAMP_PRECISION.
* any inputs to eliminate anything to the right of 6 digits *
* anyway. If there are no fractional seconds, then do not * In float mode, don't print fractional seconds before 1 AD,
* bother printing a decimal point at all. - thomas 2001-09-29 * since it's unlikely there's any precision left ...
*/ */
#ifdef HAVE_INT64_TIMESTAMP #ifdef HAVE_INT64_TIMESTAMP
if (fsec != 0) if (fsec != 0)
{ {
sprintf((str + strlen(str)), ":%02d", tm->tm_sec); sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
sprintf((str + strlen(str)), ".%06d", fsec);
#else #else
if ((fsec != 0) && (tm->tm_year > 0)) if ((fsec != 0) && (tm->tm_year > 0))
{ {
sprintf((str + strlen(str)), ":%013.10f", sec); sprintf((str + strlen(str)), ":%09.6f", tm->tm_sec + fsec);
#endif #endif
TrimTrailingZeros(str); TrimTrailingZeros(str);
} }
@@ -3292,21 +3267,20 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha
tm->tm_hour, tm->tm_min); tm->tm_hour, tm->tm_min);
/* /*
* If we have fractional seconds, then include a decimal point * Print fractional seconds if any. The field widths here should
* We will do up to 6 fractional digits, and we have rounded * be at least equal to MAX_TIMESTAMP_PRECISION.
* any inputs to eliminate anything to the right of 6 digits *
* anyway. If there are no fractional seconds, then do not * In float mode, don't print fractional seconds before 1 AD,
* bother printing a decimal point at all. - thomas 2001-09-29 * since it's unlikely there's any precision left ...
*/ */
#ifdef HAVE_INT64_TIMESTAMP #ifdef HAVE_INT64_TIMESTAMP
if (fsec != 0) if (fsec != 0)
{ {
sprintf((str + strlen(str)), ":%02d", tm->tm_sec); sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
sprintf((str + strlen(str)), ".%06d", fsec);
#else #else
if ((fsec != 0) && (tm->tm_year > 0)) if ((fsec != 0) && (tm->tm_year > 0))
{ {
sprintf((str + strlen(str)), ":%013.10f", sec); sprintf((str + strlen(str)), ":%09.6f", tm->tm_sec + fsec);
#endif #endif
TrimTrailingZeros(str); TrimTrailingZeros(str);
} }
@@ -3339,21 +3313,20 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha
tm->tm_hour, tm->tm_min); tm->tm_hour, tm->tm_min);
/* /*
* If we have fractional seconds, then include a decimal point * Print fractional seconds if any. The field widths here should
* We will do up to 6 fractional digits, and we have rounded * be at least equal to MAX_TIMESTAMP_PRECISION.
* any inputs to eliminate anything to the right of 6 digits *
* anyway. If there are no fractional seconds, then do not * In float mode, don't print fractional seconds before 1 AD,
* bother printing a decimal point at all. - thomas 2001-09-29 * since it's unlikely there's any precision left ...
*/ */
#ifdef HAVE_INT64_TIMESTAMP #ifdef HAVE_INT64_TIMESTAMP
if (fsec != 0) if (fsec != 0)
{ {
sprintf((str + strlen(str)), ":%02d", tm->tm_sec); sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
sprintf((str + strlen(str)), ".%06d", fsec);
#else #else
if ((fsec != 0) && (tm->tm_year > 0)) if ((fsec != 0) && (tm->tm_year > 0))
{ {
sprintf((str + strlen(str)), ":%013.10f", sec); sprintf((str + strlen(str)), ":%09.6f", tm->tm_sec + fsec);
#endif #endif
TrimTrailingZeros(str); TrimTrailingZeros(str);
} }
@@ -3394,21 +3367,20 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha
sprintf((str + 10), " %02d:%02d", tm->tm_hour, tm->tm_min); sprintf((str + 10), " %02d:%02d", tm->tm_hour, tm->tm_min);
/* /*
* If we have fractional seconds, then include a decimal point * Print fractional seconds if any. The field widths here should
* We will do up to 6 fractional digits, and we have rounded * be at least equal to MAX_TIMESTAMP_PRECISION.
* any inputs to eliminate anything to the right of 6 digits *
* anyway. If there are no fractional seconds, then do not * In float mode, don't print fractional seconds before 1 AD,
* bother printing a decimal point at all. - thomas 2001-09-29 * since it's unlikely there's any precision left ...
*/ */
#ifdef HAVE_INT64_TIMESTAMP #ifdef HAVE_INT64_TIMESTAMP
if (fsec != 0) if (fsec != 0)
{ {
sprintf((str + strlen(str)), ":%02d", tm->tm_sec); sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
sprintf((str + strlen(str)), ".%06d", fsec);
#else #else
if ((fsec != 0) && (tm->tm_year > 0)) if ((fsec != 0) && (tm->tm_year > 0))
{ {
sprintf((str + strlen(str)), ":%013.10f", sec); sprintf((str + strlen(str)), ":%09.6f", tm->tm_sec + fsec);
#endif #endif
TrimTrailingZeros(str); TrimTrailingZeros(str);
} }

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: date.h,v 1.21 2002/09/04 20:31:45 momjian Exp $ * $Id: date.h,v 1.21.2.1 2003/01/29 01:09:03 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@@ -41,7 +41,7 @@ typedef struct
#ifdef HAVE_INT64_TIMESTAMP #ifdef HAVE_INT64_TIMESTAMP
#define MAX_TIME_PRECISION 6 #define MAX_TIME_PRECISION 6
#else #else
#define MAX_TIME_PRECISION 13 #define MAX_TIME_PRECISION 10
#endif #endif
/* /*