/** * Determine whether this column is required. This is equivalent to calling {@code !isOptional()}. * * @return {@code true} if it is required (not optional), or {@code false} otherwise * @see #isOptional() */ default boolean isRequired() { return !isOptional(); }
/** * Determine if the values in the named column is optional. * * @param columnName the name of the column * @return {@code true} if a column exists in this table and is optional, or {@code false} otherwise */ default boolean isOptional(String columnName) { Column column = columnWithName(columnName); return column == null ? false : column.isOptional(); }
/** * Converts a string object for an object type of {@link LocalDate}. * If the column definition allows null and default value is 0000-00-00, we need return null; * else 0000-00-00 will be replaced with 1970-01-01; * * @param column the column definition describing the {@code data} value; never null * @param value the string object to be converted into a {@link LocalDate} type; * @return the converted value; */ private Object convertToLocalDate(Column column, String value) { final boolean zero = EPOCH_EQUIVALENT_DATE.matcher(value).matches() || "0".equals(value); if (zero && column.isOptional()) { return null; } if (zero) { value = EPOCH_DATE; } return LocalDate.from(DateTimeFormatter.ISO_LOCAL_DATE.parse(value)); }
/** * Converts a string object for an object type of {@link Timestamp}. * If the column definition allows null and default value is 0000-00-00 00:00:00, we need return null, * else 0000-00-00 00:00:00 will be replaced with 1970-01-01 00:00:00; * * @param column the column definition describing the {@code data} value; never null * @param value the string object to be converted into a {@link Timestamp} type; * @return the converted value; */ private Object convertToTimestamp(Column column, String value) { final boolean matches = EPOCH_EQUIVALENT_TIMESTAMP.matcher(value).matches() || "0".equals(value) || EPOCH_TIMESTAMP.equals(value); if (matches) { if (column.isOptional()) { return null; } return Timestamp.from(Instant.EPOCH); } return Timestamp.valueOf(value).toInstant().atZone(ZoneId.systemDefault()); }
/** * Converts a string object for an object type of {@link LocalDateTime}. * If the column definition allows null and default value is 0000-00-00 00:00:00, we need return null, * else 0000-00-00 00:00:00 will be replaced with 1970-01-01 00:00:00; * * @param column the column definition describing the {@code data} value; never null * @param value the string object to be converted into a {@link LocalDateTime} type; * @return the converted value; */ private Object convertToLocalDateTime(Column column, String value) { final boolean matches = EPOCH_EQUIVALENT_TIMESTAMP.matcher(value).matches() || "0".equals(value); if (matches) { if (column.isOptional()) { return null; } value = EPOCH_TIMESTAMP; } return LocalDateTime.from(timestampFormat(column.length()).parse(value)); }
/** * 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 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; }
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; }
@Test public void shouldFilterColumnsUsingPredicate() { assertThat(table.filterColumns(c->c.isAutoIncremented())).containsExactly(c4); assertThat(table.filterColumns(c->c.isGenerated())).containsExactly(c1); assertThat(table.filterColumns(c->c.isOptional())).containsExactly(c3,c4); }
@Test public void parseUnsignedBigIntDefaultValueToBigDecimal() { final MySqlValueConverters converters = new MySqlValueConverters(JdbcValueConverters.DecimalMode.DOUBLE, TemporalPrecisionMode.CONNECT, JdbcValueConverters.BigIntUnsignedMode.PRECISE); final AbstractDdlParser parser = parserProducer.apply(converters); String sql = "CREATE TABLE UNSIGNED_BIGINT_TABLE (\n" + " A BIGINT UNSIGNED NULL DEFAULT 0,\n" + " B BIGINT UNSIGNED NULL DEFAULT '10',\n" + " C BIGINT UNSIGNED NULL,\n" + " D BIGINT UNSIGNED NOT NULL,\n" + " E BIGINT UNSIGNED NOT NULL DEFAULT 0,\n" + " F BIGINT UNSIGNED NOT NULL DEFAULT '0',\n" + " G BIGINT UNSIGNED NOT NULL DEFAULT '18446744073709551615'\n" + ");"; parser.parse(sql, tables); Table table = tables.forTable(new TableId(null, null, "UNSIGNED_BIGINT_TABLE")); assertThat(table.columnWithName("A").defaultValue()).isEqualTo(BigDecimal.ZERO); assertThat(table.columnWithName("B").defaultValue()).isEqualTo(new BigDecimal(10)); assertThat(table.columnWithName("C").isOptional()).isEqualTo(true); assertThat(table.columnWithName("C").hasDefaultValue()).isTrue(); assertThat(table.columnWithName("D").isOptional()).isEqualTo(false); assertThat(table.columnWithName("D").hasDefaultValue()).isFalse(); assertThat(table.columnWithName("E").isOptional()).isEqualTo(false); assertThat(table.columnWithName("E").defaultValue()).isEqualTo(BigDecimal.ZERO); assertThat(table.columnWithName("F").defaultValue()).isEqualTo(BigDecimal.ZERO); assertThat(table.columnWithName("G").defaultValue()).isEqualTo(new BigDecimal("18446744073709551615")); }
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); }
@Test public void parseUnsignedBigIntDefaultValueToLong() { String sql = "CREATE TABLE UNSIGNED_BIGINT_TABLE (\n" + " A BIGINT UNSIGNED NULL DEFAULT 0,\n" + " B BIGINT UNSIGNED NULL DEFAULT '10',\n" + " C BIGINT UNSIGNED NULL,\n" + " D BIGINT UNSIGNED NOT NULL,\n" + " E BIGINT UNSIGNED NOT NULL DEFAULT 0,\n" + " F BIGINT UNSIGNED NOT NULL DEFAULT '0'\n" + ");"; parser.parse(sql, tables); Table table = tables.forTable(new TableId(null, null, "UNSIGNED_BIGINT_TABLE")); assertThat(table.columnWithName("A").defaultValue()).isEqualTo(0L); assertThat(table.columnWithName("B").defaultValue()).isEqualTo(10L); assertThat(table.columnWithName("C").isOptional()).isEqualTo(true); assertThat(table.columnWithName("C").hasDefaultValue()).isTrue(); assertThat(table.columnWithName("D").isOptional()).isEqualTo(false); assertThat(table.columnWithName("D").hasDefaultValue()).isFalse(); assertThat(table.columnWithName("E").isOptional()).isEqualTo(false); assertThat(table.columnWithName("E").defaultValue()).isEqualTo(0L); assertThat(table.columnWithName("F").defaultValue()).isEqualTo(0L); }
@Test @FixFor("DBZ-860") public void shouldTreatPrimaryKeyColumnsImplicitlyAsNonNull() { String ddl = "CREATE TABLE data(id INT, PRIMARY KEY (id))" + "CREATE TABLE datadef(id INT DEFAULT 0, PRIMARY KEY (id))"; parser.parse(ddl, tables); Table table = tables.forTable(new TableId(null, null, "data")); assertThat(table.columnWithName("id").isOptional()).isEqualTo(false); assertThat(table.columnWithName("id").hasDefaultValue()).isEqualTo(false); Table tableDef = tables.forTable(new TableId(null, null, "datadef")); assertThat(tableDef.columnWithName("id").isOptional()).isEqualTo(false); assertThat(tableDef.columnWithName("id").hasDefaultValue()).isEqualTo(true); assertThat(tableDef.columnWithName("id").defaultValue()).isEqualTo(0); ddl = "CREATE TABLE data(id INT DEFAULT 1, PRIMARY KEY (id))"; parser.parse(ddl, tables); table = tables.forTable(new TableId(null, null, "data")); assertThat(table.columnWithName("id").isOptional()).isEqualTo(false); assertThat(table.columnWithName("id").hasDefaultValue()).isEqualTo(true); assertThat(table.columnWithName("id").defaultValue()).isEqualTo(1); }
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); }
@Test public void parseUnsignedIntDefaultValue() { String sql = "CREATE TABLE UNSIGNED_INT_TABLE (\n" + " A INT UNSIGNED NULL DEFAULT 0,\n" + " B INT UNSIGNED NULL DEFAULT '10',\n" + " C INT UNSIGNED NULL,\n" + " D INT UNSIGNED NOT NULL,\n" + " E INT UNSIGNED NOT NULL DEFAULT 0,\n" + " F INT UNSIGNED NOT NULL DEFAULT '0',\n" + " G INT UNSIGNED NOT NULL DEFAULT '4294967295'\n" + ");"; parser.parse(sql, tables); Table table = tables.forTable(new TableId(null, null, "UNSIGNED_INT_TABLE")); assertThat(table.columnWithName("A").defaultValue()).isEqualTo(0L); assertThat(table.columnWithName("B").defaultValue()).isEqualTo(10L); assertThat(table.columnWithName("C").isOptional()).isEqualTo(true); assertThat(table.columnWithName("C").hasDefaultValue()).isTrue(); assertThat(table.columnWithName("D").isOptional()).isEqualTo(false); assertThat(table.columnWithName("D").hasDefaultValue()).isFalse(); assertThat(table.columnWithName("E").isOptional()).isEqualTo(false); assertThat(table.columnWithName("E").defaultValue()).isEqualTo(0L); assertThat(table.columnWithName("F").defaultValue()).isEqualTo(0L); assertThat(table.columnWithName("G").defaultValue()).isEqualTo(4294967295L); }
@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(); }
@Test public void parseUnsignedTinyintDefaultValue() { String sql = "CREATE TABLE UNSIGNED_TINYINT_TABLE (" + " A TINYINT UNSIGNED NULL DEFAULT 0," + " B TINYINT UNSIGNED NULL DEFAULT '10'," + " C TINYINT UNSIGNED NULL," + " D TINYINT UNSIGNED NOT NULL," + " E TINYINT UNSIGNED NOT NULL DEFAULT 0," + " F TINYINT UNSIGNED NOT NULL DEFAULT '0'," + " G TINYINT UNSIGNED NOT NULL DEFAULT '255'" + ");"; parser.parse(sql, tables); Table table = tables.forTable(new TableId(null, null, "UNSIGNED_TINYINT_TABLE")); assertThat(table.columnWithName("A").defaultValue()).isEqualTo((short) 0); assertThat(table.columnWithName("B").defaultValue()).isEqualTo((short) 10); assertThat(table.columnWithName("C").isOptional()).isEqualTo(true); assertThat(table.columnWithName("C").hasDefaultValue()).isTrue(); assertThat(table.columnWithName("C").defaultValue()).isNull(); assertThat(table.columnWithName("D").isOptional()).isEqualTo(false); assertThat(table.columnWithName("D").hasDefaultValue()).isFalse(); assertThat(table.columnWithName("E").isOptional()).isEqualTo(false); assertThat(table.columnWithName("E").defaultValue()).isEqualTo((short) 0); assertThat(table.columnWithName("F").defaultValue()).isEqualTo((short) 0); assertThat(table.columnWithName("G").defaultValue()).isEqualTo((short) 255); }
@Test public void parseDefaultValue() { String ddl = "CREATE TABLE tmp (id INT NOT NULL, " + "columnA CHAR(60) NOT NULL DEFAULT 'A'," + "columnB INT NOT NULL DEFAULT 1," + "columnC VARCHAR(10) NULL DEFAULT 'C'," + "columnD VARCHAR(10) NULL DEFAULT NULL," + "columnE VARCHAR(10) NOT NULL," + "my_date datetime NOT NULL DEFAULT '2018-04-27 13:28:43');"; parser.parse(ddl, tables); Table table = tables.forTable(new TableId(null, null, "tmp")); assertThat(table.columnWithName("id").isOptional()).isEqualTo(false); assertThat(table.columnWithName("columnA").defaultValue()).isEqualTo("A"); assertThat(table.columnWithName("columnB").defaultValue()).isEqualTo(1); assertThat(table.columnWithName("columnC").defaultValue()).isEqualTo("C"); assertThat(table.columnWithName("columnD").defaultValue()).isEqualTo(null); assertThat(table.columnWithName("columnE").defaultValue()).isEqualTo(null); }