pg-обещание возвращает целые числа в виде строк

У меня есть этот простой запрос к таблице, которая содержит столбец типа bigint,

Однако когда я запрашиваю его, pg-обещание возвращает значения этого столбца в виде строки. Я не могу найти информацию об этом в документации. Это стандартное поведение?

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 в качестве строки вместо int:

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

Есть ли что-нибудь, чтобы дать pg-обещание вернуть фактический тип?

4 ответа

Решение

Это действительно стандартное поведение.

bigint является 64-битным, и все 64-битные целые числа возвращаются базовым драйвером node-postgres как тип string32-битные возвращаются как 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-типы):

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

Обратите внимание, что решения 2 и 3 делают то же самое, но на двух разных уровнях:

  • явное локальное преобразование в решении 2
  • неявное глобальное преобразование в решении 3

@ 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. быть нежизнеспособным (в любой из версий, кроме упомянутой).

Решения, о которых я упоминал ранее, - это копирование за пределы отображения, как я делал в моем текущем проекте. (отметьте фрагмент выше, чтобы скопировать его)

Или использовать соответствующее отображение непосредственно по списку.

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 пакет (узел-постгрес).

Так что, как только это будет принято. Начальные примеры начнут работать.

Обратите внимание, что мой источник изначально был официальным README pg-types:
https://github.com/brianc/node-pg-types

Еще одно и последнее примечание:

Это касается использования машинописи.

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 - это путь.

Другой альтернативой, особенно если вы используете JavaScript без поддержки BigInt, является приведение значения к int в запросе SQL, как таковое:

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.

просто напишите следующий код

      const { types } = require('pg');
types.setTypeParser(20, (val) => parseInt(val));
Другие вопросы по тегам