From 3613ee1cf2ed74a36757a003200b268e7701fe76 Mon Sep 17 00:00:00 2001 From: Ryan Hafer Date: Mon, 3 Apr 2017 23:46:27 -0400 Subject: [PATCH 1/2] Update the mysql node to have a direct query input in the node configuration to free up the msg.topic. --- storage/mysql/68-mysql.html | 60 ++++++++++++-- storage/mysql/68-mysql.js | 77 ++++++++++++++---- storage/mysql/README.md | 35 +++++++-- storage/mysql/icons/sql.png | Bin 0 -> 1694 bytes storage/mysql/package.json | 16 ++++ test/storage/mysql/mysql_spec.js | 129 +++++++++++++++++++++++++++++++ 6 files changed, 287 insertions(+), 30 deletions(-) create mode 100644 storage/mysql/icons/sql.png create mode 100644 test/storage/mysql/mysql_spec.js diff --git a/storage/mysql/68-mysql.html b/storage/mysql/68-mysql.html index 85abf6b73..7bb068fc0 100644 --- a/storage/mysql/68-mysql.html +++ b/storage/mysql/68-mysql.html @@ -53,41 +53,85 @@ +
+ + + +
+
+ + +
+
+
+
diff --git a/storage/mysql/68-mysql.js b/storage/mysql/68-mysql.js index a3362fa50..ae29dee1d 100644 --- a/storage/mysql/68-mysql.js +++ b/storage/mysql/68-mysql.js @@ -102,6 +102,8 @@ module.exports = function(RED) { RED.nodes.createNode(this,n); this.mydb = n.mydb; this.mydbConfig = RED.nodes.getNode(this.mydb); + this.query = n.query; + this.parameterSource = n.parameterSource || 'payload'; if (this.mydbConfig) { this.mydbConfig.connect(); @@ -118,24 +120,67 @@ module.exports = function(RED) { node.on("input", function(msg) { if (node.mydbConfig.connected) { - if (typeof msg.topic === 'string') { - //console.log("query:",msg.topic); - var bind = Array.isArray(msg.payload) ? msg.payload : []; - node.mydbConfig.connection.query(msg.topic, bind, function(err, rows) { - if (err) { - node.error(err,msg); - node.status({fill:"red",shape:"ring",text:"Error"}); - } - else { - msg.payload = rows; - node.send(msg); - node.status({fill:"green",shape:"dot",text:"OK"}); - } - }); + node.status({fill:"green",shape:"dot",text:"connected"}); + + // Query to be executed + var query = node.query; + + // Array of input parameters + var parameters = []; + + if(query.length){ + // Search for all paramters in a query + var parametersUsed = node.query.match(/\{\{[A-z\.0-9]*?\}\}/g); + var parameterSourcePath = node.parameterSource.split('.'); + + var sourceObject = msg; + + // Defaults to top level + var parameterSourceKey = parameterSourcePath.shift(); + while(parameterSourceKey){ + sourceObject = sourceObject[parameterSourceKey]; + + parameterSourceKey = parameterSourcePath.shift(); + } + // Loop matched parameters in query + if(parametersUsed) { + for(var i=0; i < parametersUsed.length; i++){ + var parameter = parametersUsed[i]; + query = query.replace(parameter,'?'); + + // Clean out {{}} characters and create a dot deliminated array of keys to traverse. + var parameterPath = parameter.replace(/[^A-z\.0-9]/g,'') + .split('.'); + + // Default to key + var value = sourceObject; + var parameterPathKey = parameterPath.shift(); + while(parameterPathKey){ + value = value[parameterPathKey]; + parameterPathKey = parameterPath.shift(); + } + + // Add to our parameter array for query execution + parameters.push(value); + } + } } - else { - if (typeof msg.topic !== 'string') { node.error("msg.topic : the query is not defined as a string"); } + else if (typeof msg.topic === 'string') { + parameters = Array.isArray(msg.payload) ? msg.payload : []; + query = msg.topic; } + + node.mydbConfig.connection.query(query, parameters, function(err, rows) { + if (err) { + node.error(err,msg); + node.status({fill:"red",shape:"ring",text:"Error"}); + } + else { + msg.payload = rows; + node.send(msg); + node.status({fill:"green",shape:"dot",text:"OK"}); + } + }); } else { node.error("Database not connected",msg); diff --git a/storage/mysql/README.md b/storage/mysql/README.md index b3b769304..8e0ac9587 100644 --- a/storage/mysql/README.md +++ b/storage/mysql/README.md @@ -15,15 +15,38 @@ Usage Allows basic access to a MySQL database. -This node uses the query operation against the configured database. This does allow both INSERTS and DELETES. +This node uses the query operation against the configured database. This does allow both INSERTS and DELETES. -By it's very nature it allows SQL injection... so be careful out there... +Using legacy method where queries are set in `msg.topic` allows SQL injection... so be careful out there... -The `msg.topic` must hold the query for the database, and the result is returned in `msg.payload`. +###Direct Inserted Queries + +With SQL queries that are directly added on the node, variables are escaped. + +SQL queries can use mustache style variable insertion. If our `msg.payload` has a property `key`, we would write a query as following: + +``` + SELECT * + FROM table + WHERE column = {{key}}; +``` + +For more escaped input information, you can refer to the documentation for [mysqljs/mysql](https://github.com/mysqljs/mysql). + +###Results Typically the returned payload will be an array of the result rows. -If nothing is found for the key then null is returned. +If nothing is found for the key then null is returned, + +###Misc. + +The reconnect timeout in milliseconds can be changed by adding a line to settings.js + +`mysqlReconnectTime: 30000,` + +###Legacy + +`msg.topic` must hold the query for the database, and the result is returned in `msg.payload`. -The reconnect retry timeout in milliseconds can be changed by adding a line to settings.js -
mysqlReconnectTime: 30000,

+`msg.payload` can contain an array of values to bind to the topic. diff --git a/storage/mysql/icons/sql.png b/storage/mysql/icons/sql.png new file mode 100644 index 0000000000000000000000000000000000000000..6a3944a02b3ddeb284491fe0e81ca6ac939ac250 GIT binary patch literal 1694 zcmX|?2~^Tq7r_6ZskmEx9G6K6MI#l&1rygqN03}1+(yNeMM(t&60{sm{hG@B3Z}*~ zG)E~58)wS0Niwm82DR+damiz;S?;@InJo@Boq6Zn_uhT?zTds~op;VX9vL2Dq;IJY z0DuvZ5Jc9j0UM&HqnYKbtd{@)vSLwU1+ig!a5PRPDw)nnVW32rT#X$7eEdb+WLi2y z083${ve>@xc|{8x#-jVeV?4rK!nk-w8jFz2W9-Wfr_gfKXt&7R z3r`aWxHvRgC={ZEZYT~f6^-%o@sEVCn}r#$#e>KRc=e>yw3d}ruT=j2O%tJxEzeDA2PN76If zk<+PI?0A;nXtjlIrn~Atc&p0t@~Y~*!M%(}Ru`k{mhR*~IlIR~pV9*{L6%cZt+!Oh zBGaH8gWEfwi^!{#{<`)}|K&LQCNGyfvbfOV)LSDq&&}W<}O=u>)!1B$lK+ZseSPeS>IZ0C)gCZ>Moj9#Y9v7cvrUM5GP3pW|l??pz70? z3S$0bT&1@y<(g2y8S&# z*JS~<#*CXD(0zxKCjNBmp|O+`@SzCWH;TCW-Jm%9!8F8ORrSVlYJ9fx(+vNx$^Sjx z%N_e)6j^)CQ&)99gHszjI{pA@{w0J`2AipXUY~3Rwf(z(Lmwz?QOD2?3N;PT zTZ1%Np2%AHy|z}aXr79&ycHG2N9FfP<@H#>m+`S!wuzV@YKe5MJar*#EcO`bbKcBD zE%0Hn;Ho;IXoq51z3`FH39QeVAW9S)=-9|9eMjnn`aDOCrs65{}~Me)|5jM<1mUk;FFsCZd-e%4f2J*L|4rwwY^(W|&iiwoX}$VxZw}#!A+@VsQ>xc@#gx@8 z<=^;o?jF?pZE7IK&gkyjBQi$c?P%V|6Vvg|Bey9U%QOCLQNYmA>FKC#Hl%_(CBtX% zPw(zx1Nj}v4*f|>+*{AW!`o6p>#xUi0{yeD;x2&`TdT(|FUqIUG>85#a2SJFKlOO` zmHF{wK4zbP1s99zZR(Ua<@b;*#g%-t-a=1a;7oLnCt{mK5E@=g0n3~UOPGzPcp*cC zHX-YR60*OwgQ_)tWPR{l(mrjR1pyJPmH1x8jO#kuH^<4ioLSt6sNiMl4z3)4v|M`i z3%+1y{K);qIq%L;2eN5Q1^6{Q;YaUv;l+D5y#1ZbTkPjAU6GCQ{7r}sh~^TclA80) zOZ&P__+bq9a&^V(sRvY-2;2Uhugn+F%LtPN`i<=>!{5`Ho@XlVG*>s*2CDz}u%6U3 zY={O;D|Z#Xh&`!J84w>n9amCCbD9iXqHH$(@Ma=!7=0uEd-L;gj!8Gy1e_oCnZF Date: Mon, 3 Apr 2017 23:58:44 -0400 Subject: [PATCH 2/2] Update missing mysql dependency for CI tests. --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index a6cbec9e6..6e96db76a 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,8 @@ "poplib" : "^0.1.7", "mailparser" : "^0.6.1", "imap" : "^0.8.18", - "msgpack-js": "^0.3.0" + "msgpack-js": "^0.3.0", + "mysql": "^2.13.0" }, "engines": { "node": ">=0.10"