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.binder.context.segment.table;
19  
20  import com.cedarsoftware.util.CaseInsensitiveSet;
21  import lombok.AccessLevel;
22  import lombok.Getter;
23  import lombok.ToString;
24  import org.apache.shardingsphere.infra.binder.context.segment.select.subquery.SubqueryTableContext;
25  import org.apache.shardingsphere.infra.binder.context.segment.select.subquery.engine.SubqueryTableContextEngine;
26  import org.apache.shardingsphere.infra.binder.context.statement.type.dml.SelectStatementContext;
27  import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment;
28  import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SubqueryTableSegment;
29  import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableNameSegment;
30  import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableSegment;
31  
32  import java.util.Collection;
33  import java.util.Collections;
34  import java.util.HashMap;
35  import java.util.LinkedList;
36  import java.util.Map;
37  import java.util.Optional;
38  
39  /**
40   * Tables context.
41   */
42  @Getter
43  @ToString
44  public final class TablesContext {
45      
46      @Getter(AccessLevel.NONE)
47      private final Collection<TableSegment> tables = new LinkedList<>();
48      
49      private final Collection<SimpleTableSegment> simpleTables = new LinkedList<>();
50      
51      private final Collection<String> tableNames = new CaseInsensitiveSet<>();
52      
53      private final Collection<String> schemaNames = new CaseInsensitiveSet<>();
54      
55      private final Collection<String> databaseNames = new CaseInsensitiveSet<>();
56      
57      @Getter(AccessLevel.NONE)
58      private final Map<String, Collection<SubqueryTableContext>> subqueryTables = new HashMap<>();
59      
60      public TablesContext(final SimpleTableSegment table) {
61          this(null == table ? Collections.emptyList() : Collections.singletonList(table));
62      }
63      
64      public TablesContext(final Collection<SimpleTableSegment> tables) {
65          this(tables, Collections.emptyMap());
66      }
67      
68      public TablesContext(final Collection<? extends TableSegment> tables, final Map<Integer, SelectStatementContext> subqueryContexts) {
69          if (tables.isEmpty()) {
70              return;
71          }
72          this.tables.addAll(tables);
73          for (TableSegment each : tables) {
74              if (each instanceof SimpleTableSegment) {
75                  SimpleTableSegment simpleTableSegment = (SimpleTableSegment) each;
76                  TableNameSegment tableName = simpleTableSegment.getTableName();
77                  if (!"DUAL".equalsIgnoreCase(tableName.getIdentifier().getValue())) {
78                      simpleTables.add(simpleTableSegment);
79                      tableNames.add(tableName.getIdentifier().getValue());
80                      // TODO support bind with all statement contains table segment @duanzhengqiang
81                      tableName.getTableBoundInfo().ifPresent(optional -> schemaNames.add(optional.getOriginalSchema().getValue()));
82                      tableName.getTableBoundInfo().ifPresent(optional -> databaseNames.add(optional.getOriginalDatabase().getValue()));
83                  }
84              }
85              if (each instanceof SubqueryTableSegment) {
86                  subqueryTables.putAll(createSubqueryTables(subqueryContexts, (SubqueryTableSegment) each));
87              }
88          }
89      }
90      
91      private Map<String, Collection<SubqueryTableContext>> createSubqueryTables(final Map<Integer, SelectStatementContext> subqueryContexts, final SubqueryTableSegment subqueryTable) {
92          if (!subqueryContexts.containsKey(subqueryTable.getSubquery().getStartIndex())) {
93              return Collections.emptyMap();
94          }
95          SelectStatementContext subqueryContext = subqueryContexts.get(subqueryTable.getSubquery().getStartIndex());
96          Map<String, SubqueryTableContext> subqueryTableContexts = new SubqueryTableContextEngine().createSubqueryTableContexts(subqueryContext, subqueryTable.getAliasName().orElse(null));
97          Map<String, Collection<SubqueryTableContext>> result = new HashMap<>(subqueryTableContexts.size(), 1F);
98          for (SubqueryTableContext each : subqueryTableContexts.values()) {
99              if (null != each.getAliasName()) {
100                 result.computeIfAbsent(each.getAliasName(), unused -> new LinkedList<>()).add(each);
101             }
102         }
103         return result;
104     }
105     
106     /**
107      * Get database name.
108      *
109      * @return database name
110      */
111     public Optional<String> getDatabaseName() {
112         return databaseNames.isEmpty() ? Optional.empty() : Optional.of(databaseNames.iterator().next());
113     }
114     
115     /**
116      * Get schema name.
117      *
118      * @return schema name
119      */
120     public Optional<String> getSchemaName() {
121         return schemaNames.isEmpty() ? Optional.empty() : Optional.of(schemaNames.iterator().next());
122     }
123 }