From 2f94fe3d096cf3220da8cda016c786d0c39b8e79 Mon Sep 17 00:00:00 2001 From: Sam Chu Date: Tue, 1 Nov 2022 17:48:19 -0700 Subject: [PATCH] [Bitsail] Refactor Oracle connector util classes --- .../jdbc/converter/JdbcValueConverter.java | 79 +++++----- .../jdbc/converter/OracleValueConverter.java | 139 +++++++--------- .../converter/PostgresValueConverter.java | 15 +- .../converter/SqlServerValueConverter.java | 9 +- .../connector/legacy/jdbc/model/Duration.java | 75 +++++++++ .../legacy/jdbc/utils/MicroIntervalUtil.java | 79 ---------- .../converter/OracleValueConverterTest.java | 141 +++++++++++++++++ .../legacy/jdbc/model/DurationTest.java | 149 ++++++++++++++++++ .../{ => upsert}/OracleUpsertUtilTest.java | 47 ++++-- 9 files changed, 506 insertions(+), 227 deletions(-) create mode 100644 bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/model/Duration.java delete mode 100644 bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/utils/MicroIntervalUtil.java create mode 100644 bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/OracleValueConverterTest.java create mode 100644 bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/model/DurationTest.java rename bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/utils/{ => upsert}/OracleUpsertUtilTest.java (51%) diff --git a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/JdbcValueConverter.java b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/JdbcValueConverter.java index df7035067..5b7280dfb 100644 --- a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/JdbcValueConverter.java +++ b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/JdbcValueConverter.java @@ -53,7 +53,6 @@ protected Object extract(ResultSet rs, int columnIndex, int columnType, String columnTypeName, - String columnName, String encoding) throws Exception { switch (columnType) { case Types.CHAR: @@ -113,9 +112,8 @@ protected Object extract(ResultSet rs, .asBitSailException( DBUtilErrorCode.UNSUPPORTED_TYPE, String.format( - "JDBC extract: The column data type in your configuration is not support. Column name:[%s], Column type:[%s]." + + "JDBC extract: The column data type in your configuration is not support. Column type name:[%s]." + " Please try to change the column data type or don't transmit this column.", - columnName, columnTypeName)); } @@ -126,16 +124,14 @@ public final Object convert(ResultSetMetaData metaData, int columnIndex, String encoding) throws Exception { int columnType = metaData.getColumnType(columnIndex); - String columnName = metaData.getColumnName(columnIndex); String columnTypeName = metaData.getColumnTypeName(columnIndex); - Object value = extract(rs, metaData, columnIndex, columnType, columnTypeName, columnName, encoding); + Object value = extract(rs, metaData, columnIndex, columnType, columnTypeName, encoding); LOG.debug("value: {}", value); - return convert(value, columnType, columnName, columnTypeName); + return convert(value, columnType, columnTypeName); } protected Object convert(Object value, int columnType, - String columnName, String columnTypeName) throws Exception { switch (columnType) { case Types.CHAR: @@ -147,53 +143,52 @@ protected Object convert(Object value, case Types.CLOB: case Types.NCLOB: case Types.ROWID: - return convertStringValue(value, columnName, columnTypeName); + return convertStringValue(value); case Types.BIT: case Types.TINYINT: case Types.SMALLINT: - return convertShortValue(value, columnName, columnTypeName); + return convertShortValue(value); case Types.INTEGER: - return convertLongValue(value, columnName, columnTypeName); + return convertLongValue(value); case Types.BIGINT: - return convertBigIntegerValue(value, columnName, columnTypeName); + return convertBigIntegerValue(value); case Types.NUMERIC: case Types.DECIMAL: - return convertBigDecimalValue(value, columnName, columnTypeName); + return convertBigDecimalValue(value); case Types.FLOAT: case Types.REAL: case Types.DOUBLE: - return convertDoubleValue(value, columnName, columnTypeName); + return convertDoubleValue(value); case Types.TIME: case Types.DATE: case Types.TIMESTAMP: - return convertTimeValue(value, columnName, columnTypeName); + return convertTimeValue(value); case Types.BINARY: case Types.VARBINARY: case Types.BLOB: case Types.LONGVARBINARY: - return convertBinaryValue(value, columnName, columnTypeName); + return convertBinaryValue(value); case Types.BOOLEAN: - return convertBooleanValue(value, columnName, columnTypeName); + return convertBooleanValue(value); case Types.ARRAY: - return convertArrayValue(value, columnName, columnTypeName); + return convertArrayValue(value); case Types.NULL: case Types.OTHER: case Types.STRUCT: - return convertObjectValue(value, columnName, columnTypeName); + return convertObjectValue(value); default: throw BitSailException .asBitSailException( DBUtilErrorCode.UNSUPPORTED_TYPE, String.format( - "JDBC convert: The column data type in your configuration is not support. Column name:[%s], Column type:[%s]." + + "JDBC convert: The column data type in your configuration is not support. Column type:[%s]." + " Please try to change the column data type or don't transmit this column.", - columnName, columnTypeName)); } @@ -279,7 +274,7 @@ private Object extractObjectValue(ResultSet rs, int columnIndex) throws SQLExcep return objectValue; } - protected String convertStringValue(Object value, String columnName, String columnTypeName) { + protected String convertStringValue(Object value) { if (Objects.isNull(value)) { return null; } @@ -287,10 +282,10 @@ protected String convertStringValue(Object value, String columnName, String colu return (String) value; } throw new IllegalArgumentException(String - .format("Unexpected value for JDBC type: %s and column %s", columnTypeName, columnName)); + .format("Unexpected value %s while converting to String", value)); } - private Short convertShortValue(Object value, String columnName, String columnTypeName) { + private Short convertShortValue(Object value) { if (Objects.isNull(value)) { return null; } @@ -301,10 +296,10 @@ private Short convertShortValue(Object value, String columnName, String columnTy return ((Number) value).shortValue(); } throw new IllegalArgumentException(String - .format("Unexpected value for JDBC type: %s and column %s", columnTypeName, columnName)); + .format("Unexpected value %s while converting to Short", value)); } - private Long convertLongValue(Object value, String columnName, String columnTypeName) { + private Long convertLongValue(Object value) { if (Objects.isNull(value)) { return null; } @@ -321,10 +316,10 @@ private Long convertLongValue(Object value, String columnName, String columnType return ((Number) value).longValue(); } throw new IllegalArgumentException(String - .format("Unexpected value for JDBC type: %s and column %s", columnTypeName, columnName)); + .format("Unexpected value %s while converting to Long", value)); } - private BigInteger convertBigIntegerValue(Object value, String columnName, String columnTypeName) { + private BigInteger convertBigIntegerValue(Object value) { if (Objects.isNull(value)) { return null; } @@ -335,10 +330,10 @@ private BigInteger convertBigIntegerValue(Object value, String columnName, Strin return (BigInteger) value; } throw new IllegalArgumentException(String - .format("Unexpected value for JDBC type: %s and column %s", columnTypeName, columnName)); + .format("Unexpected value %s while converting to BigInteger", value)); } - private BigDecimal convertBigDecimalValue(Object value, String columnName, String columnTypeName) { + private BigDecimal convertBigDecimalValue(Object value) { if (Objects.isNull(value)) { return null; } @@ -352,10 +347,10 @@ private BigDecimal convertBigDecimalValue(Object value, String columnName, Strin return (BigDecimal) value; } throw new IllegalArgumentException(String - .format("Unexpected value for JDBC type: %s and column %s", columnTypeName, columnName)); + .format("Unexpected value %s while converting to BigDecimal", value)); } - protected Double convertDoubleValue(Object value, String columnName, String columnTypeName) { + protected Double convertDoubleValue(Object value) { if (Objects.isNull(value)) { return null; } @@ -369,10 +364,10 @@ protected Double convertDoubleValue(Object value, String columnName, String colu return ((BigDecimal) value).doubleValue(); } throw new IllegalArgumentException(String - .format("Unexpected value for JDBC type: %s and column %s", columnTypeName, columnName)); + .format("Unexpected value %s while converting to Double", value)); } - protected Object convertTimeValue(Object value, String columnName, String columnTypeName) { + protected Object convertTimeValue(Object value) { if (Objects.isNull(value)) { return null; } @@ -389,10 +384,10 @@ protected Object convertTimeValue(Object value, String columnName, String column return (Date) value; } throw new IllegalArgumentException(String - .format("Unexpected value for JDBC type: %s and column %s", columnTypeName, columnName)); + .format("Unexpected value %s while converting to Time", value)); } - private byte[] convertBinaryValue(Object value, String columnName, String columnTypeName) { + private byte[] convertBinaryValue(Object value) { if (Objects.isNull(value)) { return null; } @@ -400,10 +395,10 @@ private byte[] convertBinaryValue(Object value, String columnName, String column return (byte[]) value; } throw new IllegalArgumentException(String - .format("Unexpected value for JDBC type: %s and column %s", columnTypeName, columnName)); + .format("Unexpected value %s while converting to byte[]", value)); } - protected Boolean convertBooleanValue(Object value, String columnName, String columnTypeName) { + protected Boolean convertBooleanValue(Object value) { if (Objects.isNull(value)) { return null; } @@ -411,10 +406,10 @@ protected Boolean convertBooleanValue(Object value, String columnName, String co return (Boolean) value; } throw new IllegalArgumentException(String - .format("Unexpected value for JDBC type: %s and column %s", columnTypeName, columnName)); + .format("Unexpected value %s while converting to Boolean", value)); } - private List convertArrayValue(Object value, String columnName, String columnTypeName) throws Exception { + private List convertArrayValue(Object value) throws Exception { if (Objects.isNull(value)) { return null; } @@ -423,15 +418,15 @@ private List convertArrayValue(Object value, String columnName, String c Object[] arrays = (Object[]) arrayElements.getArray(); List tmpArrays = Lists.newArrayList(); for (Object element : arrays) { - tmpArrays.add(convert(element, arrayElements.getBaseType(), columnName, arrayElements.getBaseTypeName())); + tmpArrays.add(convert(element, arrayElements.getBaseType(), arrayElements.getBaseTypeName())); } return tmpArrays; } throw new IllegalArgumentException(String - .format("Unexpected value for JDBC type: %s and column %s", columnTypeName, columnName)); + .format("Unexpected value %s while converting to Array", value)); } - private Object convertObjectValue(Object value, String columnName, String columnTypeName) throws SQLException { + private Object convertObjectValue(Object value) { if (Objects.isNull(value)) { return null; } diff --git a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/OracleValueConverter.java b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/OracleValueConverter.java index 631100195..25801ead6 100644 --- a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/OracleValueConverter.java +++ b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/OracleValueConverter.java @@ -17,19 +17,18 @@ package com.bytedance.bitsail.connector.legacy.jdbc.converter; -import com.bytedance.bitsail.connector.legacy.jdbc.utils.MicroIntervalUtil; +import com.bytedance.bitsail.connector.legacy.jdbc.model.Duration; +import com.google.common.annotations.VisibleForTesting; +import lombok.NonNull; import oracle.jdbc.OracleResultSet; import oracle.jdbc.OracleTypes; import oracle.sql.INTERVALDS; import oracle.sql.INTERVALYM; -import oracle.sql.TIMESTAMP; import org.apache.commons.dbcp.DelegatingResultSet; import java.sql.ResultSet; import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Timestamp; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -40,11 +39,7 @@ public class OracleValueConverter extends JdbcValueConverter { private static final Pattern INTERVAL_DAY_SECOND_PATTERN = Pattern.compile("([+\\-])?(\\d+) (\\d+):(\\d+):(\\d+).(\\d+)"); - private static final int PATTERN_GROUP_DAYS_IDX = 2; - private static final int PATTERN_GROUP_HOURS_IDX = 3; - private static final int PATTERN_GROUP_MINUTES_IDX = 4; - private static final int PATTERN_GROUP_SECONDS_IDX = 5; - private static final int PATTERN_GROUP_MICROSECONDS_IDX = 6; + private static final Pattern INTERVAL_YEAR_MONTH_PATTERN = Pattern.compile("([+\\-])?(\\d+)-(\\d+)"); private static final int MICROS_LENGTH = 6; private IntervalHandlingMode intervalMode; @@ -59,7 +54,6 @@ private static OracleResultSet unwrap(ResultSet rs) { } else if (rs instanceof DelegatingResultSet) { oracleResultSet = unwrap(((DelegatingResultSet) rs).getInnermostDelegate()); } - return oracleResultSet; } @@ -69,114 +63,81 @@ protected Object extract(ResultSet rs, int columnIndex, int columnType, String columnTypeName, - String columnName, String encoding) throws Exception { OracleResultSet oracleResultSet = unwrap(rs); if (Objects.isNull(oracleResultSet)) { - return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, columnName, encoding); + return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, encoding); } int oracleColumnType = metaData.getColumnType(columnIndex); switch (oracleColumnType) { case OracleTypes.TIMESTAMPTZ: case OracleTypes.TIMESTAMPLTZ: - return getTimestampWithoutConnection(oracleResultSet, columnIndex); + return oracleResultSet.getTIMESTAMP(columnIndex).timestampValue(); case OracleTypes.INTERVALDS: - return getIntervalDSValue(oracleResultSet, columnIndex); + return oracleResultSet.getINTERVALDS(columnIndex); case OracleTypes.INTERVALYM: - return getIntervalYMValue(oracleResultSet, columnIndex); + return oracleResultSet.getINTERVALYM(columnIndex); case OracleTypes.BINARY_FLOAT: case OracleTypes.BINARY_DOUBLE: return extractDoubleValue(oracleResultSet, columnIndex); default: - return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, columnName, encoding); + return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, encoding); } } @Override - protected Object convert(Object value, int columnType, String columnName, String columnTypeName) throws Exception { + protected Object convert(Object value, int columnType, String columnTypeName) throws Exception { switch (columnType) { case OracleTypes.TIMESTAMPTZ: case OracleTypes.TIMESTAMPLTZ: - return convertTimeValue(value, columnName, columnTypeName); + return convertTimeValue(value); case OracleTypes.INTERVALDS: - return convertIntervalDSValue((INTERVALDS) value, this.intervalMode); case OracleTypes.INTERVALYM: - return convertIntervalYMValue((INTERVALYM) value, this.intervalMode); + return convertInterval(value); case OracleTypes.BINARY_FLOAT: case OracleTypes.BINARY_DOUBLE: return value; default: - return super.convert(value, columnType, columnName, columnTypeName); + return super.convert(value, columnType, columnTypeName); } } - private Timestamp getTimestampWithoutConnection(OracleResultSet rs, - int columnIndex) throws SQLException { - TIMESTAMP timestamp = rs.getTIMESTAMP(columnIndex); - return timestamp.timestampValue(); - } - - private Object getIntervalDSValue(OracleResultSet rs, - int columnIndex) throws Exception { - return rs.getINTERVALDS(columnIndex); - } - - private Object getIntervalYMValue(OracleResultSet rs, - int columnIndex) throws Exception { - return rs.getINTERVALYM(columnIndex); - } - - private Object convertIntervalDSValue(INTERVALDS interval, IntervalHandlingMode mode) throws Exception { + @VisibleForTesting + @SuppressWarnings("checkstyle:MagicNumber") + Object convertInterval(final Object interval) { + if (this.intervalMode == null) { + throw new IllegalArgumentException("Fail to convert interval for oracle with null mode. value: " + interval); + } final String intervalStr = interval.toString(); - if (mode.equals(IntervalHandlingMode.STRING)) { + if (this.intervalMode.equals(IntervalHandlingMode.STRING)) { return intervalStr; - } else if (mode.equals(IntervalHandlingMode.NUMERIC)) { + } + // Handle IntervalHandlingMode.NUMERIC + if (interval instanceof INTERVALDS) { final Matcher m = INTERVAL_DAY_SECOND_PATTERN.matcher(intervalStr); if (m.matches()) { final int sign = "-".equals(m.group(1)) ? -1 : 1; - return MicroIntervalUtil.durationMicros( - 0, - 0, - sign * Integer.parseInt(m.group(PATTERN_GROUP_DAYS_IDX)), - sign * Integer.parseInt(m.group(PATTERN_GROUP_HOURS_IDX)), - sign * Integer.parseInt(m.group(PATTERN_GROUP_MINUTES_IDX)), - sign * Integer.parseInt(m.group(PATTERN_GROUP_SECONDS_IDX)), - sign * Integer.parseInt(MicroIntervalUtil.pad(m.group(PATTERN_GROUP_MICROSECONDS_IDX), MICROS_LENGTH, '0')), - MicroIntervalUtil.DAYS_PER_MONTH_AVG); + final Duration duration = Duration.builder() + .days(sign * Integer.parseInt(m.group(2))) + .hours(sign * Integer.parseInt(m.group(3))) + .minutes(sign * Integer.parseInt(m.group(4))) + .seconds(sign * Integer.parseInt(m.group(5))) + .microseconds(sign * Integer.parseInt(convertNumRightToFloatingPointToMicro(m.group(6)))) + .build(); + return duration.toMicros(); } - } - throw new IllegalArgumentException("Fail to convert interval_day_to_seconds for oracle, mode: " + mode + " value: " + interval.toString()); - } - - private Object convertIntervalYMValue(INTERVALYM interval, IntervalHandlingMode mode) throws Exception { - final String intervalStr = interval.toString(); - if (mode.equals(IntervalHandlingMode.STRING)) { - return intervalStr; - } else if (mode.equals(IntervalHandlingMode.NUMERIC)) { - int sign = 1; - int start = 0; - if (intervalStr.charAt(0) == '-') { - sign = -1; - start = 1; - } - for (int i = 1; i < intervalStr.length(); i++) { - if (intervalStr.charAt(i) == '-') { - final int year = sign * Integer.parseInt(intervalStr.substring(start, i)); - final int month = sign * Integer.parseInt(intervalStr.substring(i + 1, intervalStr.length())); - return MicroIntervalUtil.durationMicros( - year, - month, - 0, - 0, - 0, - 0, - 0, - MicroIntervalUtil.DAYS_PER_MONTH_AVG); - } + } else if (interval instanceof INTERVALYM) { + final Matcher m = INTERVAL_YEAR_MONTH_PATTERN.matcher(intervalStr); + if (m.matches()) { + final int sign = "-".equals(m.group(1)) ? -1 : 1; + final Duration duration = Duration.builder() + .years(sign * Integer.parseInt(m.group(2))) + .months(sign * Integer.parseInt(m.group(3))) + .build(); + return duration.toMicros(); } } - - throw new Exception("Fail to convert interval_year_to_month for oracle, mode: " + mode + " value: " + interval.toString()); + throw new IllegalArgumentException("Fail to convert interval for oracle, mode: " + this.intervalMode + " value: " + interval); } /** @@ -215,4 +176,22 @@ public String getValue() { return value; } } + + /** + * Convert number right to floating point to micro number + * + * @param numRightToFloatingPoint the string to be converted into micro number + * @return micro number string + */ + @VisibleForTesting + String convertNumRightToFloatingPointToMicro(@NonNull final String numRightToFloatingPoint) { + if (numRightToFloatingPoint.length() > MICROS_LENGTH) { + return numRightToFloatingPoint.substring(0, MICROS_LENGTH); + } + final StringBuilder sb = new StringBuilder(numRightToFloatingPoint); + while (sb.length() < MICROS_LENGTH) { + sb.append('0'); + } + return sb.toString(); + } } diff --git a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/PostgresValueConverter.java b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/PostgresValueConverter.java index a1ba0f947..85cdc23cd 100644 --- a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/PostgresValueConverter.java +++ b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/PostgresValueConverter.java @@ -53,11 +53,10 @@ protected Object extract(ResultSet rs, int columnIndex, int columnType, String columnTypeName, - String columnName, String encoding) throws Exception { PgResultSet pgResultSet = unwrap(rs); if (Objects.isNull(pgResultSet)) { - return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, columnName, encoding); + return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, encoding); } String pgColumnTypeName = metaData.getColumnTypeName(columnIndex); int pgColumnType; @@ -65,7 +64,7 @@ protected Object extract(ResultSet rs, pgColumnType = Oid.valueOf(pgColumnTypeName); } catch (Exception e) { LOG.debug("Column type name = {} is invalid.", columnTypeName); - return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, columnName, encoding); + return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, encoding); } switch (pgColumnType) { @@ -76,24 +75,24 @@ protected Object extract(ResultSet rs, case Oid.BOOL: return extractBooleanValue(pgResultSet, columnIndex); default: - return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, columnName, encoding); + return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, encoding); } } @Override - protected Object convert(Object value, int columnType, String columnName, String columnTypeName) throws Exception { + protected Object convert(Object value, int columnType, String columnTypeName) throws Exception { int pgColumnType; try { pgColumnType = Oid.valueOf(columnTypeName); } catch (Exception e) { LOG.debug("Column type name = {} is invalid.", columnTypeName); - return super.convert(value, columnType, columnName, columnTypeName); + return super.convert(value, columnType, columnTypeName); } switch (pgColumnType) { case Oid.BOOL: - return convertBooleanValue(value, columnName, columnTypeName); + return convertBooleanValue(value); default: - return super.convert(value, columnType, columnName, columnTypeName); + return super.convert(value, columnType, columnTypeName); } } diff --git a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/SqlServerValueConverter.java b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/SqlServerValueConverter.java index da7065746..ed3f168fe 100644 --- a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/SqlServerValueConverter.java +++ b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/SqlServerValueConverter.java @@ -50,11 +50,10 @@ protected Object extract(ResultSet rs, int columnIndex, int columnType, String columnTypeName, - String columnName, String encoding) throws Exception { SQLServerResultSet sqlserverResultSet = unwrap(rs); if (Objects.isNull(sqlserverResultSet)) { - return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, columnName, encoding); + return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, encoding); } SQLServerResultSetMetaData sqlServerResultSetMetaData = (SQLServerResultSetMetaData) metaData; int sqlserverColumnType = sqlServerResultSetMetaData.getColumnType(columnIndex); @@ -68,12 +67,12 @@ protected Object extract(ResultSet rs, case Types.MONEY: return extractMoneyValue(sqlserverResultSet, columnIndex); default: - return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, columnName, encoding); + return super.extract(rs, metaData, columnIndex, columnType, columnTypeName, encoding); } } @Override - protected Object convert(Object value, int columnType, String columnName, String columnTypeName) throws Exception { + protected Object convert(Object value, int columnType, String columnTypeName) throws Exception { switch (columnType) { case Types.DATETIME: case Types.DATETIMEOFFSET: @@ -82,7 +81,7 @@ protected Object convert(Object value, int columnType, String columnName, String case Types.MONEY: return value; default: - return super.convert(value, columnType, columnName, columnTypeName); + return super.convert(value, columnType, columnTypeName); } } diff --git a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/model/Duration.java b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/model/Duration.java new file mode 100644 index 000000000..aa1858dc1 --- /dev/null +++ b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/model/Duration.java @@ -0,0 +1,75 @@ +/* + * 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 com.bytedance.bitsail.connector.legacy.jdbc.model; + +import com.google.common.annotations.VisibleForTesting; +import lombok.Builder; +import lombok.Getter; + +import java.math.BigInteger; + +/** + * A time-based amount of time. This class models a quantity or amount of time by parsing time units including years, + * months, days, hours, minutes, seconds, and microseconds. + */ +@Builder +public class Duration { + @VisibleForTesting + static final double DAYS_PER_MONTH_AVG = 365.25 / 12.0d; + + @VisibleForTesting + static final int MONTHS_PER_YEAR = 12; + + @VisibleForTesting + static final BigInteger HOURS_PER_DAY = BigInteger.valueOf(24); + + @VisibleForTesting + static final BigInteger MINUTES_PER_HOUR = BigInteger.valueOf(60); + + @VisibleForTesting + static final BigInteger SECONDS_PER_MINUTE = BigInteger.valueOf(60); + + @VisibleForTesting + static final BigInteger MICROSECONDS_PER_SECOND = BigInteger.valueOf(1_000_000L); + + private final int years; + private final int months; + private final int days; + private final int hours; + private final int minutes; + private final int seconds; + private final int microseconds; + + @Builder.Default + @Getter + private double daysPerMonthAvg = DAYS_PER_MONTH_AVG; + + /** + * Converts this duration to the total length in microseconds. + * + * @return the total length of the duration in microseconds + */ + public BigInteger toMicros() { + long numberOfMonths = years * MONTHS_PER_YEAR + months; + BigInteger numberOfDays = BigInteger.valueOf((long) (numberOfMonths * daysPerMonthAvg) + days); + BigInteger numberOfHours = numberOfDays.multiply(HOURS_PER_DAY).add(BigInteger.valueOf(hours)); + BigInteger numberOfMinutes = numberOfHours.multiply(MINUTES_PER_HOUR).add(BigInteger.valueOf(minutes)); + BigInteger numberOfSeconds = numberOfMinutes.multiply(SECONDS_PER_MINUTE).add(BigInteger.valueOf(seconds)); + return numberOfSeconds.multiply(MICROSECONDS_PER_SECOND).add(BigInteger.valueOf(microseconds)); + } +} diff --git a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/utils/MicroIntervalUtil.java b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/utils/MicroIntervalUtil.java deleted file mode 100644 index cd9e0436b..000000000 --- a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/main/java/com/bytedance/bitsail/connector/legacy/jdbc/utils/MicroIntervalUtil.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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. - * - * Original Files: apache/flink(https://github.com/apache/flink) - * Copyright: Copyright 2014-2022 The Apache Software Foundation - * SPDX-License-Identifier: Apache License 2.0 - * - * This file may have been modified by ByteDance Ltd. and/or its affiliates. - */ - -package com.bytedance.bitsail.connector.legacy.jdbc.utils; - -import java.time.temporal.ChronoUnit; - -public class MicroIntervalUtil { - public static final double DAYS_PER_MONTH_AVG = 365.25 / 12.0d; - private static final int MONTHS_PER_YEAR = 12; - private static final int HOURS_PER_DAY = 24; - private static final int MINUTES_PER_HOUR = 60; - private static final long MICROSECONDS_PER_SECOND = 1_000_000L; - /** - * Converts a number of time units to a duration in microseconds. - * - * @param years a number of years - * @param months a number of months - * @param days a number of days - * @param hours a number of hours - * @param minutes a number of minutes - * @param seconds a number of seconds - * @param micros a number of microseconds - * @param daysPerMonthAvg an optional value representing a days per month average; if null, the default duration - * from {@link ChronoUnit#MONTHS} is used. - * @return @return Approximate representation of the given interval as a number of microseconds - */ - public static long durationMicros(int years, int months, int days, int hours, int minutes, double seconds, - int micros, Double daysPerMonthAvg) { - if (daysPerMonthAvg == null) { - daysPerMonthAvg = (double) ChronoUnit.MONTHS.getDuration().toDays(); - } - double numberOfDays = ((years * MONTHS_PER_YEAR) + months) * daysPerMonthAvg + days; - double numberOfSeconds = - (((numberOfDays * HOURS_PER_DAY + hours) * MINUTES_PER_HOUR) + minutes) * MINUTES_PER_HOUR + seconds; - return (long) (numberOfSeconds * MICROSECONDS_PER_SECOND + micros); - } - - /** - * Pad the string with the specific character to ensure the string is at least the specified length. - * - * @param original the string to be padded; may not be null - * @param length the minimum desired length; must be positive - * @param padChar the character to use for padding, if the supplied string is not long enough - * @return the padded string of the desired length - */ - public static String pad(String original, - int length, - char padChar) { - if (original.length() >= length) { - return original; - } - StringBuilder sb = new StringBuilder(original); - while (sb.length() < length) { - sb.append(padChar); - } - return sb.toString(); - } -} diff --git a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/OracleValueConverterTest.java b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/OracleValueConverterTest.java new file mode 100644 index 000000000..4c8bb9bb2 --- /dev/null +++ b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/converter/OracleValueConverterTest.java @@ -0,0 +1,141 @@ +/* + * 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 com.bytedance.bitsail.connector.legacy.jdbc.converter; + +import oracle.jdbc.OracleResultSet; +import oracle.jdbc.OracleResultSetMetaData; +import oracle.jdbc.OracleTypes; +import oracle.sql.INTERVALDS; +import oracle.sql.INTERVALYM; +import org.junit.Test; + +import java.math.BigInteger; +import java.sql.Types; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class OracleValueConverterTest { + private OracleValueConverter stringOracleValueConverter = + new OracleValueConverter(OracleValueConverter.IntervalHandlingMode.STRING); + private OracleValueConverter numericOracleValueConverter = + new OracleValueConverter(OracleValueConverter.IntervalHandlingMode.NUMERIC); + + @Test + public void testExtract() throws Exception { + OracleResultSet mockOracleResultSet = mock(OracleResultSet.class); + OracleResultSetMetaData mockMetaData = mock(OracleResultSetMetaData.class); + final int columnIndex = 0; + final INTERVALDS intervalds = new INTERVALDS(); + when(mockMetaData.getColumnType(columnIndex)).thenReturn(OracleTypes.INTERVALDS); + when(mockOracleResultSet.getINTERVALDS(columnIndex)).thenReturn(intervalds); + assertEquals(intervalds, numericOracleValueConverter.extract(mockOracleResultSet, mockMetaData, columnIndex, 0, null, null)); + } + + @Test + public void testConvertCharColumnType() throws Exception { + String varchar = "varchar"; + assertEquals(varchar, stringOracleValueConverter.convert(varchar, Types.VARCHAR, null)); + } + + @Test + public void testConvertTimestamptzColumnType() throws Exception { + int value = 1; + assertEquals(value, stringOracleValueConverter.convert(value, OracleTypes.TIMESTAMPTZ, null)); + } + + @Test + public void testConvertIntervalColumnType() throws Exception { + final INTERVALYM intervalym = new INTERVALYM("2022-10"); + assertEquals("2022-10", stringOracleValueConverter.convert(intervalym, OracleTypes.INTERVALYM, null)); + } + + @Test + public void testConvertBinaryFloatColumnType() throws Exception { + final Object value = 1; + assertEquals(value, stringOracleValueConverter.convert(value, OracleTypes.BINARY_FLOAT, null)); + } + + @Test(expected = IllegalArgumentException.class) + public void testConvertIntervalNullMode() { + final OracleValueConverter nullOracleValueConverter = + new OracleValueConverter(OracleValueConverter.IntervalHandlingMode.parse("test")); + final INTERVALYM intervalym = new INTERVALYM("2022-10"); + nullOracleValueConverter.convertInterval(intervalym); + } + + @Test + public void testConvertIntervalString() { + final INTERVALYM intervalym = new INTERVALYM("2022-10"); + assertEquals("2022-10", stringOracleValueConverter.convertInterval(intervalym)); + } + + @Test(expected = IllegalArgumentException.class) + public void testConvertIntervalWrongInterval() { + numericOracleValueConverter.convertInterval("test"); + } + + @Test + public void testConvertIntervalYM() { + final INTERVALYM intervalym = new INTERVALYM("2022-10"); + assertEquals(BigInteger.valueOf(63_835_689_600_000_000L), numericOracleValueConverter.convertInterval(intervalym)); + } + + @Test + public void testConvertIntervalDS() { + final INTERVALDS intervalds = new INTERVALDS("1 2:34:56.789"); + assertEquals(BigInteger.valueOf(95_696_789_000L), numericOracleValueConverter.convertInterval(intervalds)); + } + + @Test + public void testIntervalHandlingModeParseNull() { + assertNull(OracleValueConverter.IntervalHandlingMode.parse(null)); + } + + @Test + public void testIntervalHandlingModeParseDifferentValues() { + assertNull(OracleValueConverter.IntervalHandlingMode.parse("test")); + } + + @Test + public void testIntervalHandlingModeParseNumeric() { + assertEquals(OracleValueConverter.IntervalHandlingMode.NUMERIC, OracleValueConverter.IntervalHandlingMode.parse("numeric")); + } + + @Test + public void testIntervalHandlingModeParseString() { + assertEquals(OracleValueConverter.IntervalHandlingMode.STRING, OracleValueConverter.IntervalHandlingMode.parse("string")); + } + + @Test + public void testConvertNumRightToFloatingPointToMicroLessThanMicroLength() { + assertEquals("123000", numericOracleValueConverter.convertNumRightToFloatingPointToMicro("123")); + } + + @Test + public void testConvertNumRightToFloatingPointToMicroOverMicroLength() { + assertEquals("123456", numericOracleValueConverter.convertNumRightToFloatingPointToMicro("1234567")); + } + + @Test + public void testConvertNumRightToFloatingPointToMicroSameAsMicroLength() { + assertEquals("123456", numericOracleValueConverter.convertNumRightToFloatingPointToMicro("123456")); + } +} diff --git a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/model/DurationTest.java b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/model/DurationTest.java new file mode 100644 index 000000000..d2ae0e9e0 --- /dev/null +++ b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/model/DurationTest.java @@ -0,0 +1,149 @@ +/* + * 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 com.bytedance.bitsail.connector.legacy.jdbc.model; + +import org.junit.Test; + +import java.math.BigInteger; +import java.util.Random; + +import static com.bytedance.bitsail.connector.legacy.jdbc.model.Duration.DAYS_PER_MONTH_AVG; +import static com.bytedance.bitsail.connector.legacy.jdbc.model.Duration.HOURS_PER_DAY; +import static com.bytedance.bitsail.connector.legacy.jdbc.model.Duration.MICROSECONDS_PER_SECOND; +import static com.bytedance.bitsail.connector.legacy.jdbc.model.Duration.MINUTES_PER_HOUR; +import static com.bytedance.bitsail.connector.legacy.jdbc.model.Duration.MONTHS_PER_YEAR; +import static com.bytedance.bitsail.connector.legacy.jdbc.model.Duration.SECONDS_PER_MINUTE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DurationTest { + + private final double delta = 1e-8; + private final Random random = new Random(); + + @Test + public void testDefaultDaysPerMonthAvg() { + assertEquals(DAYS_PER_MONTH_AVG, Duration.builder().build().getDaysPerMonthAvg(), delta); + } + + @Test + public void testToMicros() { + for (int i = 0; i < 100; i++) { + final int years = random.nextInt(100); + final int months = random.nextInt(100); + final double daysPerMonthAvg = 30.5; + final int days = random.nextInt(100); + final int hours = random.nextInt(100); + final int minutes = random.nextInt(100); + final int seconds = random.nextInt(100); + final int microseconds = random.nextInt(100); + final BigInteger expected = BigInteger.valueOf((long) ((years * MONTHS_PER_YEAR + months) * daysPerMonthAvg + days)) + .multiply(HOURS_PER_DAY).add(BigInteger.valueOf(hours)) + .multiply(MINUTES_PER_HOUR).add(BigInteger.valueOf(minutes)) + .multiply(SECONDS_PER_MINUTE).add(BigInteger.valueOf(seconds)) + .multiply(MICROSECONDS_PER_SECOND).add(BigInteger.valueOf(microseconds)); + final Duration duration = Duration.builder() + .years(years) + .months(months) + .daysPerMonthAvg(daysPerMonthAvg) + .days(days) + .hours(hours) + .minutes(minutes) + .seconds(seconds) + .microseconds(microseconds) + .build(); + assertEquals(expected, duration.toMicros()); + assertTrue(expected.compareTo(BigInteger.ZERO) >= 0); + } + } + + @Test + public void testToMicrosWithYearsOnly() { + for (int i = 0; i < 100; i++) { + final int years = random.nextInt(100); + final BigInteger expected = BigInteger.valueOf((long) (years * MONTHS_PER_YEAR * DAYS_PER_MONTH_AVG)) + .multiply(HOURS_PER_DAY).multiply(MINUTES_PER_HOUR).multiply(SECONDS_PER_MINUTE).multiply(MICROSECONDS_PER_SECOND); + assertEquals(expected, Duration.builder().years(years).build().toMicros()); + assertTrue(expected.compareTo(BigInteger.ZERO) >= 0); + } + } + + @Test + public void testToMicrosWithMonthsOnly() { + for (int i = 0; i < 100; i++) { + final int months = random.nextInt(100); + final BigInteger expected = BigInteger.valueOf((long) (months * DAYS_PER_MONTH_AVG)) + .multiply(HOURS_PER_DAY).multiply(MINUTES_PER_HOUR).multiply(SECONDS_PER_MINUTE).multiply(MICROSECONDS_PER_SECOND); + assertEquals(expected, Duration.builder().months(months).build().toMicros()); + assertTrue(expected.compareTo(BigInteger.ZERO) >= 0); + } + } + + @Test + public void testToMicrosWithDaysOnly() { + for (int i = 0; i < 100; i++) { + final int days = random.nextInt(100); + final BigInteger expected = BigInteger.valueOf(days) + .multiply(HOURS_PER_DAY).multiply(MINUTES_PER_HOUR).multiply(SECONDS_PER_MINUTE).multiply(MICROSECONDS_PER_SECOND); + assertEquals(expected, Duration.builder().days(days).build().toMicros()); + assertTrue(expected.compareTo(BigInteger.ZERO) >= 0); + } + } + + @Test + public void testToMicrosWithHoursOnly() { + for (int i = 0; i < 100; i++) { + final int hours = random.nextInt(100); + final BigInteger expected = BigInteger.valueOf(hours) + .multiply(MINUTES_PER_HOUR).multiply(SECONDS_PER_MINUTE).multiply(MICROSECONDS_PER_SECOND); + assertEquals(expected, Duration.builder().hours(hours).build().toMicros()); + assertTrue(expected.compareTo(BigInteger.ZERO) >= 0); + } + } + + @Test + public void testToMicrosWithMinutesOnly() { + for (int i = 0; i < 100; i++) { + final int minutes = random.nextInt(100); + final BigInteger expected = BigInteger.valueOf(minutes) + .multiply(SECONDS_PER_MINUTE).multiply(MICROSECONDS_PER_SECOND); + assertEquals(expected, Duration.builder().minutes(minutes).build().toMicros()); + assertTrue(expected.compareTo(BigInteger.ZERO) >= 0); + } + } + + @Test + public void testToMicrosWithSecondsOnly() { + for (int i = 0; i < 100; i++) { + final int seconds = random.nextInt(100); + final BigInteger expected = BigInteger.valueOf(seconds).multiply(MICROSECONDS_PER_SECOND); + assertEquals(expected, Duration.builder().seconds(seconds).build().toMicros()); + assertTrue(expected.compareTo(BigInteger.ZERO) >= 0); + } + } + + @Test + public void testToMicrosWithMicrosecondsOnly() { + for (int i = 0; i < 100; i++) { + final int microseconds = random.nextInt(100); + final BigInteger expected = BigInteger.valueOf(microseconds); + assertEquals(expected, Duration.builder().microseconds(microseconds).build().toMicros()); + assertTrue(expected.compareTo(BigInteger.ZERO) >= 0); + } + } +} diff --git a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/utils/OracleUpsertUtilTest.java b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/utils/upsert/OracleUpsertUtilTest.java similarity index 51% rename from bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/utils/OracleUpsertUtilTest.java rename to bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/utils/upsert/OracleUpsertUtilTest.java index a556cbd72..5cac9c618 100644 --- a/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/utils/OracleUpsertUtilTest.java +++ b/bitsail-connectors/bitsail-connectors-legacy/bitsail-connector-jdbc/src/test/java/com/bytedance/bitsail/connector/legacy/jdbc/utils/upsert/OracleUpsertUtilTest.java @@ -15,38 +15,59 @@ * limitations under the License. */ -package com.bytedance.bitsail.connector.legacy.jdbc.utils; +package com.bytedance.bitsail.connector.legacy.jdbc.utils.upsert; import com.bytedance.bitsail.connector.legacy.jdbc.sink.OracleOutputFormat; -import com.bytedance.bitsail.connector.legacy.jdbc.utils.upsert.OracleUpsertUtil; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import junit.framework.TestCase; import org.junit.Assert; import org.junit.Test; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; public class OracleUpsertUtilTest extends TestCase { + @Test - public void testGenUpsertTemplate() { - List columns = new ArrayList<>(); - for (int i = 0; i < 2; i++) { - columns.add("COL" + i); - } + public void testGenUpsertTemplateWithNullUpsertKeys() { + String[] shardKeys = new String[]{"byte"}; + OracleUpsertUtil upsertUtil = new OracleUpsertUtil(new OracleOutputFormat(), shardKeys, null); + // Test overwriting query MERGE INTO + String expectedOverwriteQuery = "INSERT INTO \"BITSAIL_TEST\" (\"COL0\",\"COL1\") values (?,?)"; + Assert.assertEquals(expectedOverwriteQuery, upsertUtil.genUpsertTemplate("BITSAIL_TEST", provideColumns(2), "")); + } + @Test + public void testGenUpsertTemplateWithEmptyUpdateColumns() { String[] shardKeys = new String[]{"byte"}; - Map> upsertKeys = new HashMap<>(); - List cols = new ArrayList<>(); - cols.add("PK"); - upsertKeys.put("primary_key", cols); + Map> upsertKeys = ImmutableMap.of("primary_key", ImmutableList.of("COL0")); + OracleUpsertUtil upsertUtil = new OracleUpsertUtil(new OracleOutputFormat(), shardKeys, upsertKeys); + // Test overwriting query MERGE INTO + String expectedOverwriteQuery = "MERGE INTO \"BITSAIL_TEST\" T1 USING (SELECT ? \"COL0\" FROM DUAL) T2 ON (T1.\"COL0\"=T2.\"COL0\") " + + "WHEN NOT MATCHED THEN INSERT (\"COL0\") VALUES (\"T2\".\"COL0\")"; + Assert.assertEquals(expectedOverwriteQuery, upsertUtil.genUpsertTemplate("BITSAIL_TEST", provideColumns(1), "")); + } + + @Test + public void testGenUpsertTemplate() { + String[] shardKeys = new String[]{"byte"}; + Map> upsertKeys = ImmutableMap.of("primary_key", ImmutableList.of("PK")); OracleUpsertUtil upsertUtil = new OracleUpsertUtil(new OracleOutputFormat(), shardKeys, upsertKeys); // Test overwriting query MERGE INTO String expectedOverwriteQuery = "MERGE INTO \"BITSAIL_TEST\" T1 USING (SELECT ? \"COL0\",? \"COL1\" FROM DUAL) T2 ON (T1.\"PK\"=T2.\"PK\") " + "WHEN MATCHED THEN UPDATE SET \"T1\".COL0=\"T2\".COL0,\"T1\".COL1=\"T2\".COL1 " + "WHEN NOT MATCHED THEN INSERT (\"COL0\",\"COL1\") VALUES (\"T2\".\"COL0\",\"T2\".\"COL1\")"; - Assert.assertEquals(expectedOverwriteQuery, upsertUtil.genUpsertTemplate("BITSAIL_TEST", columns, "")); + Assert.assertEquals(expectedOverwriteQuery, upsertUtil.genUpsertTemplate("BITSAIL_TEST", provideColumns(2), "")); + } + + private List provideColumns(final int size) { + final List columns = new ArrayList<>(); + for (int i = 0; i < size; i++) { + columns.add("COL" + i); + } + return columns; } } \ No newline at end of file