@sap/hdbext
Version:
Hana-client extension library and utility functions for using SAP HANA in node.js
146 lines (122 loc) • 4.93 kB
JavaScript
;
var async = require('async');
var VError = require('verror');
var debug = require('debug')('hdbext:sp');
var StoredProcedure = require('./StoredProcedure');
module.exports = load;
function load(client, schema, name, callback) {
var procInfo = {
schema: null,
name: name,
metadata: null
};
async.waterfall([
function resolveSchema(cb) {
if (schema) {
return cb(null, schema);
}
client.exec('SELECT CURRENT_SCHEMA FROM DUMMY', function (err, rs) {
if (err) {
return cb(err);
}
cb(null, rs[0].CURRENT_SCHEMA);
});
},
function resolveProcedureInfo(schema, cb) {
procInfo.schema = schema;
fetchMetadata(client, procInfo, function (err, metadata) {
if (err) {
return cb(err);
}
procInfo.metadata = metadata;
if (procInfo.metadata.length) {
return cb(null, procInfo);
}
checkIfProcedureExists(client, procInfo, function (err, exists) {
if (err) {
return cb(err);
}
if (exists) {
debug('Procedure %s.%s does not have any parameters', procInfo.schema, procInfo.name);
return cb(null, procInfo);
}
checkSynonyms(client, procInfo, function (err, result) {
if (err) {
return cb(err);
}
if (!result.isSynonym) {
return cb(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, result.schema, result.name);
// use the data of the referenced procedure
procInfo.schema = result.schema;
procInfo.name = result.name;
procInfo.metadata = null;
fetchMetadata(client, procInfo, function (err, metadata) {
if (err) {
return cb(err);
}
procInfo.metadata = metadata;
cb(null, procInfo);
});
});
});
});
},
function buildSpFunction(procInfo, cb) {
var sp = new StoredProcedure(client, procInfo.schema, procInfo.name, procInfo.metadata);
var spFunction = StoredProcedure.prototype.exec.bind(sp);
spFunction.paramsMetadata = procInfo.metadata;
cb(null, spFunction);
}
], callback);
}
function fetchMetadata(client, procInfo, cb) {
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], cb);
}
function checkSynonyms(client, procInfo, cb) {
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 cb(err);
}
var result = { isSynonym: !!rs.length };
if (!result.isSynonym) {
return cb(null, result);
}
if (rs[0].IS_VALID !== 'TRUE') {
debug('Data for synonym %s.%s: %j', procInfo.schema, procInfo.name, rs[0]);
return cb(new VError('Synonym %s.%s is not valid', procInfo.schema, procInfo.name));
}
result.schema = rs[0].OBJECT_SCHEMA;
result.name = rs[0].OBJECT_NAME;
cb(null, result);
});
}
function checkIfProcedureExists(client, procInfo, cb) {
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 cb(err);
}
return cb(null, !!rs[0].count);
});
}