1
1
/* -----------------------------------------------------------------------
2
2
* formatting.c
3
3
*
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 $
5
5
*
6
6
*
7
7
* Portions Copyright (c) 1999-2009, PostgreSQL Global Development Group
@@ -335,6 +335,7 @@ typedef struct
335
335
#define NUM_F_MULTI (1 << 11)
336
336
#define NUM_F_PLUS_POST (1 << 12)
337
337
#define NUM_F_MINUS_POST (1 << 13)
338
+ #define NUM_F_EEEE (1 << 14)
338
339
339
340
#define NUM_LSIGN_PRE (-1)
340
341
#define NUM_LSIGN_POST 1
@@ -355,6 +356,7 @@ typedef struct
355
356
#define IS_PLUS (_f ) ((_f)->flag & NUM_F_PLUS)
356
357
#define IS_ROMAN (_f ) ((_f)->flag & NUM_F_ROMAN)
357
358
#define IS_MULTI (_f ) ((_f)->flag & NUM_F_MULTI)
359
+ #define IS_EEEE (_f ) ((_f)->flag & NUM_F_EEEE)
358
360
359
361
/* ----------
360
362
* Format picture cache
@@ -821,7 +823,7 @@ static const KeyWord NUM_keywords[] = {
821
823
{"B" , 1 , NUM_B }, /* B */
822
824
{"C" , 1 , NUM_C }, /* C */
823
825
{"D" , 1 , NUM_D }, /* D */
824
- {"E " , 1 , NUM_E }, /* E */
826
+ {"EEEE " , 4 , NUM_E }, /* E */
825
827
{"FM" , 2 , NUM_FM }, /* F */
826
828
{"G" , 1 , NUM_G }, /* G */
827
829
{"L" , 1 , NUM_L }, /* L */
@@ -837,7 +839,7 @@ static const KeyWord NUM_keywords[] = {
837
839
{"b" , 1 , NUM_B }, /* b */
838
840
{"c" , 1 , NUM_C }, /* c */
839
841
{"d" , 1 , NUM_D }, /* d */
840
- {"e " , 1 , NUM_E }, /* e */
842
+ {"eeee " , 4 , NUM_E }, /* e */
841
843
{"fm" , 2 , NUM_FM }, /* f */
842
844
{"g" , 1 , NUM_G }, /* g */
843
845
{"l" , 1 , NUM_L }, /* l */
@@ -1044,6 +1046,14 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
1044
1046
if (n -> type != NODE_TYPE_ACTION )
1045
1047
return ;
1046
1048
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
+
1047
1057
switch (n -> key -> id )
1048
1058
{
1049
1059
case NUM_9 :
@@ -1217,10 +1227,25 @@ NUMDesc_prepare(NUMDesc *num, FormatNode *n)
1217
1227
break ;
1218
1228
1219
1229
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 ;
1224
1249
}
1225
1250
1226
1251
return ;
@@ -4145,6 +4170,15 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
4145
4170
if (Np -> Num -> zero_start )
4146
4171
-- Np -> Num -> zero_start ;
4147
4172
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
+
4148
4182
/*
4149
4183
* Roman correction
4150
4184
*/
@@ -4153,7 +4187,7 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
4153
4187
if (!Np -> is_to_char )
4154
4188
ereport (ERROR ,
4155
4189
(errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
4156
- errmsg ("\"RN\" not supported" )));
4190
+ errmsg ("\"RN\" not supported for input " )));
4157
4191
4158
4192
Np -> Num -> lsign = Np -> Num -> pre_lsign_num = Np -> Num -> post =
4159
4193
Np -> Num -> pre = Np -> num_pre = Np -> sign = 0 ;
@@ -4240,7 +4274,7 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
4240
4274
4241
4275
#ifdef DEBUG_TO_FROM_CHAR
4242
4276
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 " ,
4244
4278
Np -> sign ,
4245
4279
Np -> number ,
4246
4280
Np -> Num -> pre ,
@@ -4256,7 +4290,8 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number,
4256
4290
IS_PLUS (Np -> Num ) ? "Yes" : "No" ,
4257
4291
IS_MINUS (Np -> Num ) ? "Yes" : "No" ,
4258
4292
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"
4260
4295
);
4261
4296
#endif
4262
4297
@@ -4626,6 +4661,39 @@ numeric_to_char(PG_FUNCTION_ARGS)
4626
4661
int_to_roman (DatumGetInt32 (DirectFunctionCall1 (numeric_int4 ,
4627
4662
NumericGetDatum (x ))));
4628
4663
}
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
+ }
4629
4697
else
4630
4698
{
4631
4699
Numeric val = value ;
@@ -4707,6 +4775,23 @@ int4_to_char(PG_FUNCTION_ARGS)
4707
4775
*/
4708
4776
if (IS_ROMAN (& Num ))
4709
4777
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
+ }
4710
4795
else
4711
4796
{
4712
4797
if (IS_MULTI (& Num ))
@@ -4785,6 +4870,33 @@ int8_to_char(PG_FUNCTION_ARGS)
4785
4870
numstr = orgnum = int_to_roman (DatumGetInt32 (
4786
4871
DirectFunctionCall1 (int84 , Int64GetDatum (value ))));
4787
4872
}
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
+ }
4788
4900
else
4789
4901
{
4790
4902
if (IS_MULTI (& Num ))
@@ -4859,6 +4971,34 @@ float4_to_char(PG_FUNCTION_ARGS)
4859
4971
4860
4972
if (IS_ROMAN (& Num ))
4861
4973
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
+ }
4862
5002
else
4863
5003
{
4864
5004
float4 val = value ;
@@ -4935,6 +5075,34 @@ float8_to_char(PG_FUNCTION_ARGS)
4935
5075
4936
5076
if (IS_ROMAN (& Num ))
4937
5077
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
+ }
4938
5106
else
4939
5107
{
4940
5108
float8 val = value ;
0 commit comments