您可以在src/backend/utils/adt/numeric.c
中找到实现细节.numeric
的比例和精度不存储在pg_type
中,因为它们不是数据类型的一部分.相关属性是pg_attribute
中的atttypmod
,因为比例和精度是列定义的一部分.
您可以从以下函数中收集在类型修饰符中对比例和精度进行编码的方式:
Datum
numerictypmodout(PG_FUNCTION_ARGS)
{
int32 typmod = PG_GETARG_INT32(0);
char *res = (char *) palloc(64);
if (typmod >= 0)
snprintf(res, 64, "(%d,%d)",
((typmod - VARHDRSZ) >> 16) & 0xffff,
(typmod - VARHDRSZ) & 0xffff);
else
*res = '\0';
PG_RETURN_CSTRING(res);
}
因此,要获得表t
第n
列的精度和比例,可以运行
SELECT (atttypmod - 4) >> 16 & 65535 AS precision,
(atttypmod - 4) & 65535 AS scale
FROM pg_attribute
WHERE attrelid = 't'::regclass
AND attname = 'n';
numeric
的二进制格式在函数numeric_send
中定义:
Datum
numeric_send(PG_FUNCTION_ARGS)
{
Numeric num = PG_GETARG_NUMERIC(0);
NumericVar x;
StringInfoData buf;
int i;
init_var_from_num(num, &x);
pq_begintypsend(&buf);
pq_sendint16(&buf, x.ndigits);
pq_sendint16(&buf, x.weight);
pq_sendint16(&buf, x.sign);
pq_sendint16(&buf, x.dscale);
for (i = 0; i < x.ndigits; i++)
pq_sendint16(&buf, x.digits[i]);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
NumericVar
的文档中描述了各个零件:
/* ----------
* NumericVar is the format we use for arithmetic. The digit-array part
* is the same as the NumericData storage format, but the header is more
* complex.
*
* The value represented by a NumericVar is determined by the sign, weight,
* ndigits, and digits[] array. If it is a "special" value (NaN or Inf)
* then only the sign field matters; ndigits should be zero, and the weight
* and dscale fields are ignored.
*
* Note: the first digit of a NumericVar's value is assumed to be multiplied
* by NBASE ** weight. Another way to say it is that there are weight+1
* digits before the decimal point. It is possible to have weight < 0.
*
* buf points at the physical start of the palloc'd digit buffer for the
* NumericVar. digits points at the first digit in actual use (the one
* with the specified weight). We normally leave an unused digit or two
* (preset to zeroes) between buf and digits, so that there is room to store
* a carry out of the top digit without reallocating space. We just need to
* decrement digits (and increment weight) to make room for the carry digit.
* (There is no such extra space in a numeric value stored in the database,
* only in a NumericVar in memory.)
*
* If buf is NULL then the digit buffer isn't actually palloc'd and should
* not be freed --- see the constants below for an example.
*
* dscale, or display scale, is the nominal precision expressed as number
* of digits after the decimal point (it must always be >= 0 at present).
* dscale may be more than the number of physically stored fractional digits,
* implying that we have suppressed storage of significant trailing zeroes.
* It should never be less than the number of stored digits, since that would
* imply hiding digits that are present. NOTE that dscale is always expressed
* in *decimal* digits, and so it may correspond to a fractional number of
* base-NBASE digits --- divide by DEC_DIGITS to convert to NBASE digits.
*
* rscale, or result scale, is the target precision for a computation.
* Like dscale it is expressed as number of *decimal* digits after the decimal
* point, and is always >= 0 at present.
* Note that rscale is not stored in variables --- it's figured on-the-fly
* from the dscales of the inputs.
*
* While we consistently use "weight" to refer to the base-NBASE weight of
* a numeric value, it is convenient in some scale-related calculations to
* make use of the base-10 weight (ie, the approximate log10 of the value).
* To avoid confusion, such a decimal-units weight is called a "dweight".
*
* NB: All the variable-level functions are written in a style that makes it
* possible to give one and the same variable as argument and destination.
* This is feasible because the digit buffer is separate from the variable.
* ----------
*/
为了方便地处理numeric
的二进制表示,应该使用libpgtypes
library.