Tedious: القيمة خارج الحدود للأنواع الرقمية

تم إنشاؤها على ٣١ أكتوبر ٢٠١٦  ·  5تعليقات  ·  مصدر: tediousjs/tedious

أواجه مشكلة حيث تمنع قيمة عددية صحيحة بيان insert الخاص بي بشكل مضجر. يعمل إدراج SQL اليدوي في قاعدة البيانات بنفس القيمة العددية.

إدراج SQL يدويًا:

INSERT INTO foo (id, foo_id, timestamp, value, value2)  
VALUES ('ff3bd5e2-dbd8-424b-bb4f-455df1fe9f9c', 'b3a0a2c6-3b58-4659-8b4f-9466cede43be',  
        '2016-11-01T00:00:00+00:00', null, 66666.55);

هذا هو الكود الخاص بي:

const bluebird = require('bluebird');
const sql = require('mssql');

const db = require('./database.js');

sql.Promise = bluebird;

const COLUMNS = {
  id: 'id',
  foo_id: 'foo_id',
  timestamp: 'timestamp',
  value: 'value',
  value2: 'value2'
};

exports.create = data => {
  return db.getConnection()
    .then(conn => {
      const ps = new sql.PreparedStatement(conn);
      ps.input(COLUMNS.id, sql.UniqueIdentifier);
      ps.input(COLUMNS.foo_id, sql.UniqueIdentifier);
      ps.input(COLUMNS.timestamp, sql.DateTimeOffset(0));
      ps.input(COLUMNS.value, sql.Numeric(30, 15));
      ps.input(COLUMNS.value2, sql.Numeric(30, 15));

      // Have to convert string to JavaScript date for insert to work
      data.timestamp = new Date(data.timestamp);
      const header = Object.keys(COLUMNS).map(key => COLUMNS[key]).join();

      const query = `INSERT INTO foo (${header}) VALUES (@${COLUMNS.id},` +
        ` @${COLUMNS.foo_id}, @${COLUMNS.timestamp}, @${COLUMNS.value},` +
        ` @${COLUMNS.value2})`;
      return ps.prepare(query)
        .then(() => ps.execute(data)
          // Releases the connection back to the pool
          .then(() => ps.unprepare()))
        .then(() => bluebird.resolve());
    });
};

يبدو أن الشفرة المملة تقوم بتحويل 66666.55 إلى عدد صحيح بقيمة 66666550000000000000. ثم يحاول أخذ باقي وحاصل هذه القيمة وكتابته في مخزن مؤقت باستخدام writeUInt32LE.

لماذا تقوم الشفرة المملة بتحويل أرقام الفاصلة العائمة إلى أعداد صحيحة وكتابتها إلى مخازن مؤقتة بدلاً من الاحتفاظ برقم الفاصلة العائمة واستخدام الوظيفة المضمنة writeDoubleLE للمخازن المؤقتة؟

هذه المشكلة تمنعني من المضي قدمًا.

known issue

التعليق الأكثر فائدة

في حالتي ، اضطررت إلى استخدام حل بديل لأن Numeric تم إعداده بواسطة مكتبة فوق Tedious (Sequelize). في الأساس ، أقوم بتنسيق المعلمة بحيث يحدث التحويل العشري داخل قاعدة البيانات وليس في الكود. لجميع الأرقام ، لا بد لي من استخدام هذا:

const value_stored = value.toFixed(desired_precision);

هذا حتى لا تفقد الدقة ولكن أيضًا لا تخنق أي شيء بين الكود الخاص بي وقاعدة البيانات التي تضع افتراضات حول الحجم والحجم لا أعرفها أو أتحكم فيها. بالطبع ، هذا أقل كفاءة في عرض النطاق الترددي من استخدام النوع الصحيح ولكن في حالتي <30 بايت لكل عقوبة سجل غير مهم.

ال 5 كومينتر

لماذا تقوم الشفرة المملة بتحويل أرقام الفاصلة العائمة إلى أعداد صحيحة ...

لأن هذا هو ما يفرضه بروتوكول TDS للقيم الرقمية.
2.2.5.5.1.6 عشري / رقمي

بالنسبة إلى sql.Numeric(30, 15) ، _p_ يساوي 30 ، و _s_ 15. إذن سيكون الرقم الصحيح 66666550000000000000 (66666.55 * 10 ^ 15) ، ويجب تمثيله بـ 16 بايت.

https://github.com/tediousjs/tedious/blob/3cb8586c2be217d9b31ef72f866986d85614920d/src/data-type.js#L519

  ...
  buffer.writeUInt64LE(value);        // 8 bytes
  buffer.writeUInt32LE(0x00000000);   // 4 bytes
  buffer.writeUInt32LE(0x00000000);   // 4 bytes

السطر الأول هو الذي به المشكلة.

https://github.com/tediousjs/tedious/blob/master/src/tracking-buffer/writable-tracking-buffer.js#L125
لا يوجد Buffer.writeInt64LE ، لذا يجب القيام به في جزأين 32 بت.

 writeUInt64LE(value) {
    this.writeInt32LE(value & -1);
    return this.writeUInt32LE(Math.floor(value * SHIFT_RIGHT_32));
  }

التحول الصحيح 32 بت هو المكان الذي يبدو أن المشكلة تظهر فيه.
ينتج Math.floor(66666550000000000000 * SHIFT_RIGHT_32) 15522015746 وهو 0x39D2F2A02 . هذا يتطلب 34 بت.

الآن أرى أن المشكلة هي أن 66666550000000000000 هو 0x39d2f2a02aed16000 ، الأمر الذي يتطلب أكثر من 64 بت. لكن هذا يثير مشكلة أنه لا يمكن تمثيل جميع الأعداد الصحيحة التي تحتوي على أكثر من 53 بت بشكل صحيح في نوع _Number_ الخاص بجافا سكريبت. قد يتطلب التعامل مع هذا الأمر بشكل صحيح بعض حك الرأس وبعض اختبارات الوحدة الجديدة.

كحل بديل ، من المحتمل أن يعمل استخدام مقياس أصغر.

pekim شكرا

في حالتي ، اضطررت إلى استخدام حل بديل لأن Numeric تم إعداده بواسطة مكتبة فوق Tedious (Sequelize). في الأساس ، أقوم بتنسيق المعلمة بحيث يحدث التحويل العشري داخل قاعدة البيانات وليس في الكود. لجميع الأرقام ، لا بد لي من استخدام هذا:

const value_stored = value.toFixed(desired_precision);

هذا حتى لا تفقد الدقة ولكن أيضًا لا تخنق أي شيء بين الكود الخاص بي وقاعدة البيانات التي تضع افتراضات حول الحجم والحجم لا أعرفها أو أتحكم فيها. بالطبع ، هذا أقل كفاءة في عرض النطاق الترددي من استخدام النوع الصحيح ولكن في حالتي <30 بايت لكل عقوبة سجل غير مهم.

في حالتي ، اضطررت إلى استخدام حل بديل لأن Numeric تم إعداده بواسطة مكتبة فوق Tedious (Sequelize). في الأساس ، أقوم بتنسيق المعلمة بحيث يحدث التحويل العشري داخل قاعدة البيانات وليس في الكود. لجميع الأرقام ، لا بد لي من استخدام هذا:

const value_stored = value.toFixed(desired_precision);

هذا حتى لا تفقد الدقة ولكن أيضًا لا تخنق أي شيء بين الكود الخاص بي وقاعدة البيانات التي تضع افتراضات حول الحجم والحجم لا أعرفها أو أتحكم فيها. بالطبع ، هذا أقل كفاءة في عرض النطاق الترددي من استخدام النوع الصحيح ولكن في حالتي <30 بايت لكل عقوبة سجل غير مهم.

هذا حل المشكلة بالنسبة لي أيضًا.

هل هناك طريقة لتطبيق هذا على المستوى العالمي بحيث يحاول كل مرة إدخال / تحديث قيمة عشرية يستدعيها .toFixed (2)؟

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات