JamesBondski / qvdlib

A library for reading QlikView Data files
Apache License 2.0
5 stars 3 forks source link

Accessing values through reader["ColumnName"] returns values from wrong columns #8

Open Cosmatevs opened 9 months ago

Cosmatevs commented 9 months ago

Here I have a test qvd file: TestJoin.zip

When I try to access field values through reader["ColumnName"], I get values from wrong fields: image

The expected output is: 1 String1 Const 0.1 8

It looks like it's caused by ordering row arrays by BitOffset: https://github.com/JamesBondski/qvdlib/blob/8fa108d1a4737eea270b60826a028f748b3be326/Bondski.QvdLib/RowReader.cs#L47-L48 While column names are mapped without additional ordering. Additionally, ordering by BitOffset doesn't look like a good idea – many columns can share the same BitOffset. It's the case in the attached file.

waleed-alharthi commented 9 months ago

hi, do you have a workaround to use meanwhile? I've ran into the same issue with some files.

waleed-alharthi commented 9 months ago

sorting columns by BitOffset before adding them to the datatable seemed to fix the immediate problem, it may bite me back further down the line so use at your own risk.

public static DataTable QvdToDatatable(string filePath)
{
    var dataTable = new DataTable();
    var qvdReader = new QvdReader(filePath);
    var headers = qvdReader.Values.OrderBy(x => x.Key.BitOffset);
    string operatorName = GetRightmostFolder(filePath);

    // Add three additional columns
    dataTable.Columns.Add("qvd_file_time", typeof(string)).Expression = $"'{File.GetCreationTime(filePath).ToString("yyyy-MM-ddTHH:mm")}'";
    dataTable.Columns.Add("qvd_file_hash", typeof(string)).Expression = $"'{GetFileHash(filePath)}'";
    dataTable.Columns.Add("qvd_file_name", typeof(string)).Expression = $"'{filePath}'";
    dataTable.Columns.Add("qvd_operator_name", typeof(string)).Expression = $"'{operatorName}'";

    foreach (var field in headers)
    {
        dataTable.Columns.Add(field.Key.Name.ToLower());
    }

    while (qvdReader.NextRow())
    {
        var qvdRow = qvdReader.GetValues();
        var dataRow = dataTable.NewRow();

        for (int i = 0; i < qvdRow.Length; i++)
        {
            dataRow[i + 4] = qvdRow[i].ToString(); // Start from index 4 to account for the additional columns
        }

        dataTable.Rows.Add(dataRow);
    }

    return dataTable;
}
Cosmatevs commented 9 months ago

@waleed-alharthi My workaround is conceptually similar but my needs were different:

var reader = new QvdReader("TestJoin.qvd");
var columnIds = reader.Header.Fields
    .OrderBy(x => x.BitOffset)
    .Select((x, i) => new { x.Name, Index = i })
    .ToDictionary(x => x.Name, x => x.Index);

while(reader.NextRow())
{
    var intColumn = reader[columnIds["IntColumn"]].ToInt();
    […]
}

And I also think it's not a safe solution.