Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Tighten unit parsing in internal values
authorMichael Paquier <michael@paquier.xyz>
Mon, 28 Aug 2023 05:27:17 +0000 (14:27 +0900)
committerMichael Paquier <michael@paquier.xyz>
Mon, 28 Aug 2023 05:27:17 +0000 (14:27 +0900)
Interval values now generate an error when the user has multiple
consecutive units or a unit without a value.  Previously, it was
possible to specify multiple units consecutively which is contrary to
what the documentation allows, so it was possible to finish with
confusing interval values.

This is a follow-up of the work done in 165d581f146b.

Author: Joseph Koshakow
Reviewed-by: Jacob Champion, Gurjeet Singh, Reid Thompson
Discussion: https://postgr.es/m/CAAvxfHd-yNO+XYnUxL=GaNZ1n+eE0V-oE0+-cC1jdjdU0KS3iw@mail.gmail.com

src/backend/utils/adt/datetime.c
src/test/regress/expected/interval.out
src/test/regress/sql/interval.sql

index 178b3f47803a2b0801eb766d7e6fe62aab60eb82..267dfd37b2e8b9bc63797c69b9ca2e45e6bfde61 100644 (file)
@@ -3278,6 +3278,7 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
 {
    bool        force_negative = false;
    bool        is_before = false;
+   bool        parsing_unit_val = false;
    char       *cp;
    int         fmask = 0,
                tmask,
@@ -3336,6 +3337,7 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
                    itm_in->tm_usec > 0)
                    itm_in->tm_usec = -itm_in->tm_usec;
                type = DTK_DAY;
+               parsing_unit_val = false;
                break;
 
            case DTK_TZ:
@@ -3373,6 +3375,7 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
                     * are reading right to left.
                     */
                    type = DTK_DAY;
+                   parsing_unit_val = false;
                    break;
                }
 
@@ -3562,10 +3565,14 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
                    default:
                        return DTERR_BAD_FORMAT;
                }
+               parsing_unit_val = false;
                break;
 
            case DTK_STRING:
            case DTK_SPECIAL:
+               /* reject consecutive unhandled units */
+               if (parsing_unit_val)
+                   return DTERR_BAD_FORMAT;
                type = DecodeUnits(i, field[i], &uval);
                if (type == IGNORE_DTF)
                    continue;
@@ -3575,6 +3582,7 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
                {
                    case UNITS:
                        type = uval;
+                       parsing_unit_val = true;
                        break;
 
                    case AGO:
@@ -3607,6 +3615,10 @@ DecodeInterval(char **field, int *ftype, int nf, int range,
    if (fmask == 0)
        return DTERR_BAD_FORMAT;
 
+   /* reject if unit appeared and was never handled */
+   if (parsing_unit_val)
+       return DTERR_BAD_FORMAT;
+
    /* finally, AGO negates everything */
    if (is_before)
    {
index 01d43b58687c3c49f51c6c1a44f60aafdaaeae3f..c0ca8e041b1631d152ff450aa75a35fb3d1c95b5 100644 (file)
@@ -1796,3 +1796,12 @@ SELECT INTERVAL '2 minutes ago 5 days';
 ERROR:  invalid input syntax for type interval: "2 minutes ago 5 days"
 LINE 1: SELECT INTERVAL '2 minutes ago 5 days';
                         ^
+-- consecutive and dangling units are not allowed.
+SELECT INTERVAL 'hour 5 months';
+ERROR:  invalid input syntax for type interval: "hour 5 months"
+LINE 1: SELECT INTERVAL 'hour 5 months';
+                        ^
+SELECT INTERVAL '1 year months days 5 hours';
+ERROR:  invalid input syntax for type interval: "1 year months days 5 hours"
+LINE 1: SELECT INTERVAL '1 year months days 5 hours';
+                        ^
index fb1ef3049048516aee383dd5602442e4ae332d47..038fc508d0c039a9bb9b16405d45b20e61ad52cf 100644 (file)
@@ -586,3 +586,7 @@ SELECT extract(epoch from interval '1000000000 days');
 -- "ago" can only appear once at the end of an interval.
 SELECT INTERVAL '42 days 2 seconds ago ago';
 SELECT INTERVAL '2 minutes ago 5 days';
+
+-- consecutive and dangling units are not allowed.
+SELECT INTERVAL 'hour 5 months';
+SELECT INTERVAL '1 year months days 5 hours';