plywood
Version:
A query planner and executor
1,217 lines (1,210 loc) • 597 kB
JavaScript
'use strict';
var { __assign, __awaiter, __extends, __generator } = require('tslib');
var hasOwnProp = require('has-own-prop');
var toArray = require('stream-to-array');
var readableStream = require('readable-stream');
var Readable = readableStream.Readable;
var Writable = readableStream.Writable;
var Transform = readableStream.Transform;
var PassThrough = readableStream.PassThrough;
var immutableClass = require('immutable-class');
var generalArraysEqual = immutableClass.generalArraysEqual;
var generalEqual = immutableClass.generalEqual;
var generalLookupsEqual = immutableClass.generalLookupsEqual;
var isImmutableClass = immutableClass.isImmutableClass;
var immutableEqual = immutableClass.immutableEqual;
var immutableArraysEqual = immutableClass.immutableArraysEqual;
var immutableLookupsEqual = immutableClass.immutableLookupsEqual;
var SimpleArray = immutableClass.SimpleArray;
var NamedArray = immutableClass.NamedArray;
var Chronoshift = require('chronoshift');
var Timezone = Chronoshift.Timezone;
var Duration = Chronoshift.Duration;
var moment = Chronoshift.moment;
var isDate = Chronoshift.isDate;
var parseISODate = Chronoshift.parseISODate;
var druidQueryToolkit = require('druid-query-toolkit');
var SqlColumn = druidQueryToolkit.SqlColumn;
var SqlExpression = druidQueryToolkit.SqlExpression;
var SqlFunction = druidQueryToolkit.SqlFunction;
var SqlCase = druidQueryToolkit.SqlCase;
var SqlQuery = druidQueryToolkit.SqlQuery;
var Introspect = druidQueryToolkit.Introspect;
var QueryResult = druidQueryToolkit.QueryResult;
var dummyObject = {};
var version = exports.version = '0.35.5';
var verboseRequesterFactory = exports.verboseRequesterFactory = function(parameters) {
var requester = parameters.requester;
var myName = parameters.name || 'rq' + String(Math.random()).substr(2, 5);
if (parameters.preQuery) {
console.warn('verboseRequesterFactory option preQuery has been renamed to onQuery');
parameters.onQuery = parameters.preQuery;
}
var printLine = parameters.printLine ||
(function (line) {
console['log'](line);
});
var onQuery = parameters.onQuery ||
(function (param) {
printLine('vvvvvvvvvvvvvvvvvvvvvvvvvv');
var ctx = param.context ? " [context: ".concat(JSON.stringify(param.context), "]") : '';
printLine("Requester ".concat(param.name, " sending query ").concat(param.queryNumber, ":").concat(ctx));
printLine(JSON.stringify(param.query, null, 2));
printLine('^^^^^^^^^^^^^^^^^^^^^^^^^^');
});
var onSuccess = parameters.onSuccess ||
(function (param) {
printLine('vvvvvvvvvvvvvvvvvvvvvvvvvv');
printLine("Requester ".concat(param.name, " got result from query ").concat(param.queryNumber, ": (in ").concat(param.time, "ms)"));
printLine(JSON.stringify(param.data, null, 2));
printLine('^^^^^^^^^^^^^^^^^^^^^^^^^^');
});
var onError = parameters.onError ||
(function (param) {
printLine('vvvvvvvvvvvvvvvvvvvvvvvvvv');
printLine("Requester ".concat(param.name, " got error in query ").concat(param.queryNumber, ": ").concat(param.error.message, " (in ").concat(param.time, "ms)"));
printLine('^^^^^^^^^^^^^^^^^^^^^^^^^^');
});
var curQueryNumber = 0;
return function (request) {
curQueryNumber++;
var myQueryNumber = curQueryNumber;
onQuery({
name: myName,
queryNumber: myQueryNumber,
query: request.query,
context: request.context,
});
var startTime = Date.now();
var stream = requester(request);
var errorSeen = false;
stream.on('error', function (error) {
errorSeen = true;
onError({
name: myName,
queryNumber: myQueryNumber,
query: request.query,
context: request.context,
time: Date.now() - startTime,
error: error,
});
});
var data = [];
stream.on('data', function (datum) {
data.push(JSON.parse(JSON.stringify(datum)));
});
stream.on('end', function () {
if (errorSeen)
return;
onSuccess({
name: myName,
queryNumber: myQueryNumber,
query: request.query,
context: request.context,
time: Date.now() - startTime,
data: data,
});
});
return stream;
};
}
var retryRequesterFactory = exports.retryRequesterFactory = function(parameters) {
var requester = parameters.requester;
var delay = parameters.delay || 500;
var retry = parameters.retry || 3;
var retryOnTimeout = Boolean(parameters.retryOnTimeout);
if (typeof delay !== 'number')
throw new TypeError('delay should be a number');
if (typeof retry !== 'number')
throw new TypeError('retry should be a number');
return function (request) {
var tries = 0;
var ended = false;
var output = new PassThrough({ objectMode: true });
function tryRequest() {
tries++;
var seenData = false;
var errored = false;
var rs = requester(request);
rs.on('error', function (e) {
errored = true;
if (seenData || tries > retry || (e.message === 'timeout' && !retryOnTimeout)) {
rs.unpipe(output);
output.emit('error', e);
ended = true;
output.end();
}
else {
setTimeout(tryRequest, delay);
}
});
rs.on('meta', function (m) {
output.emit('meta', m);
});
rs.on('data', function (_d) {
seenData = true;
});
rs.on('end', function () {
if (ended || errored)
return;
output.end();
});
rs.pipe(output, { end: false });
}
tryRequest();
return output;
};
}
var concurrentLimitRequesterFactory = exports.concurrentLimitRequesterFactory = function(parameters) {
var requester = parameters.requester;
var concurrentLimit = parameters.concurrentLimit || 5;
if (typeof concurrentLimit !== 'number')
throw new TypeError('concurrentLimit should be a number');
var requestQueue = [];
var outstandingRequests = 0;
function requestFinished() {
outstandingRequests--;
if (!(requestQueue.length && outstandingRequests < concurrentLimit))
return;
var queueItem = requestQueue.shift();
outstandingRequests++;
var stream = requester(queueItem.request);
stream.on('error', requestFinished);
stream.on('end', requestFinished);
pipeWithError(stream, queueItem.stream);
}
return function (request) {
if (outstandingRequests < concurrentLimit) {
outstandingRequests++;
var stream = requester(request);
stream.on('error', requestFinished);
stream.on('end', requestFinished);
return stream;
}
else {
var stream = new PassThrough({ objectMode: true });
requestQueue.push({
request: request,
stream: stream,
});
return stream;
}
};
}
var promiseWhile = exports.promiseWhile = function(condition, action) {
var loop = function () {
if (!condition())
return Promise.resolve(null);
return Promise.resolve(action()).then(loop);
};
return Promise.resolve(null).then(loop);
}
var ReadableError = (function (_super) {
__extends(ReadableError, _super);
function ReadableError(message, options) {
if (options === void 0) { options = {}; }
var _this = _super.call(this, options) || this;
var err = typeof message === 'string' ? new Error(message) : message;
setTimeout(function () {
_this.emit('error', err);
}, 1);
return _this;
}
ReadableError.prototype._read = function () { };
return ReadableError;
}(Readable));
exports.ReadableError = ReadableError;
var StreamConcat = (function (_super) {
__extends(StreamConcat, _super);
function StreamConcat(options) {
var _this = _super.call(this, options) || this;
_this.next = options.next;
_this.currentStream = null;
_this.streamIndex = 0;
_this._nextStream();
return _this;
}
StreamConcat.prototype._nextStream = function () {
var _this = this;
this.currentStream = null;
this.currentStream = this.next();
if (this.currentStream == null) {
this.push(null);
}
else {
this.currentStream.pipe(this, { end: false });
this.currentStream.on('error', function (e) { return _this.emit('error', e); });
this.currentStream.on('end', this._nextStream.bind(this));
}
};
return StreamConcat;
}(PassThrough));
exports.StreamConcat = StreamConcat;
var repeat = exports.repeat = function(str, times) {
return new Array(times + 1).join(str);
}
var indentBy = exports.indentBy = function(str, indent) {
var spaces = repeat(' ', indent);
return str
.split('\n')
.map(function (x) { return spaces + x; })
.join('\n');
}
var dictEqual = exports.dictEqual = function(dictA, dictB) {
if (dictA === dictB)
return true;
if (!dictA !== !dictB)
return false;
var keys = Object.keys(dictA);
if (keys.length !== Object.keys(dictB).length)
return false;
for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
var key = keys_1[_i];
if (dictA[key] !== dictB[key])
return false;
}
return true;
}
var shallowCopy = exports.shallowCopy = function(thing) {
var newThing = {};
for (var k in thing) {
if (hasOwnProp(thing, k))
newThing[k] = thing[k];
}
return newThing;
}
var deduplicateSort = exports.deduplicateSort = function(a) {
a = a.sort();
var newA = [];
var last = null;
for (var _i = 0, a_1 = a; _i < a_1.length; _i++) {
var v = a_1[_i];
if (v !== last)
newA.push(v);
last = v;
}
return newA;
}
var mapLookup = exports.mapLookup = function(thing, fn) {
var newThing = Object.create(null);
for (var k in thing) {
if (hasOwnProp(thing, k))
newThing[k] = fn(thing[k]);
}
return newThing;
}
var emptyLookup = exports.emptyLookup = function(lookup) {
for (var k in lookup) {
if (hasOwnProp(lookup, k))
return false;
}
return true;
}
var nonEmptyLookup = exports.nonEmptyLookup = function(lookup) {
return !emptyLookup(lookup);
}
var clip = exports.clip = function(x) {
var rx = Math.round(x);
return Math.abs(x - rx) < 1e-5 ? rx : x;
}
var safeAdd = exports.safeAdd = function(num, delta) {
var stringDelta = String(delta);
var dotIndex = stringDelta.indexOf('.');
if (dotIndex === -1 || stringDelta.length === 18) {
return num + delta;
}
else {
var scale = Math.pow(10, stringDelta.length - dotIndex - 1);
return (num * scale + delta * scale) / scale;
}
}
var safeRange = exports.safeRange = function(num, delta) {
var stringDelta = String(delta);
var dotIndex = stringDelta.indexOf('.');
if (dotIndex === -1 || stringDelta.length === 18) {
return {
start: num,
end: num + delta,
};
}
else {
var scale = Math.pow(10, stringDelta.length - dotIndex - 1);
num = clip(num * scale) / scale;
return {
start: num,
end: (num * scale + delta * scale) / scale,
};
}
}
var continuousFloorExpression = exports.continuousFloorExpression = function(variable, floorFn, size, offset) {
var expr = variable;
if (offset !== 0) {
expr = expr + ' - ' + offset;
}
if (offset !== 0 && size !== 1) {
expr = '(' + expr + ')';
}
if (size !== 1) {
expr = expr + ' / ' + size;
}
expr = floorFn + '(' + expr + ')';
if (size !== 1) {
expr = expr + ' * ' + size;
}
if (offset !== 0) {
expr = expr + ' + ' + offset;
}
return expr;
}
var ExtendableError = (function (_super) {
__extends(ExtendableError, _super);
function ExtendableError(message) {
var _this = _super.call(this, message) || this;
_this.name = _this.constructor.name;
_this.message = message;
if (typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(_this, _this.constructor);
}
else {
_this.stack = new Error(message).stack;
}
return _this;
}
return ExtendableError;
}(Error));
exports.ExtendableError = ExtendableError;
var pluralIfNeeded = exports.pluralIfNeeded = function(n, thing) {
return "".concat(n, " ").concat(thing).concat(n === 1 ? '' : 's');
}
var pipeWithError = exports.pipeWithError = function(src, dest) {
src.pipe(dest);
src.on('error', function (e) { return dest.emit('error', e); });
return dest;
}
var handleNullCheckIfNeeded = exports.handleNullCheckIfNeeded = function(xs, nullCheck, andOr, fn) {
if (!xs.length) {
return andOr === 'AND' ? 'FALSE' : 'TRUE';
}
var withoutNull = xs.filter(function (x) { return x != null; });
if (withoutNull.length === xs.length) {
return fn(xs);
}
else if (withoutNull.length === 0) {
return nullCheck;
}
else {
return "(".concat(nullCheck, " ").concat(andOr, " ").concat(fn(withoutNull), ")");
}
}
var SQLDialect = (function () {
function SQLDialect() {
this.escapedTableName = null;
}
SQLDialect.prototype.setTable = function (name) {
if (name) {
this.escapedTableName = name.length === 1 ? name : this.escapeName(name);
}
else {
this.escapedTableName = null;
}
};
SQLDialect.prototype.nullConstant = function () {
return 'NULL';
};
SQLDialect.prototype.emptyGroupBy = function () {
return "GROUP BY ''";
};
SQLDialect.prototype.escapeName = function (name) {
name = name.replace(/"/g, '""');
return '"' + name + '"';
};
SQLDialect.prototype.maybeNamespacedName = function (name) {
var escapedName = this.escapeName(name);
if (this.escapedTableName) {
return this.escapedTableName + '.' + escapedName;
}
else {
return escapedName;
}
};
SQLDialect.prototype.escapeLiteral = function (name) {
if (name === null)
return this.nullConstant();
name = name.replace(/'/g, "''");
return "'" + name + "'";
};
SQLDialect.prototype.booleanToSQL = function (bool) {
return ('' + bool).toUpperCase();
};
SQLDialect.prototype.ipToSQL = function (ip) {
return ip.toString();
};
SQLDialect.prototype.floatDivision = function (numerator, denominator) {
return "(".concat(numerator, "/").concat(denominator, ")");
};
SQLDialect.prototype.numberOrTimeToSQL = function (x) {
if (x === null)
return this.nullConstant();
if (x.toISOString) {
return this.timeToSQL(x);
}
else {
return this.numberToSQL(x);
}
};
SQLDialect.prototype.numberToSQL = function (num) {
if (num === null)
return this.nullConstant();
return '' + num;
};
SQLDialect.prototype.dateToSQLDateString = function (date) {
return date
.toISOString()
.replace('T', ' ')
.replace('Z', '')
.replace(/\.000$/, '')
.replace(/ 00:00:00$/, '');
};
SQLDialect.prototype.aggregateFilterIfNeeded = function (inputSQL, expressionSQL, elseSQL) {
if (elseSQL === void 0) { elseSQL = null; }
var whereIndex = inputSQL.indexOf(' WHERE ');
if (whereIndex === -1)
return expressionSQL;
var filterSQL = inputSQL.substr(whereIndex + 7);
return this.ifThenElseExpression(filterSQL, expressionSQL, elseSQL);
};
SQLDialect.prototype.concatExpression = function (_a, _b) {
throw new Error('must implement');
};
SQLDialect.prototype.containsExpression = function (_a, _b, _insensitive) {
throw new Error('must implement');
};
SQLDialect.prototype.mvContainsExpression = function (_a, _b) {
throw new Error('must implement');
};
SQLDialect.prototype.mvFilterOnlyExpression = function (_a, _b) {
throw new Error('must implement');
};
SQLDialect.prototype.mvOverlapExpression = function (_a, _b) {
throw new Error('must implement');
};
SQLDialect.prototype.substrExpression = function (a, position, length) {
return "SUBSTR(".concat(a, ",").concat(position + 1, ",").concat(length, ")");
};
SQLDialect.prototype.coalesceExpression = function (a, b) {
return "COALESCE(".concat(a, ", ").concat(b, ")");
};
SQLDialect.prototype.countDistinctExpression = function (a, _parameterAttributeName) {
return "COUNT(DISTINCT ".concat(a, ")");
};
SQLDialect.prototype.ifThenElseExpression = function (a, b, c) {
var elsePart = typeof c === 'string' ? " ELSE ".concat(c) : '';
return "CASE WHEN ".concat(a, " THEN ").concat(b).concat(elsePart, " END");
};
SQLDialect.prototype.filterAggregatorExpression = function (aggregate, whereFilter) {
var whereIndex = whereFilter.indexOf('WHERE');
return "".concat(aggregate).concat(whereIndex !== -1 ? "FILTER (".concat(whereFilter.substr(whereIndex), ")") : '');
};
SQLDialect.prototype.isNotDistinctFromExpression = function (a, b) {
var nullConst = this.nullConstant();
if (a === nullConst)
return "".concat(b, " IS ").concat(nullConst);
if (b === nullConst)
return "".concat(a, " IS ").concat(nullConst);
return "(".concat(a, " IS NOT DISTINCT FROM ").concat(b, ")");
};
SQLDialect.prototype.regexpExpression = function (expression, regexp) {
return "(".concat(expression, " REGEXP ").concat(this.escapeLiteral(regexp), ")");
};
SQLDialect.prototype.inExpression = function (operand, start, end, bounds) {
if (start === end && bounds === '[]')
return "".concat(operand, "=").concat(start);
var startSQL = null;
if (start !== this.nullConstant()) {
startSQL = start + (bounds[0] === '[' ? '<=' : '<') + operand;
}
var endSQL = null;
if (end !== this.nullConstant()) {
endSQL = operand + (bounds[1] === ']' ? '<=' : '<') + end;
}
if (startSQL) {
return endSQL ? "(".concat(startSQL, " AND ").concat(endSQL, ")") : startSQL;
}
else {
return endSQL ? endSQL : 'TRUE';
}
};
SQLDialect.prototype.lengthExpression = function (a) {
return "CHAR_LENGTH(".concat(a, ")");
};
SQLDialect.prototype.quantileExpression = function (_str, _quantile, _parameterAttributeName) {
throw new Error('dialect does not implement quantile');
};
SQLDialect.prototype.logExpression = function (base, operand) {
if (base === String(Math.E))
return "LN(".concat(operand, ")");
return "LOG(".concat(base, ",").concat(operand, ")");
};
SQLDialect.prototype.lookupExpression = function (_base, _lookup) {
throw new Error('can not express a lookup as a function');
};
SQLDialect.prototype.ipMatchExpression = function (_colName, _searchString, _ipSearchType) {
throw new Error('must implement');
};
SQLDialect.prototype.ipSearchExpression = function (_colName, _searchString, _ipSearchType) {
throw new Error('must implement');
};
SQLDialect.prototype.ipStringifyExpression = function (_operand) {
throw new Error('must implement');
};
return SQLDialect;
}());
exports.SQLDialect = SQLDialect;
var MySQLDialect = (function (_super) {
__extends(MySQLDialect, _super);
function MySQLDialect() {
return _super.call(this) || this;
}
MySQLDialect.prototype.escapeName = function (name) {
name = name.replace(/`/g, '``');
return '`' + name + '`';
};
MySQLDialect.prototype.escapeLiteral = function (name) {
if (name === null)
return this.nullConstant();
return JSON.stringify(name);
};
MySQLDialect.prototype.timeToSQL = function (date) {
if (!date)
return this.nullConstant();
return "TIMESTAMP('".concat(this.dateToSQLDateString(date), "')");
};
MySQLDialect.prototype.stringArrayToSQL = function (_value) {
throw new Error('must implement');
};
MySQLDialect.prototype.concatExpression = function (a, b) {
return "CONCAT(".concat(a, ",").concat(b, ")");
};
MySQLDialect.prototype.containsExpression = function (a, b, insensitive) {
if (insensitive) {
a = "LOWER(".concat(a, ")");
b = "LOWER(".concat(b, ")");
}
return "LOCATE(".concat(b, ",").concat(a, ")>0");
};
MySQLDialect.prototype.isNotDistinctFromExpression = function (a, b) {
return "(".concat(a, "<=>").concat(b, ")");
};
MySQLDialect.prototype.castExpression = function (inputType, operand, targetType) {
var castFunction = MySQLDialect.CAST_TO_FUNCTION[targetType][inputType];
if (!castFunction) {
throw new Error("unsupported cast from ".concat(inputType, " to ").concat(targetType, " in MySQL dialect"));
}
return castFunction.replace(/\$\$/g, operand);
};
MySQLDialect.prototype.utcToWalltime = function (operand, timezone) {
if (timezone.isUTC())
return operand;
return "CONVERT_TZ(".concat(operand, ",'+0:00','").concat(timezone, "')");
};
MySQLDialect.prototype.walltimeToUTC = function (operand, timezone) {
if (timezone.isUTC())
return operand;
return "CONVERT_TZ(".concat(operand, ",'").concat(timezone, "','+0:00')");
};
MySQLDialect.prototype.timeFloorExpression = function (operand, duration, timezone) {
var bucketFormat = MySQLDialect.TIME_BUCKETING[duration.toString()];
if (!bucketFormat)
throw new Error("unsupported duration '".concat(duration, "'"));
return this.walltimeToUTC("DATE_FORMAT(".concat(this.utcToWalltime(operand, timezone), ",'").concat(bucketFormat, "')"), timezone);
};
MySQLDialect.prototype.timeBucketExpression = function (operand, duration, timezone) {
return this.timeFloorExpression(operand, duration, timezone);
};
MySQLDialect.prototype.timePartExpression = function (operand, part, timezone) {
var timePartFunction = MySQLDialect.TIME_PART_TO_FUNCTION[part];
if (!timePartFunction)
throw new Error("unsupported part ".concat(part, " in MySQL dialect"));
return timePartFunction.replace(/\$\$/g, this.utcToWalltime(operand, timezone));
};
MySQLDialect.prototype.timeShiftExpression = function (operand, duration, step, _timezone) {
if (step === 0)
return operand;
var sqlFn = step > 0 ? 'DATE_ADD(' : 'DATE_SUB(';
var spans = duration.multiply(Math.abs(step)).valueOf();
if (spans.week) {
return sqlFn + operand + ', INTERVAL ' + String(spans.week) + ' WEEK)';
}
if (spans.year || spans.month) {
var expr = String(spans.year || 0) + '-' + String(spans.month || 0);
operand = sqlFn + operand + ", INTERVAL '" + expr + "' YEAR_MONTH)";
}
if (spans.day || spans.hour || spans.minute || spans.second) {
var expr = String(spans.day || 0) +
' ' +
[spans.hour || 0, spans.minute || 0, spans.second || 0].join(':');
operand = sqlFn + operand + ", INTERVAL '" + expr + "' DAY_SECOND)";
}
return operand;
};
MySQLDialect.prototype.extractExpression = function (_operand, _regexp) {
throw new Error('MySQL must implement extractExpression (https://github.com/mysqludf/lib_mysqludf_preg)');
};
MySQLDialect.prototype.indexOfExpression = function (str, substr) {
return "LOCATE(".concat(substr, ", ").concat(str, ") - 1");
};
MySQLDialect.TIME_BUCKETING = {
PT1S: '%Y-%m-%d %H:%i:%SZ',
PT1M: '%Y-%m-%d %H:%i:00Z',
PT1H: '%Y-%m-%d %H:00:00Z',
P1D: '%Y-%m-%d 00:00:00Z',
P1M: '%Y-%m-01 00:00:00Z',
P1Y: '%Y-01-01 00:00:00Z',
};
MySQLDialect.TIME_PART_TO_FUNCTION = {
SECOND_OF_MINUTE: 'SECOND($$)',
SECOND_OF_HOUR: '(MINUTE($$)*60+SECOND($$))',
SECOND_OF_DAY: '((HOUR($$)*60+MINUTE($$))*60+SECOND($$))',
SECOND_OF_WEEK: '(((WEEKDAY($$)*24)+HOUR($$)*60+MINUTE($$))*60+SECOND($$))',
SECOND_OF_MONTH: '((((DAYOFMONTH($$)-1)*24)+HOUR($$)*60+MINUTE($$))*60+SECOND($$))',
SECOND_OF_YEAR: '((((DAYOFYEAR($$)-1)*24)+HOUR($$)*60+MINUTE($$))*60+SECOND($$))',
MINUTE_OF_HOUR: 'MINUTE($$)',
MINUTE_OF_DAY: 'HOUR($$)*60+MINUTE($$)',
MINUTE_OF_WEEK: '(WEEKDAY($$)*24)+HOUR($$)*60+MINUTE($$)',
MINUTE_OF_MONTH: '((DAYOFMONTH($$)-1)*24)+HOUR($$)*60+MINUTE($$)',
MINUTE_OF_YEAR: '((DAYOFYEAR($$)-1)*24)+HOUR($$)*60+MINUTE($$)',
HOUR_OF_DAY: 'HOUR($$)',
HOUR_OF_WEEK: '(WEEKDAY($$)*24+HOUR($$))',
HOUR_OF_MONTH: '((DAYOFMONTH($$)-1)*24+HOUR($$))',
HOUR_OF_YEAR: '((DAYOFYEAR($$)-1)*24+HOUR($$))',
DAY_OF_WEEK: '(WEEKDAY($$)+1)',
DAY_OF_MONTH: 'DAYOFMONTH($$)',
DAY_OF_YEAR: 'DAYOFYEAR($$)',
WEEK_OF_YEAR: 'WEEK($$)',
MONTH_OF_YEAR: 'MONTH($$)',
YEAR: 'YEAR($$)',
};
MySQLDialect.CAST_TO_FUNCTION = {
TIME: {
NUMBER: 'FROM_UNIXTIME($$ / 1000)',
},
NUMBER: {
TIME: 'UNIX_TIMESTAMP($$) * 1000',
STRING: 'CAST($$ AS SIGNED)',
},
STRING: {
NUMBER: 'CAST($$ AS CHAR)',
},
};
return MySQLDialect;
}(SQLDialect));
exports.MySQLDialect = MySQLDialect;
var PostgresDialect = (function (_super) {
__extends(PostgresDialect, _super);
function PostgresDialect() {
return _super.call(this) || this;
}
PostgresDialect.prototype.emptyGroupBy = function () {
return "GROUP BY ''=''";
};
PostgresDialect.prototype.timeToSQL = function (date) {
if (!date)
return this.nullConstant();
return "TIMESTAMP '".concat(this.dateToSQLDateString(date), "'");
};
PostgresDialect.prototype.stringArrayToSQL = function (_value) {
throw new Error('must implement');
};
PostgresDialect.prototype.concatExpression = function (a, b) {
return "(".concat(a, "||").concat(b, ")");
};
PostgresDialect.prototype.containsExpression = function (a, b, insensitive) {
if (insensitive) {
a = "LOWER(".concat(a, ")");
b = "LOWER(".concat(b, ")");
}
return "POSITION(".concat(b, " IN ").concat(a, ")>0");
};
PostgresDialect.prototype.regexpExpression = function (expression, regexp) {
return "(".concat(expression, " ~ '").concat(regexp, "')");
};
PostgresDialect.prototype.castExpression = function (inputType, operand, targetType) {
var castFunction = PostgresDialect.CAST_TO_FUNCTION[targetType][inputType];
if (!castFunction) {
throw new Error("unsupported cast from ".concat(inputType, " to ").concat(targetType, " in Postgres dialect"));
}
return castFunction.replace(/\$\$/g, operand);
};
PostgresDialect.prototype.utcToWalltime = function (operand, timezone) {
if (timezone.isUTC())
return operand;
return "(".concat(operand, " AT TIME ZONE 'UTC' AT TIME ZONE '").concat(timezone, "')");
};
PostgresDialect.prototype.walltimeToUTC = function (operand, timezone) {
if (timezone.isUTC())
return operand;
return "(".concat(operand, " AT TIME ZONE '").concat(timezone, "' AT TIME ZONE 'UTC')");
};
PostgresDialect.prototype.timeFloorExpression = function (operand, duration, timezone) {
var bucketFormat = PostgresDialect.TIME_BUCKETING[duration.toString()];
if (!bucketFormat)
throw new Error("unsupported duration '".concat(duration, "'"));
return this.walltimeToUTC("DATE_TRUNC('".concat(bucketFormat, "',").concat(this.utcToWalltime(operand, timezone), ")"), timezone);
};
PostgresDialect.prototype.timeBucketExpression = function (operand, duration, timezone) {
return this.timeFloorExpression(operand, duration, timezone);
};
PostgresDialect.prototype.timePartExpression = function (operand, part, timezone) {
var timePartFunction = PostgresDialect.TIME_PART_TO_FUNCTION[part];
if (!timePartFunction)
throw new Error("unsupported part ".concat(part, " in Postgres dialect"));
return timePartFunction.replace(/\$\$/g, this.utcToWalltime(operand, timezone));
};
PostgresDialect.prototype.timeShiftExpression = function (operand, duration, step, _timezone) {
if (step === 0)
return operand;
var sqlFn = step > 0 ? 'DATE_ADD(' : 'DATE_SUB(';
var spans = duration.multiply(Math.abs(step)).valueOf();
if (spans.week) {
return sqlFn + operand + ', INTERVAL ' + String(spans.week) + ' WEEK)';
}
if (spans.year || spans.month) {
var expr = String(spans.year || 0) + '-' + String(spans.month || 0);
operand = sqlFn + operand + ", INTERVAL '" + expr + "' YEAR_MONTH)";
}
if (spans.day || spans.hour || spans.minute || spans.second) {
var expr = String(spans.day || 0) +
' ' +
[spans.hour || 0, spans.minute || 0, spans.second || 0].join(':');
operand = sqlFn + operand + ", INTERVAL '" + expr + "' DAY_SECOND)";
}
return operand;
};
PostgresDialect.prototype.extractExpression = function (operand, regexp) {
return "(SELECT (REGEXP_MATCHES(".concat(operand, ", '").concat(regexp, "'))[1])");
};
PostgresDialect.prototype.indexOfExpression = function (str, substr) {
return "POSITION(".concat(substr, " IN ").concat(str, ") - 1");
};
PostgresDialect.TIME_BUCKETING = {
PT1S: 'second',
PT1M: 'minute',
PT1H: 'hour',
P1D: 'day',
P1W: 'week',
P1M: 'month',
P3M: 'quarter',
P1Y: 'year',
};
PostgresDialect.TIME_PART_TO_FUNCTION = {
SECOND_OF_MINUTE: "DATE_PART('second',$$)",
SECOND_OF_HOUR: "(DATE_PART('minute',$$)*60+DATE_PART('second',$$))",
SECOND_OF_DAY: "((DATE_PART('hour',$$)*60+DATE_PART('minute',$$))*60+DATE_PART('second',$$))",
SECOND_OF_WEEK: "((((CAST((DATE_PART('dow',$$)+6) AS int)%7)*24)+DATE_PART('hour',$$)*60+DATE_PART('minute',$$))*60+DATE_PART('second',$$))",
SECOND_OF_MONTH: "((((DATE_PART('day',$$)-1)*24)+DATE_PART('hour',$$)*60+DATE_PART('minute',$$))*60+DATE_PART('second',$$))",
SECOND_OF_YEAR: "((((DATE_PART('doy',$$)-1)*24)+DATE_PART('hour',$$)*60+DATE_PART('minute',$$))*60+DATE_PART('second',$$))",
MINUTE_OF_HOUR: "DATE_PART('minute',$$)",
MINUTE_OF_DAY: "DATE_PART('hour',$$)*60+DATE_PART('minute',$$)",
MINUTE_OF_WEEK: "((CAST((DATE_PART('dow',$$)+6) AS int)%7)*24)+DATE_PART('hour',$$)*60+DATE_PART('minute',$$)",
MINUTE_OF_MONTH: "((DATE_PART('day',$$)-1)*24)+DATE_PART('hour',$$)*60+DATE_PART('minute',$$)",
MINUTE_OF_YEAR: "((DATE_PART('doy',$$)-1)*24)+DATE_PART('hour',$$)*60+DATE_PART('minute',$$)",
HOUR_OF_DAY: "DATE_PART('hour',$$)",
HOUR_OF_WEEK: "((CAST((DATE_PART('dow',$$)+6) AS int)%7)*24+DATE_PART('hour',$$))",
HOUR_OF_MONTH: "((DATE_PART('day',$$)-1)*24+DATE_PART('hour',$$))",
HOUR_OF_YEAR: "((DATE_PART('doy',$$)-1)*24+DATE_PART('hour',$$))",
DAY_OF_WEEK: "(CAST((DATE_PART('dow',$$)+6) AS int)%7)+1",
DAY_OF_MONTH: "DATE_PART('day',$$)",
DAY_OF_YEAR: "DATE_PART('doy',$$)",
WEEK_OF_YEAR: "DATE_PART('week',$$)",
MONTH_OF_YEAR: "DATE_PART('month',$$)",
YEAR: "DATE_PART('year',$$)",
};
PostgresDialect.CAST_TO_FUNCTION = {
TIME: {
NUMBER: 'TO_TIMESTAMP($$::double precision / 1000)',
},
NUMBER: {
TIME: 'EXTRACT(EPOCH FROM $$) * 1000',
STRING: '$$::float',
},
STRING: {
NUMBER: '$$::text',
},
};
return PostgresDialect;
}(SQLDialect));
exports.PostgresDialect = PostgresDialect;
var DruidDialect = (function (_super) {
__extends(DruidDialect, _super);
function DruidDialect(options) {
if (options === void 0) { options = {}; }
var _this = _super.call(this) || this;
_this.attributes = options.attributes;
return _this;
}
DruidDialect.prototype.dateToSQLDateString = function (date) {
return date
.toISOString()
.replace('T', ' ')
.replace('Z', '')
.replace(/\.000$/, '');
};
DruidDialect.prototype.floatDivision = function (numerator, denominator) {
return "(".concat(numerator, "*1.0/").concat(denominator, ")");
};
DruidDialect.prototype.emptyGroupBy = function () {
return 'GROUP BY ()';
};
DruidDialect.prototype.timeToSQL = function (date) {
if (!date)
return this.nullConstant();
return "TIMESTAMP '".concat(this.dateToSQLDateString(date), "'");
};
DruidDialect.prototype.stringArrayToSQL = function (value) {
var _this = this;
var arr = value.map(function (v) { return _this.escapeLiteral(v); });
return "ARRAY[".concat(arr.join(','), "]");
};
DruidDialect.prototype.ipParse = function (value) {
return "IP_PARSE(".concat(value, ")");
};
DruidDialect.prototype.ipPrefixParse = function (value) {
return "IP_PREFIX_PARSE(".concat(value, ")");
};
DruidDialect.prototype.concatExpression = function (a, b) {
return "(".concat(a, "||").concat(b, ")");
};
DruidDialect.prototype.containsExpression = function (a, b, insensitive) {
return "".concat(insensitive ? 'ICONTAINS_STRING' : 'CONTAINS_STRING', "(CAST(").concat(a, " AS VARCHAR),").concat(b, ")");
};
DruidDialect.prototype.mvContainsExpression = function (a, b) {
return "MV_CONTAINS(".concat(a, ", ").concat(this.stringArrayToSQL(b), ")");
};
DruidDialect.prototype.mvFilterOnlyExpression = function (a, b) {
return "MV_FILTER_ONLY(".concat(a, ", ").concat(this.stringArrayToSQL(b), ")");
};
DruidDialect.prototype.mvOverlapExpression = function (a, b) {
return "MV_OVERLAP(".concat(a, ", ").concat(this.stringArrayToSQL(b), ")");
};
DruidDialect.prototype.substrExpression = function (a, position, length) {
return "SUBSTRING(".concat(a, ",").concat(position + 1, ",").concat(length, ")");
};
DruidDialect.prototype.countDistinctExpression = function (a, parameterAttributeName) {
var attribute = NamedArray.findByName(this.attributes || [], parameterAttributeName);
var nativeType = attribute ? attribute.nativeType : undefined;
switch (nativeType) {
case 'HLLSketch':
return "APPROX_COUNT_DISTINCT_DS_HLL(".concat(a, ")");
case 'thetaSketch':
return "APPROX_COUNT_DISTINCT_DS_THETA(".concat(a, ")");
case 'hyperUnique':
return "APPROX_COUNT_DISTINCT(".concat(a, ")");
default:
return "COUNT(DISTINCT ".concat(a, ")");
}
};
DruidDialect.prototype.isNotDistinctFromExpression = function (a, b) {
var nullConst = this.nullConstant();
if (a === nullConst)
return "".concat(b, " IS ").concat(nullConst);
if (b === nullConst)
return "".concat(a, " IS ").concat(nullConst);
return "(".concat(a, "=").concat(b, ")");
};
DruidDialect.prototype.castExpression = function (inputType, operand, targetType) {
if (targetType === 'SET/STRING')
targetType = 'STRING';
if (inputType === targetType)
return operand;
var castForInput = DruidDialect.CAST_TO_FUNCTION[targetType];
var castFunction = castForInput[inputType || '_'] || castForInput['_'];
if (!castFunction) {
throw new Error("unsupported cast from ".concat(inputType || 'unknown', " to ").concat(targetType, " in Druid dialect"));
}
return castFunction.replace(/\$\$/g, operand);
};
DruidDialect.prototype.operandAsTimestamp = function (operand) {
return operand.includes('__time') ? operand : "CAST(".concat(operand, " AS TIMESTAMP)");
};
DruidDialect.prototype.timeFloorExpression = function (operand, duration, timezone) {
return "TIME_FLOOR(".concat(this.operandAsTimestamp(operand), ", ").concat(this.escapeLiteral(duration.toString()), ", NULL, ").concat(this.escapeLiteral(timezone.toString()), ")");
};
DruidDialect.prototype.timeBucketExpression = function (operand, duration, timezone) {
return this.timeFloorExpression(operand, duration, timezone);
};
DruidDialect.prototype.timePartExpression = function (operand, part, timezone) {
var timePartFunction = DruidDialect.TIME_PART_TO_FUNCTION[part];
if (!timePartFunction)
throw new Error("unsupported part ".concat(part, " in Druid dialect"));
return timePartFunction
.replace(/\$\$/g, this.operandAsTimestamp(operand))
.replace(/##/g, this.escapeLiteral(timezone.toString()));
};
DruidDialect.prototype.timeShiftExpression = function (operand, duration, step, timezone) {
return "TIME_SHIFT(".concat(this.operandAsTimestamp(operand), ", ").concat(this.escapeLiteral(duration.toString()), ", ").concat(step, ", ").concat(this.escapeLiteral(timezone.toString()), ")");
};
DruidDialect.prototype.extractExpression = function (operand, regexp) {
return "REGEXP_EXTRACT(CAST(".concat(operand, " AS VARCHAR), ").concat(this.escapeLiteral(regexp), ", 1)");
};
DruidDialect.prototype.regexpExpression = function (expression, regexp) {
return "REGEXP_LIKE(CAST(".concat(expression, " AS VARCHAR), ").concat(this.escapeLiteral(regexp), ")");
};
DruidDialect.prototype.indexOfExpression = function (str, substr) {
return "POSITION(".concat(substr, " IN ").concat(str, ") - 1");
};
DruidDialect.prototype.quantileExpression = function (str, quantile, parameterAttributeName) {
var attribute = NamedArray.findByName(this.attributes || [], parameterAttributeName);
var nativeType = attribute ? attribute.nativeType : undefined;
switch (nativeType) {
case 'approximateHistogram':
return "APPROX_QUANTILE(".concat(str, ", ").concat(quantile, ")");
default:
return "APPROX_QUANTILE_DS(".concat(str, ", ").concat(quantile, ")");
}
};
DruidDialect.prototype.logExpression = function (base, operand) {
if (base === String(Math.E))
return "LN(".concat(operand, ")");
if (base === '10')
return "LOG10(".concat(operand, ")");
return "LN(".concat(operand, ")/LN(").concat(base, ")");
};
DruidDialect.prototype.lookupExpression = function (base, lookup) {
return "LOOKUP(".concat(base, ", ").concat(this.escapeLiteral(lookup), ")");
};
DruidDialect.prototype.ipMatchExpression = function (columnName, searchString, ipSearchType) {
return ipSearchType === 'ipPrefix'
? "IP_MATCH(".concat(this.escapeLiteral(searchString.toString()), ", ").concat(columnName, ")")
: "IP_MATCH(".concat(columnName, ", ").concat(this.escapeLiteral(searchString.toString()), ")");
};
DruidDialect.prototype.ipSearchExpression = function (columnName, searchString, ipSearchType) {
return ipSearchType === 'ipPrefix'
? "IP_SEARCH(".concat(this.escapeLiteral(searchString.toString()), ", ").concat(columnName, ")")
: "IP_SEARCH(".concat(columnName, ", ").concat(this.escapeLiteral(searchString.toString()), ")");
};
DruidDialect.prototype.ipStringifyExpression = function (operand) {
return "IP_STRINGIFY(".concat(operand, ")");
};
DruidDialect.TIME_PART_TO_FUNCTION = {
SECOND_OF_MINUTE: "TIME_EXTRACT($$,'SECOND',##)",
SECOND_OF_HOUR: "(TIME_EXTRACT($$,'MINUTE',##)*60+TIME_EXTRACT($$,'SECOND',##))",
SECOND_OF_DAY: "((TIME_EXTRACT($$,'HOUR',##)*60+TIME_EXTRACT($$,'MINUTE',##))*60+TIME_EXTRACT($$,'SECOND',##))",
SECOND_OF_WEEK: "(((MOD(CAST((TIME_EXTRACT($$,'DOW',##)+6) AS int),7)*24)+TIME_EXTRACT($$,'HOUR',##)*60+TIME_EXTRACT($$,'MINUTE',##))*60+TIME_EXTRACT($$,'SECOND',##))",
SECOND_OF_MONTH: "((((TIME_EXTRACT($$,'DAY',##)-1)*24)+TIME_EXTRACT($$,'HOUR',##)*60+TIME_EXTRACT($$,'MINUTE',##))*60+TIME_EXTRACT($$,'SECOND',##))",
SECOND_OF_YEAR: "((((TIME_EXTRACT($$,'DOY',##)-1)*24)+TIME_EXTRACT($$,'HOUR',##)*60+TIME_EXTRACT($$,'MINUTE',##))*60+TIME_EXTRACT($$,'SECOND',##))",
MINUTE_OF_HOUR: "TIME_EXTRACT($$,'MINUTE',##)",
MINUTE_OF_DAY: "TIME_EXTRACT($$,'HOUR',##)*60+TIME_EXTRACT($$,'MINUTE',##)",
MINUTE_OF_WEEK: "(MOD(CAST((TIME_EXTRACT($$,'DOW',##)+6) AS int),7)*24)+TIME_EXTRACT($$,'HOUR',##)*60+TIME_EXTRACT($$,'MINUTE',##)",
MINUTE_OF_MONTH: "((TIME_EXTRACT($$,'DAY',##)-1)*24)+TIME_EXTRACT($$,'HOUR',##)*60+TIME_EXTRACT($$,'MINUTE',##)",
MINUTE_OF_YEAR: "((TIME_EXTRACT($$,'DOY',##)-1)*24)+TIME_EXTRACT($$,'HOUR',##)*60+TIME_EXTRACT($$,'MINUTE',##)",
HOUR_OF_DAY: "TIME_EXTRACT($$,'HOUR',##)",
HOUR_OF_WEEK: "(MOD(CAST((TIME_EXTRACT($$,'DOW',##)+6) AS int),7)*24+TIME_EXTRACT($$,'HOUR',##))",
HOUR_OF_MONTH: "((TIME_EXTRACT($$,'DAY',##)-1)*24+TIME_EXTRACT($$,'HOUR',##))",
HOUR_OF_YEAR: "((TIME_EXTRACT($$,'DOY',##)-1)*24+TIME_EXTRACT($$,'HOUR',##))",
DAY_OF_WEEK: "MOD(CAST((TIME_EXTRACT($$,'DOW',##)+6) AS int),7)+1",
DAY_OF_MONTH: "TIME_EXTRACT($$,'DAY',##)",
DAY_OF_YEAR: "TIME_EXTRACT($$,'DOY',##)",
WEEK_OF_YEAR: "TIME_EXTRACT($$,'WEEK',##)",
MONTH_OF_YEAR: "TIME_EXTRACT($$,'MONTH',##)",
YEAR: "TIME_EXTRACT($$,'YEAR',##)",
};
DruidDialect.CAST_TO_FUNCTION = {
TIME: {
NUMBER: 'MILLIS_TO_TIMESTAMP(CAST($$ AS BIGINT))',
_: 'CAST($$ AS TIMESTAMP)',
},
NUMBER: {
TIME: 'CAST($$ AS BIGINT)',
STRING: 'CAST($$ AS DOUBLE)',
_: 'CAST($$ AS DOUBLE)',
},
STRING: {
NUMBER: 'CAST($$ AS VARCHAR)',
_: 'CAST($$ AS VARCHAR)',
},
BOOLEAN: {
NUMBER: '($$ = 1)',
STRING: "($$ = 'true')",
_: "(CAST($$ AS VARCHAR) IN ('1','true'))",
},
};
return DruidDialect;
}(SQLDialect));
exports.DruidDialect = DruidDialect;
var getValueType = exports.getValueType = function(value) {
var typeofValue = typeof value;
if (typeofValue === 'object') {
if (value === null) {
return 'NULL';
}
else if (isDate(value)) {
return 'TIME';
}
else if (hasOwnProp(value, 'ip') && Ip.isIp(value.ip)) {
return 'IP';
}
else if (hasOwnProp(value, 'start') && hasOwnProp(value, 'end')) {
if (isDate(value.start) || isDate(value.end))
return 'TIME_RANGE';
if (typeof value.start === 'number' || typeof value.end === 'number')
return 'NUMBER_RANGE';
if (typeof value.start === 'string' || typeof value.end === 'string')
return 'STRING_RANGE';
throw new Error('unrecognizable range');
}
else {
var ctrType = value.constructor.type;
if (!ctrType) {
if (value instanceof Expression) {
throw new Error("expression used as datum value ".concat(value));
}
else {
throw new Error("can not have an object without a type: ".concat(JSON.stringify(value)));
}
}
if (ctrType === 'SET')
ctrType += '/' + value.setType;
return ctrType;
}
}
else {
if (typeofValue !== 'boolean' && typeofValue !== 'number' && typeofValue !== 'string') {
throw new TypeError('unsupported JS type ' + typeofValue);
}
return typeofValue.toUpperCase();
}
}
var getFullType = exports.getFullType = function(value) {
var myType = getValueType(value);
return myType === 'DATASET' ? value.getFullType() : { type: myType };
}
var getFullTypeFromDatum = exports.getFullTypeFromDatum = function(datum) {
var datasetType = {};
for (var k in datum) {
if (!hasOwnProp(datum, k))
continue;
datasetType[k] = getFullType(datum[k]);
}
return {
type: 'DATASET',
datasetType: datasetType,
};
}
function timeFromJS(v) {
switch (typeof v) {
case 'string':
case 'number':
return new Date(v);
case 'object':
if (v.toISOString)
return v;
if (v === null)
return null;
if (v.value)
return new Date(v.value);
throw new Error("can not interpret ".concat(JSON.stringify(v), " as TIME"));
default:
throw new Error("can not interpret ".concat(v, " as TIME"));
}
}
var valueFromJS = exports.valueFromJS = function(v, typeOverride) {
if (typeOverride === void 0) { typeOverride = null; }
if (v == null) {
return null;
}
else if (Array.isArray(v)) {
if (v.length && typeof v[0] !== 'object') {
return Set.fromJS(v);
}
else {
return Dataset.fromJS(v);
}
}
else {
var typeofV = typeof v;
if (typeofV === 'object') {
switch (typeOverride || v.type) {
case 'NUMBER': {
var n = Number(v.value);
if (isNaN(n))
throw new Error("bad number value '".concat(v.value, "'"));
return n;
}
case 'NUMBER_RANGE':
return NumberRange.fromJS(v);
case 'STRING_RANGE':
return StringRange.fromJS(v);
case 'IP':
return Ip.fromJS(v);
case 'TIME':
return timeFromJS(v);
case 'TIME_RANGE':
return TimeRange.fromJS(v);
case 'SET':
return Set.fromJS(v);
case 'DATASET':
return Dataset.fromJS(v);
default:
if (String(typeOverride).indexOf('SET') === 0 || Array.isArray(v.elements)) {
return Set.fromJS(v);
}
if (v.toISOString) {
return v;
}
if (typeOverride) {
throw new Error("unknown type ".concat(typeOverride, " on ").concat(JSON.stringify(v)));
}
else {
throw new Error("can not have an object without a 'type' as a datum value: ".concat(JSON.stringify(v)));
}
}
}
else if (typeofV === 'string' && typeOverride === 'TIME') {
return new Date(v);
}
else if (typeofV === 'number' && isNaN(v)) {
return null;
}
}
return v;
}
var valueToJS = exports.valueToJS = function(v) {
if (v == null) {
return null;
}
else {
var typeofV = typeof v;
if (typeofV === 'object') {
if (v.toISOString) {
return v;
}
else if (v.toJS) {
return v.toJS();
}
else {
throw new Error("can not convert ".concat(JSON.stringify(v), " to JS"));
}
}
else if (typeofV === 'number' && !isFinite(v)) {
return String(v);
}
}
return v;
}
var datumHasExternal = exports.datumHasExternal = function(datum) {
for (var name_1 in datum) {
var value = datum[name_1];
if (value instanceof External)
return true;
if (value instanceof Dataset && value.hasExternal())
return true;
}
return false;
}
var introspectDatum = exports.introspectDatum = function(datum) {
var promises = [];
var newDatum = Object.create(null);
Object.keys(datum).forEach(function (name) {
var v = datum[name];
if (v instanceof External && v.needsIntrospect()) {
promises.push(v.introspect().then(function (introspectedExter