|
|
@@ -98,6 +98,43 @@
|
|
|
|
#include "utils/numeric.h"
|
|
|
|
#include "utils/numeric.h"
|
|
|
|
#include "utils/pg_locale.h"
|
|
|
|
#include "utils/pg_locale.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
|
|
|
|
* Convenience macros for error handling
|
|
|
|
|
|
|
|
* ----------
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* Two macros below help to handle errors in functions that take
|
|
|
|
|
|
|
|
* 'bool *have_error' argument. When this argument is not NULL, it's expected
|
|
|
|
|
|
|
|
* that function will suppress ereports when possible. Instead it should
|
|
|
|
|
|
|
|
* return some default value and set *have_error flag.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* RETURN_ERROR() macro intended to wrap ereport() calls. When have_error
|
|
|
|
|
|
|
|
* function argument is not NULL, then instead of ereport'ing we set
|
|
|
|
|
|
|
|
* *have_error flag and go to on_error label. It's supposed that jump
|
|
|
|
|
|
|
|
* resources will be freed and some 'default' value returned.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* CHECK_ERROR() jumps on_error label when *have_error flag is defined and set.
|
|
|
|
|
|
|
|
* It's supposed to be used for immediate exit from the function on error
|
|
|
|
|
|
|
|
* after call of another function with 'bool *have_error' argument.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define RETURN_ERROR(throw_error) \
|
|
|
|
|
|
|
|
do { \
|
|
|
|
|
|
|
|
if (have_error) \
|
|
|
|
|
|
|
|
{ \
|
|
|
|
|
|
|
|
*have_error = true; \
|
|
|
|
|
|
|
|
goto on_error; \
|
|
|
|
|
|
|
|
} \
|
|
|
|
|
|
|
|
else \
|
|
|
|
|
|
|
|
{ \
|
|
|
|
|
|
|
|
throw_error; \
|
|
|
|
|
|
|
|
} \
|
|
|
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define CHECK_ERROR \
|
|
|
|
|
|
|
|
do { \
|
|
|
|
|
|
|
|
if (have_error && *have_error) \
|
|
|
|
|
|
|
|
goto on_error; \
|
|
|
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
/* ----------
|
|
|
|
* Routines flags
|
|
|
|
* Routines flags
|
|
|
|
* ----------
|
|
|
|
* ----------
|
|
|
@@ -1012,7 +1049,7 @@ static void parse_format(FormatNode *node, const char *str, const KeyWord *kw,
|
|
|
|
static void DCH_to_char(FormatNode *node, bool is_interval,
|
|
|
|
static void DCH_to_char(FormatNode *node, bool is_interval,
|
|
|
|
TmToChar *in, char *out, Oid collid);
|
|
|
|
TmToChar *in, char *out, Oid collid);
|
|
|
|
static void DCH_from_char(FormatNode *node, char *in, TmFromChar *out,
|
|
|
|
static void DCH_from_char(FormatNode *node, char *in, TmFromChar *out,
|
|
|
|
bool std);
|
|
|
|
bool std, bool *have_error);
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG_TO_FROM_CHAR
|
|
|
|
#ifdef DEBUG_TO_FROM_CHAR
|
|
|
|
static void dump_index(const KeyWord *k, const int *index);
|
|
|
|
static void dump_index(const KeyWord *k, const int *index);
|
|
|
@@ -1023,15 +1060,21 @@ static const char *get_th(char *num, int type);
|
|
|
|
static char *str_numth(char *dest, char *num, int type);
|
|
|
|
static char *str_numth(char *dest, char *num, int type);
|
|
|
|
static int adjust_partial_year_to_2020(int year);
|
|
|
|
static int adjust_partial_year_to_2020(int year);
|
|
|
|
static int strspace_len(char *str);
|
|
|
|
static int strspace_len(char *str);
|
|
|
|
static void from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode);
|
|
|
|
static void from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode,
|
|
|
|
static void from_char_set_int(int *dest, const int value, const FormatNode *node);
|
|
|
|
bool *have_error);
|
|
|
|
static int from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node);
|
|
|
|
static void from_char_set_int(int *dest, const int value, const FormatNode *node,
|
|
|
|
static int from_char_parse_int(int *dest, char **src, FormatNode *node);
|
|
|
|
bool *have_error);
|
|
|
|
|
|
|
|
static int from_char_parse_int_len(int *dest, char **src, const int len,
|
|
|
|
|
|
|
|
FormatNode *node, bool *have_error);
|
|
|
|
|
|
|
|
static int from_char_parse_int(int *dest, char **src, FormatNode *node,
|
|
|
|
|
|
|
|
bool *have_error);
|
|
|
|
static int seq_search(char *name, const char *const *array, int type, int max, int *len);
|
|
|
|
static int seq_search(char *name, const char *const *array, int type, int max, int *len);
|
|
|
|
static int from_char_seq_search(int *dest, char **src, const char *const *array, int type, int max, FormatNode *node);
|
|
|
|
static int from_char_seq_search(int *dest, char **src,
|
|
|
|
|
|
|
|
const char *const *array, int type, int max,
|
|
|
|
|
|
|
|
FormatNode *node, bool *have_error);
|
|
|
|
static void do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
static void do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
struct pg_tm *tm, fsec_t *fsec, int *fprec,
|
|
|
|
struct pg_tm *tm, fsec_t *fsec, int *fprec,
|
|
|
|
uint32 *flags);
|
|
|
|
uint32 *flags, bool *have_error);
|
|
|
|
static char *fill_str(char *str, int c, int max);
|
|
|
|
static char *fill_str(char *str, int c, int max);
|
|
|
|
static FormatNode *NUM_cache(int len, NUMDesc *Num, text *pars_str, bool *shouldFree);
|
|
|
|
static FormatNode *NUM_cache(int len, NUMDesc *Num, text *pars_str, bool *shouldFree);
|
|
|
|
static char *int_to_roman(int number);
|
|
|
|
static char *int_to_roman(int number);
|
|
|
@@ -2233,21 +2276,26 @@ strspace_len(char *str)
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Puke if the date mode has already been set, and the caller attempts to set
|
|
|
|
* Puke if the date mode has already been set, and the caller attempts to set
|
|
|
|
* it to a conflicting mode.
|
|
|
|
* it to a conflicting mode.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
static void
|
|
|
|
from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode)
|
|
|
|
from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode, bool *have_error)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (mode != FROM_CHAR_DATE_NONE)
|
|
|
|
if (mode != FROM_CHAR_DATE_NONE)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (tmfc->mode == FROM_CHAR_DATE_NONE)
|
|
|
|
if (tmfc->mode == FROM_CHAR_DATE_NONE)
|
|
|
|
tmfc->mode = mode;
|
|
|
|
tmfc->mode = mode;
|
|
|
|
else if (tmfc->mode != mode)
|
|
|
|
else if (tmfc->mode != mode)
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("invalid combination of date conventions"),
|
|
|
|
errmsg("invalid combination of date conventions"),
|
|
|
|
errhint("Do not mix Gregorian and ISO week date "
|
|
|
|
errhint("Do not mix Gregorian and ISO week date "
|
|
|
|
"conventions in a formatting template.")));
|
|
|
|
"conventions in a formatting template."))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
on_error:
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
@@ -2255,18 +2303,25 @@ from_char_set_mode(TmFromChar *tmfc, const FromCharDateMode mode)
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Puke if the destination integer has previously been set to some other
|
|
|
|
* Puke if the destination integer has previously been set to some other
|
|
|
|
* non-zero value.
|
|
|
|
* non-zero value.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
static void
|
|
|
|
from_char_set_int(int *dest, const int value, const FormatNode *node)
|
|
|
|
from_char_set_int(int *dest, const int value, const FormatNode *node,
|
|
|
|
|
|
|
|
bool *have_error)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (*dest != 0 && *dest != value)
|
|
|
|
if (*dest != 0 && *dest != value)
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("conflicting values for \"%s\" field in formatting string",
|
|
|
|
errmsg("conflicting values for \"%s\" field in "
|
|
|
|
|
|
|
|
"formatting string",
|
|
|
|
node->key->name),
|
|
|
|
node->key->name),
|
|
|
|
errdetail("This value contradicts a previous setting for "
|
|
|
|
errdetail("This value contradicts a previous setting "
|
|
|
|
"the same field type.")));
|
|
|
|
"for the same field type."))));
|
|
|
|
*dest = value;
|
|
|
|
*dest = value;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
on_error:
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
@@ -2288,9 +2343,13 @@ from_char_set_int(int *dest, const int value, const FormatNode *node)
|
|
|
|
* Note that from_char_parse_int() provides a more convenient wrapper where
|
|
|
|
* Note that from_char_parse_int() provides a more convenient wrapper where
|
|
|
|
* the length of the field is the same as the length of the format keyword (as
|
|
|
|
* the length of the field is the same as the length of the format keyword (as
|
|
|
|
* with DD and MI).
|
|
|
|
* with DD and MI).
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
|
|
|
|
|
|
|
|
* and -1 is returned.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node)
|
|
|
|
from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node,
|
|
|
|
|
|
|
|
bool *have_error)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
long result;
|
|
|
|
long result;
|
|
|
|
char copy[DCH_MAX_ITEM_SIZ + 1];
|
|
|
|
char copy[DCH_MAX_ITEM_SIZ + 1];
|
|
|
@@ -2323,51 +2382,60 @@ from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node)
|
|
|
|
char *last;
|
|
|
|
char *last;
|
|
|
|
|
|
|
|
|
|
|
|
if (used < len)
|
|
|
|
if (used < len)
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("source string too short for \"%s\" formatting field",
|
|
|
|
errmsg("source string too short for \"%s\" "
|
|
|
|
|
|
|
|
"formatting field",
|
|
|
|
node->key->name),
|
|
|
|
node->key->name),
|
|
|
|
errdetail("Field requires %d characters, but only %d "
|
|
|
|
errdetail("Field requires %d characters, "
|
|
|
|
"remain.",
|
|
|
|
"but only %d remain.",
|
|
|
|
len, used),
|
|
|
|
len, used),
|
|
|
|
errhint("If your source string is not fixed-width, try "
|
|
|
|
errhint("If your source string is not fixed-width, "
|
|
|
|
"using the \"FM\" modifier.")));
|
|
|
|
"try using the \"FM\" modifier."))));
|
|
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
errno = 0;
|
|
|
|
result = strtol(copy, &last, 10);
|
|
|
|
result = strtol(copy, &last, 10);
|
|
|
|
used = last - copy;
|
|
|
|
used = last - copy;
|
|
|
|
|
|
|
|
|
|
|
|
if (used > 0 && used < len)
|
|
|
|
if (used > 0 && used < len)
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("invalid value \"%s\" for \"%s\"",
|
|
|
|
errmsg("invalid value \"%s\" for \"%s\"",
|
|
|
|
copy, node->key->name),
|
|
|
|
copy, node->key->name),
|
|
|
|
errdetail("Field requires %d characters, but only %d "
|
|
|
|
errdetail("Field requires %d characters, "
|
|
|
|
"could be parsed.", len, used),
|
|
|
|
"but only %d could be parsed.",
|
|
|
|
errhint("If your source string is not fixed-width, try "
|
|
|
|
len, used),
|
|
|
|
"using the \"FM\" modifier.")));
|
|
|
|
errhint("If your source string is not fixed-width, "
|
|
|
|
|
|
|
|
"try using the \"FM\" modifier."))));
|
|
|
|
|
|
|
|
|
|
|
|
*src += used;
|
|
|
|
*src += used;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (*src == init)
|
|
|
|
if (*src == init)
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("invalid value \"%s\" for \"%s\"",
|
|
|
|
errmsg("invalid value \"%s\" for \"%s\"",
|
|
|
|
copy, node->key->name),
|
|
|
|
copy, node->key->name),
|
|
|
|
errdetail("Value must be an integer.")));
|
|
|
|
errdetail("Value must be an integer."))));
|
|
|
|
|
|
|
|
|
|
|
|
if (errno == ERANGE || result < INT_MIN || result > INT_MAX)
|
|
|
|
if (errno == ERANGE || result < INT_MIN || result > INT_MAX)
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
errmsg("value for \"%s\" in source string is out of range",
|
|
|
|
errmsg("value for \"%s\" in source string is out of range",
|
|
|
|
node->key->name),
|
|
|
|
node->key->name),
|
|
|
|
errdetail("Value must be in the range %d to %d.",
|
|
|
|
errdetail("Value must be in the range %d to %d.",
|
|
|
|
INT_MIN, INT_MAX)));
|
|
|
|
INT_MIN, INT_MAX))));
|
|
|
|
|
|
|
|
|
|
|
|
if (dest != NULL)
|
|
|
|
if (dest != NULL)
|
|
|
|
from_char_set_int(dest, (int) result, node);
|
|
|
|
{
|
|
|
|
|
|
|
|
from_char_set_int(dest, (int) result, node, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return *src - init;
|
|
|
|
return *src - init;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
on_error:
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
@@ -2380,9 +2448,9 @@ from_char_parse_int_len(int *dest, char **src, const int len, FormatNode *node)
|
|
|
|
* required length explicitly.
|
|
|
|
* required length explicitly.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
from_char_parse_int(int *dest, char **src, FormatNode *node)
|
|
|
|
from_char_parse_int(int *dest, char **src, FormatNode *node, bool *have_error)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return from_char_parse_int_len(dest, src, node->key->len, node);
|
|
|
|
return from_char_parse_int_len(dest, src, node->key->len, node, have_error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
/* ----------
|
|
|
@@ -2465,11 +2533,12 @@ seq_search(char *name, const char *const *array, int type, int max, int *len)
|
|
|
|
* pointed to by 'dest', advance 'src' to the end of the part of the string
|
|
|
|
* pointed to by 'dest', advance 'src' to the end of the part of the string
|
|
|
|
* which matched, and return the number of characters consumed.
|
|
|
|
* which matched, and return the number of characters consumed.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* If the string doesn't match, throw an error.
|
|
|
|
* If the string doesn't match, throw an error if 'have_error' is NULL,
|
|
|
|
|
|
|
|
* otherwise set '*have_error' and return -1.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
from_char_seq_search(int *dest, char **src, const char *const *array, int type, int max,
|
|
|
|
from_char_seq_search(int *dest, char **src, const char *const *array, int type,
|
|
|
|
FormatNode *node)
|
|
|
|
int max, FormatNode *node, bool *have_error)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
|
@@ -2481,15 +2550,18 @@ from_char_seq_search(int *dest, char **src, const char *const *array, int type,
|
|
|
|
Assert(max <= DCH_MAX_ITEM_SIZ);
|
|
|
|
Assert(max <= DCH_MAX_ITEM_SIZ);
|
|
|
|
strlcpy(copy, *src, max + 1);
|
|
|
|
strlcpy(copy, *src, max + 1);
|
|
|
|
|
|
|
|
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("invalid value \"%s\" for \"%s\"",
|
|
|
|
errmsg("invalid value \"%s\" for \"%s\"",
|
|
|
|
copy, node->key->name),
|
|
|
|
copy, node->key->name),
|
|
|
|
errdetail("The given value did not match any of the allowed "
|
|
|
|
errdetail("The given value did not match any of "
|
|
|
|
"values for this field.")));
|
|
|
|
"the allowed values for this field."))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*src += len;
|
|
|
|
*src += len;
|
|
|
|
return len;
|
|
|
|
return len;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
on_error:
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------
|
|
|
|
/* ----------
|
|
|
@@ -3089,10 +3161,13 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Note: we currently don't have any to_interval() function, so there
|
|
|
|
* Note: we currently don't have any to_interval() function, so there
|
|
|
|
* is no need here for INVALID_FOR_INTERVAL checks.
|
|
|
|
* is no need here for INVALID_FOR_INTERVAL checks.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
|
|
|
|
* ----------
|
|
|
|
* ----------
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
static void
|
|
|
|
DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std,
|
|
|
|
|
|
|
|
bool *have_error)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
FormatNode *n;
|
|
|
|
FormatNode *n;
|
|
|
|
char *s;
|
|
|
|
char *s;
|
|
|
@@ -3132,10 +3207,10 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
if (*s == n->character[0])
|
|
|
|
if (*s == n->character[0])
|
|
|
|
s++;
|
|
|
|
s++;
|
|
|
|
else
|
|
|
|
else
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("unmatched format separator \"%c\"",
|
|
|
|
errmsg("unmatched format separator \"%c\"",
|
|
|
|
n->character[0])));
|
|
|
|
n->character[0]))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!fx_mode)
|
|
|
|
else if (!fx_mode)
|
|
|
|
{
|
|
|
|
{
|
|
|
@@ -3191,7 +3266,8 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
from_char_set_mode(out, n->key->date_mode);
|
|
|
|
from_char_set_mode(out, n->key->date_mode, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
switch (n->key->id)
|
|
|
|
switch (n->key->id)
|
|
|
|
{
|
|
|
|
{
|
|
|
@@ -3203,8 +3279,10 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
case DCH_a_m:
|
|
|
|
case DCH_a_m:
|
|
|
|
case DCH_p_m:
|
|
|
|
case DCH_p_m:
|
|
|
|
from_char_seq_search(&value, &s, ampm_strings_long,
|
|
|
|
from_char_seq_search(&value, &s, ampm_strings_long,
|
|
|
|
ALL_UPPER, n->key->len, n);
|
|
|
|
ALL_UPPER, n->key->len, n, have_error);
|
|
|
|
from_char_set_int(&out->pm, value % 2, n);
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
from_char_set_int(&out->pm, value % 2, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
out->clock = CLOCK_12_HOUR;
|
|
|
|
out->clock = CLOCK_12_HOUR;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_AM:
|
|
|
|
case DCH_AM:
|
|
|
@@ -3212,30 +3290,37 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
case DCH_am:
|
|
|
|
case DCH_am:
|
|
|
|
case DCH_pm:
|
|
|
|
case DCH_pm:
|
|
|
|
from_char_seq_search(&value, &s, ampm_strings,
|
|
|
|
from_char_seq_search(&value, &s, ampm_strings,
|
|
|
|
ALL_UPPER, n->key->len, n);
|
|
|
|
ALL_UPPER, n->key->len, n, have_error);
|
|
|
|
from_char_set_int(&out->pm, value % 2, n);
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
from_char_set_int(&out->pm, value % 2, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
out->clock = CLOCK_12_HOUR;
|
|
|
|
out->clock = CLOCK_12_HOUR;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_HH:
|
|
|
|
case DCH_HH:
|
|
|
|
case DCH_HH12:
|
|
|
|
case DCH_HH12:
|
|
|
|
from_char_parse_int_len(&out->hh, &s, 2, n);
|
|
|
|
from_char_parse_int_len(&out->hh, &s, 2, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
out->clock = CLOCK_12_HOUR;
|
|
|
|
out->clock = CLOCK_12_HOUR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_HH24:
|
|
|
|
case DCH_HH24:
|
|
|
|
from_char_parse_int_len(&out->hh, &s, 2, n);
|
|
|
|
from_char_parse_int_len(&out->hh, &s, 2, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_MI:
|
|
|
|
case DCH_MI:
|
|
|
|
from_char_parse_int(&out->mi, &s, n);
|
|
|
|
from_char_parse_int(&out->mi, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_SS:
|
|
|
|
case DCH_SS:
|
|
|
|
from_char_parse_int(&out->ss, &s, n);
|
|
|
|
from_char_parse_int(&out->ss, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_MS: /* millisecond */
|
|
|
|
case DCH_MS: /* millisecond */
|
|
|
|
len = from_char_parse_int_len(&out->ms, &s, 3, n);
|
|
|
|
len = from_char_parse_int_len(&out->ms, &s, 3, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25
|
|
|
|
* 25 is 0.25 and 250 is 0.25 too; 025 is 0.025 and not 0.25
|
|
|
@@ -3256,7 +3341,8 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
case DCH_US: /* microsecond */
|
|
|
|
case DCH_US: /* microsecond */
|
|
|
|
len = from_char_parse_int_len(&out->us, &s,
|
|
|
|
len = from_char_parse_int_len(&out->us, &s,
|
|
|
|
n->key->id == DCH_US ? 6 :
|
|
|
|
n->key->id == DCH_US ? 6 :
|
|
|
|
out->ff, n);
|
|
|
|
out->ff, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
out->us *= len == 1 ? 100000 :
|
|
|
|
out->us *= len == 1 ? 100000 :
|
|
|
|
len == 2 ? 10000 :
|
|
|
|
len == 2 ? 10000 :
|
|
|
@@ -3267,16 +3353,18 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_SSSS:
|
|
|
|
case DCH_SSSS:
|
|
|
|
from_char_parse_int(&out->ssss, &s, n);
|
|
|
|
from_char_parse_int(&out->ssss, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_tz:
|
|
|
|
case DCH_tz:
|
|
|
|
case DCH_TZ:
|
|
|
|
case DCH_TZ:
|
|
|
|
case DCH_OF:
|
|
|
|
case DCH_OF:
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("formatting field \"%s\" is only supported in to_char",
|
|
|
|
errmsg("formatting field \"%s\" is only supported in to_char",
|
|
|
|
n->key->name)));
|
|
|
|
n->key->name))));
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_TZH:
|
|
|
|
case DCH_TZH:
|
|
|
|
|
|
|
|
|
|
|
@@ -3300,82 +3388,102 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
out->tzsign = +1;
|
|
|
|
out->tzsign = +1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
from_char_parse_int_len(&out->tzh, &s, 2, n);
|
|
|
|
from_char_parse_int_len(&out->tzh, &s, 2, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_TZM:
|
|
|
|
case DCH_TZM:
|
|
|
|
/* assign positive timezone sign if TZH was not seen before */
|
|
|
|
/* assign positive timezone sign if TZH was not seen before */
|
|
|
|
if (!out->tzsign)
|
|
|
|
if (!out->tzsign)
|
|
|
|
out->tzsign = +1;
|
|
|
|
out->tzsign = +1;
|
|
|
|
from_char_parse_int_len(&out->tzm, &s, 2, n);
|
|
|
|
from_char_parse_int_len(&out->tzm, &s, 2, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_A_D:
|
|
|
|
case DCH_A_D:
|
|
|
|
case DCH_B_C:
|
|
|
|
case DCH_B_C:
|
|
|
|
case DCH_a_d:
|
|
|
|
case DCH_a_d:
|
|
|
|
case DCH_b_c:
|
|
|
|
case DCH_b_c:
|
|
|
|
from_char_seq_search(&value, &s, adbc_strings_long,
|
|
|
|
from_char_seq_search(&value, &s, adbc_strings_long,
|
|
|
|
ALL_UPPER, n->key->len, n);
|
|
|
|
ALL_UPPER, n->key->len, n, have_error);
|
|
|
|
from_char_set_int(&out->bc, value % 2, n);
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
from_char_set_int(&out->bc, value % 2, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_AD:
|
|
|
|
case DCH_AD:
|
|
|
|
case DCH_BC:
|
|
|
|
case DCH_BC:
|
|
|
|
case DCH_ad:
|
|
|
|
case DCH_ad:
|
|
|
|
case DCH_bc:
|
|
|
|
case DCH_bc:
|
|
|
|
from_char_seq_search(&value, &s, adbc_strings,
|
|
|
|
from_char_seq_search(&value, &s, adbc_strings,
|
|
|
|
ALL_UPPER, n->key->len, n);
|
|
|
|
ALL_UPPER, n->key->len, n, have_error);
|
|
|
|
from_char_set_int(&out->bc, value % 2, n);
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
from_char_set_int(&out->bc, value % 2, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_MONTH:
|
|
|
|
case DCH_MONTH:
|
|
|
|
case DCH_Month:
|
|
|
|
case DCH_Month:
|
|
|
|
case DCH_month:
|
|
|
|
case DCH_month:
|
|
|
|
from_char_seq_search(&value, &s, months_full, ONE_UPPER,
|
|
|
|
from_char_seq_search(&value, &s, months_full, ONE_UPPER,
|
|
|
|
MAX_MONTH_LEN, n);
|
|
|
|
MAX_MONTH_LEN, n, have_error);
|
|
|
|
from_char_set_int(&out->mm, value + 1, n);
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
from_char_set_int(&out->mm, value + 1, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_MON:
|
|
|
|
case DCH_MON:
|
|
|
|
case DCH_Mon:
|
|
|
|
case DCH_Mon:
|
|
|
|
case DCH_mon:
|
|
|
|
case DCH_mon:
|
|
|
|
from_char_seq_search(&value, &s, months, ONE_UPPER,
|
|
|
|
from_char_seq_search(&value, &s, months, ONE_UPPER,
|
|
|
|
MAX_MON_LEN, n);
|
|
|
|
MAX_MON_LEN, n, have_error);
|
|
|
|
from_char_set_int(&out->mm, value + 1, n);
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
from_char_set_int(&out->mm, value + 1, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_MM:
|
|
|
|
case DCH_MM:
|
|
|
|
from_char_parse_int(&out->mm, &s, n);
|
|
|
|
from_char_parse_int(&out->mm, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_DAY:
|
|
|
|
case DCH_DAY:
|
|
|
|
case DCH_Day:
|
|
|
|
case DCH_Day:
|
|
|
|
case DCH_day:
|
|
|
|
case DCH_day:
|
|
|
|
from_char_seq_search(&value, &s, days, ONE_UPPER,
|
|
|
|
from_char_seq_search(&value, &s, days, ONE_UPPER,
|
|
|
|
MAX_DAY_LEN, n);
|
|
|
|
MAX_DAY_LEN, n, have_error);
|
|
|
|
from_char_set_int(&out->d, value, n);
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
from_char_set_int(&out->d, value, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
out->d++;
|
|
|
|
out->d++;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_DY:
|
|
|
|
case DCH_DY:
|
|
|
|
case DCH_Dy:
|
|
|
|
case DCH_Dy:
|
|
|
|
case DCH_dy:
|
|
|
|
case DCH_dy:
|
|
|
|
from_char_seq_search(&value, &s, days, ONE_UPPER,
|
|
|
|
from_char_seq_search(&value, &s, days, ONE_UPPER,
|
|
|
|
MAX_DY_LEN, n);
|
|
|
|
MAX_DY_LEN, n, have_error);
|
|
|
|
from_char_set_int(&out->d, value, n);
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
from_char_set_int(&out->d, value, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
out->d++;
|
|
|
|
out->d++;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_DDD:
|
|
|
|
case DCH_DDD:
|
|
|
|
from_char_parse_int(&out->ddd, &s, n);
|
|
|
|
from_char_parse_int(&out->ddd, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_IDDD:
|
|
|
|
case DCH_IDDD:
|
|
|
|
from_char_parse_int_len(&out->ddd, &s, 3, n);
|
|
|
|
from_char_parse_int_len(&out->ddd, &s, 3, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_DD:
|
|
|
|
case DCH_DD:
|
|
|
|
from_char_parse_int(&out->dd, &s, n);
|
|
|
|
from_char_parse_int(&out->dd, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_D:
|
|
|
|
case DCH_D:
|
|
|
|
from_char_parse_int(&out->d, &s, n);
|
|
|
|
from_char_parse_int(&out->d, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_ID:
|
|
|
|
case DCH_ID:
|
|
|
|
from_char_parse_int_len(&out->d, &s, 1, n);
|
|
|
|
from_char_parse_int_len(&out->d, &s, 1, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
/* Shift numbering to match Gregorian where Sunday = 1 */
|
|
|
|
/* Shift numbering to match Gregorian where Sunday = 1 */
|
|
|
|
if (++out->d > 7)
|
|
|
|
if (++out->d > 7)
|
|
|
|
out->d = 1;
|
|
|
|
out->d = 1;
|
|
|
@@ -3383,7 +3491,8 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_WW:
|
|
|
|
case DCH_WW:
|
|
|
|
case DCH_IW:
|
|
|
|
case DCH_IW:
|
|
|
|
from_char_parse_int(&out->ww, &s, n);
|
|
|
|
from_char_parse_int(&out->ww, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_Q:
|
|
|
|
case DCH_Q:
|
|
|
@@ -3398,11 +3507,13 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
* We still parse the source string for an integer, but it
|
|
|
|
* We still parse the source string for an integer, but it
|
|
|
|
* isn't stored anywhere in 'out'.
|
|
|
|
* isn't stored anywhere in 'out'.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
from_char_parse_int((int *) NULL, &s, n);
|
|
|
|
from_char_parse_int((int *) NULL, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_CC:
|
|
|
|
case DCH_CC:
|
|
|
|
from_char_parse_int(&out->cc, &s, n);
|
|
|
|
from_char_parse_int(&out->cc, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_Y_YYY:
|
|
|
|
case DCH_Y_YYY:
|
|
|
@@ -3414,11 +3525,12 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
|
|
|
|
|
|
|
|
matched = sscanf(s, "%d,%03d%n", &millennia, &years, &nch);
|
|
|
|
matched = sscanf(s, "%d,%03d%n", &millennia, &years, &nch);
|
|
|
|
if (matched < 2)
|
|
|
|
if (matched < 2)
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("invalid input string for \"Y,YYY\"")));
|
|
|
|
errmsg("invalid input string for \"Y,YYY\""))));
|
|
|
|
years += (millennia * 1000);
|
|
|
|
years += (millennia * 1000);
|
|
|
|
from_char_set_int(&out->year, years, n);
|
|
|
|
from_char_set_int(&out->year, years, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
out->yysz = 4;
|
|
|
|
out->yysz = 4;
|
|
|
|
s += nch;
|
|
|
|
s += nch;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
@@ -3426,47 +3538,62 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_YYYY:
|
|
|
|
case DCH_YYYY:
|
|
|
|
case DCH_IYYY:
|
|
|
|
case DCH_IYYY:
|
|
|
|
from_char_parse_int(&out->year, &s, n);
|
|
|
|
from_char_parse_int(&out->year, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
out->yysz = 4;
|
|
|
|
out->yysz = 4;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_YYY:
|
|
|
|
case DCH_YYY:
|
|
|
|
case DCH_IYY:
|
|
|
|
case DCH_IYY:
|
|
|
|
if (from_char_parse_int(&out->year, &s, n) < 4)
|
|
|
|
len = from_char_parse_int(&out->year, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
if (len < 4)
|
|
|
|
out->year = adjust_partial_year_to_2020(out->year);
|
|
|
|
out->year = adjust_partial_year_to_2020(out->year);
|
|
|
|
out->yysz = 3;
|
|
|
|
out->yysz = 3;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_YY:
|
|
|
|
case DCH_YY:
|
|
|
|
case DCH_IY:
|
|
|
|
case DCH_IY:
|
|
|
|
if (from_char_parse_int(&out->year, &s, n) < 4)
|
|
|
|
len = from_char_parse_int(&out->year, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
if (len < 4)
|
|
|
|
out->year = adjust_partial_year_to_2020(out->year);
|
|
|
|
out->year = adjust_partial_year_to_2020(out->year);
|
|
|
|
out->yysz = 2;
|
|
|
|
out->yysz = 2;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_Y:
|
|
|
|
case DCH_Y:
|
|
|
|
case DCH_I:
|
|
|
|
case DCH_I:
|
|
|
|
if (from_char_parse_int(&out->year, &s, n) < 4)
|
|
|
|
len = from_char_parse_int(&out->year, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
if (len < 4)
|
|
|
|
out->year = adjust_partial_year_to_2020(out->year);
|
|
|
|
out->year = adjust_partial_year_to_2020(out->year);
|
|
|
|
out->yysz = 1;
|
|
|
|
out->yysz = 1;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_RM:
|
|
|
|
case DCH_RM:
|
|
|
|
from_char_seq_search(&value, &s, rm_months_upper,
|
|
|
|
from_char_seq_search(&value, &s, rm_months_upper,
|
|
|
|
ALL_UPPER, MAX_RM_LEN, n);
|
|
|
|
ALL_UPPER, MAX_RM_LEN, n, have_error);
|
|
|
|
from_char_set_int(&out->mm, MONTHS_PER_YEAR - value, n);
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
from_char_set_int(&out->mm, MONTHS_PER_YEAR - value,
|
|
|
|
|
|
|
|
n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_rm:
|
|
|
|
case DCH_rm:
|
|
|
|
from_char_seq_search(&value, &s, rm_months_lower,
|
|
|
|
from_char_seq_search(&value, &s, rm_months_lower,
|
|
|
|
ALL_LOWER, MAX_RM_LEN, n);
|
|
|
|
ALL_LOWER, MAX_RM_LEN, n, have_error);
|
|
|
|
from_char_set_int(&out->mm, MONTHS_PER_YEAR - value, n);
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
from_char_set_int(&out->mm, MONTHS_PER_YEAR - value,
|
|
|
|
|
|
|
|
n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_W:
|
|
|
|
case DCH_W:
|
|
|
|
from_char_parse_int(&out->w, &s, n);
|
|
|
|
from_char_parse_int(&out->w, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_J:
|
|
|
|
case DCH_J:
|
|
|
|
from_char_parse_int(&out->j, &s, n);
|
|
|
|
from_char_parse_int(&out->j, &s, n, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
SKIP_THth(s, n->suffix);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -3490,19 +3617,22 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out, bool std)
|
|
|
|
if (std)
|
|
|
|
if (std)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (n->type != NODE_TYPE_END)
|
|
|
|
if (n->type != NODE_TYPE_END)
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("input string is too short for datetime format")));
|
|
|
|
errmsg("input string is too short for datetime format"))));
|
|
|
|
|
|
|
|
|
|
|
|
while (*s != '\0' && isspace((unsigned char) *s))
|
|
|
|
while (*s != '\0' && isspace((unsigned char) *s))
|
|
|
|
s++;
|
|
|
|
s++;
|
|
|
|
|
|
|
|
|
|
|
|
if (*s != '\0')
|
|
|
|
if (*s != '\0')
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("trailing characters remain in input string after "
|
|
|
|
errmsg("trailing characters remain in input string "
|
|
|
|
"datetime format")));
|
|
|
|
"after datetime format"))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
on_error:
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
@@ -3523,9 +3653,13 @@ DCH_prevent_counter_overflow(void)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Get mask of date/time/zone components present in format nodes. */
|
|
|
|
/*
|
|
|
|
|
|
|
|
* Get mask of date/time/zone components present in format nodes.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
|
|
|
|
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
static int
|
|
|
|
DCH_datetime_type(FormatNode *node)
|
|
|
|
DCH_datetime_type(FormatNode *node, bool *have_error)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
FormatNode *n;
|
|
|
|
FormatNode *n;
|
|
|
|
int flags = 0;
|
|
|
|
int flags = 0;
|
|
|
@@ -3566,10 +3700,10 @@ DCH_datetime_type(FormatNode *node)
|
|
|
|
case DCH_tz:
|
|
|
|
case DCH_tz:
|
|
|
|
case DCH_TZ:
|
|
|
|
case DCH_TZ:
|
|
|
|
case DCH_OF:
|
|
|
|
case DCH_OF:
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
|
|
errmsg("formatting field \"%s\" is only supported in to_char",
|
|
|
|
errmsg("formatting field \"%s\" is only supported in to_char",
|
|
|
|
n->key->name)));
|
|
|
|
n->key->name))));
|
|
|
|
flags |= DCH_ZONED;
|
|
|
|
flags |= DCH_ZONED;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case DCH_TZH:
|
|
|
|
case DCH_TZH:
|
|
|
@@ -3623,6 +3757,7 @@ DCH_datetime_type(FormatNode *node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
on_error:
|
|
|
|
return flags;
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -3917,7 +4052,7 @@ to_timestamp(PG_FUNCTION_ARGS)
|
|
|
|
fsec_t fsec;
|
|
|
|
fsec_t fsec;
|
|
|
|
int fprec;
|
|
|
|
int fprec;
|
|
|
|
|
|
|
|
|
|
|
|
do_to_timestamp(date_txt, fmt, false, &tm, &fsec, &fprec, NULL);
|
|
|
|
do_to_timestamp(date_txt, fmt, false, &tm, &fsec, &fprec, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
/* Use the specified time zone, if any. */
|
|
|
|
/* Use the specified time zone, if any. */
|
|
|
|
if (tm.tm_zone)
|
|
|
|
if (tm.tm_zone)
|
|
|
@@ -3956,7 +4091,7 @@ to_date(PG_FUNCTION_ARGS)
|
|
|
|
struct pg_tm tm;
|
|
|
|
struct pg_tm tm;
|
|
|
|
fsec_t fsec;
|
|
|
|
fsec_t fsec;
|
|
|
|
|
|
|
|
|
|
|
|
do_to_timestamp(date_txt, fmt, false, &tm, &fsec, NULL, NULL);
|
|
|
|
do_to_timestamp(date_txt, fmt, false, &tm, &fsec, NULL, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
/* Prevent overflow in Julian-day routines */
|
|
|
|
/* Prevent overflow in Julian-day routines */
|
|
|
|
if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
|
|
|
|
if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
|
|
|
@@ -3983,17 +4118,21 @@ to_date(PG_FUNCTION_ARGS)
|
|
|
|
* the presence of date/time/zone components in the format string.
|
|
|
|
* the presence of date/time/zone components in the format string.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* When timezone component is present, the corresponding offset is set to '*tz'.
|
|
|
|
* When timezone component is present, the corresponding offset is set to '*tz'.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* If 'have_error' is NULL, then errors are thrown, else '*have_error' is set
|
|
|
|
|
|
|
|
* and zero value is returned.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
Datum
|
|
|
|
parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
|
|
|
|
parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
|
|
|
|
int32 *typmod, int *tz)
|
|
|
|
int32 *typmod, int *tz, bool *have_error)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct pg_tm tm;
|
|
|
|
struct pg_tm tm;
|
|
|
|
fsec_t fsec;
|
|
|
|
fsec_t fsec;
|
|
|
|
int fprec = 0;
|
|
|
|
int fprec = 0;
|
|
|
|
uint32 flags;
|
|
|
|
uint32 flags;
|
|
|
|
|
|
|
|
|
|
|
|
do_to_timestamp(date_txt, fmt, strict, &tm, &fsec, &fprec, &flags);
|
|
|
|
do_to_timestamp(date_txt, fmt, strict, &tm, &fsec, &fprec, &flags, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
*typmod = fprec ? fprec : -1; /* fractional part precision */
|
|
|
|
*typmod = fprec ? fprec : -1; /* fractional part precision */
|
|
|
|
|
|
|
|
|
|
|
@@ -4021,15 +4160,15 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
Assert(!strict);
|
|
|
|
Assert(!strict);
|
|
|
|
|
|
|
|
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("missing time zone in input string for type timestamptz")));
|
|
|
|
errmsg("missing time zone in input string for type timestamptz"))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tm2timestamp(&tm, fsec, tz, &result) != 0)
|
|
|
|
if (tm2timestamp(&tm, fsec, tz, &result) != 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
errmsg("timestamptz out of range")));
|
|
|
|
errmsg("timestamptz out of range"))));
|
|
|
|
|
|
|
|
|
|
|
|
AdjustTimestampForTypmod(&result, *typmod);
|
|
|
|
AdjustTimestampForTypmod(&result, *typmod);
|
|
|
|
|
|
|
|
|
|
|
@@ -4041,9 +4180,9 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
|
|
|
|
Timestamp result;
|
|
|
|
Timestamp result;
|
|
|
|
|
|
|
|
|
|
|
|
if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
|
|
|
|
if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
errmsg("timestamp out of range")));
|
|
|
|
errmsg("timestamp out of range"))));
|
|
|
|
|
|
|
|
|
|
|
|
AdjustTimestampForTypmod(&result, *typmod);
|
|
|
|
AdjustTimestampForTypmod(&result, *typmod);
|
|
|
|
|
|
|
|
|
|
|
@@ -4055,9 +4194,9 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (flags & DCH_ZONED)
|
|
|
|
if (flags & DCH_ZONED)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("datetime format is zoned but not timed")));
|
|
|
|
errmsg("datetime format is zoned but not timed"))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
@@ -4065,20 +4204,20 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
|
|
|
|
|
|
|
|
|
|
|
|
/* Prevent overflow in Julian-day routines */
|
|
|
|
/* Prevent overflow in Julian-day routines */
|
|
|
|
if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
|
|
|
|
if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
errmsg("date out of range: \"%s\"",
|
|
|
|
errmsg("date out of range: \"%s\"",
|
|
|
|
text_to_cstring(date_txt))));
|
|
|
|
text_to_cstring(date_txt)))));
|
|
|
|
|
|
|
|
|
|
|
|
result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) -
|
|
|
|
result = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) -
|
|
|
|
POSTGRES_EPOCH_JDATE;
|
|
|
|
POSTGRES_EPOCH_JDATE;
|
|
|
|
|
|
|
|
|
|
|
|
/* Now check for just-out-of-range dates */
|
|
|
|
/* Now check for just-out-of-range dates */
|
|
|
|
if (!IS_VALID_DATE(result))
|
|
|
|
if (!IS_VALID_DATE(result))
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
errmsg("date out of range: \"%s\"",
|
|
|
|
errmsg("date out of range: \"%s\"",
|
|
|
|
text_to_cstring(date_txt))));
|
|
|
|
text_to_cstring(date_txt)))));
|
|
|
|
|
|
|
|
|
|
|
|
*typid = DATEOID;
|
|
|
|
*typid = DATEOID;
|
|
|
|
return DateADTGetDatum(result);
|
|
|
|
return DateADTGetDatum(result);
|
|
|
@@ -4096,7 +4235,7 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
|
|
|
|
int dterr = DecodeTimezone(unconstify(char *, tm.tm_zone), tz);
|
|
|
|
int dterr = DecodeTimezone(unconstify(char *, tm.tm_zone), tz);
|
|
|
|
|
|
|
|
|
|
|
|
if (dterr)
|
|
|
|
if (dterr)
|
|
|
|
DateTimeParseError(dterr, text_to_cstring(date_txt), "timetz");
|
|
|
|
RETURN_ERROR(DateTimeParseError(dterr, text_to_cstring(date_txt), "timetz"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
@@ -4107,15 +4246,15 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
Assert(!strict);
|
|
|
|
Assert(!strict);
|
|
|
|
|
|
|
|
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("missing time zone in input string for type timetz")));
|
|
|
|
errmsg("missing time zone in input string for type timetz"))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tm2timetz(&tm, fsec, *tz, result) != 0)
|
|
|
|
if (tm2timetz(&tm, fsec, *tz, result) != 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
errmsg("timetz out of range")));
|
|
|
|
errmsg("timetz out of range"))));
|
|
|
|
|
|
|
|
|
|
|
|
AdjustTimeForTypmod(&result->time, *typmod);
|
|
|
|
AdjustTimeForTypmod(&result->time, *typmod);
|
|
|
|
|
|
|
|
|
|
|
@@ -4127,9 +4266,9 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
|
|
|
|
TimeADT result;
|
|
|
|
TimeADT result;
|
|
|
|
|
|
|
|
|
|
|
|
if (tm2time(&tm, fsec, &result) != 0)
|
|
|
|
if (tm2time(&tm, fsec, &result) != 0)
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
|
|
errmsg("time out of range")));
|
|
|
|
errmsg("time out of range"))));
|
|
|
|
|
|
|
|
|
|
|
|
AdjustTimeForTypmod(&result, *typmod);
|
|
|
|
AdjustTimeForTypmod(&result, *typmod);
|
|
|
|
|
|
|
|
|
|
|
@@ -4139,11 +4278,12 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
ereport(ERROR,
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("datetime format is not dated and not timed")));
|
|
|
|
errmsg("datetime format is not dated and not timed"))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
on_error:
|
|
|
|
return (Datum) 0;
|
|
|
|
return (Datum) 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -4159,17 +4299,24 @@ parse_datetime(text *date_txt, text *fmt, bool strict, Oid *typid,
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* The TmFromChar is then analysed and converted into the final results in
|
|
|
|
* The TmFromChar is then analysed and converted into the final results in
|
|
|
|
* struct 'tm' and 'fsec'.
|
|
|
|
* struct 'tm' and 'fsec'.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* Bit mask of date/time/zone components found in 'fmt' is returned in 'flags'.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* 'std' specifies standard parsing mode.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* If 'have_error' is NULL, then errors are thrown, else '*have_error' is set.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
static void
|
|
|
|
do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
struct pg_tm *tm, fsec_t *fsec, int *fprec,
|
|
|
|
struct pg_tm *tm, fsec_t *fsec, int *fprec,
|
|
|
|
uint32 *flags)
|
|
|
|
uint32 *flags, bool *have_error)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
FormatNode *format;
|
|
|
|
FormatNode *format = NULL;
|
|
|
|
TmFromChar tmfc;
|
|
|
|
TmFromChar tmfc;
|
|
|
|
int fmt_len;
|
|
|
|
int fmt_len;
|
|
|
|
char *date_str;
|
|
|
|
char *date_str;
|
|
|
|
int fmask;
|
|
|
|
int fmask;
|
|
|
|
|
|
|
|
bool incache = false;
|
|
|
|
|
|
|
|
|
|
|
|
date_str = text_to_cstring(date_txt);
|
|
|
|
date_str = text_to_cstring(date_txt);
|
|
|
|
|
|
|
|
|
|
|
@@ -4183,7 +4330,6 @@ do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
if (fmt_len)
|
|
|
|
if (fmt_len)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
char *fmt_str;
|
|
|
|
char *fmt_str;
|
|
|
|
bool incache;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fmt_str = text_to_cstring(fmt);
|
|
|
|
fmt_str = text_to_cstring(fmt);
|
|
|
|
|
|
|
|
|
|
|
@@ -4193,8 +4339,6 @@ do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
* Allocate new memory if format picture is bigger than static
|
|
|
|
* Allocate new memory if format picture is bigger than static
|
|
|
|
* cache and do not use cache (call parser always)
|
|
|
|
* cache and do not use cache (call parser always)
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
incache = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
|
|
|
|
format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
|
|
|
|
|
|
|
|
|
|
|
|
parse_format(format, fmt_str, DCH_keywords, DCH_suff, DCH_index,
|
|
|
|
parse_format(format, fmt_str, DCH_keywords, DCH_suff, DCH_index,
|
|
|
@@ -4216,15 +4360,21 @@ do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
/* dump_index(DCH_keywords, DCH_index); */
|
|
|
|
/* dump_index(DCH_keywords, DCH_index); */
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
DCH_from_char(format, date_str, &tmfc, std);
|
|
|
|
DCH_from_char(format, date_str, &tmfc, std, have_error);
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
pfree(fmt_str);
|
|
|
|
pfree(fmt_str);
|
|
|
|
|
|
|
|
|
|
|
|
if (flags)
|
|
|
|
if (flags)
|
|
|
|
*flags = DCH_datetime_type(format);
|
|
|
|
*flags = DCH_datetime_type(format, have_error);
|
|
|
|
|
|
|
|
|
|
|
|
if (!incache)
|
|
|
|
if (!incache)
|
|
|
|
|
|
|
|
{
|
|
|
|
pfree(format);
|
|
|
|
pfree(format);
|
|
|
|
|
|
|
|
format = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CHECK_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_TMFC(&tmfc);
|
|
|
|
DEBUG_TMFC(&tmfc);
|
|
|
@@ -4253,11 +4403,13 @@ do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
if (tmfc.clock == CLOCK_12_HOUR)
|
|
|
|
if (tmfc.clock == CLOCK_12_HOUR)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (tm->tm_hour < 1 || tm->tm_hour > HOURS_PER_DAY / 2)
|
|
|
|
if (tm->tm_hour < 1 || tm->tm_hour > HOURS_PER_DAY / 2)
|
|
|
|
ereport(ERROR,
|
|
|
|
{
|
|
|
|
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("hour \"%d\" is invalid for the 12-hour clock",
|
|
|
|
errmsg("hour \"%d\" is invalid for the 12-hour clock",
|
|
|
|
tm->tm_hour),
|
|
|
|
tm->tm_hour),
|
|
|
|
errhint("Use the 24-hour clock, or give an hour between 1 and 12.")));
|
|
|
|
errhint("Use the 24-hour clock, or give an hour between 1 and 12."))));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tmfc.pm && tm->tm_hour < HOURS_PER_DAY / 2)
|
|
|
|
if (tmfc.pm && tm->tm_hour < HOURS_PER_DAY / 2)
|
|
|
|
tm->tm_hour += HOURS_PER_DAY / 2;
|
|
|
|
tm->tm_hour += HOURS_PER_DAY / 2;
|
|
|
@@ -4361,9 +4513,11 @@ do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
if (!tm->tm_year && !tmfc.bc)
|
|
|
|
if (!tm->tm_year && !tmfc.bc)
|
|
|
|
ereport(ERROR,
|
|
|
|
{
|
|
|
|
|
|
|
|
RETURN_ERROR(ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
|
|
|
|
errmsg("cannot calculate day of year without year information")));
|
|
|
|
errmsg("cannot calculate day of year without year information"))));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
|
|
|
|
if (tmfc.mode == FROM_CHAR_DATE_ISOWEEK)
|
|
|
|
{
|
|
|
|
{
|
|
|
@@ -4420,7 +4574,7 @@ do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
* said DTERR_MD_FIELD_OVERFLOW, because we don't want to print an
|
|
|
|
* said DTERR_MD_FIELD_OVERFLOW, because we don't want to print an
|
|
|
|
* irrelevant hint about datestyle.
|
|
|
|
* irrelevant hint about datestyle.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
DateTimeParseError(DTERR_FIELD_OVERFLOW, date_str, "timestamp");
|
|
|
|
RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, date_str, "timestamp"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -4429,7 +4583,9 @@ do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
tm->tm_min < 0 || tm->tm_min >= MINS_PER_HOUR ||
|
|
|
|
tm->tm_min < 0 || tm->tm_min >= MINS_PER_HOUR ||
|
|
|
|
tm->tm_sec < 0 || tm->tm_sec >= SECS_PER_MINUTE ||
|
|
|
|
tm->tm_sec < 0 || tm->tm_sec >= SECS_PER_MINUTE ||
|
|
|
|
*fsec < INT64CONST(0) || *fsec >= USECS_PER_SEC)
|
|
|
|
*fsec < INT64CONST(0) || *fsec >= USECS_PER_SEC)
|
|
|
|
DateTimeParseError(DTERR_FIELD_OVERFLOW, date_str, "timestamp");
|
|
|
|
{
|
|
|
|
|
|
|
|
RETURN_ERROR(DateTimeParseError(DTERR_FIELD_OVERFLOW, date_str, "timestamp"));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Save parsed time-zone into tm->tm_zone if it was specified */
|
|
|
|
/* Save parsed time-zone into tm->tm_zone if it was specified */
|
|
|
|
if (tmfc.tzsign)
|
|
|
|
if (tmfc.tzsign)
|
|
|
@@ -4438,7 +4594,9 @@ do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
|
|
|
|
|
|
|
|
if (tmfc.tzh < 0 || tmfc.tzh > MAX_TZDISP_HOUR ||
|
|
|
|
if (tmfc.tzh < 0 || tmfc.tzh > MAX_TZDISP_HOUR ||
|
|
|
|
tmfc.tzm < 0 || tmfc.tzm >= MINS_PER_HOUR)
|
|
|
|
tmfc.tzm < 0 || tmfc.tzm >= MINS_PER_HOUR)
|
|
|
|
DateTimeParseError(DTERR_TZDISP_OVERFLOW, date_str, "timestamp");
|
|
|
|
{
|
|
|
|
|
|
|
|
RETURN_ERROR(DateTimeParseError(DTERR_TZDISP_OVERFLOW, date_str, "timestamp"));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tz = psprintf("%c%02d:%02d",
|
|
|
|
tz = psprintf("%c%02d:%02d",
|
|
|
|
tmfc.tzsign > 0 ? '+' : '-', tmfc.tzh, tmfc.tzm);
|
|
|
|
tmfc.tzsign > 0 ? '+' : '-', tmfc.tzh, tmfc.tzm);
|
|
|
@@ -4448,6 +4606,11 @@ do_to_timestamp(text *date_txt, text *fmt, bool std,
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_TM(tm);
|
|
|
|
DEBUG_TM(tm);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
on_error:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (format && !incache)
|
|
|
|
|
|
|
|
pfree(format);
|
|
|
|
|
|
|
|
|
|
|
|
pfree(date_str);
|
|
|
|
pfree(date_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|