CartoDB-SQL-API/app/models/psql.js

109 lines
3.3 KiB
JavaScript

var _ = require('underscore')
, Step = require('step')
, pg = require('pg');//.native; // disabled for now due to: https://github.com/brianc/node-postgres/issues/48
_.mixin(require('underscore.string'));
// PSQL
//
// A simple postgres wrapper with logic about username and database to connect
//
// * intended for use with pg_bouncer
// * defaults to connecting with a "READ ONLY" user to given DB if not passed a specific user_id
var PSQL = function(user_id, db, limit, offset){
var error_text = "Incorrect access parameters. If you are accessing via OAuth, please check your tokens are correct. For public users, please ensure your table is published."
if (!_.isString(user_id) && !_.isString(db)) throw new Error(error_text);
var me = {
public_user: "publicuser"
, user_id: user_id
, db: db
, limit: limit
, offset: offset
, client: null
};
me.username = function(){
var username = this.public_user;
if (_.isString(this.user_id))
username = _.template(global.settings.db_user, {user_id: this.user_id});
return username;
};
me.database = function(){
var database = db;
if (_.isString(this.user_id))
database = _.template(global.settings.db_base_name, {user_id: this.user_id});
return database;
};
// memoizes connection in object. move to proper pool.
me.connect = function(callback){
var that = this
var conString = "tcp://" + this.username() + "@" + global.settings.db_host + ":" + global.settings.db_port + "/" + this.database();
if (that.client) {
return callback(null, that.client);
} else {
var err = null;
var client = new pg.Client(conString);
client.connect();
that.client = client;
return callback(err, client);
}
};
me.query = function(sql, callback){
var that = this;
Step(
function(){
that.sanitize(sql, this);
},
function(err, clean){
if (err) throw err;
that.connect(this);
},
function(err, client){
if (err) return callback(err, null);
client.query(that.window_sql(sql), this);
},
function(err, res){
//if (err) console.log(err);
callback(err, res)
}
);
};
me.end = function(){
this.client.end();
};
// little hack for UI
me.window_sql = function(sql){
// only window select functions
if (_.isNumber(this.limit) && _.isNumber(this.offset) && /^\s*SELECT.*$/.test(sql.toUpperCase())){
return "SELECT * FROM (" + sql + ") AS cdbq_1 LIMIT " + this.limit + " OFFSET " + this.offset;
} else {
return sql;
}
};
// throw exception if system table detected
me.sanitize = function(sql, callback){
if (sql.match(/\s+pg_.+/)){
var error = new SyntaxError("system tables are forbidden");
error.http_status = 403;
throw error;
} else {
callback(null,true);
}
};
return me;
};
module.exports = PSQL;