UNPKG

@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
'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; }