Skip to content

driver-sql (mssql): substring operators on a uniqueidentifier column compare SQL Server's upper-case text form while reads answer lower-case, so $contains / $startsWith with a lower-case GUID fragment misses #31395

Closed

Description

@objectstack-fleet

epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Filing gate: ③ out-of-scope finding of the #31327 card thread, escalated by the CONTRACT_REVIEW_TIER record on PR #31381: "noted, not filed" is not a carrier.
Ruling-ref: none
Dedupe (open AND closed): "uniqueidentifier contains", "GUID substring case" → none.
Blocked-by: #31327
Reader: the epic PM dispatches it after #31327 lands.
type: Bug

维护者速读

Task

  • For a column whose declared or introspected type is uniqueidentifier, the substring arms compare LOWER(CONVERT(nvarchar(36), col)), or refuse substring operators on it with a typed refusal. Pick one and state it in the docs.
  • Live pins in the mssql cell, covering both cases of the fragment.

Clause-②: no expected · Metadata-protocol: none.

Activity

  1. added
    bugSomething isn't working
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Nov 8, 2026
  2. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p3 · domain:server · blocked · 2026-11-08T06:53Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi

    Unblocked · re-verified · 2026-11-08T16:04Z (idle chore ①, execution-duties.md)

  4. added
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    pm:queueReady for the PM dispatch loop
    on Nov 8, 2026
  5. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi

    Claim: PM loop round 1 · #31395 · 2026-11-08T16:10Z · fix (p3 bug; unblocked by the seat 6723757275)
    Session: session_01ADHkmPF1skfu2kx89BEXZi
    Account: os-steve
    Branch: claude/project-thread-2ilf2k (the card thread's harness-pinned branch; platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-31395
    Domain: domain:server
    Seat: domain:server#1
    File surface: packages/drivers/driver-sql/src/ (the substring-operator emitters for the mssql dialect: sql-driver-filter-helpers.ts and/or sql-driver-predicate-emitters.ts, sql-driver-mssql-*), their tests, the SQL Server dialect docs page, a changeset (stop on breach; explain in the report)
    Container & model: S/M, mode:thread, model: default tier, effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: driver-sql (objectstack) compiles the substring arms against SQL Server's upper-case uniqueidentifier text while #31327 made reads answer lower-case · no platform path covers it today · reached by any filter with a lower-case GUID fragment on a federated SQL Server object; federation-only dialect landed 2026-11-08 (#31381), no measured user yet
    Thread-read: 6723757275
    Premise: holds (read at 645ed3d1: the [#31327] lower-case read comment is in sql-driver-class-head.ts; no LOWER(CONVERT(nvarchar(36), …)) arm and no typed refusal for substring operators on uniqueidentifier)
    Serial constraints cleared: no open PR touches packages/drivers/driver-sql/src/ (open PR list read at claim time); sibling #31390 (oracle HEXTORAW, same package, different dialect file) is unclaimed; #31394 (service-analytics) is outside this surface.
    Order: standard order + server-seat1/31395-delta.md (project files). If the dev picks the refusal route instead, Clause-② becomes yes (narrowing) and the dev writes it as measured.

  6. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmSddVo92aurMz8Z5Vf
    os-dev-report

    {
      "issue": 31395,
      "status": "done",
      "branch": "claude/project-thread-2ilf2k",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/31623",
      "session": "session_01ADHkmSddVo92aurMz8Z5Vf",
      "premise_still_valid": true,
      "summary": "Route: compare LOWER(CONVERT(nvarchar(36), col)), no refusal. Which columns are uniqueidentifier is read from the remote catalog (introspectColumns / INFORMATION_SCHEMA.COLUMNS) once per object by the new SqlDriver#primeMssqlGuidColumns (FieldMappingFacet), only when the where holds a text operator, for the object and every composed ReadableIds leaf; every read door primes behind isMssql (findRows, count, aggregate, distinct, updateMany, deleteMany, analyzeQuery, findWithWindowFunctions). The compiler passes a per-field guidColumn flag; applyLike / applyLikePattern route a GUID column on mssql to two new sibling emitters (mssqlGuidTextMatch, mssqlGuidLikePattern in sql-driver-predicate-emitters.ts); textMatchPredicate and likePatternPredicate are byte-identical to main per the seat's serial note. Docs (external-datasources.mdx section 8, GUIDs bullet) state the choice; changeset @objectstack/driver-sql patch.",
      "tests": "Head 2de7d355. Compiled: src/sql-driver-mssql-dialect.test.ts 34/34 (+19 new pins: every text operator on a GUID column in both fragment cases, key column, nvarchar control, equality control, five-dialect no-flag control, catalog gating, ReadableIds leaf, failed catalog read). Live SQL Server 2022 cell stood up in the container (docker mcr.microsoft.com/mssql/server:2022-latest): src/sql-driver-mssql-federation-conformance.test.ts 229/229 with OS_EXPECT_MSSQL_FEDERATION=1 (+17 cases, find AND count). Base vs head measured live (table in PR body): lower-case fragment 0 -> 1 row on $contains/$startsWith/$endsWith/$like; upper-case 1 -> 0; $icontains/$ilike 1 -> 1; nvarchar column identical. driver-sql full with the live cell: 246 files passed, 12 skipped; 4361 tests passed. service-datasource 74/74 (1291), objectql 570/570 (11571). Ablations via ablation-replace.mjs, each restored blob == HEAD and git diff HEAD empty: A1 applyLike routing off -> 13 red; A2 catalog gate off -> 1 red; A3 flag on every column -> nvarchar control red; A4 likePattern routing on every dialect -> five-dialect control red; A5 catalog type filter off -> nvarchar control red (first attempt refused by the tool as a no-op, redone); L1 live flag never true -> 12 live red. No dist leg: the suites import driver-sql src.",
      "gates": "Head 2de7d355. dispatch-gates --commands --repo objectstack-ai/objectstack derived 99; all run; --ran reconciliation: 99 derived, 99 run, 0 NOT-MEASURED, 0 UNRUN (exit 0). First pass exit 3 (prerequisite) on check-plugin-teardown-shape --self-test (fixture commit fetched), check:dual-build-cjs-loads and spec check:skill-examples (closure build) -> re-run exit 0. eslint --no-inline-config packages/drivers/driver-sql/src exit 0. driver-sql typecheck exit 0. check:driver-conformance before 66 covered / 6 DEBT / 0 exempt, after identical. PR check-runs: in_progress at report time (not awaited).",
      "dogfood_battery": "2de7d355 · os-verify-lock VERDICT command-exit 0 (1675s) · Test Files 440 passed, 3 skipped (443) · Tests 3633 passed, 41 skipped (3674) · no failures. Run because the delta lists it; the title has no ! and the claim no (narrowing) arm.",
      "mcp_calls": "4 — reads only: issue_read get, issue_read get_comments x2, pull_request_read get. No MCP write.",
      "api_writes": "2 — relay pr_create (PR #31623, read-back 11178 bytes sent == stored), this os-dev-report comment via post-stamped.mjs. git push x2 to the branch (not REST).",
      "open_questions": [
        {
          "question": "Answer change for an UPPER-case GUID fragment: the case-exact operators now follow the lower-case read form, so `$startsWith: '3F2504E0'` (and `$contains` / `$endsWith` / `$like`) which matched SQL Server's upper-case rendering on main now matches nothing, and `$notContains` flips with it. No accepted request is refused, so Clause-② stays `no` as claimed, but the answer set moves. Keep?",
          "options": [
            "A keep: case-exact against the read form, the same answer Postgres / SQLite / memory give for a GUID stored as lower-case text; $icontains / $ilike match either case",
            "B fold both sides for a GUID column only: any case matches, but $contains on a GUID then folds while the #6518 contract says it is case-exact"
          ],
          "recommendation": "A, because it is what a read shows the caller and what every other dialect answers; B makes the case-exact operators fold on one column type of one dialect"
        },
        {
          "question": "A failed catalog read is silent (records nothing, asks again next read), as primeMssqlPagingKey is. Warn once instead?",
          "options": [
            "A keep silent: the data read on the same connection fails loudly anyway, and the precedent is the paging key",
            "B warn once per object naming that GUID columns are matched as SQL Server's upper-case text until the catalog answers"
          ],
          "recommendation": "A, matching the #31327 precedent; B is a small follow-up if the seat wants it"
        },
        {
          "question": "Scope widening inside the same defect class: $like / $ilike (pattern operators, not in the card's substring list) also read the GUID column as text and are fixed the same way, through one flag. Accept in this PR?",
          "options": [
            "A accept: same column-reading defect, same emitter, same gates; leaving it would keep $like and $contains disagreeing on one column",
            "B split $like / $ilike to their own card"
          ],
          "recommendation": "A"
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": [],
      "files_changed": [
        "packages/drivers/driver-sql/src/sql-driver-aggregation.ts",
        "packages/drivers/driver-sql/src/sql-driver-bulk-operations.ts",
        "packages/drivers/driver-sql/src/sql-driver-crud.ts",
        "packages/drivers/driver-sql/src/sql-driver-distinct.ts",
        "packages/drivers/driver-sql/src/sql-driver-field-mapping.ts",
        "packages/drivers/driver-sql/src/sql-driver-filter-helpers.ts",
        "packages/drivers/driver-sql/src/sql-driver-mssql-dialect.test.ts",
        "packages/drivers/driver-sql/src/sql-driver-mssql-federation-conformance.test.ts",
        "packages/drivers/driver-sql/src/sql-driver-predicate-emitters.ts",
        "packages/drivers/driver-sql/src/sql-driver-query-plan.ts",
        "packages/drivers/driver-sql/src/sql-driver-window-functions.ts",
        "packages/drivers/driver-sql/src/sql-driver.ts",
        "content/docs/data-modeling/external-datasources.mdx",
        ".changeset/31395-mssql-guid-text-match.md"
      ],
      "line_budget": "479 changed lines (+447 / -32) across 14 files; source 210-ish lines incl. docblocks, tests the rest; under the 3000 human-merge threshold",
      "deviations": [
        "PR opened with no assignee: pr_create read-back lists none; the standard order forbids MCP writes, so the harness's MCP assignee/reviewer step was not taken",
        "The live SQL Server cell was stood up locally with docker; CI provisions none, so CI runs the new live cases as the file's named skip",
        "Ablation A5 first attempt was refused by ablation-replace (replacement text already present, nothing ran); redone with a distinct replacement",
        "Three derived gates answered exit 3 on the first pass (prerequisites) and were re-run green before reconciliation",
        "test:repo (vitest repo project) not run locally; CI's Test Core runs it",
        "Per the seat's serial note the GUID construct was moved out of textMatchPredicate / likePatternPredicate into sibling emitters before the PR (commit 2de7d355)"
      ]
    }

    Stamped 2026-11-08T17:21Z.

  7. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi

    ACCEPT → PR #31623 comment 6724635636 · head 2de7d355 · 2026-11-08T17:24Z · open questions 1–3 ruled A there · landing waits for every check green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:databugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions