@sap/hdbext
Version:
Hana-client extension library and utility functions for using SAP HANA in node.js
135 lines (118 loc) • 4.91 kB
JavaScript
;
var VError = require('verror');
var debug = require('debug')('hdbext:sp');
var StoredProcedure = require('./StoredProcedure');
module.exports = loadPromise;
async function loadPromise(client, schema, name) {
var procInfo = {
schema: null,
name: name,
metadata: null
};
function resolveSchema() {
return new Promise ((resolve, reject) => {
client.exec('SELECT CURRENT_SCHEMA FROM DUMMY', function (err, rs) {
if (err) { return reject(err); }
schema = rs[0].CURRENT_SCHEMA;
resolve (schema);
});
});
}
if (!schema) {
await resolveSchema();
}
async function resolveProcedureInfoPromise (schema) {
procInfo.schema = schema;
await fetchMetadataPromise(client, procInfo);
if (procInfo.metadata.length) {
return procInfo;
}
var exist = await checkIfProcedureExistsPromise(client, procInfo);
if (exist) {
debug('Procedure %s.%s does not have any parameters', procInfo.schema, procInfo.name);
return procInfo;
}
var checkSynResult = await checkSynonymsPromise(client, procInfo);
if (!checkSynResult.isSynonym) {
throw new VError('Procedure %s.%s does not exist', procInfo.schema, procInfo.name);
}
debug('%s.%s is a synonym for %s.%s', procInfo.schema, procInfo.name, checkSynResult.schema, checkSynResult.name);
// use the data of the referenced procedure
procInfo.schema = checkSynResult.schema;
procInfo.name = checkSynResult.name;
procInfo.metadata = null;
await fetchMetadataPromise(client, procInfo);
return procInfo;
}
function buildSpFunctionPromise(procInfo) {
return new Promise ((resolve) => {
var sp = new StoredProcedure(client, procInfo.schema, procInfo.name, procInfo.metadata);
var spFunction = StoredProcedure.prototype.exec.bind(sp);
spFunction.paramsMetadata = procInfo.metadata;
resolve(spFunction);
});
}
var procInfoPromise = await resolveProcedureInfoPromise(schema);
return await buildSpFunctionPromise(procInfoPromise);
}
function fetchMetadataPromise(client, procInfo) {
return new Promise((resolve, reject) => {
var sqlProcedureMetadata = "SELECT \
PARAMS.PARAMETER_NAME, \
PARAMS.DATA_TYPE_NAME, \
PARAMS.PARAMETER_TYPE, \
PARAMS.HAS_DEFAULT_VALUE, \
PARAMS.IS_INPLACE_TYPE, \
PARAMS.TABLE_TYPE_SCHEMA, \
PARAMS.TABLE_TYPE_NAME, \
\
CASE WHEN SYNONYMS.OBJECT_NAME IS NULL THEN 'FALSE' ELSE 'TRUE' END AS IS_TABLE_TYPE_SYNONYM, \
IFNULL(SYNONYMS.OBJECT_SCHEMA, '') AS OBJECT_SCHEMA, \
IFNULL(SYNONYMS.OBJECT_NAME, '') AS OBJECT_NAME \
\
FROM SYS.PROCEDURE_PARAMETERS AS PARAMS \
LEFT JOIN SYS.SYNONYMS AS SYNONYMS \
ON SYNONYMS.SCHEMA_NAME = PARAMS.TABLE_TYPE_SCHEMA AND SYNONYMS.SYNONYM_NAME = PARAMS.TABLE_TYPE_NAME \
WHERE PARAMS.SCHEMA_NAME = ? AND PARAMS.PROCEDURE_NAME = ? \
ORDER BY PARAMS.POSITION";
client.exec(sqlProcedureMetadata, [procInfo.schema, procInfo.name], (err, metadata) => {
if (err) {
return reject(err);
}
procInfo.metadata = metadata;
resolve();
});
});
}
function checkSynonymsPromise (client, procInfo) {
return new Promise ((resolve, reject) => {
var sqlLookupSynonym = 'SELECT OBJECT_SCHEMA, OBJECT_NAME, IS_VALID FROM SYS.SYNONYMS WHERE SCHEMA_NAME = ? AND SYNONYM_NAME = ?';
client.exec(sqlLookupSynonym, [procInfo.schema, procInfo.name], function (err, rs) {
if (err) {
return reject(err);
}
var result = { isSynonym: !!rs.length };
if (!result.isSynonym) {
return resolve(result);
}
if (rs[0].IS_VALID !== 'TRUE') {
debug('Data for synonym %s.%s: %j', procInfo.schema, procInfo.name, rs[0]);
return reject(new VError('Synonym %s.%s is not valid', procInfo.schema, procInfo.name));
}
result.schema = rs[0].OBJECT_SCHEMA;
result.name = rs[0].OBJECT_NAME;
resolve(result);
});
});
}
function checkIfProcedureExistsPromise(client, procInfo) {
return new Promise ((resolve, reject) => {
var sqlCheckIfProcedureExists = 'SELECT COUNT(0) AS "count" FROM SYS.PROCEDURES WHERE SCHEMA_NAME = ? AND PROCEDURE_NAME = ?';
client.exec(sqlCheckIfProcedureExists, [procInfo.schema, procInfo.name], function (err, rs) {
if (err) {
return reject(err);
}
resolve(!!rs[0].count);
});
});
}