From: Tom Lane Date: Wed, 29 Jan 2003 01:09:03 +0000 (+0000) Subject: Repair array subscript overrun identified by Yichen Xie. Reduce the X-Git-Tag: REL7_3_2~17 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=726b7f3b3cc067de053f7d7c0aa488e9c6c211f8;p=postgresql 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.) --- diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index c81c0bb970..e4f3f7adab 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -1,5 +1,5 @@ @@ -86,18 +86,18 @@ $Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.106.2.2 2002/11/21 23:31: binary data - - character(n) - char(n) - fixed-length character string - - character varying(n) varchar(n) variable-length character string + + character(n) + char(n) + fixed-length character string + + cidr @@ -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 SQL: bit, bit varying, boolean, char, - character, character varying, + character varying, character, varchar, date, double precision, integer, interval, numeric, decimal, real, @@ -654,10 +654,11 @@ NUMERIC - The serial 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 serial data type is not a true type, but merely + a notational convenience for setting up identifier columns + (similar to the AUTO_INCREMENT property + supported by some other databases). In the current + implementation, specifying CREATE TABLE tablename ( @@ -683,33 +684,50 @@ CREATE TABLE tablename ( not automatic. + + To use a serial column to insert the next value of + the sequence into the table, specify that the serial + column should be assigned the default value. This can be done + either be excluding from the column from the list of columns in + the INSERT statement, or through the use of + the DEFAULT keyword. + + The type names serial and serial4 are equivalent: both create integer columns. The type names bigserial and serial8 work just the same way, except that they create a bigint column. bigserial should be used if you anticipate - the use of more than 231 identifiers over the lifetime of the table. + the use of more than 231 identifiers over the + lifetime of the table. - The sequence created by a serial type is automatically - dropped when - the owning column is dropped, and cannot be dropped otherwise. - (This was not true in PostgreSQL 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 serial type is + automatically dropped when the owning column is dropped, and + cannot be dropped otherwise. (This was not true in + PostgreSQL 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 serial column itself; if + any other columns reference the sequence (perhaps by manually + calling the nextval()) function), they may be broken + if the sequence is removed. Using serial columns in + fashion is considered bad form. - - Prior to PostgreSQL 7.3, serial - implied UNIQUE. This is no longer automatic. If - you wish a serial column to be UNIQUE or a - PRIMARY KEY it must now be specified, same as with - any other data type. - + + + Prior to PostgreSQL 7.3, serial + implied UNIQUE. This is no longer automatic. + If you wish a serial column to be UNIQUE or a + PRIMARY KEY it must now be specified, just as + with any other data type. + + @@ -793,14 +811,14 @@ CREATE TABLE tablename ( - - character(n), char(n) - fixed-length, blank padded - character varying(n), varchar(n) variable-length with limit + + character(n), char(n) + fixed-length, blank padded + text variable unlimited length @@ -817,29 +835,29 @@ CREATE TABLE tablename ( SQL defines two primary character types: - character(n) and character - varying(n), where n is a - positive integer. Both of these types can store strings up to + character varying(n) and + character(n), where n + is a positive integer. Both of these types can store strings up to 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 - SQL standard.) If the string to be stored is - shorter than the declared length, values of type - character will be space-padded; values of type - character varying will simply store the shorter + the string will be truncated to the maximum length. (This somewhat + bizarre exception is required by the SQL + standard.) If the string to be stored is shorter than the declared + length, values of type character will be space-padded; + values of type character varying will simply store the + shorter string. - If one explicitly casts a value to - character(n) or character - varying(n), then an overlength value will - be truncated to n characters without raising an - error. (This too is required by the SQL - standard.) + If one explicitly casts a value to character + varying(n) or + character(n), then an over-length + value will be truncated to n characters without + raising an error. (This too is required by the + SQL standard.) @@ -852,14 +870,14 @@ CREATE TABLE tablename ( - The notations char(n) and - varchar(n) are aliases for - character(n) and character - varying(n), - respectively. character without length specifier is - equivalent to character(1); if character - varying is used without length specifier, the type accepts - strings of any size. The latter is a PostgreSQL extension. + The notations varchar(n) and + char(n) are aliases for character + varying(n) and + character(n), respectively. + character without length specifier is equivalent to + character(1); if character varying is used + without length specifier, the type accepts strings of any size. The + latter is a PostgreSQL extension. @@ -943,19 +961,18 @@ SELECT b, char_length(b) FROM test2; There are two other fixed-length character types in - PostgreSQL, shown in . - The name type - exists only 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 - NAMEDATALEN. 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 - "char" (note the quotes) is different from - char(1) in that it only uses one byte of storage. It - is internally used in the system catalogs as a poor-man's - enumeration type. + PostgreSQL, shown in . The name + type exists only 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 + NAMEDATALEN. 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 "char" + (note the quotes) is different from char(1) in that it + only uses one byte of storage. It is internally used in the system + catalogs as a poor-man's enumeration type. @@ -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 p is from 0 to 6 for the - timestamp and interval types, 0 to 13 - for the time types. + timestamp and interval types. @@ -1297,6 +1313,12 @@ SELECT b, char_length(b) FROM test2; + + For the time types, the allowed range of + p is from 0 to 6 when eight-byte integer + storage is used, or from 0 to 10 when floating-point storage is used. + + 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; The time type can be specified as time or as time without time zone. The optional precision - p should be between 0 and 13, and + p should be between 0 and 6, and defaults to the precision of the input time literal. diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 7f15784320..f9d976ad65 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -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? */ diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index 38655ceb5b..b21c5f217d 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -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); } diff --git a/src/include/utils/date.h b/src/include/utils/date.h index eaaac77c16..4b3edd0b98 100644 --- a/src/include/utils/date.h +++ b/src/include/utils/date.h @@ -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 /*