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

io.cdap.plugin.db.sink.CommonFieldsValidator Maven / Gradle / Ivy

There is a newer version: 1.11.2
Show newest version
/*
 * Copyright © 2019 Cask Data, Inc.
 *
 * Licensed 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 io.cdap.plugin.db.sink;

import com.google.common.base.Preconditions;
import io.cdap.cdap.api.data.schema.Schema;
import io.cdap.cdap.etl.api.FailureCollector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;

/**
 * Common fields validator.
 */
public class CommonFieldsValidator implements FieldsValidator {
  protected static final Logger LOG = LoggerFactory.getLogger(CommonFieldsValidator.class);

  @Override
  public void validateFields(Schema inputSchema, ResultSet resultSet, FailureCollector collector) throws SQLException {
    ResultSetMetaData rsMetaData = resultSet.getMetaData();
    Preconditions.checkNotNull(inputSchema.getFields());
    for (Schema.Field field : inputSchema.getFields()) {
      int columnIndex = resultSet.findColumn(field.getName());
      boolean isColumnNullable = (ResultSetMetaData.columnNullable == rsMetaData.isNullable(columnIndex));
      boolean isNotNullAssignable = !isColumnNullable && field.getSchema().isNullable();
      String name = field.getName();
      if (isNotNullAssignable) {
        collector.addFailure(
          String.format("Field '%s' was given as nullable but database column is not nullable.", name),
          "Ensure that the field is not nullable.").withInputSchemaField(name);
      }

      if (!isFieldCompatible(field, rsMetaData, columnIndex)) {
        String sqlTypeName = rsMetaData.getColumnTypeName(columnIndex);
        Schema fieldSchema = field.getSchema().isNullable() ? field.getSchema().getNonNullable() : field.getSchema();
        collector.addFailure(
          String.format("Field '%s' was given as type '%s' but the database column is actually of type '%s'.",
                        name, fieldSchema.getDisplayName(), sqlTypeName),
          "Ensure that the field is not nullable.").withInputSchemaField(name);
      }
    }
  }

  /**
   * Checks if field is compatible to be written into database column of the given sql index.
   *
   * @param field    field of the explicit input schema.
   * @param metadata resultSet metadata.
   * @param index    sql column index.
   * @return 'true' if field is compatible to be written, 'false' otherwise.
   */
  public boolean isFieldCompatible(Schema.Field field, ResultSetMetaData metadata, int index) throws SQLException {
    Schema fieldSchema = field.getSchema().isNullable() ? field.getSchema().getNonNullable() : field.getSchema();
    Schema.Type fieldType = fieldSchema.getType();
    Schema.LogicalType fieldLogicalType = fieldSchema.getLogicalType();

    int sqlType = metadata.getColumnType(index);
    boolean isSigned = metadata.isSigned(index);
    int precision = metadata.getPrecision(index);

    return isFieldCompatible(fieldType, fieldLogicalType, sqlType, precision, isSigned);
  }


  /**
   * Checks if field is compatible to be written into database column of the given sql index.
   *
   * @param fieldType        field type.
   * @param fieldLogicalType filed logical type.
   * @param sqlType          code of sql type.
   * @param precision
   * @return 'true' if field is compatible to be written, 'false' otherwise.
   */
  public boolean isFieldCompatible(Schema.Type fieldType,
                                   Schema.LogicalType fieldLogicalType,
                                   int sqlType,
                                   int precision,
                                   boolean isSigned) {
    // Handle logical types first
    if (fieldLogicalType != null) {
      switch (fieldLogicalType) {
        case DATE:
          return sqlType == Types.DATE;
        case TIME_MILLIS:
        case TIME_MICROS:
          return sqlType == Types.TIME;
        case TIMESTAMP_MILLIS:
        case TIMESTAMP_MICROS:
          return sqlType == Types.TIMESTAMP;
        case DECIMAL:
          return sqlType == Types.NUMERIC
            || sqlType == Types.DECIMAL
            || (sqlType == Types.BIGINT && !isSigned && precision >= 19);
      }
    }

    switch (fieldType) {
      case NULL:
        return true;
      case BOOLEAN:
        return sqlType == Types.BOOLEAN
          || sqlType == Types.BIT;
      case INT:
        return sqlType == Types.INTEGER
          || sqlType == Types.SMALLINT
          || sqlType == Types.TINYINT;
      case LONG:
        return sqlType == Types.BIGINT
          || (!isSigned && sqlType == Types.INTEGER);
      case FLOAT:
        return sqlType == Types.REAL
          || sqlType == Types.FLOAT;
      case DOUBLE:
        return sqlType == Types.DOUBLE;
      case BYTES:
        return sqlType == Types.BINARY
          || sqlType == Types.VARBINARY
          || sqlType == Types.LONGVARBINARY
          || sqlType == Types.BLOB;
      case STRING:
        return sqlType == Types.VARCHAR
          || sqlType == Types.CHAR
          || sqlType == Types.CLOB
          || sqlType == Types.LONGNVARCHAR
          || sqlType == Types.LONGVARCHAR
          || sqlType == Types.NCHAR
          || sqlType == Types.NCLOB
          || sqlType == Types.NVARCHAR;
      default:
        return false;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy