Skip to content

Combined: v4.0.21 — Ternary parenthesization, EmulatorFlaky removal, string literal aliases, FilterPredicate, insertion order, decimal scale + cross-platform CI#69

Draft
McNultyyy wants to merge 20 commits into
mainfrom
mcnultyyy/merge-prs-65-66-68
Draft

Combined: v4.0.21 — Ternary parenthesization, EmulatorFlaky removal, string literal aliases, FilterPredicate, insertion order, decimal scale + cross-platform CI#69
McNultyyy wants to merge 20 commits into
mainfrom
mcnultyyy/merge-prs-65-66-68

Conversation

@McNultyyy
Copy link
Copy Markdown
Collaborator

@McNultyyy McNultyyy commented May 19, 2026

Summary

This PR combines six bug-fix PRs into a single release (v4.0.21):

PR #65 — Fix COUNT(expr) with nested ternary expressions (#64)

  • ExprToString now parenthesises ternary/coalesce sub-expressions when inside higher-precedence operators
  • Fixes miscounting documents in COUNT() when nested ternary expressions are involved
  • 10 integration tests added

PR #66 — Remove EmulatorFlaky trait, use adaptive concurrency

  • Replaces blanket EmulatorFlaky exclusions with adaptive concurrency (50 parallel for in-memory, 10 for emulator)
  • All tests now run against all targets with no exclusions beyond InMemoryOnly

PR #68 — Fix string literal aliases on Linux + cross-platform CI (#67)

  • ProjectAggregateFields now evaluates literal/expression fields instead of treating them as property paths
  • DefaultQueryPlanStrategy bypasses SDK aggregate pipeline when literals appear alongside aggregates
  • Integration tests now run on both Linux and Windows in CI
  • 5 integration tests added

PR #71 — Fix FilterPredicate missing properties (#70)

  • Treats missing properties as null in FilterPredicate instead of throwing
  • Fixes patch operations with filter predicates referencing non-existent properties
  • Integration tests added

PR #73 — Return documents in insertion order for queries without ORDER BY (#72)

  • Added insertion-order tracking to InMemoryContainer that maintains document position across create, replace, upsert, and delete operations
  • Queries without ORDER BY now return documents in insertion order, matching real Cosmos DB behavior
  • Integration tests added

PR #78 — Fix decimal scale preservation and SUM aggregate cross-platform (#75)

  • Custom CosmosJsonWriter normalizes numeric representations during ParseJson to match real Cosmos DB (whole numbers as integers, fractional values with minimal representation)
  • Aggregate assignment points normalize whole-number doubles to longs before serialization, fixing SUM inconsistency across platforms
  • Query plan strategy now suppresses all aggregate info on non-Windows platforms, preventing SDK pipeline conflicts
  • Fixed double-to-long overflow at long.MaxValue boundary
  • 23 integration tests added

Version

Bumped to 4.0.21

Supersedes

Closes #64, Closes #67, Closes #70, Closes #72, Closes #75

Once merged, PRs #65, #66, #68, #71, #73, and #78 should be closed without merging.

McNultyyy and others added 9 commits May 18, 2026 23:03
… round-trip (#64)

ExprToString did not wrap TernaryExpression or CoalesceExpression in
parentheses when they appeared as operands of higher-precedence binary,
unary, BETWEEN, IN, or LIKE operators. When the SDK's transformed query
was round-tripped through SimplifySdkQuery → re-parse, the missing
parentheses produced a different AST — causing COUNT(expr) to evaluate
the wrong condition and miscount documents.

Added WrapIfLowPrecedence helper that wraps ternary/coalesce nodes in
parens when they appear in operator positions that would otherwise
re-parse with different associativity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The EmulatorFlaky trait was blanket-excluding 12 tests from all emulator
runs, creating a regression gap. The root cause was a single test
(ConcurrentReadsOfNonExistent) overwhelming the emulator with 50 parallel
requests.

Instead of excluding the entire class, make the concurrency adaptive:
- 50 concurrent reads for in-memory (fast, no resource constraints)
- 10 concurrent reads for emulator targets (same assertion, lower volume)

Changes:
- Remove [Trait(TestTraits.Target, TestTraits.EmulatorFlaky)] from
  Issue18EdgeCaseIntegrationTests
- Remove EmulatorFlaky constant from TestTraits.cs
- Remove Target!=EmulatorFlaky filter from scripts/run-tests.ps1

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Windows emulator can return 404 with substatus 0 during an
intermediate startup state (between 403/1008 and fully ready). This was
causing the EmulatorWarmup tool and EmulatorRetry to treat it as fatal,
failing the parity workflow before tests even ran.

Add 404/0 to the transient error set in both:
- tests/EmulatorWarmup/Program.cs (warmup tool)
- tests/.../EmulatorSession.cs (shared retry helper)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Documents how to publish beta/prerelease and stable packages via the
existing release.yml workflow (tag push convention) for AI/LLM agent
discoverability.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
String literal aliases (e.g. SELECT 'Settlement' AS Label, COUNT(1) ...)
returned null on Linux because ProjectAggregateFields treated them as
property paths via SelectToken. On Windows the bug was masked by
ServiceInterop's native aggregate pipeline.

The fix checks for non-identifier SqlExpr nodes in the else branch and
evaluates them via EvaluateSqlExpression, matching the existing pattern
used in the GROUP BY aggregate path.

Covers string, numeric, boolean, and null literal aliases with
integration tests.

Bumps version to 4.0.19.

Closes #67

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…gences

ServiceInterop.dll is only available on Windows, causing fundamentally
different code paths to execute. Adding a windows-latest integration
test job ensures both paths are tested on every PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ases-linux' into mcnultyyy/merge-prs-65-66-68
@McNultyyy McNultyyy requested a review from lemonlion as a code owner May 19, 2026 22:20
@McNultyyy McNultyyy marked this pull request as draft May 19, 2026 22:20
)

In real Cosmos DB, FilterPredicate on patch operations treats missing
properties as null, so 'FROM c WHERE c.prop = null' matches documents
where the property is absent (e.g. due to NullValueHandling.Ignore).

Add treatUndefinedAsNull parameter to the WHERE evaluation chain,
passed as true only from the two FilterPredicate call sites. This
preserves existing query semantics (undefined != null) while fixing
the FilterPredicate behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@McNultyyy McNultyyy changed the title Combined: Fix ternary parenthesization, remove EmulatorFlaky trait, fix string literal aliases + cross-platform CI Combined: Fix ternary parenthesization, remove EmulatorFlaky trait, fix string literal aliases + cross-platform CI, fix FilterPredicate missing properties May 20, 2026
McNultyyy and others added 3 commits May 20, 2026 14:47
Queries without ORDER BY now return documents in insertion order,
matching real Cosmos DB behavior. Previously documents were returned
in hash-map order due to ConcurrentDictionary enumeration.

Added insertion-order tracking list to InMemoryContainer that maintains
document position across create, replace, upsert, and delete operations.
GetAllItemsForPartition() now iterates this ordered list instead of the
dictionary directly.

Fixes #72

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comprehensive edge case tests covering:
- Large batch (55 docs) insertion order
- Multi-partition interleaved insertion order
- Repeated replaces preserving position
- Mixed operations sequence (create/delete/replace/upsert)
- SELECT projections maintaining order
- WHERE filter preserving relative order
- TOP and OFFSET/LIMIT pagination order
- Empty container and single document edge cases
- Delete all + recreate ordering
- Upsert-only creates ordering
- DISTINCT VALUE preserving first occurrence order
- COUNT and SUM aggregates not crashing
- Cross-partition vs single-partition order
- Rapid sequential creates order
- ORDER BY overriding insertion order
- Range filter preserving relative order
- Multiple non-consecutive deletes preserving remaining order

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@McNultyyy McNultyyy changed the title Combined: Fix ternary parenthesization, remove EmulatorFlaky trait, fix string literal aliases + cross-platform CI, fix FilterPredicate missing properties Combined: Fix ternary parenthesization, remove EmulatorFlaky, fix string literal aliases, fix FilterPredicate, fix query insertion order + cross-platform CI May 20, 2026
McNultyyy and others added 6 commits May 20, 2026 15:38
Bug 1 (cross-platform): Round-trip scale modification - whole numbers gain
trailing .0 and trailing zeros are stripped from fractional values.

Bug 2 (Linux-specific): SUM aggregate returns different decimal scales on
Windows vs Linux - whole number sums gain trailing .0 on Linux only.

Test results:
- Windows: 13 fail (Bug 1 round-trip), 10 pass (SUM + significant digits)
- Linux: 19 fail (Bug 1 + Bug 2), 4 pass (significant digits only)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Update trailing-zero tests to expect stripping (real Cosmos behavior):
  100.50 -> 100.5, 25.00 -> 25, 0.10 -> 0.1, etc.
- Update multi-property test to expect 100.5 not 100.50
- Tighten SUM fractional assertion to expect 50.5 only
- Fix run-tests.ps1: pass -p:NuGetAudit=false to warmup dotnet run
  so it works in offline Docker containers (linux+emulator-linux)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…#75)

- Add CosmosJsonWriter that normalizes numbers during JSON serialization:
  whole-number doubles/decimals written as integers (matching real Cosmos DB)
- Apply NormalizeNumericResult to SUM/AVG aggregate assignment points
- Fix double-to-long overflow: use strict < comparison at long.MaxValue boundary
- Bypass SDK AggregateQueryPipelineStage for all aggregate queries in query plan
  (fixes Linux where ServiceInterop is unavailable and SDK expects payload envelope)
- Remove unnecessary async from test methods (fixes CS1998 on .NET 8 SDK)
- Bump version to 4.0.18

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…servation

Fix decimal scale preservation and SUM aggregate cross-platform (#75)
@McNultyyy McNultyyy changed the title Combined: Fix ternary parenthesization, remove EmulatorFlaky, fix string literal aliases, fix FilterPredicate, fix query insertion order + cross-platform CI Combined: v4.0.21 — Ternary parenthesization, EmulatorFlaky removal, string literal aliases, FilterPredicate, insertion order, decimal scale + cross-platform CI May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment