Changeset: dcae411fd381 for MonetDB
URL: http://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=dcae411fd381
Added Files:
clients/nodejs/Tests/nodetest.js
clients/nodejs/monetdb/README
clients/nodejs/monetdb/mapiclient.js
clients/nodejs/monetdb/package.json
Modified Files:
clients/R/MonetDB.R/R/mapi.R
sql/backends/monet5/sql_scenario.c
Branch: default
Log Message:
Merge with Oct2014 branch.
diffs (truncated from 529 to 300 lines):
diff --git a/clients/R/MonetDB.R/R/mapi.R b/clients/R/MonetDB.R/R/mapi.R
--- a/clients/R/MonetDB.R/R/mapi.R
+++ b/clients/R/MonetDB.R/R/mapi.R
@@ -1,6 +1,5 @@
# MAPI implementation for R
-PROTOCOL_v8 <- 8
PROTOCOL_v9 <- 9
MAX_PACKET_SIZE <- 8192
@@ -232,14 +231,12 @@ REPLY_SIZE <- 100 # Apparently, -1 me
# no need to check the returned values, as there is none. If we get no
error, all is well.
return(env)
}
-
}
}
.mapiParseHeader <- function(line, stupidInverseColsRows=FALSE) {
- tableinfo <- strsplit(line, " ", fixed=TRUE, useBytes=TRUE)
- tableinfo <- tableinfo[[1]]
-
+ tableinfo <- strsplit(line, " ", fixed=TRUE, useBytes=TRUE)[[1]]
+
id <- as.numeric(tableinfo[2])
if (!stupidInverseColsRows) {
rows <- as.numeric(tableinfo[3])
@@ -338,8 +335,7 @@ REPLY_SIZE <- 100 # Apparently, -1 me
.monetdbd.command <- function(passphrase, host="localhost", port=50000L,
timeout=86400L) {
socket <- .mapiConnect(host, port, timeout)
.mapiAuthenticate(socket, "merovingian", "monetdb", passphrase,
language="control")
- .mapiWrite(socket, "#all status\n")
- ret <- .mapiRead(socket)
+ ret <- .mapiRequest(socket, "#all status\n")
.mapiDisconnect(socket)
return (ret)
}
diff --git a/clients/nodejs/Tests/nodetest.js b/clients/nodejs/Tests/nodetest.js
new file mode 100644
--- /dev/null
+++ b/clients/nodejs/Tests/nodetest.js
@@ -0,0 +1,78 @@
+var monetdb = require('../monetdb');
+var assert = require('assert');
+
+var dbport = parseInt(process.argv[2]);
+var dbname = process.argv[3];
+
+/* lets first check some failing connection attempts */
+monetdb.connect({host:'veryinvalidhostnamethathopefullyresolvesnowhere'},
function(resp) {
+ assert.equal(false,resp.success);
+ assert(resp.message.trim().length > 0);
+});
+
+monetdb.connect({dbname:'nonexist', port:dbport}, function(resp) {
+ assert.equal(false,resp.success);
+ assert(resp.message.trim().length > 0);
+});
+
+monetdb.connect({dbname:dbname, user:'nonexist', port:dbport}, function(resp) {
+ assert.equal(false,resp.success);
+ assert(resp.message.trim().length > 0);
+});
+
+/* now actually connect */
+var conn = monetdb.connect({dbname:dbname, port:dbport}, function(resp) {
+ assert.equal(true,resp.success);
+});
+
+
+/* some querying */
+conn.query('start transaction');
+
+conn.query('create table foo(a int, b float, c clob)');
+conn.query("insert into foo values
(42,4.2,'42'),(43,4.3,'43'),(44,4.4,'44'),(45,4.5,'45')");
+
+conn.query('select * from foo', function(res) {
+ assert.equal(true, res.success);
+
+ assert.equal('table', res.type);
+ assert.equal(4, res.rows);
+ assert.equal(3, res.cols);
+
+ assert.equal(3, res.structure.length);
+
+ assert.equal('a', res.structure[0].column);
+ assert.equal('int', res.structure[0].type);
+
+ assert.equal(4, res.data.length);
+
+ assert.equal(42, res.data[0][res.structure[0].index]);
+ assert.equal(4.3, res.data[1][1]);
+ assert.equal('44', res.data[2][2]);
+});
+
+conn.query('delete from foo; drop table foo; rollback');
+
+/* query that will force multi-block operations */
+function rep(str,n) {
+ ret = '';
+ for (var i = 0; i< n; i++) {
+ ret += str;
+ }
+ return ret;
+}
+var longstr = rep('ABCDEFGHIJKLMNOP',10000);
+
+conn.query("SELECT '"+longstr+"'", function(res) {
+ assert.equal(true, res.success);
+ assert.equal(longstr,res.data[0][0]);
+
+});
+
+/* failing query */
+conn.query('MEHR BIER', function(res) {
+ assert.equal(false,res.success);
+ assert(res.message.trim().length > 0);
+});
+
+conn.close();
\ No newline at end of file
diff --git a/clients/nodejs/monetdb/README b/clients/nodejs/monetdb/README
new file mode 100644
--- /dev/null
+++ b/clients/nodejs/monetdb/README
@@ -0,0 +1,11 @@
+This package connects node.js and MonetDB
+
+Example usage:
+
+var conn = require('monetdb').connect({'dbname':'mydb'} , function(response) {
+ if (response.success) console.log('connected');
+});
+
+conn.request('SELECT 1', function(response) {
+ console.log(response);
+});
\ No newline at end of file
diff --git a/clients/nodejs/monetdb/mapiclient.js
b/clients/nodejs/monetdb/mapiclient.js
new file mode 100644
--- /dev/null
+++ b/clients/nodejs/monetdb/mapiclient.js
@@ -0,0 +1,356 @@
+var net = require('net');
+var crypto = require('crypto');
+
+function MonetDBConnection(options, conncallback) {
+ this.state = 'new';
+ this.options = options;
+ this.read_leftover = 0;
+ this.read_final = false;
+ this.read_str = '';
+ this.read_callback = undefined;
+ this.conn_callback = conncallback;
+ this.mapi_blocksize = 8192;
+
+ this.queryqueue = [];
+ var thizz = this;
+ this.socket = net.connect(options.port, options.host, function() {
+ thizz.state = 'connected';
+ });
+ this.socket.on('data', function(data) {
+ thizz.handleInput(data);
+ });
+ this.socket.on('end', function() {
+ thizz.state = 'disconnected';
+ });
+ this.socket.on('error', function(x) {
+ if (conncallback != undefined)
+ conncallback({'success':false, 'message':x.toString()});
+ });
+ /* some setup */
+ this.request('Xreply_size -1', undefined, true);
+ this.request('Xauto_commit 1', undefined, true);
+ /* get server environment into connector */
+ this.request('SELECT * FROM env()', function(x) {
+ thizz.env = {};
+ x.data.forEach(function(l) {
+ thizz.env[l.name] = l.value;
+ });
+ });
+ this.request('SELECT 42', function(x) {
+ if (this.conn_callback != undefined)
+ this.conn_callback({'success':true, 'message':'ok'});
+ });
+}
+
+MonetDBConnection.prototype.request =
+MonetDBConnection.prototype.query = function(message, callback, raw) {
+ if (!raw) {
+ message = 's'+message+';';
+ }
+ this.queryqueue.push({'message' : message , 'callback' : callback})
+}
+
+
+MonetDBConnection.prototype.handleMessage = function(message) {
+ if (this.options.debug)
+ console.log('RX ['+this.state+']: '+message);
+
+ /* prompt, good */
+ if (message == '') {
+ this.state = 'ready';
+ this.nextOp();
+ return;
+ }
+
+ /* monetdbd redirect, ignore. We will get another challenge soon */
+ if (message.charAt(0) == '^') {
+ return;
+ }
+
+ if (this.state == 'connected') {
+ /* error message during authentication? */
+ if (message.charAt(0) == '!') {
+ message = 'Error:
'+message.substring(1,message.length-1);
+ if (this.conn_callback != undefined)
+ this.conn_callback({'success':false,
'message':message});
+ return;
+ }
+
+ // means we get the challenge from the server
+ var authch = message.split(':');
+ var salt = authch[0];
+ var dbname = authch[1];
+ var pwhash = __sha512(__sha512(this.options.password) + salt)
+ var response = 'LIT:' + this.options.user + ':{SHA512}' +
pwhash + ':' +
+ this.options.language + ':' + this.options.dbname + ':';
+ this.sendMessage(response);
+ return;
+ }
+
+ var response = {};
+
+ /* error message */
+ if (message.charAt(0) == '!') {
+ response.success = false;
+ response.message = message.substring(1,message.length-1);
+ }
+
+ /* query result */
+ if (message.charAt(0) == '&') {
+ response = _parseresponse(message);
+ response.success = true;
+ response.message = 'ok';
+ }
+
+ if (this.read_callback != undefined) {
+ this.read_callback(response);
+ this.read_callback = undefined;
+ }
+ this.nextOp();
+}
+
+
+MonetDBConnection.prototype.nextOp = function() {
+ if (this.queryqueue.length < 1) {
+ return;
+ }
+ var op = this.queryqueue.shift();
+ this.sendMessage(op.message);
+ this.read_callback = op.callback;
+}
+
+MonetDBConnection.prototype.handleInput = function(data) {
+ /* we need to read a header obviously */
+ if (this.read_leftover == 0) {
+ var hdr = data.readUInt16LE(0);
+ this.read_leftover = (hdr >> 1);
+ this.read_final = (hdr & 1) == 1;
+ data = data.slice(2);
+ }
+ if (this.options.debug)
+ console.log('reading ' + this.read_leftover + ' bytes, final='
+ this.read_final);
+
+ /* what is in the buffer is not necessary the entire block */
+ var read_cnt = Math.min(data.length, this.read_leftover);
+ this.read_str = this.read_str + data.toString('utf8', 0, read_cnt);
+ this.read_leftover -= read_cnt;
+
+ /* if there is something left to read, we will be called again */
+ if (this.read_leftover > 0) {
+ return;
+ }
+
+ /* pass on reassembled messages */
+ if (this.read_leftover == 0 && this.read_final) {
+ this.handleMessage(this.read_str);
+ this.read_str = '';
+ }
+
+ /* also, the buffer might contain more blocks or parts thereof */
+ if (data.length > read_cnt) {
+ var leftover = new Buffer(data.length - read_cnt);
+ data.copy(leftover, 0, read_cnt, data.length);
+ this.handleInput(leftover);
+ }
+
+};
+
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list