/** * Determine whether this column has a {@link #typeName()} or {@link #jdbcType()} to which a character set applies. * * @return {@code true} if a character set applies the column's type, or {@code false} otherwise */ default boolean typeUsesCharset() { switch (jdbcType()) { case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: case Types.CLOB: case Types.NCHAR: case Types.NVARCHAR: case Types.LONGNVARCHAR: case Types.NCLOB: case Types.DATALINK: case Types.SQLXML: return true; default: return false; } } }
protected Object readField(ResultSet rs, int fieldNo, Column actualColumn) throws SQLException { if(actualColumn.jdbcType() == Types.TIME) { return readTimeField(rs, fieldNo); } // This is for DATETIME columns (a logical date + time without time zone) // by reading them with a calendar based on the default time zone, we make sure that the value // is constructed correctly using the database's (or connection's) time zone else if (actualColumn.jdbcType() == Types.TIMESTAMP) { return rs.getTimestamp(fieldNo, Calendar.getInstance()); } else { return rs.getObject(fieldNo); } }
@Override public ValueConverter create(Column column) { switch (column.jdbcType()) { case Types.CHAR: // variable-length case Types.VARCHAR: // variable-length case Types.LONGVARCHAR: // variable-length case Types.CLOB: // variable-length case Types.NCHAR: // fixed-length case Types.NVARCHAR: // fixed-length case Types.LONGNVARCHAR: // fixed-length case Types.NCLOB: // fixed-length case Types.DATALINK: return converter; default: return ValueConverter.passthrough(); } }
/** * Convert an unknown data value. * * @param column the column definition describing the {@code data} value; never null * @param fieldDefn the field definition; never null * @param data the data object to be converted into a {@link Date Kafka Connect date} type; never null * @return the converted value, or null if the conversion could not be made and the column allows nulls * @throws IllegalArgumentException if the value could not be converted but the column does not allow nulls */ protected Object handleUnknownData(Column column, Field fieldDefn, Object data) { if (column.isOptional() || fieldDefn.schema().isOptional()) { Class<?> dataClass = data.getClass(); logger.warn("Unexpected value for JDBC type {} and column {}: class={}", column.jdbcType(), column, dataClass.isArray() ? dataClass.getSimpleName() : dataClass.getName()); // don't include value in case its // sensitive return null; } throw new IllegalArgumentException("Unexpected value for JDBC type " + column.jdbcType() + " and column " + column + ": class=" + data.getClass()); // don't include value in case its sensitive }
@Override protected ByteBuffer convertByteArray(Column column, byte[] data) { // DBZ-254 right-pad fixed-length binary column values with 0x00 (zero byte) if (column.jdbcType() == Types.BINARY && data.length < column.length()) { data = Arrays.copyOf(data, column.length()); } return super.convertByteArray(column, data); }
/** * Converts a value object for an expected JDBC type of {@link Types#CHAR}, {@link Types#VARCHAR}, * {@link Types#LONGVARCHAR}, {@link Types#CLOB}, {@link Types#NCHAR}, {@link Types#NVARCHAR}, {@link Types#LONGNVARCHAR}, * {@link Types#NCLOB}, {@link Types#DATALINK}, and {@link Types#SQLXML}. * * @param column the column definition describing the {@code data} value; never null * @param fieldDefn the field definition; never null * @param data the data object to be converted into a {@link Date Kafka Connect date} type; never null * @return the converted value, or null if the conversion could not be made and the column allows nulls * @throws IllegalArgumentException if the value could not be converted but the column does not allow nulls */ protected Object convertString(Column column, Field fieldDefn, Object data) { return convertValue(column, fieldDefn, data, "", (r) -> { if (data instanceof SQLXML) { try { r.deliver(((SQLXML)data).getString()); } catch (SQLException e) { throw new RuntimeException("Error processing data from " + column.jdbcType() + " and column " + column + ": class=" + data.getClass(), e); } } else { r.deliver(data.toString()); } }); }
@Override public ValueConverter converter(Column column, Field fieldDefn) { switch (column.jdbcType()) { // Numeric integers case Types.TINYINT: // values are an 8-bit unsigned integer value between 0 and 255, we thus need to store it in short int return (data) -> convertSmallInt(column, fieldDefn, data); // Floating point case microsoft.sql.Types.SMALLMONEY: case microsoft.sql.Types.MONEY: return (data) -> convertDecimal(column, fieldDefn, data); case microsoft.sql.Types.DATETIMEOFFSET: return (data) -> convertTimestampWithZone(column, fieldDefn, data); // TODO Geometry and geography supported since 6.5.0 default: return super.converter(column, fieldDefn); } }
@Override public SchemaBuilder schemaBuilder(Column column) { switch (column.jdbcType()) { // Numeric integers case Types.TINYINT: // values are an 8-bit unsigned integer value between 0 and 255, we thus need to store it in short int return SchemaBuilder.int16(); // Floating point case microsoft.sql.Types.SMALLMONEY: case microsoft.sql.Types.MONEY: return SpecialValueDecimal.builder(decimalMode, column.length(), column.scale().get()); case microsoft.sql.Types.DATETIMEOFFSET: return ZonedTimestamp.builder(); default: return super.schemaBuilder(column); } }
/** * Add to the supplied {@link SchemaBuilder} a field for the column with the given information. * * @param builder the schema builder; never null * @param column the column definition * @param mapper the mapping function for the column; may be null if the columns is not to be mapped to different values */ protected void addField(SchemaBuilder builder, Column column, ColumnMapper mapper) { SchemaBuilder fieldBuilder = valueConverterProvider.schemaBuilder(column); if (fieldBuilder != null) { if (mapper != null) { // Let the mapper add properties to the schema ... mapper.alterFieldSchema(column, fieldBuilder); } if (column.isOptional()) fieldBuilder.optional(); // if the default value is provided if (column.hasDefaultValue()) { fieldBuilder.defaultValue(column.defaultValue()); } builder.field(column.name(), fieldBuilder.build()); if (LOGGER.isDebugEnabled()) { LOGGER.debug("- field '{}' ({}{}) from column {}", column.name(), builder.isOptional() ? "OPTIONAL " : "", fieldBuilder.type(), column); } } else { LOGGER.warn("Unexpected JDBC type '{}' for column '{}' that will be ignored", column.jdbcType(), column.name()); } }
@Override public boolean equals(Object obj) { if (obj == this) return true; if (obj instanceof Column) { Column that = (Column) obj; return this.name().equalsIgnoreCase(that.name()) && this.typeExpression().equalsIgnoreCase(that.typeExpression()) && this.typeName().equalsIgnoreCase(that.typeName()) && this.jdbcType() == that.jdbcType() && Strings.equalsIgnoreCase(this.charsetName(),that.charsetName()) && this.position() == that.position() && this.length() == that.length() && this.scale().equals(that.scale()) && this.isOptional() == that.isOptional() && this.isAutoIncremented() == that.isAutoIncremented() && this.isGenerated() == that.isGenerated() && Objects.equals(this.defaultValue(), that.defaultValue()) && this.hasDefaultValue() == that.hasDefaultValue(); } return false; }
if (selectedColumn != null) { changedColumns.add(column.edit() .jdbcType(selectedColumn.jdbcType()) .type(selectedColumn.typeName(), selectedColumn.typeExpression()) .length(selectedColumn.length())
return value; switch (column.jdbcType()) { case Types.DATE: return convertToLocalDate(column, value);
private Document toDocument(Column column) { Document document = Document.create(); document.setString("name", column.name()); document.setNumber("jdbcType", column.jdbcType()); if (column.nativeType() != Column.UNSET_INT_VALUE) { document.setNumber("nativeType", column.nativeType()); } document.setString("typeName", column.typeName()); document.setString("typeExpression", column.typeExpression()); document.setString("charsetName", column.charsetName()); if (column.length() != Column.UNSET_INT_VALUE) { document.setNumber("length", column.length()); } column.scale().ifPresent(s -> document.setNumber("scale", s)); document.setNumber("position", column.position()); document.setBoolean("optional", column.isOptional()); document.setBoolean("autoIncremented", column.isAutoIncremented()); document.setBoolean("generated", column.isGenerated()); return document; }
protected void assertColumn(Table table, String name, String typeName, int jdbcType, int length, int scale, boolean optional, boolean generated, boolean autoIncremented ) { Column column = table.columnWithName(name); assertThat(column.name()).isEqualTo(name); assertThat(column.typeName()).isEqualTo(typeName); assertThat(column.jdbcType()).isEqualTo(jdbcType); assertThat(column.length()).isEqualTo(length); if (scale == Column.UNSET_INT_VALUE) { assertFalse(column.scale().isPresent()); } else { assertThat(column.scale().get()).isEqualTo(scale); } assertThat(column.isOptional()).isEqualTo(optional); assertThat(column.isGenerated()).isEqualTo(generated); assertThat(column.isAutoIncremented()).isEqualTo(autoIncremented); }
protected void assertColumn(Table table, String name, String typeName, int jdbcType, int length, int scale, boolean optional, boolean generated, boolean autoIncremented) { Column column = table.columnWithName(name); assertThat(column.name()).isEqualTo(name); assertThat(column.typeName()).isEqualTo(typeName); assertThat(column.jdbcType()).isEqualTo(jdbcType); assertThat(column.length()).isEqualTo(length); if (scale == Column.UNSET_INT_VALUE) { assertFalse(column.scale().isPresent()); } else { assertThat(column.scale().get()).isEqualTo(scale); } assertThat(column.isOptional()).isEqualTo(optional); assertThat(column.isGenerated()).isEqualTo(generated); assertThat(column.isAutoIncremented()).isEqualTo(autoIncremented); }
switch (column.jdbcType()) { case Types.CHAR: // variable-length case Types.VARCHAR: // variable-length
@Test public void shouldCreateColumnWithAllFieldsSetToNonDefaults() { createColumnWithAllFieldsSetToNonDefaults(); assertThat(column.name()).isEqualTo("price"); assertThat(column.typeName()).isEqualTo("NUMBER"); assertThat(column.jdbcType()).isEqualTo(Types.DOUBLE); assertThat(column.length()).isEqualTo(5); assertThat(column.scale().get()).isEqualTo(2); assertThat(column.position()).isEqualTo(4); assertThat(column.isOptional()).isTrue(); assertThat(column.isAutoIncremented()).isTrue(); assertThat(column.isGenerated()).isTrue(); }
protected void assertColumn(Table table, String name, String typeName, int jdbcType, int length, String charsetName, boolean optional) { Column column = table.columnWithName(name); assertThat(column.name()).isEqualTo(name); assertThat(column.typeName()).isEqualTo(typeName); assertThat(column.jdbcType()).isEqualTo(jdbcType); assertThat(column.length()).isEqualTo(length); assertThat(column.charsetName()).isEqualTo(charsetName); assertFalse(column.scale().isPresent()); assertThat(column.isOptional()).isEqualTo(optional); assertThat(column.isGenerated()).isFalse(); assertThat(column.isAutoIncremented()).isFalse(); }
@Test public void shouldCreateColumnWithAllFieldsSetToDefaults() { Column column = editor.create(); assertThat(column.name()).isNull(); assertThat(column.typeName()).isNull(); assertThat(column.jdbcType()).isEqualTo(Types.INTEGER); assertThat(column.length()).isEqualTo(-1); Assert.assertFalse(column.scale().isPresent()); assertThat(column.position()).isEqualTo(1); assertThat(column.isOptional()).isTrue(); assertThat(column.isAutoIncremented()).isFalse(); assertThat(column.isGenerated()).isFalse(); }
@Override public ValueConverter converter(Column column, Field fieldDefn) { switch (column.jdbcType()) { case Types.NULL: return (data) -> null;