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.sqlfederation.resultset;
19  
20  import org.apache.calcite.avatica.SqlType;
21  import org.apache.calcite.rel.type.RelDataType;
22  import org.apache.calcite.rel.type.RelDataTypeFactory;
23  import org.apache.calcite.rel.type.RelDataTypeFactoryImpl.JavaType;
24  import org.apache.calcite.schema.Schema;
25  import org.apache.calcite.schema.Table;
26  import org.apache.calcite.sql.type.SqlTypeName;
27  import org.apache.shardingsphere.database.connector.core.DefaultDatabase;
28  import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
29  import org.apache.shardingsphere.infra.binder.context.segment.select.projection.Projection;
30  import org.apache.shardingsphere.infra.binder.context.segment.select.projection.impl.ColumnProjection;
31  import org.apache.shardingsphere.sqlfederation.compiler.sql.type.SQLFederationDataTypeFactory;
32  import org.apache.shardingsphere.sqlfederation.resultset.converter.DialectSQLFederationColumnTypeConverter;
33  
34  import java.math.BigInteger;
35  import java.sql.ResultSetMetaData;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Optional;
39  
40  /**
41   * SQL federation result set meta data.
42   */
43  public final class SQLFederationResultSetMetaData extends SQLFederationWrapperAdapter implements ResultSetMetaData {
44      
45      private final Schema sqlFederationSchema;
46      
47      private final RelDataTypeFactory typeFactory;
48      
49      private final List<Projection> expandProjections;
50      
51      private final DatabaseType databaseType;
52      
53      private final RelDataType resultColumnType;
54      
55      private final Map<Integer, String> indexAndColumnLabels;
56      
57      private final DialectSQLFederationColumnTypeConverter columnTypeConverter;
58      
59      public SQLFederationResultSetMetaData(final Schema sqlFederationSchema, final List<Projection> expandProjections, final DatabaseType databaseType, final RelDataType resultColumnType,
60                                            final Map<Integer, String> indexAndColumnLabels, final DialectSQLFederationColumnTypeConverter columnTypeConverter) {
61          this.sqlFederationSchema = sqlFederationSchema;
62          typeFactory = SQLFederationDataTypeFactory.getInstance();
63          this.expandProjections = expandProjections;
64          this.databaseType = databaseType;
65          this.resultColumnType = resultColumnType;
66          this.indexAndColumnLabels = indexAndColumnLabels;
67          this.columnTypeConverter = columnTypeConverter;
68      }
69      
70      @Override
71      public int getColumnCount() {
72          return indexAndColumnLabels.size();
73      }
74      
75      @Override
76      public boolean isAutoIncrement(final int column) {
77          return false;
78      }
79      
80      @Override
81      public boolean isCaseSensitive(final int column) {
82          return false;
83      }
84      
85      @Override
86      public boolean isSearchable(final int column) {
87          return false;
88      }
89      
90      @Override
91      public boolean isCurrency(final int column) {
92          return false;
93      }
94      
95      @Override
96      public int isNullable(final int column) {
97          Optional<Table> table = findTableName(column).flatMap(optional -> Optional.ofNullable(sqlFederationSchema.tables().get(optional)));
98          return !table.isPresent() || table.get().getRowType(typeFactory).isNullable() ? columnNullable : columnNoNulls;
99      }
100     
101     @Override
102     public boolean isSigned(final int column) {
103         return true;
104     }
105     
106     @Override
107     public int getColumnDisplaySize(final int column) {
108         return findTableName(column).flatMap(optional -> Optional.ofNullable(sqlFederationSchema.tables().get(optional))).map(optional -> optional.getRowType(typeFactory).getPrecision()).orElse(0);
109     }
110     
111     @Override
112     public String getColumnLabel(final int column) {
113         return indexAndColumnLabels.size() < column ? resultColumnType.getFieldList().get(column - 1).getName() : indexAndColumnLabels.get(column);
114     }
115     
116     @Override
117     public String getColumnName(final int column) {
118         return expandProjections.size() < column ? resultColumnType.getFieldList().get(column - 1).getName() : expandProjections.get(column - 1).getColumnName();
119     }
120     
121     @Override
122     public String getSchemaName(final int column) {
123         return DefaultDatabase.LOGIC_NAME;
124     }
125     
126     @Override
127     public int getPrecision(final int column) {
128         Optional<Table> table = findTableName(column).flatMap(optional -> Optional.ofNullable(sqlFederationSchema.tables().get(optional)));
129         return !table.isPresent() || RelDataType.PRECISION_NOT_SPECIFIED == table.get().getRowType(typeFactory).getPrecision() ? 0 : table.get().getRowType(typeFactory).getPrecision();
130     }
131     
132     @Override
133     public int getScale(final int column) {
134         Optional<Table> table = findTableName(column).flatMap(optional -> Optional.ofNullable(sqlFederationSchema.tables().get(optional)));
135         return !table.isPresent() || RelDataType.SCALE_NOT_SPECIFIED == table.get().getRowType(typeFactory).getScale() ? 0 : table.get().getRowType(typeFactory).getScale();
136     }
137     
138     @Override
139     public String getTableName(final int column) {
140         return findTableName(column).orElse("");
141     }
142     
143     @Override
144     public String getCatalogName(final int column) {
145         return DefaultDatabase.LOGIC_NAME;
146     }
147     
148     @Override
149     public int getColumnType(final int column) {
150         RelDataType relDataType = resultColumnType.getFieldList().get(column - 1).getType();
151         // TODO remove this logic when calcite supports BigInteger type
152         if (relDataType instanceof JavaType && BigInteger.class.isAssignableFrom(((JavaType) relDataType).getJavaClass())) {
153             return SqlType.BIGINT.id;
154         }
155         return null == columnTypeConverter ? relDataType.getSqlTypeName().getJdbcOrdinal() : columnTypeConverter.convertColumnType(relDataType.getSqlTypeName());
156     }
157     
158     @Override
159     public String getColumnTypeName(final int column) {
160         SqlTypeName originalSqlTypeName = resultColumnType.getFieldList().get(column - 1).getType().getSqlTypeName();
161         SqlTypeName convertSqlTypeName = SqlTypeName.getNameForJdbcType(
162                 null == columnTypeConverter ? originalSqlTypeName.getJdbcOrdinal() : columnTypeConverter.convertColumnType(originalSqlTypeName));
163         return null == convertSqlTypeName ? originalSqlTypeName.getName() : convertSqlTypeName.getName();
164     }
165     
166     @Override
167     public boolean isReadOnly(final int column) {
168         return false;
169     }
170     
171     @Override
172     public boolean isWritable(final int column) {
173         return false;
174     }
175     
176     @Override
177     public boolean isDefinitelyWritable(final int column) {
178         return false;
179     }
180     
181     @Override
182     public String getColumnClassName(final int column) {
183         return resultColumnType.getFieldList().get(column - 1).getType().getSqlTypeName().getClass().getName();
184     }
185     
186     private Optional<String> findTableName(final int column) {
187         Projection projection = expandProjections.size() < column
188                 ? new ColumnProjection(null, resultColumnType.getFieldList().get(column - 1).getName(), null, databaseType)
189                 : expandProjections.get(column - 1);
190         return projection instanceof ColumnProjection ? Optional.ofNullable(((ColumnProjection) projection).getOriginalTable().getValue()) : Optional.empty();
191     }
192 }