Repository navigation
fix: pass max lengths to tedious as a number - #1907
Merged
Merged
Conversation
A length of 'max' (e.g. `sql.NVarChar('max')` or a Table column with
`length: 'max'`) was handed to tedious as the string. tedious only
compares a length numerically, and since 20.3.0 it decides whether to
write the PLP header and the PLP terminator with opposite comparisons,
so a word fails both and the value is sent without its terminator. The
server rejects the request with "The incoming tabular data stream (TDS)
protocol stream is incorrect".
A TVP column without a length (e.g. `sql.NVarChar` with no length)
fails the same way, because tedious writes TVP columns with the length
they are given rather than resolving a missing one.
Map 'max' to 65535 at the tedious boundary for parameters, bulk load
columns and TVP columns, and send a missing TVP length as 65535 for
NVarChar, VarChar and VarBinary, as tedious wrote it before 20.3.0;
the SQL declarations still emit `max`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dhensby
force-pushed
the
fix/tedious-max-length
branch
from
October 4, 2026 22:39
f5329ed to
f4f94be
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The msnodesqlv8 fix still discards defined but falsy output parameter initial values.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Fixes tedious max-length serialization and msnodesqlv8 output-parameter binding.
Changes:
- Converts
'max'and missing TVP lengths to numeric max lengths for tedious. - Aligns msnodesqlv8 output markers with bound values.
- Adds integration coverage and SQL fixtures.
| File | Description |
|---|---|
lib/tedious/request.js |
Normalizes lengths passed to tedious. |
lib/msnodesqlv8/request.js |
Adds output-value assignment markers. |
test/common/tests.js |
Tests max-length parameters and TVPs. |
test/tedious/tedious.js |
Registers tedious tests. |
test/msnodesqlv8/msnodesqlv8.js |
Registers msnodesqlv8 tests. |
test/prepare.sql |
Creates the TVP test fixtures. |
test/cleanup.sql |
Removes the TVP test fixtures. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
query() and batch() on msnodesqlv8 bound an output parameter's initial value but only wrote a `set @name=?` marker for inputs, so the value was never assigned and the output started as NULL. Values are bound by position, so an output with an initial value declared before an input also shifted the input onto the output's value. An initial value of 0, '' or false was skipped altogether, by query() and by execute(), so those outputs started as NULL too. Write the marker for every value that is bound, and treat only a null initial value as no value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dhensby
force-pushed
the
fix/tedious-max-length
branch
from
October 4, 2026 22:59
efcba06 to
a05796a
Compare
|
🎉 This PR is included in version 12.7.3 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Problem
Since tedious 20.3.0, any length given as the word
'max'produces a malformed TDS stream, and the server rejects the request with:node-mssql passes the string
'max'straight through to tedious, which only compares lengths numerically. In 20.3's PLP write path, the header is chosen withlength <= maximumLengthand the terminator withlength > maximumLength. A string fails both checks, so the value is written without its terminator.Affected with tedious ≥ 20.3.0 (reproduced on 20.3.0–20.3.4; 20.2.3 and earlier are fine):
sql.NVarChar('max')andsql.VarBinary('max')input/output parametersTablecolumns withlength: 'max'(NVarChar,VarBinary)table.columns.add('c', sql.NVarChar): tedious writes TVP columns with the length it's given and doesn't resolve a missing one, soundefinedfails both comparisons the same way (NVarChar,VarBinary)Numeric
sql.MAXis unaffected.Reported upstream as tediousjs/tedious#1810 (introduced by tediousjs/tedious#1774). This PR keeps node-mssql working regardless of when that lands.
This is what is failing the tedious 20 CI jobs on #1906. That PR's brace-expansion bump is unrelated; CI installs
tedious@^20without pinning a version and now picks up 20.3.x.Fix
Convert
'max'to 65535 where node-mssql hands lengths to tedious: parameters, bulk-load columns and TVP columns. A TVPNVarChar/VarChar/VarBinarycolumn with no length is also sent as 65535, which is how tedious ≤ 20.2 treated it. The generated SQL declarations still emitmax, and size validation (assertSafeTypeSize) is unchanged.msnodesqlv8: output parameter initial values
Registering the new tests in the msnodesqlv8 suite, so both suites stay in step, exposed a separate long-standing msnodesqlv8 bug, fixed in its own commit (
fix: set output parameter initial values in msnodesqlv8 queries).query()/batch()bound an output parameter's initial value but only wrote aset @name=?marker for inputs. So:NULL(its initial value was ignored);Separately, an initial value of
0,''orfalsewas skipped altogether by bothquery()andexecute()(a truthiness check), so those outputs started asNULLtoo. tedious sends them.The fix writes a marker for every value that is bound, and treats only
null(whatoutput()stores when no value is given) as "no initial value", inquery()/batch()andexecute()alike. msnodesqlv8 can't run locally; I checked the generated SQL with a stubbed driver (markers and values now line up in every case), and Windows CI is the real check.Tests
parameters with length as maxtest covering nvarchar/varchar/varbinary inputs and an nvarchar(max) output. Only the bulk path was covered before.new Table with string columns without a lengthTVP test (adds aMSSQLTestMaxTypetable type and__testMaxTypeprocedure to the test fixtures). It goes through a stored procedure because msnodesqlv8 can't take a TVP in an ad-hoc query.output parameters with a falsy initial valuetest (0,''andfalseoutputs throughquery(), and a0output throughexecute()).maxtests (parameters with length as max,bulk insert with length as max,new Table with string columns without a length) are now registered in both the tedious and msnodesqlv8 suites. Other tests that the msnodesqlv8 suite is missing are tracked separately.maxtests fail on tedious 20.3.4. With it, the tedious suite passes on 20.3.4, 20.2.3 and the locked 19.2.2. Lint and unit tests pass.🤖 Generated with Claude Code