jsmdb
Version:
JSMumps Database Component
380 lines (294 loc) • 9.44 kB
JavaScript
/*
* JSMumps Database API
*
* child.js: the child process
*
*
* Copyright (C) 2017, 2021 Coherent Logic Development LLC
*
* Author: John P. Willis <jpw@coherent-logic.com>
*
*/
const nodem = require('nodem');
const _ = require('underscore');
const log = require('jsmlog');
var current = {
global: null,
subscripts: [],
isLocal: false
};
var logLevel = process.argv[2];
var logger = new log.Logger({
logLevel: logLevel,
moduleName: "jsmdb (worker, pid " + process.pid + ")"
})
var db = new nodem.Gtm();
var result = db.open();
if(!result.ok) {
sendMessage('INIT_ERROR', {message: "Error opening database"});
}
else {
logger.debug("opened M database with pid " + result.pid);
}
const nodemAPI = {
getObject: function (opts) {
var result = null;
try {
result = {
data: nodemAPI.getObjectX(),
ok: 1
};
}
catch(ex) {
result = {
data: {},
ok: 0,
ErrorMessage: ex,
ErrorCode: ""
};
}
return result;
},
getObjectX: function (subscripts, outputStruct) {
if(subscripts) {
var mSubscripts = fastClone(subscripts);
}
else {
var mSubscripts = fastClone(current.subscripts);
}
if(!outputStruct) {
var outStruct = {};
if(current.isLocal) {
if(db.data({local: current.global}).defined % 10) {
outStruct[""] = db.get({local: current.global}).data;
}
}
else {
if(db.data({global: current.global}).defined % 10) {
outStruct[""] = db.get({global: current.global}).data;
}
}
console.log(JSON.stringify(outStruct, null, 4));
}
else {
var outStruct = fastClone(outputStruct);
}
var lastResult = false;
mSubscripts.push("");
while(!lastResult) {
if(current.isLocal) {
var order = db.order({local: current.global, subscripts: mSubscripts});
}
else {
var order = db.order({global: current.global, subscripts: mSubscripts});
}
if(!order.ok) {
logger.debug("getObjectX() failed on nodem order() call");
throw("NodeM error calling order()");
}
if(order.result === "") {
lastResult = true;
continue;
}
mSubscripts = order.subscripts;
var structSubs = mSubscripts.slice(current.subscripts.length);
if(current.isLocal) {
var data = db.data({local: current.global, subscripts: mSubscripts});
}
else {
var data = db.data({global: current.global, subscripts: mSubscripts});
}
switch(data.defined) {
case 11:
if(current.isLocal) {
var nodeValue = db.get({local: current.global, subscripts: mSubscripts});
}
else {
var nodeValue = db.get({global: current.global, subscripts: mSubscripts});
}
if(!nodeValue.ok) {
logger.debug("getObjectX() failed on nodem get() call");
throw("NodeM error calling get()");
}
buildObject(outStruct, structSubs, nodeValue.data, true);
_.extend(outStruct, nodemAPI.getObjectX(mSubscripts, outStruct));
break;
case 10:
_.extend(outStruct, nodemAPI.getObjectX(mSubscripts, outStruct))
break;
case 1:
if(current.isLocal) {
buildObject(outStruct, structSubs, db.get({local: current.global, subscripts: mSubscripts}).data, false);
}
else {
buildObject(outStruct, structSubs, db.get({global: current.global, subscripts: mSubscripts}).data, false);
}
break;
}
}
return outStruct;
},
setObject: function (opts) {
var result = null;
try {
result = {
data: nodemAPI.setObjectX(opts.data),
ok: 1
};
}
catch(ex) {
result = {
data: {},
ok: 0,
ErrorMessage: "Error in setObject()",
ErrorCode: ""
};
}
return result;
},
setObjectX: function (inputObject, subscripts) {
if(subscripts) {
var subs = fastClone(subscripts);
}
else {
var subs = fastClone(current.subscripts);
}
for(var key in inputObject) {
subs.push(key);
switch(typeof inputObject[key]) {
case 'object':
nodemAPI.setObjectX(inputObject[key], subs);
break;
case 'string':
case 'number':
var result = db.set({global: current.global, subscripts: subs, data: inputObject[key]});
if(!result.ok) {
logger.debug("setObjectX() failed on nodem set() call");
throw("NodeM error calling set()");
}
break;
}
subs.pop();
}
return;
},
get: function (opts) {
return db.get({global: current.global, subscripts: current.subscripts});
},
set: function (opts) {
return db.set({global: current.global, subscripts: current.subscripts, data: opts.data});
},
merge: function(opts) {
return db.merge(opts);
},
kill: function (opts) {
return db.kill({global: current.global, subscripts: current.subscripts, data: opts.data});
},
lock: function (opts) {
return db.lock({global: current.global, subscripts: current.subscripts, timeout: opts.timeout || 0});
},
unlock: function (opts) {
return db.unlock({global: current.global, subscripts: current.subscripts});
},
data: function (opts) {
return db.data({global: current.global, subscripts: current.subscripts});
},
order: function (opts) {
return db.order({global: current.global, subscripts: current.subscripts});
},
query: function (opts) {
return db.next_node({global: current.global, subscripts: current.subscripts});
},
function: function (opts) {
return db.function({function: opts.func, arguments: opts.args});
},
procedure: function (opts) {
return db.procedure({procedure: opts.proc, arguments: opts.args})
},
localDirectory: function (opts) {
return db.localDirectory();
}
};
function fastClone(obj)
{
return JSON.parse(JSON.stringify(obj));
}
function isNumeric(n)
{
return !isNaN(parseFloat(n)) && isFinite(n);
}
function buildObject(obj, keyArray, value, lastKeyEmpty)
{
if(lastKeyEmpty) {
keyArray.push("");
}
lastKeyIndex = keyArray.length - 1;
for(var i = 0; i < lastKeyIndex; ++ i) {
key = keyArray[i];
if (!(key in obj)) {
obj[key] = {};
}
obj = obj[key];
}
obj[keyArray[lastKeyIndex]] = value;
}
function handleMessage(msg)
{
logger.debug("received " + msg.action);
if(msg.action === "CP_SHUTDOWN") {
logger.info("received CP_SHUTDOWN; closing database");
db.close({resetTerminal: true});
process.exit();
}
if(!msg.options) {
throw("Invalid message sent to worker process.");
}
current.global = msg.options.global || null;
current.subscripts = msg.options.subscripts || [];
for(key in msg.locals) {
val = msg.locals[key];
db.set({
local: key,
data: val
});
}
var result = nodemAPI[msg.action](msg.options);
if(result.ok) {
var locals = db.localDirectory();
result.locals = {};
locals.forEach((itm) => {
if(db.data(itm) > 1) {
current = {
global: itm,
subscripts: [],
isLocal: true
};
result.locals[itm] = nodemAPI.getObject().data;
}
else {
result.locals[itm] = db.get(itm);
}
});
current.isLocal = false;
//console.log(JSON.stringify(result, null, 4));
sendMessage('DBOP_COMPLETE', result);
//TODO: pull in local symbol table
//TODO: send message to parent containing local symbol table
}
else {
var errObj = {
message: result.ErrorMessage || "",
code: result.ErrorCode || ""
};
sendMessage('DBOP_ERROR', errObj);
}
}
function sendMessage(type, data)
{
var msg = {
type: type,
data: data
};
process.send(msg);
}
process.on('message', handleMessage);