我尝试将通过 libpq 查询的数据以二进制格式转换为 C 中的 Arrow 格式。为此,我通过 PQftype() 查询相应列的数据类型 Oids 并将它们与 Arrow 数据类型进行匹配。但我不知道如何处理数字。
查询SELECT oid,typname FROM pg_type;
返回数字1700,但如何获得精度和小数位数?
我无法在文档中找到有用的信息。我是不是找错地方了? https://www.postgresql.org/docs/13/datatype-numeric.html#DATATYPE-NUMERIC-DECIMAL
我可以从二进制数字中得到什么?
提前致谢:)
编辑
因此,在 Laurenz 的帮助下,我成功地组合了一个函数,该函数将二进制形式的数字从 libpq 转换为基本的字符串表示形式。当然,只有当您不打算将数字转换为字符串时,二进制结果才有意义,但它有助于理解数字的格式。
二进制形式基本上是具有不同含义的 2 字节整数的列表,并且数字整数只是以字符串形式连接起来。这是 49273.64,二进制和字段的精度为 20,小数位数为 2:
ndigits | 00000000 | 00000011 | weight | 00000000 | 00000001 | sign | 00000000 | 00000000 | dscale | 00000000 | 00000010 | digits | 00000000 | 00000100 | 00100100 | 00111001 | 00011001 | 00000000
ndigits: 3, weight: 1, sign: 0, dscale: 2, digits: 4 | 9273 | 6400
char *getStrFromNumeric(u_int16_t *numvar){
u_int16_t ndigits = ntohs(numvar[0]); // how many u_int16_t at numvar[4]
int16_t dscale = ntohs(numvar[3]); // how many char digits after decimal point
int16_t weight = ntohs(numvar[1])+1; // weight+1 is how many u_int16_t from numvar[4] are before decimal point. here weight already gets +1 at initialization.
char *result = (char *)malloc(sizeof(char)*(weight+dscale)+1+1+2); // +1+1 -> '\0' and '.'
char *copyStr = (char *) malloc(sizeof (char)*5);
int strindex = 0;
int numvarindex = 0;
while(weight>0){
sprintf(copyStr, "%d", ntohs(numvar[numvarindex+4]));
sprintf(&(result[strindex]), "%s", copyStr);
strindex += strlen(copyStr);
numvarindex++;
weight--;
}
sprintf(&(result[strindex]), ".");
strindex++;
while(dscale>0){
sprintf(copyStr, "%d", ntohs(numvar[numvarindex+4]));
dscale -= strlen(copyStr);
sprintf(&(result[strindex]), "%s", copyStr);
strindex += strlen(copyStr);
numvarindex++;
}
sprintf(&(result[strindex]), "\0");
return result;
}
最佳答案
您可以在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.
* ----------
*/
为了方便处理数字
的二进制表示,您应该使用 libpgtypes
library .
关于c - 如何用 C 处理 PostgreSQL 中的数字数据类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72788883/