View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shardingsphere.infra.session.query;
19  
20  import com.google.common.base.Joiner;
21  import lombok.Getter;
22  import org.apache.shardingsphere.database.exception.core.exception.syntax.database.NoDatabaseSelectedException;
23  import org.apache.shardingsphere.database.exception.core.exception.syntax.database.UnknownDatabaseException;
24  import org.apache.shardingsphere.infra.annotation.HighFrequencyInvocation;
25  import org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
26  import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
27  import org.apache.shardingsphere.infra.exception.generic.UnsupportedSQLOperationException;
28  import org.apache.shardingsphere.infra.hint.HintValueContext;
29  import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
30  import org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
31  import org.apache.shardingsphere.infra.session.connection.ConnectionContext;
32  import org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
33  import org.apache.shardingsphere.sql.parser.statement.core.statement.attribute.type.AllowNotUseDatabaseSQLStatementAttribute;
34  
35  import java.util.Collection;
36  import java.util.Collections;
37  import java.util.List;
38  import java.util.Optional;
39  
40  /**
41   * Query context.
42   */
43  @HighFrequencyInvocation
44  @Getter
45  public final class QueryContext {
46      
47      private final SQLStatementContext sqlStatementContext;
48      
49      private final String sql;
50      
51      private final List<Object> parameters;
52      
53      private final HintValueContext hintValueContext;
54      
55      private final ConnectionContext connectionContext;
56      
57      private final ShardingSphereMetaData metaData;
58      
59      private final Collection<String> usedDatabaseNames;
60      
61      private final boolean useCache;
62      
63      public QueryContext(final SQLStatementContext sqlStatementContext, final String sql, final List<Object> params, final HintValueContext hintValueContext, final ConnectionContext connectionContext,
64                          final ShardingSphereMetaData metaData) {
65          this(sqlStatementContext, sql, params, hintValueContext, connectionContext, metaData, false);
66      }
67      
68      public QueryContext(final SQLStatementContext sqlStatementContext, final String sql, final List<Object> params, final HintValueContext hintValueContext, final ConnectionContext connectionContext,
69                          final ShardingSphereMetaData metaData, final boolean useCache) {
70          this.sqlStatementContext = sqlStatementContext;
71          this.sql = sql;
72          parameters = params;
73          this.hintValueContext = hintValueContext;
74          this.connectionContext = connectionContext;
75          this.metaData = metaData;
76          usedDatabaseNames = getUsedDatabaseNames(sqlStatementContext, connectionContext, metaData);
77          this.useCache = useCache;
78      }
79      
80      private Collection<String> getUsedDatabaseNames(final SQLStatementContext sqlStatementContext, final ConnectionContext connectionContext, final ShardingSphereMetaData metaData) {
81          Collection<String> databaseNamesFromSQL = sqlStatementContext.getTablesContext().getDatabaseNames();
82          return databaseNamesFromSQL.isEmpty()
83                  ? connectionContext.getCurrentDatabaseName().map(Collections::singletonList)
84                          .orElseGet(() -> getUsedDatabaseNamesFromSQLStatementAttribute(sqlStatementContext.getSqlStatement(), metaData))
85                  : databaseNamesFromSQL;
86      }
87      
88      private List<String> getUsedDatabaseNamesFromSQLStatementAttribute(final SQLStatement sqlStatement, final ShardingSphereMetaData metaData) {
89          Optional<AllowNotUseDatabaseSQLStatementAttribute> attribute = sqlStatement.getAttributes().findAttribute(AllowNotUseDatabaseSQLStatementAttribute.class);
90          if (attribute.isPresent() && attribute.get().isAllowNotUseDatabase()) {
91              return attribute.get().findDatabaseName().map(Collections::singletonList).orElseGet(() -> findAnyDatabaseName(metaData).map(Collections::singletonList).orElse(Collections.emptyList()));
92          }
93          return Collections.emptyList();
94      }
95      
96      private Optional<String> findAnyDatabaseName(final ShardingSphereMetaData metaData) {
97          for (ShardingSphereDatabase each : metaData.getAllDatabases()) {
98              if (each.isComplete()) {
99                  return Optional.of(each.getName());
100             }
101         }
102         return Optional.empty();
103     }
104     
105     /**
106      * Get used database.
107      *
108      * @return used database
109      */
110     public ShardingSphereDatabase getUsedDatabase() {
111         ShardingSpherePreconditions.checkState(usedDatabaseNames.size() <= 1,
112                 () -> new UnsupportedSQLOperationException(String.format("Can not support multiple logic databases [%s]", Joiner.on(", ").join(usedDatabaseNames))));
113         ShardingSpherePreconditions.checkState(usedDatabaseNames.size() == 1, NoDatabaseSelectedException::new);
114         String databaseName = usedDatabaseNames.iterator().next();
115         ShardingSpherePreconditions.checkState(metaData.containsDatabase(databaseName), () -> new UnknownDatabaseException(databaseName));
116         return metaData.getDatabase(databaseName);
117     }
118 }