Skip to content

Commit 630684d

Browse files
committed
Improve documentation of ParseDateTime(). Reorder tests to prevent
writing one more value into return arrays than will fit. This is potentially a stack smash, though I do not think it is a problem in current uses of the routine, since a failure return causes elog anyway.
1 parent 9d41073 commit 630684d

File tree

2 files changed

+39
-24
lines changed

2 files changed

+39
-24
lines changed

src/backend/utils/adt/datetime.c

+37-22
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.111 2003/08/05 17:39:19 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.112 2003/08/05 18:30:21 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -699,30 +699,54 @@ TrimTrailingZeros(char *str)
699699

700700

701701
/* ParseDateTime()
702-
* Break string into tokens based on a date/time context.
702+
* Break string into tokens based on a date/time context.
703+
* Returns 0 if successful, -1 if bogus input detected.
704+
*
705+
* timestr - the input string
706+
* lowstr - workspace for field string storage (must be large enough for
707+
* a copy of the input string, including trailing null)
708+
* field[] - pointers to field strings are returned in this array
709+
* ftype[] - field type indicators are returned in this array
710+
* maxfields - dimensions of the above two arrays
711+
* *numfields - set to the actual number of fields detected
712+
*
713+
* The fields extracted from the input are stored as separate, null-terminated
714+
* strings in the workspace at lowstr. Any text is converted to lower case.
715+
*
703716
* Several field types are assigned:
704717
* DTK_NUMBER - digits and (possibly) a decimal point
705718
* DTK_DATE - digits and two delimiters, or digits and text
706719
* DTK_TIME - digits, colon delimiters, and possibly a decimal point
707720
* DTK_STRING - text (no digits)
708721
* DTK_SPECIAL - leading "+" or "-" followed by text
709722
* DTK_TZ - leading "+" or "-" followed by digits
723+
*
710724
* Note that some field types can hold unexpected items:
711725
* DTK_NUMBER can hold date fields (yy.ddd)
712726
* DTK_STRING can hold months (January) and time zones (PST)
713727
* DTK_DATE can hold Posix time zones (GMT-8)
714728
*/
715729
int
716-
ParseDateTime(char *timestr, char *lowstr,
730+
ParseDateTime(const char *timestr, char *lowstr,
717731
char **field, int *ftype, int maxfields, int *numfields)
718732
{
719733
int nf = 0;
720-
char *cp = timestr;
734+
const char *cp = timestr;
721735
char *lp = lowstr;
722736

723737
/* outer loop through fields */
724738
while (*cp != '\0')
725739
{
740+
/* Ignore spaces between fields */
741+
if (isspace((unsigned char) *cp))
742+
{
743+
cp++;
744+
continue;
745+
}
746+
747+
/* Record start of current field */
748+
if (nf >= maxfields)
749+
return -1;
726750
field[nf] = lp;
727751

728752
/* leading digit? then date or time */
@@ -745,32 +769,32 @@ ParseDateTime(char *timestr, char *lowstr,
745769
else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
746770
{
747771
/* save delimiting character to use later */
748-
char *dp = cp;
772+
char delim = *cp;
749773

750774
*lp++ = *cp++;
751775
/* second field is all digits? then no embedded text month */
752776
if (isdigit((unsigned char) *cp))
753777
{
754-
ftype[nf] = ((*dp == '.') ? DTK_NUMBER : DTK_DATE);
778+
ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE);
755779
while (isdigit((unsigned char) *cp))
756780
*lp++ = *cp++;
757781

758782
/*
759783
* insist that the delimiters match to get a
760784
* three-field date.
761785
*/
762-
if (*cp == *dp)
786+
if (*cp == delim)
763787
{
764788
ftype[nf] = DTK_DATE;
765789
*lp++ = *cp++;
766-
while (isdigit((unsigned char) *cp) || (*cp == *dp))
790+
while (isdigit((unsigned char) *cp) || (*cp == delim))
767791
*lp++ = *cp++;
768792
}
769793
}
770794
else
771795
{
772796
ftype[nf] = DTK_DATE;
773-
while (isalnum((unsigned char) *cp) || (*cp == *dp))
797+
while (isalnum((unsigned char) *cp) || (*cp == delim))
774798
*lp++ = tolower((unsigned char) *cp++);
775799
}
776800
}
@@ -809,20 +833,14 @@ ParseDateTime(char *timestr, char *lowstr,
809833
*/
810834
if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
811835
{
812-
char *dp = cp;
836+
char delim = *cp;
813837

814838
ftype[nf] = DTK_DATE;
815839
*lp++ = *cp++;
816-
while (isdigit((unsigned char) *cp) || (*cp == *dp))
840+
while (isdigit((unsigned char) *cp) || (*cp == delim))
817841
*lp++ = *cp++;
818842
}
819843
}
820-
/* skip leading spaces */
821-
else if (isspace((unsigned char) *cp))
822-
{
823-
cp++;
824-
continue;
825-
}
826844
/* sign? then special or numeric timezone */
827845
else if ((*cp == '+') || (*cp == '-'))
828846
{
@@ -851,12 +869,11 @@ ParseDateTime(char *timestr, char *lowstr,
851869
else
852870
return -1;
853871
}
854-
/* ignore punctuation but use as delimiter */
872+
/* ignore other punctuation but use as delimiter */
855873
else if (ispunct((unsigned char) *cp))
856874
{
857875
cp++;
858876
continue;
859-
860877
}
861878
/* otherwise, something is not right... */
862879
else
@@ -865,14 +882,12 @@ ParseDateTime(char *timestr, char *lowstr,
865882
/* force in a delimiter after each field */
866883
*lp++ = '\0';
867884
nf++;
868-
if (nf > MAXDATEFIELDS)
869-
return -1;
870885
}
871886

872887
*numfields = nf;
873888

874889
return 0;
875-
} /* ParseDateTime() */
890+
}
876891

877892

878893
/* DecodeDateTime()

src/include/utils/datetime.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
1010
* Portions Copyright (c) 1994, Regents of the University of California
1111
*
12-
* $Id: datetime.h,v 1.43 2003/08/04 02:40:15 momjian Exp $
12+
* $Id: datetime.h,v 1.44 2003/08/05 18:30:21 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -277,7 +277,7 @@ extern void GetCurrentTimeUsec(struct tm * tm, fsec_t *fsec, int *tzp);
277277
extern void j2date(int jd, int *year, int *month, int *day);
278278
extern int date2j(int year, int month, int day);
279279

280-
extern int ParseDateTime(char *timestr, char *lowstr,
280+
extern int ParseDateTime(const char *timestr, char *lowstr,
281281
char **field, int *ftype,
282282
int maxfields, int *numfields);
283283
extern int DecodeDateTime(char **field, int *ftype,

0 commit comments

Comments
 (0)