All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.druid.sql.avatica.AbstractDruidJdbcStatement Maven / Gradle / Ivy

There is a newer version: 31.0.0
Show newest version
/*
 * 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.druid.sql.avatica;

import com.google.common.base.Preconditions;
import org.apache.calcite.avatica.AvaticaParameter;
import org.apache.calcite.avatica.ColumnMetaData;
import org.apache.calcite.avatica.Meta;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.druid.java.util.common.ISE;
import org.apache.druid.sql.avatica.DruidJdbcResultSet.ResultFetcherFactory;
import org.apache.druid.sql.calcite.planner.Calcites;
import org.apache.druid.sql.calcite.planner.PrepareResult;

import java.io.Closeable;
import java.sql.DatabaseMetaData;
import java.util.ArrayList;
import java.util.List;

/**
 * Common implementation for the JDBC {@code Statement} and
 * {@code PreparedStatement} implementations in Druid. Statement use
 * {@link DruidJdbcResultSet} objects to iterate through rows: zero
 * or one may be open at any time, and a single statement supports
 * multiple result sets concurrently. Druid closes the result set after
 * the last batch in compliance with this note on page 137 of the
 * 
 * JDBC 4.1 specification:
 * 

* Some JDBC driver implementations may also implicitly close the * ResultSet when the ResultSet type is TYPE_FORWARD_ONLY and the next * method of ResultSet returns false. */ public abstract class AbstractDruidJdbcStatement implements Closeable { public static final long START_OFFSET = 0; protected final String connectionId; protected final int statementId; protected final ResultFetcherFactory fetcherFactory; protected Throwable throwable; protected DruidJdbcResultSet resultSet; public AbstractDruidJdbcStatement( final String connectionId, final int statementId, final ResultFetcherFactory fetcherFactory ) { this.connectionId = Preconditions.checkNotNull(connectionId, "connectionId"); this.statementId = statementId; this.fetcherFactory = fetcherFactory; } protected static Meta.Signature createSignature( final PrepareResult prepareResult, final String sql ) { List params = new ArrayList<>(); final RelDataType parameterRowType = prepareResult.getParameterRowType(); for (RelDataTypeField field : parameterRowType.getFieldList()) { RelDataType type = field.getType(); params.add(createParameter(field, type)); } return Meta.Signature.create( createColumnMetaData(prepareResult.getReturnedRowType()), sql, params, Meta.CursorFactory.ARRAY, Meta.StatementType.SELECT // We only support SELECT ); } private static AvaticaParameter createParameter( final RelDataTypeField field, final RelDataType type ) { // signed is always false because no way to extract from RelDataType, and the only usage of this AvaticaParameter // constructor I can find, in CalcitePrepareImpl, does it this way with hard coded false return new AvaticaParameter( false, type.getPrecision(), type.getScale(), type.getSqlTypeName().getJdbcOrdinal(), type.getSqlTypeName().getName(), Calcites.sqlTypeNameJdbcToJavaClass(type.getSqlTypeName()).getName(), field.getName() ); } public static List createColumnMetaData(final RelDataType rowType) { final List columns = new ArrayList<>(); List fieldList = rowType.getFieldList(); for (int i = 0; i < fieldList.size(); i++) { RelDataTypeField field = fieldList.get(i); final ColumnMetaData.AvaticaType columnType; if (field.getType().getSqlTypeName() == SqlTypeName.ARRAY) { final ColumnMetaData.Rep elementRep = rep(field.getType().getComponentType().getSqlTypeName()); final ColumnMetaData.ScalarType elementType = ColumnMetaData.scalar( field.getType().getComponentType().getSqlTypeName().getJdbcOrdinal(), field.getType().getComponentType().getSqlTypeName().getName(), elementRep ); final ColumnMetaData.Rep arrayRep = rep(field.getType().getSqlTypeName()); columnType = ColumnMetaData.array( elementType, field.getType().getSqlTypeName().getName(), arrayRep ); } else { final ColumnMetaData.Rep rep = rep(field.getType().getSqlTypeName()); columnType = ColumnMetaData.scalar( field.getType().getSqlTypeName().getJdbcOrdinal(), field.getType().getSqlTypeName().getName(), rep ); } columns.add( new ColumnMetaData( i, // ordinal false, // auto increment true, // case sensitive false, // searchable false, // currency field.getType().isNullable() ? DatabaseMetaData.columnNullable : DatabaseMetaData.columnNoNulls, // nullable true, // signed field.getType().getPrecision(), // display size field.getName(), // label null, // column name null, // schema name field.getType().getPrecision(), // precision field.getType().getScale(), // scale null, // table name null, // catalog name columnType, // avatica type true, // read only false, // writable false, // definitely writable columnType.columnClassName() // column class name ) ); } return columns; } private static ColumnMetaData.Rep rep(final SqlTypeName sqlType) { if (SqlTypeName.CHAR_TYPES.contains(sqlType)) { return ColumnMetaData.Rep.STRING; } else if (SqlTypeName.DATETIME_TYPES.contains(sqlType) || SqlTypeName.NUMERIC_TYPES.contains(sqlType)) { return ColumnMetaData.Rep.NUMBER; } else if (sqlType == SqlTypeName.BOOLEAN) { return ColumnMetaData.Rep.BOOLEAN; } else if (sqlType == SqlTypeName.OTHER) { return ColumnMetaData.Rep.OBJECT; } else if (sqlType == SqlTypeName.ARRAY) { return ColumnMetaData.Rep.ARRAY; } else { throw new ISE("No rep for SQL type [%s]", sqlType); } } public Meta.Frame nextFrame(final long fetchOffset, final int fetchMaxRowCount) { Meta.Frame frame = requireResultSet().nextFrame(fetchOffset, fetchMaxRowCount); // Implicitly close after the last result frame. if (frame.done) { closeResultSet(); } return frame; } public abstract Meta.Signature getSignature(); public void closeResultSet() { // Lock held only to get the result set, not during cleanup. DruidJdbcResultSet currentResultSet; synchronized (this) { currentResultSet = resultSet; resultSet = null; } if (currentResultSet != null) { currentResultSet.close(); } } protected synchronized DruidJdbcResultSet requireResultSet() { if (resultSet == null) { throw new ISE("No result set open for statement [%d]", statementId); } return resultSet; } public long getCurrentOffset() { return requireResultSet().getCurrentOffset(); } public synchronized boolean isDone() { return resultSet == null ? true : resultSet.isDone(); } @Override public synchronized void close() { closeResultSet(); } public String getConnectionId() { return connectionId; } public int getStatementId() { return statementId; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy