censql
Version:
A NodeJS command line client for SAP HANA
435 lines (344 loc) • 9.62 kB
JavaScript
var charm = require('charm')(process.stdout);
var colors = require("colors");
var async = require("async");
var _ = require('lodash');
var StudioFormatter = require('./StudioFormatter.js');
var StudioDbHandler = require('./StudioDbHandler.js');
var StudioSqlConsole = require('./StudioSqlConsole.js');
var argv = require('optimist').argv;
var StudioSession = function(screen, hdb, commandHandler) {
this.screen = screen;
this.hdb = hdb;
this.commandHandler = commandHandler;
this.studioDbHandler = new StudioDbHandler(hdb, this.commandHandler);
this.sqlConsole = new StudioSqlConsole(screen, this.studioDbHandler);
this.formatter = new StudioFormatter(this, screen, this.sqlConsole);
this.dataPreviewRows = argv.preview_size || 500;
/**
* Print temp loading screen
*/
this.screen.clear();
this.screen.print(colors.yellow("Loading..."));
/**
* Clone hana connection
*/
this.studioDbHandler.init(function(err, data) {
if (err) {
this.screen.error(err, function() {
process.exit(1);
});
} else {
this.init();
}
}.bind(this))
}
StudioSession.prototype.init = function() {
/**
* Current focus
* @type {String}
*/
this.focus = "sql-console";
/**
* Copy the keypress function
* @type [Function]
*/
this.oldKeyPress = process.stdin._events.keypress;
/**
* Disable keyboard input
* @type [Function]
*/
process.stdin._events.keypress = function() {}
this.studioDbHandler.getSchemas(function(err, schemas) {
if (err) {
this.screen.clear();
this.formatter.fullPageAlert(err, "bgRed", false, true);
return
}
this.studioDbHandler.getTables(schemas[0].SCHEMA_NAME, function(err, tables) {
if (err) {
this.screen.clear();
this.formatter.fullPageAlert(err, "bgRed", false, true);
return
}
/**
* Draw
*/
this.formatter.init(schemas, tables);
/**
* Load control keys
*/
this.setControlKeys();
/**
* Bind keypresses to out listener
* @type [Function]
*/
process.stdin._events.keypress = this.onKeyPress.bind(this);
}.bind(this));
}.bind(this));
}
StudioSession.prototype.setControlKeys = function() {
/**
* Odd but allows keys[IsControllPressed][IsShiftPressed][KeyName]()
*/
this.controlKeys = {
false: {
false: {},
true: {}
},
true: {
false: {},
true: {}
}
};
this.loadUiControls();
switch (this.focus) {
case "sql-console":
this.loadSqlControls();
break;
case "data-pane":
this.loadDatapaneControls();
break;
}
}
StudioSession.prototype.loadSqlControls = function() {
/**
* Move console cursor
*/
this.controlKeys.false.false.right = function() {
this.sqlConsole.moveCursor(1, 0, true);
}.bind(this);
this.controlKeys.false.false.left = function() {
this.sqlConsole.moveCursor(-1, 0, true);
}.bind(this);
this.controlKeys.false.false.down = function() {
this.sqlConsole.moveCursor(0, 1, true)
}.bind(this);
this.controlKeys.false.false.up = function() {
this.sqlConsole.moveCursor(0, -1, true)
}.bind(this);
/**
* Backspace
*/
this.controlKeys.false.false.backspace = function() {
this.sqlConsole.backspace();
}.bind(this);
/**
* Goto start of line
*/
this.controlKeys.false.false.home = function() {
this.sqlConsole.moveCursor(-Infinity, 0, true)
}.bind(this);
/**
* Goto end of line
*/
this.controlKeys.false.false.end = function() {
this.sqlConsole.moveCursor(Infinity, 0, true)
}.bind(this);
/**
* Pageup and down height of console
*/
this.controlKeys.false.false.pageup = function() {
this.sqlConsole.moveScroll(-this.sqlConsole.region.h);
}.bind(this);
this.controlKeys.false.false.pagedown = function() {
this.sqlConsole.moveScroll(this.sqlConsole.region.h);
}.bind(this);
/**
* Run query
*/
this.controlKeys.false.false.enter = function() {
this.runUserQuery();
}.bind(this);
this.controlKeys.true.false.delete = function() {
this.sqlConsole.clear();
}.bind(this)
this.controlKeys.false.false.delete = function() {
this.sqlConsole.deleteChar();
}.bind(this)
}
StudioSession.prototype.runUserQuery = function() {
var query = this.sqlConsole.getQuery();
this.studioDbHandler.exec(query, function(err, data) {
if (err) {
this.formatter.fullPageAlert(err);
return;
}
this.formatter.drawOueryOutputView(query, data);
}.bind(this));
}
StudioSession.prototype.loadUiControls = function() {
/**
* Rotate schemas
*/
this.controlKeys.false.true.down = function() {
this.formatter.rotateSchemas(1);
}.bind(this);
this.controlKeys.false.true.up = function() {
this.formatter.rotateSchemas(-1);
}.bind(this);
/**
* Select schema
*/
this.controlKeys.false.true.right = function() {
if (this.formatter.tableListMode == "Views") {
this.studioDbHandler.getViews(this.formatter.schemas[0].SCHEMA_NAME, function(err, data) {
if (err) {
this.formatter.fullPageAlert(err);
process.exit(1);
}
this.formatter.setTables(data);
}.bind(this))
} else {
this.studioDbHandler.getTables(this.formatter.schemas[0].SCHEMA_NAME, function(err, data) {
if (err) {
this.formatter.fullPageAlert(err);
process.exit(1);
}
this.formatter.setTables(data);
}.bind(this))
}
}.bind(this);
/**
* Switch between tables and views
*/
this.controlKeys.false.true.tab = function() {
this.toggleTableBoxView();
}.bind(this);
/**
* Toggle between datapane and sql console
*/
this.controlKeys.false.false.tab = function() {
this.toggleFocus();
this.setControlKeys();
this.sqlConsole.moveCursor();
}.bind(this);
/**
* Exit studio
*/
this.controlKeys.true.false.c = function() {
this.exitStudio();
}.bind(this);
/**
* Rotate tables
*/
this.controlKeys.true.false.down = function() {
this.formatter.rotateTables(1);
}.bind(this);
this.controlKeys.true.false.up = function() {
this.formatter.rotateTables(-1);
}.bind(this);
/**
* Load table/view into datapane
*/
this.controlKeys.true.false.right = function() {
if (this.formatter.schemas[0] && this.formatter.tables[0]) {
if (this.focus == "sql-console") {
this.sqlConsole.type('"' + this.formatter.schemas[0].SCHEMA_NAME + '"."' + this.formatter.tables[0].NAME + '"')
} else {
this.loadTableView(this.formatter.schemas[0].SCHEMA_NAME, this.formatter.tables[0].NAME, this.formatter.tableListMode == "Views");
}
}
}.bind(this);
}
StudioSession.prototype.loadDatapaneControls = function() {
/**
* Scroll datapane
*/
this.controlKeys.false.false.right = function() {
this.formatter.scrollDataPaneDebounced(10, 0)
}.bind(this);
this.controlKeys.false.false.left = function() {
this.formatter.scrollDataPaneDebounced(-10, 0)
}.bind(this);
this.controlKeys.false.false.down = function() {
this.formatter.scrollDataPaneDebounced(0, 3)
}.bind(this);
this.controlKeys.false.false.up = function() {
this.formatter.scrollDataPaneDebounced(0, -3)
}.bind(this)
/**
* Scroll entire page
*/
this.controlKeys.false.false.pagedown = function() {
this.formatter.scrollDataPaneDebounced(0, Math.abs(this.formatter.height - 10))
}.bind(this);
this.controlKeys.false.false.pageup = function() {
this.formatter.scrollDataPaneDebounced(0, -Math.abs(this.formatter.height - 10))
}.bind(this);
/**
* Goto top left of table
*/
this.controlKeys.false.false.home = function() {
this.formatter.scrollDataPaneDebounced(-Infinity, -Infinity)
}.bind(this);
}
StudioSession.prototype.toggleFocus = function() {
if (this.focus == "sql-console") {
this.focus = "data-pane";
} else {
this.focus = "sql-console";
}
this.formatter.drawBorder();
this.formatter.drawHelpText();
}
StudioSession.prototype.onKeyPress = function(ch, key) {
var pressed = false;
/**
* If we have a control key for this input run it, if not type it
*/
if (key) {
if (_.has(this.controlKeys, key.ctrl + "." + key.shift + "." + key.name)) {
_.get(this.controlKeys, key.ctrl + "." + key.shift + "." + key.name)();
pressed = true;
}
}
/**
* Type into SQL console
*/
if (!pressed && this.focus == "sql-console" && ch && !(key && key.ctrl)) {
this.sqlConsole.type(ch);
}
}
StudioSession.prototype.loadTableView = function(schema, table, isView) {
this.formatter.fullPageAlert('Loading ' + this.dataPreviewRows + ' rows from "' + schema + '"."' + table + '"...', "bgYellow");
async.parallel([
function(callback) {
this.studioDbHandler.loadStructure(schema, table, callback)
}.bind(this),
function(callback) {
this.studioDbHandler.selectAllLimit(schema, table, this.dataPreviewRows, callback)
}.bind(this),
], function(err, data) {
if (err) {
this.formatter.fullPageAlert(err);
return;
}
this.formatter.drawTableView(schema, table, data[0], data[1], isView);
}.bind(this))
}
StudioSession.prototype.toggleTableBoxView = function() {
if (this.formatter.tableListMode == "Tables") {
this.formatter.setTableListMode("Views");
this.studioDbHandler.getViews(this.formatter.schemas[0].SCHEMA_NAME, function(err, data) {
if (err) {
this.formatter.fullPageAlert(err);
process.exit(1);
}
this.formatter.setTables(data);
}.bind(this))
} else {
this.formatter.setTableListMode("Tables");
this.studioDbHandler.getTables(this.formatter.schemas[0].SCHEMA_NAME, function(err, data) {
if (err) {
this.formatter.fullPageAlert(err);
process.exit(1);
}
this.formatter.setTables(data);
}.bind(this))
}
}
StudioSession.prototype.exitStudio = function() {
this.formatter.byebye();
process.exit(0);
}
module.exports = StudioSession;