diff --git a/storage/postgres/110-postgres.html b/storage/postgres/110-postgres.html
new file mode 100644
index 00000000..6fae6ac1
--- /dev/null
+++ b/storage/postgres/110-postgres.html
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/storage/postgres/110-postgres.js b/storage/postgres/110-postgres.js
new file mode 100644
index 00000000..4e2d41ee
--- /dev/null
+++ b/storage/postgres/110-postgres.js
@@ -0,0 +1,123 @@
+/**
+ * Copyright 2013 Kris Daniels.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ **/
+
+var RED = require(process.env.NODE_RED_HOME+"/red/red");
+var pg=require('pg');
+var named=require('node-postgres-named');
+var querystring = require('querystring');
+
+RED.app.get('/postgresdb/:id',function(req,res) {
+ var credentials = RED.nodes.getCredentials(req.params.id);
+ if (credentials) {
+ res.send(JSON.stringify({user:credentials.user,hasPassword:(credentials.password&&credentials.password!="")}));
+ } else {
+ res.send(JSON.stringify({}));
+ }
+});
+
+RED.app.delete('/postgresdb/:id',function(req,res) {
+ RED.nodes.deleteCredentials(req.params.id);
+ res.send(200);
+});
+
+RED.app.post('/postgresdb/:id',function(req,res) {
+ var body = "";
+ req.on('data', function(chunk) {
+ body+=chunk;
+ });
+ req.on('end', function(){
+ var newCreds = querystring.parse(body);
+ var credentials = RED.nodes.getCredentials(req.params.id)||{};
+ if (newCreds.user == null || newCreds.user == "") {
+ delete credentials.user;
+ } else {
+ credentials.user = newCreds.user;
+ }
+ if (newCreds.password == "") {
+ delete credentials.password;
+ } else {
+ credentials.password = newCreds.password||credentials.password;
+ }
+ RED.nodes.addCredentials(req.params.id,credentials);
+ res.send(200);
+ });
+});
+
+
+function PostgresDatabaseNode(n) {
+ RED.nodes.createNode(this,n);
+ this.hostname = n.hostname;
+ this.port = n.port;
+ this.db = n.db;
+
+ var credentials = RED.nodes.getCredentials(n.id);
+ if (credentials) {
+ this.user = credentials.user;
+ this.password = credentials.password;
+ }
+}
+
+RED.nodes.registerType("postgresdb",PostgresDatabaseNode);
+
+function PostgresNode(n) {
+ RED.nodes.createNode(this,n);
+
+ this.topic = n.topic;
+ this.postgresdb = n.postgresdb;
+ this.postgresConfig = RED.nodes.getNode(this.postgresdb);
+ this.sqlquery = n.sqlquery;
+ this.output = n.output;
+
+ var node = this;
+
+ if(this.postgresConfig)
+ {
+
+ var conString = 'postgres://'+this.postgresConfig.user +':' + this.postgresConfig.password + '@' + this.postgresConfig.hostname + ':' + this.postgresConfig.port + '/' + this.postgresConfig.db;
+ node.clientdb = new pg.Client(conString);
+ named.patch(node.clientdb);
+
+ node.clientdb.connect(function(err){
+ if(err) { node.error(err); }
+ else {
+ node.on('input',
+ function(msg){
+ if(!msg.queryParameters) msg.queryParameters={};
+ node.clientdb.query(msg.payload,
+ msg.queryParameters,
+ function (err, results) {
+ if(err) { node.error(err); }
+ else {
+ if(node.output)
+ {
+ msg.payload = results.rows;
+ node.send(msg);
+ }
+ }
+ });
+ });
+ }
+ });
+ } else {
+ this.error("missing postgres configuration");
+ }
+
+ this.on("close", function() {
+ if(node.clientdb) node.clientdb.end();
+ });
+}
+
+RED.nodes.registerType("postgres",PostgresNode);
diff --git a/storage/postgres/icons/postgres.png b/storage/postgres/icons/postgres.png
new file mode 100644
index 00000000..e46c3169
Binary files /dev/null and b/storage/postgres/icons/postgres.png differ