Search in sources :

Example 6 with QueryModel

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;
    }
}
Also used : GroupByFunction(io.questdb.griffin.engine.functions.GroupByFunction) SymbolFunction(io.questdb.griffin.engine.functions.SymbolFunction) GroupByFunction(io.questdb.griffin.engine.functions.GroupByFunction) AnalyticFunction(io.questdb.griffin.engine.analytic.AnalyticFunction) GroupByRecordCursorFactory(io.questdb.griffin.engine.groupby.vect.GroupByRecordCursorFactory) QueryModel(io.questdb.griffin.model.QueryModel) SortedLightRecordCursorFactory(io.questdb.griffin.engine.orderby.SortedLightRecordCursorFactory) SortedRecordCursorFactory(io.questdb.griffin.engine.orderby.SortedRecordCursorFactory) LimitRecordCursorFactory(io.questdb.griffin.engine.LimitRecordCursorFactory) GroupByRecordCursorFactory(io.questdb.griffin.engine.groupby.vect.GroupByRecordCursorFactory) EmptyTableRecordCursorFactory(io.questdb.griffin.engine.EmptyTableRecordCursorFactory) CachedAnalyticRecordCursorFactory(io.questdb.griffin.engine.analytic.CachedAnalyticRecordCursorFactory)

Example 7 with QueryModel

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;
    }
}
Also used : QueryModel(io.questdb.griffin.model.QueryModel) SymbolFunction(io.questdb.griffin.engine.functions.SymbolFunction) GroupByFunction(io.questdb.griffin.engine.functions.GroupByFunction) AnalyticFunction(io.questdb.griffin.engine.analytic.AnalyticFunction) SortedLightRecordCursorFactory(io.questdb.griffin.engine.orderby.SortedLightRecordCursorFactory) SortedRecordCursorFactory(io.questdb.griffin.engine.orderby.SortedRecordCursorFactory) LimitRecordCursorFactory(io.questdb.griffin.engine.LimitRecordCursorFactory) GroupByRecordCursorFactory(io.questdb.griffin.engine.groupby.vect.GroupByRecordCursorFactory) EmptyTableRecordCursorFactory(io.questdb.griffin.engine.EmptyTableRecordCursorFactory) CachedAnalyticRecordCursorFactory(io.questdb.griffin.engine.analytic.CachedAnalyticRecordCursorFactory) EmptyTableRecordCursorFactory(io.questdb.griffin.engine.EmptyTableRecordCursorFactory)

Aggregations

QueryModel (io.questdb.griffin.model.QueryModel)7 EmptyTableRecordCursorFactory (io.questdb.griffin.engine.EmptyTableRecordCursorFactory)3 LimitRecordCursorFactory (io.questdb.griffin.engine.LimitRecordCursorFactory)3 CachedAnalyticRecordCursorFactory (io.questdb.griffin.engine.analytic.CachedAnalyticRecordCursorFactory)3 GroupByRecordCursorFactory (io.questdb.griffin.engine.groupby.vect.GroupByRecordCursorFactory)3 SortedLightRecordCursorFactory (io.questdb.griffin.engine.orderby.SortedLightRecordCursorFactory)3 SortedRecordCursorFactory (io.questdb.griffin.engine.orderby.SortedRecordCursorFactory)3 AnalyticFunction (io.questdb.griffin.engine.analytic.AnalyticFunction)2 GroupByFunction (io.questdb.griffin.engine.functions.GroupByFunction)2 SymbolFunction (io.questdb.griffin.engine.functions.SymbolFunction)2 QueryColumn (io.questdb.griffin.model.QueryColumn)2 ExecutionModel (io.questdb.griffin.model.ExecutionModel)1 ExpressionNode (io.questdb.griffin.model.ExpressionNode)1 Test (org.junit.Test)1