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"