11/* -----------------------------------------------------------------------
22 * formatting.c
33 *
4- * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.159 2009/07/06 19:11:39 heikki Exp $
4+ * $PostgreSQL: pgsql/src/backend/utils/adt/formatting.c,v 1.160 2009/08/10 18:29:26 tgl Exp $
55 *
66 *
77 * Portions Copyright (c) 1999-2009, PostgreSQL Global Development Group
@@ -335,6 +335,7 @@ typedef struct
335335#define NUM_F_MULTI (1 << 11)
336336#define NUM_F_PLUS_POST (1 << 12)
337337#define NUM_F_MINUS_POST (1 << 13)
338+ #define NUM_F_EEEE (1 << 14)
338339
339340#define NUM_LSIGN_PRE (-1)
340341#define NUM_LSIGN_POST 1
@@ -355,6 +356,7 @@ typedef struct
355356#define IS_PLUS (_f ) ((_f)->flag & NUM_F_PLUS)
356357#define IS_ROMAN (_f ) ((_f)->flag & NUM_F_ROMAN)
357358#define IS_MULTI (_f ) ((_f)->flag & NUM_F_MULTI)
359+ #define IS_EEEE (_f ) ((_f)->flag & NUM_F_EEEE)
358360
359361/* ----------
360362 * Format picture cache
@@ -821,7 +823,7 @@ static const KeyWord NUM_keywords[] = {
821823 {"B" , 1 , NUM_B }, /* B */
822824 {"C" , 1 , NUM_C }, /* C */
823825 {"D" , 1 , NUM_D }, /* D */
824- {"E " , 1 , NUM_E }, /* E */
826+ {"EEEE " , 4 , NUM_E }, /* E */
825827 {"FM" , 2 , NUM_FM }, /* F */
826828 {"G" , 1 , NUM_G }, /* G */
827829 {"L" , 1 , NUM_L }, /* L */
@@ -837,7 +839,7 @@ static const KeyWord NUM_keywords[] = {
837839 {"b" , 1 , NUM_B }, /* b */
838840 {"c" , 1 , NUM_C }, /* c */
839841 {"d" , 1 , NUM_D }, /* d */
840- {"e " , 1 , NUM_E }, /* e */
842+ {"eeee " , 4 , NUM_E }, /* e */
841843 {"fm" , 2 , NUM_FM }, /* f */
842844 {"g" , 1 , NUM_G }, /* g */
843845 {"l" , 1 , NUM_L }, /* l */
@@ -1044,6 +1046,14 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
10441046 if (n -> type != NODE_TYPE_ACTION )
10451047 return ;
10461048
1049+ if (IS_EEEE (num ) && n -> key -> id != NUM_E )
1050+ {
1051+ NUM_cache_remove (last_NUMCacheEntry );
1052+ ereport (ERROR ,
1053+ (errcode (ERRCODE_SYNTAX_ERROR ),
1054+ errmsg ("\"EEEE\" must be the last pattern used" )));
1055+ }
1056+
10471057 switch (n -> key -> id )
10481058 {
10491059 case NUM_9 :
@@ -1217,10 +1227,25 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
12171227 break ;
12181228
12191229 case NUM_E :
1220- NUM_cache_remove (last_NUMCacheEntry );
1221- ereport (ERROR ,
1222- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
1223- errmsg ("\"E\" is not supported" )));
1230+ if (IS_EEEE (num ))
1231+ {
1232+ NUM_cache_remove (last_NUMCacheEntry );
1233+ ereport (ERROR ,
1234+ (errcode (ERRCODE_SYNTAX_ERROR ),
1235+ errmsg ("cannot use \"EEEE\" twice" )));
1236+ }
1237+ if (IS_BLANK (num ) || IS_FILLMODE (num ) || IS_LSIGN (num ) ||
1238+ IS_BRACKET (num ) || IS_MINUS (num ) || IS_PLUS (num ) ||
1239+ IS_ROMAN (num ) || IS_MULTI (num ))
1240+ {
1241+ NUM_cache_remove (last_NUMCacheEntry );
1242+ ereport (ERROR ,
1243+ (errcode (ERRCODE_SYNTAX_ERROR ),
1244+ errmsg ("\"EEEE\" is incompatible with other formats" ),
1245+ errdetail ("\"EEEE\" may only be used together with digit and decimal point patterns." )));
1246+ }
1247+ num -> flag |= NUM_F_EEEE ;
1248+ break ;
12241249 }
12251250
12261251 return ;
@@ -4145,6 +4170,15 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
41454170 if (Np -> Num -> zero_start )
41464171 -- Np -> Num -> zero_start ;
41474172
4173+ if (IS_EEEE (Np -> Num ))
4174+ {
4175+ if (!Np -> is_to_char )
4176+ ereport (ERROR ,
4177+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
4178+ errmsg ("\"EEEE\" not supported for input" )));
4179+ return strcpy (inout , number );
4180+ }
4181+
41484182 /*
41494183 * Roman correction
41504184 */
@@ -4153,7 +4187,7 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
41534187 if (!Np -> is_to_char )
41544188 ereport (ERROR ,
41554189 (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
4156- errmsg ("\"RN\" not supported" )));
4190+ errmsg ("\"RN\" not supported for input " )));
41574191
41584192 Np -> Num -> lsign = Np -> Num -> pre_lsign_num = Np -> Num -> post =
41594193 Np -> Num -> pre = Np -> num_pre = Np -> sign = 0 ;
@@ -4240,7 +4274,7 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
42404274
42414275#ifdef DEBUG_TO_FROM_CHAR
42424276 elog (DEBUG_elog_output ,
4243- "\n\tSIGN: '%c'\n\tNUM: '%s'\n\tPRE: %d\n\tPOST: %d\n\tNUM_COUNT: %d\n\tNUM_PRE: %d\n\tSIGN_WROTE: %s\n\tZERO: %s\n\tZERO_START: %d\n\tZERO_END: %d\n\tLAST_RELEVANT: %s\n\tBRACKET: %s\n\tPLUS: %s\n\tMINUS: %s\n\tFILLMODE: %s\n\tROMAN: %s" ,
4277+ "\n\tSIGN: '%c'\n\tNUM: '%s'\n\tPRE: %d\n\tPOST: %d\n\tNUM_COUNT: %d\n\tNUM_PRE: %d\n\tSIGN_WROTE: %s\n\tZERO: %s\n\tZERO_START: %d\n\tZERO_END: %d\n\tLAST_RELEVANT: %s\n\tBRACKET: %s\n\tPLUS: %s\n\tMINUS: %s\n\tFILLMODE: %s\n\tROMAN: %s\n\tEEEE: %s " ,
42444278 Np -> sign ,
42454279 Np -> number ,
42464280 Np -> Num -> pre ,
@@ -4256,7 +4290,8 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
42564290 IS_PLUS (Np -> Num ) ? "Yes" : "No" ,
42574291 IS_MINUS (Np -> Num ) ? "Yes" : "No" ,
42584292 IS_FILLMODE (Np -> Num ) ? "Yes" : "No" ,
4259- IS_ROMAN (Np -> Num ) ? "Yes" : "No"
4293+ IS_ROMAN (Np -> Num ) ? "Yes" : "No" ,
4294+ IS_EEEE (Np -> Num ) ? "Yes" : "No"
42604295 );
42614296#endif
42624297
@@ -4626,6 +4661,39 @@ numeric_to_char(PG_FUNCTION_ARGS)
46264661 int_to_roman (DatumGetInt32 (DirectFunctionCall1 (numeric_int4 ,
46274662 NumericGetDatum (x ))));
46284663 }
4664+ else if (IS_EEEE (& Num ))
4665+ {
4666+ orgnum = numeric_out_sci (value , Num .post );
4667+
4668+ /*
4669+ * numeric_out_sci() does not emit a sign for positive numbers. We
4670+ * need to add a space in this case so that positive and negative
4671+ * numbers are aligned. We also have to do the right thing for NaN.
4672+ */
4673+ if (strcmp (orgnum , "NaN" ) == 0 )
4674+ {
4675+ /*
4676+ * Allow 6 characters for the leading sign, the decimal point, "e",
4677+ * the exponent's sign and two exponent digits.
4678+ */
4679+ numstr = (char * ) palloc (Num .pre + Num .post + 7 );
4680+ fill_str (numstr , '#' , Num .pre + Num .post + 6 );
4681+ * numstr = ' ' ;
4682+ * (numstr + Num .pre + 1 ) = '.' ;
4683+ }
4684+ else if (* orgnum != '-' )
4685+ {
4686+ numstr = (char * ) palloc (strlen (orgnum ) + 2 );
4687+ * numstr = ' ' ;
4688+ strcpy (numstr + 1 , orgnum );
4689+ len = strlen (numstr );
4690+ }
4691+ else
4692+ {
4693+ numstr = orgnum ;
4694+ len = strlen (orgnum );
4695+ }
4696+ }
46294697 else
46304698 {
46314699 Numeric val = value ;
@@ -4707,6 +4775,23 @@ int4_to_char(PG_FUNCTION_ARGS)
47074775 */
47084776 if (IS_ROMAN (& Num ))
47094777 numstr = orgnum = int_to_roman (value );
4778+ else if (IS_EEEE (& Num ))
4779+ {
4780+ /* we can do it easily because float8 won't lose any precision */
4781+ float8 val = (float8 ) value ;
4782+
4783+ orgnum = (char * ) palloc (MAXDOUBLEWIDTH + 1 );
4784+ snprintf (orgnum , MAXDOUBLEWIDTH + 1 , "%+.*e" , Num .post , val );
4785+
4786+ /*
4787+ * Swap a leading positive sign for a space.
4788+ */
4789+ if (* orgnum == '+' )
4790+ * orgnum = ' ' ;
4791+
4792+ len = strlen (orgnum );
4793+ numstr = orgnum ;
4794+ }
47104795 else
47114796 {
47124797 if (IS_MULTI (& Num ))
@@ -4785,6 +4870,33 @@ int8_to_char(PG_FUNCTION_ARGS)
47854870 numstr = orgnum = int_to_roman (DatumGetInt32 (
47864871 DirectFunctionCall1 (int84 , Int64GetDatum (value ))));
47874872 }
4873+ else if (IS_EEEE (& Num ))
4874+ {
4875+ /* to avoid loss of precision, must go via numeric not float8 */
4876+ Numeric val ;
4877+
4878+ val = DatumGetNumeric (DirectFunctionCall1 (int8_numeric ,
4879+ Int64GetDatum (value )));
4880+ orgnum = numeric_out_sci (val , Num .post );
4881+
4882+ /*
4883+ * numeric_out_sci() does not emit a sign for positive numbers. We
4884+ * need to add a space in this case so that positive and negative
4885+ * numbers are aligned. We don't have to worry about NaN here.
4886+ */
4887+ if (* orgnum != '-' )
4888+ {
4889+ numstr = (char * ) palloc (strlen (orgnum ) + 2 );
4890+ * numstr = ' ' ;
4891+ strcpy (numstr + 1 , orgnum );
4892+ len = strlen (numstr );
4893+ }
4894+ else
4895+ {
4896+ numstr = orgnum ;
4897+ len = strlen (orgnum );
4898+ }
4899+ }
47884900 else
47894901 {
47904902 if (IS_MULTI (& Num ))
@@ -4859,6 +4971,34 @@ float4_to_char(PG_FUNCTION_ARGS)
48594971
48604972 if (IS_ROMAN (& Num ))
48614973 numstr = orgnum = int_to_roman ((int ) rint (value ));
4974+ else if (IS_EEEE (& Num ))
4975+ {
4976+ numstr = orgnum = (char * ) palloc (MAXDOUBLEWIDTH + 1 );
4977+ if (isnan (value ) || is_infinite (value ))
4978+ {
4979+ /*
4980+ * Allow 6 characters for the leading sign, the decimal point, "e",
4981+ * the exponent's sign and two exponent digits.
4982+ */
4983+ numstr = (char * ) palloc (Num .pre + Num .post + 7 );
4984+ fill_str (numstr , '#' , Num .pre + Num .post + 6 );
4985+ * numstr = ' ' ;
4986+ * (numstr + Num .pre + 1 ) = '.' ;
4987+ }
4988+ else
4989+ {
4990+ snprintf (orgnum , MAXDOUBLEWIDTH + 1 , "%+.*e" , Num .post , value );
4991+
4992+ /*
4993+ * Swap a leading positive sign for a space.
4994+ */
4995+ if (* orgnum == '+' )
4996+ * orgnum = ' ' ;
4997+
4998+ len = strlen (orgnum );
4999+ numstr = orgnum ;
5000+ }
5001+ }
48625002 else
48635003 {
48645004 float4 val = value ;
@@ -4935,6 +5075,34 @@ float8_to_char(PG_FUNCTION_ARGS)
49355075
49365076 if (IS_ROMAN (& Num ))
49375077 numstr = orgnum = int_to_roman ((int ) rint (value ));
5078+ else if (IS_EEEE (& Num ))
5079+ {
5080+ numstr = orgnum = (char * ) palloc (MAXDOUBLEWIDTH + 1 );
5081+ if (isnan (value ) || is_infinite (value ))
5082+ {
5083+ /*
5084+ * Allow 6 characters for the leading sign, the decimal point, "e",
5085+ * the exponent's sign and two exponent digits.
5086+ */
5087+ numstr = (char * ) palloc (Num .pre + Num .post + 7 );
5088+ fill_str (numstr , '#' , Num .pre + Num .post + 6 );
5089+ * numstr = ' ' ;
5090+ * (numstr + Num .pre + 1 ) = '.' ;
5091+ }
5092+ else
5093+ {
5094+ snprintf (orgnum , MAXDOUBLEWIDTH + 1 , "%+.*e" , Num .post , value );
5095+
5096+ /*
5097+ * Swap a leading positive sign for a space.
5098+ */
5099+ if (* orgnum == '+' )
5100+ * orgnum = ' ' ;
5101+
5102+ len = strlen (orgnum );
5103+ numstr = orgnum ;
5104+ }
5105+ }
49385106 else
49395107 {
49405108 float8 val = value ;
0 commit comments