globalstorage
Version:
Global Storage is a Global Distributed Data Warehouse
446 lines (395 loc) • 13 kB
JavaScript
'use strict';
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
var _require = require('@metarhia/common'),
iter = _require.iter;
var _require2 = require('./pg.utils'),
escapeIdentifier = _require2.escapeIdentifier,
escapeKey = _require2.escapeKey,
generateQueryParams = _require2.generateQueryParams;
var allowedConditions = ['=', '!=', '<', '<=', '>', '>=', 'LIKE'];
var supportedOps = {
select: Set,
innerJoin: Array,
selectDistinct: null,
count: Array,
avg: Array,
min: Array,
max: Array,
sum: Array,
where: Array,
groupBy: Set,
orderBy: Set,
from: Set,
limit: null,
offset: null
};
var functionHandlers = {
count: function count(op) {
return "count(".concat(op.field, ")");
},
avg: function avg(op) {
return "avg(".concat(op.field, ")");
},
min: function min(op) {
return "min(".concat(op.field, ")");
},
max: function max(op) {
return "max(".concat(op.field, ")");
},
sum: function sum(op) {
return "sum(".concat(op.field, ")");
}
};
var checkType = function checkType(value, name, type) {
if (_typeof(value) !== type) {
throw new TypeError("Invalid '".concat(name, "' value (").concat(value, ") type, expected '").concat(type, "'"));
}
};
var makeParamValue = function makeParamValue(cond, value, params) {
if (cond === 'IN' || cond === 'NOT IN') {
var startIndex = params.length || 1;
params.push.apply(params, _toConsumableArray(value));
return '(' + generateQueryParams(value.length, startIndex) + ')';
} else if (cond && cond.endsWith('ANY')) {
params.push(value);
return "($".concat(params.length, ")");
} else {
params.push(value);
return "$".concat(params.length);
}
};
var SelectBuilder =
/*#__PURE__*/
function () {
function SelectBuilder() {
_classCallCheck(this, SelectBuilder);
this.operations = new Map();
var _arr = Object.keys(supportedOps);
for (var _i = 0; _i < _arr.length; _i++) {
var op = _arr[_i];
var _constructor = supportedOps[op];
this.operations.set(op, _constructor ? new _constructor() : null);
}
}
_createClass(SelectBuilder, [{
key: "from",
value: function from(tableName) {
this.operations.get('from').add(escapeIdentifier(tableName));
return this;
}
}, {
key: "select",
value: function select() {
var select = this.operations.get('select');
for (var _len = arguments.length, fields = new Array(_len), _key = 0; _key < _len; _key++) {
fields[_key] = arguments[_key];
}
iter(fields).map(escapeKey).forEach(function (f) {
return select.add(f);
});
return this;
}
}, {
key: "innerJoin",
value: function innerJoin(tableName, leftKey, rightKey) {
this.operations.get('innerJoin').push({
table: escapeIdentifier(tableName),
leftKey: escapeKey(leftKey),
rightKey: escapeKey(rightKey)
});
return this;
}
}, {
key: "distinct",
value: function distinct() {
this.operations.set('selectDistinct', true);
return this;
}
}, {
key: "where",
value: function where(key, cond, value) {
cond = cond.toUpperCase();
if (!allowedConditions.includes(cond)) {
throw new Error("The operator \"".concat(cond, "\" is not permitted"));
}
this.operations.get('where').push({
key: escapeKey(key),
value: value,
cond: cond
});
return this;
}
}, {
key: "whereNot",
value: function whereNot(key, cond, value) {
cond = cond.toUpperCase();
if (!allowedConditions.includes(cond)) {
throw new Error("The operator \"".concat(cond, "\" is not permitted"));
}
this.operations.get('where').push({
key: escapeKey(key),
value: value,
cond: cond,
mod: 'NOT'
});
return this;
}
}, {
key: "whereNull",
value: function whereNull(key) {
this.operations.get('where').push({
key: escapeKey(key),
cond: 'IS',
value: 'null'
});
return this;
}
}, {
key: "whereNotNull",
value: function whereNotNull(key) {
this.operations.get('where').push({
key: escapeKey(key),
cond: 'IS',
value: 'null',
mod: 'NOT'
});
return this;
}
}, {
key: "whereIn",
value: function whereIn(key, conds) {
this.operations.get('where').push({
key: escapeKey(key),
cond: 'IN',
value: conds
});
return this;
}
}, {
key: "whereNotIn",
value: function whereNotIn(key, conds) {
this.operations.get('where').push({
key: escapeKey(key),
cond: 'NOT IN',
value: conds
});
return this;
}
}, {
key: "whereAny",
value: function whereAny(key, value) {
this.operations.get('where').push({
key: escapeKey(key),
cond: '= ANY',
value: value
});
return this;
}
}, {
key: "orderBy",
value: function orderBy(field) {
var dir = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'ASC';
dir = dir.toUpperCase();
this.operations.get('orderBy').add({
field: escapeKey(field),
dir: dir
});
return this;
}
}, {
key: "groupBy",
value: function groupBy() {
var groupBy = this.operations.get('groupBy');
for (var _len2 = arguments.length, fields = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
fields[_key2] = arguments[_key2];
}
iter(fields).map(escapeKey).forEach(function (f) {
return groupBy.add(f);
});
return this;
}
}, {
key: "limit",
value: function limit(_limit) {
checkType(_limit, 'limit', 'number');
this.operations.set('limit', _limit);
return this;
}
}, {
key: "offset",
value: function offset(_offset) {
checkType(_offset, 'offset', 'number');
this.operations.set('offset', _offset);
return this;
}
}, {
key: "count",
value: function count() {
var field = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '*';
if (field !== '*') field = escapeKey(field);
this.operations.get('count').push({
field: field
});
return this;
}
}, {
key: "avg",
value: function avg(field) {
this.operations.get('avg').push({
field: escapeKey(field)
});
return this;
}
}, {
key: "min",
value: function min(field) {
this.operations.get('min').push({
field: escapeKey(field)
});
return this;
}
}, {
key: "max",
value: function max(field) {
this.operations.get('max').push({
field: escapeKey(field)
});
return this;
}
}, {
key: "sum",
value: function sum(field) {
this.operations.get('sum').push({
field: escapeKey(field)
});
return this;
}
}, {
key: "processSelect",
value: function processSelect(query, clauses) {
if (clauses.size > 0) {
return query + ' ' + iter(clauses).reduce(function (acc, id) {
return acc + ', ' + id;
});
}
return query + ' *';
}
}, {
key: "processOperations",
value: function processOperations(query, operations, functionHandlers) {
var _arr2 = Object.keys(functionHandlers);
var _loop = function _loop() {
var fn = _arr2[_i2];
var ops = operations.get(fn);
if (ops.length === 0) return "continue";else if (query.endsWith(' *')) query = query.slice(0, -2);else query += ',';
var handler = functionHandlers[fn];
query += ops // eslint-disable-next-line no-loop-func
.reduce(function (acc, op) {
return acc + handler(op, query) + ',';
}, ' ').slice(0, -1);
};
for (var _i2 = 0; _i2 < _arr2.length; _i2++) {
var _ret = _loop();
if (_ret === "continue") continue;
}
return query;
}
}, {
key: "processWhere",
value: function processWhere(query, clauses, params) {
// TODO(lundibundi): support braces
query += ' WHERE';
for (var i = 0; i < clauses.length; ++i) {
var clause = clauses[i];
if (i !== 0) {
if (clause.or) query += ' OR';else query += ' AND';
}
if (clause.mod) query += " ".concat(clause.mod);
query += " ".concat(clause.key, " ").concat(clause.cond, " ") + makeParamValue(clause.cond, clause.value, params);
}
return query;
}
}, {
key: "processOrder",
value: function processOrder(query, clauses) {
var it = iter(clauses);
var firstClause = it.next().value;
query += " ORDER BY ".concat(firstClause.field, " ").concat(firstClause.dir);
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = it[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var order = _step.value;
query += ", ".concat(order.field, " ").concat(order.dir);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return query;
}
}, {
key: "build",
value: function build() {
var params = [];
var query = 'SELECT';
if (this.operations.get('selectDistinct')) query += ' DISTINCT';
query = this.processSelect(query, this.operations.get('select'));
query = this.processOperations(query, this.operations, functionHandlers);
var tableNames = this.operations.get('from');
if (tableNames.size === 0) {
throw new Error('Cannot generate SQL, tableName is not defined');
}
query += ' FROM ' + iter(tableNames).join(', ');
this.operations.get('innerJoin').forEach(function (_ref) {
var table = _ref.table,
leftKey = _ref.leftKey,
rightKey = _ref.rightKey;
query += " INNER JOIN ".concat(table, " ON ").concat(leftKey, " = ").concat(rightKey);
});
var whereClauses = this.operations.get('where');
if (whereClauses.length > 0) {
query = this.processWhere(query, whereClauses, params);
}
var groupClauses = this.operations.get('groupBy');
if (groupClauses.size > 0) {
query += ' GROUP BY ' + iter(groupClauses).join(', ');
}
var orderClauses = this.operations.get('orderBy');
if (orderClauses.size > 0) {
query = this.processOrder(query, orderClauses);
}
var limit = this.operations.get('limit');
if (limit) {
query += " LIMIT ".concat(makeParamValue(null, limit, params));
}
var offset = this.operations.get('offset');
if (offset) {
query += " OFFSET ".concat(makeParamValue(null, offset, params));
}
return [query, params];
}
}]);
return SelectBuilder;
}();
module.exports = {
SelectBuilder: SelectBuilder
};