loopback-connector-saphana
Version:
SAP HANA connector for loopback-datasource-juggler
311 lines (289 loc) • 10.7 kB
JavaScript
module.exports = mixinDiscovery;
function mixinDiscovery(SAPHANA) {
var async = require('async');
function paginateSQL(sql, orderBy, options) {
options = options || {};
var limit = '';
if (options.limit || options.skip || options.offset) {
limit = ' LIMIT ' + options.limit;
if (options.offset) {
limit = limit + ' OFFSET ' + (options.offset || options.skip || 0); // Offset starts from 0
}
}
if (orderBy) {
sql += ' ORDER BY ' + orderBy;
}
return sql + limit;
}
/**
* Build sql for listing tables
*
* @param options {all: for all owners, owner: for a given owner}
* @returns {string} The sql statement
*/
function queryTables(options) {
var sqlTables = null;
var owner = options.owner || options.schema;
if (options.all && !owner) {
sqlTables = paginateSQL('SELECT \'table\' AS "type", table_name AS "name", schema_name AS "owner"'
+ ' FROM SYS.TABLES', 'schema_name, table_name', options);
} else if (owner) {
sqlTables = paginateSQL('SELECT \'table\' AS "type", table_name AS "name", schema_name AS "owner"'
+ ' FROM SYS.TABLES WHERE schema_name = \'' + owner + '\'', 'schema_name, table_name', options);
} else {
sqlTables = paginateSQL('SELECT \'table\' AS "type", table_name AS "name",'
+ ' schema_name AS "owner" FROM SYS.TABLES WHERE schema_name = current_schema',
'table_name', options);
}
return sqlTables;
}
/**
* Build sql for listing views
*
* @param options {all: for all owners, owner: for a given owner}
* @returns {string} The sql statement
*/
function queryViews(options) {
var sqlViews = null;
if (options.views) {
var owner = options.owner || options.schema;
if (options.all && !owner) {
sqlViews = paginateSQL('SELECT \'view\' AS "type", view_name AS "name",'
+ ' schema_name AS "owner" FROM SYS.VIEWS',
'schema_name, view_name', options);
} else if (owner) {
sqlViews = paginateSQL('SELECT \'view\' AS "type", view_name AS "name",'
+ ' schema_name AS "owner" FROM SYS.VIEWS WHERE schema_name = \'' + owner + '\'',
'schema_name, view_name', options);
} else {
sqlViews = paginateSQL('SELECT \'view\' AS "type", view_name AS "name",'
+ ' current_schema AS "owner" FROM SYS.VIEWS',
'view_name', options);
}
}
return sqlViews;
}
/**
* Discover model definitions
*
* @param {Object} options Options for discovery
* @param {Function} [cb] The callback function
*/
SAPHANA.prototype.discoverModelDefinitions = function (options, cb) {
if (!cb && typeof options === 'function') {
cb = options;
options = {};
}
options = options || {};
var self = this;
var calls = [function (callback) {
self.query(queryTables(options), callback);
}];
if (options.views) {
calls.push(function (callback) {
self.query(queryViews(options), callback);
});
}
async.parallel(calls, function (err, data) {
if (err) {
cb(err, data);
} else {
var merged = [];
merged = merged.concat(data.shift());
if (data.length) {
merged = merged.concat(data.shift());
}
cb(err, merged);
}
});
};
/**
* Normalize the arguments
*
* @param table string, required
* @param options object, optional
* @param cb function, optional
*/
function getArgs(table, options, cb) {
if ('string' !== typeof table || !table) {
throw new Error('table is a required string argument: ' + table);
}
options = options || {};
if (!cb && 'function' === typeof options) {
cb = options;
options = {};
}
if (typeof options !== 'object') {
throw new Error('options must be an object: ' + options);
}
return {
owner: options.owner || options.schema,
table: table,
options: options,
cb: cb
};
}
/**
* Build the sql statement to query columns for a given table
*
* @param owner
* @param table
* @returns {String} The sql statement
*/
function queryColumns(owner, table) {
var sql = null;
if (owner) {
sql = paginateSQL('SELECT schema_name AS "owner", table_name AS "tableName", column_name AS "columnName", data_type_name AS "dataType",'
+ ' length AS "dataLength", scale AS "dataScale", is_nullable AS "nullable"'
+ ' FROM SYS.TABLE_COLUMNS'
+ ' WHERE schema_name = \'' + owner + '\''
+ (table ? ' AND table_name=\'' + table + '\'' : ''),
'table_name, position', {});
} else {
sql = paginateSQL('SELECT current_schema AS "owner", table_name AS "tableName", column_name AS "columnName", data_type_name AS "dataType",'
+ ' length AS "dataLength", scale AS "dataScale", is_nullable AS "nullable"'
+ ' FROM SYS.TABLE_COLUMNS'
+ (table ? ' WHERE table_name = \'' + table + '\'' : ''),
'table_name, position', {});
}
return sql;
}
/**
* Discover model properties from a table
*
* @param {String} table The table name
* @param {Object} options The options for discovery
* @param {Function} [cb] The callback function
*
*/
SAPHANA.prototype.discoverModelProperties = function (table, options, cb) {
var args = getArgs(table, options, cb);
var owner = args.owner;
table = args.table;
options = args.options;
cb = args.cb;
var sql = queryColumns(owner, table);
var callback = function (err, results) {
if (err) {
cb(err, results);
} else {
results.map(function (r) {
r.type = hdbDataTypeToJSONType(r.dataType, r.dataLength);
});
cb(err, results);
}
};
this.query(sql, callback);
};
/**
* Build the sql statement for querying primary keys of a given table
*
* @param owner
* @param table
* @returns {string}
*/
function queryForPrimaryKeys(owner, table) {
var sql = 'SELECT cs.schema_name AS "owner", '
+ 'cs.table_name AS "tableName", cs.column_name AS "columnName",'
+ ' cs.position AS "keySeq",'
+ ' cs.constraint_name AS "pkName" FROM'
+ ' SYS.CONSTRAINTS cs'
+ ' WHERE cs.is_primary_key = \'TRUE\'';
if (owner) {
sql += ' AND cs.schema_name = \'' + owner + '\'';
}
if (table) {
sql += ' AND cs.table_name = \'' + table + '\'';
}
sql += ' ORDER BY cs.schema_name, cs.table_name, cs.position ';
return sql;
}
/**
* Discover primary keys for a given table
*
* @param {String} table The table name
* @param {Object} options The options for discovery
* @param {Function} [cb] The callback function
*/
SAPHANA.prototype.discoverPrimaryKeys = function (table, options, cb) {
var args = getArgs(table, options, cb);
var owner = args.owner;
table = args.table;
options = args.options;
cb = args.cb;
var sql = queryForPrimaryKeys(owner, table);
this.query(sql, cb);
};
/**
* Build the sql statement for querying foreign keys of a given table
*
* @param owner
* @param table
* @returns {string}
*/
function queryForeignKeys(owner, table) {
var sql =
'SELECT rc.referenced_schema_name AS "fkOwner", rc.constraint_name AS "fkName", rc.referenced_table_name AS "fkTableName",'
+ ' rc.referenced_column_name AS "fkColumnName", tc.position AS "keySeq",'
+ ' rc.schema_name AS "pkOwner", \'PK\' AS "pkName", '
+ ' rc.table_name AS "pkTableName", rc.column_name AS "pkColumnName"'
+ ' FROM SYS.REFERENTIAL_CONSTRAINTS rc'
+ ' LEFT JOIN SYS.TABLE_COLUMNS AS tc'
+ ' ON rc.schema_name = tc.schema_name AND rc.table_name = tc.table_name AND rc.column_name = tc.column_name';
if (owner) {
sql += ' WHERE rc.schema_name =\'' + owner + '\'';
} else {
sql += ' WHERE rc.schema_name = current_schema';
}
if (table) {
sql += ' AND rc.table_name = \'' + table + '\'';
}
sql += ' ORDER BY rc.schema_name, rc.table_name, tc.position ';
return sql;
}
/**
* Discover foreign keys for a given table
*
* @param {String} table The table name
* @param {Object} options The options for discovery
* @param {Function} [cb] The callback function
*/
SAPHANA.prototype.discoverForeignKeys = function (table, options, cb) {
var args = getArgs(table, options, cb);
var owner = args.owner;
table = args.table;
options = args.options;
cb = args.cb;
var sql = queryForeignKeys(owner, table);
this.query(sql, cb);
};
function hdbDataTypeToJSONType(hdbType, dataLength) {
var type = hdbType.toUpperCase();
switch (type) {
case 'VARCHAR':
return (dataLength === 1) ? 'Boolean' : 'String';
case 'NVARCHAR':
case 'ALPHANUM':
case 'SHORTTEXT':
return 'String';
case 'VARBINARY':
return 'Binary';
case 'TINYINT':
case 'SMALLINT':
case 'INTEGER':
case 'BIGINT':
case 'SMALLDECIMAL':
case 'DECIMAL':
case 'REAL':
case 'DOUBLE':
return 'Number';
case 'DATE':
case 'TIME':
case 'SECONDDATE':
case 'TIMESTAMP':
return 'Date';
default:
return 'String';
}
}
}