pg-promise returns 整数作为字符串
pg-promise returns integers as strings
我对包含类型 bigint
的列的 table 进行了简单查询。
然而,当我查询它时,pg-promise return将此列的值作为字符串。我在文档中找不到相关信息。这是标准行为吗?
var ids = [180, 120];
db.any('SELECT id_brand, brand from catalog_brand WHERE id_brand in (:csv)', [ids])
.then((data) => {
// return results
});
data
采用以下形式,id 为字符串而不是 int:
[{id_brand: "180", brand: "Ford"}, {id_brand: "120", brand: "Nike"}]
有什么可以指示 pg-promise 到 return 实际类型吗?
下面有很多是历史积累的。但是,如果您使用的是 Node.js v10.4.0 或更高版本,则可以跳过所有这些,跳转到底部的 UPDATE-2
部分。
这确实是标准行为。
bigint
是 64 位的,所有 64 位整数都由底层 node-postgres 驱动程序作为类型 string
返回,而 32 位整数作为 number
.
原因是64位整数在JavaScript中没有精确的原生表示,只能以一定的精度表示64位数字,这不是suitable 表示完整的 64 位数字范围。
另请参阅:How to do 64bit Integer arithmetic in Node.js?
此问题有三种可能的解决方案,选择最适合您的一种:
解决方案 1
不要使用 64 位整数来存储 Id-s,如果您的 table 预计不会超过 40 亿条记录,请改用默认的 int
类型,这为32位,自动返回整数
解决方案 2
即时将返回的 id-s 转换为整数,但请记住,一旦您的 id-s 达到足够高的数字(53 位),转换后的值将变得扭曲/改变。
但是,您可以使用可以将字符串正确转换为 64 位整数的专用库(请参阅上面的 link),但是跨查询使用它可能很笨拙。
即时转换您的 id-s 的示例:
db.each('SELECT id_brand FROM catalog_brand WHERE id_brand in (: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 of the library added support for the native BigInt 类型,如果您是 运行 Node.js v10.4.0 或更高版本,现在可以使用。
让驱动程序自动使用 BigInt for BIGINT
+ BIGSERIAL
:
pgp.pg.types.setTypeParser(20, BigInt); // Type Id 20 = BIGINT | BIGSERIAL
有关详细信息,请参阅项目 WiKi 中的 BigInt Manual。
@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.
不可行(在提到的任何版本中)。
我之前提到的解决方案是复制过去 映射在我当前的项目中。 (检查上面所有的片段以复制它)
或者直接给list使用对应的映射
pgp.pg.types.setTypeParser(20, parseInt);
另一种解决方法是直接使用 pg-types 包。在它的最新版本中。
const types = require('pg-types');
// types.builtins.INT8
否则 PR 由 @vitaly-t 填充,可以在下面的 link 中看到:
https://github.com/brianc/node-postgres/pull/1937/commits/c7666214833715ac2494b81865cfe1ea7cef9289
使用 pg
包 (node-postgres) 更新 pg-types
的版本。
所以一旦它被接受。首字母示例将开始工作。
请注意,我的来源最初是 pg-types
:
的官方 README
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);
});
也去检查上面 vitaly-t 答案的 UPDATE-2 部分
另一种选择,特别是如果您使用 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 (:csv)', [ids])
.then((data) => {
// return results
});
当然,如果id_brand
的值大于最大整数,这对你没有帮助。
只需编写以下代码
const { types } = require('pg');
types.setTypeParser(20, (val) => parseInt(val));
我对包含类型 bigint
的列的 table 进行了简单查询。
然而,当我查询它时,pg-promise return将此列的值作为字符串。我在文档中找不到相关信息。这是标准行为吗?
var ids = [180, 120];
db.any('SELECT id_brand, brand from catalog_brand WHERE id_brand in (:csv)', [ids])
.then((data) => {
// return results
});
data
采用以下形式,id 为字符串而不是 int:
[{id_brand: "180", brand: "Ford"}, {id_brand: "120", brand: "Nike"}]
有什么可以指示 pg-promise 到 return 实际类型吗?
下面有很多是历史积累的。但是,如果您使用的是 Node.js v10.4.0 或更高版本,则可以跳过所有这些,跳转到底部的 UPDATE-2
部分。
这确实是标准行为。
bigint
是 64 位的,所有 64 位整数都由底层 node-postgres 驱动程序作为类型 string
返回,而 32 位整数作为 number
.
原因是64位整数在JavaScript中没有精确的原生表示,只能以一定的精度表示64位数字,这不是suitable 表示完整的 64 位数字范围。
另请参阅:How to do 64bit Integer arithmetic in Node.js?
此问题有三种可能的解决方案,选择最适合您的一种:
解决方案 1
不要使用 64 位整数来存储 Id-s,如果您的 table 预计不会超过 40 亿条记录,请改用默认的 int
类型,这为32位,自动返回整数
解决方案 2
即时将返回的 id-s 转换为整数,但请记住,一旦您的 id-s 达到足够高的数字(53 位),转换后的值将变得扭曲/改变。
但是,您可以使用可以将字符串正确转换为 64 位整数的专用库(请参阅上面的 link),但是跨查询使用它可能很笨拙。
即时转换您的 id-s 的示例:
db.each('SELECT id_brand FROM catalog_brand WHERE id_brand in (: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 of the library added support for the native BigInt 类型,如果您是 运行 Node.js v10.4.0 或更高版本,现在可以使用。
让驱动程序自动使用 BigInt for BIGINT
+ BIGSERIAL
:
pgp.pg.types.setTypeParser(20, BigInt); // Type Id 20 = BIGINT | BIGSERIAL
有关详细信息,请参阅项目 WiKi 中的 BigInt Manual。
@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.
不可行(在提到的任何版本中)。
我之前提到的解决方案是复制过去 映射在我当前的项目中。 (检查上面所有的片段以复制它)
或者直接给list使用对应的映射
pgp.pg.types.setTypeParser(20, parseInt);
另一种解决方法是直接使用 pg-types 包。在它的最新版本中。
const types = require('pg-types');
// types.builtins.INT8
否则 PR 由 @vitaly-t 填充,可以在下面的 link 中看到:
https://github.com/brianc/node-postgres/pull/1937/commits/c7666214833715ac2494b81865cfe1ea7cef9289
使用 pg
包 (node-postgres) 更新 pg-types
的版本。
所以一旦它被接受。首字母示例将开始工作。
请注意,我的来源最初是 pg-types
:
的官方 README
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);
});
也去检查上面 vitaly-t 答案的 UPDATE-2 部分
另一种选择,特别是如果您使用 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 (:csv)', [ids])
.then((data) => {
// return results
});
当然,如果id_brand
的值大于最大整数,这对你没有帮助。
只需编写以下代码
const { types } = require('pg');
types.setTypeParser(20, (val) => parseInt(val));