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
checkin-list@monetdb.org
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to