use of io.questdb.griffin.model.QueryModel 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.model.QueryModel in project questdb by bluestreak01.
the class SqlCodeGenerator method generateJoins.
private RecordCursorFactory generateJoins(QueryModel model, SqlExecutionContext executionContext) throws SqlException {
final ObjList<QueryModel> joinModels = model.getJoinModels();
IntList ordered = model.getOrderedJoinModels();
RecordCursorFactory master = null;
CharSequence masterAlias = null;
try {
int n = ordered.size();
assert n > 1;
for (int i = 0; i < n; i++) {
int index = ordered.getQuick(i);
QueryModel slaveModel = joinModels.getQuick(index);
if (i > 0) {
executionContext.pushTimestampRequiredFlag(joinsRequiringTimestamp[slaveModel.getJoinType()]);
} else {
// i == 0
// This is first model in the sequence of joins
// TS requirement is symmetrical on both right and left sides
// check if next join requires a timestamp
int nextJointType = joinModels.getQuick(ordered.getQuick(1)).getJoinType();
executionContext.pushTimestampRequiredFlag(joinsRequiringTimestamp[nextJointType]);
}
RecordCursorFactory slave = null;
try {
// compile
slave = generateQuery(slaveModel, executionContext, index > 0);
// check if this is the root of joins
if (master == null) {
// This is an opportunistic check of order by clause
// to determine if we can get away ordering main record source only
// Ordering main record source could benefit from rowid access thus
// making it faster compared to ordering of join record source that
// doesn't allow rowid access.
master = slave;
masterAlias = slaveModel.getName();
} else {
// not the root, join to "master"
final int joinType = slaveModel.getJoinType();
final RecordMetadata masterMetadata = master.getMetadata();
final RecordMetadata slaveMetadata = slave.getMetadata();
switch(joinType) {
case JOIN_CROSS:
master = new CrossJoinRecordCursorFactory(createJoinMetadata(masterAlias, masterMetadata, slaveModel.getName(), slaveMetadata), master, slave, masterMetadata.getColumnCount());
masterAlias = null;
break;
case JOIN_ASOF:
validateBothTimestamps(slaveModel, masterMetadata, slaveMetadata);
processJoinContext(index == 1, slaveModel.getContext(), masterMetadata, slaveMetadata);
if (slave.recordCursorSupportsRandomAccess() && !fullFatJoins) {
if (listColumnFilterA.size() > 0 && listColumnFilterB.size() > 0) {
master = createAsOfJoin(createJoinMetadata(masterAlias, masterMetadata, slaveModel.getName(), slaveMetadata), master, RecordSinkFactory.getInstance(asm, masterMetadata, listColumnFilterB, true), slave, RecordSinkFactory.getInstance(asm, slaveMetadata, listColumnFilterA, true), masterMetadata.getColumnCount());
} else {
master = new AsOfJoinNoKeyRecordCursorFactory(createJoinMetadata(masterAlias, masterMetadata, slaveModel.getName(), slaveMetadata), master, slave, masterMetadata.getColumnCount());
}
} else {
master = createFullFatJoin(master, masterMetadata, masterAlias, slave, slaveMetadata, slaveModel.getName(), slaveModel.getJoinKeywordPosition(), CREATE_FULL_FAT_AS_OF_JOIN);
}
masterAlias = null;
break;
case JOIN_LT:
validateBothTimestamps(slaveModel, masterMetadata, slaveMetadata);
processJoinContext(index == 1, slaveModel.getContext(), masterMetadata, slaveMetadata);
if (slave.recordCursorSupportsRandomAccess() && !fullFatJoins) {
if (listColumnFilterA.size() > 0 && listColumnFilterB.size() > 0) {
master = createLtJoin(createJoinMetadata(masterAlias, masterMetadata, slaveModel.getName(), slaveMetadata), master, RecordSinkFactory.getInstance(asm, masterMetadata, listColumnFilterB, true), slave, RecordSinkFactory.getInstance(asm, slaveMetadata, listColumnFilterA, true), masterMetadata.getColumnCount());
} else {
master = new LtJoinNoKeyRecordCursorFactory(createJoinMetadata(masterAlias, masterMetadata, slaveModel.getName(), slaveMetadata), master, slave, masterMetadata.getColumnCount());
}
} else {
master = createFullFatJoin(master, masterMetadata, masterAlias, slave, slaveMetadata, slaveModel.getName(), slaveModel.getJoinKeywordPosition(), CREATE_FULL_FAT_LT_JOIN);
}
masterAlias = null;
break;
case JOIN_SPLICE:
validateBothTimestamps(slaveModel, masterMetadata, slaveMetadata);
processJoinContext(index == 1, slaveModel.getContext(), masterMetadata, slaveMetadata);
if (slave.recordCursorSupportsRandomAccess() && master.recordCursorSupportsRandomAccess() && !fullFatJoins) {
master = createSpliceJoin(// splice join result does not have timestamp
createJoinMetadata(masterAlias, masterMetadata, slaveModel.getName(), slaveMetadata, -1), master, RecordSinkFactory.getInstance(asm, masterMetadata, listColumnFilterB, true), slave, RecordSinkFactory.getInstance(asm, slaveMetadata, listColumnFilterA, true), masterMetadata.getColumnCount());
} else {
assert false;
}
break;
default:
processJoinContext(index == 1, slaveModel.getContext(), masterMetadata, slaveMetadata);
master = createHashJoin(createJoinMetadata(masterAlias, masterMetadata, slaveModel.getName(), slaveMetadata), master, slave, joinType);
masterAlias = null;
break;
}
}
} catch (Throwable th) {
Misc.free(master);
Misc.free(slave);
throw th;
} finally {
executionContext.popTimestampRequiredFlag();
}
// check if there are post-filters
ExpressionNode filter = slaveModel.getPostJoinWhereClause();
if (filter != null) {
master = new FilteredRecordCursorFactory(master, functionParser.parseFunction(filter, master.getMetadata(), executionContext));
}
}
// unfortunately we had to go all out to create join metadata
// now it is time to check if we have constant conditions
ExpressionNode constFilter = model.getConstWhereClause();
if (constFilter != null) {
Function function = functionParser.parseFunction(constFilter, null, executionContext);
if (!function.getBool(null)) {
// do not copy metadata here
// this would have been JoinRecordMetadata, which is new instance anyway
// we have to make sure that this metadata is safely transitioned
// to empty cursor factory
JoinRecordMetadata metadata = (JoinRecordMetadata) master.getMetadata();
metadata.incrementRefCount();
RecordCursorFactory factory = new EmptyTableRecordCursorFactory(metadata);
Misc.free(master);
return factory;
}
}
return master;
} catch (Throwable e) {
Misc.free(master);
throw e;
}
}
Aggregations