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