@@ -815,6 +815,62 @@ numeric_is_integral(Numeric num)
815
815
return (arg .ndigits == 0 || arg .ndigits <= arg .weight + 1 );
816
816
}
817
817
818
+ /*
819
+ * make_numeric_typmod() -
820
+ *
821
+ * Pack numeric precision and scale values into a typmod. The upper 16 bits
822
+ * are used for the precision (though actually not all these bits are needed,
823
+ * since the maximum allowed precision is 1000). The lower 16 bits are for
824
+ * the scale, but since the scale is constrained to the range [-1000, 1000],
825
+ * we use just the lower 11 of those 16 bits, and leave the remaining 5 bits
826
+ * unset, for possible future use.
827
+ *
828
+ * For purely historical reasons VARHDRSZ is then added to the result, thus
829
+ * the unused space in the upper 16 bits is not all as freely available as it
830
+ * might seem. (We can't let the result overflow to a negative int32, as
831
+ * other parts of the system would interpret that as not-a-valid-typmod.)
832
+ */
833
+ static inline int32
834
+ make_numeric_typmod (int precision , int scale )
835
+ {
836
+ return ((precision << 16 ) | (scale & 0x7ff )) + VARHDRSZ ;
837
+ }
838
+
839
+ /*
840
+ * Because of the offset, valid numeric typmods are at least VARHDRSZ
841
+ */
842
+ static inline bool
843
+ is_valid_numeric_typmod (int32 typmod )
844
+ {
845
+ return typmod >= (int32 ) VARHDRSZ ;
846
+ }
847
+
848
+ /*
849
+ * numeric_typmod_precision() -
850
+ *
851
+ * Extract the precision from a numeric typmod --- see make_numeric_typmod().
852
+ */
853
+ static inline int
854
+ numeric_typmod_precision (int32 typmod )
855
+ {
856
+ return ((typmod - VARHDRSZ ) >> 16 ) & 0xffff ;
857
+ }
858
+
859
+ /*
860
+ * numeric_typmod_scale() -
861
+ *
862
+ * Extract the scale from a numeric typmod --- see make_numeric_typmod().
863
+ *
864
+ * Note that the scale may be negative, so we must do sign extension when
865
+ * unpacking it. We do this using the bit hack (x^1024)-1024, which sign
866
+ * extends an 11-bit two's complement number x.
867
+ */
868
+ static inline int
869
+ numeric_typmod_scale (int32 typmod )
870
+ {
871
+ return (((typmod - VARHDRSZ ) & 0x7ff ) ^ 1024 ) - 1024 ;
872
+ }
873
+
818
874
/*
819
875
* numeric_maximum_size() -
820
876
*
@@ -826,11 +882,11 @@ numeric_maximum_size(int32 typmod)
826
882
int precision ;
827
883
int numeric_digits ;
828
884
829
- if (typmod < ( int32 ) ( VARHDRSZ ))
885
+ if (! is_valid_numeric_typmod ( typmod ))
830
886
return -1 ;
831
887
832
888
/* precision (ie, max # of digits) is in upper bits of typmod */
833
- precision = (( typmod - VARHDRSZ ) >> 16 ) & 0xffff ;
889
+ precision = numeric_typmod_precision ( typmod ) ;
834
890
835
891
/*
836
892
* This formula computes the maximum number of NumericDigits we could need
@@ -1084,20 +1140,20 @@ numeric_support(PG_FUNCTION_ARGS)
1084
1140
Node * source = (Node * ) linitial (expr -> args );
1085
1141
int32 old_typmod = exprTypmod (source );
1086
1142
int32 new_typmod = DatumGetInt32 (((Const * ) typmod )-> constvalue );
1087
- int32 old_scale = (old_typmod - VARHDRSZ ) & 0xffff ;
1088
- int32 new_scale = (new_typmod - VARHDRSZ ) & 0xffff ;
1089
- int32 old_precision = (old_typmod - VARHDRSZ ) >> 16 & 0xffff ;
1090
- int32 new_precision = (new_typmod - VARHDRSZ ) >> 16 & 0xffff ;
1143
+ int32 old_scale = numeric_typmod_scale (old_typmod ) ;
1144
+ int32 new_scale = numeric_typmod_scale (new_typmod ) ;
1145
+ int32 old_precision = numeric_typmod_precision (old_typmod ) ;
1146
+ int32 new_precision = numeric_typmod_precision (new_typmod ) ;
1091
1147
1092
1148
/*
1093
- * If new_typmod < VARHDRSZ , the destination is unconstrained;
1094
- * that's always OK. If old_typmod >= VARHDRSZ , the source is
1149
+ * If new_typmod is invalid , the destination is unconstrained;
1150
+ * that's always OK. If old_typmod is valid , the source is
1095
1151
* constrained, and we're OK if the scale is unchanged and the
1096
1152
* precision is not decreasing. See further notes in function
1097
1153
* header comment.
1098
1154
*/
1099
- if (new_typmod < ( int32 ) VARHDRSZ ||
1100
- (old_typmod >= ( int32 ) VARHDRSZ &&
1155
+ if (! is_valid_numeric_typmod ( new_typmod ) ||
1156
+ (is_valid_numeric_typmod ( old_typmod ) &&
1101
1157
new_scale == old_scale && new_precision >= old_precision ))
1102
1158
ret = relabel_to_typmod (source , new_typmod );
1103
1159
}
@@ -1119,11 +1175,11 @@ numeric (PG_FUNCTION_ARGS)
1119
1175
Numeric num = PG_GETARG_NUMERIC (0 );
1120
1176
int32 typmod = PG_GETARG_INT32 (1 );
1121
1177
Numeric new ;
1122
- int32 tmp_typmod ;
1123
1178
int precision ;
1124
1179
int scale ;
1125
1180
int ddigits ;
1126
1181
int maxdigits ;
1182
+ int dscale ;
1127
1183
NumericVar var ;
1128
1184
1129
1185
/*
@@ -1140,17 +1196,19 @@ numeric (PG_FUNCTION_ARGS)
1140
1196
* If the value isn't a valid type modifier, simply return a copy of the
1141
1197
* input value
1142
1198
*/
1143
- if (typmod < ( int32 ) ( VARHDRSZ ))
1199
+ if (! is_valid_numeric_typmod ( typmod ))
1144
1200
PG_RETURN_NUMERIC (duplicate_numeric (num ));
1145
1201
1146
1202
/*
1147
1203
* Get the precision and scale out of the typmod value
1148
1204
*/
1149
- tmp_typmod = typmod - VARHDRSZ ;
1150
- precision = (tmp_typmod >> 16 ) & 0xffff ;
1151
- scale = tmp_typmod & 0xffff ;
1205
+ precision = numeric_typmod_precision (typmod );
1206
+ scale = numeric_typmod_scale (typmod );
1152
1207
maxdigits = precision - scale ;
1153
1208
1209
+ /* The target display scale is non-negative */
1210
+ dscale = Max (scale , 0 );
1211
+
1154
1212
/*
1155
1213
* If the number is certainly in bounds and due to the target scale no
1156
1214
* rounding could be necessary, just make a copy of the input and modify
@@ -1160,17 +1218,17 @@ numeric (PG_FUNCTION_ARGS)
1160
1218
*/
1161
1219
ddigits = (NUMERIC_WEIGHT (num ) + 1 ) * DEC_DIGITS ;
1162
1220
if (ddigits <= maxdigits && scale >= NUMERIC_DSCALE (num )
1163
- && (NUMERIC_CAN_BE_SHORT (scale , NUMERIC_WEIGHT (num ))
1221
+ && (NUMERIC_CAN_BE_SHORT (dscale , NUMERIC_WEIGHT (num ))
1164
1222
|| !NUMERIC_IS_SHORT (num )))
1165
1223
{
1166
1224
new = duplicate_numeric (num );
1167
1225
if (NUMERIC_IS_SHORT (num ))
1168
1226
new -> choice .n_short .n_header =
1169
1227
(num -> choice .n_short .n_header & ~NUMERIC_SHORT_DSCALE_MASK )
1170
- | (scale << NUMERIC_SHORT_DSCALE_SHIFT );
1228
+ | (dscale << NUMERIC_SHORT_DSCALE_SHIFT );
1171
1229
else
1172
1230
new -> choice .n_long .n_sign_dscale = NUMERIC_SIGN (new ) |
1173
- ((uint16 ) scale & NUMERIC_DSCALE_MASK );
1231
+ ((uint16 ) dscale & NUMERIC_DSCALE_MASK );
1174
1232
PG_RETURN_NUMERIC (new );
1175
1233
}
1176
1234
@@ -1206,12 +1264,12 @@ numerictypmodin(PG_FUNCTION_ARGS)
1206
1264
(errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
1207
1265
errmsg ("NUMERIC precision %d must be between 1 and %d" ,
1208
1266
tl [0 ], NUMERIC_MAX_PRECISION )));
1209
- if (tl [1 ] < 0 || tl [1 ] > tl [ 0 ] )
1267
+ if (tl [1 ] < NUMERIC_MIN_SCALE || tl [1 ] > NUMERIC_MAX_SCALE )
1210
1268
ereport (ERROR ,
1211
1269
(errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
1212
- errmsg ("NUMERIC scale %d must be between 0 and precision %d" ,
1213
- tl [1 ], tl [ 0 ] )));
1214
- typmod = (( tl [0 ] << 16 ) | tl [1 ]) + VARHDRSZ ;
1270
+ errmsg ("NUMERIC scale %d must be between %d and %d" ,
1271
+ tl [1 ], NUMERIC_MIN_SCALE , NUMERIC_MAX_SCALE )));
1272
+ typmod = make_numeric_typmod ( tl [0 ], tl [1 ]);
1215
1273
}
1216
1274
else if (n == 1 )
1217
1275
{
@@ -1221,7 +1279,7 @@ numerictypmodin(PG_FUNCTION_ARGS)
1221
1279
errmsg ("NUMERIC precision %d must be between 1 and %d" ,
1222
1280
tl [0 ], NUMERIC_MAX_PRECISION )));
1223
1281
/* scale defaults to zero */
1224
- typmod = (tl [0 ] << 16 ) + VARHDRSZ ;
1282
+ typmod = make_numeric_typmod (tl [0 ], 0 ) ;
1225
1283
}
1226
1284
else
1227
1285
{
@@ -1240,10 +1298,10 @@ numerictypmodout(PG_FUNCTION_ARGS)
1240
1298
int32 typmod = PG_GETARG_INT32 (0 );
1241
1299
char * res = (char * ) palloc (64 );
1242
1300
1243
- if (typmod >= 0 )
1301
+ if (is_valid_numeric_typmod ( typmod ) )
1244
1302
snprintf (res , 64 , "(%d,%d)" ,
1245
- (( typmod - VARHDRSZ ) >> 16 ) & 0xffff ,
1246
- (typmod - VARHDRSZ ) & 0xffff );
1303
+ numeric_typmod_precision ( typmod ) ,
1304
+ numeric_typmod_scale (typmod ) );
1247
1305
else
1248
1306
* res = '\0' ;
1249
1307
@@ -7428,18 +7486,21 @@ apply_typmod(NumericVar *var, int32 typmod)
7428
7486
int ddigits ;
7429
7487
int i ;
7430
7488
7431
- /* Do nothing if we have a default typmod (-1) */
7432
- if (typmod < ( int32 ) ( VARHDRSZ ))
7489
+ /* Do nothing if we have an invalid typmod */
7490
+ if (! is_valid_numeric_typmod ( typmod ))
7433
7491
return ;
7434
7492
7435
- typmod -= VARHDRSZ ;
7436
- precision = (typmod >> 16 ) & 0xffff ;
7437
- scale = typmod & 0xffff ;
7493
+ precision = numeric_typmod_precision (typmod );
7494
+ scale = numeric_typmod_scale (typmod );
7438
7495
maxdigits = precision - scale ;
7439
7496
7440
7497
/* Round to target scale (and set var->dscale) */
7441
7498
round_var (var , scale );
7442
7499
7500
+ /* but don't allow var->dscale to be negative */
7501
+ if (var -> dscale < 0 )
7502
+ var -> dscale = 0 ;
7503
+
7443
7504
/*
7444
7505
* Check for overflow - note we can't do this before rounding, because
7445
7506
* rounding could raise the weight. Also note that the var's weight could
@@ -7514,12 +7575,11 @@ apply_typmod_special(Numeric num, int32 typmod)
7514
7575
return ;
7515
7576
7516
7577
/* Do nothing if we have a default typmod (-1) */
7517
- if (typmod < ( int32 ) ( VARHDRSZ ))
7578
+ if (! is_valid_numeric_typmod ( typmod ))
7518
7579
return ;
7519
7580
7520
- typmod -= VARHDRSZ ;
7521
- precision = (typmod >> 16 ) & 0xffff ;
7522
- scale = typmod & 0xffff ;
7581
+ precision = numeric_typmod_precision (typmod );
7582
+ scale = numeric_typmod_scale (typmod );
7523
7583
7524
7584
ereport (ERROR ,
7525
7585
(errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
0 commit comments