fix: ensure correct MSSQL parameter conversion in where conditions

Fixes input parameter conversion in SelectQueryBuilder when using an MSSQL connection.

Closes #11285
This commit is contained in:
Sudhir Shrestha 2025-03-21 03:09:23 +05:45 committed by GitHub
parent 8c2b2ae240
commit ecae9f5990
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 231 additions and 0 deletions

View File

@ -45,6 +45,7 @@ import { AuroraMysqlDriver } from "../driver/aurora-mysql/AuroraMysqlDriver"
import { InstanceChecker } from "../util/InstanceChecker"
import { FindOperator } from "../find-options/FindOperator"
import { ApplyValueTransformers } from "../util/ApplyValueTransformers"
import { SqlServerDriver } from "../driver/sqlserver/SqlServerDriver"
/**
* Allows to build complex sql queries in a fashion way and execute those queries.
@ -4265,6 +4266,7 @@ export class SelectQueryBuilder<Entity extends ObjectLiteral>
if (InstanceChecker.isEqualOperator(where[key])) {
parameterValue = where[key].value
}
if (column.transformer) {
if (parameterValue instanceof FindOperator) {
parameterValue.transformValue(column.transformer)
@ -4276,6 +4278,25 @@ export class SelectQueryBuilder<Entity extends ObjectLiteral>
}
}
// MSSQL requires parameters to carry extra type information
if (this.connection.driver.options.type === "mssql") {
const driver = this.connection.driver as SqlServerDriver
if (parameterValue instanceof FindOperator) {
if (parameterValue.type !== "raw") {
parameterValue.transformValue({
to: (v) =>
driver.parametrizeValue(column, v),
from: (v) => v,
})
}
} else {
parameterValue = driver.parametrizeValue(
column,
parameterValue,
)
}
}
// if (parameterValue === null) {
// andConditions.push(`${aliasPath} IS NULL`);
//

View File

@ -0,0 +1,13 @@
import { Column, Entity, Index, PrimaryGeneratedColumn } from "../../../../src"
@Entity({
name: "user",
})
export class User {
@PrimaryGeneratedColumn()
id: number
@Index()
@Column({ type: "varchar", nullable: true })
memberId: string
}

View File

@ -0,0 +1,197 @@
import "reflect-metadata"
import { expect } from "chai"
import sinon from "sinon"
import {
createTestingConnections,
closeTestingConnections,
reloadTestingDatabases,
} from "../../utils/test-utils"
import { DataSource, MssqlParameter, Not, Raw } from "../../../src/index.js"
import { SqlServerQueryRunner } from "../../../src/driver/sqlserver/SqlServerQueryRunner"
import { User } from "./entity/user"
import { PostgresQueryRunner } from "../../../src/driver/postgres/PostgresQueryRunner"
describe("github issues > #11285 Missing MSSQL input type", () => {
describe("mssql connection", () => {
let dataSources: DataSource[]
before(
async () =>
(dataSources = await createTestingConnections({
entities: [User],
enabledDrivers: ["mssql"],
schemaCreate: true,
dropSchema: true,
})),
)
beforeEach(() => reloadTestingDatabases(dataSources))
after(() => closeTestingConnections(dataSources))
afterEach(() => sinon.restore())
it("should convert input parameter to MssqlParameter", () =>
Promise.all(
dataSources.map(async (dataSource) => {
const user = new User()
user.memberId = "test-member-id"
await dataSource.manager.save([user])
const selectSpy = sinon.spy(
SqlServerQueryRunner.prototype,
"query",
)
const users = await dataSource.getRepository(User).find({
where: {
memberId: user.memberId,
},
})
expect(users).to.have.length(1)
expect(users[0].memberId).to.be.equal(user.memberId)
expect(selectSpy.calledOnce).to.be.true
sinon.assert.calledWithMatch(
selectSpy,
sinon.match.any,
sinon.match((value) => {
return (
Array.isArray(value) &&
value.length === 1 &&
value[0] instanceof MssqlParameter &&
value[0].value === user.memberId &&
value[0].type === "varchar"
)
}),
)
}),
))
it("should convert input parameter with FindOperator to MssqlParameter", () =>
Promise.all(
dataSources.map(async (dataSource) => {
const user = new User()
user.memberId = "test-member-id"
const user2 = new User()
user2.memberId = "test-member-id-2"
await dataSource.manager.save([user, user2])
const selectSpy = sinon.spy(
SqlServerQueryRunner.prototype,
"query",
)
const users = await dataSource.getRepository(User).find({
where: {
memberId: Not(user2.memberId),
},
})
expect(users).to.have.length(1)
expect(users[0].memberId).to.be.equal(user.memberId)
expect(selectSpy.calledOnce).to.be.true
sinon.assert.calledWithMatch(
selectSpy,
sinon.match.any,
sinon.match((value) => {
return (
Array.isArray(value) &&
value.length === 1 &&
value[0] instanceof MssqlParameter &&
value[0].value === user2.memberId &&
value[0].type === "varchar"
)
}),
)
}),
))
it("should not convert input parameter with raw FindOperator", () =>
Promise.all(
dataSources.map(async (dataSource) => {
const user = new User()
user.memberId = "test-member-id"
await dataSource.manager.save([user])
const selectSpy = sinon.spy(
SqlServerQueryRunner.prototype,
"query",
)
const users = await dataSource.getRepository(User).find({
where: {
memberId: Raw(`'${user.memberId}'`),
},
})
expect(users).to.have.length(1)
expect(users[0].memberId).to.be.equal(user.memberId)
expect(selectSpy.calledOnce).to.be.true
sinon.assert.calledWithMatch(
selectSpy,
sinon.match.any,
sinon.match((value) => {
return Array.isArray(value) && value.length === 0
}),
)
}),
))
})
describe("other connections", () => {
let dataSources: DataSource[]
before(
async () =>
(dataSources = await createTestingConnections({
entities: [User],
enabledDrivers: ["postgres"],
schemaCreate: true,
dropSchema: true,
})),
)
beforeEach(() => reloadTestingDatabases(dataSources))
after(() => closeTestingConnections(dataSources))
afterEach(() => sinon.restore())
it("should used the input parameter as it is", () =>
Promise.all(
dataSources.map(async (dataSource) => {
const user = new User()
user.memberId = "test-member-id"
await dataSource.manager.save([user])
const selectSpy = sinon.spy(
PostgresQueryRunner.prototype,
"query",
)
const users = await dataSource.getRepository(User).find({
where: {
memberId: user.memberId,
},
})
expect(users).to.have.length(1)
expect(users[0].memberId).to.be.equal(user.memberId)
expect(selectSpy.calledOnce).to.be.true
sinon.assert.calledWithMatch(
selectSpy,
sinon.match.any,
sinon.match((value) => {
return value[0] === user.memberId
}),
)
}),
))
})
})