Background
We wanted several mappView clients to run database queries independently and see only their own results, so the natural design was one MpDatabaseQuery config with a shared result type, and a single array indexed per client slot — something like:
gDbClientData : ARRAY\[0..2] OF DbClientData_typ;
with the query’s target PV pointing at a specific slot, e.g.:
<Property ID="PV" Value="::gDbClientData[0].Rows[].number" />
That way each client’s data would live in its own array element, and per-client mappView bindings (via clientInfo.slotId) would just read/write the matching index. Simple, and it’s how you’d normally handle “N instances of the same thing.”
The problem
It doesn’t work — but it fails silently. With a literal index in the target path (gDbClientData[0].Rows[]), mapp still reports success: Error = FALSE, correct ArraySize, correct Rows.Total. But every row gets written to element [0] of the Rows array — the row index never advances — so all you see is the last row repeated, no error anywhere in the logbook or logger.
Root cause: the target PV path may contain struct members, but only one [] and no literal index. Any literal index anywhere in the path breaks the internal row-counter mapping, without any diagnostic.
Confirmed with a small test matrix:
| PV path | Result |
|---|---|
gDbTestNumber[] |
correct |
gDbClient0.RowsNumber[] (struct member, no literal index) |
correct |
gDbClient1.Rows[] (array of struct) |
correct |
gDbClientData[1].RowsNumber[] (literal index) |
wrong — all rows on [0] |
The fix
Since the slot index can’t appear in the PV path, per-client results have to be separate named variables instead of one indexed array:
gDbClient0 : DbClientData_typ;
gDbClient1 : DbClientData_typ;
gDbClient2 : DbClientData_typ;
Each has its own query entry pointing at its own variable (::gDbClient0.Rows[], etc.). mappView’s per-client binding still works the same way via clientInfo.slotId — only the bound refIds change from gDbClientData[N]… to gDbClientN…. In ST, since these are separate variables rather than an array, we rebind a reference per iteration (CASE i OF 0: pRef ACCESS ADR(gDbClient0); ...).