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.segment.from.impl;
19  
20  import com.cedarsoftware.util.CaseInsensitiveSet;
21  import lombok.AccessLevel;
22  import lombok.NoArgsConstructor;
23  import org.apache.shardingsphere.infra.binder.segment.from.SimpleTableSegmentBinderContext;
24  import org.apache.shardingsphere.infra.binder.segment.from.TableSegmentBinderContext;
25  import org.apache.shardingsphere.infra.binder.statement.SQLStatementBinderContext;
26  import org.apache.shardingsphere.infra.database.core.metadata.database.DialectDatabaseMetaData;
27  import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
28  import org.apache.shardingsphere.infra.database.core.type.DatabaseTypeRegistry;
29  import org.apache.shardingsphere.infra.database.opengauss.type.OpenGaussDatabaseType;
30  import org.apache.shardingsphere.infra.database.postgresql.type.PostgreSQLDatabaseType;
31  import org.apache.shardingsphere.infra.exception.kernel.metadata.TableNotFoundException;
32  import org.apache.shardingsphere.infra.exception.core.ShardingSpherePreconditions;
33  import org.apache.shardingsphere.infra.exception.dialect.exception.syntax.database.NoDatabaseSelectedException;
34  import org.apache.shardingsphere.infra.metadata.database.schema.manager.SystemSchemaManager;
35  import org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereColumn;
36  import org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
37  import org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereTable;
38  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.column.ColumnSegment;
39  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ColumnProjectionSegment;
40  import org.apache.shardingsphere.sql.parser.sql.common.segment.dml.item.ProjectionSegment;
41  import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.OwnerSegment;
42  import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.bounded.ColumnSegmentBoundedInfo;
43  import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.bounded.TableSegmentBoundedInfo;
44  import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.SimpleTableSegment;
45  import org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.TableNameSegment;
46  import org.apache.shardingsphere.sql.parser.sql.common.value.identifier.IdentifierValue;
47  
48  import java.util.Collection;
49  import java.util.Collections;
50  import java.util.LinkedList;
51  import java.util.Map;
52  import java.util.Optional;
53  
54  /**
55   * Simple table segment binder.
56   */
57  @NoArgsConstructor(access = AccessLevel.PRIVATE)
58  public final class SimpleTableSegmentBinder {
59      
60      private static final Collection<String> SYSTEM_CATALOG_TABLES = new CaseInsensitiveSet<>(4, 1F);
61      
62      private static final String PG_CATALOG = "pg_catalog";
63      
64      static {
65          SYSTEM_CATALOG_TABLES.add("pg_database");
66          SYSTEM_CATALOG_TABLES.add("pg_tables");
67          SYSTEM_CATALOG_TABLES.add("pg_roles");
68          SYSTEM_CATALOG_TABLES.add("pg_settings");
69      }
70      
71      /**
72       * Bind simple table segment with metadata.
73       *
74       * @param segment simple table segment
75       * @param statementBinderContext statement binder context
76       * @param tableBinderContexts table binder contexts
77       * @return bounded simple table segment
78       */
79      public static SimpleTableSegment bind(final SimpleTableSegment segment, final SQLStatementBinderContext statementBinderContext, final Map<String, TableSegmentBinderContext> tableBinderContexts) {
80          fillPivotColumnNamesInBinderContext(segment, statementBinderContext);
81          IdentifierValue originalDatabase = getDatabaseName(segment, statementBinderContext);
82          IdentifierValue originalSchema = getSchemaName(segment, statementBinderContext);
83          ShardingSpherePreconditions.checkNotNull(originalDatabase.getValue(), NoDatabaseSelectedException::new);
84          checkTableExists(segment.getTableName().getIdentifier().getValue(), statementBinderContext, originalDatabase.getValue(), originalSchema.getValue());
85          ShardingSphereSchema schema = statementBinderContext.getMetaData().getDatabase(originalDatabase.getValue()).getSchema(originalSchema.getValue());
86          tableBinderContexts.put((segment.getAliasName().orElseGet(() -> segment.getTableName().getIdentifier().getValue())).toLowerCase(),
87                  createSimpleTableBinderContext(segment, schema, originalDatabase, originalSchema, statementBinderContext));
88          TableNameSegment tableNameSegment = new TableNameSegment(segment.getTableName().getStartIndex(), segment.getTableName().getStopIndex(), segment.getTableName().getIdentifier());
89          tableNameSegment.setTableBoundedInfo(new TableSegmentBoundedInfo(originalDatabase, originalSchema));
90          SimpleTableSegment result = new SimpleTableSegment(tableNameSegment);
91          segment.getOwner().ifPresent(result::setOwner);
92          segment.getAliasSegment().ifPresent(result::setAlias);
93          return result;
94      }
95      
96      private static void fillPivotColumnNamesInBinderContext(final SimpleTableSegment segment, final SQLStatementBinderContext statementBinderContext) {
97          segment.getPivot().ifPresent(optional -> optional.getPivotColumns().forEach(each -> statementBinderContext.getPivotColumnNames().add(each.getIdentifier().getValue().toLowerCase())));
98      }
99      
100     private static IdentifierValue getDatabaseName(final SimpleTableSegment tableSegment, final SQLStatementBinderContext statementBinderContext) {
101         DialectDatabaseMetaData dialectDatabaseMetaData = new DatabaseTypeRegistry(statementBinderContext.getDatabaseType()).getDialectDatabaseMetaData();
102         Optional<OwnerSegment> owner = dialectDatabaseMetaData.getDefaultSchema().isPresent() ? tableSegment.getOwner().flatMap(OwnerSegment::getOwner) : tableSegment.getOwner();
103         return new IdentifierValue(owner.map(optional -> optional.getIdentifier().getValue()).orElse(statementBinderContext.getDefaultDatabaseName()));
104     }
105     
106     private static IdentifierValue getSchemaName(final SimpleTableSegment segment, final SQLStatementBinderContext statementBinderContext) {
107         if (segment.getOwner().isPresent()) {
108             return segment.getOwner().get().getIdentifier();
109         }
110         // TODO getSchemaName according to search path
111         DatabaseType databaseType = statementBinderContext.getDatabaseType();
112         if ((databaseType instanceof PostgreSQLDatabaseType || databaseType instanceof OpenGaussDatabaseType)
113                 && SYSTEM_CATALOG_TABLES.contains(segment.getTableName().getIdentifier().getValue())) {
114             return new IdentifierValue(PG_CATALOG);
115         }
116         return new IdentifierValue(new DatabaseTypeRegistry(databaseType).getDefaultSchemaName(statementBinderContext.getDefaultDatabaseName()));
117     }
118     
119     private static SimpleTableSegmentBinderContext createSimpleTableBinderContext(final SimpleTableSegment segment, final ShardingSphereSchema schema,
120                                                                                   final IdentifierValue originalDatabase, final IdentifierValue originalSchema,
121                                                                                   final SQLStatementBinderContext statementBinderContext) {
122         Collection<ShardingSphereColumn> columnNames =
123                 Optional.ofNullable(schema.getTable(segment.getTableName().getIdentifier().getValue())).map(ShardingSphereTable::getColumnValues).orElseGet(Collections::emptyList);
124         Collection<ProjectionSegment> projectionSegments = new LinkedList<>();
125         DialectDatabaseMetaData dialectDatabaseMetaData = new DatabaseTypeRegistry(statementBinderContext.getDatabaseType()).getDialectDatabaseMetaData();
126         for (ShardingSphereColumn each : columnNames) {
127             ColumnSegment columnSegment = new ColumnSegment(0, 0, new IdentifierValue(each.getName(), dialectDatabaseMetaData.getQuoteCharacter()));
128             columnSegment.setOwner(new OwnerSegment(0, 0, segment.getAlias().orElse(segment.getTableName().getIdentifier())));
129             columnSegment.setColumnBoundedInfo(new ColumnSegmentBoundedInfo(originalDatabase, originalSchema, segment.getTableName().getIdentifier(),
130                     new IdentifierValue(each.getName(), dialectDatabaseMetaData.getQuoteCharacter())));
131             ColumnProjectionSegment columnProjectionSegment = new ColumnProjectionSegment(columnSegment);
132             columnProjectionSegment.setVisible(each.isVisible());
133             projectionSegments.add(columnProjectionSegment);
134         }
135         return new SimpleTableSegmentBinderContext(projectionSegments);
136     }
137     
138     private static void checkTableExists(final String tableName, final SQLStatementBinderContext statementBinderContext, final String databaseName, final String schemaName) {
139         if ("dual".equalsIgnoreCase(tableName)) {
140             return;
141         }
142         if (SystemSchemaManager.isSystemTable(schemaName, tableName)) {
143             return;
144         }
145         if (statementBinderContext.getExternalTableBinderContexts().containsKey(tableName)) {
146             return;
147         }
148         ShardingSpherePreconditions.checkState(statementBinderContext.getMetaData().containsDatabase(databaseName)
149                 && statementBinderContext.getMetaData().getDatabase(databaseName).containsSchema(schemaName)
150                 && statementBinderContext.getMetaData().getDatabase(databaseName).getSchema(schemaName).containsTable(tableName),
151                 () -> new TableNotFoundException(tableName));
152     }
153 }