diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 46f45f6654..5a5c526426 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -50,7 +50,6 @@ * ideal code * - use Assert() * - add support for abstime - * - add support for roman number to standard number conversion * - add support for number spelling * - add support for string to string formatting (we must be better * than Oracle :-), @@ -983,6 +982,7 @@ static void do_to_timestamp(text *date_txt, text *fmt, static char *fill_str(char *str, int c, int max); static FormatNode *NUM_cache(int len, NUMDesc *Num, text *pars_str, bool *shouldFree); static char *int_to_roman(int number); +static int roman_to_int(char *numerals, int len); static void NUM_prepare_locale(NUMProc *Np); static char *get_last_relevant_decnum(char *num); static void NUM_numpart_from_char(NUMProc *Np, int id, int input_len); @@ -4127,6 +4127,101 @@ int_to_roman(int number) } +static int +roman_to_int(char *numerals, int len) +{ + int i, total, prev, curr, prevCount; + bool sub, l, v, d; + total = prev = curr = prevCount = 0; + sub = l = v = d = false; + + for (i = len-1; i >= 0; i--) + { + switch (numerals[i]) + { + case 'M': + case 'm': + curr = 1000; + break; + case 'D': + case 'd': + curr = 500; + if (d || prev > curr) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid character \"D\" at position %d", i+1))); + d = true; + break; + case 'C': + case 'c': + curr = 100; + break; + case 'L': + case 'l': + curr = 50; + if (l || prev > curr) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid character \"L\" at position %d", i+1))); + l = true; + break; + case 'X': + case 'x': + curr = 10; + break; + case 'V': + case 'v': + curr = 5; + if (v || prev > curr) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid character \"V\" at position %d", i+1))); + v = true; + break; + case 'I': + case 'i': + curr = 1; + break; + default: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid character \"%c\"", numerals[i]))); + } + if (prev == curr) + { + if (++prevCount == 3) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("character \"%c\" repeated too many times", numerals[i]))); + } + else + { + prevCount = 0; + } + if (prev <= curr) + { + total += curr; + sub = false; + prev = curr; + } + else + { + if (sub) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("duplicate subtraction at position %d", i+1))); + if (total / curr > 10) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("subtraction value too small at position %d", i+1))); + sub = true; + total -= curr; + prev = prev - curr; + } + } + return total; +} + /* ---------- * Locale @@ -4684,11 +4779,6 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, */ if (IS_ROMAN(Np->Num)) { - if (!Np->is_to_char) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("\"RN\" not supported for input"))); - Np->Num->lsign = Np->Num->pre_lsign_num = Np->Num->post = Np->Num->pre = Np->out_pre_spaces = Np->sign = 0; @@ -5118,39 +5208,57 @@ numeric_to_number(PG_FUNCTION_ARGS) format = NUM_cache(len, &Num, fmt, &shouldFree); - numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1); - - NUM_processor(format, &Num, VARDATA_ANY(value), numstr, - VARSIZE_ANY_EXHDR(value), 0, 0, false, PG_GET_COLLATION()); + if (IS_ROMAN(&Num)) + { + char *cp = text_to_cstring(value); + while (*cp) { + if (!isspace(*cp)) + break; + cp++; + } + len = strlen(cp); + if (!len) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("no number"))); + result = DirectFunctionCall1(int4_numeric, + Int32GetDatum(roman_to_int(cp, len))); + } + else + { + numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1); - scale = Num.post; - precision = Num.pre + Num.multi + scale; + NUM_processor(format, &Num, VARDATA_ANY(value), numstr, + VARSIZE_ANY_EXHDR(value), 0, 0, false, PG_GET_COLLATION()); - if (shouldFree) - pfree(format); + scale = Num.post; + precision = Num.pre + Num.multi + scale; - result = DirectFunctionCall3(numeric_in, + result = DirectFunctionCall3(numeric_in, CStringGetDatum(numstr), ObjectIdGetDatum(InvalidOid), Int32GetDatum(((precision << 16) | scale) + VARHDRSZ)); - if (IS_MULTI(&Num)) - { - Numeric x; - Numeric a = DatumGetNumeric(DirectFunctionCall1(int4_numeric, - Int32GetDatum(10))); - Numeric b = DatumGetNumeric(DirectFunctionCall1(int4_numeric, - Int32GetDatum(-Num.multi))); - - x = DatumGetNumeric(DirectFunctionCall2(numeric_power, - NumericGetDatum(a), - NumericGetDatum(b))); - result = DirectFunctionCall2(numeric_mul, - result, - NumericGetDatum(x)); + if (IS_MULTI(&Num)) + { + Numeric x; + Numeric a = DatumGetNumeric(DirectFunctionCall1(int4_numeric, + Int32GetDatum(10))); + Numeric b = DatumGetNumeric(DirectFunctionCall1(int4_numeric, + Int32GetDatum(-Num.multi))); + + x = DatumGetNumeric(DirectFunctionCall2(numeric_power, + NumericGetDatum(a), + NumericGetDatum(b))); + result = DirectFunctionCall2(numeric_mul, + result, + NumericGetDatum(x)); + } + pfree(numstr); } + if (shouldFree) + pfree(format); - pfree(numstr); return result; }