Skip to content

Commit 9e5a38d

Browse files
committed
feat(mssql-json): enable JSON data type - type mapping and error codes (Phase 2)
Treat SQL Server 2025+ JSON columns exactly like string columns (2026-06-09 design, issue #2768). Engine-only change validated by unit tests. - TypeHelper._sqlDbTypeToType[SqlDbType.Json] = typeof(string) (T004) - SqlTypeConstants: register json as supported literal (T004) - MsSqlDbExceptionParser BadRequestExceptionCodes += 13608-13614 so SQL JSON errors map to HTTP 400 (T006) - unit tests for json type mapping and 400 error mapping (T005/T007) The profiles DB fixture (T002) and Profile config entity (T003) are DEFERRED to Phase 3: the native json type requires SQL Server 2025+/Azure SQL and cannot be created on the LocalDB engine used by CI. They ship with the Phase 3 integration tests behind server-version gating. No changes to PostgreSQL/MySQL/DwSql/CosmosDB (FR-012).
1 parent 8028a93 commit 9e5a38d

5 files changed

Lines changed: 56 additions & 1 deletion

File tree

src/Core/Models/SqlTypeConstants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public static class SqlTypeConstants
4747
{ "time", true }, // SqlDbType.Time
4848
{ "datetime2", true }, // SqlDbType.DateTime2
4949
{ "datetimeoffset", true }, // SqlDbType.DateTimeOffset
50+
{ "json", true }, // SqlDbType.Json (SQL Server 2025+) treated as string
5051
{ "", false }, // SqlDbType.Udt and SqlDbType.Structured provided by SQL as empty strings (unsupported)
5152
{ "numeric", true} // Not present in SqlDbType, however can be returned by sql functions like LAG and should map to decimal.
5253
};

src/Core/Resolvers/MsSqlDbExceptionParser.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ public MsSqlDbExceptionParser(RuntimeConfigProvider configProvider) : base(confi
3030
"4435", "4436", "4437", "4438", "4439", "4440", "4441",
3131
"4442", "4443", "4444", "4445", "4446", "4447", "4448",
3232
"4450", "4451", "4452", "4453", "4454", "4455", "4456",
33-
"4457", "4933", "4934", "4936", "4988", "8102"
33+
"4457", "4933", "4934", "4936", "4988", "8102",
34+
// JSON data type validation errors (SQL Server 2025+). Invalid JSON
35+
// supplied for a json column is a client error, mapped to HTTP 400.
36+
"13608", "13609", "13610", "13611", "13612", "13613", "13614"
3437
});
3538

3639
TransientExceptionCodes.UnionWith(new List<string>

src/Core/Services/TypeHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public static class TypeHelper
9797
[SqlDbType.Float] = typeof(double),
9898
[SqlDbType.Image] = typeof(byte[]),
9999
[SqlDbType.Int] = typeof(int),
100+
[SqlDbType.Json] = typeof(string),
100101
[SqlDbType.Money] = typeof(decimal),
101102
[SqlDbType.NChar] = typeof(char),
102103
[SqlDbType.NText] = typeof(string),

src/Service.Tests/OpenApiDocumentor/CLRtoJsonValueTypeUnitTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,17 @@ public void ResolveUnderlyingTypeForNullableValueType(Type nullableType)
111111
Assert.AreNotEqual(notExpected: JsonDataType.Undefined, actual: TypeHelper.GetJsonDataTypeFromSystemType(nullableType));
112112
Assert.IsNotNull(TypeHelper.GetDbTypeFromSystemType(nullableType));
113113
}
114+
115+
/// <summary>
116+
/// Validates that the SQL Server 2025+ 'json' data type literal resolves to the
117+
/// CLR string type and maps to JsonDataType.String, i.e. DAB treats a JSON column
118+
/// just like a string column.
119+
/// </summary>
120+
[TestMethod]
121+
public void JsonSqlDbTypeResolvesToString()
122+
{
123+
Type resolvedType = TypeHelper.GetSystemTypeFromSqlDbType("json");
124+
Assert.AreEqual(typeof(string), resolvedType);
125+
Assert.AreEqual(JsonDataType.String, TypeHelper.GetJsonDataTypeFromSystemType(resolvedType));
126+
}
114127
}

src/Service.Tests/UnitTests/DbExceptionParserUnitTests.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,42 @@ public void TestIsTransientExceptionMethod(bool expected, int number)
9494

9595
Assert.AreEqual(expected, dbExceptionParser.IsTransientException(SqlTestHelper.CreateSqlException(number)));
9696
}
97+
98+
/// <summary>
99+
/// Validates that JSON data type validation error codes raised by SQL Server 2025+
100+
/// when invalid JSON is supplied for a json column are mapped to HTTP 400 Bad Request.
101+
/// </summary>
102+
/// <param name="number">SQL error code populated in SqlException.Number.</param>
103+
[DataTestMethod]
104+
[DataRow(13608, DisplayName = "JSON validation error code 13608 maps to 400")]
105+
[DataRow(13609, DisplayName = "JSON validation error code 13609 maps to 400")]
106+
[DataRow(13610, DisplayName = "JSON validation error code 13610 maps to 400")]
107+
[DataRow(13611, DisplayName = "JSON validation error code 13611 maps to 400")]
108+
[DataRow(13612, DisplayName = "JSON validation error code 13612 maps to 400")]
109+
[DataRow(13613, DisplayName = "JSON validation error code 13613 maps to 400")]
110+
[DataRow(13614, DisplayName = "JSON validation error code 13614 maps to 400")]
111+
public void TestJsonValidationErrorsMapToBadRequest(int number)
112+
{
113+
RuntimeConfig mockConfig = new(
114+
Schema: "",
115+
DataSource: new(DatabaseType.MSSQL, "", new()),
116+
Runtime: new(
117+
Rest: new(),
118+
GraphQL: new(),
119+
Mcp: new(),
120+
Host: new(null, null, HostMode.Development)
121+
),
122+
Entities: new(new Dictionary<string, Entity>())
123+
);
124+
MockFileSystem fileSystem = new();
125+
fileSystem.AddFile(FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME, new MockFileData(mockConfig.ToJson()));
126+
FileSystemRuntimeConfigLoader loader = new(fileSystem);
127+
RuntimeConfigProvider provider = new(loader);
128+
DbExceptionParser dbExceptionParser = new MsSqlDbExceptionParser(provider);
129+
130+
Assert.AreEqual(
131+
System.Net.HttpStatusCode.BadRequest,
132+
dbExceptionParser.GetHttpStatusCodeForException(SqlTestHelper.CreateSqlException(number)));
133+
}
97134
}
98135
}

0 commit comments

Comments
 (0)