001/*
002 *  Copyright (c) 2022-2024, Mybatis-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.mybatisflex.core.query;
017
018import com.mybatisflex.core.FlexConsts;
019import com.mybatisflex.core.constant.SqlConsts;
020import com.mybatisflex.core.dialect.IDialect;
021import com.mybatisflex.core.util.CollectionUtil;
022import com.mybatisflex.core.util.SqlUtil;
023import com.mybatisflex.core.util.StringUtil;
024
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.List;
028import java.util.Objects;
029import java.util.stream.Collectors;
030
031/**
032 * 数据库 聚合函数,例如 count(id) ,max(account.age) 等等
033 */
034public class FunctionQueryColumn extends QueryColumn implements HasParamsColumn {
035
036    protected String fnName;
037    protected List<QueryColumn> columns;
038
039    public FunctionQueryColumn(String fnName) {
040        SqlUtil.keepColumnSafely(fnName);
041        this.fnName = fnName;
042        this.columns = new ArrayList<>();
043    }
044
045    public FunctionQueryColumn(String fnName, String... columns) {
046        this(fnName);
047        for (String column : columns) {
048            // SqlUtil.keepColumnSafely(column)
049            this.columns.add(new QueryColumn(column));
050        }
051    }
052
053    public FunctionQueryColumn(String fnName, QueryColumn... columns) {
054        this(fnName);
055        this.columns.addAll(Arrays.asList(columns));
056    }
057
058    public String getFnName() {
059        return fnName;
060    }
061
062    public void setFnName(String fnName) {
063        this.fnName = fnName;
064    }
065
066    public List<QueryColumn> getColumns() {
067        return columns;
068    }
069
070    public void setColumns(List<QueryColumn> columns) {
071        this.columns = columns;
072    }
073
074    @Override
075    public Object[] getParamValues() {
076        if (CollectionUtil.isEmpty(columns)) {
077            return FlexConsts.EMPTY_ARRAY;
078        }
079
080        List<Object> params = new ArrayList<>();
081
082        for (QueryColumn queryColumn : columns) {
083            if (queryColumn instanceof HasParamsColumn) {
084                Object[] paramValues = ((HasParamsColumn) queryColumn).getParamValues();
085                params.addAll(Arrays.asList(paramValues));
086            }
087        }
088
089        return params.toArray();
090    }
091
092    @Override
093    public String toSelectSql(List<QueryTable> queryTables, IDialect dialect) {
094        String sql = getSql(queryTables, dialect);
095        if (StringUtil.noText(alias)) {
096            return fnName + WrapperUtil.withBracket(sql);
097        }
098        return fnName + WrapperUtil.withAlias(sql, alias, dialect);
099    }
100
101    @Override
102    protected String toConditionSql(List<QueryTable> queryTables, IDialect dialect) {
103        String sql = getSql(queryTables, dialect);
104        return fnName + WrapperUtil.withBracket(sql);
105    }
106
107    /**
108     * <p>获取函数括号里面的 SQL 内容。
109     *
110     * <p>如果函数括号里面没有内容,就返回空字符串,这样构建出来就是函数名加括号。
111     *
112     * <p>例如,NOW() 函数的构建:
113     * <pre>{@code
114     * FunctionQueryColumn c1 = new FunctionQueryColumn("NOW");
115     * FunctionQueryColumn c2 = new FunctionQueryColumn("NOW", new StringQueryColumn(""));
116     * }</pre>
117     */
118    private String getSql(List<QueryTable> queryTables, IDialect dialect) {
119        if (CollectionUtil.isEmpty(columns)) {
120            return SqlConsts.EMPTY;
121        }
122
123        String sql = columns.stream()
124            .filter(Objects::nonNull)
125            .map(c -> c.toSelectSql(queryTables, dialect))
126            .collect(Collectors.joining(SqlConsts.DELIMITER));
127
128        if (StringUtil.noText(sql)) {
129            return SqlConsts.EMPTY;
130        }
131
132        return sql;
133    }
134
135
136    @Override
137    public String toString() {
138        return "FunctionQueryColumn{" +
139            "fnName='" + fnName + '\'' +
140            ", columns=" + columns +
141            '}';
142    }
143
144    @Override
145    public FunctionQueryColumn clone() {
146        FunctionQueryColumn clone = (FunctionQueryColumn) super.clone();
147        // deep clone ...
148        clone.columns = CollectionUtil.cloneArrayList(this.columns);
149        return clone;
150    }
151
152
153}