node-odbc icon indicating copy to clipboard operation
node-odbc copied to clipboard

[BUG] Losing decimal places

Open Relbot opened this issue 1 year ago • 12 comments

Describe your system Running on Fedora Linux 38 (Workstation Edition) Kernal Version: Linux 6.2.9-300.fc38.x86_64

  • odbc Package Version: ^2.4.9
  • ODBC Driver: Zen-Client-linux-15.21-005.000.x86_64
  • Database Name: Pervasive SQL (Actian Zen)
  • Database Version: 15.21-005.000
  • Node.js Version: 18.18.2
  • Node.js OS:

Describe the bug When I query any database table where the rows contain decimal numbers I lose the decimal places Example: column X contains 3.5, when I query this column the result is only 3.

column result definition is

{
   name: "vk_1",
   dataType: 2,
   dataTypeName: 'SQL_NUMERIC',
   columnSize: 9,
   decimalDigits: 2,
   nullable: false
}

Expected behavior Result should be 3.5 instead of 3

If I do the same query with isql I get the result 3.5

Best Regards Tell

Relbot avatar Oct 26 '24 15:10 Relbot

@markdirish do you have any solution for this? Help appreciated

Relbot avatar Oct 31 '24 14:10 Relbot

I’d be willing to pay if someone can help me solve this problem.

Relbot avatar Nov 12 '24 12:11 Relbot

@kadler can you help?

Relbot avatar Nov 14 '24 18:11 Relbot

Can you please provide a trace of the problem?

kadler avatar Nov 14 '24 21:11 kadler

Hi @kadler, Thx for your fast answer. Here the trace odbcTrace.log

The query starts at row 138.

Relbot avatar Nov 14 '24 23:11 Relbot

Ok. From the trace, I see that for vk_1, this is described by the driver as a NUMERIC(9, 2). We bind this as SQL_C_CHAR with a buffer of length 11 (9 total digits + decimal separator + null terminator). The relevant trace info is:

[ODBC][897611][1731625276.699660][SQLDescribeCol.c][504]
		Exit:[SQL_SUCCESS]                
			Column Name = [vk_1]                
			Data Type = 0x7f617c005fd4 -> 2                
			Column Size = 0x7f617c005fd8 -> 9 (64 bits)                
			Decimal Digits = 0x7f617c005fe0 -> 2                
			Nullable = 0x7f617c005ff0 -> 0
[ODBC][897611][1731625276.699664][SQLBindCol.c][245]
		Entry:
			Statement = 0x7f617c000d50
			Column Number = 3
			Target Type = 1 SQL_CHAR
			Target Value = 0x7f617c006010
			Buffer Length = 11
			StrLen Or Ind = 0x7f617c006e10

This should be enough room to successfully retrieve the converted data. Unfortunately, ODBC trace does not show the data that was retrieved. I would say that perhaps this is a driver issue, but as isql shows the correct data and it also uses SQL_C_CHAR (albeit with SQLGetData, not SQLBindCol, but I don't think that would matter) it's probably not that.

Looks like our SQL_NUMERIC handling converts to a number via atof, which I know is notorious for causing problems with locale-sensitive parsing. Do you happen to run in a locale which uses a different decimal separator, eg. 3,5?

kadler avatar Nov 15 '24 00:11 kadler

One potential fix would be to comment out these lines and rebuild: https://github.com/IBM/node-odbc/blob/3694c4d3d749d8c642c6994ac85eddfcf6de8630/src/odbc_connection.cpp#L3382-L3389

This would cause the data to be bound as a double, obviating the need to call atof to convert it to one later. TBH I'm not sure why we bother binding as char if we have to convert to double anyway. Looks like it's always been binding to SQL_C_CHAR since SQLBindCol support was introduced, which was based on idb-connector code. I suspect just poor code or having to deal with the limited way that SQLCLI supports conversions.

kadler avatar Nov 15 '24 00:11 kadler

Thank you for your help. Regarding your question,

Do you happen to run in a locale which uses a different decimal separator, e.g., 3,5?

no, the number is stored with a “.” (dot) in the database.

Regarding your second response, I will give it a try and get back to you.

Relbot avatar Nov 15 '24 07:11 Relbot

@kadler

Over the past few days, I tested everything thoroughly. Your solution, with commenting out the conversation as you suggested, works perfectly—100%!

Thank you so much for your help; it’s been incredibly useful. Is there any other way I can thank you besides expressing my gratitude here?

Best Regards Tell aka. Relbot

Relbot avatar Nov 19 '24 10:11 Relbot

Thanks, glad it worked! I'm gonna re-open to track making the change here.

kadler avatar Nov 19 '24 15:11 kadler

Here's a workaround that worked for us: multiply the column by 1.0.

Example: instead of

SELECT timestamp, temperature FROM weather_data;

do

SELECT timestamp, (temperature * 1.0) as temperature FROM weather_data;

0xfacade avatar Jan 23 '25 15:01 0xfacade

Perhaps this is related: ColumnInfo.decimalDigits is reported as 0, when it is actually 1. This might also be an issue in our ODBC driver, though (no idea what is responsible for what).

0xfacade avatar Jan 23 '25 15:01 0xfacade