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.driver.jdbc.core.datasource.metadata;
19  
20  import com.google.common.base.Strings;
21  import org.apache.shardingsphere.driver.jdbc.adapter.AdaptedDatabaseMetaData;
22  import org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection;
23  import org.apache.shardingsphere.driver.jdbc.core.resultset.DatabaseMetaDataResultSet;
24  import org.apache.shardingsphere.infra.database.core.connector.ConnectionProperties;
25  import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
26  import org.apache.shardingsphere.infra.rule.attribute.datanode.DataNodeRuleAttribute;
27  
28  import java.sql.Connection;
29  import java.sql.DatabaseMetaData;
30  import java.sql.ResultSet;
31  import java.sql.SQLException;
32  import java.util.Collection;
33  import java.util.Optional;
34  
35  /**
36   * ShardingSphere database meta data.
37   */
38  public final class ShardingSphereDatabaseMetaData extends AdaptedDatabaseMetaData {
39      
40      private final ShardingSphereConnection connection;
41      
42      private final Collection<ShardingSphereRule> rules;
43      
44      private String currentPhysicalDataSourceName;
45      
46      private Connection currentPhysicalConnection;
47      
48      private DatabaseMetaData currentDatabaseMetaData;
49      
50      public ShardingSphereDatabaseMetaData(final ShardingSphereConnection connection) throws SQLException {
51          super(connection.getDatabaseConnectionManager().getRandomConnection().getMetaData());
52          this.connection = connection;
53          rules = connection.getContextManager().getMetaDataContexts().getMetaData().getDatabase(connection.getDatabaseName()).getRuleMetaData().getRules();
54      }
55      
56      @Override
57      public Connection getConnection() throws SQLException {
58          if (null == currentPhysicalConnection) {
59              currentPhysicalConnection = connection.getDatabaseConnectionManager().getRandomConnection();
60          }
61          return currentPhysicalConnection;
62      }
63      
64      @Override
65      public ResultSet getSuperTypes(final String catalog, final String schemaPattern, final String typeNamePattern) throws SQLException {
66          return createDatabaseMetaDataResultSet(getDatabaseMetaData().getSuperTypes(getActualCatalog(catalog), getActualSchema(schemaPattern), typeNamePattern));
67      }
68      
69      @Override
70      public ResultSet getSuperTables(final String catalog, final String schemaPattern, final String tableNamePattern) throws SQLException {
71          return createDatabaseMetaDataResultSet(getDatabaseMetaData().getSuperTables(getActualCatalog(catalog), getActualSchema(schemaPattern), getActualTableNamePattern(tableNamePattern)));
72      }
73      
74      @Override
75      public ResultSet getAttributes(final String catalog, final String schemaPattern, final String typeNamePattern, final String attributeNamePattern) throws SQLException {
76          return createDatabaseMetaDataResultSet(getDatabaseMetaData().getAttributes(getActualCatalog(catalog), getActualSchema(schemaPattern), typeNamePattern, attributeNamePattern));
77      }
78      
79      @Override
80      public ResultSet getProcedures(final String catalog, final String schemaPattern, final String procedureNamePattern) throws SQLException {
81          return createDatabaseMetaDataResultSet(getDatabaseMetaData().getProcedures(getActualCatalog(catalog), getActualSchema(schemaPattern), procedureNamePattern));
82      }
83      
84      @Override
85      public ResultSet getProcedureColumns(final String catalog, final String schemaPattern, final String procedureNamePattern, final String columnNamePattern) throws SQLException {
86          return createDatabaseMetaDataResultSet(getDatabaseMetaData().getProcedureColumns(getActualCatalog(catalog), getActualSchema(schemaPattern), procedureNamePattern, columnNamePattern));
87      }
88      
89      @Override
90      public ResultSet getTables(final String catalog, final String schemaPattern, final String tableNamePattern, final String[] types) throws SQLException {
91          return createDatabaseMetaDataResultSet(getDatabaseMetaData().getTables(getActualCatalog(catalog), getActualSchema(schemaPattern), getActualTableNamePattern(tableNamePattern), types));
92      }
93      
94      @Override
95      public ResultSet getSchemas() throws SQLException {
96          return createDatabaseMetaDataResultSet(getDatabaseMetaData().getSchemas());
97      }
98      
99      @Override
100     public ResultSet getSchemas(final String catalog, final String schemaPattern) throws SQLException {
101         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getSchemas(getActualCatalog(catalog), getActualSchema(schemaPattern)));
102     }
103     
104     @Override
105     public ResultSet getCatalogs() throws SQLException {
106         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getCatalogs());
107     }
108     
109     @Override
110     public ResultSet getTableTypes() throws SQLException {
111         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getTableTypes());
112     }
113     
114     @Override
115     public ResultSet getColumns(final String catalog, final String schemaPattern, final String tableNamePattern, final String columnNamePattern) throws SQLException {
116         return createDatabaseMetaDataResultSet(
117                 getDatabaseMetaData().getColumns(getActualCatalog(catalog), getActualSchema(schemaPattern), getActualTableNamePattern(tableNamePattern), columnNamePattern));
118     }
119     
120     @Override
121     public ResultSet getColumnPrivileges(final String catalog, final String schema, final String table, final String columnNamePattern) throws SQLException {
122         return createDatabaseMetaDataResultSet(
123                 getDatabaseMetaData().getColumnPrivileges(getActualCatalog(catalog), getActualSchema(schema), getActualTable(getActualCatalog(catalog), table), columnNamePattern));
124     }
125     
126     @Override
127     public ResultSet getTablePrivileges(final String catalog, final String schemaPattern, final String tableNamePattern) throws SQLException {
128         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getTablePrivileges(getActualCatalog(catalog), getActualSchema(schemaPattern), getActualTableNamePattern(tableNamePattern)));
129     }
130     
131     @Override
132     public ResultSet getBestRowIdentifier(final String catalog, final String schema, final String table, final int scope, final boolean nullable) throws SQLException {
133         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getBestRowIdentifier(getActualCatalog(catalog), getActualSchema(schema),
134                 getActualTable(getActualCatalog(catalog), table), scope, nullable));
135     }
136     
137     @Override
138     public ResultSet getVersionColumns(final String catalog, final String schema, final String table) throws SQLException {
139         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getVersionColumns(getActualCatalog(catalog), getActualSchema(schema), getActualTable(getActualCatalog(catalog), table)));
140     }
141     
142     @Override
143     public ResultSet getPrimaryKeys(final String catalog, final String schema, final String table) throws SQLException {
144         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getPrimaryKeys(getActualCatalog(catalog), getActualSchema(schema), getActualTable(getActualCatalog(catalog), table)));
145     }
146     
147     @Override
148     public ResultSet getImportedKeys(final String catalog, final String schema, final String table) throws SQLException {
149         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getImportedKeys(getActualCatalog(catalog), getActualSchema(schema), getActualTable(getActualCatalog(catalog), table)));
150     }
151     
152     @Override
153     public ResultSet getExportedKeys(final String catalog, final String schema, final String table) throws SQLException {
154         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getExportedKeys(getActualCatalog(catalog), getActualSchema(schema), getActualTable(getActualCatalog(catalog), table)));
155     }
156     
157     @Override
158     public ResultSet getCrossReference(final String parentCatalog,
159                                        final String parentSchema, final String parentTable, final String foreignCatalog, final String foreignSchema, final String foreignTable) throws SQLException {
160         return createDatabaseMetaDataResultSet(
161                 getDatabaseMetaData().getCrossReference(getActualCatalog(parentCatalog), getActualSchema(parentSchema), parentTable, foreignCatalog, foreignSchema, foreignTable));
162     }
163     
164     @Override
165     public ResultSet getTypeInfo() throws SQLException {
166         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getTypeInfo());
167     }
168     
169     @Override
170     public ResultSet getIndexInfo(final String catalog, final String schema, final String table, final boolean unique, final boolean approximate) throws SQLException {
171         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getIndexInfo(getActualCatalog(catalog), getActualSchema(schema),
172                 getActualTable(getActualCatalog(catalog), table), unique, approximate));
173     }
174     
175     @Override
176     public ResultSet getUDTs(final String catalog, final String schemaPattern, final String typeNamePattern, final int[] types) throws SQLException {
177         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getUDTs(getActualCatalog(catalog), getActualSchema(schemaPattern), typeNamePattern, types));
178     }
179     
180     @Override
181     public ResultSet getClientInfoProperties() throws SQLException {
182         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getClientInfoProperties());
183     }
184     
185     @Override
186     public ResultSet getFunctions(final String catalog, final String schemaPattern, final String functionNamePattern) throws SQLException {
187         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getFunctions(getActualCatalog(catalog), getActualSchema(schemaPattern), functionNamePattern));
188     }
189     
190     @Override
191     public ResultSet getFunctionColumns(final String catalog, final String schemaPattern, final String functionNamePattern, final String columnNamePattern) throws SQLException {
192         return createDatabaseMetaDataResultSet(getDatabaseMetaData().getFunctionColumns(getActualCatalog(catalog), getActualSchema(schemaPattern), functionNamePattern, columnNamePattern));
193     }
194     
195     @Override
196     public ResultSet getPseudoColumns(final String catalog, final String schemaPattern, final String tableNamePattern, final String columnNamePattern) throws SQLException {
197         return createDatabaseMetaDataResultSet(
198                 getDatabaseMetaData().getPseudoColumns(getActualCatalog(catalog), getActualSchema(schemaPattern), getActualTableNamePattern(tableNamePattern), columnNamePattern));
199     }
200     
201     private String getActualTableNamePattern(final String tableNamePattern) {
202         return null == tableNamePattern
203                 ? null
204                 : findDataNodeRuleAttribute().filter(optional -> optional.findFirstActualTable(tableNamePattern).isPresent()).map(optional -> "%" + tableNamePattern + "%").orElse(tableNamePattern);
205     }
206     
207     private String getActualTable(final String catalog, final String table) {
208         return null == table ? null : findDataNodeRuleAttribute().map(each -> findActualTable(each, catalog, table).orElse(table)).orElse(table);
209     }
210     
211     private Optional<String> findActualTable(final DataNodeRuleAttribute ruleAttribute, final String catalog, final String table) {
212         return Strings.isNullOrEmpty(catalog) ? ruleAttribute.findFirstActualTable(table) : ruleAttribute.findActualTableByCatalog(catalog, table);
213     }
214     
215     private Optional<DataNodeRuleAttribute> findDataNodeRuleAttribute() {
216         for (ShardingSphereRule each : rules) {
217             Optional<DataNodeRuleAttribute> ruleAttribute = each.getAttributes().findAttribute(DataNodeRuleAttribute.class);
218             if (ruleAttribute.isPresent()) {
219                 return ruleAttribute;
220             }
221         }
222         return Optional.empty();
223     }
224     
225     private ResultSet createDatabaseMetaDataResultSet(final ResultSet resultSet) throws SQLException {
226         return new DatabaseMetaDataResultSet(resultSet, rules);
227     }
228     
229     private String getActualCatalog(final String catalog) {
230         if (null == catalog) {
231             return null;
232         }
233         // TODO consider get actual catalog by logic catalog rather than random physical datasource's catalog.
234         ConnectionProperties connectionProps = connection.getContextManager()
235                 .getMetaDataContexts().getMetaData().getDatabase(connection.getDatabaseName()).getResourceMetaData().getStorageUnits().get(getDataSourceName()).getConnectionProperties();
236         return connectionProps.getCatalog();
237     }
238     
239     private String getActualSchema(final String schema) {
240         if (null == schema) {
241             return null;
242         }
243         // TODO consider get actual schema by logic catalog rather than random physical datasource's schema.
244         ConnectionProperties connectionProps = connection.getContextManager()
245                 .getMetaDataContexts().getMetaData().getDatabase(connection.getDatabaseName()).getResourceMetaData().getStorageUnits().get(getDataSourceName()).getConnectionProperties();
246         return Optional.ofNullable(connectionProps.getSchema()).map(String::toUpperCase).orElse(null);
247     }
248     
249     private String getDataSourceName() {
250         if (null == currentPhysicalDataSourceName) {
251             currentPhysicalDataSourceName = connection.getDatabaseConnectionManager().getRandomPhysicalDataSourceName();
252         }
253         return currentPhysicalDataSourceName;
254     }
255     
256     private DatabaseMetaData getDatabaseMetaData() throws SQLException {
257         if (null == currentDatabaseMetaData) {
258             currentDatabaseMetaData = getConnection().getMetaData();
259         }
260         return currentDatabaseMetaData;
261     }
262 }