use of io.questdb.griffin.engine.functions.GroupByFunction in project questdb by bluestreak01.
the class SampleByInterpolateRecordCursorFactory method interpolateBoundaryRange.
private void interpolateBoundaryRange(long x1, long x2, Record record) {
// interpolating boundary
for (int i = 0; i < groupByTwoPointFunctionCount; i++) {
GroupByFunction function = groupByTwoPointFunctions.getQuick(i);
MapValue startValue = findDataMapValue2(record, x1);
MapValue endValue = findDataMapValue3(record, x2);
InterpolationUtil.interpolateBoundary(function, sampler.nextTimestamp(x1), startValue, endValue, true);
InterpolationUtil.interpolateBoundary(function, x2, startValue, endValue, false);
}
}
use of io.questdb.griffin.engine.functions.GroupByFunction in project questdb by bluestreak01.
the class SqlCodeGenerator method generateSelectGroupBy.
private RecordCursorFactory generateSelectGroupBy(QueryModel model, SqlExecutionContext executionContext) throws SqlException {
// fail fast if we cannot create timestamp sampler
final ExpressionNode sampleByNode = model.getSampleBy();
if (sampleByNode != null) {
return generateSampleBy(model, executionContext, sampleByNode, model.getSampleByUnit());
}
RecordCursorFactory factory = null;
try {
ObjList<QueryColumn> columns;
ExpressionNode columnExpr;
// generate special case plan for "select count() from somewhere"
columns = model.getColumns();
if (columns.size() == 1) {
CharSequence columnName = columns.getQuick(0).getName();
columnExpr = columns.getQuick(0).getAst();
if (columnExpr.type == FUNCTION && columnExpr.paramCount == 0 && isCountKeyword(columnExpr.token)) {
// check if count() was not aliased, if it was, we need to generate new metadata, bummer
final RecordMetadata metadata = isCountKeyword(columnName) ? CountRecordCursorFactory.DEFAULT_COUNT_METADATA : new GenericRecordMetadata().add(new TableColumnMetadata(Chars.toString(columnName), 0, ColumnType.LONG));
return new CountRecordCursorFactory(metadata, generateSubQuery(model, executionContext));
}
}
tempKeyIndexesInBase.clear();
tempKeyIndex.clear();
arrayColumnTypes.clear();
tempKeyKinds.clear();
boolean pageFramingSupported = false;
boolean specialCaseKeys = false;
// check for special case time function aggregations
final QueryModel nested = model.getNestedModel();
assert nested != null;
// check if underlying model has reference to hour(column) function
if (nested.getSelectModelType() == QueryModel.SELECT_MODEL_VIRTUAL && (columnExpr = nested.getColumns().getQuick(0).getAst()).type == FUNCTION && isHourKeyword(columnExpr.token) && columnExpr.paramCount == 1 && columnExpr.rhs.type == LITERAL) {
specialCaseKeys = true;
factory = generateSubQuery(nested, executionContext);
pageFramingSupported = factory.supportPageFrameCursor();
if (pageFramingSupported) {
// find position of the hour() argument in the factory meta
tempKeyIndexesInBase.add(factory.getMetadata().getColumnIndex(columnExpr.rhs.token));
// find position of hour() alias in selected columns
// also make sure there are no other literal column than our function reference
final CharSequence functionColumnName = columns.getQuick(0).getName();
for (int i = 0, n = columns.size(); i < n; i++) {
columnExpr = columns.getQuick(i).getAst();
if (columnExpr.type == LITERAL) {
if (Chars.equals(columnExpr.token, functionColumnName)) {
tempKeyIndex.add(i);
// storage dimension for Rosti is INT when we use hour(). This function produces INT.
tempKeyKinds.add(GKK_HOUR_INT);
arrayColumnTypes.add(ColumnType.INT);
} else {
// there is something else here, fallback to default implementation
pageFramingSupported = false;
break;
}
}
}
} else {
factory = Misc.free(factory);
}
}
if (factory == null) {
factory = generateSubQuery(model, executionContext);
pageFramingSupported = factory.supportPageFrameCursor();
}
RecordMetadata metadata = factory.getMetadata();
// inspect model for possibility of vector aggregate intrinsics
if (pageFramingSupported && assembleKeysAndFunctionReferences(columns, metadata, !specialCaseKeys)) {
// create metadata from everything we've gathered
GenericRecordMetadata meta = new GenericRecordMetadata();
// start with keys
for (int i = 0, n = tempKeyIndex.size(); i < n; i++) {
final int indexInThis = tempKeyIndex.getQuick(i);
final int indexInBase = tempKeyIndexesInBase.getQuick(i);
final int type = arrayColumnTypes.getColumnType(i);
if (ColumnType.isSymbol(type)) {
meta.add(indexInThis, new TableColumnMetadata(Chars.toString(columns.getQuick(indexInThis).getName()), 0, type, false, 0, metadata.isSymbolTableStatic(indexInBase), null));
} else {
meta.add(indexInThis, new TableColumnMetadata(Chars.toString(columns.getQuick(indexInThis).getName()), 0, type, null));
}
}
// add aggregates
for (int i = 0, n = tempVecConstructors.size(); i < n; i++) {
VectorAggregateFunctionConstructor constructor = tempVecConstructors.getQuick(i);
int indexInBase = tempVecConstructorArgIndexes.getQuick(i);
int indexInThis = tempAggIndex.getQuick(i);
VectorAggregateFunction vaf = constructor.create(tempKeyKinds.size() == 0 ? 0 : tempKeyKinds.getQuick(0), indexInBase, executionContext.getWorkerCount());
tempVaf.add(vaf);
meta.add(indexInThis, new TableColumnMetadata(Chars.toString(columns.getQuick(indexInThis).getName()), 0, vaf.getType(), null));
}
if (tempKeyIndexesInBase.size() == 0) {
return new GroupByNotKeyedVectorRecordCursorFactory(configuration, factory, meta, tempVaf);
}
if (tempKeyIndexesInBase.size() == 1) {
for (int i = 0, n = tempVaf.size(); i < n; i++) {
tempVaf.getQuick(i).pushValueTypes(arrayColumnTypes);
}
GroupByUtils.validateGroupByColumns(model, 1);
return new GroupByRecordCursorFactory(configuration, factory, meta, arrayColumnTypes, executionContext.getWorkerCount(), tempVaf, tempKeyIndexesInBase.getQuick(0), tempKeyIndex.getQuick(0), tempSymbolSkewIndexes);
}
}
if (specialCaseKeys) {
// uh-oh, we had special case keys, but could not find implementation for the functions
// release factory we created unnecessarily
Misc.free(factory);
// create factory on top level model
factory = generateSubQuery(model, executionContext);
// and reset metadata
metadata = factory.getMetadata();
}
final int timestampIndex = getTimestampIndex(model, factory);
keyTypes.clear();
valueTypes.clear();
listColumnFilterA.clear();
final int columnCount = model.getColumns().size();
ObjList<GroupByFunction> groupByFunctions = new ObjList<>(columnCount);
GroupByUtils.prepareGroupByFunctions(model, metadata, functionParser, executionContext, groupByFunctions, groupByFunctionPositions, valueTypes);
final ObjList<Function> recordFunctions = new ObjList<>(columnCount);
final GenericRecordMetadata groupByMetadata = new GenericRecordMetadata();
GroupByUtils.prepareGroupByRecordFunctions(model, metadata, listColumnFilterA, groupByFunctions, groupByFunctionPositions, recordFunctions, recordFunctionPositions, groupByMetadata, keyTypes, valueTypes.getColumnCount(), true, timestampIndex);
if (keyTypes.getColumnCount() == 0) {
return new GroupByNotKeyedRecordCursorFactory(factory, groupByMetadata, groupByFunctions, recordFunctions, valueTypes.getColumnCount());
}
return new io.questdb.griffin.engine.groupby.GroupByRecordCursorFactory(configuration, factory, listColumnFilterA, asm, keyTypes, valueTypes, groupByMetadata, groupByFunctions, recordFunctions);
} catch (Throwable e) {
Misc.free(factory);
throw e;
}
}
use of io.questdb.griffin.engine.functions.GroupByFunction in project questdb by bluestreak01.
the class SqlCodeGenerator method generateSampleBy.
@NotNull
private RecordCursorFactory generateSampleBy(QueryModel model, SqlExecutionContext executionContext, ExpressionNode sampleByNode, ExpressionNode sampleByUnits) throws SqlException {
executionContext.pushTimestampRequiredFlag(true);
try {
final ExpressionNode timezoneName = model.getSampleByTimezoneName();
final Function timezoneNameFunc;
final int timezoneNameFuncPos;
final ExpressionNode offset = model.getSampleByOffset();
final Function offsetFunc;
final int offsetFuncPos;
if (timezoneName != null) {
timezoneNameFunc = functionParser.parseFunction(timezoneName, EmptyRecordMetadata.INSTANCE, executionContext);
timezoneNameFuncPos = timezoneName.position;
} else {
timezoneNameFunc = StrConstant.NULL;
timezoneNameFuncPos = 0;
}
if (offset != null) {
offsetFunc = functionParser.parseFunction(offset, EmptyRecordMetadata.INSTANCE, executionContext);
offsetFuncPos = offset.position;
} else {
offsetFunc = StrConstant.NULL;
offsetFuncPos = 0;
}
final RecordCursorFactory factory = generateSubQuery(model, executionContext);
// we require timestamp
final int timestampIndex = getTimestampIndex(model, factory);
if (timestampIndex == -1) {
Misc.free(factory);
throw SqlException.$(model.getModelPosition(), "base query does not provide dedicated TIMESTAMP column");
}
final RecordMetadata metadata = factory.getMetadata();
final ObjList<ExpressionNode> sampleByFill = model.getSampleByFill();
final TimestampSampler timestampSampler;
if (sampleByUnits == null) {
timestampSampler = TimestampSamplerFactory.getInstance(sampleByNode.token, sampleByNode.position);
} else {
Function sampleByPeriod = functionParser.parseFunction(sampleByNode, EmptyRecordMetadata.INSTANCE, executionContext);
if (!sampleByPeriod.isConstant() || (sampleByPeriod.getType() != ColumnType.LONG && sampleByPeriod.getType() != ColumnType.INT)) {
sampleByPeriod.close();
throw SqlException.$(sampleByNode.position, "sample by period must be a constant expression of INT or LONG type");
}
long period = sampleByPeriod.getLong(null);
sampleByPeriod.close();
timestampSampler = TimestampSamplerFactory.getInstance(period, sampleByUnits.token, sampleByUnits.position);
}
final int fillCount = sampleByFill.size();
try {
keyTypes.clear();
valueTypes.clear();
listColumnFilterA.clear();
if (fillCount == 1 && Chars.equalsLowerCaseAscii(sampleByFill.getQuick(0).token, "linear")) {
return new SampleByInterpolateRecordCursorFactory(configuration, factory, timestampSampler, model, listColumnFilterA, functionParser, executionContext, asm, keyTypes, valueTypes, entityColumnFilter, recordFunctionPositions, groupByFunctionPositions, timestampIndex);
}
final int columnCount = model.getColumns().size();
final ObjList<GroupByFunction> groupByFunctions = new ObjList<>(columnCount);
// first value is always timestamp
valueTypes.add(ColumnType.TIMESTAMP);
GroupByUtils.prepareGroupByFunctions(model, metadata, functionParser, executionContext, groupByFunctions, groupByFunctionPositions, valueTypes);
final ObjList<Function> recordFunctions = new ObjList<>(columnCount);
final GenericRecordMetadata groupByMetadata = new GenericRecordMetadata();
GroupByUtils.prepareGroupByRecordFunctions(model, metadata, listColumnFilterA, groupByFunctions, groupByFunctionPositions, recordFunctions, recordFunctionPositions, groupByMetadata, keyTypes, valueTypes.getColumnCount(), false, timestampIndex);
boolean isFillNone = fillCount == 0 || fillCount == 1 && Chars.equalsLowerCaseAscii(sampleByFill.getQuick(0).token, "none");
boolean allGroupsFirstLast = isFillNone && allGroupsFirstLastWithSingleSymbolFilter(model, metadata);
if (allGroupsFirstLast) {
SingleSymbolFilter symbolFilter = factory.convertToSampleByIndexDataFrameCursorFactory();
if (symbolFilter != null) {
return new SampleByFirstLastRecordCursorFactory(factory, timestampSampler, groupByMetadata, model.getColumns(), metadata, timezoneNameFunc, timezoneNameFuncPos, offsetFunc, offsetFuncPos, timestampIndex, symbolFilter, configuration.getSampleByIndexSearchPageSize());
}
}
if (fillCount == 1 && Chars.equalsLowerCaseAscii(sampleByFill.getQuick(0).token, "prev")) {
if (keyTypes.getColumnCount() == 0) {
return new SampleByFillPrevNotKeyedRecordCursorFactory(factory, timestampSampler, groupByMetadata, groupByFunctions, recordFunctions, timestampIndex, valueTypes.getColumnCount(), timezoneNameFunc, timezoneNameFuncPos, offsetFunc, offsetFuncPos);
}
return new SampleByFillPrevRecordCursorFactory(configuration, factory, timestampSampler, listColumnFilterA, asm, keyTypes, valueTypes, groupByMetadata, groupByFunctions, recordFunctions, timestampIndex, timezoneNameFunc, timezoneNameFuncPos, offsetFunc, offsetFuncPos);
}
if (isFillNone) {
if (keyTypes.getColumnCount() == 0) {
// this sample by is not keyed
return new SampleByFillNoneNotKeyedRecordCursorFactory(factory, timestampSampler, groupByMetadata, groupByFunctions, recordFunctions, valueTypes.getColumnCount(), timestampIndex, timezoneNameFunc, timezoneNameFuncPos, offsetFunc, offsetFuncPos);
}
return new SampleByFillNoneRecordCursorFactory(configuration, factory, groupByMetadata, groupByFunctions, recordFunctions, timestampSampler, listColumnFilterA, asm, keyTypes, valueTypes, timestampIndex, timezoneNameFunc, timezoneNameFuncPos, offsetFunc, offsetFuncPos);
}
if (fillCount == 1 && isNullKeyword(sampleByFill.getQuick(0).token)) {
if (keyTypes.getColumnCount() == 0) {
return new SampleByFillNullNotKeyedRecordCursorFactory(factory, timestampSampler, groupByMetadata, groupByFunctions, recordFunctions, recordFunctionPositions, valueTypes.getColumnCount(), timestampIndex, timezoneNameFunc, timezoneNameFuncPos, offsetFunc, offsetFuncPos);
}
return new SampleByFillNullRecordCursorFactory(configuration, factory, timestampSampler, listColumnFilterA, asm, keyTypes, valueTypes, groupByMetadata, groupByFunctions, recordFunctions, recordFunctionPositions, timestampIndex, timezoneNameFunc, timezoneNameFuncPos, offsetFunc, offsetFuncPos);
}
assert fillCount > 0;
if (keyTypes.getColumnCount() == 0) {
return new SampleByFillValueNotKeyedRecordCursorFactory(factory, timestampSampler, sampleByFill, groupByMetadata, groupByFunctions, recordFunctions, recordFunctionPositions, valueTypes.getColumnCount(), timestampIndex, timezoneNameFunc, timezoneNameFuncPos, offsetFunc, offsetFuncPos);
}
return new SampleByFillValueRecordCursorFactory(configuration, factory, timestampSampler, listColumnFilterA, asm, sampleByFill, keyTypes, valueTypes, groupByMetadata, groupByFunctions, recordFunctions, recordFunctionPositions, timestampIndex, timezoneNameFunc, timezoneNameFuncPos, offsetFunc, offsetFuncPos);
} catch (Throwable e) {
factory.close();
throw e;
}
} finally {
executionContext.popTimestampRequiredFlag();
}
}
Aggregations