99
99
#include "utils/pg_locale.h"
100
100
101
101
/* ----------
102
- * Routines type
102
+ * Routines flags
103
103
* ----------
104
104
*/
105
- #define DCH_TYPE 1 /* DATE-TIME version */
106
- #define NUM_TYPE 2 /* NUMBER version */
105
+ #define DCH_FLAG 0x1 /* DATE-TIME flag */
106
+ #define NUM_FLAG 0x2 /* NUMBER flag */
107
+ #define STD_FLAG 0x4 /* STANDARD flag */
107
108
108
109
/* ----------
109
110
* KeyWord Index (ascii from position 32 (' ') to 126 (~))
@@ -384,6 +385,7 @@ typedef struct
384
385
{
385
386
FormatNode format [DCH_CACHE_SIZE + 1 ];
386
387
char str [DCH_CACHE_SIZE + 1 ];
388
+ bool std ;
387
389
bool valid ;
388
390
int age ;
389
391
} DCHCacheEntry ;
@@ -1000,11 +1002,12 @@ static const KeySuffix *suff_search(const char *str, const KeySuffix *suf, int t
1000
1002
static bool is_separator_char (const char * str );
1001
1003
static void NUMDesc_prepare (NUMDesc * num , FormatNode * n );
1002
1004
static void parse_format (FormatNode * node , const char * str , const KeyWord * kw ,
1003
- const KeySuffix * suf , const int * index , int ver , NUMDesc * Num );
1005
+ const KeySuffix * suf , const int * index , uint32 flags , NUMDesc * Num );
1004
1006
1005
1007
static void DCH_to_char (FormatNode * node , bool is_interval ,
1006
1008
TmToChar * in , char * out , Oid collid );
1007
- static void DCH_from_char (FormatNode * node , char * in , TmFromChar * out );
1009
+ static void DCH_from_char (FormatNode * node , char * in , TmFromChar * out ,
1010
+ bool std );
1008
1011
1009
1012
#ifdef DEBUG_TO_FROM_CHAR
1010
1013
static void dump_index (const KeyWord * k , const int * index );
@@ -1021,7 +1024,7 @@ static int from_char_parse_int_len(int *dest, char **src, const int len, FormatN
1021
1024
static int from_char_parse_int (int * dest , char * * src , FormatNode * node );
1022
1025
static int seq_search (char * name , const char * const * array , int type , int max , int * len );
1023
1026
static int from_char_seq_search (int * dest , char * * src , const char * const * array , int type , int max , FormatNode * node );
1024
- static void do_to_timestamp (text * date_txt , text * fmt ,
1027
+ static void do_to_timestamp (text * date_txt , text * fmt , bool std ,
1025
1028
struct pg_tm * tm , fsec_t * fsec , int * fprec );
1026
1029
static char * fill_str (char * str , int c , int max );
1027
1030
static FormatNode * NUM_cache (int len , NUMDesc * Num , text * pars_str , bool * shouldFree );
@@ -1033,9 +1036,9 @@ static void NUM_numpart_to_char(NUMProc *Np, int id);
1033
1036
static char * NUM_processor (FormatNode * node , NUMDesc * Num , char * inout ,
1034
1037
char * number , int input_len , int to_char_out_pre_spaces ,
1035
1038
int sign , bool is_to_char , Oid collid );
1036
- static DCHCacheEntry * DCH_cache_getnew (const char * str );
1037
- static DCHCacheEntry * DCH_cache_search (const char * str );
1038
- static DCHCacheEntry * DCH_cache_fetch (const char * str );
1039
+ static DCHCacheEntry * DCH_cache_getnew (const char * str , bool std );
1040
+ static DCHCacheEntry * DCH_cache_search (const char * str , bool std );
1041
+ static DCHCacheEntry * DCH_cache_fetch (const char * str , bool std );
1039
1042
static NUMCacheEntry * NUM_cache_getnew (const char * str );
1040
1043
static NUMCacheEntry * NUM_cache_search (const char * str );
1041
1044
static NUMCacheEntry * NUM_cache_fetch (const char * str );
@@ -1278,7 +1281,7 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
1278
1281
*/
1279
1282
static void
1280
1283
parse_format (FormatNode * node , const char * str , const KeyWord * kw ,
1281
- const KeySuffix * suf , const int * index , int ver , NUMDesc * Num )
1284
+ const KeySuffix * suf , const int * index , uint32 flags , NUMDesc * Num )
1282
1285
{
1283
1286
FormatNode * n ;
1284
1287
@@ -1296,7 +1299,7 @@ parse_format(FormatNode *node, const char *str, const KeyWord *kw,
1296
1299
/*
1297
1300
* Prefix
1298
1301
*/
1299
- if (ver == DCH_TYPE &&
1302
+ if (( flags & DCH_FLAG ) &&
1300
1303
(s = suff_search (str , suf , SUFFTYPE_PREFIX )) != NULL )
1301
1304
{
1302
1305
suffix |= s -> id ;
@@ -1317,13 +1320,13 @@ parse_format(FormatNode *node, const char *str, const KeyWord *kw,
1317
1320
/*
1318
1321
* NUM version: Prepare global NUMDesc struct
1319
1322
*/
1320
- if (ver == NUM_TYPE )
1323
+ if (flags & NUM_FLAG )
1321
1324
NUMDesc_prepare (Num , n );
1322
1325
1323
1326
/*
1324
1327
* Postfix
1325
1328
*/
1326
- if (ver == DCH_TYPE && * str &&
1329
+ if (( flags & DCH_FLAG ) && * str &&
1327
1330
(s = suff_search (str , suf , SUFFTYPE_POSTFIX )) != NULL )
1328
1331
{
1329
1332
n -> suffix |= s -> id ;
@@ -1337,11 +1340,34 @@ parse_format(FormatNode *node, const char *str, const KeyWord *kw,
1337
1340
{
1338
1341
int chlen ;
1339
1342
1340
- /*
1341
- * Process double-quoted literal string, if any
1342
- */
1343
- if (* str == '"' )
1343
+ if (flags & STD_FLAG )
1344
+ {
1345
+ /*
1346
+ * Standard mode, allow only following separators: "-./,':; "
1347
+ */
1348
+ if (strchr ("-./,':; " , * str ) == NULL )
1349
+ ereport (ERROR ,
1350
+ (errcode (ERRCODE_INVALID_DATETIME_FORMAT ),
1351
+ errmsg ("invalid datetime format separator: \"%s\"" ,
1352
+ pnstrdup (str , pg_mblen (str )))));
1353
+
1354
+ if (* str == ' ' )
1355
+ n -> type = NODE_TYPE_SPACE ;
1356
+ else
1357
+ n -> type = NODE_TYPE_SEPARATOR ;
1358
+
1359
+ n -> character [0 ] = * str ;
1360
+ n -> character [1 ] = '\0' ;
1361
+ n -> key = NULL ;
1362
+ n -> suffix = 0 ;
1363
+ n ++ ;
1364
+ str ++ ;
1365
+ }
1366
+ else if (* str == '"' )
1344
1367
{
1368
+ /*
1369
+ * Process double-quoted literal string, if any
1370
+ */
1345
1371
str ++ ;
1346
1372
while (* str )
1347
1373
{
@@ -1373,7 +1399,7 @@ parse_format(FormatNode *node, const char *str, const KeyWord *kw,
1373
1399
str ++ ;
1374
1400
chlen = pg_mblen (str );
1375
1401
1376
- if (ver == DCH_TYPE && is_separator_char (str ))
1402
+ if (( flags & DCH_FLAG ) && is_separator_char (str ))
1377
1403
n -> type = NODE_TYPE_SEPARATOR ;
1378
1404
else if (isspace ((unsigned char ) * str ))
1379
1405
n -> type = NODE_TYPE_SPACE ;
@@ -3060,13 +3086,13 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
3060
3086
* ----------
3061
3087
*/
3062
3088
static void
3063
- DCH_from_char (FormatNode * node , char * in , TmFromChar * out )
3089
+ DCH_from_char (FormatNode * node , char * in , TmFromChar * out , bool std )
3064
3090
{
3065
3091
FormatNode * n ;
3066
3092
char * s ;
3067
3093
int len ,
3068
3094
value ;
3069
- bool fx_mode = false ;
3095
+ bool fx_mode = std ;
3070
3096
3071
3097
/* number of extra skipped characters (more than given in format string) */
3072
3098
int extra_skip = 0 ;
@@ -3089,7 +3115,23 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
3089
3115
3090
3116
if (n -> type == NODE_TYPE_SPACE || n -> type == NODE_TYPE_SEPARATOR )
3091
3117
{
3092
- if (!fx_mode )
3118
+ if (std )
3119
+ {
3120
+ /*
3121
+ * Standard mode requires strict matching between format
3122
+ * string separators/spaces and input string.
3123
+ */
3124
+ Assert (n -> character [0 ] && !n -> character [1 ]);
3125
+
3126
+ if (* s == n -> character [0 ])
3127
+ s ++ ;
3128
+ else
3129
+ ereport (ERROR ,
3130
+ (errcode (ERRCODE_INVALID_DATETIME_FORMAT ),
3131
+ errmsg ("unmatched format separator \"%c\"" ,
3132
+ n -> character [0 ])));
3133
+ }
3134
+ else if (!fx_mode )
3093
3135
{
3094
3136
/*
3095
3137
* In non FX (fixed format) mode one format string space or
@@ -3434,6 +3476,27 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
3434
3476
}
3435
3477
}
3436
3478
}
3479
+
3480
+ /*
3481
+ * Standard parsing mode doesn't allow unmatched format patterns or
3482
+ * trailing characters in the input string.
3483
+ */
3484
+ if (std )
3485
+ {
3486
+ if (n -> type != NODE_TYPE_END )
3487
+ ereport (ERROR ,
3488
+ (errcode (ERRCODE_INVALID_DATETIME_FORMAT ),
3489
+ errmsg ("input string is too short for datetime format" )));
3490
+
3491
+ while (* s != '\0' && isspace ((unsigned char ) * s ))
3492
+ s ++ ;
3493
+
3494
+ if (* s != '\0' )
3495
+ ereport (ERROR ,
3496
+ (errcode (ERRCODE_INVALID_DATETIME_FORMAT ),
3497
+ errmsg ("trailing characters remain in input string after "
3498
+ "datetime format" )));
3499
+ }
3437
3500
}
3438
3501
3439
3502
/*
@@ -3456,7 +3519,7 @@ DCH_prevent_counter_overflow(void)
3456
3519
3457
3520
/* select a DCHCacheEntry to hold the given format picture */
3458
3521
static DCHCacheEntry *
3459
- DCH_cache_getnew (const char * str )
3522
+ DCH_cache_getnew (const char * str , bool std )
3460
3523
{
3461
3524
DCHCacheEntry * ent ;
3462
3525
@@ -3506,6 +3569,7 @@ DCH_cache_getnew(const char *str)
3506
3569
MemoryContextAllocZero (TopMemoryContext , sizeof (DCHCacheEntry ));
3507
3570
ent -> valid = false;
3508
3571
StrNCpy (ent -> str , str , DCH_CACHE_SIZE + 1 );
3572
+ ent -> std = std ;
3509
3573
ent -> age = (++ DCHCounter );
3510
3574
/* caller is expected to fill format, then set valid */
3511
3575
++ n_DCHCache ;
@@ -3515,7 +3579,7 @@ DCH_cache_getnew(const char *str)
3515
3579
3516
3580
/* look for an existing DCHCacheEntry matching the given format picture */
3517
3581
static DCHCacheEntry *
3518
- DCH_cache_search (const char * str )
3582
+ DCH_cache_search (const char * str , bool std )
3519
3583
{
3520
3584
/* Ensure we can advance DCHCounter below */
3521
3585
DCH_prevent_counter_overflow ();
@@ -3524,7 +3588,7 @@ DCH_cache_search(const char *str)
3524
3588
{
3525
3589
DCHCacheEntry * ent = DCHCache [i ];
3526
3590
3527
- if (ent -> valid && strcmp (ent -> str , str ) == 0 )
3591
+ if (ent -> valid && strcmp (ent -> str , str ) == 0 && ent -> std == std )
3528
3592
{
3529
3593
ent -> age = (++ DCHCounter );
3530
3594
return ent ;
@@ -3536,21 +3600,21 @@ DCH_cache_search(const char *str)
3536
3600
3537
3601
/* Find or create a DCHCacheEntry for the given format picture */
3538
3602
static DCHCacheEntry *
3539
- DCH_cache_fetch (const char * str )
3603
+ DCH_cache_fetch (const char * str , bool std )
3540
3604
{
3541
3605
DCHCacheEntry * ent ;
3542
3606
3543
- if ((ent = DCH_cache_search (str )) == NULL )
3607
+ if ((ent = DCH_cache_search (str , std )) == NULL )
3544
3608
{
3545
3609
/*
3546
3610
* Not in the cache, must run parser and save a new format-picture to
3547
3611
* the cache. Do not mark the cache entry valid until parsing
3548
3612
* succeeds.
3549
3613
*/
3550
- ent = DCH_cache_getnew (str );
3614
+ ent = DCH_cache_getnew (str , std );
3551
3615
3552
- parse_format (ent -> format , str , DCH_keywords ,
3553
- DCH_suff , DCH_index , DCH_TYPE , NULL );
3616
+ parse_format (ent -> format , str , DCH_keywords , DCH_suff , DCH_index ,
3617
+ DCH_FLAG | ( std ? STD_FLAG : 0 ) , NULL );
3554
3618
3555
3619
ent -> valid = true;
3556
3620
}
@@ -3595,14 +3659,14 @@ datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval, Oid collid)
3595
3659
format = (FormatNode * ) palloc ((fmt_len + 1 ) * sizeof (FormatNode ));
3596
3660
3597
3661
parse_format (format , fmt_str , DCH_keywords ,
3598
- DCH_suff , DCH_index , DCH_TYPE , NULL );
3662
+ DCH_suff , DCH_index , DCH_FLAG , NULL );
3599
3663
}
3600
3664
else
3601
3665
{
3602
3666
/*
3603
3667
* Use cache buffers
3604
3668
*/
3605
- DCHCacheEntry * ent = DCH_cache_fetch (fmt_str );
3669
+ DCHCacheEntry * ent = DCH_cache_fetch (fmt_str , false );
3606
3670
3607
3671
incache = true;
3608
3672
format = ent -> format ;
@@ -3744,7 +3808,7 @@ to_timestamp(PG_FUNCTION_ARGS)
3744
3808
fsec_t fsec ;
3745
3809
int fprec ;
3746
3810
3747
- do_to_timestamp (date_txt , fmt , & tm , & fsec , & fprec );
3811
+ do_to_timestamp (date_txt , fmt , false, & tm , & fsec , & fprec );
3748
3812
3749
3813
/* Use the specified time zone, if any. */
3750
3814
if (tm .tm_zone )
@@ -3783,7 +3847,7 @@ to_date(PG_FUNCTION_ARGS)
3783
3847
struct pg_tm tm ;
3784
3848
fsec_t fsec ;
3785
3849
3786
- do_to_timestamp (date_txt , fmt , & tm , & fsec , NULL );
3850
+ do_to_timestamp (date_txt , fmt , false, & tm , & fsec , NULL );
3787
3851
3788
3852
/* Prevent overflow in Julian-day routines */
3789
3853
if (!IS_VALID_JULIAN (tm .tm_year , tm .tm_mon , tm .tm_mday ))
@@ -3818,7 +3882,7 @@ to_date(PG_FUNCTION_ARGS)
3818
3882
* struct 'tm' and 'fsec'.
3819
3883
*/
3820
3884
static void
3821
- do_to_timestamp (text * date_txt , text * fmt ,
3885
+ do_to_timestamp (text * date_txt , text * fmt , bool std ,
3822
3886
struct pg_tm * tm , fsec_t * fsec , int * fprec )
3823
3887
{
3824
3888
FormatNode * format ;
@@ -3853,15 +3917,15 @@ do_to_timestamp(text *date_txt, text *fmt,
3853
3917
3854
3918
format = (FormatNode * ) palloc ((fmt_len + 1 ) * sizeof (FormatNode ));
3855
3919
3856
- parse_format (format , fmt_str , DCH_keywords ,
3857
- DCH_suff , DCH_index , DCH_TYPE , NULL );
3920
+ parse_format (format , fmt_str , DCH_keywords , DCH_suff , DCH_index ,
3921
+ DCH_FLAG | ( std ? STD_FLAG : 0 ) , NULL );
3858
3922
}
3859
3923
else
3860
3924
{
3861
3925
/*
3862
3926
* Use cache buffers
3863
3927
*/
3864
- DCHCacheEntry * ent = DCH_cache_fetch (fmt_str );
3928
+ DCHCacheEntry * ent = DCH_cache_fetch (fmt_str , std );
3865
3929
3866
3930
incache = true;
3867
3931
format = ent -> format ;
@@ -3872,7 +3936,7 @@ do_to_timestamp(text *date_txt, text *fmt,
3872
3936
/* dump_index(DCH_keywords, DCH_index); */
3873
3937
#endif
3874
3938
3875
- DCH_from_char (format , date_str , & tmfc );
3939
+ DCH_from_char (format , date_str , & tmfc , std );
3876
3940
3877
3941
pfree (fmt_str );
3878
3942
@@ -4241,7 +4305,7 @@ NUM_cache_fetch(const char *str)
4241
4305
zeroize_NUM (& ent -> Num );
4242
4306
4243
4307
parse_format (ent -> format , str , NUM_keywords ,
4244
- NULL , NUM_index , NUM_TYPE , & ent -> Num );
4308
+ NULL , NUM_index , NUM_FLAG , & ent -> Num );
4245
4309
4246
4310
ent -> valid = true;
4247
4311
}
@@ -4273,7 +4337,7 @@ NUM_cache(int len, NUMDesc *Num, text *pars_str, bool *shouldFree)
4273
4337
zeroize_NUM (Num );
4274
4338
4275
4339
parse_format (format , str , NUM_keywords ,
4276
- NULL , NUM_index , NUM_TYPE , Num );
4340
+ NULL , NUM_index , NUM_FLAG , Num );
4277
4341
}
4278
4342
else
4279
4343
{
0 commit comments