Skip to content

feat: Json data type#1683

Open
matthewerwin wants to merge 9 commits into
tediousjs:masterfrom
matthewerwin:feat-json-support
Open

feat: Json data type#1683
matthewerwin wants to merge 9 commits into
tediousjs:masterfrom
matthewerwin:feat-json-support

Conversation

@matthewerwin

@matthewerwin matthewerwin commented Mar 21, 2025

Copy link
Copy Markdown

Tested that this is working with mssql against Azure Sql Server.

Notes:

  • validates JSON if parameter value is a string
  • encodes as JSON if parameter value is an object
  • passes 'null' to the server if parameter value is null
  • The inclusion of this new data type does not create any breaking changes to the codebase

I used the following logic to deduce the implementation:

  1. Referenced 0xF4 and PLP based on SqlClient
  2. That no parameter collation bytes are sent because Microsoft is very clear that JSON collation is fixed to Latin1_General_100_BIN2_UTF8 to match JSON Spec
  3. That the protocol only needed 0xf4 id prefix and no length bytes since its a newer data type which doesn't have a legacy <= 8000 implementation like varchar did (and the 'json' type has no length setting in SQL)
  4. That due to the fixed collation that UTF-8 encoding in validate() is sufficient

I did not use the iconv-lite library b/c it seemed like unnecessary overhead vs the direct Buffer.from( ) support in Node.

This resolves this Feature Request I created earlier before deciding to address with a PR after a little digging.

@matthewerwin matthewerwin changed the title feature: Json data type feat: Json data type Mar 21, 2025
@matthewerwin

Copy link
Copy Markdown
Author

@arthurschreiber @dhensby -- is there a plan to roll any updates out to Tedious in the near future? Noticed there has been no movement on the repo in 6+ months.

I had a couple in-flight items including this PR and would like to do what I can to help if needed. Another one was

tediousjs/node-mssql#1727

and it seems a deprecation on Microsoft's side related to that as well:
#1684

Generally it seems some maintenance things are accumulating without any regular interval releases from the Tedious team. Happy to help organize and move some things through for a release including reviewing other PR's if it's a matter of limited time to organize/test/review.

@arthurschreiber

Copy link
Copy Markdown
Collaborator

👋🏻 Heya! You're right, I didn't have a lot of time to spend on tedious recently.

If you don't mind, can you mention me on whatever issues are the most urgent in your eyes, and I'll promise to carve out some time to get them reviewed and merged. 🙇

@matthewerwin

Copy link
Copy Markdown
Author

@arthurschreiber you bet -- I'll make a pass through the open PR's & tickets over the next week and see what stands out.

@arthurschreiber
arthurschreiber requested a review from Copilot May 20, 2025 10:40

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds support for a JSON data type by implementing its PLP encoding and registering it in the type registry.

  • Introduce src/data-types/json.ts to handle JSON validation, length resolution, and PLP data streaming.
  • Register the new Json type in src/data-type.ts, update the type lookup tables, and augment the documentation.

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/data-types/json.ts New module defining the JSON type’s declaration, validation, and PLP encoding.
src/data-type.ts Imports and registers Json in TYPE/TYPES and updates the docs table.

Comment thread src/data-types/json.ts Outdated
Comment thread src/data-types/json.ts Outdated
Comment on lines +16 to +17
return parameter.value.length;
else return JSON.stringify(parameter.value).length;

Copilot AI May 20, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using .length on a string gives UTF-16 code-unit count, not the byte length for UTF-8. Switch to Buffer.byteLength(value, 'utf8') to get the correct byte length.

Suggested change
return parameter.value.length;
else return JSON.stringify(parameter.value).length;
return Buffer.byteLength(parameter.value, 'utf8');
else return Buffer.byteLength(JSON.stringify(parameter.value), 'utf8');

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also isn't accurate. To my knowledge this is sent by Tedious as nvarchar at serialization time -- the correct length is as indicated. @arthurschreiber thoughts?

Comment thread src/data-types/json.ts Outdated
@peterbud

peterbud commented Mar 8, 2026

Copy link
Copy Markdown

Is there a plan to merge this PR? It would be highly appreciated my many users I believe

@arthurschreiber

Copy link
Copy Markdown
Collaborator

Thanks for the work on this @matthewerwin, and apologies again for the long silence — I've gone through this in depth now. The core wire encoding for sending a json parameter looks right and matches what SqlClient does (single-byte 0xF4 TYPE_INFO, no collation, PLP-encoded UTF-8, 0xFF…FF for PLP NULL). A few things need to happen before this can ship, and since I have push access to your branch I'm happy to push the fixes myself rather than round-trip through review comments — just say if you'd rather take any of them yourself.

Issues found, roughly by severity:

  1. No JSONSUPPORT feature-extension negotiation. Per MS-TDS, JSON support is enabled via a LOGIN7 FeatureExt handshake (feature id 0x0D) that the server acknowledges in FEATUREEXTACK — that's how SqlClient and mssql-jdbc gate the type. As written we send 0xF4 unconditionally: against SQL Server ≤ 2022 that surfaces as a cryptic msg 8016 (Data type 0xF4 is unknown) protocol error, and against SQL 2025/Azure it works only because the server currently tolerates unnegotiated writes. I'd like to add the FeatureExt (the structure from fix: rework FeatureExt generation #1718 makes this straightforward) and fail client-side with a clear error when the server doesn't ack it.

  2. No read path. metadata-parser.ts / value-parser.ts don't handle 0xF4, so registering Json in the TYPE map only changes the error message when a server sends a json column (Unrecognised type Json instead of Unrecognised data type 0xF4). Today that's masked because an un-negotiating client gets json columns downcast to varchar — but the moment we negotiate JSONSUPPORT (point 1), the server will send 0xF4 in result metadata and every SELECT of a json column would kill the token stream. So 1 and 2 have to land together. The values are plain PLP UTF-8, so the read side is close to the existing max-varchar/Xml path.

  3. Malformed PLP stream for empty values. generateParameterData returns without yielding anything for a zero-length value, but generateParameterLength has already emitted the unknown-length PLP header, which obligates the 00 00 00 00 terminator. It's currently unreachable (empty string fails JSON.parse in validate), but it's a latent protocol bug — the terminator should always be yielded, matching the nvarchar implementation.

  4. validate() edge cases. A Buffer value silently takes the JSON.stringify branch and inserts {"type":"Buffer","data":[…]}; a function/symbol makes JSON.stringify return undefined and produces a confusing Buffer.from(undefined) TypeError. These should be explicit TypeErrors like our other types throw. Related: resolveLength runs after validate() has replaced the value with a Buffer, so it returns JSON.stringify(buffer).length — nothing consumes it for this type, so I'd just remove it rather than keep a wrong implementation.

  5. Tests. We'll need unit coverage in test/unit/data-type.ts (buffer-level assertions for generateTypeInfo / generateParameterLength / generateParameterData, null and multi-byte UTF-8 cases) plus integration coverage once the negotiation and read path exist.

Plus some minor style alignment (braced conditionals, ===, dropping the boxed-String acceptance — no other type accepts new String(…)).

I'll start pushing to this branch shortly. Thanks again for doing the protocol digging — the SqlClient/MS-TDS references in the description made verifying this much easier.

arthurschreiber and others added 6 commits July 17, 2026 16:23
The unknown-length PLP header emitted by generateParameterLength
obligates a terminator, but zero-length values returned without
yielding one, producing a malformed TDS stream.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Buffers previously took the JSON.stringify branch and were silently
mangled into their internal representation, and values that stringify
to undefined (functions, symbols) produced a confusing error from
Buffer.from. Both now throw an explicit TypeError.

Also drop resolveLength: it ran after validate() had already replaced
the value with a Buffer, so it returned the length of the Buffer's
JSON.stringify representation. Nothing consumes the length for this
type.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per MS-TDS, the json data type (0xF4) is enabled through the
JSONSUPPORT feature extension (0x0D). Request version 1 in the LOGIN7
feature extension block, parse the server's acknowledgement, and track
it as Connection#serverSupportsJson. Sending a TYPES.Json parameter to
a server that did not acknowledge the feature now fails client-side
with a descriptive RequestError instead of a cryptic protocol error
from the server.

Also add the read path: json TYPE_INFO carries no additional metadata
(no length or collation bytes), and values arrive as PLP-encoded UTF-8
strings in ROW, NBCROW and RETURNVALUE tokens.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unit coverage for the Json type's wire encoding (type info, PLP
parameter length and data, validation), the JSON_SUPPORT feature
extension acknowledgement, and parsing of json COLMETADATA and
PLP-encoded json values in ROW and NBCROW tokens.

Integration tests exercise both server capabilities: round-trips of
string, object and null parameters plus json result columns on servers
that acknowledge JSON_SUPPORT, and the client-side EJSONNOTSUPPORTED
error on servers that do not.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@arthurschreiber

arthurschreiber commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

All of the above is now resolved on this branch:

  • 5793724 — merged latest master (clean, no conflicts; picks up the Node ≥ 22 requirement, the TypeScript test conversion, and the FeatureExt rework from fix: rework FeatureExt generation #1718).
  • 0630b26 — issues 1 + 2: JSONSUPPORT is requested at login, the FEATUREEXTACK is tracked on the connection, and a TYPES.JSON parameter sent to a server that didn't ack now fails client-side with a descriptive RequestError (EJSONNOTSUPPORTED). The read path handles json COLMETADATA (no length/collation bytes, verified against SqlClient's implementation) and PLP UTF-8 values in ROW, NBCROW and RETURNVALUE tokens, returned as strings.
  • 127dd31 — issue 3: the PLP terminator is now always emitted for non-null values.
  • d53f039 — issue 4: explicit TypeErrors for Buffers and non-serializable values; resolveLength removed. The style nits are folded into these commits as well.
  • 527cea2 — issue 5: unit coverage for the wire encoding, validation, ack parsing and token parsing (incl. multi-byte UTF-8), plus a new test/integration/json-test.ts that exercises both server capabilities — round-trips on servers that ack JSONSUPPORT, and the EJSONNOTSUPPORTED error on servers that don't.
  • 091ed6f — one thing not in the list above: the type is exposed as TYPES.JSON rather than TYPES.Json, matching how JavaScript itself names it (the global JSON object). Since the type has never been released, this isn't a breaking change.

CI status: lint, CodeQL and the unit suite pass. The claude-review check fails for infrastructure reasons only (fork PRs get no OIDC token / API key), and the Azure SQL jobs skip on fork PRs for the same secrets reason.

One remaining caveat: the integration tests lint and typecheck, but I haven't been able to run them against a real SQL Server 2025 / Azure SQL instance. @matthewerwin since you have an Azure SQL setup that supports json, it would be a great help if you could run test/integration/json-test.ts against it (or re-test your original mssql scenario on this branch) to confirm the negotiation works end-to-end.

Matches how JavaScript itself names it (the global `JSON` object),
which reads more natively than the PascalCase `Json`. The type has
never been released, so this is not a breaking change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.08%. Comparing base (650843a) to head (091ed6f).

Files with missing lines Patch % Lines
src/token/returnvalue-token-parser.ts 0.00% 2 Missing ⚠️
src/token/handler.ts 66.66% 0 Missing and 1 partial ⚠️
src/token/nbcrow-token-parser.ts 50.00% 0 Missing and 1 partial ⚠️
src/token/row-token-parser.ts 50.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1683      +/-   ##
==========================================
- Coverage   80.84%   80.08%   -0.77%     
==========================================
  Files          90       91       +1     
  Lines        4887     4945      +58     
  Branches      924      935      +11     
==========================================
+ Hits         3951     3960       +9     
- Misses        640      696      +56     
+ Partials      296      289       -7     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@matthewerwin

matthewerwin commented Jul 17, 2026

Copy link
Copy Markdown
Author

@arthurschreiber thanks for the teamwork effort on this! I’ll have a closer look (and run all tests) when I return from Miami (Tues 7/21) but looks solid based on your write up.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants