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">
@@ -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>
</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>
<entry><type>character varying(<replaceable>n</replaceable>)</type></entry>
<entry><type>varchar(<replaceable>n</replaceable>)</type></entry>
<entry>variable-length character string</entry>
</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>
<entry><type>cidr</type></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
<acronym>SQL</acronym>: <type>bit</type>, <type>bit
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
precision</type>, <type>integer</type>, <type>interval</type>,
<type>numeric</type>, <type>decimal</type>, <type>real</type>,
@@ -654,10 +654,11 @@ NUMERIC
</indexterm>
<para>
The <type>serial</type> data types are not truly types, but are a
notational convenience for setting up unique identifier columns
in tables.
In the current implementation, specifying
The <type>serial</type> data type is not a true type, but merely
a notational convenience for setting up identifier columns
(similar to the <literal>AUTO_INCREMENT</literal> property
supported by some other databases). In the current
implementation, specifying
<programlisting>
CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
@@ -683,33 +684,50 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
not automatic.
</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>
The type names <type>serial</type> and <type>serial4</type> are
equivalent: both create <type>integer</type> columns. The type
names <type>bigserial</type> and <type>serial8</type> work just
the same way, except that they create a <type>bigint</type>
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>
The sequence created by a <type>serial</type> type is automatically
dropped when
the owning column is dropped, and cannot be dropped otherwise.
(This was not true in <productname>PostgreSQL</productname> releases
before 7.3. Note that this automatic drop linkage will not occur for a
sequence created by reloading a dump from a pre-7.3 database; the dump
file does not contain the information needed to establish the dependency
link.)
The sequence created by a <type>serial</type> type is
automatically dropped when the owning column is dropped, and
cannot be dropped otherwise. (This was not true in
<productname>PostgreSQL</productname> releases before 7.3. Note
that this automatic drop linkage will not occur for a sequence
created by reloading a dump from a pre-7.3 database; the dump
file does not contain the information needed to establish the
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>
<note><para>
Prior to <productname>PostgreSQL</productname> 7.3, <type>serial</type>
implied <literal>UNIQUE</literal>. This is no longer automatic. If
you wish a serial column to be <literal>UNIQUE</literal> or a
<literal>PRIMARY KEY</literal> it must now be specified, same as with
any other data type.
</para></note>
<note>
<para>
Prior to <productname>PostgreSQL</> 7.3, <type>serial</type>
implied <literal>UNIQUE</literal>. This is no longer automatic.
If you wish a serial column to be <literal>UNIQUE</literal> or a
<literal>PRIMARY KEY</literal> it must now be specified, just as
with any other data type.
</para>
</note>
</sect2>
</sect1>
@@ -793,14 +811,14 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
</row>
</thead>
<tbody>
<row>
<entry><type>character(<replaceable>n</>)</type>, <type>char(<replaceable>n</>)</type></entry>
<entry>fixed-length, blank padded</entry>
</row>
<row>
<entry><type>character varying(<replaceable>n</>)</type>, <type>varchar(<replaceable>n</>)</type></entry>
<entry>variable-length with limit</entry>
</row>
<row>
<entry><type>character(<replaceable>n</>)</type>, <type>char(<replaceable>n</>)</type></entry>
<entry>fixed-length, blank padded</entry>
</row>
<row>
<entry><type>text</type></entry>
<entry>variable unlimited length</entry>
@@ -817,29 +835,29 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
<para>
<acronym>SQL</acronym> defines two primary character types:
<type>character(<replaceable>n</>)</type> and <type>character
varying(<replaceable>n</>)</type>, where <replaceable>n</> is a
positive integer. Both of these types can store strings up to
<type>character varying(<replaceable>n</>)</type> and
<type>character(<replaceable>n</>)</type>, where <replaceable>n</>
is a positive integer. Both of these types can store strings up to
<replaceable>n</> characters in length. An attempt to store a
longer string into a column of these types will result in an
error, unless the excess characters are all spaces, in which case
the string will be truncated to the maximum length. (This
somewhat bizarre exception is required by the
<acronym>SQL</acronym> standard.) If the string to be stored is
shorter than the declared length, values of type
<type>character</type> will be space-padded; values of type
<type>character varying</type> will simply store the shorter
the string will be truncated to the maximum length. (This somewhat
bizarre exception is required by the <acronym>SQL</acronym>
standard.) If the string to be stored is shorter than the declared
length, values of type <type>character</type> will be space-padded;
values of type <type>character varying</type> will simply store the
shorter
string.
</para>
<note>
<para>
If one explicitly casts a value to
<type>character(<replaceable>n</>)</type> or <type>character
varying(<replaceable>n</>)</type>, then an overlength value will
be truncated to <replaceable>n</> characters without raising an
error. (This too is required by the <acronym>SQL</acronym>
standard.)
If one explicitly casts a value to <type>character
varying(<replaceable>n</>)</type> or
<type>character(<replaceable>n</>)</type>, then an over-length
value will be truncated to <replaceable>n</> characters without
raising an error. (This too is required by the
<acronym>SQL</acronym> standard.)
</para>
</note>
@@ -852,14 +870,14 @@ CREATE TABLE <replaceable class="parameter">tablename</replaceable> (
</note>
<para>
The notations <type>char(<replaceable>n</>)</type> and
<type>varchar(<replaceable>n</>)</type> are aliases for
<type>character(<replaceable>n</>)</type> and <type>character
varying(<replaceable>n</>)</type>,
respectively. <type>character</type> without length specifier is
equivalent to <type>character(1)</type>; if <type>character
varying</type> is used without length specifier, the type accepts
strings of any size. The latter is a <productname>PostgreSQL</> extension.
The notations <type>varchar(<replaceable>n</>)</type> and
<type>char(<replaceable>n</>)</type> are aliases for <type>character
varying(<replaceable>n</>)</type> and
<type>character(<replaceable>n</>)</type>, respectively.
<type>character</type> without length specifier is equivalent to
<type>character(1)</type>; if <type>character varying</type> is used
without length specifier, the type accepts strings of any size. The
latter is a <productname>PostgreSQL</> extension.
</para>
<para>
@@ -943,19 +961,18 @@ SELECT b, char_length(b) FROM test2;
<para>
There are two other fixed-length character types in
<productname>PostgreSQL</productname>, shown in <xref linkend="datatype-character-special-table">.
The <type>name</type> type
exists <emphasis>only</emphasis> for storage of internal catalog
names and is not intended for use by the general user. Its length
is currently defined as 64 bytes (63 usable characters plus terminator)
but should be referenced using the constant
<symbol>NAMEDATALEN</symbol>. The length is set at compile time
(and is therefore adjustable for special uses); the default
maximum length may change in a future release. The type
<type>"char"</type> (note the quotes) is different from
<type>char(1)</type> in that it only uses one byte of storage. It
is internally used in the system catalogs as a poor-man's
enumeration type.
<productname>PostgreSQL</productname>, shown in <xref
linkend="datatype-character-special-table">. The <type>name</type>
type exists <emphasis>only</emphasis> for storage of internal
catalog names and is not intended for use by the general user. Its
length is currently defined as 64 bytes (63 usable characters plus
terminator) but should be referenced using the constant
<symbol>NAMEDATALEN</symbol>. The length is set at compile time (and
is therefore adjustable for special uses); the default maximum
length may change in a future release. The type <type>"char"</type>
(note the quotes) is different from <type>char(1)</type> in that it
only uses one byte of storage. It is internally used in the system
catalogs as a poor-man's enumeration type.
</para>
<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
is no explicit bound on precision. The allowed range of
<replaceable>p</replaceable> is from 0 to 6 for the
<type>timestamp</type> and <type>interval</type> types, 0 to 13
for the <type>time</type> types.
<type>timestamp</type> and <type>interval</type> types.
</para>
<note>
@@ -1297,6 +1313,12 @@ SELECT b, char_length(b) FROM test2;
</para>
</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>
Time zones, and time-zone conventions, are influenced by
political decisions, not just earth geometry. Time zones around the
@@ -1468,7 +1490,7 @@ SELECT b, char_length(b) FROM test2;
<para>
The <type>time</type> type can be specified as <type>time</type> or
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.
</para>

View File

@@ -8,7 +8,7 @@
*
*
* 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)
{
#ifdef HAVE_INT64_TIMESTAMP
static const int64 TimeScales[MAX_TIMESTAMP_PRECISION + 1] = {
static const int64 TimeScales[MAX_TIME_PRECISION + 1] = {
INT64CONST(1000000),
INT64CONST(100000),
INT64CONST(10000),
@@ -629,7 +629,7 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
INT64CONST(1)
};
static const int64 TimeOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
static const int64 TimeOffsets[MAX_TIME_PRECISION + 1] = {
INT64CONST(500000),
INT64CONST(50000),
INT64CONST(5000),
@@ -640,14 +640,19 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
};
#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,
10,
100,
1000,
10000,
100000,
1000000
1000000,
10000000,
100000000,
1000000000,
10000000000
};
#endif
@@ -656,7 +661,7 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
/*
* Note: this round-to-nearest code is not completely consistent
* 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
* worth trying to be consistent?
*/

View File

@@ -8,7 +8,7 @@
*
*
* 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
/* chop off trailing zeros... */
/* chop off trailing zeros... but leave at least 2 fractional digits */
while ((*(str + len - 1) == '0')
&& (*(str + len - 3) != '.'))
{
len--;
*(str + len) = '\0';
}
return;
}
@@ -3145,33 +3144,22 @@ EncodeDateOnly(struct tm * tm, int style, char *str)
int
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))
return -1;
#ifndef HAVE_INT64_TIMESTAMP
sec = (tm->tm_sec + fsec);
#endif
sprintf(str, "%02d:%02d", tm->tm_hour, tm->tm_min);
/*
* If we have fractional seconds, then include a decimal point We will
* do up to 6 fractional digits, and we have rounded any inputs to
* eliminate anything to the right of 6 digits anyway. If there are no
* fractional seconds, then do not bother printing a decimal point at
* all. - thomas 2001-09-29
* Print fractional seconds if any. The field widths here should be
* at least equal to the larger of MAX_TIME_PRECISION and
* MAX_TIMESTAMP_PRECISION.
*/
if (fsec != 0)
{
#ifdef HAVE_INT64_TIMESTAMP
sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
sprintf((str + strlen(str)), ".%06d", fsec);
sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
#else
sprintf((str + strlen(str)), ":%013.10f", sec);
sprintf((str + strlen(str)), ":%013.10f", tm->tm_sec + fsec);
#endif
/* chop off trailing pairs of zeros... */
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';
}
else
#ifdef HAVE_INT64_TIMESTAMP
sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
#else
sprintf((str + strlen(str)), ":%02.0f", sec);
#endif
if (tzp != NULL)
{
@@ -3217,20 +3201,12 @@ EncodeDateTime(struct tm * tm, fsec_t fsec, int *tzp, char **tzn, int style, cha
hour,
min;
#ifndef HAVE_INT64_TIMESTAMP
fsec_t sec;
#endif
/*
* 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((tm->tm_mon >= 1) && (tm->tm_mon <= 12));
#ifndef HAVE_INT64_TIMESTAMP
sec = (tm->tm_sec + fsec);
#endif
switch (style)
{
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);
/*
* If we have fractional seconds, then include a decimal point
* We will do up to 6 fractional digits, and we have rounded
* any inputs to eliminate anything to the right of 6 digits
* anyway. If there are no fractional seconds, then do not
* bother printing a decimal point at all. - thomas 2001-09-29
* Print fractional seconds if any. The field widths here should
* be at least equal to MAX_TIMESTAMP_PRECISION.
*
* In float mode, don't print fractional seconds before 1 AD,
* since it's unlikely there's any precision left ...
*/
#ifdef HAVE_INT64_TIMESTAMP
if (fsec != 0)
{
sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
sprintf((str + strlen(str)), ".%06d", fsec);
sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
#else
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
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);
/*
* If we have fractional seconds, then include a decimal point
* We will do up to 6 fractional digits, and we have rounded
* any inputs to eliminate anything to the right of 6 digits
* anyway. If there are no fractional seconds, then do not
* bother printing a decimal point at all. - thomas 2001-09-29
* Print fractional seconds if any. The field widths here should
* be at least equal to MAX_TIMESTAMP_PRECISION.
*
* In float mode, don't print fractional seconds before 1 AD,
* since it's unlikely there's any precision left ...
*/
#ifdef HAVE_INT64_TIMESTAMP
if (fsec != 0)
{
sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
sprintf((str + strlen(str)), ".%06d", fsec);
sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
#else
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
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);
/*
* If we have fractional seconds, then include a decimal point
* We will do up to 6 fractional digits, and we have rounded
* any inputs to eliminate anything to the right of 6 digits
* anyway. If there are no fractional seconds, then do not
* bother printing a decimal point at all. - thomas 2001-09-29
* Print fractional seconds if any. The field widths here should
* be at least equal to MAX_TIMESTAMP_PRECISION.
*
* In float mode, don't print fractional seconds before 1 AD,
* since it's unlikely there's any precision left ...
*/
#ifdef HAVE_INT64_TIMESTAMP
if (fsec != 0)
{
sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
sprintf((str + strlen(str)), ".%06d", fsec);
sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
#else
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
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);
/*
* If we have fractional seconds, then include a decimal point
* We will do up to 6 fractional digits, and we have rounded
* any inputs to eliminate anything to the right of 6 digits
* anyway. If there are no fractional seconds, then do not
* bother printing a decimal point at all. - thomas 2001-09-29
* Print fractional seconds if any. The field widths here should
* be at least equal to MAX_TIMESTAMP_PRECISION.
*
* In float mode, don't print fractional seconds before 1 AD,
* since it's unlikely there's any precision left ...
*/
#ifdef HAVE_INT64_TIMESTAMP
if (fsec != 0)
{
sprintf((str + strlen(str)), ":%02d", tm->tm_sec);
sprintf((str + strlen(str)), ".%06d", fsec);
sprintf((str + strlen(str)), ":%02d.%06d", tm->tm_sec, fsec);
#else
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
TrimTrailingZeros(str);
}

View File

@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* 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
#define MAX_TIME_PRECISION 6
#else
#define MAX_TIME_PRECISION 13
#define MAX_TIME_PRECISION 10
#endif
/*