【问题标题】:pg-promise returns integers as stringspg-promise 将整数作为字符串返回
【发布时间】:2017-01-03 06:09:23
【问题描述】:

我对包含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返回实际类型吗?

【问题讨论】:

    标签: node.js pg-promise


    【解决方案1】:

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


    这确实是标准行为。

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

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

    另见:How to do 64bit Integer arithmetic in Node.js?


    这个问题有三种可能的解决方案,选择最适合你的一个:

    解决方案 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);
    

    UPDATE-1

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

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

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

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

    UPDATE-2

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

    要让驱动程序自动使用BigInt for BIGINT + BIGSERIAL

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

    更多详情,请参阅项目维基中的BigInt Manual

    【讨论】:

    • 在使用COUNT(*) 处理查询时,我在使用Do not know how to serialize a BigInt 类型的UPDATE 2 中的解决方案时遇到了麻烦。我看到查询中的简单类型转换工作正常。类似'SELECT count(*)::int as value ...。你会建议不要这样做吗?
    • @umbe1987 如果您收到该错误,则表示您在某处的数据上使用了JSON.stringify,您绝对不应该这样做。要序列化包含BigInt-s 的数据,请使用pgp.as.json,如NigInt Manual 中所述。
    • 谢谢@vitaly-t。经过一些试验和错误,我正确设置了typeParsers,并在我的 express js 应用程序中使用res.send(pgp.as.json(result, true)); 以 JSON 格式从服务器发送数据,一切正常!
    • 你还能写一些关于数值列的注释吗?该库也为它返回字符串
    【解决方案2】:

    @vitaly-t 回答解释一切!

    对于@vitaly-t answer的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(在我的情况下,由于某种原因在 typescript 中)。你可以直接复制过去。或者直接使用对应的映射号。

    更新(避免混淆)

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

    解决方案 就像我在当前项目中所做的那样,复制过去 映射。 (检查上面所有的sn-p来复制它)

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

    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-typespg 包(node-postgres)的版本。

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

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

     另一个也是最后一点:

    这涉及到 typescript 的使用。

    pg-types typescript 打字不包括与当前版本"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);
    });
    

    还请检查上述 vitaly-t 答案的 UPDATE-2 部分 https://stackoverflow.com/a/39176670/7668448

    【讨论】:

    • 您使用的是哪个版本的pg?据我所知,它永远不会在那里公开builtins
    • 您提供的信息不准确。我已经跟进了,打开了this issue
    • "pg": "^7.11.0"(带打字稿)
    • 它不适用于任何现有版本的pg。不知道你是如何测试它的。请参阅我上面链接的问题。
    • UPDATE-2在我的回答中添加;)
    【解决方案3】:

    另一种选择,特别是如果您使用 JavaScript 没有 BigInt 支持,则将值转换为 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,这对您没有帮助。

    【讨论】:

    • 不是一个好的解决方案,修改所有查询,当你可以用一行代码为整个项目实现它时 - pgp.pg.types.setTypeParser(20, parseInt)
    【解决方案4】:

    只需编写以下代码

    const { types } = require('pg');
    types.setTypeParser(20, (val) => parseInt(val));
    

    【讨论】:

    • 错误的库,不好的例子。
    猜你喜欢
    • 2021-01-21
    • 1970-01-01
    • 2016-07-01
    • 2015-12-28
    • 2012-03-15
    • 2019-04-24
    • 2017-04-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多