fix: QueryBinder.bindIdSet skips invalid id entries instead of throwing - #9535
fix: QueryBinder.bindIdSet skips invalid id entries instead of throwing#9535anmolshres98 wants to merge 8 commits into
QueryBinder.bindIdSet skips invalid id entries instead of throwing#9535Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates QueryBinder.bindIdSet in core-common to match legacy native binder behavior by tolerating nullish/invalid entries in bound id sets, preventing TypeError crashes during compression and improving migration compatibility for callers moving from ECSqlStatement to QueryBinder.
Changes:
- Filter
bindIdSetinputs to skip non-Id64String/ invalid id entries and log a warning with the skipped count. - Add unit tests to confirm invalid/nullish entries are skipped and that ids are sorted/deduplicated.
- Add a Rush change file documenting the behavioral fix.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| core/common/src/ConcurrentQuery.ts | Filters invalid id entries before compression and emits a warning when entries are skipped. |
| core/common/src/test/ConcurrentQuery.test.ts | Adds regression tests for skipping invalid entries and for sort/dedupe behavior. |
| common/changes/@itwin/core-common/issue-9530-bindidset-null_2026-07-22-19-25.json | Records the change as a non-breaking fix in core-common. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
core/common/src/ConcurrentQuery.ts:504
CompressedId64Set.sortAndCompress(ids)will allocate a second array viaArray.from(ids)even thoughidsis already a mutable array. Since this method already materializes an array, you can avoid the extra allocation by sorting in-place and callingCompressedId64Set.compressArray(ids).
Object.defineProperty(this._args, name, {
enumerable: true, value: {
type: QueryParamType.IdSet,
value: CompressedId64Set.sortAndCompress(ids),
},
There was a problem hiding this comment.
Similar to what @pmconne wrote in #9537 (comment) - I don't get how this is a problem. We're using typescript, not javascript.
Update: invalid Id64String like "" or "0" are maybe worth doing something about. But, instead of silently skipping them with a warning that nobody will see, I think we should throw.
|
I think the "we're using typescript" framing assumes the compiler is in the loop, and for id arrays flowing into // compiles clean under strict: true, no casts, no `!`, no `any` written by the caller
const reader = iModel.createQueryReader("SELECT Parent.Id AS parentId FROM bis.Element");
const parentIds: Id64String[] = [];
for await (const row of reader)
parentIds.push(row.parentId); // row.parentId is `any` (QueryRowProxy), so this type-checks
// one root element with NULL Parent.Id -> parentIds contains `undefined`
new QueryBinder().bindIdSet(1, parentIds); // TypeError before this fixI verified this against a real iModel: a NULL id column comes back from the reader as
On why this hasn't surfaced before: my best guess is it was masked. The legacy native On throw vs skip: throwing was option 1 in #9530. @khanaffan recommended skip+warn (option 2) for parity with legacy -- created w/ help from AnmolDroid 🤖 |
In my opinion this is a problem in consumer code and not in I understand that the goal of this PR is to make migration from deprecated Ultimately I think the problem is with EDIT: if we decide that |
…nmolshres98/issue-9530-bindidset-null
|
After reading @grigasp and @saskliutas points, I would say I am swayed and agree with them. It is true that I opted to skip to mirror the deprecated api. It is however, imo, also true that that behavior masks bad data by the consumer. Like @saskliutas said, migration to a different api is the perfect time to force the consumers to also clean up their calls. I have pushed updates so that now on invalid data, an |
|
This pull request is now in conflicts. Could you fix it @anmolshres98? 🙏 |
…9530-bindidset-null # Conflicts: # docs/changehistory/NextVersion.md
Closes #9530
bindIdSetused to throw an uncaughtTypeErroronundefined/null(or otherwise invalid) entries in the id array, e.g. when collecting ids from a nullable column viaECSqlReader. An earlier version of this PR matched the legacyECSqlStatement.bindIdSetbehavior by silently skipping invalid entries with a logged warning.Per discussion on this PR (see comments below), silently dropping entries hides real bugs in consumer code rather than surfacing them, so this now throws instead. The throw is a descriptive
ITwinError(scope"itwin-QueryBinder", key"invalid-arguments").Also removed a stray no-op
uniqueIteratorcall.