"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ReturningResultsEntityUpdator = void 0; const error_1 = require("../error"); /** * Updates entity with returning results in the entity insert and update operations. */ class ReturningResultsEntityUpdator { // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- constructor(queryRunner, expressionMap) { this.queryRunner = queryRunner; this.expressionMap = expressionMap; } // ------------------------------------------------------------------------- // Public Methods // ------------------------------------------------------------------------- /** * Updates entities with a special columns after updation query execution. */ async update(updateResult, entities) { const metadata = this.expressionMap.mainAlias.metadata; await Promise.all(entities.map(async (entity, entityIndex) => { // if database supports returning/output statement then we already should have updating values in the raw data returned by insert query if (this.queryRunner.connection.driver.isReturningSqlSupported("update")) { if (this.queryRunner.connection.driver.options.type === "oracle" && Array.isArray(updateResult.raw) && this.expressionMap.extraReturningColumns.length > 0) { updateResult.raw = updateResult.raw.reduce((newRaw, rawItem, rawItemIndex) => { newRaw[this.expressionMap.extraReturningColumns[rawItemIndex].databaseName] = rawItem[0]; return newRaw; }, {}); } const result = Array.isArray(updateResult.raw) ? updateResult.raw[entityIndex] : updateResult.raw; const returningColumns = this.queryRunner.connection.driver.createGeneratedMap(metadata, result); if (returningColumns) { this.queryRunner.manager.merge(metadata.target, entity, returningColumns); updateResult.generatedMaps.push(returningColumns); } } else { // for driver which do not support returning/output statement we need to perform separate query and load what we need const updationColumns = this.expressionMap.extraReturningColumns; if (updationColumns.length > 0) { // get entity id by which we will get needed data const entityId = this.expressionMap.mainAlias.metadata.getEntityIdMap(entity); if (!entityId) throw new error_1.TypeORMError(`Cannot update entity because entity id is not set in the entity.`); // execute query to get needed data const loadedReturningColumns = (await this.queryRunner.manager .createQueryBuilder() .select(metadata.primaryColumns.map((column) => metadata.targetName + "." + column.propertyPath)) .addSelect(updationColumns.map((column) => metadata.targetName + "." + column.propertyPath)) .from(metadata.target, metadata.targetName) .where(entityId) .withDeleted() .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 .getOne()); if (loadedReturningColumns) { this.queryRunner.manager.merge(metadata.target, entity, loadedReturningColumns); updateResult.generatedMaps.push(loadedReturningColumns); } } } })); } /** * Updates entities with a special columns after insertion query execution. */ async insert(insertResult, entities) { const metadata = this.expressionMap.mainAlias.metadata; let insertionColumns = metadata.getInsertionReturningColumns(); // to prevent extra select SQL execution for databases not supporting RETURNING // in the case if we have generated column and it's value returned by underlying driver // we remove this column from the insertionColumns list const needToCheckGenerated = this.queryRunner.connection.driver.isReturningSqlSupported("insert"); insertionColumns = insertionColumns.filter((column) => { if (!column.isGenerated) return true; return needToCheckGenerated === true; }); const generatedMaps = entities.map((entity, entityIndex) => { if (this.queryRunner.connection.driver.options.type === "oracle" && Array.isArray(insertResult.raw) && this.expressionMap.extraReturningColumns.length > 0) { insertResult.raw = insertResult.raw.reduce((newRaw, rawItem, rawItemIndex) => { newRaw[this.expressionMap.extraReturningColumns[rawItemIndex].databaseName] = rawItem[0]; return newRaw; }, {}); } // get all values generated by a database for us const result = Array.isArray(insertResult.raw) ? insertResult.raw[entityIndex] : insertResult.raw; const generatedMap = this.queryRunner.connection.driver.createGeneratedMap(metadata, result, entityIndex, entities.length) || {}; if (entityIndex in this.expressionMap.locallyGenerated) { this.queryRunner.manager.merge(metadata.target, generatedMap, this.expressionMap.locallyGenerated[entityIndex]); } this.queryRunner.manager.merge(metadata.target, entity, generatedMap); return generatedMap; }); // for postgres and mssql we use returning/output statement to get values of inserted default and generated values // for other drivers we have to re-select this data from the database if (insertionColumns.length > 0 && !this.queryRunner.connection.driver.isReturningSqlSupported("insert")) { const entityIds = entities.map((entity) => { const entityId = metadata.getEntityIdMap(entity); // We have to check for an empty `entityId` - if we don't, the query against the database // effectively drops the `where` clause entirely and the first record will be returned - // not what we want at all. if (!entityId) throw new error_1.TypeORMError(`Cannot update entity because entity id is not set in the entity.`); return entityId; }); // to select just inserted entities we need a criteria to select by. // for newly inserted entities in drivers which do not support returning statement // row identifier can only be an increment column // (since its the only thing that can be generated by those databases) // or (and) other primary key which is defined by a user and inserted value has it const returningResult = await this.queryRunner.manager .createQueryBuilder() .select(metadata.primaryColumns.map((column) => metadata.targetName + "." + column.propertyPath)) .addSelect(insertionColumns.map((column) => metadata.targetName + "." + column.propertyPath)) .from(metadata.target, metadata.targetName) .where(entityIds) .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 .getMany(); entities.forEach((entity, entityIndex) => { this.queryRunner.manager.merge(metadata.target, generatedMaps[entityIndex], returningResult[entityIndex]); this.queryRunner.manager.merge(metadata.target, entity, returningResult[entityIndex]); }); } entities.forEach((entity, entityIndex) => { const entityId = metadata.getEntityIdMap(entity); insertResult.identifiers.push(entityId); insertResult.generatedMaps.push(generatedMaps[entityIndex]); }); } /** * Columns we need to be returned from the database when we update entity. */ getUpdationReturningColumns() { return this.expressionMap.mainAlias.metadata.columns.filter((column) => { return (column.asExpression !== undefined || column.isUpdateDate || column.isVersion); }); } /** * Columns we need to be returned from the database when we soft delete and restore entity. */ getSoftDeletionReturningColumns() { return this.expressionMap.mainAlias.metadata.columns.filter((column) => { return (column.asExpression !== undefined || column.isUpdateDate || column.isVersion || column.isDeleteDate); }); } } exports.ReturningResultsEntityUpdator = ReturningResultsEntityUpdator; //# sourceMappingURL=ReturningResultsEntityUpdator.js.map