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)
10441046if (n -> type != NODE_TYPE_ACTION )
10451047return ;
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+
10471057switch (n -> key -> id )
10481058{
10491059case NUM_9 :
@@ -1217,10 +1227,25 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
12171227break ;
12181228
12191229case 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
12261251return ;
@@ -4145,6 +4170,15 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
41454170if (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,
41534187if (!Np -> is_to_char )
41544188ereport (ERROR ,
41554189(errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
4156- errmsg ("\"RN\" not supported" )));
4190+ errmsg ("\"RN\" not supported for input " )));
41574191
41584192Np -> Num -> lsign = Np -> Num -> pre_lsign_num = Np -> Num -> post =
41594193Np -> 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
42424276elog (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 " ,
42444278Np -> sign ,
42454279Np -> number ,
42464280Np -> Num -> pre ,
@@ -4256,7 +4290,8 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
42564290IS_PLUS (Np -> Num ) ?"Yes" :"No" ,
42574291IS_MINUS (Np -> Num ) ?"Yes" :"No" ,
42584292IS_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)
46264661int_to_roman (DatumGetInt32 (DirectFunctionCall1 (numeric_int4 ,
46274662NumericGetDatum (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+ }
46294697else
46304698{
46314699Numeric val = value ;
@@ -4707,6 +4775,23 @@ int4_to_char(PG_FUNCTION_ARGS)
47074775 */
47084776if (IS_ROMAN (& Num ))
47094777numstr = 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+ }
47104795else
47114796{
47124797if (IS_MULTI (& Num ))
@@ -4785,6 +4870,33 @@ int8_to_char(PG_FUNCTION_ARGS)
47854870numstr = orgnum = int_to_roman (DatumGetInt32 (
47864871DirectFunctionCall1 (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+ }
47884900else
47894901{
47904902if (IS_MULTI (& Num ))
@@ -4859,6 +4971,34 @@ float4_to_char(PG_FUNCTION_ARGS)
48594971
48604972if (IS_ROMAN (& Num ))
48614973numstr = 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+ }
48625002else
48635003{
48645004float4 val = value ;
@@ -4935,6 +5075,34 @@ float8_to_char(PG_FUNCTION_ARGS)
49355075
49365076if (IS_ROMAN (& Num ))
49375077numstr = 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+ }
49385106else
49395107{
49405108float8 val = value ;