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.resultset;
19  
20  import com.cedarsoftware.util.CaseInsensitiveMap;
21  import lombok.EqualsAndHashCode;
22  import org.apache.shardingsphere.driver.exception.ResultSetClosedException;
23  import org.apache.shardingsphere.infra.exception.kernel.syntax.ColumnIndexOutOfRangeException;
24  import org.apache.shardingsphere.infra.exception.kernel.syntax.ColumnLabelNotFoundException;
25  import org.apache.shardingsphere.driver.jdbc.unsupported.AbstractUnsupportedDatabaseMetaDataResultSet;
26  import org.apache.shardingsphere.infra.executor.sql.execute.result.query.impl.driver.jdbc.type.util.ResultSetUtils;
27  import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
28  import org.apache.shardingsphere.infra.rule.attribute.datanode.DataNodeRuleAttribute;
29  
30  import java.math.BigDecimal;
31  import java.net.URL;
32  import java.sql.Date;
33  import java.sql.ResultSet;
34  import java.sql.ResultSetMetaData;
35  import java.sql.SQLException;
36  import java.sql.Statement;
37  import java.sql.Time;
38  import java.sql.Timestamp;
39  import java.util.ArrayList;
40  import java.util.Collection;
41  import java.util.HashSet;
42  import java.util.Iterator;
43  import java.util.LinkedList;
44  import java.util.List;
45  import java.util.Map;
46  import java.util.Optional;
47  
48  /**
49   * Database meta data result set.
50   */
51  public final class DatabaseMetaDataResultSet extends AbstractUnsupportedDatabaseMetaDataResultSet {
52      
53      private static final String TABLE_NAME = "TABLE_NAME";
54      
55      private static final String INDEX_NAME = "INDEX_NAME";
56      
57      private final int type;
58      
59      private final int concurrency;
60      
61      private final Collection<ShardingSphereRule> rules;
62      
63      private final ResultSetMetaData resultSetMetaData;
64      
65      private final Map<String, Integer> columnLabelIndexMap;
66      
67      private final Iterator<DatabaseMetaDataObject> databaseMetaDataObjectIterator;
68      
69      private final ResultSet resultSet;
70      
71      private volatile boolean closed;
72      
73      private DatabaseMetaDataObject currentDatabaseMetaDataObject;
74      
75      public DatabaseMetaDataResultSet(final ResultSet resultSet, final Collection<ShardingSphereRule> rules) throws SQLException {
76          this.resultSet = resultSet;
77          this.rules = rules;
78          type = resultSet.getType();
79          concurrency = resultSet.getConcurrency();
80          resultSetMetaData = resultSet.getMetaData();
81          columnLabelIndexMap = initIndexMap();
82          databaseMetaDataObjectIterator = initIterator(resultSet);
83      }
84      
85      private Map<String, Integer> initIndexMap() throws SQLException {
86          Map<String, Integer> result = new CaseInsensitiveMap<>();
87          for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
88              result.put(resultSetMetaData.getColumnLabel(i), i);
89          }
90          return result;
91      }
92      
93      private Iterator<DatabaseMetaDataObject> initIterator(final ResultSet resultSet) throws SQLException {
94          Collection<DatabaseMetaDataObject> result = new LinkedList<>();
95          Collection<DatabaseMetaDataObject> removeDuplicationSet = new HashSet<>();
96          int tableNameColumnIndex = columnLabelIndexMap.getOrDefault(TABLE_NAME, -1);
97          int indexNameColumnIndex = columnLabelIndexMap.getOrDefault(INDEX_NAME, -1);
98          while (resultSet.next()) {
99              DatabaseMetaDataObject databaseMetaDataObject = generateDatabaseMetaDataObject(tableNameColumnIndex, indexNameColumnIndex, resultSet);
100             if (!removeDuplicationSet.contains(databaseMetaDataObject)) {
101                 result.add(databaseMetaDataObject);
102                 removeDuplicationSet.add(databaseMetaDataObject);
103             }
104         }
105         return result.iterator();
106     }
107     
108     private DatabaseMetaDataObject generateDatabaseMetaDataObject(final int tableNameColumnIndex, final int indexNameColumnIndex, final ResultSet resultSet) throws SQLException {
109         DatabaseMetaDataObject result = new DatabaseMetaDataObject(resultSetMetaData.getColumnCount());
110         Optional<DataNodeRuleAttribute> ruleAttribute = findDataNodeRuleAttribute();
111         for (int i = 1; i <= columnLabelIndexMap.size(); i++) {
112             if (tableNameColumnIndex == i) {
113                 String tableName = resultSet.getString(i);
114                 Optional<String> logicTableName = ruleAttribute.isPresent() ? ruleAttribute.get().findLogicTableByActualTable(tableName) : Optional.empty();
115                 result.addObject(logicTableName.orElse(tableName));
116             } else if (indexNameColumnIndex == i) {
117                 String tableName = resultSet.getString(tableNameColumnIndex);
118                 String indexName = resultSet.getString(i);
119                 result.addObject(null != indexName && indexName.endsWith(tableName) ? indexName.substring(0, indexName.length() - tableName.length() - 1) : indexName);
120             } else {
121                 result.addObject(resultSet.getObject(i));
122             }
123         }
124         return result;
125     }
126     
127     private Optional<DataNodeRuleAttribute> findDataNodeRuleAttribute() {
128         for (ShardingSphereRule each : rules) {
129             Optional<DataNodeRuleAttribute> ruleAttribute = each.getAttributes().findAttribute(DataNodeRuleAttribute.class);
130             if (ruleAttribute.isPresent()) {
131                 return ruleAttribute;
132             }
133         }
134         return Optional.empty();
135     }
136     
137     @Override
138     public boolean next() throws SQLException {
139         checkClosed();
140         if (databaseMetaDataObjectIterator.hasNext()) {
141             currentDatabaseMetaDataObject = databaseMetaDataObjectIterator.next();
142             return true;
143         }
144         return false;
145     }
146     
147     @Override
148     public void close() throws SQLException {
149         checkClosed();
150         closed = true;
151     }
152     
153     @Override
154     public boolean wasNull() throws SQLException {
155         checkClosed();
156         return false;
157     }
158     
159     @Override
160     public BigDecimal getBigDecimal(final int columnIndex, final int scale) throws SQLException {
161         return getBigDecimal(columnIndex, true, scale);
162     }
163     
164     @Override
165     public BigDecimal getBigDecimal(final String columnLabel, final int scale) throws SQLException {
166         return getBigDecimal(findColumn(columnLabel), scale);
167     }
168     
169     @Override
170     public BigDecimal getBigDecimal(final int columnIndex) throws SQLException {
171         return getBigDecimal(columnIndex, false, 0);
172     }
173     
174     private BigDecimal getBigDecimal(final int columnIndex, final boolean needScale, final int scale) throws SQLException {
175         checkClosed();
176         checkColumnIndex(columnIndex);
177         return (BigDecimal) ResultSetUtils.convertBigDecimalValue(currentDatabaseMetaDataObject.getObject(columnIndex), needScale, scale);
178     }
179     
180     @Override
181     public BigDecimal getBigDecimal(final String columnLabel) throws SQLException {
182         return getBigDecimal(findColumn(columnLabel));
183     }
184     
185     @Override
186     public Statement getStatement() throws SQLException {
187         return resultSet.getStatement();
188     }
189     
190     @Override
191     public String getString(final int columnIndex) throws SQLException {
192         checkClosed();
193         checkColumnIndex(columnIndex);
194         return (String) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), String.class);
195     }
196     
197     @Override
198     public String getString(final String columnLabel) throws SQLException {
199         return getString(findColumn(columnLabel));
200     }
201     
202     @Override
203     public String getNString(final int columnIndex) throws SQLException {
204         return getString(columnIndex);
205     }
206     
207     @Override
208     public String getNString(final String columnLabel) throws SQLException {
209         return getString(columnLabel);
210     }
211     
212     @Override
213     public boolean getBoolean(final int columnIndex) throws SQLException {
214         checkClosed();
215         checkColumnIndex(columnIndex);
216         return (boolean) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), boolean.class);
217     }
218     
219     @Override
220     public boolean getBoolean(final String columnLabel) throws SQLException {
221         return getBoolean(findColumn(columnLabel));
222     }
223     
224     @Override
225     public byte getByte(final int columnIndex) throws SQLException {
226         checkClosed();
227         checkColumnIndex(columnIndex);
228         return (byte) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), byte.class);
229     }
230     
231     @Override
232     public byte getByte(final String columnLabel) throws SQLException {
233         return getByte(findColumn(columnLabel));
234     }
235     
236     @Override
237     public short getShort(final int columnIndex) throws SQLException {
238         checkClosed();
239         checkColumnIndex(columnIndex);
240         return (short) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), short.class);
241     }
242     
243     @Override
244     public short getShort(final String columnLabel) throws SQLException {
245         return getShort(findColumn(columnLabel));
246     }
247     
248     @Override
249     public int getInt(final int columnIndex) throws SQLException {
250         checkClosed();
251         checkColumnIndex(columnIndex);
252         return (int) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), int.class);
253     }
254     
255     @Override
256     public int getInt(final String columnLabel) throws SQLException {
257         return getInt(findColumn(columnLabel));
258     }
259     
260     @Override
261     public long getLong(final int columnIndex) throws SQLException {
262         checkClosed();
263         checkColumnIndex(columnIndex);
264         return (long) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), long.class);
265     }
266     
267     @Override
268     public long getLong(final String columnLabel) throws SQLException {
269         return getLong(findColumn(columnLabel));
270     }
271     
272     @Override
273     public float getFloat(final int columnIndex) throws SQLException {
274         checkClosed();
275         checkColumnIndex(columnIndex);
276         return (float) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), float.class);
277     }
278     
279     @Override
280     public float getFloat(final String columnLabel) throws SQLException {
281         return getFloat(findColumn(columnLabel));
282     }
283     
284     @Override
285     public double getDouble(final int columnIndex) throws SQLException {
286         checkClosed();
287         checkColumnIndex(columnIndex);
288         return (double) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), double.class);
289     }
290     
291     @Override
292     public double getDouble(final String columnLabel) throws SQLException {
293         return getDouble(findColumn(columnLabel));
294     }
295     
296     @Override
297     public byte[] getBytes(final int columnIndex) throws SQLException {
298         checkClosed();
299         checkColumnIndex(columnIndex);
300         return (byte[]) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), byte[].class);
301     }
302     
303     @Override
304     public byte[] getBytes(final String columnLabel) throws SQLException {
305         return getBytes(findColumn(columnLabel));
306     }
307     
308     @Override
309     public Date getDate(final int columnIndex) throws SQLException {
310         checkClosed();
311         checkColumnIndex(columnIndex);
312         return (Date) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), Date.class);
313     }
314     
315     @Override
316     public Date getDate(final String columnLabel) throws SQLException {
317         return getDate(findColumn(columnLabel));
318     }
319     
320     @Override
321     public Time getTime(final int columnIndex) throws SQLException {
322         checkClosed();
323         checkColumnIndex(columnIndex);
324         return (Time) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), Time.class);
325     }
326     
327     @Override
328     public Time getTime(final String columnLabel) throws SQLException {
329         return getTime(findColumn(columnLabel));
330     }
331     
332     @Override
333     public Timestamp getTimestamp(final int columnIndex) throws SQLException {
334         checkClosed();
335         checkColumnIndex(columnIndex);
336         return (Timestamp) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), Timestamp.class);
337     }
338     
339     @Override
340     public Timestamp getTimestamp(final String columnLabel) throws SQLException {
341         return getTimestamp(findColumn(columnLabel));
342     }
343     
344     @Override
345     public URL getURL(final int columnIndex) throws SQLException {
346         checkClosed();
347         checkColumnIndex(columnIndex);
348         return (URL) ResultSetUtils.convertValue(currentDatabaseMetaDataObject.getObject(columnIndex), URL.class);
349     }
350     
351     @Override
352     public URL getURL(final String columnLabel) throws SQLException {
353         return getURL(findColumn(columnLabel));
354     }
355     
356     @Override
357     public ResultSetMetaData getMetaData() throws SQLException {
358         checkClosed();
359         return resultSetMetaData;
360     }
361     
362     @Override
363     public Object getObject(final int columnIndex) throws SQLException {
364         checkClosed();
365         checkColumnIndex(columnIndex);
366         return currentDatabaseMetaDataObject.getObject(columnIndex);
367     }
368     
369     @Override
370     public Object getObject(final String columnLabel) throws SQLException {
371         return getObject(findColumn(columnLabel));
372     }
373     
374     @Override
375     public int findColumn(final String columnLabel) throws SQLException {
376         checkClosed();
377         if (!columnLabelIndexMap.containsKey(columnLabel)) {
378             throw new ColumnLabelNotFoundException(columnLabel).toSQLException();
379         }
380         return columnLabelIndexMap.get(columnLabel);
381     }
382     
383     @Override
384     public int getType() throws SQLException {
385         checkClosed();
386         return type;
387     }
388     
389     @Override
390     public int getConcurrency() throws SQLException {
391         checkClosed();
392         return concurrency;
393     }
394     
395     @Override
396     public void setFetchDirection(final int direction) throws SQLException {
397         resultSet.setFetchDirection(direction);
398     }
399     
400     @Override
401     public int getFetchDirection() throws SQLException {
402         return resultSet.getFetchDirection();
403     }
404     
405     @Override
406     public int getFetchSize() throws SQLException {
407         return resultSet.getFetchSize();
408     }
409     
410     @Override
411     public void setFetchSize(final int rows) throws SQLException {
412         resultSet.setFetchSize(rows);
413     }
414     
415     @Override
416     public boolean isClosed() {
417         return closed;
418     }
419     
420     private void checkClosed() throws SQLException {
421         if (closed) {
422             throw new ResultSetClosedException().toSQLException();
423         }
424     }
425     
426     private void checkColumnIndex(final int columnIndex) throws SQLException {
427         if (columnIndex < 1 || columnIndex > resultSetMetaData.getColumnCount()) {
428             throw new ColumnIndexOutOfRangeException(columnIndex).toSQLException();
429         }
430     }
431     
432     @EqualsAndHashCode
433     private static final class DatabaseMetaDataObject {
434         
435         private final List<Object> objects;
436         
437         private DatabaseMetaDataObject(final int columnCount) {
438             objects = new ArrayList<>(columnCount);
439         }
440         
441         public void addObject(final Object object) {
442             objects.add(object);
443         }
444         
445         public Object getObject(final int index) {
446             return objects.get(index - 1);
447         }
448     }
449 }