Skip to content

v1.8.37 regression (#136): the ORDER BY memory cursor types every numeric as int32 and caps the record at 64 KB #146

Description

@russimicro

Reported by RusSoft Ltda. Found while adopting v1.8.37 in a Harbour/FiveWin ERP.

Symptom

Error ADSCDX/1012
Called from: ADSEXECUTESQLDIRECT(0)

on this query, which worked before v1.8.37:

Select {static} * From [articulo.dat] as a
 WHERE 1 = 1
   AND UPPER(a.creffacart) <> 'N' AND UPPER(a.crefhabart) <> 'N'
   AND UPPER(a.cnombreart) LIKE 'A%'
 order by a.cnombreart

ARTICULO is an ADT table with 187 fields, ~34,600 rows, and many
N(n,d) columns (prices, quantities, costs).

Cause

#136 made a single-table SELECT ... ORDER BY / DISTINCT / LIMIT
materialise through build_memory_result() (src/abi/ace_exports.cpp).
That helper was written for system.* catalogs and sp_* result sets,
where every column is small and integral. It has two properties that do
not hold for a user table:

if (col.type == 'N') {
    f.raw_type = 11;
    f.type = ...::Integer;
    f.length = 4;            // int32 — the caller's length/decimals are ignored
}
...
rlen += f.length;
if (rlen > 0xFFFFu)
    return Error{AE_INTERNAL_ERROR, 0, "result table record too large", tag};
  1. Every numeric column is flattened to a 4-byte integer. The ORDER BY
    block does pass sc.decimals = fd.decimals and sc.length = 20, but
    build_memory_result discards both. A price of 12345.67 comes back as
    12345, and anything past 2^31 wraps. This is silent data loss — worse
    than the error.
  2. The record is capped at 64 KB. A wide table can exceed it outright.

Suggested fix

Give the materialised cursor the source field descriptors instead of
re-deriving them: copy raw_type, length and decimals from
tbl->field_descriptor(fi) for each projected column. Either teach
build_memory_result to honour a full DbfField (leaving the SpCol
overload for the catalog callers), or materialise into a temp table created
from the source structure.

We carry the second shape in our tree (a temp DBF built with
AdsCreateTable from the source structure, closed and deleted with the
cursor) and it has run this ERP's ~60 SQL call sites for weeks. Happy to
send it as a PR if that direction is welcome — say the word and we will
open one against main.

Repro

Any table with an N(12,2) column and more than a handful of fields:

SELECT * FROM [t] ORDER BY somecol

then read the numeric column back — decimals are gone. Widen the table
past 64 KB per record and the statement fails instead.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions