<replaceable>n</replaceable> is the number of digits following
<literal>V</literal>. <literal>V</literal> with
<function>to_number</function> divides in a similar manner.
+ The <literal>V</literal> can be thought of as marking the position
+ of an implicit decimal point in the input or output string.
<function>to_char</function> and <function>to_number</function>
do not support the use of
<literal>V</literal> combined with a decimal point
}
+/*
+ * Convert integer to Roman numerals
+ * Result is upper-case and not blank-padded (NUM_processor converts as needed)
+ * If input is out-of-range, produce '###############'
+ */
static char *
int_to_roman(int number)
{
result = (char *) palloc(16);
*result = '\0';
+ /*
+ * This range limit is the same as in Oracle(TM). The difficulty with
+ * handling 4000 or more is that we'd need to use more than 3 "M"'s, and
+ * more than 3 of the same digit isn't considered a valid Roman string.
+ */
if (number > 3999 || number < 1)
{
fill_str(result, '#', 15);
return result;
}
+
+ /* Convert to decimal, then examine each digit */
len = snprintf(numstr, sizeof(numstr), "%d", number);
+ Assert(len > 0 && len <= 4);
for (p = numstr; *p != '\0'; p++, --len)
{
num = *p - ('0' + 1);
if (num < 0)
- continue;
-
- if (len > 3)
+ continue; /* ignore zeroes */
+ /* switch on current column position */
+ switch (len)
{
- while (num-- != -1)
- strcat(result, "M");
- }
- else
- {
- if (len == 3)
+ case 4:
+ while (num-- >= 0)
+ strcat(result, "M");
+ break;
+ case 3:
strcat(result, rm100[num]);
- else if (len == 2)
+ break;
+ case 2:
strcat(result, rm10[num]);
- else if (len == 1)
+ break;
+ case 1:
strcat(result, rm1[num]);
+ break;
}
}
return result;
char *numstr,
*orgnum,
*p;
- Numeric x;
NUM_TOCHAR_prepare;
*/
if (IS_ROMAN(&Num))
{
- x = DatumGetNumeric(DirectFunctionCall2(numeric_round,
- NumericGetDatum(value),
- Int32GetDatum(0)));
- numstr =
- int_to_roman(DatumGetInt32(DirectFunctionCall1(numeric_int4,
- NumericGetDatum(x))));
+ int32 intvalue;
+ bool err;
+
+ /* Round and convert to int */
+ intvalue = numeric_int4_opt_error(value, &err);
+ /* On overflow, just use PG_INT32_MAX; int_to_roman will cope */
+ if (err)
+ intvalue = PG_INT32_MAX;
+ numstr = int_to_roman(intvalue);
}
else if (IS_EEEE(&Num))
{
{
int numstr_pre_len;
Numeric val = value;
+ Numeric x;
if (IS_MULTI(&Num))
{
NUM_TOCHAR_prepare;
/*
- * On DateType depend part (int32)
+ * On DateType depend part (int64)
*/
if (IS_ROMAN(&Num))
{
- /* Currently don't support int8 conversion to roman... */
- numstr = int_to_roman(DatumGetInt32(DirectFunctionCall1(int84, Int64GetDatum(value))));
+ int32 intvalue;
+
+ /* On overflow, just use PG_INT32_MAX; int_to_roman will cope */
+ if (value <= PG_INT32_MAX && value >= PG_INT32_MIN)
+ intvalue = (int32) value;
+ else
+ intvalue = PG_INT32_MAX;
+ numstr = int_to_roman(intvalue);
}
else if (IS_EEEE(&Num))
{
NUM_TOCHAR_prepare;
if (IS_ROMAN(&Num))
- numstr = int_to_roman((int) rint(value));
+ {
+ int32 intvalue;
+
+ /* See notes in ftoi4() */
+ value = rint(value);
+ /* On overflow, just use PG_INT32_MAX; int_to_roman will cope */
+ if (!isnan(value) && FLOAT4_FITS_IN_INT32(value))
+ intvalue = (int32) value;
+ else
+ intvalue = PG_INT32_MAX;
+ numstr = int_to_roman(intvalue);
+ }
else if (IS_EEEE(&Num))
{
if (isnan(value) || isinf(value))
NUM_TOCHAR_prepare;
if (IS_ROMAN(&Num))
- numstr = int_to_roman((int) rint(value));
+ {
+ int32 intvalue;
+
+ /* See notes in dtoi4() */
+ value = rint(value);
+ /* On overflow, just use PG_INT32_MAX; int_to_roman will cope */
+ if (!isnan(value) && FLOAT8_FITS_IN_INT32(value))
+ intvalue = (int32) value;
+ else
+ intvalue = PG_INT32_MAX;
+ numstr = int_to_roman(intvalue);
+ }
else if (IS_EEEE(&Num))
{
if (isnan(value) || isinf(value))
-4567890123456789
(5 rows)
+SELECT to_char(q2, '9999999999999999PL') FROM INT8_TBL;
+ to_char
+--------------------
+ 456+
+ 4567890123456789+
+ 123+
+ 4567890123456789+
+ -4567890123456789
+(5 rows)
+
SELECT to_char(q2, 'FMS9999999999999999') FROM INT8_TBL;
to_char
-------------------
456789-0123456789
(5 rows)
+SELECT to_char(q2, 'FMRN') FROM INT8_TBL;
+ to_char
+-----------------
+ CDLVI
+ ###############
+ CXXIII
+ ###############
+ ###############
+(5 rows)
+
+SELECT to_char(1234, '9.99EEEE');
+ to_char
+-----------
+ 1.23e+03
+(1 row)
+
+SELECT to_char(1234::int8, '9.99eeee');
+ to_char
+-----------
+ 1.23e+03
+(1 row)
+
+SELECT to_char(-1234::int8, '9.99eeee');
+ to_char
+-----------
+ -1.23e+03
+(1 row)
+
+SELECT to_char(1234, '99999V99');
+ to_char
+----------
+ 123400
+(1 row)
+
+SELECT to_char(1234::int8, '99999V99');
+ to_char
+----------
+ 123400
+(1 row)
+
-- check min/max values and overflow behavior
select '-9223372036854775808'::int8;
int8
-2.493e+07
(10 rows)
+SELECT to_char(val, 'FMRN') FROM num_data;
+ to_char
+-----------------
+ ###############
+ ###############
+ ###############
+ IV
+ ###############
+ ###############
+ ###############
+ ###############
+ ###############
+ ###############
+(10 rows)
+
WITH v(val) AS
(VALUES('0'::numeric),('-4.2'),('4.2e9'),('1.2e-5'),('inf'),('-inf'),('nan'))
SELECT val,
##########.####
(1 row)
+SELECT to_char('100'::numeric, 'rn');
+ to_char
+-----------------
+ c
+(1 row)
+
+SELECT to_char('1234'::numeric, 'rn');
+ to_char
+-----------------
+ mccxxxiv
+(1 row)
+
+SELECT to_char('1235'::float4, 'rn');
+ to_char
+-----------------
+ mccxxxv
+(1 row)
+
+SELECT to_char('1236'::float8, 'rn');
+ to_char
+-----------------
+ mccxxxvi
+(1 row)
+
+SELECT to_char('1237'::float8, 'fmrn');
+ to_char
+-----------
+ mccxxxvii
+(1 row)
+
+SELECT to_char('100e9'::numeric, 'RN');
+ to_char
+-----------------
+ ###############
+(1 row)
+
+SELECT to_char('100e9'::float4, 'RN');
+ to_char
+-----------------
+ ###############
+(1 row)
+
+SELECT to_char('100e9'::float8, 'RN');
+ to_char
+-----------------
+ ###############
+(1 row)
+
+SELECT to_char(1234.56::numeric, '99999V99');
+ to_char
+----------
+ 123456
+(1 row)
+
+SELECT to_char(1234.56::float4, '99999V99');
+ to_char
+----------
+ 123456
+(1 row)
+
+SELECT to_char(1234.56::float8, '99999V99');
+ to_char
+----------
+ 123456
+(1 row)
+
-- Check parsing of literal text in a format string
SELECT to_char('100'::numeric, 'foo999');
to_char
42
(1 row)
+SELECT to_number('123456', '99999V99');
+ to_number
+-------------------------
+ 1234.560000000000000000
+(1 row)
+
RESET lc_numeric;
--
-- Input syntax
FROM INT8_TBL;
SELECT to_char(q2, 'MI9999999999999999') FROM INT8_TBL;
+SELECT to_char(q2, '9999999999999999PL') FROM INT8_TBL;
SELECT to_char(q2, 'FMS9999999999999999') FROM INT8_TBL;
SELECT to_char(q2, 'FM9999999999999999THPR') FROM INT8_TBL;
SELECT to_char(q2, 'SG9999999999999999th') FROM INT8_TBL;
SELECT to_char(q2, 'S 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 . 9 9 9') FROM INT8_TBL;
SELECT to_char(q2, E'99999 "text" 9999 "9999" 999 "\\"text between quote marks\\"" 9999') FROM INT8_TBL;
SELECT to_char(q2, '999999SG9999999999') FROM INT8_TBL;
+SELECT to_char(q2, 'FMRN') FROM INT8_TBL;
+
+SELECT to_char(1234, '9.99EEEE');
+SELECT to_char(1234::int8, '9.99eeee');
+SELECT to_char(-1234::int8, '9.99eeee');
+SELECT to_char(1234, '99999V99');
+SELECT to_char(1234::int8, '99999V99');
-- check min/max values and overflow behavior
SELECT to_char(val, '999999SG9999999999') FROM num_data;
SELECT to_char(val, 'FM9999999999999999.999999999999999') FROM num_data;
SELECT to_char(val, '9.999EEEE') FROM num_data;
+SELECT to_char(val, 'FMRN') FROM num_data;
WITH v(val) AS
(VALUES('0'::numeric),('-4.2'),('4.2e9'),('1.2e-5'),('inf'),('-inf'),('nan'))
SELECT to_char('100'::numeric, 'FM999');
SELECT to_char('12345678901'::float8, 'FM9999999999D9999900000000000000000');
+SELECT to_char('100'::numeric, 'rn');
+SELECT to_char('1234'::numeric, 'rn');
+SELECT to_char('1235'::float4, 'rn');
+SELECT to_char('1236'::float8, 'rn');
+SELECT to_char('1237'::float8, 'fmrn');
+SELECT to_char('100e9'::numeric, 'RN');
+SELECT to_char('100e9'::float4, 'RN');
+SELECT to_char('100e9'::float8, 'RN');
+
+SELECT to_char(1234.56::numeric, '99999V99');
+SELECT to_char(1234.56::float4, '99999V99');
+SELECT to_char(1234.56::float8, '99999V99');
+
-- Check parsing of literal text in a format string
SELECT to_char('100'::numeric, 'foo999');
SELECT to_char('100'::numeric, 'f\oo999');
SELECT to_number('1234.56','L99,999.99');
SELECT to_number('1,234.56','L99,999.99');
SELECT to_number('42nd', '99th');
+SELECT to_number('123456', '99999V99');
RESET lc_numeric;
--