typeorm
Version:
Data-Mapper ORM for TypeScript, ES7, ES6, ES5. Supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, MongoDB databases.
272 lines (270 loc) • 17.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClosureSubjectExecutor = void 0;
var tslib_1 = require("tslib");
var CannotAttachTreeChildrenEntityError_1 = require("../../error/CannotAttachTreeChildrenEntityError");
var OrmUtils_1 = require("../../util/OrmUtils");
var SqlServerDriver_1 = require("../../driver/sqlserver/SqlServerDriver");
/**
* Executes subject operations for closure entities.
*/
var ClosureSubjectExecutor = /** @class */ (function () {
// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------
function ClosureSubjectExecutor(queryRunner) {
this.queryRunner = queryRunner;
}
// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------
/**
* Executes operations when subject is being inserted.
*/
ClosureSubjectExecutor.prototype.insert = function (subject) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var closureJunctionInsertMap, parent, escape_1, tableName, queryParams_1, ancestorColumnNames, descendantColumnNames, childEntityIds1, whereCondition;
var _this = this;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
closureJunctionInsertMap = {};
subject.metadata.closureJunctionTable.ancestorColumns.forEach(function (column) {
closureJunctionInsertMap[column.databaseName] = subject.identifier;
});
subject.metadata.closureJunctionTable.descendantColumns.forEach(function (column) {
closureJunctionInsertMap[column.databaseName] = subject.identifier;
});
// insert values into the closure junction table
return [4 /*yield*/, this.queryRunner
.manager
.createQueryBuilder()
.insert()
.into(subject.metadata.closureJunctionTable.tablePath)
.values(closureJunctionInsertMap)
.updateEntity(false)
.callListeners(false)
.execute()];
case 1:
// insert values into the closure junction table
_a.sent();
parent = subject.metadata.treeParentRelation.getEntityValue(subject.entity);
if (!parent && subject.parentSubject && subject.parentSubject.entity) // if entity was attached via children
parent = subject.parentSubject.insertedValueSet ? subject.parentSubject.insertedValueSet : subject.parentSubject.entity;
if (!parent) return [3 /*break*/, 3];
escape_1 = function (alias) { return _this.queryRunner.connection.driver.escape(alias); };
tableName = this.getTableName(subject.metadata.closureJunctionTable.tablePath);
queryParams_1 = [];
ancestorColumnNames = subject.metadata.closureJunctionTable.ancestorColumns.map(function (column) {
return escape_1(column.databaseName);
});
descendantColumnNames = subject.metadata.closureJunctionTable.descendantColumns.map(function (column) {
return escape_1(column.databaseName);
});
childEntityIds1 = subject.metadata.primaryColumns.map(function (column) {
queryParams_1.push(column.getEntityValue(subject.insertedValueSet ? subject.insertedValueSet : subject.entity));
return _this.queryRunner.connection.driver.createParameter("child_entity_" + column.databaseName, queryParams_1.length - 1);
});
whereCondition = subject.metadata.closureJunctionTable.descendantColumns.map(function (column) {
var columnName = escape_1(column.databaseName);
var parentId = column.referencedColumn.getEntityValue(parent);
if (!parentId)
throw new CannotAttachTreeChildrenEntityError_1.CannotAttachTreeChildrenEntityError(subject.metadata.name);
queryParams_1.push(parentId);
var parameterName = _this.queryRunner.connection.driver.createParameter("parent_entity_" + column.referencedColumn.databaseName, queryParams_1.length - 1);
return columnName + " = " + parameterName;
});
return [4 /*yield*/, this.queryRunner.query("INSERT INTO " + tableName + " (" + tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(ancestorColumnNames)), tslib_1.__read(descendantColumnNames)).join(", ") + ") " +
("SELECT " + ancestorColumnNames.join(", ") + ", " + childEntityIds1.join(", ") + " FROM " + tableName + " WHERE " + whereCondition.join(" AND ")), queryParams_1)];
case 2:
_a.sent();
_a.label = 3;
case 3: return [2 /*return*/];
}
});
});
};
/**
* Executes operations when subject is being updated.
*/
ClosureSubjectExecutor.prototype.update = function (subject) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var parent, entity, oldParent, oldParentId, parentId, escape, closureTable, ancestorColumnNames, descendantColumnNames, createSubQuery, parameters, _a, _b, column, queryParams_2, tableName, superAlias_1, subAlias_1, select, entityWhereCondition, parentWhereCondition;
var e_1, _c;
var _this = this;
return tslib_1.__generator(this, function (_d) {
switch (_d.label) {
case 0:
parent = subject.metadata.treeParentRelation.getEntityValue(subject.entity);
if (!parent && subject.parentSubject && subject.parentSubject.entity) // if entity was attached via children
parent = subject.parentSubject.entity;
entity = subject.databaseEntity;
if (!entity && parent) // if entity was attached via children
entity = subject.metadata.treeChildrenRelation.getEntityValue(parent).find(function (child) {
return Object.entries(subject.identifier).every(function (_a) {
var _b = tslib_1.__read(_a, 2), key = _b[0], value = _b[1];
return child[key] === value;
});
});
// Exit if the parent or the entity where never set
if (entity === undefined || parent === undefined) {
return [2 /*return*/];
}
oldParent = subject.metadata.treeParentRelation.getEntityValue(entity);
oldParentId = subject.metadata.getEntityIdMap(oldParent);
parentId = subject.metadata.getEntityIdMap(parent);
// Exit if the new and old parents are the same
if (OrmUtils_1.OrmUtils.compareIds(oldParentId, parentId)) {
return [2 /*return*/];
}
escape = function (alias) { return _this.queryRunner.connection.driver.escape(alias); };
closureTable = subject.metadata.closureJunctionTable;
ancestorColumnNames = closureTable.ancestorColumns.map(function (column) {
return escape(column.databaseName);
});
descendantColumnNames = closureTable.descendantColumns.map(function (column) {
return escape(column.databaseName);
});
createSubQuery = function (qb, alias) {
var e_2, _a;
var subAlias = "sub" + alias;
var subSelect = qb.createQueryBuilder()
.select(descendantColumnNames.join(", "))
.from(closureTable.tablePath, subAlias);
try {
// Create where conditions e.g. (WHERE "subdescendant"."id_ancestor" = :value_id)
for (var _b = tslib_1.__values(closureTable.ancestorColumns), _c = _b.next(); !_c.done; _c = _b.next()) {
var column = _c.value;
subSelect.andWhere(escape(subAlias) + "." + escape(column.databaseName) + " = :value_" + column.referencedColumn.databaseName);
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_2) throw e_2.error; }
}
return qb.createQueryBuilder()
.select(descendantColumnNames.join(", "))
.from("(" + subSelect.getQuery() + ")", alias)
.setParameters(subSelect.getParameters())
.getQuery();
};
parameters = {};
try {
for (_a = tslib_1.__values(subject.metadata.primaryColumns), _b = _a.next(); !_b.done; _b = _a.next()) {
column = _b.value;
parameters["value_" + column.databaseName] = entity[column.databaseName];
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
}
finally { if (e_1) throw e_1.error; }
}
return [4 /*yield*/, this.queryRunner
.manager
.createQueryBuilder()
.delete()
.from(closureTable.tablePath)
.where(function (qb) { return "(" + descendantColumnNames.join(", ") + ") IN (" + createSubQuery(qb, "descendant") + ")"; })
.andWhere(function (qb) { return "(" + ancestorColumnNames.join(", ") + ") NOT IN (" + createSubQuery(qb, "ancestor") + ")"; })
.setParameters(parameters)
.execute()];
case 1:
_d.sent();
if (!parent) return [3 /*break*/, 3];
queryParams_2 = [];
tableName = this.getTableName(closureTable.tablePath);
superAlias_1 = escape("supertree");
subAlias_1 = escape("subtree");
select = tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(ancestorColumnNames.map(function (columnName) { return superAlias_1 + "." + columnName; }))), tslib_1.__read(descendantColumnNames.map(function (columnName) { return subAlias_1 + "." + columnName; })));
entityWhereCondition = subject.metadata.closureJunctionTable.ancestorColumns.map(function (column) {
var columnName = escape(column.databaseName);
var entityId = column.referencedColumn.getEntityValue(entity);
queryParams_2.push(entityId);
var parameterName = _this.queryRunner.connection.driver.createParameter("entity_" + column.referencedColumn.databaseName, queryParams_2.length - 1);
return subAlias_1 + "." + columnName + " = " + parameterName;
});
parentWhereCondition = subject.metadata.closureJunctionTable.descendantColumns.map(function (column) {
var columnName = escape(column.databaseName);
var parentId = column.referencedColumn.getEntityValue(parent);
if (!parentId)
throw new CannotAttachTreeChildrenEntityError_1.CannotAttachTreeChildrenEntityError(subject.metadata.name);
queryParams_2.push(parentId);
var parameterName = _this.queryRunner.connection.driver.createParameter("parent_entity_" + column.referencedColumn.databaseName, queryParams_2.length - 1);
return superAlias_1 + "." + columnName + " = " + parameterName;
});
return [4 /*yield*/, this.queryRunner.query("INSERT INTO " + tableName + " (" + tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(ancestorColumnNames)), tslib_1.__read(descendantColumnNames)).join(", ") + ") " +
("SELECT " + select.join(", ") + " ") +
("FROM " + tableName + " AS " + superAlias_1 + ", " + tableName + " AS " + subAlias_1 + " ") +
("WHERE " + tslib_1.__spreadArray(tslib_1.__spreadArray([], tslib_1.__read(entityWhereCondition)), tslib_1.__read(parentWhereCondition)).join(" AND ")), queryParams_2)];
case 2:
_d.sent();
_d.label = 3;
case 3: return [2 /*return*/];
}
});
});
};
/**
* Executes operations when subject is being removed.
*/
ClosureSubjectExecutor.prototype.remove = function (subjects) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var escape, identifiers, closureTable, generateWheres, ancestorWhere, descendantWhere;
var _this = this;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
// Only mssql need to execute deletes for the juntion table as it doesn't support multi cascade paths.
if (!(this.queryRunner.connection.driver instanceof SqlServerDriver_1.SqlServerDriver)) {
return [2 /*return*/];
}
if (!Array.isArray(subjects))
subjects = [subjects];
escape = function (alias) { return _this.queryRunner.connection.driver.escape(alias); };
identifiers = subjects.map(function (subject) { return subject.identifier; });
closureTable = subjects[0].metadata.closureJunctionTable;
generateWheres = function (columns) {
return columns.map(function (column) {
var data = identifiers.map(function (identifier) { return identifier[column.referencedColumn.databaseName]; });
return escape(column.databaseName) + " IN (" + data.join(", ") + ")";
}).join(" AND ");
};
ancestorWhere = generateWheres(closureTable.ancestorColumns);
descendantWhere = generateWheres(closureTable.descendantColumns);
return [4 /*yield*/, this.queryRunner
.manager
.createQueryBuilder()
.delete()
.from(closureTable.tablePath)
.where(ancestorWhere)
.orWhere(descendantWhere)
.execute()];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
/**
* Gets escaped table name with schema name if SqlServer or Postgres driver used with custom
* schema name, otherwise returns escaped table name.
*/
ClosureSubjectExecutor.prototype.getTableName = function (tablePath) {
var _this = this;
return tablePath.split(".")
.map(function (i) {
// this condition need because in SQL Server driver when custom database name was specified and schema name was not, we got `dbName..tableName` string, and doesn't need to escape middle empty string
return i === "" ? i : _this.queryRunner.connection.driver.escape(i);
}).join(".");
};
return ClosureSubjectExecutor;
}());
exports.ClosureSubjectExecutor = ClosureSubjectExecutor;
//# sourceMappingURL=ClosureSubjectExecutor.js.map