@@ -815,6 +815,62 @@ numeric_is_integral(Numeric num)
815815 return (arg .ndigits == 0 || arg .ndigits <= arg .weight + 1 );
816816}
817817
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+
818874/*
819875 * numeric_maximum_size() -
820876 *
@@ -826,11 +882,11 @@ numeric_maximum_size(int32 typmod)
826882 int precision ;
827883 int numeric_digits ;
828884
829- if (typmod < ( int32 ) ( VARHDRSZ ))
885+ if (! is_valid_numeric_typmod ( typmod ))
830886 return -1 ;
831887
832888 /* precision (ie, max # of digits) is in upper bits of typmod */
833- precision = (( typmod - VARHDRSZ ) >> 16 ) & 0xffff ;
889+ precision = numeric_typmod_precision ( typmod ) ;
834890
835891 /*
836892 * This formula computes the maximum number of NumericDigits we could need
@@ -1084,20 +1140,20 @@ numeric_support(PG_FUNCTION_ARGS)
10841140 Node * source = (Node * ) linitial (expr -> args );
10851141 int32 old_typmod = exprTypmod (source );
10861142 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 ) ;
10911147
10921148 /*
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
10951151 * constrained, and we're OK if the scale is unchanged and the
10961152 * precision is not decreasing. See further notes in function
10971153 * header comment.
10981154 */
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 ) &&
11011157 new_scale == old_scale && new_precision >= old_precision ))
11021158 ret = relabel_to_typmod (source , new_typmod );
11031159 }
@@ -1119,11 +1175,11 @@ numeric (PG_FUNCTION_ARGS)
11191175 Numeric num = PG_GETARG_NUMERIC (0 );
11201176 int32 typmod = PG_GETARG_INT32 (1 );
11211177 Numeric new ;
1122- int32 tmp_typmod ;
11231178 int precision ;
11241179 int scale ;
11251180 int ddigits ;
11261181 int maxdigits ;
1182+ int dscale ;
11271183 NumericVar var ;
11281184
11291185 /*
@@ -1140,17 +1196,19 @@ numeric (PG_FUNCTION_ARGS)
11401196 * If the value isn't a valid type modifier, simply return a copy of the
11411197 * input value
11421198 */
1143- if (typmod < ( int32 ) ( VARHDRSZ ))
1199+ if (! is_valid_numeric_typmod ( typmod ))
11441200 PG_RETURN_NUMERIC (duplicate_numeric (num ));
11451201
11461202 /*
11471203 * Get the precision and scale out of the typmod value
11481204 */
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 );
11521207 maxdigits = precision - scale ;
11531208
1209+ /* The target display scale is non-negative */
1210+ dscale = Max (scale , 0 );
1211+
11541212 /*
11551213 * If the number is certainly in bounds and due to the target scale no
11561214 * rounding could be necessary, just make a copy of the input and modify
@@ -1160,17 +1218,17 @@ numeric (PG_FUNCTION_ARGS)
11601218 */
11611219 ddigits = (NUMERIC_WEIGHT (num ) + 1 ) * DEC_DIGITS ;
11621220 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 ))
11641222 || !NUMERIC_IS_SHORT (num )))
11651223 {
11661224 new = duplicate_numeric (num );
11671225 if (NUMERIC_IS_SHORT (num ))
11681226 new -> choice .n_short .n_header =
11691227 (num -> choice .n_short .n_header & ~NUMERIC_SHORT_DSCALE_MASK )
1170- | (scale << NUMERIC_SHORT_DSCALE_SHIFT );
1228+ | (dscale << NUMERIC_SHORT_DSCALE_SHIFT );
11711229 else
11721230 new -> choice .n_long .n_sign_dscale = NUMERIC_SIGN (new ) |
1173- ((uint16 ) scale & NUMERIC_DSCALE_MASK );
1231+ ((uint16 ) dscale & NUMERIC_DSCALE_MASK );
11741232 PG_RETURN_NUMERIC (new );
11751233 }
11761234
@@ -1206,12 +1264,12 @@ numerictypmodin(PG_FUNCTION_ARGS)
12061264 (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
12071265 errmsg ("NUMERIC precision %d must be between 1 and %d" ,
12081266 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 )
12101268 ereport (ERROR ,
12111269 (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 ]);
12151273 }
12161274 else if (n == 1 )
12171275 {
@@ -1221,7 +1279,7 @@ numerictypmodin(PG_FUNCTION_ARGS)
12211279 errmsg ("NUMERIC precision %d must be between 1 and %d" ,
12221280 tl [0 ], NUMERIC_MAX_PRECISION )));
12231281 /* scale defaults to zero */
1224- typmod = (tl [0 ] << 16 ) + VARHDRSZ ;
1282+ typmod = make_numeric_typmod (tl [0 ], 0 ) ;
12251283 }
12261284 else
12271285 {
@@ -1240,10 +1298,10 @@ numerictypmodout(PG_FUNCTION_ARGS)
12401298 int32 typmod = PG_GETARG_INT32 (0 );
12411299 char * res = (char * ) palloc (64 );
12421300
1243- if (typmod >= 0 )
1301+ if (is_valid_numeric_typmod ( typmod ) )
12441302 snprintf (res , 64 , "(%d,%d)" ,
1245- (( typmod - VARHDRSZ ) >> 16 ) & 0xffff ,
1246- (typmod - VARHDRSZ ) & 0xffff );
1303+ numeric_typmod_precision ( typmod ) ,
1304+ numeric_typmod_scale (typmod ) );
12471305 else
12481306 * res = '\0' ;
12491307
@@ -7428,18 +7486,21 @@ apply_typmod(NumericVar *var, int32 typmod)
74287486 int ddigits ;
74297487 int i ;
74307488
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 ))
74337491 return ;
74347492
7435- typmod -= VARHDRSZ ;
7436- precision = (typmod >> 16 ) & 0xffff ;
7437- scale = typmod & 0xffff ;
7493+ precision = numeric_typmod_precision (typmod );
7494+ scale = numeric_typmod_scale (typmod );
74387495 maxdigits = precision - scale ;
74397496
74407497 /* Round to target scale (and set var->dscale) */
74417498 round_var (var , scale );
74427499
7500+ /* but don't allow var->dscale to be negative */
7501+ if (var -> dscale < 0 )
7502+ var -> dscale = 0 ;
7503+
74437504 /*
74447505 * Check for overflow - note we can't do this before rounding, because
74457506 * rounding could raise the weight. Also note that the var's weight could
@@ -7514,12 +7575,11 @@ apply_typmod_special(Numeric num, int32 typmod)
75147575 return ;
75157576
75167577 /* Do nothing if we have a default typmod (-1) */
7517- if (typmod < ( int32 ) ( VARHDRSZ ))
7578+ if (! is_valid_numeric_typmod ( typmod ))
75187579 return ;
75197580
7520- typmod -= VARHDRSZ ;
7521- precision = (typmod >> 16 ) & 0xffff ;
7522- scale = typmod & 0xffff ;
7581+ precision = numeric_typmod_precision (typmod );
7582+ scale = numeric_typmod_scale (typmod );
75237583
75247584 ereport (ERROR ,
75257585 (errcode (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE ),
0 commit comments