@sap/xsodata
Version:
Expose data from a HANA database as OData V2 service with help of .xsodata files.
353 lines (290 loc) • 10.8 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
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;
}