mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
Fix/11466 mssql find operator (#11468)
* chore: enable driver `mssql` for test of issue #3113 * chore: add test case for #11298 to issue #11285 to prevent functionality of #11285 * fix: unhandled find operator with array value for mssql (#11466) * fixup! fix: unhandled find operator with array value for mssql (#11466) * Revert "chore: enable driver `mssql` for test of issue #3113" This reverts commit a302d63eeac5892e920d97705b4230414ef81e6d. * fixup! chore: add test case for #11298 to issue #11285 to prevent functionality of #11285 --------- Co-authored-by: Christian Forgács <christian@wunderbit.de>
This commit is contained in:
parent
23bb1ee271
commit
e9eaf79604
@ -27,6 +27,7 @@ import { TableForeignKey } from "../../schema-builder/table/TableForeignKey"
|
||||
import { TypeORMError } from "../../error"
|
||||
import { InstanceChecker } from "../../util/InstanceChecker"
|
||||
import { UpsertType } from "../types/UpsertType"
|
||||
import { FindOperator } from "../../find-options/FindOperator"
|
||||
|
||||
/**
|
||||
* Organizes communication with SQL Server DBMS.
|
||||
@ -973,6 +974,39 @@ export class SqlServerDriver implements Driver {
|
||||
return new MssqlParameter(value, normalizedType as any)
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively wraps values (including those inside FindOperators) into MssqlParameter instances,
|
||||
* ensuring correct type metadata is passed to the SQL Server driver.
|
||||
*
|
||||
* - If the value is a FindOperator containing an array, all elements are individually parametrized.
|
||||
* - If the value is a non-raw FindOperator, a transformation is applied to its internal value.
|
||||
* - Otherwise, the value is passed directly to parametrizeValue for wrapping.
|
||||
*
|
||||
* This ensures SQL Server receives properly typed parameters for queries involving operators like
|
||||
* In, MoreThan, Between, etc.
|
||||
*/
|
||||
parametrizeValues(column: ColumnMetadata, value: any) {
|
||||
if (value instanceof FindOperator) {
|
||||
if (Array.isArray(value.value)) {
|
||||
for (let i = 0; i < value.value.length; i++) {
|
||||
value.value[i] = this.parametrizeValues(
|
||||
column,
|
||||
value.value[i],
|
||||
)
|
||||
}
|
||||
} else if (value.type !== "raw") {
|
||||
value.transformValue({
|
||||
to: (v) => this.parametrizeValue(column, v),
|
||||
from: (v) => v,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
value = this.parametrizeValue(column, value)
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Sql server's parameters needs to be wrapped into special object with type information about this value.
|
||||
* This method wraps all values of the given object into MssqlParameter based on their column definitions in the given table.
|
||||
|
||||
@ -4280,21 +4280,9 @@ 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,
|
||||
)
|
||||
}
|
||||
parameterValue = (
|
||||
this.connection.driver as SqlServerDriver
|
||||
).parametrizeValues(column, parameterValue)
|
||||
}
|
||||
|
||||
// if (parameterValue === null) {
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
import "reflect-metadata"
|
||||
import "../../../utils/test-setup"
|
||||
import { DataSource, LessThan, MoreThan } from "../../../../src"
|
||||
import {
|
||||
And,
|
||||
DataSource,
|
||||
In,
|
||||
IsNull,
|
||||
LessThan,
|
||||
MoreThan,
|
||||
Not,
|
||||
Or,
|
||||
} from "../../../../src"
|
||||
import {
|
||||
closeTestingConnections,
|
||||
createTestingConnections,
|
||||
@ -468,6 +477,58 @@ describe("find options > where", () => {
|
||||
}),
|
||||
))
|
||||
|
||||
it("where with or + and find operator", () =>
|
||||
Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
await prepareData(connection.manager)
|
||||
|
||||
const posts = await connection
|
||||
.createQueryBuilder(Post, "post")
|
||||
.setFindOptions({
|
||||
where: {
|
||||
counters: {
|
||||
likedUsers: {
|
||||
firstName: And(
|
||||
In(["Gyro", "Timber"]),
|
||||
Not(Or(IsNull(), In(["Foo", "Bar"]))),
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
order: {
|
||||
id: "asc",
|
||||
},
|
||||
})
|
||||
.getMany()
|
||||
posts.should.be.eql([
|
||||
{
|
||||
id: 1,
|
||||
title: "Post #1",
|
||||
text: "About post #1",
|
||||
counters: { likes: 1 },
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Post #2",
|
||||
text: "About post #2",
|
||||
counters: { likes: 2 },
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Post #3",
|
||||
text: "About post #3",
|
||||
counters: { likes: 1 },
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Post #4",
|
||||
text: "About post #4",
|
||||
counters: { likes: 1 },
|
||||
},
|
||||
])
|
||||
}),
|
||||
))
|
||||
|
||||
it("where relations with operators", () =>
|
||||
Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
|
||||
@ -7,7 +7,14 @@ import {
|
||||
closeTestingConnections,
|
||||
reloadTestingDatabases,
|
||||
} from "../../utils/test-utils"
|
||||
import { DataSource, MssqlParameter, Not, Raw } from "../../../src/index.js"
|
||||
import {
|
||||
And,
|
||||
DataSource,
|
||||
In,
|
||||
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"
|
||||
@ -143,6 +150,49 @@ describe("github issues > #11285 Missing MSSQL input type", () => {
|
||||
)
|
||||
}),
|
||||
))
|
||||
|
||||
it("should convert input parameter with FindOperator with array 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: And(Not(In([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"
|
||||
)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
))
|
||||
})
|
||||
|
||||
describe("other connections", () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user