MethodCallExpression visitSelectExpression()

in subprojects/groovy-ginq/src/main/groovy/org/apache/groovy/ginq/provider/collection/GinqAstWalker.groovy [672:834]


    MethodCallExpression visitSelectExpression(SelectExpression selectExpression) {
        getCurrentGinqExpression().putNodeMetaData(__VISITING_SELECT, true)
        Expression selectMethodReceiver = selectExpression.getNodeMetaData(__METHOD_CALL_RECEIVER)
        DataSourceExpression dataSourceExpression = selectExpression.dataSourceExpression
        Expression projectionExpr = selectExpression.getProjectionExpr()

        List<Expression> expressionList = ((TupleExpression) projectionExpr).getExpressions()
        validateGroupCols(expressionList)

        boolean hasRnVariable = false
        boolean hasOverMethodCallExpression = false
        projectionExpr.visit(new GinqAstBaseVisitor() {
            @Override
            void visitVariableExpression(VariableExpression expression) {
                if (_RN == expression.text) {
                    hasRnVariable = true
                }
                super.visitVariableExpression(expression)
            }

            @Override
            void visitMethodCallExpression(MethodCallExpression call) {
                if (isOverMethodCall(call)) {
                    hasOverMethodCallExpression = true
                } else {
                    super.visitMethodCallExpression(call)
                }
            }
        })

        final boolean enableCount = !hasRnVariable && hasOverMethodCallExpression

        Expression lambdaCode = expressionList.get(0)
        def expressionListSize = expressionList.size()
        if (expressionListSize > 1 || (expressionListSize == 1 && lambdaCode instanceof CastExpression)) {
            ConstructorCallExpression namedListCtorCallExpression = constructNamedRecordCtorCallExpression(expressionList, MD_SELECT_NAME_LIST)
            lambdaCode = namedListCtorCallExpression
        }

        final boolean parallel = isParallel()
        final VariableExpression supplyAsyncLambdaParam = varX(supplyAsyncLambdaParamName)
        lambdaCode = ((ListExpression) new ListExpression(Collections.singletonList(lambdaCode)).transformExpression(new ExpressionTransformer() {
            @Override
            Expression transform(Expression expression) {
                if (expression instanceof VariableExpression) {
                    if (_RN == expression.text) {
                        getCurrentGinqExpression().putNodeMetaData(__RN_USED, true)
                        return parallel ? supplyAsyncLambdaParam : callX(varX(getRowNumberName()), 'get')
                    }
                }

                if (expression instanceof AbstractGinqExpression) {
                    return callX(
                            classX(QUERYABLE_HELPER_TYPE), "singleValue",
                            visit((AbstractGinqExpression) expression)
                    )
                }

                if (expression instanceof MethodCallExpression) {
                    if (isOverMethodCall(expression)) {
                        if (expression.objectExpression instanceof MethodCallExpression) {
                            VariableExpression wqVar = varX(getWindowQueryableName())

                            String lambdaParamName = getLambdaParamName(dataSourceExpression, lambdaCode)
                            VariableExpression currentRecordVar = varX(lambdaParamName)

                            getCurrentGinqExpression().putNodeMetaData(__VISITING_WINDOW_FUNCTION, true)
                            def windowFunctionMethodCallExpression = (MethodCallExpression) expression.objectExpression

                            Expression result = null
                            if (windowFunctionMethodCallExpression.methodAsString in WINDOW_FUNCTION_LIST) {
                                def argumentListExpression = (ArgumentListExpression) windowFunctionMethodCallExpression.arguments
                                List<Expression> argumentExpressionList = []
                                if (windowFunctionMethodCallExpression.methodAsString !in [FUNCTION_ROW_NUMBER, FUNCTION_RANK, FUNCTION_DENSE_RANK, FUNCTION_PERCENT_RANK, FUNCTION_CUME_DIST] && argumentListExpression.expressions) {
                                    def windowFunctionLambdaCode = argumentListExpression.getExpression(0)
                                    def windowFunctionLambdaName = '__wfp'
                                    def rootObjectExpression = findRootObjectExpression(windowFunctionLambdaCode)

                                    windowFunctionLambdaCode = ((ListExpression) (new ListExpression(Collections.singletonList(windowFunctionLambdaCode)).transformExpression(new ExpressionTransformer() {
                                        @Override
                                        Expression transform(Expression expr) {
                                            if (expr instanceof VariableExpression) {
                                                def isJoin = dataSourceExpression instanceof JoinExpression
                                                if (rootObjectExpression.text == expr.text) {
                                                    if (isJoin) {
                                                        windowFunctionLambdaName = getLambdaParamName(dataSourceExpression, expr)
                                                        return correctVars(dataSourceExpression, windowFunctionLambdaName, expr)
                                                    }

                                                    return varX(windowFunctionLambdaName)
                                                } else if (FUNCTION_AGG == windowFunctionMethodCallExpression.methodAsString && _G == expr.text) {
                                                    if (isJoin) {
                                                        windowFunctionLambdaName = getLambdaParamName(dataSourceExpression, expr)
                                                    }

                                                    return callX(
                                                            classX(QUERYABLE_HELPER_TYPE),
                                                            "navigate",
                                                            args(varX(windowFunctionLambdaName), getMetaDataMethodCall(MD_ALIAS_NAME_LIST))
                                                    )
                                                }
                                            }
                                            return expr.transformExpression(this)
                                        }
                                    }))).getExpression(0)

                                    if (windowFunctionMethodCallExpression.methodAsString in [FUNCTION_NTILE]) {
                                        argumentExpressionList << argumentListExpression.getExpression(0)
                                    } else {
                                        argumentExpressionList << lambdaX(
                                                params(param(dynamicType(), windowFunctionLambdaName)),
                                                block(stmt(windowFunctionLambdaCode))
                                        )
                                    }

                                    if (windowFunctionMethodCallExpression.methodAsString in [FUNCTION_LEAD, FUNCTION_LAG, FUNCTION_NTH_VALUE]) {
                                        List<Expression> exprList = argumentListExpression.getExpressions()
                                        if (exprList.size() > 1) {
                                            argumentExpressionList.addAll(exprList.subList(1, exprList.size()))
                                        }
                                    }
                                }

                                def windowDefinitionFactoryMethodCallExpression = constructWindowDefinitionFactoryMethodCallExpression(expression, dataSourceExpression)
                                Expression newObjectExpression = callX(wqVar, 'over', args(
                                        callX(TUPLE_TYPE, 'tuple', args(currentRecordVar, parallel ? supplyAsyncLambdaParam : getRowNumberMethodCall())),
                                        windowDefinitionFactoryMethodCallExpression
                                ))
                                result = callX(
                                        newObjectExpression,
                                        windowFunctionMethodCallExpression.methodAsString,
                                        args(argumentExpressionList)
                                )
                            } else {
                                collectSyntaxError(new GinqSyntaxError(
                                        "Unsupported window function: `${windowFunctionMethodCallExpression.methodAsString}`",
                                        windowFunctionMethodCallExpression.getLineNumber(), windowFunctionMethodCallExpression.getColumnNumber()
                                ))
                            }

                            getCurrentGinqExpression().putNodeMetaData(__VISITING_WINDOW_FUNCTION, false)

                            return result
                        }
                    }
                }

                return expression.transformExpression(this)
            }
        })).getExpression(0)

        List<Expression> extra = []
        if (enableCount || rowNumberUsed) {
            getCurrentGinqExpression().putNodeMetaData(__RN_USED, true)
            extra << callX(varX(rowNumberName), 'getAndIncrement')
        }

        def selectMethodCallExpression = callXWithLambda(selectMethodReceiver, "select", dataSourceExpression, parallel, lambdaCode, extra, param(dynamicType(), getWindowQueryableName()))

        getCurrentGinqExpression().putNodeMetaData(__VISITING_SELECT, false)

        return selectMethodCallExpression
    }