• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

typeorm / typeorm / 15089093306

17 May 2025 09:03PM UTC coverage: 50.109% (-26.2%) from 76.346%
15089093306

Pull #11437

github

naorpeled
add comment about vector <#>
Pull Request #11437: feat(postgres): support vector data type

5836 of 12767 branches covered (45.71%)

Branch coverage included in aggregate %.

16 of 17 new or added lines in 4 files covered. (94.12%)

6283 existing lines in 64 files now uncovered.

12600 of 24025 relevant lines covered (52.45%)

28708.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.33
/src/query-builder/ReturningResultsEntityUpdator.ts
1
import { ObjectLiteral } from "../common/ObjectLiteral"
2
import { QueryRunner } from "../query-runner/QueryRunner"
3
import { QueryExpressionMap } from "./QueryExpressionMap"
4
import { ColumnMetadata } from "../metadata/ColumnMetadata"
5
import { UpdateResult } from "./result/UpdateResult"
6
import { InsertResult } from "./result/InsertResult"
7
import { TypeORMError } from "../error"
6✔
8

9
/**
10
 * Updates entity with returning results in the entity insert and update operations.
11
 */
12
export class ReturningResultsEntityUpdator {
6✔
13
    // -------------------------------------------------------------------------
14
    // Constructor
15
    // -------------------------------------------------------------------------
16

17
    constructor(
18
        protected queryRunner: QueryRunner,
55,500✔
19
        protected expressionMap: QueryExpressionMap,
55,500✔
20
    ) {}
21

22
    // -------------------------------------------------------------------------
23
    // Public Methods
24
    // -------------------------------------------------------------------------
25

26
    /**
27
     * Updates entities with a special columns after updation query execution.
28
     */
29
    async update(
30
        updateResult: UpdateResult,
31
        entities: ObjectLiteral[],
32
    ): Promise<void> {
33
        const metadata = this.expressionMap.mainAlias!.metadata
709✔
34

35
        await Promise.all(
709✔
36
            entities.map(async (entity, entityIndex) => {
37
                // if database supports returning/output statement then we already should have updating values in the raw data returned by insert query
38
                if (
709✔
39
                    this.queryRunner.connection.driver.isReturningSqlSupported(
40
                        "update",
41
                    )
42
                ) {
43
                    if (
151!
44
                        this.queryRunner.connection.driver.options.type ===
151!
45
                            "oracle" &&
46
                        Array.isArray(updateResult.raw) &&
47
                        this.expressionMap.extraReturningColumns.length > 0
48
                    ) {
UNCOV
49
                        updateResult.raw = updateResult.raw.reduce(
×
50
                            (newRaw, rawItem, rawItemIndex) => {
UNCOV
51
                                newRaw[
×
52
                                    this.expressionMap.extraReturningColumns[
53
                                        rawItemIndex
54
                                    ].databaseName
55
                                ] = rawItem[0]
UNCOV
56
                                return newRaw
×
57
                            },
58
                            {} as ObjectLiteral,
59
                        )
60
                    }
61
                    const result = Array.isArray(updateResult.raw)
151!
62
                        ? updateResult.raw[entityIndex]
63
                        : updateResult.raw
64
                    const returningColumns =
65
                        this.queryRunner.connection.driver.createGeneratedMap(
151✔
66
                            metadata,
67
                            result,
68
                        )
69
                    if (returningColumns) {
151✔
70
                        this.queryRunner.manager.merge(
30✔
71
                            metadata.target as any,
72
                            entity,
73
                            returningColumns,
74
                        )
75
                        updateResult.generatedMaps.push(returningColumns)
30✔
76
                    }
77
                } else {
78
                    // for driver which do not support returning/output statement we need to perform separate query and load what we need
79
                    const updationColumns =
80
                        this.expressionMap.extraReturningColumns
558✔
81
                    if (updationColumns.length > 0) {
558✔
82
                        // get entity id by which we will get needed data
83
                        const entityId =
84
                            this.expressionMap.mainAlias!.metadata.getEntityIdMap(
148✔
85
                                entity,
86
                            )
87
                        if (!entityId)
148!
88
                            throw new TypeORMError(
×
89
                                `Cannot update entity because entity id is not set in the entity.`,
90
                            )
91

92
                        // execute query to get needed data
93
                        const loadedReturningColumns =
94
                            (await this.queryRunner.manager
148✔
95
                                .createQueryBuilder()
96
                                .select(
97
                                    metadata.primaryColumns.map(
98
                                        (column) =>
99
                                            metadata.targetName +
148✔
100
                                            "." +
101
                                            column.propertyPath,
102
                                    ),
103
                                )
104
                                .addSelect(
105
                                    updationColumns.map(
106
                                        (column) =>
107
                                            metadata.targetName +
173✔
108
                                            "." +
109
                                            column.propertyPath,
110
                                    ),
111
                                )
112
                                .from(metadata.target, metadata.targetName)
113
                                .where(entityId)
114
                                .withDeleted()
115
                                .setOption("create-pojo") // use POJO because created object can contain default values, e.g. property = null and those properties might be overridden by merge process
116
                                .getOne()) as any
117

118
                        if (loadedReturningColumns) {
148✔
119
                            this.queryRunner.manager.merge(
148✔
120
                                metadata.target as any,
121
                                entity,
122
                                loadedReturningColumns,
123
                            )
124
                            updateResult.generatedMaps.push(
148✔
125
                                loadedReturningColumns,
126
                            )
127
                        }
128
                    }
129
                }
130
            }),
131
        )
132
    }
133

134
    /**
135
     * Updates entities with a special columns after insertion query execution.
136
     */
137
    async insert(
138
        insertResult: InsertResult,
139
        entities: ObjectLiteral[],
140
    ): Promise<void> {
141
        const metadata = this.expressionMap.mainAlias!.metadata
50,550✔
142
        let insertionColumns = metadata.getInsertionReturningColumns()
50,550✔
143

144
        // to prevent extra select SQL execution for databases not supporting RETURNING
145
        // in the case if we have generated column and it's value returned by underlying driver
146
        // we remove this column from the insertionColumns list
147
        const needToCheckGenerated =
148
            this.queryRunner.connection.driver.isReturningSqlSupported("insert")
50,550✔
149
        insertionColumns = insertionColumns.filter((column) => {
50,550✔
150
            if (!column.isGenerated) return true
27,053✔
151
            return needToCheckGenerated === true
22,777✔
152
        })
153

154
        const generatedMaps = entities.map((entity, entityIndex) => {
50,550✔
155
            if (
114,587✔
156
                Array.isArray(insertResult.raw) &&
188,562✔
157
                this.expressionMap.extraReturningColumns.length > 0
158
            ) {
159
                if (
46,573!
160
                    this.queryRunner.connection.driver.options.type === "oracle"
161
                ) {
UNCOV
162
                    insertResult.raw = insertResult.raw.reduce(
×
163
                        (newRaw, rawItem, rawItemIndex) => {
UNCOV
164
                            newRaw[
×
165
                                this.expressionMap.extraReturningColumns[
166
                                    rawItemIndex
167
                                ].databaseName
168
                            ] = rawItem[0]
UNCOV
169
                            return newRaw
×
170
                        },
171
                        {} as ObjectLiteral,
172
                    )
173
                } else if (
46,573!
174
                    this.queryRunner.connection.driver.options.type ===
175
                    "spanner"
176
                ) {
177
                    insertResult.raw = insertResult.raw[0]
×
178
                }
179
            }
180

181
            // get all values generated by a database for us
182
            const result = Array.isArray(insertResult.raw)
114,587✔
183
                ? insertResult.raw[entityIndex]
184
                : insertResult.raw
185

186
            const generatedMap =
187
                this.queryRunner.connection.driver.createGeneratedMap(
114,587✔
188
                    metadata,
189
                    result,
190
                    entityIndex,
191
                    entities.length,
192
                ) || {}
193

194
            if (entityIndex in this.expressionMap.locallyGenerated) {
114,587✔
195
                this.queryRunner.manager.merge(
339✔
196
                    metadata.target as any,
197
                    generatedMap,
198
                    this.expressionMap.locallyGenerated[entityIndex],
199
                )
200
            }
201

202
            this.queryRunner.manager.merge(
114,587✔
203
                metadata.target as any,
204
                entity,
205
                generatedMap,
206
            )
207

208
            return generatedMap
114,587✔
209
        })
210

211
        // for postgres and mssql we use returning/output statement to get values of inserted default and generated values
212
        // for other drivers we have to re-select this data from the database
213
        if (
50,550✔
214
            insertionColumns.length > 0 &&
56,068✔
215
            !this.queryRunner.connection.driver.isReturningSqlSupported(
216
                "insert",
217
            )
218
        ) {
219
            const entityIds = entities.map((entity) => {
2,432✔
220
                const entityId = metadata.getEntityIdMap(entity)!
2,432✔
221

222
                // We have to check for an empty `entityId` - if we don't, the query against the database
223
                // effectively drops the `where` clause entirely and the first record will be returned -
224
                // not what we want at all.
225
                if (!entityId)
2,432!
226
                    throw new TypeORMError(
×
227
                        `Cannot update entity because entity id is not set in the entity.`,
228
                    )
229

230
                return entityId
2,432✔
231
            })
232

233
            // to select just inserted entities we need a criteria to select by.
234
            // for newly inserted entities in drivers which do not support returning statement
235
            // row identifier can only be an increment column
236
            // (since its the only thing that can be generated by those databases)
237
            // or (and) other primary key which is defined by a user and inserted value has it
238

239
            const returningResult: any = await this.queryRunner.manager
2,432✔
240
                .createQueryBuilder()
241
                .select(
242
                    metadata.primaryColumns.map(
243
                        (column) =>
244
                            metadata.targetName + "." + column.propertyPath,
2,457✔
245
                    ),
246
                )
247
                .addSelect(
248
                    insertionColumns.map(
249
                        (column) =>
250
                            metadata.targetName + "." + column.propertyPath,
3,264✔
251
                    ),
252
                )
253
                .from(metadata.target, metadata.targetName)
254
                .where(entityIds)
255
                .setOption("create-pojo") // use POJO because created object can contain default values, e.g. property = null and those properties might be overridden by merge process
256
                .getMany()
257

258
            entities.forEach((entity, entityIndex) => {
2,432✔
259
                this.queryRunner.manager.merge(
2,432✔
260
                    metadata.target as any,
261
                    generatedMaps[entityIndex],
262
                    returningResult[entityIndex],
263
                )
264

265
                this.queryRunner.manager.merge(
2,432✔
266
                    metadata.target as any,
267
                    entity,
268
                    returningResult[entityIndex],
269
                )
270
            })
271
        }
272

273
        entities.forEach((entity, entityIndex) => {
50,550✔
274
            const entityId = metadata.getEntityIdMap(entity)!
114,587✔
275
            insertResult.identifiers.push(entityId)
114,587✔
276
            insertResult.generatedMaps.push(generatedMaps[entityIndex])
114,587✔
277
        })
278
    }
279

280
    /**
281
     * Columns we need to be returned from the database when we update entity.
282
     */
283
    getUpdationReturningColumns(): ColumnMetadata[] {
284
        return this.expressionMap.mainAlias!.metadata.columns.filter(
601✔
285
            (column) => {
286
                return (
2,567✔
287
                    column.asExpression !== undefined ||
7,643✔
288
                    column.isUpdateDate ||
289
                    column.isVersion
290
                )
291
            },
292
        )
293
    }
294

295
    /**
296
     * Columns we need to be returned from the database when we soft delete and restore entity.
297
     */
298
    getSoftDeletionReturningColumns(): ColumnMetadata[] {
299
        return this.expressionMap.mainAlias!.metadata.columns.filter(
132✔
300
            (column) => {
301
                return (
552✔
302
                    column.asExpression !== undefined ||
2,184✔
303
                    column.isUpdateDate ||
304
                    column.isVersion ||
305
                    column.isDeleteDate
306
                )
307
            },
308
        )
309
    }
310
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc