Как вставить десятичное число с типом Diesel PgNumeric?

Я не понимаю, как использовать тип PgNumeric для десятичных чисел. Я заметил в тестах, что 1.0 и -31.0 были вставлены в таблицу с использованием следующих экземпляров:

PgNumeric::Positive { weight: 0, scale: 1, digits: vec![1] } 

а также

PgNumeric::Negative  {weight: 0, scale: 1, digits: vec![31] }

Кажется, я не могу понять, как вставить в таблицу значение с цифрами справа от десятичной дроби (например, 5.4321).


person Cameron Mochrie    schedule 03.07.2016    source источник
comment
Заставить Дизеля делать все, что я хочу, это ... сложно. Вы уверены, что вам нужно использовать объект напрямую, а не что-то вроде "1.0::numeric"?   -  person Shepmaster    schedule 04.07.2016


Ответы (1)


Краткий ответ: PgNumeric::Positive { weight: 0, scale: 4, digits: [5, 4321] }

Еще примеры:

// 9.87654321::numeric
PgNumeric::Positive { weight: 0, scale: 8, digits: [9, 8765, 4321] }

// 12345.6789::numeric
PgNumeric::Positive { weight: 1, scale: 4, digits: [1, 2345, 6789] }

// 100000000.000000002::numeric
PgNumeric::Positive { weight: 2, scale: 9, digits: [1, 0, 0, 0, 0, 2000] }

// 0.3::numeric
PgNumeric::Positive { weight: -1, scale: 1, digits: [3000] }

Похоже, что алгоритм такой:

  1. Сгруппируйте свой номер в блоки по 4 цифры, начиная с десятичной точки и постепенно увеличиваясь. Это цифры.

  2. Подсчитайте количество фрагментов, необходимых для представления целой части, и вычтите один. Это вес.

  3. Подсчитайте количество цифр, необходимое для представления дробной части. Это масштаб.

Испытательная привязь

Вот код, с которым я играю, извлеченный из тестов:

extern crate diesel;

use diesel::*;
use diesel::types::*;

use diesel::pg::data_types::PgNumeric;
use diesel::pg::PgConnection;

 type PgBackend = <PgConnection as Connection>::Backend;

fn main() {
    let query = "100000000.000000002::numeric";
    let expected_value = PgNumeric::Negative {
        digits: vec![31],
        weight: 0,
        scale: 1,
    };
    assert_eq!(expected_value, query_single_value::<Numeric, PgNumeric>(query));
}

fn query_single_value<T, U: Queryable<T, PgBackend>>(sql_str: &str) -> U
    where PgBackend: HasSqlType<T>,
{
    use diesel::expression::dsl::sql;
    let connection = connection();
    select(sql::<T>(sql_str)).first(&connection).unwrap()
}

fn connection() -> PgConnection {
    let result = connection_without_transaction();
    result.begin_test_transaction().unwrap();
    result
}

fn connection_without_transaction() -> PgConnection {
    let connection_url = "postgres://localhost/some_db";
    ::diesel::pg::PgConnection::establish(&connection_url).unwrap()
}

Потенциально полезная справочная информация

Из документации Postgres:

Масштаб числа - это количество десятичных цифр в дробной части справа от десятичной точки. точность числового числа - это общее количество значащих цифр в целом числе, то есть количество цифр по обе стороны от десятичной точки. Таким образом, число 23,5141 имеет точность 6 и шкалу 4.

Однако в коде Postgres говорится:

/*
 * In the NumericShort format, the remaining 14 bits of the header word
 * (n_short.n_header) are allocated as follows: 1 for sign (positive or
 * negative), 6 for dynamic scale, and 7 for weight.  In practice, most
 * commonly-encountered values can be represented this way.
 *
 * In the NumericLong format, the remaining 14 bits of the header word
 * (n_long.n_sign_dscale) represent the display scale; and the weight is
 * stored separately in n_weight.
 */
person Shepmaster    schedule 04.07.2016
comment
Просто хотел добавить к этому и упомянуть, что PgNumeric на самом деле не предназначен для непосредственного использования пользователями. Вместо этого это прямое представление того, как он хранится в PG. Это похоже на наш тип PgTimestamp, на котором построены наши имплименты более высокого уровня, но пользователи должны использовать вместо этого chrono::NaiveDateTime или std::time::SystemTime. Разница в том, что у нас нет вещи более высокого уровня, на которую мы вместо этого сериализуемся. С радостью добавлю, если есть разумный ящик для работы с bigdecimal. - person sgrif; 04.07.2016