blob: f3fa61cecaad8b2b6fb4d0c8fccb53a914b5da8c [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.doris.nereids;
import org.apache.doris.analysis.Expr;
import org.apache.doris.analysis.UserIdentity;
import org.apache.doris.catalog.DatabaseIf;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.common.Pair;
import org.apache.doris.datasource.CatalogIf;
import org.apache.doris.mysql.privilege.DataMaskPolicy;
import org.apache.doris.mysql.privilege.RowFilterPolicy;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.Variable;
import org.apache.doris.nereids.util.Utils;
import org.apache.doris.proto.Types.PUniqueId;
import org.apache.doris.qe.ResultSet;
import org.apache.doris.qe.cache.CacheProxy;
import org.apache.doris.thrift.TUniqueId;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
/** SqlCacheContext */
public class SqlCacheContext {
private final UserIdentity userIdentity;
private final TUniqueId queryId;
// if contains udf/udaf/tableValuesFunction we can not process it and skip use sql cache
private volatile boolean cannotProcessExpression;
private volatile String originSql;
private volatile String physicalPlan;
private volatile long latestPartitionId = -1;
private volatile long latestPartitionTime = -1;
private volatile long latestPartitionVersion = -1;
private volatile long sumOfPartitionNum = -1;
private final Set<FullTableName> usedTables = Sets.newLinkedHashSet();
// value: ddl sql
private final Map<FullTableName, String> usedViews = Maps.newLinkedHashMap();
// value: usedColumns
private final Map<FullTableName, Set<String>> checkPrivilegeTablesOrViews = Maps.newLinkedHashMap();
private final Map<FullTableName, List<RowFilterPolicy>> rowPolicies = Maps.newLinkedHashMap();
private final Map<FullColumnName, Optional<DataMaskPolicy>> dataMaskPolicies = Maps.newLinkedHashMap();
private final Set<Variable> usedVariables = Sets.newLinkedHashSet();
// key: the expression which **contains** nondeterministic function, e.g. date_add(date_column, date(now()))
// value: the expression which already try to fold nondeterministic function,
// e.g. date_add(date_column, '2024-01-01')
// note that value maybe contains nondeterministic function too, when fold failed
private final List<Pair<Expression, Expression>> foldFullNondeterministicPairs = Lists.newArrayList();
// key: the expression which **is** nondeterministic function, e.g. now()
// value: the expression which already try to fold nondeterministic function, e.g. '2024-01-01 10:01:03'
private final List<Pair<Expression, Expression>> foldNondeterministicPairs = Lists.newArrayList();
private volatile boolean hasUnsupportedTables;
private final List<ScanTable> scanTables = Lists.newArrayList();
private volatile CacheProxy cacheProxy;
private volatile List<Expr> resultExprs;
private volatile List<String> colLabels;
private volatile PUniqueId cacheKeyMd5;
private volatile ResultSet resultSetInFe;
public SqlCacheContext(UserIdentity userIdentity, TUniqueId queryId) {
this.userIdentity = Objects.requireNonNull(userIdentity, "userIdentity cannot be null");
this.queryId = Objects.requireNonNull(queryId, "queryId cannot be null");
}
public String getPhysicalPlan() {
return physicalPlan;
}
public void setPhysicalPlan(String physicalPlan) {
this.physicalPlan = physicalPlan;
}
public void setCannotProcessExpression(boolean cannotProcessExpression) {
this.cannotProcessExpression = cannotProcessExpression;
}
public boolean containsCannotProcessExpression() {
return cannotProcessExpression;
}
public boolean hasUnsupportedTables() {
return hasUnsupportedTables;
}
public void setHasUnsupportedTables(boolean hasUnsupportedTables) {
this.hasUnsupportedTables = hasUnsupportedTables;
}
/** addUsedTable */
public synchronized void addUsedTable(TableIf tableIf) {
if (tableIf == null) {
return;
}
DatabaseIf database = tableIf.getDatabase();
if (database == null) {
setCannotProcessExpression(true);
return;
}
CatalogIf catalog = database.getCatalog();
if (catalog == null) {
setCannotProcessExpression(true);
return;
}
usedTables.add(
new FullTableName(database.getCatalog().getName(), database.getFullName(), tableIf.getName())
);
}
/** addUsedView */
public synchronized void addUsedView(TableIf tableIf, String ddlSql) {
if (tableIf == null) {
return;
}
DatabaseIf database = tableIf.getDatabase();
if (database == null) {
setCannotProcessExpression(true);
return;
}
CatalogIf catalog = database.getCatalog();
if (catalog == null) {
setCannotProcessExpression(true);
return;
}
usedViews.put(
new FullTableName(database.getCatalog().getName(), database.getFullName(), tableIf.getName()),
ddlSql
);
}
/** addNeedCheckPrivilegeTablesOrViews */
public synchronized void addCheckPrivilegeTablesOrViews(TableIf tableIf, Set<String> usedColumns) {
if (tableIf == null) {
return;
}
DatabaseIf database = tableIf.getDatabase();
if (database == null) {
setCannotProcessExpression(true);
return;
}
CatalogIf catalog = database.getCatalog();
if (catalog == null) {
setCannotProcessExpression(true);
return;
}
FullTableName fullTableName = new FullTableName(catalog.getName(), database.getFullName(), tableIf.getName());
Set<String> existsColumns = checkPrivilegeTablesOrViews.get(fullTableName);
if (existsColumns == null) {
checkPrivilegeTablesOrViews.put(fullTableName, usedColumns);
} else {
ImmutableSet.Builder<String> allUsedColumns = ImmutableSet.builderWithExpectedSize(
existsColumns.size() + usedColumns.size());
allUsedColumns.addAll(existsColumns);
allUsedColumns.addAll(usedColumns);
checkPrivilegeTablesOrViews.put(fullTableName, allUsedColumns.build());
}
}
public synchronized void setRowFilterPolicy(
String catalog, String db, String table, List<? extends RowFilterPolicy> rowFilterPolicy) {
rowPolicies.put(new FullTableName(catalog, db, table), Utils.fastToImmutableList(rowFilterPolicy));
}
public synchronized Map<FullTableName, List<RowFilterPolicy>> getRowFilterPolicies() {
return ImmutableMap.copyOf(rowPolicies);
}
public synchronized void addDataMaskPolicy(
String catalog, String db, String table, String columnName, Optional<DataMaskPolicy> dataMaskPolicy) {
dataMaskPolicies.put(
new FullColumnName(catalog, db, table, columnName.toLowerCase(Locale.ROOT)), dataMaskPolicy
);
}
public synchronized Map<FullColumnName, Optional<DataMaskPolicy>> getDataMaskPolicies() {
return ImmutableMap.copyOf(dataMaskPolicies);
}
public synchronized void addUsedVariable(Variable value) {
usedVariables.add(value);
}
public synchronized List<Variable> getUsedVariables() {
return ImmutableList.copyOf(usedVariables);
}
public synchronized void addFoldFullNondeterministicPair(Expression unfold, Expression fold) {
foldFullNondeterministicPairs.add(Pair.of(unfold, fold));
}
public synchronized List<Pair<Expression, Expression>> getFoldFullNondeterministicPairs() {
return ImmutableList.copyOf(foldFullNondeterministicPairs);
}
public synchronized void addFoldNondeterministicPair(Expression unfold, Expression fold) {
foldNondeterministicPairs.add(Pair.of(unfold, fold));
}
public synchronized List<Pair<Expression, Expression>> getFoldNondeterministicPairs() {
return ImmutableList.copyOf(foldNondeterministicPairs);
}
public boolean isCannotProcessExpression() {
return cannotProcessExpression;
}
public UserIdentity getUserIdentity() {
return userIdentity;
}
public long getLatestPartitionTime() {
return latestPartitionTime;
}
public void setLatestPartitionTime(long latestPartitionTime) {
this.latestPartitionTime = latestPartitionTime;
}
public long getLatestPartitionVersion() {
return latestPartitionVersion;
}
public void setLatestPartitionVersion(long latestPartitionVersion) {
this.latestPartitionVersion = latestPartitionVersion;
}
public long getLatestPartitionId() {
return latestPartitionId;
}
public void setLatestPartitionId(long latestPartitionId) {
this.latestPartitionId = latestPartitionId;
}
public long getSumOfPartitionNum() {
return sumOfPartitionNum;
}
public void setSumOfPartitionNum(long sumOfPartitionNum) {
this.sumOfPartitionNum = sumOfPartitionNum;
}
public CacheProxy getCacheProxy() {
return cacheProxy;
}
public void setCacheProxy(CacheProxy cacheProxy) {
this.cacheProxy = cacheProxy;
}
public Set<FullTableName> getUsedTables() {
return ImmutableSet.copyOf(usedTables);
}
public Map<FullTableName, String> getUsedViews() {
return ImmutableMap.copyOf(usedViews);
}
public synchronized Map<FullTableName, Set<String>> getCheckPrivilegeTablesOrViews() {
return ImmutableMap.copyOf(checkPrivilegeTablesOrViews);
}
public synchronized Map<FullTableName, List<RowFilterPolicy>> getRowPolicies() {
return ImmutableMap.copyOf(rowPolicies);
}
public synchronized void addScanTable(ScanTable scanTable) {
this.scanTables.add(scanTable);
}
public synchronized List<ScanTable> getScanTables() {
return ImmutableList.copyOf(scanTables);
}
public List<Expr> getResultExprs() {
return resultExprs;
}
public void setResultExprs(List<Expr> resultExprs) {
this.resultExprs = ImmutableList.copyOf(resultExprs);
}
public List<String> getColLabels() {
return colLabels;
}
public void setColLabels(List<String> colLabels) {
this.colLabels = ImmutableList.copyOf(colLabels);
}
public TUniqueId getQueryId() {
return queryId;
}
/** getOrComputeCacheKeyMd5 */
public PUniqueId getOrComputeCacheKeyMd5() {
if (cacheKeyMd5 == null && originSql != null) {
synchronized (this) {
if (cacheKeyMd5 != null) {
return cacheKeyMd5;
}
StringBuilder cacheKey = new StringBuilder(originSql);
for (Entry<FullTableName, String> entry : usedViews.entrySet()) {
cacheKey.append("|")
.append(entry.getKey())
.append("=")
.append(entry.getValue());
}
for (Variable usedVariable : usedVariables) {
cacheKey.append("|")
.append(usedVariable.getType().name())
.append(":")
.append(usedVariable.getName())
.append("=")
.append(usedVariable.getRealExpression().toSql());
}
for (Pair<Expression, Expression> pair : foldNondeterministicPairs) {
cacheKey.append("|")
.append(pair.key().toSql())
.append("=")
.append(pair.value().toSql());
}
for (Entry<FullTableName, List<RowFilterPolicy>> entry : rowPolicies.entrySet()) {
List<RowFilterPolicy> policy = entry.getValue();
if (policy.isEmpty()) {
continue;
}
cacheKey.append("|")
.append(entry.getKey())
.append("=")
.append(policy);
}
for (Entry<FullColumnName, Optional<DataMaskPolicy>> entry : dataMaskPolicies.entrySet()) {
if (!entry.getValue().isPresent()) {
continue;
}
cacheKey.append("|")
.append(entry.getKey())
.append("=")
.append(entry.getValue().map(Object::toString).orElse(""));
}
cacheKeyMd5 = CacheProxy.getMd5(cacheKey.toString());
}
}
return cacheKeyMd5;
}
public void setOriginSql(String originSql) {
this.originSql = originSql.trim();
}
public Optional<ResultSet> getResultSetInFe() {
return Optional.ofNullable(resultSetInFe);
}
public void setResultSetInFe(ResultSet resultSetInFe) {
this.resultSetInFe = resultSetInFe;
}
/** FullTableName */
@lombok.Data
@lombok.AllArgsConstructor
public static class FullTableName {
public final String catalog;
public final String db;
public final String table;
@Override
public String toString() {
return catalog + "." + db + "." + table;
}
}
/** FullColumnName */
@lombok.Data
@lombok.AllArgsConstructor
public static class FullColumnName {
public final String catalog;
public final String db;
public final String table;
public final String column;
@Override
public String toString() {
return catalog + "." + db + "." + table + "." + column;
}
}
/** ScanTable */
@lombok.Data
@lombok.AllArgsConstructor
public static class ScanTable {
public final FullTableName fullTableName;
public final long latestTimestamp;
public final long latestVersion;
public final List<Long> scanPartitions = Lists.newArrayList();
public void addScanPartition(Long partitionId) {
this.scanPartitions.add(partitionId);
}
}
}