28

我对包含类型列的表进行了这个简单的查询bigint

但是,当我查询它时,pg-promise将此列的值作为字符串返回。我在文档中找不到有关此的信息。这是标准行为吗?

var ids = [180, 120];

db.any('SELECT id_brand, brand from catalog_brand WHERE id_brand in ($1:csv)', [ids])
    .then((data) => {
        // return results
    });

data采用以下形式,id 为 string 而不是 int:

[{id_brand: "180", brand: "Ford"}, {id_brand: "120", brand: "Nike"}]

有什么可以指示pg-promise返回实际类型吗?

4

4 回答 4

49

下面有很多历史积累。但如果您使用的是 Node.js v10.4.0 或更高版本,则可以跳过所有这些,并跳转UPDATE-2到底部的部分。


这确实是标准行为。

bigint是 64 位的,所有 64 位整数都由底层node-postgres驱动程序返回为 type string,而 32 位整数则返回为number.

原因是 64 位整数在 JavaScript 中没有精确的原生表示,只能以一定精度表示 64 位数字,不适合表示全范围的 64 位数字。

另请参阅:如何在 Node.js 中进行 64 位整数运算?


此问题有三种可能的解决方案,请选择最适合您的一种:

解决方案 1

不要使用 64 位整数来存储 Id-s,如果您的表预计不会有超过 40 亿条记录,请改用默认int类型,即 32 位,并且会自动以整数形式返回。

解决方案 2

即时将返回的 id-s 转换为整数,但请记住,一旦您的 id-s 达到足够高的数字(53 位),转换后的值就会失真/改变。

但是,您可以使用可以将字符串正确转换为 64 位整数的专用库(请参阅上面的链接),但这可能很难跨查询使用。


即时转换 id-s 的示例:

db.each('SELECT id_brand FROM catalog_brand WHERE id_brand in ($1:csv)', [ids], cat=> {
    cat.id_brand = parseInt(cat.id_brand)
})
    .then(rows => {
        // id_brand is now an integer in each row
    });

请参阅Database.each

再举一个例子,记录计数总是返回为bigint,因此获得这些计数的最佳方法是通过内联值转换 + 转换,如下所示:

db.one('SELECT count(*) FROM catalog_brand', [], c => +c.count)
    .then(count => {
        // count = a proper integer value, rather than an object with a string
    });

请参阅Database.one

解决方案 3

您可以使底层node-postgres驱动程序忽略转换安全性并将此类类型转换为任何地方的整数。我不能说这是否是一个好主意,只是可以通过pgp.pg.types.setTypeParser(...)(参见pg-types)轻松完成:

// Convert bigserial + bigint (both with typeId = 20) to integer:
pgp.pg.types.setTypeParser(20, parseInt);

更新-1

通过 TypeScript使用pg-promisev9 或更高版本时,您可以将上面的代码替换为:

pgp.pg.types.setTypeParser(TypeId.INT8, parseInt);

请注意,解决方案 2 和 3 做同样的事情,但在两个不同的级别:

  • 解决方案 2 中的显式本地转换
  • 解决方案 3 中的隐式全局转换

更新-2

该库的9.3.0 版本添加了对本机BigInt类型的支持,如果您运行的是 Node.js v10.4.0 或更高版本,现在您可以使用它。

要使驱动程序自动将BigInt用于BIGINT+ BIGSERIAL

pgp.pg.types.setTypeParser(20, BigInt); // Type Id 20 = BIGINT | BIGSERIAL

有关详细信息,请参阅项目维基中的BigInt 手册

于 2016-08-27T01:40:51.040 回答
8

@vitaly-t 回答解释一切!

对于@vitaly-t 答案的postgree(解决方案3)的隐式全局转换。

这里你需要知道的:

const typesBuiltins = {
    BOOL: 16,
    BYTEA: 17,
    CHAR: 18,
    INT8: 20,
    INT2: 21,
    INT4: 23,
    REGPROC: 24,
    TEXT: 25,
    OID: 26,
    TID: 27,
    XID: 28,
    CID: 29,
    JSON: 114,
    XML: 142,
    PG_NODE_TREE: 194,
    SMGR: 210,
    PATH: 602,
    POLYGON: 604,
    CIDR: 650,
    FLOAT4: 700,
    FLOAT8: 701,
    ABSTIME: 702,
    RELTIME: 703,
    TINTERVAL: 704,
    CIRCLE: 718,
    MACADDR8: 774,
    MONEY: 790,
    MACADDR: 829,
    INET: 869,
    ACLITEM: 1033,
    BPCHAR: 1042,
    VARCHAR: 1043,
    DATE: 1082,
    TIME: 1083,
    TIMESTAMP: 1114,
    TIMESTAMPTZ: 1184,
    INTERVAL: 1186,
    TIMETZ: 1266,
    BIT: 1560,
    VARBIT: 1562,
    NUMERIC: 1700,
    REFCURSOR: 1790,
    REGPROCEDURE: 2202,
    REGOPER: 2203,
    REGOPERATOR: 2204,
    REGCLASS: 2205,
    REGTYPE: 2206,
    UUID: 2950,
    TXID_SNAPSHOT: 2970,
    PG_LSN: 3220,
    PG_NDISTINCT: 3361,
    PG_DEPENDENCIES: 3402,
    TSVECTOR: 3614,
    TSQUERY: 3615,
    GTSVECTOR: 3642,
    REGCONFIG: 3734,
    REGDICTIONARY: 3769,
    JSONB: 3802,
    REGNAMESPACE: 4089,
    REGROLE: 4096
};

你可以在这里找到
https://github.com/brianc/node-pg-types/blob/master/lib/builtins.js

通常你可以通过这种方式访问​​它

const pg = require('pg');

pg.types.setTypeParser(pg.types.builtins.INT8, (value: string) => {
   return parseInt(value);
});

pg.types.setTypeParser(pg.types.builtins.FLOAT8, (value: string) => {
    return parseFloat(value);
});

pg.types.setTypeParser(pg.types.builtins.NUMERIC, (value: string) => {
    return parseFloat(value);
});

这通常会处理所有数字数据。

如果由于某种原因 pg.types.builtins 不可访问(在我的情况下,由于某种原因在打字稿中)。你可以直接复制过去。或者直接使用对应的映射号。

更新(避免混淆)

到目前为止,“pg”:“^7.11.0”。pg 正在使用 pg-types 2.0.1,它根本不包含内置函数。之前的所有版本也是如此。这导致访问pg.types.builtins.不可行(在提到的任何版本中)。

我之前提到的解决方案是像我在当前项目中所做的那样复制过去映射。(检查上面的所有片段以复制它) 在此处输入图像描述 在此处输入图像描述

或者直接使用对应的映射给定的列表。

pgp.pg.types.setTypeParser(20, parseInt);

另一种解决方法是直接使用 pg-types 包。在它的最新版本中。

const types = require('pg-types');
// types.builtins.INT8

否则,一个 PR 由@vitaly-t 填写,可以在下面的链接中看到:
https ://github.com/brianc/node-postgres/pull/1937/commits/c7666214833715ac2494b81865cfe1ea7cef9289

其中更新了pg-typeswithingpg包 (node-postgres) 的版本。 在此处输入图像描述

所以一旦被接受。首字母示例将开始工作。

请注意,我的来源最初是官方自述文件pg-types
https ://github.com/brianc/node-pg-types

在此处输入图像描述

 另一个也是最后一个注意事项:

这涉及到typescript的使用。

pg-types打字稿打字不包括与当前版本一样的内置函数"pg-types": "^2.1.0"。它要更新了。所以你要么自己添加打字。

typeof types & {builtins: {[key in builtinsTypes]: number}}

其中 builtinsTypes 是所有属性名称的并集。

(但是我发现复制粘贴孔对象的速度更快、更短、更干净)。

你可以用下面的枚举来做到这一点

enum TypeId {
        BOOL = 16,
        BYTEA = 17,
        CHAR = 18,
        INT8 = 20,
        INT2 = 21,
        INT4 = 23,
        REGPROC = 24,
        TEXT = 25,
        OID = 26,
        TID = 27,
        XID = 28,
        CID = 29,
        JSON = 114,
        XML = 142,
        PG_NODE_TREE = 194,
        SMGR = 210,
        PATH = 602,
        POLYGON = 604,
        CIDR = 650,
        FLOAT4 = 700,
        FLOAT8 = 701,
        ABSTIME = 702,
        RELTIME = 703,
        TINTERVAL = 704,
        CIRCLE = 718,
        MACADDR8 = 774,
        MONEY = 790,
        MACADDR = 829,
        INET = 869,
        ACLITEM = 1033,
        BPCHAR = 1042,
        VARCHAR = 1043,
        DATE = 1082,
        TIME = 1083,
        TIMESTAMP = 1114,
        TIMESTAMPTZ = 1184,
        INTERVAL = 1186,
        TIMETZ = 1266,
        BIT = 1560,
        VARBIT = 1562,
        NUMERIC = 1700,
        REFCURSOR = 1790,
        REGPROCEDURE = 2202,
        REGOPER = 2203,
        REGOPERATOR = 2204,
        REGCLASS = 2205,
        REGTYPE = 2206,
        UUID = 2950,
        TXID_SNAPSHOT = 2970,
        PG_LSN = 3220,
        PG_NDISTINCT = 3361,
        PG_DEPENDENCIES = 3402,
        TSVECTOR = 3614,
        TSQUERY = 3615,
        GTSVECTOR = 3642,
        REGCONFIG = 3734,
        REGDICTIONARY = 3769,
        JSONB = 3802,
        REGNAMESPACE = 4089,
        REGROLE = 4096
}

pg-promise https://github.com/vitaly-t/pg-promise/blob/v9/typescript/pg-subset.d.ts#L103中完成

一旦一切更新。从 pg 中使用是要走的路。

更新

软件包已更新。您可以按预期使用它。

import { types } from 'pg';

// data parsing
types.setTypeParser(types.builtins.INT8, (value: string) => {
    return parseInt(value);
});

types.setTypeParser(types.builtins.FLOAT8, (value: string) => {
    return parseFloat(value);
});

types.setTypeParser(types.builtins.NUMERIC, (value: string) => {
    return parseFloat(value);
});

也去检查上面vitally-t答案 的部分UPDATE-2 https://stackoverflow.com/a/39176670/7668448

于 2019-07-25T21:41:23.427 回答
1

另一种选择,特别是如果您使用支持 BigInt 的 JavaScript,则将值转换为 SQL 查询中的 int,如下所示:

var ids = [180, 120];

// Cast id_brand to an int to ensure it is not parsed as a string.
db.any('SELECT id_brand::int, brand from catalog_brand WHERE id_brand in ($1:csv)', [ids])
    .then((data) => {
        // return results
    });

当然,如果id_brand值大于最大 int,这对您没有帮助。

于 2019-12-06T12:07:23.530 回答
0

只需编写以下代码

const { types } = require('pg');
types.setTypeParser(20, (val) => parseInt(val));
于 2021-07-28T15:10:33.640 回答