Ответ 1
Многое ниже того, что накапливалось исторически. Но если вы используете Node.js v10.4.0 или новее, вы можете пропустить все это и перейти в раздел UPDATE-2
внизу.
Это действительно стандартное поведение.
bigint
является 64-разрядным, и все 64-разрядные целые числа возвращаются базовым драйвером node-postgres как тип string
, а 32-разрядные возвращаются как number
.
Причина этого заключается в том, что 64-разрядное целое число не имеет точного собственного представления в JavaScript, которое может представлять только 64-разрядные числа с определенной точностью и которое не подходит для представления полного диапазона 64-разрядных чисел.
См. также: Как выполнить 64-разрядную целочисленную арифметику в Node.js?
Существует три возможных решения этой проблемы, выберите наиболее подходящий для вас:
Решение 1
Не используйте 64-битные целые числа для хранения идентификаторов, если ваша таблица никогда не будет содержать более 4 миллиардов записей, используйте вместо этого тип по умолчанию int
, который является 32-битным и будет возвращен как целое число автоматически.
Решение 2
Преобразуйте возвращаемые идентификаторы в целые числа на лету, но имейте в виду, что как только ваши идентификаторы достигнут достаточно высоких значений (53 бита), преобразованные значения будут искажены/изменены.
Однако вы можете использовать специализированную библиотеку, которая может правильно преобразовать строку в 64-разрядное целое число (см. ссылку выше), но это может быть неудобно для использования в запросах.
Пример преобразования ваших идентификаторов на лету:
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
При использовании pg-promise
v9 или новее через TypeScript вы можете заменить приведенный выше код следующим:
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 в проекте WiKi.
Ответ 2
@vitaly-t ответ, объясни все!
Для неявного глобального преобразования в postgree (решение 3) ответа @vitaly-t.
Вот что вам нужно знать:
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.
не является жизнеспособным (в любой версии, кроме упомянутой).
Решения, как я уже упоминал ранее, заключается в копировании сопоставления, как я делал в своем текущем проекте. (отметьте фрагмент выше, чтобы скопировать его)
![enter image description here]()
Или использовать соответствующее сопоставление непосредственно по списку.
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-types
с помощью пакета pg
(node-postgres).
![enter image description here]()
Так что однажды это принято. Начальные примеры начнут работать.
Обратите внимание, что мой источник изначально был официальным README pg-types
:
https://github.com/brianc/node-pg-types
![enter image description here]()
Еще одно и последнее примечание:
Это касается использования машинописи.
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);
});
Также ознакомьтесь с частью ОБНОВЛЕНИЯ-2 приведенного выше ответа vitaly-t.fooobar.com/info/334794/...