@sap/xsodata
Version:
Expose data from a HANA database as OData V2 service with help of .xsodata files.
275 lines (209 loc) • 10.2 kB
JavaScript
'use strict';
//Include
const sql = require('./sqlStatement');
const sqlTools = require('./sqlTools');
const utils = require('../utils/utils');
const EntityType = require('../model/entityType.js');
//Code
exports.createPutStatementsCreateTmpTables = function (context, asyncDone) {
context.logger.silly('createPutStatements', 'createPutStatementsCreateTmpTables');
const dbSegLast = context.oData.dbSegmentLast;
const sqlContext = {
context: context,
netId: context.uniqueNetworkRequestID,
reqId: context.uniqueRequestID,
rId: 1,
dbSegLast: dbSegLast,
systemQueryParameters: context.oData.systemQueryParameters
};
context.sql = sqlContext;
// set context.sql.container to be able to use dataCollectorGet to execute SELECT queries
context.sql.container = dbSegLast.sql.stmContainer = new sql.PutContainer();
startPutMasterDbSegCreate(sqlContext);
return asyncDone(null, context);
};
exports.createPutStatementsInsert = function (context, asyncDone) {
context.logger.silly('createPutStatements', 'createPutStatementsInsert');
const sqlContext = context.sql;
startPutMasterDbSegInsert(sqlContext);
return asyncDone(null, context);
};
/**
* Create SQL statements for the master table (=end of resource path)
* @param sqlContext
*/
function startPutMasterDbSegCreate(sqlContext) {
sqlContext.context.logger.debug('createPutStatements', 'startPutMasterDbSegCreate');
const dbSeg = sqlContext.dbSegLast;
const stmtContainer = dbSeg.sql.stmContainer;
dbSeg.sql.rIdNew = sqlTools.rIdToNewTableName(dbSeg._Alias, sqlContext.netId, sqlContext.reqId, sqlContext.rId++);
dbSeg.sql.rIdOld = sqlTools.rIdToOldTableName(dbSeg._Alias, sqlContext.netId, sqlContext.reqId, sqlContext.rId++);
//create statement for master table
stmtContainer.createTmp = masterTableCreate(sqlContext, dbSeg.sql.rIdNew);
stmtContainer.createTmpOld = masterTableCreate(sqlContext, dbSeg.sql.rIdOld);
if (sqlContext.context.db.isExternalHandledConnection === true) {
// We only truncate and delete temp tables when db connection is handled external
// otherwise we will self disconnect and temp tables will be deleted automatically
// build the truncate statement for created temporary table;
stmtContainer.createTmpTruncate = sql.buildTableStatement(sql.Truncate, sqlContext, stmtContainer.createTmp.table);
stmtContainer.createTmpOldTruncate = sql.buildTableStatement(sql.Truncate, sqlContext, stmtContainer.createTmpOld.table);
// build the drop statement for created temporary table;
stmtContainer.createTmpDrop = sql.buildTableStatement(sql.Drop, sqlContext, stmtContainer.createTmp.table);
stmtContainer.createTmpOldDrop = sql.buildTableStatement(sql.Drop, sqlContext, stmtContainer.createTmpOld.table);
}
}
/**
* Create SQL statements for the master table (=end of resource path)
* @param sqlContext
*/
function startPutMasterDbSegInsert(sqlContext) {
sqlContext.context.logger.debug('createPutStatements', 'startPutMasterDbSegInsert');
const dbSeg = sqlContext.dbSegLast;
const stmtContainer = dbSeg.sql.stmContainer;
let selectETagStmt;
//create insert statement for master table
stmtContainer.insertTmp = masterTableInsertToNew(sqlContext, dbSeg.sql.rIdNew);
//create statement to insert created data into new temp table
//stmtContainer.updateTmpCreateInfo = masterTableUpdateCreateInfoInNewTmpTable(sqlContext, dbSeg.sql.rIdNew);
//create insert statement for master table
stmtContainer.insertTmpOld = masterTableInsertToOld(sqlContext, dbSeg.sql.rIdOld);
//create update statement for master table
stmtContainer.updateReal = masterTableUpdate(sqlContext, dbSeg.sql.rIdNew);
// adjust stmtContainer to be able to use dataCollectorGet to execute the SELECT statement for the ETag
stmtContainer.selectFromTmp = masterTableSelect(sqlContext, dbSeg.sql.rIdNew);
stmtContainer.selectCreateInfoFromTmpOld = createSqlForSelectCreateInfoFromTmpOld(sqlContext, dbSeg.sql.rIdOld);
stmtContainer.select = [];
stmtContainer.selectTmp = [];
if (utils.isETagRequired(sqlContext.context, dbSeg)) {
//create select statement to generate ETag for the updated entity
selectETagStmt = sql.createSelectStmtForETag(dbSeg);
stmtContainer.select.push({ stm: selectETagStmt, dbSeg: dbSeg });
}
}
function masterTableCreate(sqlContext, rId) {
sqlContext.context.logger.debug('createPostStatements', 'masterTableCreate (ordered selects)');
const dbSeg = sqlContext.dbSegLast;
const stmCreate = new sql.Create();
stmCreate.setModifiers(['local', 'temporary', dbSeg.entityType.tableStoreType]);
stmCreate.setTableName(rId);
const properties = dbSeg.getPropertiesForCreate();
const propertiesDbOrdered = dbSeg.makeDbOrdered(properties);
stmCreate.addProperties(propertiesDbOrdered);
if (dbSeg._ExpandedNavigations.length > 0) {
stmCreate.addProperties(dbSeg.getNavPropertiesForCreate());
}
return stmCreate;
}
function masterTableInsertToNew(sqlContext, rId) {
sqlContext.context.logger.debug('createPutStatements', 'masterTableInsertToNew (ordered selects)');
const dbSeg = sqlContext.dbSegLast;
const stm = new sql.Insert();
stm.setTableName({ table: rId });
//SELECT
const properties = dbSeg.getQKeyProperties();
const all = properties.concat(dbSeg.getQNonKeyProperties());
const allOrdered = dbSeg.makeDbOrdered(all);
stm.addNames(allOrdered);
//the data is inserted later on
return stm;
}
function masterTableInsertToOld(sqlContext, rId) {
sqlContext.context.logger.debug('createPutStatements', 'masterTableInsertToNew (ordered selects)');
const dbSeg = sqlContext.dbSegLast;
const subSelect = new sql.Select();
if (dbSeg.entityType.kind === 3 && dbSeg.entityType._entityType.parameters && dbSeg.entityType._entityType.parameters.viaKey === true) {
subSelect.addSelects(dbSeg.getPropertiesForSelect());
subSelect.addSelects(dbSeg.getKeyPropertiesNotSelectedForSelect());
subSelect.addFrom(dbSeg.getAliasedTableName(), dbSeg.getInputParameters());
subSelect.addWhereKeyValuePairs(dbSeg.getQKeyWithValues());
} else {
const properties = dbSeg.getQKeyPropertiesForSelect();
const all = properties.concat(dbSeg.getQNonKeyPropertiesForSelect());
const allOrdered = dbSeg.makeDbOrdered(all);
subSelect.addSelects(allOrdered);
subSelect.setFrom(dbSeg.getAliasedTableName());
subSelect.addWhereKeyValuePairs(dbSeg.getQKeyWithValuesDB());
}
const stm = new sql.Insert();
stm.setTableName({ table: rId });
stm.setSubSelect(subSelect);
return stm;
}
/**
* Create select SQL statement for master table
* @param sqlContext
* @param rId
* @returns {exports.Select}
*/
function masterTableUpdate(sqlContext, rId) {
sqlContext.context.logger.debug('createPutStatements', 'masterTableUpdate');
const dbSeg = sqlContext.dbSegLast;
const createBy = dbSeg.entityType.getAddAdmindata('create', 'by');
const createAt = dbSeg.entityType.getAddAdmindata('create', 'at');
const update = new sql.Update();
update.setTable(dbSeg.getAliasedTableName('PERM')); //overwrite alias
//update.addSetCopyProperties(dbSeg.getQKeyProperties(), 'TEMP');
const copyPropertiesOrig = dbSeg.getQNonKeyProperties();
const copyProperties = [];
for (const prop of copyPropertiesOrig) {
if (!((createBy && prop.property === 'CREATED_BY') ||
(createAt && prop.property === 'CREATED_AT'))) {
copyProperties.push(prop);
}
}
update.addSetCopyProperties(copyProperties, 'TEMP');
update.setFrom({
table: rId,
alias: 'TEMP'
});
update.addWhereKeyValuePairs(dbSeg.getQKeyWithValues('PERM'));
return update;
}
/**
* Create select SQL statement to select from tmp table
* Since a application exit may be used to do the insert into the real table
* and possible create a custom key, we can't select with the client-key from the real table
* so in this case the application has the possiblity to pass the new key back to the client.
* @param sqlContext
* @param rId
* @returns {exports.Select}
*/
function masterTableSelect(sqlContext, rId) {
sqlContext.context.logger.debug('createPutStatements', 'masterTableSelect (ordered selects)');
const dbSeg = sqlContext.dbSegLast;
const stmSelect = new sql.Select();
stmSelect.addSelect(dbSeg.getQKeyPropertiesWith0123AliasForSelect(true));
let all = null;
if (dbSeg.entityType.kind === EntityType.entityKind.calculationView && dbSeg.entityType._entityType.parameters && dbSeg.entityType._entityType.parameters.viaKey === true) {
all = dbSeg.getSelectFragmentsFromAllProperties(true);
} else {
all = dbSeg.getPropertiesForSelect(true);
}
const allOrdered = dbSeg.makeDbOrdered(all);
stmSelect.addSelects(allOrdered);
if (dbSeg._ExpandedNavigations.length > 0) {
stmSelect.addSelects(dbSeg.getNavPropertiesForSelect(true));
}
if (utils.isETagRequired(sqlContext.context, dbSeg)) {
sql._addETagToSelect(dbSeg, stmSelect, rId);
}
stmSelect.setFrom({ schema: null, table: rId, alias: null });
return stmSelect;
}
function createSqlForSelectCreateInfoFromTmpOld(sqlContext, rId) {
sqlContext.context.logger.debug('createPutStatements', 'createInfoFromTmpOld');
const dbSeg = sqlContext.dbSegLast;
const createBy = dbSeg.entityType.getAddAdmindata('create', 'by');
const createAt = dbSeg.entityType.getAddAdmindata('create', 'at');
const stmSelect = new sql.Select();
const formulas = [];
if (createBy) {
formulas.push(new sql.SelectFormula(null, 'CREATED_BY'));
}
if (createAt) {
formulas.push(new sql.SelectFormula(null, 'CREATED_AT'));
}
stmSelect.addSelects(formulas);
stmSelect.setFrom({ schema: null, table: rId, alias: null });
return stmSelect;
}