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

com.databricks.jdbc.api.impl.volume.VolumeOperationResult Maven / Gradle / Ivy

package com.databricks.jdbc.api.impl.volume;

import static com.databricks.jdbc.common.DatabricksJdbcConstants.ALLOWED_STAGING_INGESTION_PATHS;
import static com.databricks.jdbc.common.DatabricksJdbcConstants.ALLOWED_VOLUME_INGESTION_PATHS;

import com.databricks.jdbc.api.IDatabricksSession;
import com.databricks.jdbc.api.impl.IExecutionResult;
import com.databricks.jdbc.api.internal.IDatabricksStatementInternal;
import com.databricks.jdbc.common.ErrorCodes;
import com.databricks.jdbc.common.ErrorTypes;
import com.databricks.jdbc.dbclient.IDatabricksHttpClient;
import com.databricks.jdbc.dbclient.impl.http.DatabricksHttpClientFactory;
import com.databricks.jdbc.exception.DatabricksSQLException;
import com.databricks.jdbc.model.core.ResultManifest;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.entity.InputStreamEntity;

/** Class to handle the result of a volume operation */
public class VolumeOperationResult implements IExecutionResult {

  private final IDatabricksSession session;
  private final IExecutionResult resultHandler;
  private final IDatabricksStatementInternal statement;
  private final IDatabricksHttpClient httpClient;
  private final long rowCount;
  private final long columnCount;

  private VolumeOperationProcessor volumeOperationProcessor;
  private int currentRowIndex;
  private VolumeInputStream volumeInputStream = null;
  private long volumeStreamContentLength = -1L;

  public VolumeOperationResult(
      long totalRows,
      long totalColumns,
      IDatabricksSession session,
      IExecutionResult resultHandler,
      IDatabricksStatementInternal statement) {
    this.rowCount = totalRows;
    this.columnCount = totalColumns;
    this.session = session;
    this.resultHandler = resultHandler;
    this.statement = statement;
    this.httpClient =
        DatabricksHttpClientFactory.getInstance().getClient(session.getConnectionContext());
    this.currentRowIndex = -1;
  }

  @VisibleForTesting
  VolumeOperationResult(
      ResultManifest manifest,
      IDatabricksSession session,
      IExecutionResult resultHandler,
      IDatabricksHttpClient httpClient,
      IDatabricksStatementInternal statement) {
    this.rowCount = manifest.getTotalRowCount();
    this.columnCount = manifest.getSchema().getColumnCount();
    this.session = session;
    this.resultHandler = resultHandler;
    this.statement = statement;
    this.httpClient = httpClient;
    this.currentRowIndex = -1;
  }

  private void initHandler(IExecutionResult resultHandler) throws DatabricksSQLException {
    String operation = getString(resultHandler.getObject(0));
    String presignedUrl = getString(resultHandler.getObject(1));
    String localFile = columnCount > 3 ? getString(resultHandler.getObject(3)) : null;
    Map headers = getHeaders(getString(resultHandler.getObject(2)));
    String allowedVolumeIngestionPaths = getAllowedVolumeIngestionPaths();
    this.volumeOperationProcessor =
        new VolumeOperationProcessor(
            operation,
            presignedUrl,
            headers,
            localFile,
            allowedVolumeIngestionPaths,
            statement.isAllowedInputStreamForVolumeOperation(),
            statement.getInputStreamForUCVolume(),
            httpClient,
            (entity) -> {
              try {
                this.setVolumeOperationEntityStream(entity);
              } catch (Exception e) {
                throw new RuntimeException(
                    "Failed to set result set volumeOperationEntityStream", e);
              }
            });
  }

  private String getAllowedVolumeIngestionPaths() {
    String allowedPaths =
        session.getClientInfoProperties().get(ALLOWED_VOLUME_INGESTION_PATHS.toLowerCase());
    if (Strings.isNullOrEmpty(allowedPaths)) {
      allowedPaths =
          session.getClientInfoProperties().getOrDefault(ALLOWED_STAGING_INGESTION_PATHS, "");
    }
    return allowedPaths;
  }

  private String getString(Object obj) {
    return obj == null ? null : obj.toString();
  }

  private Map getHeaders(String headersVal) throws DatabricksSQLException {
    if (headersVal != null && !headersVal.isEmpty()) {
      // Map is encoded in extra [] while doing toString
      String headers =
          headersVal.charAt(0) == '['
              ? headersVal.substring(1, headersVal.length() - 1)
              : headersVal;
      if (!headers.isEmpty()) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
          return objectMapper.readValue(headers, Map.class);
        } catch (JsonProcessingException e) {
          throw new DatabricksSQLException(
              "Failed to parse headers",
              e,
              ErrorTypes.VOLUME_OPERATION_ERROR,
              ErrorCodes.VOLUME_OPERATION_PARSING_ERROR);
        }
      }
    }
    return new HashMap<>();
  }

  private void validateMetadata() throws DatabricksSQLException {
    // For now, we only support one row for Volume operation
    if (rowCount > 1) {
      throw new DatabricksSQLException("Too many rows for Volume Operation");
    }
    if (columnCount > 4) {
      throw new DatabricksSQLException("Too many columns for Volume Operation");
    }
    if (columnCount < 3) {
      throw new DatabricksSQLException("Too few columns for Volume Operation");
    }
  }

  @Override
  public Object getObject(int columnIndex) throws DatabricksSQLException {
    if (currentRowIndex < 0) {
      throw new DatabricksSQLException("Invalid row access");
    }
    if (columnIndex == 0) {
      return volumeOperationProcessor.getStatus().name();
    } else {
      throw new DatabricksSQLException("Invalid column access");
    }
  }

  @Override
  public long getCurrentRow() {
    return currentRowIndex;
  }

  @Override
  public boolean next() throws DatabricksSQLException {
    if (hasNext()) {
      validateMetadata();
      resultHandler.next();
      initHandler(resultHandler);
      volumeOperationProcessor.process();

      if (volumeOperationProcessor.getStatus()
          == VolumeOperationProcessor.VolumeOperationStatus.FAILED) {
        throw new DatabricksSQLException(
            "Volume operation failed: " + volumeOperationProcessor.getErrorMessage());
      }
      if (volumeOperationProcessor.getStatus()
          == VolumeOperationProcessor.VolumeOperationStatus.ABORTED) {
        throw new DatabricksSQLException(
            "Volume operation aborted: " + volumeOperationProcessor.getErrorMessage());
      }
      currentRowIndex++;
      return true;
    } else {
      return false;
    }
  }

  public void setVolumeOperationEntityStream(HttpEntity httpEntity) throws IOException {
    this.volumeInputStream = new VolumeInputStream(httpEntity);
    this.volumeStreamContentLength = httpEntity.getContentLength();
  }

  public InputStreamEntity getVolumeOperationInputStream() {
    return new InputStreamEntity(this.volumeInputStream, this.volumeStreamContentLength);
  }

  @Override
  public boolean hasNext() {
    return resultHandler.hasNext();
  }

  @Override
  public void close() {
    resultHandler.close();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy