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};
- 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.
- 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.
Reported by RusSoft Ltda. Found while adopting
v1.8.37in a Harbour/FiveWin ERP.Symptom
on this query, which worked before v1.8.37:
ARTICULOis an ADT table with 187 fields, ~34,600 rows, and manyN(n,d)columns (prices, quantities, costs).Cause
#136made a single-tableSELECT ... ORDER BY / DISTINCT / LIMITmaterialise through
build_memory_result()(src/abi/ace_exports.cpp).That helper was written for
system.*catalogs andsp_*result sets,where every column is small and integral. It has two properties that do
not hold for a user table:
block does pass
sc.decimals = fd.decimalsandsc.length = 20, butbuild_memory_resultdiscards both. A price of12345.67comes back as12345, and anything past 2^31 wraps. This is silent data loss — worsethan the error.
Suggested fix
Give the materialised cursor the source field descriptors instead of
re-deriving them: copy
raw_type,lengthanddecimalsfromtbl->field_descriptor(fi)for each projected column. Either teachbuild_memory_resultto honour a fullDbfField(leaving theSpColoverload 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
AdsCreateTablefrom the source structure, closed and deleted with thecursor) 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:then read the numeric column back — decimals are gone. Widen the table
past 64 KB per record and the statement fails instead.