| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.drill.jdbc.impl; |
| |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Modifier; |
| import java.math.BigDecimal; |
| import java.sql.DatabaseMetaData; |
| import java.sql.SQLException; |
| import java.sql.Time; |
| import java.sql.Timestamp; |
| import java.sql.Types; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.stream.Collectors; |
| |
| import javax.validation.constraints.NotNull; |
| |
| import org.apache.calcite.avatica.AvaticaParameter; |
| import org.apache.calcite.avatica.AvaticaStatement; |
| import org.apache.calcite.avatica.AvaticaUtils; |
| import org.apache.calcite.avatica.ColumnMetaData; |
| import org.apache.calcite.avatica.ColumnMetaData.StructType; |
| import org.apache.calcite.avatica.Meta; |
| import org.apache.calcite.avatica.MetaImpl; |
| import org.apache.calcite.avatica.MissingResultsException; |
| import org.apache.calcite.avatica.NoSuchStatementException; |
| import org.apache.calcite.avatica.QueryState; |
| import org.apache.calcite.avatica.remote.TypedValue; |
| import org.apache.drill.common.exceptions.DrillRuntimeException; |
| import org.apache.drill.common.util.DrillStringUtils; |
| import org.apache.drill.exec.client.ServerMethod; |
| import org.apache.drill.exec.proto.UserBitShared.DrillPBError; |
| import org.apache.drill.exec.proto.UserProtos.CatalogMetadata; |
| import org.apache.drill.exec.proto.UserProtos.ColumnMetadata; |
| import org.apache.drill.exec.proto.UserProtos.GetCatalogsResp; |
| import org.apache.drill.exec.proto.UserProtos.GetColumnsResp; |
| import org.apache.drill.exec.proto.UserProtos.GetSchemasResp; |
| import org.apache.drill.exec.proto.UserProtos.GetTablesResp; |
| import org.apache.drill.exec.proto.UserProtos.LikeFilter; |
| import org.apache.drill.exec.proto.UserProtos.RequestStatus; |
| import org.apache.drill.exec.proto.UserProtos.SchemaMetadata; |
| import org.apache.drill.exec.proto.UserProtos.TableMetadata; |
| import org.apache.drill.exec.rpc.DrillRpcFuture; |
| import org.apache.drill.exec.rpc.RpcException; |
| |
| import com.google.common.collect.ImmutableMap; |
| |
| public class DrillMetaImpl extends MetaImpl { |
| private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DrillMetaImpl.class); |
| |
| // TODO: Use more central version of these constants if available. |
| |
| /** JDBC conventional(?) number of fractional decimal digits for REAL. */ |
| private static final int DECIMAL_DIGITS_REAL = 7; |
| /** JDBC conventional(?) number of fractional decimal digits for FLOAT. */ |
| private static final int DECIMAL_DIGITS_FLOAT = DECIMAL_DIGITS_REAL; |
| /** JDBC conventional(?) number of fractional decimal digits for DOUBLE. */ |
| private static final int DECIMAL_DIGITS_DOUBLE = 15; |
| |
| /** Radix used to report precisions of "datetime" types. */ |
| private static final int RADIX_DATETIME = 10; |
| /** Radix used to report precisions of interval types. */ |
| private static final int RADIX_INTERVAL = 10; |
| |
| |
| final DrillConnectionImpl connection; |
| |
| DrillMetaImpl(DrillConnectionImpl connection) { |
| super(connection); |
| this.connection = connection; |
| } |
| |
| private static Signature newSignature(String sql) { |
| return new Signature( |
| new DrillColumnMetaDataList(), |
| sql, |
| Collections.<AvaticaParameter> emptyList(), |
| Collections.<String, Object>emptyMap(), |
| null, // CursorFactory set to null, as SQL requests use DrillCursor |
| Meta.StatementType.SELECT); |
| } |
| |
| private MetaResultSet s(String s) { |
| try { |
| logger.debug("Running {}", s); |
| |
| AvaticaStatement statement = connection.createStatement(); |
| return MetaResultSet.create(connection.id, statement.getId(), true, |
| newSignature(s), null); |
| } catch (Exception e) { |
| // Wrap in RuntimeException because Avatica's abstract method declarations |
| // didn't allow for SQLException! |
| throw new DrillRuntimeException("Failure while attempting to get DatabaseMetadata.", e); |
| } |
| } |
| |
| /** Information about type mapping. */ |
| private static class TypeInfo { |
| private static final Map<Class<?>, TypeInfo> MAPPING = ImmutableMap.<Class<?>, TypeInfo> builder() |
| .put(boolean.class, of(Types.BOOLEAN, "BOOLEAN")) |
| .put(Boolean.class, of(Types.BOOLEAN, "BOOLEAN")) |
| .put(Byte.TYPE, of(Types.TINYINT, "TINYINT")) |
| .put(Byte.class, of(Types.TINYINT, "TINYINT")) |
| .put(Short.TYPE, of(Types.SMALLINT, "SMALLINT")) |
| .put(Short.class, of(Types.SMALLINT, "SMALLINT")) |
| .put(Integer.TYPE, of(Types.INTEGER, "INTEGER")) |
| .put(Integer.class, of(Types.INTEGER, "INTEGER")) |
| .put(Long.TYPE, of(Types.BIGINT, "BIGINT")) |
| .put(Long.class, of(Types.BIGINT, "BIGINT")) |
| .put(Float.TYPE, of(Types.FLOAT, "FLOAT")) |
| .put(Float.class, of(Types.FLOAT, "FLOAT")) |
| .put(Double.TYPE, of(Types.DOUBLE, "DOUBLE")) |
| .put(Double.class, of(Types.DOUBLE, "DOUBLE")) |
| .put(String.class, of(Types.VARCHAR, "CHARACTER VARYING")) |
| .put(BigDecimal.class, of(Types.DECIMAL, "DECIMAL")) |
| .put(java.sql.Date.class, of(Types.DATE, "DATE")) |
| .put(Time.class, of(Types.TIME, "TIME")) |
| .put(Timestamp.class, of(Types.TIMESTAMP, "TIMESTAMP")) |
| .build(); |
| |
| private final int sqlType; |
| private final String sqlTypeName; |
| |
| public TypeInfo(int sqlType, String sqlTypeName) { |
| this.sqlType = sqlType; |
| this.sqlTypeName = sqlTypeName; |
| } |
| |
| private static TypeInfo of(int sqlType, String sqlTypeName) { |
| return new TypeInfo(sqlType, sqlTypeName); |
| } |
| |
| public static TypeInfo get(Class<?> clazz) { |
| return MAPPING.get(clazz); |
| } |
| } |
| |
| /** Metadata describing a column. |
| * Copied from Avatica with several fixes |
| * */ |
| public static class MetaColumn implements Named { |
| public final String tableCat; |
| public final String tableSchem; |
| public final String tableName; |
| public final String columnName; |
| public final int dataType; |
| public final String typeName; |
| public final Integer columnSize; |
| public final Integer bufferLength = null; |
| public final Integer decimalDigits; |
| public final Integer numPrecRadix; |
| public final int nullable; |
| public final String remarks = null; |
| public final String columnDef = null; |
| public final Integer sqlDataType = null; |
| public final Integer sqlDatetimeSub = null; |
| public final Integer charOctetLength; |
| public final int ordinalPosition; |
| @NotNull |
| public final String isNullable; |
| public final String scopeCatalog = null; |
| public final String scopeSchema = null; |
| public final String scopeTable = null; |
| public final Short sourceDataType = null; |
| @NotNull |
| public final String isAutoincrement = ""; |
| @NotNull |
| public final String isGeneratedcolumn = ""; |
| |
| public MetaColumn( |
| String tableCat, |
| String tableSchem, |
| String tableName, |
| String columnName, |
| int dataType, |
| String typeName, |
| Integer columnSize, |
| Integer decimalDigits, |
| Integer numPrecRadix, |
| int nullable, |
| Integer charOctetLength, |
| int ordinalPosition, |
| String isNullable) { |
| this.tableCat = tableCat; |
| this.tableSchem = tableSchem; |
| this.tableName = tableName; |
| this.columnName = columnName; |
| this.dataType = dataType; |
| this.typeName = typeName; |
| this.columnSize = columnSize; |
| this.decimalDigits = decimalDigits; |
| this.numPrecRadix = numPrecRadix; |
| this.nullable = nullable; |
| this.charOctetLength = charOctetLength; |
| this.ordinalPosition = ordinalPosition; |
| this.isNullable = isNullable; |
| } |
| |
| @Override |
| public String getName() { |
| return columnName; |
| } |
| } |
| |
| private static LikeFilter newLikeFilter(final Pat pattern) { |
| if (pattern == null || pattern.s == null) { |
| return null; |
| } |
| |
| return LikeFilter.newBuilder().setPattern(pattern.s).setEscape("\\").build(); |
| } |
| |
| /** |
| * Quote the provided string as a LIKE pattern |
| * |
| * @param v the value to quote |
| * @return a LIKE pattern matching exactly v, or {@code null} if v is {@code null} |
| */ |
| private static Pat quote(String v) { |
| if (v == null) { |
| return null; |
| } |
| |
| StringBuilder sb = new StringBuilder(v.length()); |
| for(int index = 0; index<v.length(); index++) { |
| char c = v.charAt(index); |
| switch(c) { |
| case '%': |
| case '_': |
| case '\\': |
| sb.append('\\').append(c); |
| break; |
| |
| default: |
| sb.append(c); |
| } |
| } |
| |
| return Pat.of(sb.toString()); |
| } |
| |
| // Overriding fieldMetaData as Calcite version create ColumnMetaData with invalid offset |
| protected static ColumnMetaData.StructType drillFieldMetaData(Class<?> clazz) { |
| final List<ColumnMetaData> list = new ArrayList<>(); |
| for (Field field : clazz.getFields()) { |
| if (Modifier.isPublic(field.getModifiers()) |
| && !Modifier.isStatic(field.getModifiers())) { |
| NotNull notNull = field.getAnnotation(NotNull.class); |
| boolean notNullable = (notNull != null || field.getType().isPrimitive()); |
| list.add( |
| drillColumnMetaData( |
| AvaticaUtils.camelToUpper(field.getName()), |
| list.size(), field.getType(), notNullable)); |
| } |
| } |
| return ColumnMetaData.struct(list); |
| } |
| |
| |
| protected static ColumnMetaData drillColumnMetaData(String name, int index, |
| Class<?> type, boolean notNullable) { |
| TypeInfo pair = TypeInfo.get(type); |
| ColumnMetaData.Rep rep = |
| ColumnMetaData.Rep.VALUE_MAP.get(type); |
| ColumnMetaData.AvaticaType scalarType = |
| ColumnMetaData.scalar(pair.sqlType, pair.sqlTypeName, rep); |
| return new ColumnMetaData( |
| index, false, true, false, false, |
| notNullable |
| ? DatabaseMetaData.columnNoNulls |
| : DatabaseMetaData.columnNullable, |
| true, -1, name, name, null, |
| 0, 0, null, null, scalarType, true, false, false, |
| scalarType.columnClassName()); |
| } |
| |
| abstract private class MetadataAdapter<CalciteMetaType, Response, ResponseValue> { |
| private final Class<? extends CalciteMetaType> clazz; |
| |
| public MetadataAdapter(Class<? extends CalciteMetaType> clazz) { |
| this.clazz = clazz; |
| } |
| |
| MetaResultSet getMeta(DrillRpcFuture<Response> future) { |
| Response response; |
| try { |
| response = future.checkedGet(); |
| } catch (RpcException e) { |
| throw new DrillRuntimeException(new SQLException("Failure getting metadata", e)); |
| } |
| |
| // Manage errors |
| if (getStatus(response) != RequestStatus.OK) { |
| DrillPBError error = getError(response); |
| throw new DrillRuntimeException(new SQLException("Failure getting metadata: " + error.getMessage())); |
| } |
| |
| try { |
| List<Object> tables = getResult(response).stream() |
| .map(this::adapt) |
| .collect(Collectors.toList()); |
| |
| Meta.Frame frame = Meta.Frame.create(0, true, tables); |
| StructType fieldMetaData = drillFieldMetaData(clazz); |
| List<String> fieldNames = fieldMetaData.columns.stream() |
| .map(c -> c.columnName) |
| .collect(Collectors.toList()); |
| List<Field> fields = Arrays.stream(clazz.getFields()) |
| .filter(field -> Modifier.isPublic(field.getModifiers()) && !Modifier.isStatic(field.getModifiers())) |
| .collect(Collectors.toList()); |
| Meta.Signature signature = Meta.Signature.create(fieldMetaData.columns, "", Collections.emptyList(), |
| CursorFactory.record(clazz, fields, fieldNames), Meta.StatementType.SELECT); |
| |
| AvaticaStatement statement = connection.createStatement(); |
| return MetaResultSet.create(connection.id, statement.getId(), true, |
| signature, frame); |
| } catch (SQLException e) { |
| // Wrap in RuntimeException because Avatica's abstract method declarations |
| // didn't allow for SQLException! |
| throw new DrillRuntimeException(new SQLException("Failure while attempting to get DatabaseMetadata.", e)); |
| } |
| } |
| |
| abstract protected RequestStatus getStatus(Response response); |
| abstract protected DrillPBError getError(Response response); |
| abstract protected List<ResponseValue> getResult(Response response); |
| abstract protected CalciteMetaType adapt(ResponseValue protoValue); |
| } |
| |
| private MetaResultSet clientGetTables(String catalog, final Pat schemaPattern, final Pat tableNamePattern, |
| final List<String> typeList) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("select " |
| + "TABLE_CATALOG as TABLE_CAT, " |
| + "TABLE_SCHEMA as TABLE_SCHEM, " |
| + "TABLE_NAME, " |
| + "TABLE_TYPE, " |
| + "'' as REMARKS, " |
| + "'' as TYPE_CAT, " |
| + "'' as TYPE_SCHEM, " |
| + "'' as TYPE_NAME, " |
| + "'' as SELF_REFERENCING_COL_NAME, " |
| + "'' as REF_GENERATION " |
| + "FROM INFORMATION_SCHEMA.`TABLES` WHERE 1=1 "); |
| |
| if (catalog != null) { |
| sb.append(" AND TABLE_CATALOG = '" + DrillStringUtils.escapeSql(catalog) + "' "); |
| } |
| |
| if (schemaPattern.s != null) { |
| sb.append(" AND TABLE_SCHEMA like '" + DrillStringUtils.escapeSql(schemaPattern.s) + "'"); |
| } |
| |
| if (tableNamePattern.s != null) { |
| sb.append(" AND TABLE_NAME like '" + DrillStringUtils.escapeSql(tableNamePattern.s) + "'"); |
| } |
| |
| if (typeList != null && typeList.size() > 0) { |
| sb.append("AND ("); |
| for (int t = 0; t < typeList.size(); t++) { |
| if (t != 0) { |
| sb.append(" OR "); |
| } |
| sb.append(" TABLE_TYPE LIKE '" + DrillStringUtils.escapeSql(typeList.get(t)) + "' "); |
| } |
| sb.append(")"); |
| } |
| |
| sb.append(" ORDER BY TABLE_TYPE, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME"); |
| |
| return s(sb.toString()); |
| } |
| |
| private MetaResultSet serverGetTables(String catalog, final Pat schemaPattern, final Pat tableNamePattern, |
| final List<String> typeList) { |
| // Catalog is not a pattern |
| final LikeFilter catalogNameFilter = newLikeFilter(quote(catalog)); |
| final LikeFilter schemaNameFilter = newLikeFilter(schemaPattern); |
| final LikeFilter tableNameFilter = newLikeFilter(tableNamePattern); |
| |
| return new MetadataAdapter<MetaImpl.MetaTable, GetTablesResp, TableMetadata>(MetaTable.class) { |
| |
| @Override |
| protected RequestStatus getStatus(GetTablesResp response) { |
| return response.getStatus(); |
| }; |
| |
| @Override |
| protected DrillPBError getError(GetTablesResp response) { |
| return response.getError(); |
| }; |
| |
| @Override |
| protected List<TableMetadata> getResult(GetTablesResp response) { |
| return response.getTablesList(); |
| } |
| |
| @Override |
| protected MetaImpl.MetaTable adapt(TableMetadata protoValue) { |
| return new MetaImpl.MetaTable(protoValue.getCatalogName(), protoValue.getSchemaName(), protoValue.getTableName(), protoValue.getType()); |
| }; |
| }.getMeta(connection.getClient().getTables(catalogNameFilter, schemaNameFilter, tableNameFilter, typeList)); |
| } |
| |
| /** |
| * Implements {@link DatabaseMetaData#getTables}. |
| */ |
| @Override |
| public MetaResultSet getTables(ConnectionHandle ch, |
| String catalog, |
| Pat schemaPattern, |
| Pat tableNamePattern, |
| List<String> typeList) { |
| if (connection.getConfig().isServerMetadataDisabled() || ! connection.getClient().getSupportedMethods().contains(ServerMethod.GET_TABLES)) { |
| return clientGetTables(catalog, schemaPattern, tableNamePattern, typeList); |
| } |
| |
| return serverGetTables(catalog, schemaPattern, tableNamePattern, typeList); |
| } |
| |
| private MetaResultSet clientGetColumns(String catalog, Pat schemaPattern, |
| Pat tableNamePattern, Pat columnNamePattern) { |
| StringBuilder sb = new StringBuilder(); |
| // TODO: Resolve the various questions noted below. |
| sb.append( |
| "SELECT " |
| // getColumns INFORMATION_SCHEMA.COLUMNS getColumns() |
| // column source column or column name |
| // number expression |
| // ------- ------------------------ ------------- |
| + /* 1 */ "\n TABLE_CATALOG as TABLE_CAT, " |
| + /* 2 */ "\n TABLE_SCHEMA as TABLE_SCHEM, " |
| + /* 3 */ "\n TABLE_NAME as TABLE_NAME, " |
| + /* 4 */ "\n COLUMN_NAME as COLUMN_NAME, " |
| |
| /* 5 DATA_TYPE */ |
| // TODO: Resolve the various questions noted below for DATA_TYPE. |
| + "\n CASE DATA_TYPE " |
| // (All values in JDBC 4.0/Java 7 java.sql.Types except for types.NULL:) |
| |
| + "\n WHEN 'ARRAY' THEN " + Types.ARRAY |
| |
| + "\n WHEN 'BIGINT' THEN " + Types.BIGINT |
| + "\n WHEN 'BINARY' THEN " + Types.BINARY |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'BINARY LARGE OBJECT' THEN " + Types.BLOB |
| + "\n WHEN 'BINARY VARYING' THEN " + Types.VARBINARY |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'BIT' THEN " + Types.BIT |
| + "\n WHEN 'BOOLEAN' THEN " + Types.BOOLEAN |
| |
| + "\n WHEN 'CHARACTER' THEN " + Types.CHAR |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'CHARACTER LARGE OBJECT' THEN " + Types.CLOB |
| + "\n WHEN 'CHARACTER VARYING' THEN " + Types.VARCHAR |
| |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'DATALINK' THEN " + Types.DATALINK |
| + "\n WHEN 'DATE' THEN " + Types.DATE |
| + "\n WHEN 'DECIMAL' THEN " + Types.DECIMAL |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'DISTINCT' THEN " + Types.DISTINCT |
| + "\n WHEN 'DOUBLE', 'DOUBLE PRECISION' THEN " + Types.DOUBLE |
| |
| + "\n WHEN 'FLOAT' THEN " + Types.FLOAT |
| |
| + "\n WHEN 'INTEGER' THEN " + Types.INTEGER |
| + "\n WHEN 'INTERVAL' THEN " + Types.OTHER |
| |
| // Resolve: Not seen in Drill yet. Can it ever appear?: |
| + "\n WHEN 'JAVA_OBJECT' THEN " + Types.JAVA_OBJECT |
| |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'LONGNVARCHAR' THEN " + Types.LONGNVARCHAR |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'LONGVARBINARY' THEN " + Types.LONGVARBINARY |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'LONGVARCHAR' THEN " + Types.LONGVARCHAR |
| |
| + "\n WHEN 'MAP' THEN " + Types.OTHER |
| |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'NATIONAL CHARACTER' THEN " + Types.NCHAR |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'NATIONAL CHARACTER LARGE OBJECT' " |
| + "\n THEN " + Types.NCLOB |
| // TODO: Resolve following about NULL (and then update comment and code): |
| // It is not clear whether Types.NULL can represent a type (perhaps the |
| // type of the literal NULL when no further type information is known?) or |
| // whether 'NULL' can appear in INFORMATION_SCHEMA.COLUMNS.DATA_TYPE. |
| // For now, since it shouldn't hurt, include 'NULL'/Types.NULL in mapping. |
| + "\n WHEN 'NULL' THEN " + Types.NULL |
| // (No NUMERIC--Drill seems to map any to DECIMAL currently.) |
| + "\n WHEN 'NUMERIC' THEN " + Types.NUMERIC |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'NATIONAL CHARACTER' THEN " + Types.NCHAR |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'NATIONAL CHARACTER VARYING' THEN " + Types.NVARCHAR |
| |
| // Resolve: Unexpectedly, has appeared in Drill. Should it? |
| + "\n WHEN 'OTHER' THEN " + Types.OTHER |
| |
| + "\n WHEN 'REAL' THEN " + Types.REAL |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'REF' THEN " + Types.REF |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'ROWID' THEN " + Types.ROWID |
| |
| + "\n WHEN 'SMALLINT' THEN " + Types.SMALLINT |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| + "\n WHEN 'SQLXML' THEN " + Types.SQLXML |
| + "\n WHEN 'STRUCT' THEN " + Types.STRUCT |
| |
| + "\n WHEN 'TIME' THEN " + Types.TIME |
| + "\n WHEN 'TIMESTAMP' THEN " + Types.TIMESTAMP |
| + "\n WHEN 'TINYINT' THEN " + Types.TINYINT |
| |
| + "\n ELSE " + Types.OTHER |
| + "\n END as DATA_TYPE, " |
| |
| + /* 6 */ "\n DATA_TYPE as TYPE_NAME, " |
| |
| /* 7 COLUMN_SIZE */ |
| /* "... COLUMN_SIZE .... |
| * For numeric data, this is the maximum precision. |
| * For character data, this is the length in characters. |
| * For datetime datatypes, this is the length in characters of the String |
| * representation (assuming the maximum allowed precision of the |
| * fractional seconds component). |
| * For binary data, this is the length in bytes. |
| * For the ROWID datatype, this is the length in bytes. |
| * Null is returned for data types where the column size is not applicable." |
| * |
| * Note: "Maximum precision" seems to mean the maximum number of |
| * significant digits that can appear (not the number of decimal digits |
| * that can be counted on, and not the maximum number of (decimal) |
| * characters needed to display a value). |
| */ |
| + "\n CASE DATA_TYPE " |
| // 0. "For boolean and bit ... 1": |
| + "\n WHEN 'BOOLEAN', 'BIT'" |
| + "\n THEN 1 " |
| |
| // 1. "For numeric data, ... the maximum precision": |
| + "\n WHEN 'TINYINT', 'SMALLINT', 'INTEGER', 'BIGINT', " |
| + "\n 'DECIMAL', 'NUMERIC', " |
| + "\n 'REAL', 'FLOAT', 'DOUBLE' " |
| + "\n THEN NUMERIC_PRECISION " |
| |
| // 2. "For character data, ... the length in characters": |
| + "\n WHEN 'CHARACTER', 'CHARACTER VARYING' " |
| + "\n THEN CHARACTER_MAXIMUM_LENGTH " |
| |
| // 3. "For datetime datatypes ... length ... String representation |
| // (assuming the maximum ... precision of ... fractional seconds ...)": |
| // SQL datetime types: |
| + "\n WHEN 'DATE' THEN 10 " // YYYY-MM-DD |
| + "\n WHEN 'TIME' THEN " |
| + "\n CASE " |
| + "\n WHEN DATETIME_PRECISION > 0 " // HH:MM:SS.sss |
| + "\n THEN 8 + 1 + DATETIME_PRECISION" |
| + "\n ELSE 8" // HH:MM:SS |
| + "\n END " |
| + "\n WHEN 'TIMESTAMP' THEN " |
| + "\n CASE " // date + "T" + time |
| + "\n WHEN DATETIME_PRECISION > 0 " |
| + " THEN 10 + 1 + 8 + 1 + DATETIME_PRECISION" |
| + "\n ELSE 10 + 1 + 8" |
| + "\n END " |
| // SQL interval types: |
| // Note: Not addressed by JDBC 4.1; providing length of current string |
| // representation (not length of, say, interval literal). |
| + "\n WHEN 'INTERVAL' THEN " |
| + "\n INTERVAL_PRECISION " |
| + "\n + " |
| + "\n CASE INTERVAL_TYPE " |
| // a. Single field, not SECOND: |
| + "\n WHEN 'YEAR', 'MONTH', 'DAY' THEN 2 " // like P...Y |
| + "\n WHEN 'HOUR', 'MINUTE' THEN 3 " // like PT...M |
| // b. Two adjacent fields, no SECOND: |
| + "\n WHEN 'YEAR TO MONTH' THEN 5 " // P...Y12M |
| + "\n WHEN 'DAY TO HOUR' THEN 6 " // P...DT12H |
| + "\n WHEN 'HOUR TO MINUTE' THEN 6 " // PT...H12M |
| // c. Three contiguous fields, no SECOND: |
| + "\n WHEN 'DAY TO MINUTE' THEN 9 " // P...DT12H12M |
| // d. With SECOND field: |
| + "\n ELSE " |
| + "\n CASE INTERVAL_TYPE " |
| + "\n WHEN 'DAY TO SECOND' THEN 12 " // P...DT12H12M12...S |
| + "\n WHEN 'HOUR TO SECOND' THEN 9 " // PT...H12M12...S |
| + "\n WHEN 'MINUTE TO SECOND' THEN 6 " // PT...M12...S |
| + "\n WHEN 'SECOND' THEN 3 " // PT......S |
| + "\n ELSE " // Make net result be -1: |
| // WORKAROUND: This "0" is to work around Drill's failure to support |
| // unary minus syntax (negation): |
| + "\n 0-INTERVAL_PRECISION - 1 " |
| + "\n END " |
| + "\n + " |
| + "\n DATETIME_PRECISION" |
| + "\n + " |
| + "\n CASE " // If frac. digits, also add 1 for decimal point. |
| + "\n WHEN DATETIME_PRECISION > 0 THEN 1" |
| + "\n ELSE 0 " |
| + "\n END" |
| // - For INTERVAL ... TO SECOND(0): "P...DT12H12M12S" |
| + "\n END " |
| |
| // 4. "For binary data, ... the length in bytes": |
| + "\n WHEN 'BINARY', 'BINARY VARYING' " |
| + "\n THEN CHARACTER_MAXIMUM_LENGTH " |
| |
| // 5. "For ... ROWID datatype...": Not in Drill? |
| |
| // 6. "Null ... for data types [for which] ... not applicable.": |
| + "\n ELSE NULL " |
| + "\n END as COLUMN_SIZE, " |
| |
| + /* 8 */ "\n CHARACTER_MAXIMUM_LENGTH as BUFFER_LENGTH, " |
| |
| /* 9 DECIMAL_DIGITS */ |
| + "\n CASE DATA_TYPE" |
| + "\n WHEN 'TINYINT', 'SMALLINT', 'INTEGER', 'BIGINT', " |
| + "\n 'DECIMAL', 'NUMERIC' THEN NUMERIC_SCALE " |
| + "\n WHEN 'REAL' THEN " + DECIMAL_DIGITS_REAL |
| + "\n WHEN 'FLOAT' THEN " + DECIMAL_DIGITS_FLOAT |
| + "\n WHEN 'DOUBLE' THEN " + DECIMAL_DIGITS_DOUBLE |
| + "\n WHEN 'DATE', 'TIME', 'TIMESTAMP' THEN DATETIME_PRECISION " |
| + "\n WHEN 'INTERVAL' THEN DATETIME_PRECISION " |
| + "\n END as DECIMAL_DIGITS, " |
| |
| /* 10 NUM_PREC_RADIX */ |
| + "\n CASE DATA_TYPE " |
| + "\n WHEN 'TINYINT', 'SMALLINT', 'INTEGER', 'BIGINT', " |
| + "\n 'DECIMAL', 'NUMERIC', " |
| + "\n 'REAL', 'FLOAT', 'DOUBLE' THEN NUMERIC_PRECISION_RADIX " |
| // (NUMERIC_PRECISION_RADIX is NULL for these:) |
| + "\n WHEN 'INTERVAL' THEN " + RADIX_INTERVAL |
| + "\n WHEN 'DATE', 'TIME', 'TIMESTAMP' THEN " + RADIX_DATETIME |
| + "\n ELSE NULL" |
| + "\n END as NUM_PREC_RADIX, " |
| |
| /* 11 NULLABLE */ |
| + "\n CASE IS_NULLABLE " |
| + "\n WHEN 'YES' THEN " + DatabaseMetaData.columnNullable |
| + "\n WHEN 'NO' THEN " + DatabaseMetaData.columnNoNulls |
| + "\n WHEN '' THEN " + DatabaseMetaData.columnNullableUnknown |
| + "\n ELSE -1" |
| + "\n END as NULLABLE, " |
| |
| + /* 12 */ "\n CAST( NULL as VARCHAR ) as REMARKS, " |
| + /* 13 */ "\n COLUMN_DEFAULT as COLUMN_DEF, " |
| + /* 14 */ "\n 0 as SQL_DATA_TYPE, " |
| + /* 15 */ "\n 0 as SQL_DATETIME_SUB, " |
| |
| /* 16 CHAR_OCTET_LENGTH */ |
| + "\n CASE DATA_TYPE" |
| + "\n WHEN 'CHARACTER', " |
| + "\n 'CHARACTER VARYING', " |
| + "\n 'NATIONAL CHARACTER', " |
| + "\n 'NATIONAL CHARACTER VARYING' " |
| + "\n THEN CHARACTER_OCTET_LENGTH " |
| + "\n ELSE NULL " |
| + "\n END as CHAR_OCTET_LENGTH, " |
| |
| + /* 17 */ "\n ORDINAL_POSITION as ORDINAL_POSITION, " |
| + /* 18 */ "\n IS_NULLABLE as IS_NULLABLE, " |
| + /* 19 */ "\n CAST( NULL as VARCHAR ) as SCOPE_CATALOG, " |
| + /* 20 */ "\n CAST( NULL as VARCHAR ) as SCOPE_SCHEMA, " |
| + /* 21 */ "\n CAST( NULL as VARCHAR ) as SCOPE_TABLE, " |
| // TODO: Change to SMALLINT when it's implemented (DRILL-2470): |
| + /* 22 */ "\n CAST( NULL as INTEGER ) as SOURCE_DATA_TYPE, " |
| + /* 23 */ "\n '' as IS_AUTOINCREMENT, " |
| + /* 24 */ "\n '' as IS_GENERATEDCOLUMN " |
| |
| + "\n FROM INFORMATION_SCHEMA.COLUMNS " |
| + "\n WHERE 1=1 "); |
| |
| if (catalog != null) { |
| sb.append("\n AND TABLE_CATALOG = '" + DrillStringUtils.escapeSql(catalog) + "'"); |
| } |
| if (schemaPattern.s != null) { |
| sb.append("\n AND TABLE_SCHEMA like '" + DrillStringUtils.escapeSql(schemaPattern.s) + "'"); |
| } |
| if (tableNamePattern.s != null) { |
| sb.append("\n AND TABLE_NAME like '" + DrillStringUtils.escapeSql(tableNamePattern.s) + "'"); |
| } |
| if (columnNamePattern.s != null) { |
| sb.append("\n AND COLUMN_NAME like '" + DrillStringUtils.escapeSql(columnNamePattern.s) + "'"); |
| } |
| |
| sb.append("\n ORDER BY TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME"); |
| |
| return s(sb.toString()); |
| } |
| |
| private MetaResultSet serverGetColumns(String catalog, Pat schemaPattern, |
| Pat tableNamePattern, Pat columnNamePattern) { |
| final LikeFilter catalogNameFilter = newLikeFilter(quote(catalog)); |
| final LikeFilter schemaNameFilter = newLikeFilter(schemaPattern); |
| final LikeFilter tableNameFilter = newLikeFilter(tableNamePattern); |
| final LikeFilter columnNameFilter = newLikeFilter(columnNamePattern); |
| |
| return new MetadataAdapter<MetaColumn, GetColumnsResp, ColumnMetadata>(MetaColumn.class) { |
| @Override |
| protected RequestStatus getStatus(GetColumnsResp response) { |
| return response.getStatus(); |
| } |
| |
| @Override |
| protected DrillPBError getError(GetColumnsResp response) { |
| return response.getError(); |
| } |
| |
| @Override |
| protected List<ColumnMetadata> getResult(GetColumnsResp response) { |
| return response.getColumnsList(); |
| }; |
| |
| private int getDataType(ColumnMetadata value) { |
| switch (value.getDataType()) { |
| case "ARRAY": |
| return Types.ARRAY; |
| |
| case "BIGINT": |
| return Types.BIGINT; |
| case "BINARY": |
| return Types.BINARY; |
| case "BINARY LARGE OBJECT": |
| return Types.BLOB; |
| case "BINARY VARYING": |
| return Types.VARBINARY; |
| case "BIT": |
| return Types.BIT; |
| case "BOOLEAN": |
| return Types.BOOLEAN; |
| case "CHARACTER": |
| return Types.CHAR; |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| case "CHARACTER LARGE OBJECT": |
| return Types.CLOB; |
| case "CHARACTER VARYING": |
| return Types.VARCHAR; |
| |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| case "DATALINK": |
| return Types.DATALINK; |
| case "DATE": |
| return Types.DATE; |
| case "DECIMAL": |
| return Types.DECIMAL; |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| case "DISTINCT": |
| return Types.DISTINCT; |
| case "DOUBLE": |
| case "DOUBLE PRECISION": |
| return Types.DOUBLE; |
| |
| case "FLOAT": |
| return Types.FLOAT; |
| |
| case "INTEGER": |
| return Types.INTEGER; |
| case "INTERVAL": |
| return Types.OTHER; |
| |
| // Resolve: Not seen in Drill yet. Can it ever appear?: |
| case "JAVA_OBJECT": |
| return Types.JAVA_OBJECT; |
| |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| case "LONGNVARCHAR": |
| return Types.LONGNVARCHAR; |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| case "LONGVARBINARY": |
| return Types.LONGVARBINARY; |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| case "LONGVARCHAR": |
| return Types.LONGVARCHAR; |
| |
| case "MAP": |
| return Types.OTHER; |
| |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| case "NATIONAL CHARACTER": |
| return Types.NCHAR; |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| case "NATIONAL CHARACTER LARGE OBJECT": |
| return Types.NCLOB; |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| case "NATIONAL CHARACTER VARYING": |
| return Types.NVARCHAR; |
| |
| // TODO: Resolve following about NULL (and then update comment and |
| // code): |
| // It is not clear whether Types.NULL can represent a type (perhaps the |
| // type of the literal NULL when no further type information is known?) |
| // or |
| // whether 'NULL' can appear in INFORMATION_SCHEMA.COLUMNS.DATA_TYPE. |
| // For now, since it shouldn't hurt, include 'NULL'/Types.NULL in |
| // mapping. |
| case "NULL": |
| return Types.NULL; |
| // (No NUMERIC--Drill seems to map any to DECIMAL currently.) |
| case "NUMERIC": |
| return Types.NUMERIC; |
| |
| // Resolve: Unexpectedly, has appeared in Drill. Should it? |
| case "OTHER": |
| return Types.OTHER; |
| |
| case "REAL": |
| return Types.REAL; |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| case "REF": |
| return Types.REF; |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| case "ROWID": |
| return Types.ROWID; |
| |
| case "SMALLINT": |
| return Types.SMALLINT; |
| // Resolve: Not seen in Drill yet. Can it appear?: |
| case "SQLXML": |
| return Types.SQLXML; |
| case "STRUCT": |
| return Types.STRUCT; |
| |
| case "TIME": |
| return Types.TIME; |
| case "TIMESTAMP": |
| return Types.TIMESTAMP; |
| case "TINYINT": |
| return Types.TINYINT; |
| |
| default: |
| return Types.OTHER; |
| } |
| } |
| |
| Integer getDecimalDigits(ColumnMetadata value) { |
| switch(value.getDataType()) { |
| case "TINYINT": |
| case "SMALLINT": |
| case "INTEGER": |
| case "BIGINT": |
| case "DECIMAL": |
| case "NUMERIC": |
| return value.hasNumericScale() ? value.getNumericScale() : null; |
| |
| case "REAL": |
| return DECIMAL_DIGITS_REAL; |
| |
| case "FLOAT": |
| return DECIMAL_DIGITS_FLOAT; |
| |
| case "DOUBLE": |
| return DECIMAL_DIGITS_DOUBLE; |
| |
| case "DATE": |
| case "TIME": |
| case "TIMESTAMP": |
| case "INTERVAL": |
| return value.getDateTimePrecision(); |
| |
| default: |
| return null; |
| } |
| } |
| |
| private Integer getNumPrecRadix(ColumnMetadata value) { |
| switch(value.getDataType()) { |
| case "TINYINT": |
| case "SMALLINT": |
| case "INTEGER": |
| case "BIGINT": |
| case "DECIMAL": |
| case "NUMERIC": |
| case "REAL": |
| case "FLOAT": |
| case "DOUBLE": |
| return value.getNumericPrecisionRadix(); |
| |
| case "INTERVAL": |
| return RADIX_INTERVAL; |
| |
| case "DATE": |
| case "TIME": |
| case "TIMESTAMP": |
| return RADIX_DATETIME; |
| |
| default: |
| return null; |
| } |
| } |
| |
| private int getNullable(ColumnMetadata value) { |
| if (!value.hasIsNullable()) { |
| return DatabaseMetaData.columnNullableUnknown; |
| } |
| return value.getIsNullable() ? DatabaseMetaData.columnNullable : DatabaseMetaData.columnNoNulls; |
| } |
| |
| private String getIsNullable(ColumnMetadata value) { |
| if (!value.hasIsNullable()) { |
| return ""; |
| } |
| return value.getIsNullable() ? "YES" : "NO"; |
| } |
| |
| private Integer getCharOctetLength(ColumnMetadata value) { |
| if (!value.hasCharMaxLength()) { |
| return null; |
| } |
| |
| switch(value.getDataType()) { |
| case "CHARACTER": |
| case "CHARACTER LARGE OBJECT": |
| case "CHARACTER VARYING": |
| case "LONGVARCHAR": |
| case "LONGNVARCHAR": |
| case "NATIONAL CHARACTER": |
| case "NATIONAL CHARACTER LARGE OBJECT": |
| case "NATIONAL CHARACTER VARYING": |
| return value.getCharOctetLength(); |
| |
| default: |
| return null; |
| } |
| } |
| |
| @Override |
| protected MetaColumn adapt(ColumnMetadata value) { |
| return new MetaColumn( |
| value.getCatalogName(), |
| value.getSchemaName(), |
| value.getTableName(), |
| value.getColumnName(), |
| getDataType(value), // It might require the full SQL type |
| value.getDataType(), |
| value.getColumnSize(), |
| getDecimalDigits(value), |
| getNumPrecRadix(value), |
| getNullable(value), |
| getCharOctetLength(value), |
| value.getOrdinalPosition(), |
| getIsNullable(value)); |
| } |
| }.getMeta(connection.getClient().getColumns(catalogNameFilter, schemaNameFilter, tableNameFilter, columnNameFilter)); |
| } |
| |
| /** |
| * Implements {@link DatabaseMetaData#getColumns}. |
| */ |
| @Override |
| public MetaResultSet getColumns(ConnectionHandle ch, String catalog, Pat schemaPattern, Pat tableNamePattern, Pat columnNamePattern) { |
| if (connection.getConfig().isServerMetadataDisabled() || ! connection.getClient().getSupportedMethods().contains(ServerMethod.GET_COLUMNS)) { |
| return clientGetColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern); |
| } |
| |
| return serverGetColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern); |
| } |
| |
| |
| private MetaResultSet serverGetSchemas(String catalog, Pat schemaPattern) { |
| final LikeFilter catalogNameFilter = newLikeFilter(quote(catalog)); |
| final LikeFilter schemaNameFilter = newLikeFilter(schemaPattern); |
| |
| return new MetadataAdapter<MetaImpl.MetaSchema, GetSchemasResp, SchemaMetadata>(MetaImpl.MetaSchema.class) { |
| @Override |
| protected RequestStatus getStatus(GetSchemasResp response) { |
| return response.getStatus(); |
| } |
| |
| @Override |
| protected List<SchemaMetadata> getResult(GetSchemasResp response) { |
| return response.getSchemasList(); |
| } |
| |
| @Override |
| protected DrillPBError getError(GetSchemasResp response) { |
| return response.getError(); |
| } |
| |
| @Override |
| protected MetaSchema adapt(SchemaMetadata value) { |
| return new MetaImpl.MetaSchema(value.getCatalogName(), value.getSchemaName()); |
| } |
| }.getMeta(connection.getClient().getSchemas(catalogNameFilter, schemaNameFilter)); |
| } |
| |
| |
| private MetaResultSet clientGetSchemas(String catalog, Pat schemaPattern) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("select " |
| + "SCHEMA_NAME as TABLE_SCHEM, " |
| + "CATALOG_NAME as TABLE_CAT " |
| + " FROM INFORMATION_SCHEMA.SCHEMATA WHERE 1=1 "); |
| |
| if (catalog != null) { |
| sb.append(" AND CATALOG_NAME = '" + DrillStringUtils.escapeSql(catalog) + "' "); |
| } |
| if (schemaPattern.s != null) { |
| sb.append(" AND SCHEMA_NAME like '" + DrillStringUtils.escapeSql(schemaPattern.s) + "'"); |
| } |
| sb.append(" ORDER BY CATALOG_NAME, SCHEMA_NAME"); |
| |
| return s(sb.toString()); |
| } |
| |
| /** |
| * Implements {@link DatabaseMetaData#getSchemas}. |
| */ |
| @Override |
| public MetaResultSet getSchemas(ConnectionHandle ch, String catalog, Pat schemaPattern) { |
| if (connection.getConfig().isServerMetadataDisabled() || ! connection.getClient().getSupportedMethods().contains(ServerMethod.GET_SCHEMAS)) { |
| return clientGetSchemas(catalog, schemaPattern); |
| } |
| |
| return serverGetSchemas(catalog, schemaPattern); |
| } |
| |
| private MetaResultSet serverGetCatalogs() { |
| return new MetadataAdapter<MetaImpl.MetaCatalog, GetCatalogsResp, CatalogMetadata>(MetaImpl.MetaCatalog.class) { |
| @Override |
| protected RequestStatus getStatus(GetCatalogsResp response) { |
| return response.getStatus(); |
| } |
| |
| @Override |
| protected List<CatalogMetadata> getResult(GetCatalogsResp response) { |
| return response.getCatalogsList(); |
| } |
| |
| @Override |
| protected DrillPBError getError(GetCatalogsResp response) { |
| return response.getError(); |
| } |
| |
| @Override |
| protected MetaImpl.MetaCatalog adapt(CatalogMetadata protoValue) { |
| return new MetaImpl.MetaCatalog(protoValue.getCatalogName()); |
| } |
| }.getMeta(connection.getClient().getCatalogs(null)); |
| } |
| |
| private MetaResultSet clientGetCatalogs() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("select " |
| + "CATALOG_NAME as TABLE_CAT " |
| + " FROM INFORMATION_SCHEMA.CATALOGS "); |
| |
| sb.append(" ORDER BY CATALOG_NAME"); |
| |
| return s(sb.toString()); |
| } |
| |
| /** |
| * Implements {@link DatabaseMetaData#getCatalogs}. |
| */ |
| @Override |
| public MetaResultSet getCatalogs(ConnectionHandle ch) { |
| if (connection.getConfig().isServerMetadataDisabled() || ! connection.getClient().getSupportedMethods().contains(ServerMethod.GET_CATALOGS)) { |
| return clientGetCatalogs(); |
| } |
| |
| return serverGetCatalogs(); |
| } |
| |
| interface Named { |
| String getName(); |
| } |
| |
| @Override |
| public StatementHandle prepare(ConnectionHandle ch, String sql, long maxRowCount) { |
| StatementHandle result = super.createStatement(ch); |
| result.signature = newSignature(sql); |
| |
| return result; |
| } |
| |
| @Override |
| @SuppressWarnings("deprecation") |
| public ExecuteResult prepareAndExecute(StatementHandle h, String sql, long maxRowCount, PrepareCallback callback) { |
| final Signature signature = newSignature(sql); |
| try { |
| synchronized (callback.getMonitor()) { |
| callback.clear(); |
| callback.assign(signature, null, -1); |
| } |
| callback.execute(); |
| final MetaResultSet metaResultSet = MetaResultSet.create(h.connectionId, h.id, false, signature, null); |
| return new ExecuteResult(Collections.singletonList(metaResultSet)); |
| } catch(SQLException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| @Override |
| public ExecuteResult prepareAndExecute(final StatementHandle handle, final String sql, final long maxRowCount, |
| int maxRowsInFirstFrame, final PrepareCallback callback) throws NoSuchStatementException { |
| return prepareAndExecute(handle, sql, maxRowCount, callback); |
| } |
| |
| @Override |
| public ExecuteBatchResult prepareAndExecuteBatch(StatementHandle statementHandle, List<String> list) throws NoSuchStatementException { |
| throw new UnsupportedOperationException(this.getClass().getSimpleName()); |
| } |
| |
| @Override |
| public ExecuteBatchResult executeBatch(StatementHandle statementHandle, List<List<TypedValue>> list) throws NoSuchStatementException { |
| throw new UnsupportedOperationException(this.getClass().getSimpleName()); |
| } |
| |
| @Override |
| public Frame fetch(StatementHandle statementHandle, long l, int i) throws NoSuchStatementException, MissingResultsException { |
| throw new UnsupportedOperationException(this.getClass().getSimpleName()); |
| } |
| |
| @Override |
| @SuppressWarnings("deprecation") |
| public ExecuteResult execute(StatementHandle statementHandle, |
| List<TypedValue> list, long l) throws NoSuchStatementException { |
| return new ExecuteResult(Collections.singletonList( |
| MetaResultSet.create(statementHandle.connectionId, statementHandle.id, |
| true, statementHandle.signature, null))); |
| } |
| |
| @Override |
| public ExecuteResult execute(StatementHandle statementHandle, |
| List<TypedValue> list, int i) throws NoSuchStatementException { |
| return execute(statementHandle, list, (long) i); |
| } |
| |
| @Override |
| public void closeStatement(StatementHandle h) { |
| // Nothing |
| } |
| |
| @Override |
| public boolean syncResults(StatementHandle statementHandle, QueryState queryState, long l) throws NoSuchStatementException { |
| throw new UnsupportedOperationException(this.getClass().getSimpleName()); |
| } |
| |
| @Override |
| public void commit(ConnectionHandle connectionHandle) { |
| throw new UnsupportedOperationException(this.getClass().getSimpleName()); |
| } |
| |
| @Override |
| public void rollback(ConnectionHandle connectionHandle) { |
| throw new UnsupportedOperationException(this.getClass().getSimpleName()); |
| } |
| |
| } |