diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/build.gradle b/eventmesh-connectors/eventmesh-connector-jdbc/build.gradle index b6d33aebe3..603cf8564f 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/build.gradle +++ b/eventmesh-connectors/eventmesh-connector-jdbc/build.gradle @@ -36,11 +36,14 @@ packageSources { dependencies { antlr("org.antlr:antlr4:4.13.0") implementation 'org.antlr:antlr4-runtime:4.13.0' + implementation 'com.alibaba:druid:1.2.20' + implementation 'org.hibernate:hibernate-core:5.6.15.Final' implementation project(":eventmesh-common") implementation project(":eventmesh-openconnect:eventmesh-openconnect-java") implementation project(":eventmesh-spi") implementation 'com.zendesk:mysql-binlog-connector-java:0.28.0' implementation 'mysql:mysql-connector-java:8.0.32' + compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/CatalogChanges.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/CatalogChanges.java index 34d2554f65..c835862eda 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/CatalogChanges.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/CatalogChanges.java @@ -25,11 +25,13 @@ import java.util.List; import lombok.Data; +import lombok.NoArgsConstructor; -@Data /** * Represents changes in a catalog, such as schema or table modifications. */ +@Data +@NoArgsConstructor public class CatalogChanges { /** @@ -52,10 +54,10 @@ public class CatalogChanges { // The table associated with the changes private Table table; // The list of columns affected by the changes - private List columns; + private List> columns; private CatalogChanges(String type, String operationType, CatalogSchema catalog, Table table, - List columns) { + List> columns) { this.type = type; this.operationType = operationType; this.catalog = catalog; @@ -81,7 +83,7 @@ public static class Builder { private String operationType; private CatalogSchema catalog; private Table table; - private List columns; + private List> columns; /** * Sets the operation type for the change. @@ -123,7 +125,7 @@ public Builder table(Table table) { * @param columns The list of Column instances. * @return The Builder instance. */ - public Builder columns(List columns) { + public Builder columns(List> columns) { this.columns = columns; return this; } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/DataChanges.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/DataChanges.java index cb0298e91f..a23e521cf2 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/DataChanges.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/DataChanges.java @@ -18,52 +18,107 @@ package org.apache.eventmesh.connector.jdbc; import lombok.Data; +import lombok.NoArgsConstructor; @Data +@NoArgsConstructor +/** + * DataChanges class representing changes in data associated with a JDBC connection. + */ public class DataChanges { private Object after; private Object before; + /** + * The type of change. + * + * {@link org.apache.eventmesh.connector.jdbc.event.DataChangeEventType} + * + */ private String type; + /** + * Constructs a DataChanges instance with 'after' and 'before' data. + * + * @param after The data after the change. + * @param before The data before the change. + */ public DataChanges(Object after, Object before) { this.after = after; this.before = before; } + /** + * Constructs a DataChanges instance with 'after', 'before' data, and a change type. + * + * @param after The data after the change. + * @param before The data before the change. + * @param type The type of change. + */ public DataChanges(Object after, Object before, String type) { this.after = after; this.before = before; this.type = type; } + /** + * Creates a new DataChanges builder. + * + * @return The DataChanges builder. + */ public static Builder newBuilder() { return new Builder(); } + /** + * Builder class for constructing DataChanges instances. + */ public static class Builder { private String type; private Object after; private Object before; + /** + * Sets the change type in the builder. + * + * @param type The type of change. + * @return The DataChanges builder. + */ public Builder withType(String type) { this.type = type; return this; } + /** + * Sets the 'after' data in the builder. + * + * @param after The data after the change. + * @return The DataChanges builder. + */ public Builder withAfter(Object after) { this.after = after; return this; } + /** + * Sets the 'before' data in the builder. + * + * @param before The data before the change. + * @return The DataChanges builder. + */ public Builder withBefore(Object before) { this.before = before; return this; } + /** + * Builds the DataChanges instance. + * + * @return The constructed DataChanges. + */ public DataChanges build() { return new DataChanges(after, before, type); } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/Field.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/Field.java index 66d583de73..bdbf7aa686 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/Field.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/Field.java @@ -17,6 +17,8 @@ package org.apache.eventmesh.connector.jdbc; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; + import java.util.List; import lombok.AllArgsConstructor; @@ -28,25 +30,25 @@ @AllArgsConstructor public class Field { - private String type; - private boolean required; private String field; private String name; + private Column column; + private List fields; - public Field(String type, boolean required, String field, String name) { - this.type = type; + public Field(Column column, boolean required, String field, String name) { + this.column = column; this.required = required; this.field = field; this.name = name; } - public Field withType(String type) { - this.type = type; + public Field withColumn(Column column) { + this.column = column; return this; } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/JdbcConnectData.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/JdbcConnectData.java index 3dca4f42f4..df749947e7 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/JdbcConnectData.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/JdbcConnectData.java @@ -17,17 +17,26 @@ package org.apache.eventmesh.connector.jdbc; +/** + * Represents data associated with a JDBC connector. + */ public final class JdbcConnectData { + /** + * Constant representing data changes in the JDBC connector. + */ public static final byte DATA_CHANGES = 1; + /** + * Constant representing schema changes in the JDBC connector. + */ public static final byte SCHEMA_CHANGES = 1 << 1; private Payload payload = new Payload(); private Schema schema; - private byte type; + private byte type = 0; public JdbcConnectData() { } @@ -67,4 +76,12 @@ public void markDataChanges() { public void markSchemaChanges() { this.type |= SCHEMA_CHANGES; } + + public boolean isDataChanges() { + return (this.type & DATA_CHANGES) != 0; + } + + public boolean isSchemaChanges() { + return (this.type & SCHEMA_CHANGES) != 0; + } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/Payload.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/Payload.java index f9b66d3d53..f861445aa3 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/Payload.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/Payload.java @@ -19,53 +19,157 @@ import org.apache.eventmesh.connector.jdbc.source.SourceMateData; -import java.util.HashMap; +import lombok.Getter; +import lombok.Setter; -public final class Payload extends HashMap { +/** + * Payload class representing the data associated with a JDBC connection. + */ +public final class Payload { + /** + * Field name for the 'after' payload. + */ public static final String AFTER_FIELD = "after"; + /** + * Field name for the 'before' payload. + */ public static final String BEFORE_FIELD = "before"; + /** + * Field name for the 'source' payload. + */ public static final String SOURCE = "source"; - public static final String DDL = "ddl"; + /** + * Field name for the 'payload.before' payload. + */ + public static final String PAYLOAD_BEFORE = "payload.before"; + + /** + * Field name for the 'payload.after' payload. + */ + public static final String PAYLOAD_AFTER = "payload.after"; + + @Getter + @Setter + private SourceMateData source; + + // Source connector's original DDL script + @Getter + @Setter + private String ddl; + + @Getter + @Setter + private CatalogChanges catalogChanges; + + @Getter + @Setter + private DataChanges dataChanges; + + @Getter + @Setter + private long timestamp; /** - * Constructs an empty HashMap with the default initial capacity (16) and the default load factor (0.75). + * Constructs an empty Payload with the default initial capacity (16) and the default load factor (0.75). */ public Payload() { - this.put("timestamp", System.currentTimeMillis()); + this.timestamp = System.currentTimeMillis(); } - public Payload withSource(SourceMateData source) { - this.put(SOURCE, source); + /** + * Sets the 'source' field in the payload. + * + * @param source The SourceMateData to set. + * @return The Payload instance. + */ + public Payload withSource(S source) { + this.source = source; return this; } + /** + * Sets the 'ddl' field in the payload. + * + * @param ddl The DDL string to set. + * @return The Payload instance. + */ public Payload withDdl(String ddl) { - this.put(DDL, ddl); + this.ddl = ddl; return this; } + /** + * Sets the 'catalogChanges' field in the payload. + * + * @param catalogChanges The CatalogChanges to set. + * @return The Payload instance. + */ public Payload withCatalogChanges(CatalogChanges catalogChanges) { - this.put("catalogChanges", catalogChanges); + this.catalogChanges = catalogChanges; return this; } + /** + * Sets the 'dataChanges' field in the payload. + * + * @param dataChanges The DataChanges to set. + * @return The Payload instance. + */ public Payload withDataChanges(DataChanges dataChanges) { - this.put("dataChanges", dataChanges); + this.dataChanges = dataChanges; return this; } + /** + * Retrieves the 'source' field from the payload. + * + * @return The SourceMateData. + */ public SourceMateData ofSourceMateData() { - return (SourceMateData) super.get(SOURCE); + return getSource(); + } + + /** + * Retrieves the 'catalogChanges' field from the payload. + * + * @return The CatalogChanges. + */ + public CatalogChanges ofCatalogChanges() { + return getCatalogChanges(); + } + + /** + * Retrieves the 'dataChanges' field from the payload. + * + * @return The DataChanges. + */ + public DataChanges ofDataChanges() { + return getDataChanges(); } + /** + * Retrieves the 'ddl' field from the payload. + * + * @return The DDL string. + */ + public String ofDdl() { + return getDdl(); + } + + /** + * Builder class for constructing Payload instances. + */ public static Builder builder() { return new Builder(); } + /** + * Builder class for constructing Payload instances. + */ public static class Builder { private final Payload payload; @@ -74,19 +178,24 @@ private Builder() { payload = new Payload(); } - public Builder put(String key, Object value) { - payload.put(key, value); - return this; - } - + /** + * Sets the 'source' field in the payload. + * + * @param source The SourceMateData to set. + * @return The Builder instance. + */ public Builder withSource(SourceMateData source) { - payload.put(SOURCE, source); + payload.withSource(source); return this; } + /** + * Builds the Payload instance. + * + * @return The constructed Payload. + */ public Payload build() { return payload; } } - } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/Schema.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/Schema.java index 084f9759bc..75674bd798 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/Schema.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/Schema.java @@ -18,13 +18,19 @@ package org.apache.eventmesh.connector.jdbc; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; import lombok.Data; @Data public class Schema { + // contains primary key and unique key + private Set keySet; + private List fields; public Schema(List fields) { @@ -38,4 +44,22 @@ public Schema() { public void add(Field field) { this.fields.add(field); } + + public void addKey(String key) { + if (keySet == null) { + keySet = new HashSet<>(); + } + this.keySet.add(key); + } + + public void addKeys(Collection keys) { + if (keySet == null) { + keySet = new HashSet<>(); + } + this.keySet.addAll(keys); + } + + public boolean containsKey() { + return keySet != null && !keySet.isEmpty(); + } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/UniversalJdbcContext.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/UniversalJdbcContext.java index af89638149..e0f656da23 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/UniversalJdbcContext.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/UniversalJdbcContext.java @@ -52,9 +52,6 @@ public OffSetCtx getOffsetContext() { return poCtx.getOffsetContext(); } - /** - * @return - */ @Override public TableId ofCurrentTableId() { return tableId; diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlTableOptions.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/common/EnumeratedValue.java similarity index 70% rename from eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlTableOptions.java rename to eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/common/EnumeratedValue.java index d0a3c1108d..484b6a2910 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlTableOptions.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/common/EnumeratedValue.java @@ -15,15 +15,19 @@ * limitations under the License. */ -package org.apache.eventmesh.connector.jdbc.table.catalog.mysql; +package org.apache.eventmesh.connector.jdbc.common; -public class MysqlTableOptions { - - public static String ENGINE = "ENGINE"; - - public static String AUTO_INCREMENT = "AUTO_INCREMENT"; - - public static String CHARSET = "CHARSET"; +/** + * An interface representing an enumerated value. + * + * @param The type of the enumerated value. + */ +public interface EnumeratedValue { - public static String COLLATE = "COLLATE"; + /** + * Gets the value of the enumerated item. + * + * @return The value of the enumerated item. + */ + T getValue(); } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/config/JdbcConfig.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/config/JdbcConfig.java index 9c7558426c..d40801854c 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/config/JdbcConfig.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/config/JdbcConfig.java @@ -49,6 +49,9 @@ public class JdbcConfig { private int connectTimeout; + // e.g jdbc:mysql://127.0.0.1:3306 + private String url; + /** * Converts the JdbcConfig object to a Properties object containing the user and password. * diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/connection/JdbcConnection.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/connection/JdbcConnection.java index 78ddcad8e4..d1802d8b96 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/connection/JdbcConnection.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/connection/JdbcConnection.java @@ -32,6 +32,7 @@ import javax.annotation.concurrent.ThreadSafe; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; /** @@ -45,6 +46,7 @@ public class JdbcConnection implements AutoCloseable { private static final String STATEMENT_DELIMITER = ";"; + @Getter private final JdbcConfig jdbcConfig; private volatile Connection connection; @@ -88,15 +90,6 @@ public void close() throws Exception { } } - /** - * Retrieves the JDBC configuration. - * - * @return The JDBC configuration. - */ - public JdbcConfig getJdbcConfig() { - return jdbcConfig; - } - /** * Sets the auto-commit mode for the connection. * @@ -223,7 +216,6 @@ public JdbcConnection executeWithoutCommitting(String... sqlStatements) throws S statement.execute(sqlStatement); } } - return this; } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/AbstractGeneralDatabaseDialect.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/AbstractGeneralDatabaseDialect.java new file mode 100644 index 0000000000..64bde49935 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/AbstractGeneralDatabaseDialect.java @@ -0,0 +1,156 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.dialect; + +import org.apache.eventmesh.connector.jdbc.config.JdbcConfig; +import org.apache.eventmesh.connector.jdbc.connection.JdbcConnection; +import org.apache.eventmesh.connector.jdbc.exception.JdbcConnectionException; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.catalog.TableId; +import org.apache.eventmesh.connector.jdbc.type.Type; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.BooleanEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.BytesEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.DateEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.DateTimeEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.DecimalEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Float32EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Float64EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Int16EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Int32EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Int64EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Int8EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.StringEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.TimeEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.YearEventMeshDataType; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.hibernate.dialect.Dialect; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public abstract class AbstractGeneralDatabaseDialect implements DatabaseDialect { + + private static final int DEFAULT_BATCH_MAX_ROWS = 20; + + private JdbcConfig config; + + private int batchMaxRows = DEFAULT_BATCH_MAX_ROWS; + + private final Map typeRegisters = new HashMap<>(32); + + private Dialect hibernateDialect; + + public AbstractGeneralDatabaseDialect(JdbcConfig config) { + this.config = config; + } + + @Override + public void configure(Dialect hibernateDialect) { + this.hibernateDialect = hibernateDialect; + } + + @Override + public boolean isValid(Connection connection, int timeout) throws JdbcConnectionException, SQLException { + return connection == null ? false : connection.isValid(timeout); + } + + @Override + public PreparedStatement createPreparedStatement(Connection connection, String sql) throws SQLException { + PreparedStatement preparedStatement = connection.prepareStatement(sql); + if (batchMaxRows > 0) { + preparedStatement.setFetchSize(batchMaxRows); + } + return preparedStatement; + } + + @Override + public Type getType(Column column) { + final String nativeType = column.getNativeType(); + if (nativeType != null) { + final Type type = typeRegisters.get(nativeType); + if (type != null) { + log.debug("found type {} for column {}", type.getClass().getName(), column.getName()); + return type; + } + } + final String dataTypeName = column.getDataType().getName(); + if (dataTypeName != null) { + final Type type = typeRegisters.get(dataTypeName); + if (type != null) { + log.debug("found type {} for column {}", type.getClass().getName(), column.getName()); + return type; + } + } + + final String jdbcTypeName = column.getJdbcType().name(); + if (jdbcTypeName != null) { + final Type type = typeRegisters.get(jdbcTypeName); + if (type != null) { + log.debug("found type {} for column {}", type.getClass().getName(), column.getName()); + return type; + } + } + + return null; + } + + protected void registerTypes() { + registerType(BooleanEventMeshDataType.INSTANCE); + registerType(Float32EventMeshDataType.INSTANCE); + registerType(Float64EventMeshDataType.INSTANCE); + registerType(Int8EventMeshDataType.INSTANCE); + registerType(Int16EventMeshDataType.INSTANCE); + registerType(Int32EventMeshDataType.INSTANCE); + registerType(Int64EventMeshDataType.INSTANCE); + registerType(StringEventMeshDataType.INSTANCE); + registerType(DateEventMeshDataType.INSTANCE); + registerType(TimeEventMeshDataType.INSTANCE); + registerType(DateTimeEventMeshDataType.INSTANCE); + registerType(DecimalEventMeshDataType.INSTANCE); + registerType(BytesEventMeshDataType.INSTANCE); + registerType(YearEventMeshDataType.INSTANCE); + } + + protected void registerType(Type type) { + type.configure(this, hibernateDialect); + Optional.ofNullable(type.ofRegistrationKeys()).orElse(new ArrayList<>(0)).forEach(key -> typeRegisters.put(key, type)); + } + + public abstract String getQualifiedTableName(TableId tableId); + + public abstract String getQualifiedText(String text); + + @Override + public String getTypeName(Dialect hibernateDialect, Column column) { + Type type = this.getType(column); + if (null != type) { + return type.getTypeName(column); + } + Long length = Optional.ofNullable(column.getColumnLength()).orElse(0L); + return hibernateDialect.getTypeName(column.getJdbcType().getVendorTypeNumber(), length, length.intValue(), + Optional.ofNullable(column.getDecimal()).orElse(0)); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/DatabaseDialect.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/DatabaseDialect.java similarity index 85% rename from eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/DatabaseDialect.java rename to eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/DatabaseDialect.java index 11378270a4..86befeae85 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/DatabaseDialect.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/DatabaseDialect.java @@ -15,11 +15,13 @@ * limitations under the License. */ -package org.apache.eventmesh.connector.jdbc; +package org.apache.eventmesh.connector.jdbc.dialect; +import org.apache.eventmesh.connector.jdbc.JdbcDriverMetaData; import org.apache.eventmesh.connector.jdbc.connection.JdbcConnection; import org.apache.eventmesh.connector.jdbc.connection.JdbcConnectionProvider; import org.apache.eventmesh.connector.jdbc.table.catalog.Catalog; +import org.apache.eventmesh.connector.jdbc.type.DatabaseTypeDialect; import java.sql.Connection; import java.sql.PreparedStatement; @@ -28,7 +30,7 @@ /** * Interface for a database dialect, which extends the ConnectionProvider and Catalog interfaces. */ -public interface DatabaseDialect extends JdbcConnectionProvider, Catalog { +public interface DatabaseDialect extends JdbcConnectionProvider, Catalog, DatabaseTypeDialect { /** * Initializes the database dialect. @@ -41,11 +43,11 @@ public interface DatabaseDialect extends JdbcConnecti void start(); /** - * Retrieves the name of the database dialect. + * Retrieves the type of the database dialect. * - * @return The name of the database dialect. + * @return The type of the database dialect. */ - String getName(); + DatabaseType getDatabaseType(); /** * Creates a prepared statement for the given SQL query using the provided database connection. @@ -70,4 +72,5 @@ public interface DatabaseDialect extends JdbcConnecti * @return The JDBC protocol. */ String jdbcProtocol(); + } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/DatabaseDialectFactory.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/DatabaseDialectFactory.java similarity index 79% rename from eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/DatabaseDialectFactory.java rename to eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/DatabaseDialectFactory.java index 8f73f9f53e..aad7984520 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/DatabaseDialectFactory.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/DatabaseDialectFactory.java @@ -15,10 +15,9 @@ * limitations under the License. */ -package org.apache.eventmesh.connector.jdbc.source.dialect; +package org.apache.eventmesh.connector.jdbc.dialect; -import org.apache.eventmesh.connector.jdbc.DatabaseDialect; -import org.apache.eventmesh.openconnect.api.config.SourceConfig; +import org.apache.eventmesh.connector.jdbc.config.JdbcConfig; import org.apache.eventmesh.spi.EventMeshExtensionType; import org.apache.eventmesh.spi.EventMeshSPI; @@ -31,9 +30,9 @@ public interface DatabaseDialectFactory { /** * Creates a database dialect based on the provided source configuration. * - * @param config the source configuration to create a database dialect for + * @param config the jdbc configuration to create a database dialect for * @return the created database dialect */ - DatabaseDialect createDatabaseDialect(SourceConfig config); + DatabaseDialect createDatabaseDialect(JdbcConfig config); } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/DatabaseType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/DatabaseType.java similarity index 83% rename from eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/DatabaseType.java rename to eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/DatabaseType.java index 2e56932004..b1db56c526 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/DatabaseType.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/DatabaseType.java @@ -15,24 +15,27 @@ * limitations under the License. */ -package org.apache.eventmesh.connector.jdbc.source.dialect; +package org.apache.eventmesh.connector.jdbc.dialect; import org.apache.commons.lang3.StringUtils; +import lombok.Getter; + public enum DatabaseType { MYSQL("mysql"); - private String name; + @Getter + private String code; - DatabaseType(String name) { - this.name = name; + DatabaseType(String code) { + this.code = code; } public static DatabaseType ofValue(String name) { DatabaseType[] databaseTypes = values(); for (DatabaseType databaseType : databaseTypes) { - if (StringUtils.equalsIgnoreCase(databaseType.name, name)) { + if (StringUtils.equalsIgnoreCase(databaseType.code, name)) { return databaseType; } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/SqlStatementAssembler.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/SqlStatementAssembler.java new file mode 100644 index 0000000000..6e3858369a --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/SqlStatementAssembler.java @@ -0,0 +1,66 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.dialect; + +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; + +import java.util.Collection; +import java.util.Iterator; +import java.util.function.Function; + +/** + * The {@code SqlStatementAssembler} class is used to assemble SQL statements by appending SQL slices. + */ +public final class SqlStatementAssembler { + + private final StringBuilder statement; + + public SqlStatementAssembler() { + statement = new StringBuilder(); + } + + public SqlStatementAssembler appendSqlSlice(String slice) { + statement.append(slice); + return this; + } + + public SqlStatementAssembler appendSqlSliceLists(String delimiter, Collection columnNames, Function function) { + for (Iterator iterator = columnNames.iterator(); iterator.hasNext();) { + statement.append(function.apply(iterator.next())); + if (iterator.hasNext()) { + statement.append(delimiter); + } + } + return this; + } + + public SqlStatementAssembler appendSqlSliceOfColumns(String delimiter, Collection> columns, Function, String> function) { + for (Iterator> iterator = columns.iterator(); iterator.hasNext();) { + statement.append(function.apply(iterator.next())); + if (iterator.hasNext()) { + statement.append(delimiter); + } + } + return this; + } + + public String build() { + return statement.toString(); + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/mysql/MysqlDatabaseDialect.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/mysql/MysqlDatabaseDialect.java similarity index 63% rename from eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/mysql/MysqlDatabaseDialect.java rename to eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/mysql/MysqlDatabaseDialect.java index bfd6a73006..014af30868 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/mysql/MysqlDatabaseDialect.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/mysql/MysqlDatabaseDialect.java @@ -15,22 +15,52 @@ * limitations under the License. */ -package org.apache.eventmesh.connector.jdbc.source.dialect.mysql; +package org.apache.eventmesh.connector.jdbc.dialect.mysql; import org.apache.eventmesh.connector.jdbc.DataTypeConvertor; import org.apache.eventmesh.connector.jdbc.JdbcDriverMetaData; +import org.apache.eventmesh.connector.jdbc.config.JdbcConfig; import org.apache.eventmesh.connector.jdbc.connection.mysql.MysqlJdbcConnection; +import org.apache.eventmesh.connector.jdbc.dialect.AbstractGeneralDatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseType; import org.apache.eventmesh.connector.jdbc.exception.CatalogException; import org.apache.eventmesh.connector.jdbc.exception.DatabaseNotExistException; import org.apache.eventmesh.connector.jdbc.exception.TableNotExistException; -import org.apache.eventmesh.connector.jdbc.source.config.JdbcSourceConfig; -import org.apache.eventmesh.connector.jdbc.source.config.SourceConnectorConfig; -import org.apache.eventmesh.connector.jdbc.source.dialect.AbstractGeneralDatabaseDialect; +import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlDataTypeConvertor; +import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlDialectSql; import org.apache.eventmesh.connector.jdbc.table.catalog.CatalogTable; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; import org.apache.eventmesh.connector.jdbc.table.catalog.DefaultColumn; +import org.apache.eventmesh.connector.jdbc.table.catalog.Options; import org.apache.eventmesh.connector.jdbc.table.catalog.PrimaryKey; +import org.apache.eventmesh.connector.jdbc.table.catalog.Table; import org.apache.eventmesh.connector.jdbc.table.catalog.TableId; import org.apache.eventmesh.connector.jdbc.table.catalog.TableSchema; +import org.apache.eventmesh.connector.jdbc.table.catalog.mysql.MysqlColumn; +import org.apache.eventmesh.connector.jdbc.table.catalog.mysql.MysqlOptions.MysqlTableOptions; +import org.apache.eventmesh.connector.jdbc.type.Type; +import org.apache.eventmesh.connector.jdbc.type.mysql.BitType; +import org.apache.eventmesh.connector.jdbc.type.mysql.BytesType; +import org.apache.eventmesh.connector.jdbc.type.mysql.DecimalType; +import org.apache.eventmesh.connector.jdbc.type.mysql.EnumType; +import org.apache.eventmesh.connector.jdbc.type.mysql.GeometryCollectionType; +import org.apache.eventmesh.connector.jdbc.type.mysql.GeometryType; +import org.apache.eventmesh.connector.jdbc.type.mysql.IntType; +import org.apache.eventmesh.connector.jdbc.type.mysql.JsonType; +import org.apache.eventmesh.connector.jdbc.type.mysql.LineStringType; +import org.apache.eventmesh.connector.jdbc.type.mysql.MediumintType; +import org.apache.eventmesh.connector.jdbc.type.mysql.MultiLineStringType; +import org.apache.eventmesh.connector.jdbc.type.mysql.MultiPointType; +import org.apache.eventmesh.connector.jdbc.type.mysql.MultiPolygonType; +import org.apache.eventmesh.connector.jdbc.type.mysql.PointType; +import org.apache.eventmesh.connector.jdbc.type.mysql.PolygonType; +import org.apache.eventmesh.connector.jdbc.type.mysql.SetType; +import org.apache.eventmesh.connector.jdbc.type.mysql.TextType; +import org.apache.eventmesh.connector.jdbc.type.mysql.TinyIntType; +import org.apache.eventmesh.connector.jdbc.type.mysql.YearType; +import org.apache.eventmesh.connector.jdbc.utils.MysqlUtils; + +import org.apache.commons.lang3.StringUtils; import java.sql.Connection; import java.sql.PreparedStatement; @@ -48,17 +78,17 @@ import lombok.extern.slf4j.Slf4j; @Slf4j -public class MysqlDatabaseDialect extends AbstractGeneralDatabaseDialect { +public class MysqlDatabaseDialect extends AbstractGeneralDatabaseDialect { private MysqlJdbcConnection connection; private DataTypeConvertor dataTypeConvertor = new MysqlDataTypeConvertor(); - private SourceConnectorConfig config; + private JdbcConfig config; - public MysqlDatabaseDialect(JdbcSourceConfig config) { - super(config.getSourceConnectorConfig()); - this.config = config.getSourceConnectorConfig(); + public MysqlDatabaseDialect(JdbcConfig config) { + super(config); + this.config = config; } @Override @@ -80,6 +110,31 @@ public void init() { } } while (!initSuccess); + // handle type register + super.registerTypes(); + registerType(BitType.INSTANCE); + registerType(SetType.INSTANCE); + registerType(EnumType.INSTANCE); + registerType(TinyIntType.INSTANCE); + registerType(JsonType.INSTANCE); + registerType(IntType.INSTANCE); + registerType(MediumintType.INSTANCE); + registerType(DecimalType.INSTANCE); + registerType(TextType.INSTANCE); + + // override YearEventMeshDateType + registerType(YearType.INSTANCE); + registerType(BytesType.INSTANCE); + + // Spatial Data Types + registerType(PointType.INSTANCE); + registerType(MultiPointType.INSTANCE); + registerType(GeometryType.INSTANCE); + registerType(GeometryCollectionType.INSTANCE); + registerType(LineStringType.INSTANCE); + registerType(MultiLineStringType.INSTANCE); + registerType(PolygonType.INSTANCE); + registerType(MultiPolygonType.INSTANCE); } @Override @@ -89,7 +144,7 @@ public void start() { private MysqlJdbcConnection initJdbcConnection() { try { - return new MysqlJdbcConnection(config.getJdbcConfig(), null, false); + return new MysqlJdbcConnection(config, null, false); } catch (Exception e) { throw new CatalogException(e); } @@ -196,7 +251,7 @@ public CatalogTable getTable(TableId tableId) throws CatalogException, TableNotE DefaultColumn column = columns.computeIfAbsent(columnName, key -> new DefaultColumn()); column.setName(columnName); int precision = tableMetaData.getPrecision(columnIndex); - column.setColumnLength(precision); + // column.setColumnLength(precision); Map dataTypeProperties = new HashMap<>(); dataTypeProperties.put(MysqlDataTypeConvertor.PRECISION, precision); int scale = tableMetaData.getScale(columnIndex); @@ -250,8 +305,8 @@ public CatalogTable getTable(TableId tableId) throws CatalogException, TableNotE } @Override - public String getName() { - return null; + public DatabaseType getDatabaseType() { + return DatabaseType.MYSQL; } @Override @@ -261,6 +316,16 @@ public PreparedStatement createPreparedStatement(Connection connection, String s return connection.prepareStatement(sql); } + @Override + public String getQualifiedTableName(TableId tableId) { + return MysqlUtils.wrapper(tableId); + } + + @Override + public String getQualifiedText(String text) { + return MysqlUtils.wrapper(text); + } + /** * Retrieves the JDBC driver meta-data associated with the database dialect. * @@ -302,4 +367,72 @@ public void close() throws Exception { this.connection.close(); } } + + @Override + public String getAutoIncrementFormatted(Column column) { + return " AUTO_INCREMENT "; + } + + @Override + public String getDefaultValueFormatted(Column column) { + Type type = this.getType(column); + String defaultValue = type.getDefaultValue(this, column); + return defaultValue; + } + + @Override + public String getCharsetOrCollateFormatted(Column column) { + StringBuilder builder = new StringBuilder(); + String charsetName = column.getCharsetName(); + if (StringUtils.isNotBlank(charsetName)) { + builder.append(" CHARACTER SET ").append(charsetName).append(" "); + } + String collationName = column.getCollationName(); + if (StringUtils.isNotBlank(collationName)) { + builder.append(" COLLATE ").append(collationName).append(" "); + } + + return builder.toString(); + } + + @Override + public String getTableOptionsFormatted(Table table) { + + Options options = table.getOptions(); + if (Objects.isNull(options) || options.isEmpty()) { + return EMPTY_STRING; + } + StringBuilder builder = new StringBuilder(); + String engine = (String) options.get(MysqlTableOptions.ENGINE); + if (StringUtils.isNotBlank(engine)) { + builder.append(String.format("ENGINE=%s ", engine)); + } + String autoIncrementNumber = (String) options.get(MysqlTableOptions.AUTO_INCREMENT); + if (StringUtils.isNotBlank(autoIncrementNumber)) { + builder.append(String.format("AUTO_INCREMENT=%s ", autoIncrementNumber)); + } + String charset = (String) options.get(MysqlTableOptions.CHARSET); + if (StringUtils.isNotBlank(charset)) { + builder.append(String.format("DEFAULT CHARSET=%s ", charset)); + } + + String collate = (String) options.get(MysqlTableOptions.COLLATE); + if (StringUtils.isNotBlank(collate)) { + builder.append(String.format(" COLLATE=%s ", collate)); + } + + String comment = table.getComment(); + if (StringUtils.isNotBlank(comment)) { + builder.append(String.format(" COMMENT='%s' ", comment)); + } + return builder.toString(); + } + + @Override + public String getCommentFormatted(Column column) { + if (StringUtils.isEmpty(column.getComment())) { + return EMPTY_STRING; + } + return "COMMENT '" + column.getComment() + "'"; + } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/mysql/MysqlDatabaseDialectFactory.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/mysql/MysqlDatabaseDialectFactory.java similarity index 68% rename from eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/mysql/MysqlDatabaseDialectFactory.java rename to eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/mysql/MysqlDatabaseDialectFactory.java index 5f34a8a40f..eb4fbe3275 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/mysql/MysqlDatabaseDialectFactory.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/dialect/mysql/MysqlDatabaseDialectFactory.java @@ -15,18 +15,17 @@ * limitations under the License. */ -package org.apache.eventmesh.connector.jdbc.source.dialect.mysql; +package org.apache.eventmesh.connector.jdbc.dialect.mysql; -import org.apache.eventmesh.connector.jdbc.DatabaseDialect; -import org.apache.eventmesh.connector.jdbc.source.config.JdbcSourceConfig; -import org.apache.eventmesh.connector.jdbc.source.dialect.DatabaseDialectFactory; -import org.apache.eventmesh.openconnect.api.config.SourceConfig; +import org.apache.eventmesh.connector.jdbc.config.JdbcConfig; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialectFactory; public class MysqlDatabaseDialectFactory implements DatabaseDialectFactory { @Override - public DatabaseDialect createDatabaseDialect(SourceConfig config) { - DatabaseDialect databaseDialect = new MysqlDatabaseDialect((JdbcSourceConfig) config); + public DatabaseDialect createDatabaseDialect(JdbcConfig config) { + DatabaseDialect databaseDialect = new MysqlDatabaseDialect(config); return databaseDialect; } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/event/AbstractEvent.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/event/AbstractEvent.java index a5c7adf985..a2aaf6a5e4 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/event/AbstractEvent.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/event/AbstractEvent.java @@ -46,9 +46,6 @@ public TableId getTableId() { return tableId; } - /** - * @return - */ @Override public JdbcConnectData getJdbcConnectData() { return data; diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/event/DataChangeEventType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/event/DataChangeEventType.java index be79cb3ebf..67051603ca 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/event/DataChangeEventType.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/event/DataChangeEventType.java @@ -17,17 +17,59 @@ package org.apache.eventmesh.connector.jdbc.event; +/** + * Enumeration representing different types of data change events. + */ public enum DataChangeEventType { - INSERT("I"), UPDATE("U"), DELETE("D"); + /** + * Represents an INSERT data change event. + */ + INSERT("I"), + + /** + * Represents an UPDATE data change event. + */ + UPDATE("U"), + + /** + * Represents a DELETE data change event. + */ + DELETE("D"); + private final String code; + /** + * Constructs a DataChangeEventType with the specified code. + * + * @param code The code representing the data change event type. + */ DataChangeEventType(String code) { this.code = code; } + /** + * Parses a DataChangeEventType from the given code. + * + * @param code The code to parse. + * @return The corresponding DataChangeEventType. + * @throws IllegalArgumentException If the provided code is unknown. + */ + public static DataChangeEventType parseFromCode(String code) { + for (DataChangeEventType type : DataChangeEventType.values()) { + if (type.code.equals(code)) { + return type; + } + } + throw new IllegalArgumentException("Unknown DataChangeEventType code: " + code); + } + + /** + * Gets the code representing the DataChangeEventType. + * + * @return The code of the DataChangeEventType. + */ public String ofCode() { return this.code; } - } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/event/SchemaChangeEventType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/event/SchemaChangeEventType.java index fbf51ef2e8..19558f2dfe 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/event/SchemaChangeEventType.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/event/SchemaChangeEventType.java @@ -17,6 +17,8 @@ package org.apache.eventmesh.connector.jdbc.event; +import org.apache.commons.lang3.StringUtils; + public enum SchemaChangeEventType { DATABASE_CREATE("D", "C"), @@ -41,4 +43,14 @@ public String ofType() { public String ofOperationType() { return this.operationType; } + + public static SchemaChangeEventType ofSchemaChangeEventType(String type, String operationType) { + SchemaChangeEventType[] types = values(); + for (SchemaChangeEventType eventType : types) { + if (StringUtils.equalsIgnoreCase(eventType.type, type) && StringUtils.equalsIgnoreCase(eventType.operationType, operationType)) { + return eventType; + } + } + return null; + } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/server/JdbcConnectorServer.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/server/JdbcConnectorServer.java index 0309aa3442..4c40370671 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/server/JdbcConnectorServer.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/server/JdbcConnectorServer.java @@ -18,6 +18,7 @@ package org.apache.eventmesh.connector.jdbc.server; import org.apache.eventmesh.connector.jdbc.config.JdbcServerConfig; +import org.apache.eventmesh.connector.jdbc.sink.JdbcSinkConnector; import org.apache.eventmesh.connector.jdbc.source.JdbcSourceConnector; import org.apache.eventmesh.openconnect.Application; import org.apache.eventmesh.openconnect.util.ConfigUtil; @@ -36,7 +37,8 @@ public static void main(String[] args) throws Exception { } if (serverConfig.isSinkEnable()) { - // TODO support + Application jdbcSinkApp = new Application(); + jdbcSinkApp.run(JdbcSinkConnector.class); } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/JdbcSinkConnector.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/JdbcSinkConnector.java new file mode 100644 index 0000000000..7a5c68f581 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/JdbcSinkConnector.java @@ -0,0 +1,165 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.sink; + +import org.apache.eventmesh.common.utils.JsonUtils; +import org.apache.eventmesh.connector.jdbc.JdbcConnectData; +import org.apache.eventmesh.connector.jdbc.config.JdbcConfig; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialectFactory; +import org.apache.eventmesh.connector.jdbc.sink.config.JdbcSinkConfig; +import org.apache.eventmesh.connector.jdbc.sink.handle.DefaultSinkRecordHandler; +import org.apache.eventmesh.connector.jdbc.sink.handle.SinkRecordHandler; +import org.apache.eventmesh.connector.jdbc.sink.hibernate.HibernateConfiguration; +import org.apache.eventmesh.connector.jdbc.source.JdbcAllFactoryLoader; +import org.apache.eventmesh.openconnect.api.config.Config; +import org.apache.eventmesh.openconnect.api.connector.ConnectorContext; +import org.apache.eventmesh.openconnect.api.connector.SinkConnectorContext; +import org.apache.eventmesh.openconnect.api.sink.Sink; +import org.apache.eventmesh.openconnect.offsetmgmt.api.data.ConnectRecord; + +import java.util.List; + +import org.hibernate.SessionFactory; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class JdbcSinkConnector implements Sink { + + private JdbcSinkConfig sinkConfig; + + private SessionFactory sessionFactory; + + private DatabaseDialect databaseDialect; + + private SinkRecordHandler sinkRecordHandler; + + /** + * Returns the class type of the configuration for this Connector. + * + * @return Class type of the configuration + */ + @Override + public Class configClass() { + return JdbcSinkConfig.class; + } + + /** + * Initializes the Connector with the provided configuration. + * + * @param config Configuration object + * @throws Exception if initialization fails + */ + @Override + public void init(Config config) throws Exception { + if (!(config instanceof JdbcSinkConfig)) { + throw new IllegalArgumentException("Config not be JdbcSinkConfig"); + } + this.sinkConfig = (JdbcSinkConfig) config; + doInit(); + } + + /** + * Initializes the Connector with the provided context. + * + * @param connectorContext connectorContext + * @throws Exception if initialization fails + */ + @Override + public void init(ConnectorContext connectorContext) throws Exception { + SinkConnectorContext sinkConnectorContext = (SinkConnectorContext) connectorContext; + this.sinkConfig = (JdbcSinkConfig) sinkConnectorContext.getSinkConfig(); + doInit(); + } + + private void doInit() { + JdbcConfig jdbcConfig = this.sinkConfig.getSinkConnectorConfig().getJdbcConfig(); + this.sessionFactory = HibernateConfiguration.newBuilder().withDruidMaxActive("20").withPassword(jdbcConfig.getPassword()) + .withUrl(jdbcConfig.getUrl()) + .withShowSql(true) + .withUser(jdbcConfig.getUser()).build(); + + String databaseType = this.sinkConfig.getSinkConnectorConfig().getDatabaseType(); + + // Get the database dialect factory and create the database dialect. + final DatabaseDialectFactory databaseDialectFactory = JdbcAllFactoryLoader.getDatabaseDialectFactory(databaseType); + this.databaseDialect = databaseDialectFactory.createDatabaseDialect(this.sinkConfig.getSinkConnectorConfig().getJdbcConfig()); + Dialect dialect = this.sessionFactory.unwrap(SessionFactoryImplementor.class).getJdbcServices().getDialect(); + this.databaseDialect.configure(dialect); + this.databaseDialect.init(); + this.sinkRecordHandler = new DefaultSinkRecordHandler(databaseDialect, sessionFactory, sinkConfig); + + } + + /** + * Starts the Connector. + * + * @throws Exception if the start operation fails + */ + @Override + public void start() throws Exception { + + } + + /** + * Commits the specified ConnectRecord object. + * + * @param record ConnectRecord object to commit + */ + @Override + public void commit(ConnectRecord record) { + + } + + /** + * Returns the name of the Connector. + * + * @return String name of the Connector + */ + @Override + public String name() { + return this.sinkConfig.getSinkConnectorConfig().getConnectorName(); + } + + /** + * Stops the Connector. + * + * @throws Exception if stopping fails + */ + @Override + public void stop() throws Exception { + + } + + @Override + public void put(List sinkRecords) { + + for (ConnectRecord record : sinkRecords) { + Object data = record.getData(); + try { + JdbcConnectData jdbcConnectData = JsonUtils.parseObject((byte[]) data, JdbcConnectData.class); + this.sinkRecordHandler.handle(jdbcConnectData); + } catch (Exception e) { + log.error("Handle ConnectRecord error", e); + } + } + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/config/JdbcSinkConfig.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/config/JdbcSinkConfig.java new file mode 100644 index 0000000000..2a5af32cad --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/config/JdbcSinkConfig.java @@ -0,0 +1,35 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.sink.config; + +import org.apache.eventmesh.openconnect.api.config.SinkConfig; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class JdbcSinkConfig extends SinkConfig { + + private boolean supportUpsert = true; + + private boolean supportDelete = true; + + public SinkConnectorConfig sinkConnectorConfig; + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/config/SinkConnectorConfig.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/config/SinkConnectorConfig.java new file mode 100644 index 0000000000..e971b7b000 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/config/SinkConnectorConfig.java @@ -0,0 +1,43 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.sink.config; + +import org.apache.eventmesh.connector.jdbc.config.JdbcConfig; + +import lombok.Data; + +/** + * Configuration parameters for a sink connector. + */ +@Data +public class SinkConnectorConfig { + + /** + * The name of the sink connector. + */ + private String connectorName; + + /** + * JDBC configuration for connecting to a database. + */ + private JdbcConfig jdbcConfig; + + public String getDatabaseType() { + return "mysql"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/DefaultSinkRecordHandler.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/DefaultSinkRecordHandler.java new file mode 100644 index 0000000000..a4ba77ae5d --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/DefaultSinkRecordHandler.java @@ -0,0 +1,333 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.sink.handle; + +import org.apache.eventmesh.common.utils.LogUtil; +import org.apache.eventmesh.connector.jdbc.CatalogChanges; +import org.apache.eventmesh.connector.jdbc.DataChanges; +import org.apache.eventmesh.connector.jdbc.Field; +import org.apache.eventmesh.connector.jdbc.JdbcConnectData; +import org.apache.eventmesh.connector.jdbc.Payload; +import org.apache.eventmesh.connector.jdbc.Schema; +import org.apache.eventmesh.connector.jdbc.common.EnumeratedValue; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.event.DataChangeEventType; +import org.apache.eventmesh.connector.jdbc.event.SchemaChangeEventType; +import org.apache.eventmesh.connector.jdbc.sink.config.JdbcSinkConfig; +import org.apache.eventmesh.connector.jdbc.source.SourceMateData; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.type.Type; + +import org.apache.commons.collections4.CollectionUtils; + +import java.sql.SQLException; +import java.util.Comparator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import org.hibernate.SessionFactory; +import org.hibernate.StatelessSession; +import org.hibernate.Transaction; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.query.NativeQuery; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class DefaultSinkRecordHandler implements SinkRecordHandler { + + protected DatabaseDialect eventMeshDialect; + + protected Dialect hibernateDialect; + + protected DialectAssemblyLine dialectAssemblyLine; + + private SessionFactory sessionFactory; + + private final StatelessSession session; + + private final JdbcSinkConfig jdbcSinkConfig; + + public DefaultSinkRecordHandler(DatabaseDialect eventMeshDialect, SessionFactory sessionFactory, JdbcSinkConfig jdbcSinkConfig) { + this.eventMeshDialect = eventMeshDialect; + this.sessionFactory = sessionFactory; + this.hibernateDialect = sessionFactory.unwrap(SessionFactoryImplementor.class).getJdbcServices().getDialect(); + this.session = this.sessionFactory.openStatelessSession(); + this.dialectAssemblyLine = DialectAssemblyLineFactory.build(eventMeshDialect, hibernateDialect); + this.jdbcSinkConfig = jdbcSinkConfig; + } + + /** + * Handles schema and data changes using the specified JDBC connection data. The method determines the type of changes (schema or data) and + * performs the necessary operations accordingly. + * + * @param connectData the JDBC connection data + * @throws Exception if an error occurs during the handling of schema or data changes + */ + @Override + public void handle(JdbcConnectData connectData) throws Exception { + Payload payload = connectData.getPayload(); + SourceMateData sourceMateData = payload.ofSourceMateData(); + if (connectData.isSchemaChanges()) { + //DDL + schemaChangeHandle(sourceMateData, payload); + } else if (connectData.isDataChanges()) { + //DML + dataChangesHandle(connectData, sourceMateData, payload); + } else { + log.warn("Unknown connect data type: {}", connectData.getType()); + } + } + + private void dataChangesHandle(JdbcConnectData connectData, SourceMateData sourceMateData, Payload payload) throws SQLException { + String sql; + // If the connectData requests for data changes + // Parse the data change event type from the payload + DataHandleMode dataHandleMode = convert2DataHandleMode( + DataChangeEventType.parseFromCode(connectData.getPayload().getDataChanges().getType())); + // Depending on the type of the data change event, handle INSERT, UPDATE or DELETE operations + switch (dataHandleMode) { + case INSERT: + // For INSERT event, create an insert statement and execute it + sql = this.dialectAssemblyLine.getInsertStatement(sourceMateData, connectData.getSchema(), payload.ofDdl()); + insert(sql, connectData.getSchema(), payload.ofDataChanges()); + break; + case UPDATE: + sql = this.dialectAssemblyLine.getUpdateStatement(sourceMateData, connectData.getSchema(), payload.ofDdl()); + update(sql, connectData.getSchema(), payload.ofDataChanges()); + break; + case UPSERT: + sql = this.dialectAssemblyLine.getUpsertStatement(sourceMateData, connectData.getSchema(), payload.ofDdl()); + upsert(sql, connectData.getSchema(), payload.ofDataChanges()); + break; + case DELETE: + // If support for DELETE is set, create a delete statement and execute it + if (jdbcSinkConfig.isSupportDelete()) { + sql = this.dialectAssemblyLine.getDeleteStatement(sourceMateData, connectData.getSchema(), payload.ofDdl()); + delete(sql, connectData.getSchema(), payload.ofDataChanges()); + } else { + log.warn("No support for DELETE"); + } + break; + case NONE: + log.warn("No data changes to handle"); + break; + default: + log.warn("Unknown data changes type: {}", connectData.getPayload().getDataChanges().getType()); + break; + } + } + + private void schemaChangeHandle(SourceMateData sourceMateData, Payload payload) throws SQLException { + final CatalogChanges catalogChanges = payload.ofCatalogChanges(); + final SchemaChangeEventType schemaChangeEventType = SchemaChangeEventType.ofSchemaChangeEventType(catalogChanges.getType(), + catalogChanges.getOperationType()); + if (schemaChangeEventType == SchemaChangeEventType.DATABASE_CREATE && this.eventMeshDialect.databaseExists( + catalogChanges.getCatalog().getName())) { + log.warn("Database {} already exists", catalogChanges.getCatalog().getName()); + return; + } + if (schemaChangeEventType == SchemaChangeEventType.TABLE_CREATE && this.eventMeshDialect.tableExists( + catalogChanges.getTable().getTableId())) { + log.warn("Table {} already exists", catalogChanges.getTable().getTableId()); + return; + } + // Create a SQL statement for database or table changes + String sql = this.dialectAssemblyLine.getDatabaseOrTableStatement(sourceMateData, catalogChanges, payload.ofDdl()); + // Apply the database and table changes with the created SQL statement + applyDatabaseAndTableChanges(sql); + } + + private void applyDatabaseAndTableChanges(String sql) { + Transaction transaction = session.beginTransaction(); + try { + LogUtil.debug(log, "Execute database/table sql: {}", () -> sql); + session.createNativeQuery(sql).executeUpdate(); + transaction.commit(); + } catch (Exception e) { + transaction.rollback(); + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unchecked") + private void insert(String sql, Schema schema, DataChanges dataChanges) throws SQLException { + final Transaction transaction = session.beginTransaction(); + try { + if (log.isDebugEnabled()) { + log.debug("execute insert sql: {}", sql); + } + final NativeQuery query = session.createNativeQuery(sql); + AtomicInteger index = new AtomicInteger(1); + Map dataChangesAfter = (Map) dataChanges.getAfter(); + Field after = schema.getFields().get(0); + after.getFields().stream().map(field -> field.getColumn()).sorted(Comparator.comparingInt(Column::getOrder)).forEach(column -> { + Type type = eventMeshDialect.getType(column); + final int bindValueNum = type.bindValue(index.get(), type.convert2DatabaseTypeValue(dataChangesAfter.get(column.getName())), query); + index.addAndGet(bindValueNum); + }); + final int result = query.executeUpdate(); + if (result != 1) { + throw new SQLException("Failed to insert row from table"); + } + transaction.commit(); + } catch (SQLException e) { + transaction.rollback(); + throw e; + } + } + + @SuppressWarnings("unchecked") + private void update(String sql, Schema schema, DataChanges dataChanges) throws SQLException { + final Transaction transaction = session.beginTransaction(); + try { + if (log.isDebugEnabled()) { + log.debug("execute update sql: {}", sql); + } + final NativeQuery query = session.createNativeQuery(sql); + AtomicInteger index = new AtomicInteger(1); + Map dataChangesAfter = (Map) dataChanges.getAfter(); + Field after = schema.getFields().get(0); + final Map> columnMap = after.getFields().stream().map(field -> field.getColumn()) + .collect(Collectors.toMap(Column::getName, column -> column)); + final Set keySet = schema.getKeySet(); + after.getFields().stream().map(field -> field.getColumn()).sorted(Comparator.comparingInt(Column::getOrder)) + .filter(column -> !keySet.contains(column.getName())).forEach(column -> { + Type type = eventMeshDialect.getType(column); + int bindValueNum = type.bindValue(index.get(), type.convert2DatabaseTypeValue(dataChangesAfter.get(column.getName())), query); + index.addAndGet(bindValueNum); + }); + schema.getKeySet().stream().forEach(key -> { + if (columnMap.containsKey(key)) { + Type type = eventMeshDialect.getType(columnMap.get(key)); + final int bindValueNum = type.bindValue(index.get(), type.convert2DatabaseTypeValue(dataChangesAfter.get(key)), query); + index.addAndGet(bindValueNum); + } + }); + final int result = query.executeUpdate(); + if (result != 1) { + throw new SQLException("Failed to update row from table"); + } + transaction.commit(); + } catch (SQLException e) { + transaction.rollback(); + throw e; + } + } + + @SuppressWarnings("unchecked") + private void upsert(String sql, Schema schema, DataChanges dataChanges) throws SQLException { + final Transaction transaction = session.beginTransaction(); + try { + if (log.isDebugEnabled()) { + log.debug("execute upsert sql: {}", sql); + } + final NativeQuery query = session.createNativeQuery(sql); + AtomicInteger index = new AtomicInteger(1); + Map dataChangesAfter = (Map) dataChanges.getAfter(); + Field after = schema.getFields().get(0); + after.getFields().stream().map(field -> field.getColumn()).sorted(Comparator.comparingInt(Column::getOrder)).forEach(column -> { + Type type = eventMeshDialect.getType(column); + final int bindValueNum = type.bindValue(index.get(), type.convert2DatabaseTypeValue(dataChangesAfter.get(column.getName())), query); + index.addAndGet(bindValueNum); + }); + final int result = query.executeUpdate(); + if (result == 0) { + throw new SQLException("Failed to update row from table"); + } + transaction.commit(); + } catch (SQLException e) { + transaction.rollback(); + throw e; + } + } + + @SuppressWarnings("unchecked") + private void delete(String sql, Schema schema, DataChanges dataChanges) throws SQLException { + final Transaction transaction = session.beginTransaction(); + + try { + LogUtil.debug(log, "execute delete sql: {}", () -> sql); + if (CollectionUtils.isEmpty(schema.getKeySet())) { + log.warn("No primary key found, skip delete"); + return; + } + final NativeQuery query = session.createNativeQuery(sql); + AtomicInteger index = new AtomicInteger(1); + Map dataChangesAfter = (Map) dataChanges.getBefore(); + final Map> columnMap = schema.getFields().get(0).getFields().stream().map(field -> field.getColumn()) + .collect(Collectors.toMap(Column::getName, column -> column)); + schema.getKeySet().stream().forEach(columnName -> { + final Column column = columnMap.get(columnName); + Type type = eventMeshDialect.getType(column); + final int bindValueNum = type.bindValue(index.get(), type.convert2DatabaseTypeValue(dataChangesAfter.get(column.getName())), query); + index.addAndGet(bindValueNum); + }); + final int result = query.executeUpdate(); + if (result != 1) { + throw new SQLException("Failed to delete row from table"); + } + transaction.commit(); + } catch (SQLException e) { + transaction.rollback(); + throw e; + } + } + + private DataHandleMode convert2DataHandleMode(DataChangeEventType type) { + + switch (type) { + case INSERT: + return DataHandleMode.INSERT; + case UPDATE: + return this.jdbcSinkConfig.isSupportUpsert() ? DataHandleMode.UPSERT : DataHandleMode.UPDATE; + case DELETE: + return this.jdbcSinkConfig.isSupportDelete() ? DataHandleMode.DELETE : DataHandleMode.NONE; + default: + return DataHandleMode.NONE; + } + } + + public enum DataHandleMode implements EnumeratedValue { + + INSERT("insert"), UPSERT("upsert"), UPDATE("update"), DELETE("delete"), NONE("none"); + + private String value; + + DataHandleMode(String value) { + this.value = value; + } + + public static DataHandleMode forValue(String value) { + for (DataHandleMode mode : DataHandleMode.values()) { + if (mode.getValue().equalsIgnoreCase(value)) { + return mode; + } + } + throw new IllegalArgumentException("No enum constant " + DataHandleMode.class.getName() + "." + value); + } + + @Override + public String getValue() { + return value; + } + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/DialectAssemblyLine.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/DialectAssemblyLine.java new file mode 100644 index 0000000000..f0ca24ecb2 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/DialectAssemblyLine.java @@ -0,0 +1,79 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.sink.handle; + +import org.apache.eventmesh.connector.jdbc.CatalogChanges; +import org.apache.eventmesh.connector.jdbc.Schema; +import org.apache.eventmesh.connector.jdbc.source.SourceMateData; + +/** + * Represents an assembly line for transforming and generating SQL statements based on specific database dialects. + */ +public interface DialectAssemblyLine { + + /** + * Retrieves the database or table statement from the given {@code SourceMateData}, {@code CatalogChanges}, and {@code statement}. + * + * @param sourceMateData the source mate data object containing the information about the source + * @param catalogChanges the catalog changes object containing the information about the catalog changes + * @param statement the statement for which the database or table statement needs to be retrieved + * @return the database or table statement of the given {@code statement} + */ + String getDatabaseOrTableStatement(SourceMateData sourceMateData, CatalogChanges catalogChanges, String statement); + + /** + * Generates an insert statement for the given source mate data, schema, and origin statement. + * + * @param sourceMateData The source mate data of the database. + * @param schema The schema of the table. + * @param originStatement The original insert statement. + * @return The insert statement with the correct syntax for the given database and table. + */ + String getInsertStatement(SourceMateData sourceMateData, Schema schema, String originStatement); + + /** + * Generates an upsert statement using the given sourceMateData, schema, and originStatement. + * + * @param sourceMateData The metadata of the data source. + * @param schema The schema to upsert into. + * @param originStatement The original upsert statement. + * @return The upsert statement as a string. + */ + String getUpsertStatement(SourceMateData sourceMateData, Schema schema, String originStatement); + + /** + * Generates a delete statement based on the given sourceMateData, schema, and original statement. + * + * @param sourceMateData The source metadata used to generate the delete statement. + * @param schema The schema used to generate the delete statement. + * @param originStatement The original statement used as a basis for the delete statement. + * @return The generated delete statement as a string. + */ + String getDeleteStatement(SourceMateData sourceMateData, Schema schema, String originStatement); + + /** + * Generates an SQL update statement based on the provided source metadata, schema, and origin statement. + * + * @param sourceMateData The source metadata to be used for generating the update statement. + * @param schema The schema to be used for generating the update statement. + * @param originStatement The original SQL statement that needs to be updated. + * @return The generated SQL update statement as a string. + */ + String getUpdateStatement(SourceMateData sourceMateData, Schema schema, String originStatement); + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/DialectAssemblyLineFactory.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/DialectAssemblyLineFactory.java new file mode 100644 index 0000000000..185c201f5b --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/DialectAssemblyLineFactory.java @@ -0,0 +1,36 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.sink.handle; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.sink.mysql.MysqlDialectAssemblyLine; + +import org.hibernate.dialect.Dialect; + +public final class DialectAssemblyLineFactory { + + public static DialectAssemblyLine build(DatabaseDialect databaseDialect, Dialect hibernateDialect) { + switch (databaseDialect.getDatabaseType()) { + case MYSQL: + return new MysqlDialectAssemblyLine(databaseDialect, hibernateDialect); + default: + return new GeneralDialectAssemblyLine(databaseDialect, hibernateDialect); + } + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/GeneralDialectAssemblyLine.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/GeneralDialectAssemblyLine.java new file mode 100644 index 0000000000..971c8479ca --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/GeneralDialectAssemblyLine.java @@ -0,0 +1,287 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.sink.handle; + +import org.apache.eventmesh.connector.jdbc.CatalogChanges; +import org.apache.eventmesh.connector.jdbc.Field; +import org.apache.eventmesh.connector.jdbc.Schema; +import org.apache.eventmesh.connector.jdbc.dialect.AbstractGeneralDatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.SqlStatementAssembler; +import org.apache.eventmesh.connector.jdbc.event.SchemaChangeEventType; +import org.apache.eventmesh.connector.jdbc.source.SourceMateData; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.catalog.Table; +import org.apache.eventmesh.connector.jdbc.table.catalog.TableId; +import org.apache.eventmesh.connector.jdbc.type.Type; + +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import org.hibernate.dialect.Dialect; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class GeneralDialectAssemblyLine implements DialectAssemblyLine { + + @Getter + private final DatabaseDialect databaseDialect; + + @Getter + private final Dialect hibernateDialect; + + public GeneralDialectAssemblyLine(DatabaseDialect databaseDialect, Dialect hibernateDialect) { + this.databaseDialect = databaseDialect; + this.hibernateDialect = hibernateDialect; + } + + @Override + public String getDatabaseOrTableStatement(SourceMateData sourceMateData, CatalogChanges catalogChanges, String statement) { + String type = catalogChanges.getType(); + String operationType = catalogChanges.getOperationType(); + SchemaChangeEventType schemaChangeEventType = SchemaChangeEventType.ofSchemaChangeEventType(type, operationType); + String sql = null; + switch (schemaChangeEventType) { + case DATABASE_CREATE: + sql = assembleCreateDatabaseSql(catalogChanges); + break; + case DATABASE_DROP: + sql = assembleDropDatabaseSql(catalogChanges); + break; + case DATABASE_ALERT: + sql = assembleAlertDatabaseSql(catalogChanges); + break; + case TABLE_CREATE: + sql = assembleCreateTableSql(catalogChanges); + break; + case TABLE_DROP: + sql = assembleDropTableSql(catalogChanges); + break; + case TABLE_ALERT: + sql = assembleAlertTableSql(catalogChanges); + break; + default: + log.warn("Type={}, OperationType={} not support", type, operationType); + } + return sql; + } + + /** + * Generates an upsert statement using the given sourceMateData, schema, and originStatement. + * + * @param sourceMateData The metadata of the data source. + * @param schema The schema to upsert into. + * @param originStatement The original upsert statement. + * @return The upsert statement as a string. + */ + @Override + public String getUpsertStatement(SourceMateData sourceMateData, Schema schema, String originStatement) { + return null; + } + + /** + * Generates a delete statement based on the given sourceMateData, schema, and original statement. + * + * @param sourceMateData The source metadata used to generate the delete statement. + * @param schema The schema used to generate the delete statement. + * @param originStatement The original statement used as a basis for the delete statement. + * @return The generated delete statement as a string. + */ + @Override + public String getDeleteStatement(SourceMateData sourceMateData, Schema schema, String originStatement) { + SqlStatementAssembler sqlStatementAssembler = new SqlStatementAssembler(); + sqlStatementAssembler.appendSqlSlice("DELETE FROM ") + .appendSqlSlice(((AbstractGeneralDatabaseDialect) databaseDialect).getQualifiedTableName(sourceMateData.ofTableId())); + sqlStatementAssembler.appendSqlSlice(" WHERE "); + if (schema.containsKey()) { + sqlStatementAssembler.appendSqlSliceLists(" AND ", schema.getKeySet(), (columnName) -> columnName + " =?"); + } else { + Field after = schema.getFields().get(0); + sqlStatementAssembler.appendSqlSliceOfColumns(" AND ", + after.getFields().stream().map(field -> field.getColumn()).sorted(Comparator.comparingInt(Column::getOrder)) + .collect(Collectors.toList()), + (column) -> column.getName() + " =?"); + } + return sqlStatementAssembler.build(); + } + + /** + * Generates an SQL update statement based on the provided source metadata, schema, and origin statement. + * + * @param sourceMateData The source metadata to be used for generating the update statement. + * @param schema The schema to be used for generating the update statement. + * @param originStatement The original SQL statement that needs to be updated. + * @return The generated SQL update statement as a string. + */ + @Override + public String getUpdateStatement(SourceMateData sourceMateData, Schema schema, String originStatement) { + final SqlStatementAssembler sqlStatementAssembler = new SqlStatementAssembler(); + final TableId tableId = sourceMateData.ofTableId(); + // primary key set + final Set keySet = schema.getKeySet(); + Field tableColumns = schema.getFields().get(0); + sqlStatementAssembler.appendSqlSlice("UPDATE "); + sqlStatementAssembler.appendSqlSlice(((AbstractGeneralDatabaseDialect) databaseDialect).getQualifiedTableName(tableId)); + sqlStatementAssembler.appendSqlSlice(" SET "); + sqlStatementAssembler.appendSqlSliceLists(", ", + tableColumns.getFields().stream().map(Field::getColumn).sorted(Comparator.comparingInt(Column::getOrder)) + .filter(column -> !keySet.contains(column.getName())).map(column -> column.getName()).collect(Collectors.toList()), + (columnName) -> columnName + " =?"); + if (schema.containsKey()) { + sqlStatementAssembler.appendSqlSlice(" WHERE "); + sqlStatementAssembler.appendSqlSliceLists(" AND ", keySet, (columnName) -> columnName + " =?"); + } else { + sqlStatementAssembler.appendSqlSlice(" WHERE "); + sqlStatementAssembler.appendSqlSliceOfColumns(" AND ", + tableColumns.getFields().stream().map(field -> field.getColumn()).sorted(Comparator.comparingInt(Column::getOrder)) + .collect(Collectors.toList()), + column -> column.getName() + " =?"); + } + return sqlStatementAssembler.build(); + } + + @Override + public String getInsertStatement(SourceMateData sourceMateData, Schema schema, String originStatement) { + final TableId tableId = sourceMateData.ofTableId(); + + List afterFields = schema.getFields().stream().filter(field -> StringUtils.equals(field.getField(), "after")) + .collect(Collectors.toList()); + + final SqlStatementAssembler sqlAssembler = new SqlStatementAssembler(); + sqlAssembler.appendSqlSlice("INSERT INTO "); + sqlAssembler.appendSqlSlice(((AbstractGeneralDatabaseDialect) databaseDialect).getQualifiedTableName(tableId)); + sqlAssembler.appendSqlSlice(" ("); + // assemble columns + Field afterField = afterFields.get(0); + List> columns = afterField.getFields().stream().map(item -> item.getColumn()).sorted(Comparator.comparingInt(Column::getOrder)) + .collect(Collectors.toList()); + sqlAssembler.appendSqlSliceOfColumns(", ", columns, column -> column.getName()); + sqlAssembler.appendSqlSlice(") VALUES ("); + // assemble values + sqlAssembler.appendSqlSliceOfColumns(", ", columns, column -> getDmlBindingValue(column)); + sqlAssembler.appendSqlSlice(")"); + + return sqlAssembler.build(); + } + + private String getDmlBindingValue(Column column) { + Type type = this.databaseDialect.getType(column); + if (type == null) { + return this.databaseDialect.getQueryBindingWithValueCast(column); + } + return type.getQueryBindingWithValue(this.databaseDialect, column); + } + + private String assembleCreateDatabaseSql(CatalogChanges catalogChanges) { + SqlStatementAssembler assembler = new SqlStatementAssembler(); + assembler.appendSqlSlice("CREATE DATABASE IF NOT EXISTS "); + assembler.appendSqlSlice(((AbstractGeneralDatabaseDialect) databaseDialect).getQualifiedText(catalogChanges.getCatalog().getName())); + return assembler.build(); + } + + private String assembleDropDatabaseSql(CatalogChanges catalogChanges) { + SqlStatementAssembler assembler = new SqlStatementAssembler(); + assembler.appendSqlSlice("DROP DATABASE IF EXISTS "); + assembler.appendSqlSlice(((AbstractGeneralDatabaseDialect) databaseDialect).getQualifiedText(catalogChanges.getCatalog().getName())); + return assembler.build(); + } + + private String assembleAlertDatabaseSql(CatalogChanges catalogChanges) { + SqlStatementAssembler assembler = new SqlStatementAssembler(); + // todo + return assembler.build(); + } + + private String assembleCreateTableSql(CatalogChanges catalogChanges) { + SqlStatementAssembler assembler = new SqlStatementAssembler(); + assembler.appendSqlSlice("CREATE TABLE IF NOT EXISTS "); + Table table = catalogChanges.getTable(); + assembler.appendSqlSlice(((AbstractGeneralDatabaseDialect) databaseDialect).getQualifiedTableName(table.getTableId())); + assembler.appendSqlSlice(" ("); + // assemble columns + List columns = catalogChanges.getColumns().stream().sorted(Comparator.comparingInt(Column::getOrder)) + .collect(Collectors.toList()); + List columnNames = columns.stream().map(item -> item.getName()).collect(Collectors.toList()); + Map columnMap = columns.stream().collect(Collectors.toMap(Column::getName, item -> item)); + assembler.appendSqlSliceLists(", ", columnNames, (columnName) -> { + StringBuilder builder = new StringBuilder(); + // assemble column name + builder.append(((AbstractGeneralDatabaseDialect) databaseDialect).getQualifiedText(columnName)); + // assemble column type + Column column = columnMap.get(columnName); + String typeName = this.databaseDialect.getTypeName(hibernateDialect, column); + builder.append(" ").append(typeName); + + builder.append(" ").append(this.databaseDialect.getCharsetOrCollateFormatted(column)); + if (Optional.ofNullable(table.getPrimaryKey().getColumnNames()).orElse(new ArrayList<>(0)).contains(columnName)) { + builder.append(" NOT NULL "); + if (column.isAutoIncremented()) { + builder.append(this.databaseDialect.getAutoIncrementFormatted(column)); + } + } else { + if (column.isNotNull()) { + builder.append(" NOT NULL "); + } + } + addColumnDefaultValue(column, builder); + builder.append(" ").append(this.databaseDialect.getCommentFormatted(column)); + // assemble column default value + return builder.toString(); + }); + // assemble primary key and others key + assembler.appendSqlSlice(", PRIMARY KEY("); + assembler.appendSqlSliceLists(",", catalogChanges.getTable().getPrimaryKey().getColumnNames(), + (columnName) -> ((AbstractGeneralDatabaseDialect) databaseDialect).getQualifiedText(columnName)); + assembler.appendSqlSlice(")"); + assembler.appendSqlSlice(")"); + assembler.appendSqlSlice(this.databaseDialect.getTableOptionsFormatted(catalogChanges.getTable())); + return assembler.build(); + } + + private void addColumnDefaultValue(Column column, StringBuilder builder) { + if (column.isNotNull() && column.getDefaultValue() == null) { + return; + } + final String defaultValueFormatted = this.databaseDialect.getDefaultValueFormatted(column); + if (StringUtils.isNotEmpty(defaultValueFormatted)) { + builder.append(" DEFAULT ").append(defaultValueFormatted); + } + } + + private String assembleDropTableSql(CatalogChanges catalogChanges) { + SqlStatementAssembler assembler = new SqlStatementAssembler(); + assembler.appendSqlSlice("DROP TABLE IF EXISTS "); + assembler.appendSqlSlice( + ((AbstractGeneralDatabaseDialect) databaseDialect).getQualifiedTableName(catalogChanges.getTable().getTableId())); + return assembler.build(); + } + + private String assembleAlertTableSql(CatalogChanges catalogChanges) { + SqlStatementAssembler assembler = new SqlStatementAssembler(); + return assembler.build(); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/SinkRecordHandler.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/SinkRecordHandler.java new file mode 100644 index 0000000000..fa6a5b841c --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/handle/SinkRecordHandler.java @@ -0,0 +1,34 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.sink.handle; + +import org.apache.eventmesh.connector.jdbc.JdbcConnectData; + +/** + * This interface represents a schema change handler. + */ +public interface SinkRecordHandler { + + /** + * Handles a schema change using the specified JDBC connection data. + * + * @param connectData the JDBC connection data + */ + void handle(JdbcConnectData connectData) throws Exception; + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/hibernate/DruidConnectionProvider.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/hibernate/DruidConnectionProvider.java new file mode 100644 index 0000000000..e261978b7a --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/hibernate/DruidConnectionProvider.java @@ -0,0 +1,114 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.sink.hibernate; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; + +import org.hibernate.HibernateException; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.service.spi.Configurable; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.pool.DruidDataSourceFactory; + +public class DruidConnectionProvider implements ConnectionProvider, Configurable { + + private DruidDataSource dataSource = new DruidDataSource(); + + /** + * Obtains a connection for Hibernate use according to the underlying strategy of this provider. + * + * @return The obtained JDBC connection + * @throws SQLException Indicates a problem opening a connection + * @throws HibernateException Indicates a problem otherwise obtaining a connection. + */ + @Override + public Connection getConnection() throws SQLException { + return dataSource.getConnection(); + } + + /** + * Release a connection from Hibernate use. + * + * @param conn The JDBC connection to release + * @throws SQLException Indicates a problem closing the connection + * @throws HibernateException Indicates a problem otherwise releasing a connection. + */ + @Override + public void closeConnection(Connection conn) throws SQLException { + conn.close(); + } + + /** + *

+ * Does this connection provider support aggressive release of JDBC connections and re-acquisition of those connections (if need be) later? + *

+ *

+ * This is used in conjunction with {@link Environment#RELEASE_CONNECTIONS} to aggressively release JDBC connections. However, the configured + * ConnectionProvider must support re-acquisition of the same underlying connection for that semantic to work. + *

+ * Typically, this is only true in managed environments where a container tracks connections by transaction or thread. + *

+ * Note that JTA semantic depends on the fact that the underlying connection provider does support aggressive release. + *

+ * @return {@code true} if aggressive releasing is supported; {@code false} otherwise. + */ + @Override + public boolean supportsAggressiveRelease() { + return false; + } + + /** + * Configure the service. + * + * @param configurationValues The configuration properties. + */ + @Override + public void configure(Map configurationValues) { + try { + DruidDataSourceFactory.config(dataSource, configurationValues); + } catch (SQLException e) { + throw new IllegalArgumentException("Config druid error", e); + } + } + + /** + * Can this wrapped service be unwrapped as the indicated type? + * + * @param unwrapType The type to check. + * @return True/false. + */ + @Override + public boolean isUnwrappableAs(Class unwrapType) { + return dataSource.isWrapperFor(unwrapType); + } + + /** + * Unproxy the service proxy + * + * @param unwrapType The java type as which to unwrap this instance. + * @return The unwrapped reference + */ + @Override + public T unwrap(Class unwrapType) { + return dataSource.unwrap(unwrapType); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/hibernate/HibernateConfiguration.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/hibernate/HibernateConfiguration.java new file mode 100644 index 0000000000..fd5d49de74 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/hibernate/HibernateConfiguration.java @@ -0,0 +1,73 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.sink.hibernate; + +import org.hibernate.SessionFactory; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.service.ServiceRegistry; + +public final class HibernateConfiguration { + + public static HibernateConfigurationBuilder newBuilder() { + return new HibernateConfigurationBuilder(); + } + + public static class HibernateConfigurationBuilder { + + private Configuration configuration; + + public HibernateConfigurationBuilder() { + this.configuration = new Configuration(); + this.configuration.setProperty("hibernate.connection.provider_class", DruidConnectionProvider.class.getName()); + } + + public HibernateConfigurationBuilder withUser(String username) { + configuration.setProperty("username", username); + return this; + } + + public HibernateConfigurationBuilder withPassword(String password) { + configuration.setProperty("password", password); + return this; + } + + public HibernateConfigurationBuilder withUrl(String url) { + configuration.setProperty("url", url); + return this; + } + + public HibernateConfigurationBuilder withDruidMaxActive(String maxActive) { + configuration.setProperty("maxActive", maxActive); + return this; + } + + public HibernateConfigurationBuilder withShowSql(boolean showSql) { + configuration.setProperty("hibernate.show_sql", Boolean.toString(showSql)); + return this; + } + + public SessionFactory build() { + ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() + .applySettings(configuration.getProperties()) + .build(); + return configuration.buildSessionFactory(serviceRegistry); + } + + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/mysql/MysqlDialectAssemblyLine.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/mysql/MysqlDialectAssemblyLine.java new file mode 100644 index 0000000000..670a2d0258 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/sink/mysql/MysqlDialectAssemblyLine.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 org.apache.eventmesh.connector.jdbc.sink.mysql; + +import org.apache.eventmesh.connector.jdbc.Field; +import org.apache.eventmesh.connector.jdbc.Schema; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.SqlStatementAssembler; +import org.apache.eventmesh.connector.jdbc.sink.handle.GeneralDialectAssemblyLine; +import org.apache.eventmesh.connector.jdbc.source.SourceMateData; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.utils.JdbcStringUtils; + +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import org.hibernate.dialect.Dialect; + +public class MysqlDialectAssemblyLine extends GeneralDialectAssemblyLine { + + public MysqlDialectAssemblyLine(DatabaseDialect databaseDialect, Dialect hibernateDialect) { + super(databaseDialect, hibernateDialect); + } + + /** + * Generates an upsert statement using the given sourceMateData, schema, and originStatement. + * + * @param sourceMateData The metadata of the data source. + * @param schema The schema to upsert into. + * @param originStatement The original upsert statement. + * @return The upsert statement as a string. + */ + @Override + public String getUpsertStatement(SourceMateData sourceMateData, Schema schema, String originStatement) { + final SqlStatementAssembler sqlStatementAssembler = new SqlStatementAssembler(); + sqlStatementAssembler.appendSqlSlice(getInsertStatement(sourceMateData, schema, originStatement)); + Field afterField = schema.getFields().get(0); + List> columns = afterField.getFields().stream().map(item -> item.getColumn()).sorted(Comparator.comparingInt(Column::getOrder)) + .collect(Collectors.toList()); + if (JdbcStringUtils.compareVersion(getDatabaseDialect().getJdbcDriverMetaData().getDatabaseProductVersion(), "8.0.20") >= 0) { + // mysql doc:https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html + // Beginning with MySQL 8.0.20, an INSERT ... SELECT ... ON DUPLICATE KEY UPDATE statement that uses VALUES() in the UPDATE clause + sqlStatementAssembler.appendSqlSlice("AS new ON DUPLICATE KEY UPDATE "); + sqlStatementAssembler.appendSqlSliceOfColumns(",", columns, column -> { + final String columnName = column.getName(); + return columnName + "=new." + columnName; + }); + + } else { + sqlStatementAssembler.appendSqlSlice(" ON DUPLICATE KEY UPDATE "); + sqlStatementAssembler.appendSqlSliceOfColumns(",", columns, column -> { + final String columnName = column.getName(); + return columnName + "=VALUES(" + columnName + ")"; + }); + + } + return sqlStatementAssembler.build(); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/AbstractEngine.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/AbstractEngine.java index 2d1080e05b..08c3823725 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/AbstractEngine.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/AbstractEngine.java @@ -18,7 +18,7 @@ package org.apache.eventmesh.connector.jdbc.source; import org.apache.eventmesh.common.ThreadWrapper; -import org.apache.eventmesh.connector.jdbc.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; import org.apache.eventmesh.connector.jdbc.source.config.JdbcSourceConfig; import org.apache.eventmesh.connector.jdbc.source.config.SourceConnectorConfig; import org.apache.eventmesh.connector.jdbc.table.catalog.TableId; diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/JdbcAllFactoryLoader.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/JdbcAllFactoryLoader.java index f3c09adb53..2529d9a5a8 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/JdbcAllFactoryLoader.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/JdbcAllFactoryLoader.java @@ -19,7 +19,7 @@ import static com.google.common.base.Preconditions.checkNotNull; -import org.apache.eventmesh.connector.jdbc.source.dialect.DatabaseDialectFactory; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialectFactory; import org.apache.eventmesh.connector.jdbc.source.dialect.cdc.CdcEngineFactory; import org.apache.eventmesh.connector.jdbc.source.dialect.snapshot.SnapshotEngineFactory; import org.apache.eventmesh.spi.EventMeshExtensionFactory; diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/JdbcSourceConnector.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/JdbcSourceConnector.java index 202d7b9ca4..8d7d9cb66d 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/JdbcSourceConnector.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/JdbcSourceConnector.java @@ -17,10 +17,10 @@ package org.apache.eventmesh.connector.jdbc.source; -import org.apache.eventmesh.connector.jdbc.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialectFactory; import org.apache.eventmesh.connector.jdbc.event.Event; import org.apache.eventmesh.connector.jdbc.source.config.JdbcSourceConfig; -import org.apache.eventmesh.connector.jdbc.source.dialect.DatabaseDialectFactory; import org.apache.eventmesh.connector.jdbc.source.dialect.cdc.CdcEngine; import org.apache.eventmesh.connector.jdbc.source.dialect.cdc.CdcEngineFactory; import org.apache.eventmesh.connector.jdbc.source.dialect.snapshot.SnapshotEngine; @@ -111,7 +111,7 @@ private void doInit() { // Get the database dialect factory and create the database dialect. final DatabaseDialectFactory databaseDialectFactory = JdbcAllFactoryLoader.getDatabaseDialectFactory(databaseType); - this.databaseDialect = databaseDialectFactory.createDatabaseDialect(sourceConfig); + this.databaseDialect = databaseDialectFactory.createDatabaseDialect(this.sourceConfig.getSourceConnectorConfig().getJdbcConfig()); this.databaseDialect.init(); // Get the snapshot engine factory and create the snapshot engine diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/SourceMateData.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/SourceMateData.java index e1f6f28fd7..bf12a36d97 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/SourceMateData.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/SourceMateData.java @@ -17,12 +17,20 @@ package org.apache.eventmesh.connector.jdbc.source; +import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlSourceMateData; +import org.apache.eventmesh.connector.jdbc.table.catalog.TableId; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + import lombok.Data; /** * Represents metadata related to a data source. */ @Data +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "connector") +@JsonSubTypes({@JsonSubTypes.Type(value = MysqlSourceMateData.class, name = "mysql")}) public class SourceMateData { /** @@ -60,4 +68,13 @@ public class SourceMateData { */ private String tableName; + /** + * This method returns the TableId object with the specified catalog name, schema name, and table name. + * + * @return the TableId object + */ + public TableId ofTableId() { + return new TableId(catalogName, schemaName, tableName); + } + } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/TaskManagerCoordinator.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/TaskManagerCoordinator.java index a1572787d7..c299fbc531 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/TaskManagerCoordinator.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/TaskManagerCoordinator.java @@ -17,6 +17,7 @@ package org.apache.eventmesh.connector.jdbc.source; +import org.apache.eventmesh.common.utils.JsonUtils; import org.apache.eventmesh.openconnect.offsetmgmt.api.data.ConnectRecord; import org.apache.commons.collections4.CollectionUtils; @@ -102,6 +103,9 @@ public List poll() { if (Objects.isNull(record)) { break; } + if (log.isDebugEnabled()) { + log.debug("record:{}", JsonUtils.toJSONString(record)); + } records.add(record); } catch (InterruptedException e) { break; diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/AbstractGeneralDatabaseDialect.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/AbstractGeneralDatabaseDialect.java deleted file mode 100644 index ae497fce5c..0000000000 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/AbstractGeneralDatabaseDialect.java +++ /dev/null @@ -1,55 +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. - */ - -package org.apache.eventmesh.connector.jdbc.source.dialect; - -import org.apache.eventmesh.connector.jdbc.DatabaseDialect; -import org.apache.eventmesh.connector.jdbc.connection.JdbcConnection; -import org.apache.eventmesh.connector.jdbc.exception.JdbcConnectionException; -import org.apache.eventmesh.connector.jdbc.source.config.SourceConnectorConfig; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; - -public abstract class AbstractGeneralDatabaseDialect implements DatabaseDialect { - - private static final int DEFAULT_BATCH_MAX_ROWS = 20; - - private SourceConnectorConfig config; - - private int batchMaxRows = DEFAULT_BATCH_MAX_ROWS; - - public AbstractGeneralDatabaseDialect(SourceConnectorConfig config) { - this.config = config; - } - - @Override - public boolean isValid(Connection connection, int timeout) throws JdbcConnectionException, SQLException { - return connection == null ? false : connection.isValid(timeout); - } - - @Override - public PreparedStatement createPreparedStatement(Connection connection, String sql) throws SQLException { - PreparedStatement preparedStatement = connection.prepareStatement(sql); - if (batchMaxRows > 0) { - preparedStatement.setFetchSize(batchMaxRows); - } - return preparedStatement; - } - -} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/MysqlAntlr4DdlParser.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/MysqlAntlr4DdlParser.java index 1cf69bde2f..9bb110815f 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/MysqlAntlr4DdlParser.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/MysqlAntlr4DdlParser.java @@ -20,12 +20,15 @@ import org.apache.eventmesh.connector.jdbc.antlr4.Antlr4DdlParser; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlLexer; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.CharsetNameContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.CollationNameContext; import org.apache.eventmesh.connector.jdbc.antlr4.listener.Antlr4DdlParserListener; import org.apache.eventmesh.connector.jdbc.ddl.DdlParserCallback; import org.apache.eventmesh.connector.jdbc.event.Event; import org.apache.eventmesh.connector.jdbc.source.config.JdbcSourceConfig; import org.apache.eventmesh.connector.jdbc.source.dialect.antlr4.mysql.listener.MySqlAntlr4DdlParserListener; import org.apache.eventmesh.connector.jdbc.table.catalog.TableId; +import org.apache.eventmesh.connector.jdbc.utils.JdbcStringUtils; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -55,6 +58,10 @@ public MysqlAntlr4DdlParser(boolean skipViews, boolean skipComments, JdbcSourceC this(skipViews, skipComments, new HashSet<>(), sourceConfig); } + public MysqlAntlr4DdlParser(boolean skipViews, boolean skipComments) { + this(skipViews, skipComments, new HashSet<>(), null); + } + @Override protected MySqlLexer buildLexerInstance(CharStream charStreams) { return new MySqlLexer(charStreams); @@ -149,4 +156,25 @@ public void addTableIdSet(Set tableIdSet) { public JdbcSourceConfig getSourceConfig() { return sourceConfig; } + + public String parseCharset(CharsetNameContext charsetNameContext) { + String charsetName = null; + if (charsetNameContext != null && charsetNameContext.getText() != null) { + charsetName = JdbcStringUtils.withoutWrapper(charsetNameContext.getText()); + } + return charsetName; + } + + public String parseCollation(CollationNameContext collationNameContext) { + String collationName = null; + if (collationNameContext != null && collationNameContext.getText() != null) { + collationName = JdbcStringUtils.withoutWrapper(collationNameContext.getText()).toLowerCase(); + /* + * for (int index = 0; index < CharsetMapping.MAP_SIZE; index++) { if + * (collationName.equals(CharsetMapping.getStaticCollationNameForCollationIndex(index))) { collationName = + * CharsetMapping.getStaticMysqlCharsetNameForCollationIndex(index); break; } } + */ + } + return collationName; + } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/listener/ColumnDefinitionParserListener.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/listener/ColumnDefinitionParserListener.java index 9be9b42ddd..23e3728d47 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/listener/ColumnDefinitionParserListener.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/listener/ColumnDefinitionParserListener.java @@ -19,29 +19,46 @@ import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.AutoIncrementColumnConstraintContext; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.CollateColumnConstraintContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.CollectionDataTypeContext; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.ColumnDefinitionContext; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.CommentColumnConstraintContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.DataTypeContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.DecimalLiteralContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.DimensionDataTypeContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.LongVarcharDataTypeContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.NationalStringDataTypeContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.NationalVaryingStringDataTypeContext; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.NullNotnullContext; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.PrimaryKeyColumnConstraintContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.SimpleDataTypeContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.SpatialDataTypeContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.StringDataTypeContext; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.UniqueKeyColumnConstraintContext; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParserBaseListener; import org.apache.eventmesh.connector.jdbc.source.dialect.antlr4.mysql.MysqlAntlr4DdlParser; import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlDataTypeConvertor; import org.apache.eventmesh.connector.jdbc.table.catalog.TableEditor; import org.apache.eventmesh.connector.jdbc.table.catalog.mysql.MysqlColumnEditor; +import org.apache.eventmesh.connector.jdbc.table.catalog.mysql.MysqlOptions.MysqlColumnOptions; import org.apache.eventmesh.connector.jdbc.table.type.EventMeshDataType; import org.apache.eventmesh.connector.jdbc.utils.JdbcStringUtils; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; import org.antlr.v4.runtime.tree.ParseTreeListener; import lombok.Getter; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; @Getter @Setter +@Slf4j public class ColumnDefinitionParserListener extends MySqlParserBaseListener { private DefaultValueParserListener defaultValueParserListener; @@ -72,11 +89,108 @@ public ColumnDefinitionParserListener(List listeners, TableEd public void enterColumnDefinition(ColumnDefinitionContext ctx) { // parse Column data type this.parser.runIfAllNotNull(() -> { - String dataTypeString = ctx.dataType().getText(); - EventMeshDataType eventMeshType = this.dataTypeConvertor.toEventMeshType(dataTypeString); - this.columnEditor.withEventMeshType(eventMeshType); - this.columnEditor.withJdbcType(this.dataTypeConvertor.toJDBCType(dataTypeString)); - this.columnEditor.withType(dataTypeString); + DataTypeContext dataTypeContext = ctx.dataType(); + String dataTypeString = null; + if (dataTypeContext instanceof StringDataTypeContext) { + StringDataTypeContext stringDataTypeCtx = (StringDataTypeContext) dataTypeContext; + dataTypeString = stringDataTypeCtx.typeName.getText(); + // parse data type length + if (stringDataTypeCtx.lengthOneDimension() != null) { + this.columnEditor.length(Integer.parseInt(stringDataTypeCtx.lengthOneDimension().decimalLiteral().getText())); + } + // parse data type charset and collation + String charsetName = parser.parseCharset(stringDataTypeCtx.charsetName()); + String collationName = parser.parseCollation(stringDataTypeCtx.collationName()); + columnEditor.charsetName(charsetName); + columnEditor.collation(collationName); + } else if (dataTypeContext instanceof NationalStringDataTypeContext) { + NationalStringDataTypeContext nationalStringDataTypeCtx = (NationalStringDataTypeContext) dataTypeContext; + dataTypeString = nationalStringDataTypeCtx.typeName.getText(); + if (nationalStringDataTypeCtx.lengthOneDimension() != null) { + this.columnEditor.length(Integer.parseInt(nationalStringDataTypeCtx.lengthOneDimension().decimalLiteral().getText())); + } + } else if (dataTypeContext instanceof NationalVaryingStringDataTypeContext) { + NationalVaryingStringDataTypeContext nationalVaryingStringDataTypeCtx = (NationalVaryingStringDataTypeContext) dataTypeContext; + dataTypeString = nationalVaryingStringDataTypeCtx.typeName.getText(); + if (nationalVaryingStringDataTypeCtx.lengthOneDimension() != null) { + this.columnEditor.length(Integer.parseInt(nationalVaryingStringDataTypeCtx.lengthOneDimension().decimalLiteral().getText())); + } + } else if (dataTypeContext instanceof DimensionDataTypeContext) { + DimensionDataTypeContext dimensionDataTypeCtx = (DimensionDataTypeContext) dataTypeContext; + dataTypeString = dimensionDataTypeCtx.typeName.getText(); + // parse column length + if (dimensionDataTypeCtx.lengthOneDimension() != null) { + this.columnEditor.length(Integer.parseInt(dimensionDataTypeCtx.lengthOneDimension().decimalLiteral().getText())); + } + // parse column scale if has scale + if (dimensionDataTypeCtx.lengthTwoDimension() != null) { + List decimalLiteralContexts = dimensionDataTypeCtx.lengthTwoDimension().decimalLiteral(); + this.columnEditor.length(Integer.parseInt(decimalLiteralContexts.get(0).getText())); + this.columnEditor.scale(Integer.parseInt(decimalLiteralContexts.get(1).getText())); + } + + if (dimensionDataTypeCtx.lengthTwoOptionalDimension() != null) { + List decimalLiteralContexts = dimensionDataTypeCtx.lengthTwoOptionalDimension().decimalLiteral(); + if (decimalLiteralContexts.get(0).REAL_LITERAL() != null) { + String[] digits = decimalLiteralContexts.get(0).getText().split("."); + if (StringUtils.isBlank(digits[0]) || Integer.valueOf(digits[0]) == 0) { + this.columnEditor.length(10); + } else { + this.columnEditor.length(Integer.valueOf(digits[0])); + } + } else { + this.columnEditor.length(Integer.parseInt(decimalLiteralContexts.get(0).getText())); + } + if (decimalLiteralContexts.size() > 1) { + this.columnEditor.scale(Integer.parseInt(decimalLiteralContexts.get(1).getText())); + } + } + if (CollectionUtils.isNotEmpty(dimensionDataTypeCtx.SIGNED())) { + this.columnEditor.withOption(MysqlColumnOptions.SIGNED, dimensionDataTypeCtx.SIGNED().get(0).getText()); + } + if (CollectionUtils.isNotEmpty(dimensionDataTypeCtx.UNSIGNED())) { + this.columnEditor.withOption(MysqlColumnOptions.UNSIGNED, dimensionDataTypeCtx.UNSIGNED().get(0).getText()); + } + if (CollectionUtils.isNotEmpty(dimensionDataTypeCtx.ZEROFILL())) { + this.columnEditor.withOption(MysqlColumnOptions.ZEROFILL, dimensionDataTypeCtx.ZEROFILL().get(0).getText()); + } + } else if (dataTypeContext instanceof SimpleDataTypeContext) { + // Do nothing for example: DATE, TINYBLOB, etc. + SimpleDataTypeContext simpleDataTypeCtx = (SimpleDataTypeContext) dataTypeContext; + dataTypeString = simpleDataTypeCtx.typeName.getText(); + } else if (dataTypeContext instanceof CollectionDataTypeContext) { + CollectionDataTypeContext collectionDataTypeContext = (CollectionDataTypeContext) dataTypeContext; + dataTypeString = collectionDataTypeContext.typeName.getText(); + if (collectionDataTypeContext.charsetName() != null) { + String charsetName = collectionDataTypeContext.charsetName().getText(); + columnEditor.charsetName(charsetName); + } + } else if (dataTypeContext instanceof SpatialDataTypeContext) { + // do nothing + SpatialDataTypeContext spatialDataTypeCtx = (SpatialDataTypeContext) dataTypeContext; + dataTypeString = spatialDataTypeCtx.typeName.getText(); + } else if (dataTypeContext instanceof LongVarcharDataTypeContext) { + LongVarcharDataTypeContext longVarcharDataTypeCtx = (LongVarcharDataTypeContext) dataTypeContext; + dataTypeString = longVarcharDataTypeCtx.typeName.getText(); + String charsetName = parser.parseCharset(longVarcharDataTypeCtx.charsetName()); + String collationName = parser.parseCollation(longVarcharDataTypeCtx.collationName()); + columnEditor.charsetName(charsetName); + columnEditor.collation(collationName); + } + // handle enum and set type values + if (StringUtils.equalsAnyIgnoreCase(dataTypeString, "ENUM", "SET")) { + CollectionDataTypeContext collectionDataTypeContext = (CollectionDataTypeContext) dataTypeContext; + List values = collectionDataTypeContext.collectionOptions().STRING_LITERAL().stream() + .map(node -> JdbcStringUtils.withoutWrapper(node.getText())).collect(Collectors.toList()); + columnEditor.enumValues(values); + } + + if (StringUtils.isNotBlank(dataTypeString)) { + EventMeshDataType eventMeshType = this.dataTypeConvertor.toEventMeshType(dataTypeString); + this.columnEditor.withEventMeshType(eventMeshType); + this.columnEditor.withJdbcType(this.dataTypeConvertor.toJDBCType(dataTypeString)); + this.columnEditor.withType(dataTypeString); + } }, columnEditor); this.parser.runIfAllNotNull(() -> { @@ -140,7 +254,7 @@ public void exitColumnDefinition(ColumnDefinitionContext ctx) { @Override public void enterCollateColumnConstraint(CollateColumnConstraintContext ctx) { if (ctx.COLLATE() != null) { - columnEditor.collate(ctx.collationName().getText()); + columnEditor.collation(ctx.collationName().getText()); } super.enterCollateColumnConstraint(ctx); } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/listener/CreateTableParserListener.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/listener/CreateTableParserListener.java index 5a72c4449b..c24f885d75 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/listener/CreateTableParserListener.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/listener/CreateTableParserListener.java @@ -21,7 +21,9 @@ import org.apache.eventmesh.connector.jdbc.Payload; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.ColumnCreateTableContext; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.CopyCreateTableContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.DecimalLiteralContext; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.QueryCreateTableContext; +import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.TableOptionAutoIncrementContext; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.TableOptionCharsetContext; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.TableOptionCollateContext; import org.apache.eventmesh.connector.jdbc.antlr4.autogeneration.MySqlParser.TableOptionEngineContext; @@ -32,9 +34,10 @@ import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlSourceMateData; import org.apache.eventmesh.connector.jdbc.table.catalog.Table; import org.apache.eventmesh.connector.jdbc.table.catalog.TableId; +import org.apache.eventmesh.connector.jdbc.table.catalog.mysql.MysqlOptions.MysqlTableOptions; import org.apache.eventmesh.connector.jdbc.table.catalog.mysql.MysqlTableEditor; -import org.apache.eventmesh.connector.jdbc.table.catalog.mysql.MysqlTableOptions; import org.apache.eventmesh.connector.jdbc.table.catalog.mysql.MysqlTableSchema; +import org.apache.eventmesh.connector.jdbc.utils.Antlr4Utils; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; @@ -98,7 +101,7 @@ public void enterColumnCreateTable(ColumnCreateTableContext ctx) { @Override public void exitColumnCreateTable(ColumnCreateTableContext ctx) { - String ddl = ctx.getText(); + String ddl = Antlr4Utils.getText(ctx); parser.runIfAllNotNull(() -> { listeners.remove(columnDefinitionListener); // help JVM GC @@ -114,7 +117,12 @@ public void exitColumnCreateTable(ColumnCreateTableContext ctx) { .catalogName(currentDatabase) .serverId(sourceConnectorConfig.getMysqlConfig().getServerId()) .build(); - Table table = new Table(tableSchema.getSimpleName(), tableSchema.getPrimaryKey(), tableSchema.getUniqueKeys(), tableSchema.getComment()); + Table table = Table.newBuilder().withTableId(tableSchema.getTableId()) + .withPrimaryKey(tableSchema.getPrimaryKey()) + .withUniqueKeys(tableSchema.getUniqueKeys()) + .withComment(tableSchema.getComment()) + .withOptions(tableSchema.getTableOptions()) + .build(); CatalogChanges changes = CatalogChanges.newBuilder().operationType(SchemaChangeEventType.TABLE_CREATE).table(table) .columns(tableSchema.getColumns()).build(); payload.withSource(sourceMateData).withDdl(ddl).withCatalogChanges(changes); @@ -136,7 +144,7 @@ private MysqlTableEditor createTableEditor(String tableName) { @Override public void enterTableOptionEngine(TableOptionEngineContext ctx) { if (ctx.ENGINE() != null) { - this.tableEditor.withOption(MysqlTableOptions.ENGINE, ctx.ENGINE().getText()); + this.tableEditor.withOption(MysqlTableOptions.ENGINE, ctx.engineName().getText()); } super.enterTableOptionEngine(ctx); } @@ -155,6 +163,16 @@ public void enterTableOptionCharset(TableOptionCharsetContext ctx) { super.enterTableOptionCharset(ctx); } + @Override + public void enterTableOptionAutoIncrement(TableOptionAutoIncrementContext ctx) { + DecimalLiteralContext decimalLiteralContext = ctx.decimalLiteral(); + if (null != decimalLiteralContext) { + String autoIncrementNumber = Antlr4Utils.getText(decimalLiteralContext); + this.tableEditor.withOption(MysqlTableOptions.AUTO_INCREMENT, autoIncrementNumber); + } + super.enterTableOptionAutoIncrement(ctx); + } + @Override public void enterTableOptionCollate(TableOptionCollateContext ctx) { if (ctx.COLLATE() != null) { diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/listener/DefaultValueParserListener.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/listener/DefaultValueParserListener.java index aad1dc65ad..51bb5fe579 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/listener/DefaultValueParserListener.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/antlr4/mysql/listener/DefaultValueParserListener.java @@ -83,7 +83,7 @@ public void enterDefaultValue(DefaultValueContext ctx) { if (stringLiteralContext.COLLATE() == null) { columnEditor.defaultValueExpression(sign + unquote(stringLiteralContext.getText())); } else { - columnEditor.collate(sign + unquote(stringLiteralContext.STRING_LITERAL(0).getText())); + columnEditor.collation(sign + unquote(stringLiteralContext.STRING_LITERAL(0).getText())); } } else if (ctx.constant().decimalLiteral() != null) { columnEditor.defaultValueExpression(sign + ctx.constant().decimalLiteral().getText()); @@ -117,14 +117,14 @@ public void enterDefaultValue(DefaultValueContext ctx) { * ; */ List currentTimestampContexts = ctx.currentTimestamp(); - if (currentTimestampContexts.size() > 1 || (ctx.ON() == null && ctx.UPDATE() == null)) { + if (currentTimestampContexts.size() > 1 && (ctx.ON() != null && ctx.UPDATE() != null)) { + StringBuilder builder = new StringBuilder(); + builder.append(currentTimestampContexts.get(0).getText()).append(" ").append(ctx.ON().getText()).append(" ").append(ctx.UPDATE()) + .append(" ").append(currentTimestampContexts.get(1).getText()); + columnEditor.defaultValueExpression(builder.toString()); + } else if (currentTimestampContexts.size() == 1) { CurrentTimestampContext currentTimestampContext = currentTimestampContexts.get(0); - // - if (currentTimestampContext.CURRENT_TIMESTAMP() != null || currentTimestampContext.NOW() != null) { - columnEditor.defaultValueExpression("1970-01-01 00:00:00"); - } else { - columnEditor.defaultValueExpression(currentTimestampContext.getText()); - } + columnEditor.defaultValueExpression(currentTimestampContext.getText()); } } else if (ctx.expression() != null) { // e.g. CREATE TABLE t2 (b BLOB DEFAULT ('abc')); diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/AbstractCdcEngine.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/AbstractCdcEngine.java index fc9d2f3724..1fb1a95579 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/AbstractCdcEngine.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/AbstractCdcEngine.java @@ -18,9 +18,9 @@ package org.apache.eventmesh.connector.jdbc.source.dialect.cdc; import org.apache.eventmesh.common.ThreadWrapper; -import org.apache.eventmesh.connector.jdbc.DatabaseDialect; import org.apache.eventmesh.connector.jdbc.JdbcContext; import org.apache.eventmesh.connector.jdbc.ddl.DdlParser; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; import org.apache.eventmesh.connector.jdbc.source.config.JdbcSourceConfig; import org.apache.eventmesh.connector.jdbc.source.config.SourceConnectorConfig; import org.apache.eventmesh.connector.jdbc.table.catalog.TableId; diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/CdcEngineFactory.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/CdcEngineFactory.java index 33e5f555b6..e08e1c8216 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/CdcEngineFactory.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/CdcEngineFactory.java @@ -17,7 +17,7 @@ package org.apache.eventmesh.connector.jdbc.source.dialect.cdc; -import org.apache.eventmesh.connector.jdbc.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; import org.apache.eventmesh.openconnect.api.config.SourceConfig; import org.apache.eventmesh.spi.EventMeshExtensionType; import org.apache.eventmesh.spi.EventMeshSPI; diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/mysql/MysqlCdcEngine.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/mysql/MysqlCdcEngine.java index 67c837caf7..147509e12d 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/mysql/MysqlCdcEngine.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/mysql/MysqlCdcEngine.java @@ -18,6 +18,7 @@ package org.apache.eventmesh.connector.jdbc.source.dialect.cdc.mysql; import org.apache.eventmesh.common.EventMeshThreadFactory; +import org.apache.eventmesh.connector.jdbc.CatalogChanges; import org.apache.eventmesh.connector.jdbc.DataChanges; import org.apache.eventmesh.connector.jdbc.DataChanges.Builder; import org.apache.eventmesh.connector.jdbc.Field; @@ -25,10 +26,12 @@ import org.apache.eventmesh.connector.jdbc.Schema; import org.apache.eventmesh.connector.jdbc.config.JdbcConfig; import org.apache.eventmesh.connector.jdbc.connection.mysql.MysqlJdbcConnection; +import org.apache.eventmesh.connector.jdbc.dialect.mysql.MysqlDatabaseDialect; import org.apache.eventmesh.connector.jdbc.event.DeleteDataEvent; import org.apache.eventmesh.connector.jdbc.event.EventConsumer; import org.apache.eventmesh.connector.jdbc.event.GeneralDataChangeEvent; import org.apache.eventmesh.connector.jdbc.event.InsertDataEvent; +import org.apache.eventmesh.connector.jdbc.event.SchemaChangeEventType; import org.apache.eventmesh.connector.jdbc.event.UpdateDataEvent; import org.apache.eventmesh.connector.jdbc.source.config.JdbcSourceConfig; import org.apache.eventmesh.connector.jdbc.source.config.MysqlConfig; @@ -40,12 +43,13 @@ import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.EventDataDeserializationExceptionData; import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.EventMeshGtidSet; import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlConstants; -import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlDatabaseDialect; import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlJdbcContext; import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlSourceMateData; import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.catalog.DefaultValueConvertor; import org.apache.eventmesh.connector.jdbc.table.catalog.TableId; import org.apache.eventmesh.connector.jdbc.table.catalog.TableSchema; +import org.apache.eventmesh.connector.jdbc.table.catalog.mysql.MysqlDefaultValueConvertorImpl; import org.apache.eventmesh.connector.jdbc.table.type.Pair; import org.apache.eventmesh.openconnect.api.config.Config; @@ -113,6 +117,8 @@ public class MysqlCdcEngine extends AbstractCdcEngine { + column.setDefaultValue(defaultValueConvertor.parseDefaultValue(column, column.getDefaultValueExpression())); + }); + } + } event.getJdbcConnectData().getPayload().ofSourceMateData().setSnapshot(false); consumers.stream().forEach(consumer -> consumer.accept(event)); } @@ -583,7 +601,7 @@ private MysqlSourceMateData buildMysqlSourceMateData(MysqlJdbcContext context, E return sourceMateData; } - private enum CdcDmlType { + public enum CdcDmlType { INSERT, UPDATE, DELETE @@ -606,20 +624,25 @@ private void handleCdcDmlData(MysqlJdbcContext context, MysqlSourceMateData sour List, Pair>> rows, CdcDmlType type) { TableSchema tableSchema = context.getCatalogTableSet().getTableSchema(tableId); - Map orderColumnMap = tableSchema.getOrderColumnMap(); - List columns = tableSchema.getColumns(); + Map> orderColumnMap = tableSchema.getOrderColumnMap(); + List> columns = tableSchema.getColumns(); List fields = null; Builder builder = DataChanges.newBuilder(); if (CollectionUtils.isNotEmpty(columns)) { fields = columns.stream() - .map(col -> new Field(col.getDataType().getName(), col.isNotNull(), col.getName(), tableId.toString())) - .collect(Collectors.toList()); + .map(col -> { + Column rebuild = Column.newBuilder().withName(col.getName()).withDataType(col.getDataType()).withJdbcType(col.getJdbcType()) + .withNativeType(col.getNativeType()).withOrder(col.getOrder()).build(); + return new Field(rebuild, col.isNotNull(), col.getName(), tableId.toString()); + }).collect(Collectors.toList()); } int columnsSize = orderColumnMap.size(); for (Pair, Pair> pair : rows) { GeneralDataChangeEvent dataEvent = buildEvent(type, tableId); - Payload payload = dataEvent.getJdbcConnectData().getPayload(); + builder.withType(dataEvent.getDataChangeEventType().ofCode()); Schema schema = new Schema(); + // set primary key + schema.addKeys(tableSchema.getPrimaryKey().getColumnNames()); Pair beforePair = Optional.ofNullable(pair.getLeft()).orElse(new Pair<>()); Serializable[] beforeRows = beforePair.getLeft(); if (null != beforeRows && beforeRows.length != 0) { @@ -633,7 +656,7 @@ private void handleCdcDmlData(MysqlJdbcContext context, MysqlSourceMateData sour beforeValues.put(orderColumnMap.get(index + 1).getName(), beforeRows[index]); } builder.withBefore(beforeValues); - Field beforeField = new Field().withField(Payload.BEFORE_FIELD).withType("field").withName("payload.before").withRequired(false); + Field beforeField = new Field().withField(Payload.BEFORE_FIELD).withName(Payload.PAYLOAD_BEFORE).withRequired(false); beforeField.withRequired(true).withFields(fields); schema.add(beforeField); } @@ -650,15 +673,15 @@ private void handleCdcDmlData(MysqlJdbcContext context, MysqlSourceMateData sour } afterValues.put(orderColumnMap.get(index + 1).getName(), afterRows[index]); } - builder.withBefore(afterValues); - Field afterField = new Field().withField(Payload.AFTER_FIELD).withType("field").withName("payload.after").withRequired(false); + builder.withAfter(afterValues); + Field afterField = new Field().withField(Payload.AFTER_FIELD).withName(Payload.PAYLOAD_AFTER).withRequired(false); afterField.withRequired(true).withFields(fields); schema.add(afterField); } + Payload payload = dataEvent.getJdbcConnectData().getPayload(); payload.withSource(sourceMateData).withDataChanges(builder.build()); dataEvent.getJdbcConnectData().setSchema(schema); consumers.stream().forEach(consumer -> consumer.accept(dataEvent)); - } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/mysql/MysqlCdcEngineFactory.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/mysql/MysqlCdcEngineFactory.java index 60c3c7b7c1..35e722fe12 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/mysql/MysqlCdcEngineFactory.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/mysql/MysqlCdcEngineFactory.java @@ -17,11 +17,11 @@ package org.apache.eventmesh.connector.jdbc.source.dialect.cdc.mysql; -import org.apache.eventmesh.connector.jdbc.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.mysql.MysqlDatabaseDialect; import org.apache.eventmesh.connector.jdbc.source.dialect.cdc.AbstractCdcEngineFactory; import org.apache.eventmesh.connector.jdbc.source.dialect.cdc.CdcEngine; import org.apache.eventmesh.connector.jdbc.source.dialect.cdc.CdcEngineFactory; -import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlDatabaseDialect; import org.apache.eventmesh.openconnect.api.config.SourceConfig; import org.apache.commons.lang3.StringUtils; diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/mysql/RowDeserializers.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/mysql/RowDeserializers.java index f605e95bde..e23839563f 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/mysql/RowDeserializers.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/cdc/mysql/RowDeserializers.java @@ -23,9 +23,9 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; -import java.time.Year; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.BitSet; import java.util.Map; import com.github.shyiko.mysql.binlog.event.TableMapEventData; @@ -99,6 +99,11 @@ protected Serializable deserializeTimestampV2(int meta, ByteArrayInputStream inp protected Serializable deserializeYear(ByteArrayInputStream inputStream) throws IOException { return RowDeserializers.deserializeYear(inputStream); } + + @Override + protected Serializable deserializeBit(int meta, ByteArrayInputStream inputStream) throws IOException { + return ((BitSet) super.deserializeBit(meta, inputStream)).toByteArray(); + } } public static class UpdateRowsEventMeshDeserializer extends UpdateRowsEventDataDeserializer { @@ -156,6 +161,12 @@ protected Serializable deserializeTimestampV2(int meta, ByteArrayInputStream inp protected Serializable deserializeYear(ByteArrayInputStream inputStream) throws IOException { return RowDeserializers.deserializeYear(inputStream); } + + @Override + protected Serializable deserializeBit(int meta, ByteArrayInputStream inputStream) throws IOException { + return ((BitSet) super.deserializeBit(meta, inputStream)).toByteArray(); + + } } public static class DeleteRowsEventMeshDeserializer extends DeleteRowsEventDataDeserializer { @@ -213,6 +224,10 @@ protected Serializable deserializeTimestampV2(int meta, ByteArrayInputStream inp protected Serializable deserializeYear(ByteArrayInputStream inputStream) throws IOException { return RowDeserializers.deserializeYear(inputStream); } + + protected Serializable deserializeBit(int meta, ByteArrayInputStream inputStream) throws IOException { + return ((BitSet) super.deserializeBit(meta, inputStream)).toByteArray(); + } } protected static Serializable deserializeTimestamp(ByteArrayInputStream inputStream) throws IOException { @@ -228,7 +243,7 @@ protected static Serializable deserializeTimestampV2(int meta, ByteArrayInputStr } protected static Serializable deserializeYear(ByteArrayInputStream inputStream) throws IOException { - return Year.of(1900 + inputStream.readInteger(1)); + return LocalDate.parse(String.format("%d-01-01", 1900 + inputStream.readInteger(1))); } /** @@ -448,4 +463,8 @@ protected static int deserializeFractionalSecondsInNanos(int fsp, ByteArrayInput return 0; } + protected static byte[] deserializeBit(int meta, ByteArrayInputStream inputStream) throws IOException { + return inputStream.read(meta); + } + } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/mysql/MysqlDataTypeConvertor.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/mysql/MysqlDataTypeConvertor.java index 33b26ebe72..f187e71fbd 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/mysql/MysqlDataTypeConvertor.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/mysql/MysqlDataTypeConvertor.java @@ -19,12 +19,23 @@ import org.apache.eventmesh.connector.jdbc.DataTypeConvertor; import org.apache.eventmesh.connector.jdbc.exception.DataTypeConvertException; -import org.apache.eventmesh.connector.jdbc.table.type.CalendarType; -import org.apache.eventmesh.connector.jdbc.table.type.DecimalType; import org.apache.eventmesh.connector.jdbc.table.type.EventMeshDataType; -import org.apache.eventmesh.connector.jdbc.table.type.PrimitiveByteArrayType; -import org.apache.eventmesh.connector.jdbc.table.type.PrimitiveType; import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.BooleanEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.BytesEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.DateEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.DateTimeEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.DecimalEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Float32EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Float64EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Int16EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Int32EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Int64EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Int8EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.NullEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.StringEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.TimeEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.YearEventMeshDataType; import java.sql.JDBCType; import java.util.Map; @@ -98,49 +109,50 @@ public EventMeshDataType toEventMeshType(MysqlType connectorDataType, MapMysql doc */ if (null == dataTypeProperties) { - return PrimitiveByteArrayType.BYTES_TYPE; + return BytesEventMeshDataType.INSTANCE; } Integer precision = (Integer) dataTypeProperties.get(MysqlDataTypeConvertor.PRECISION); if (precision != null && precision == 1) { - return PrimitiveType.BOOLEAN_TYPE; + return BooleanEventMeshDataType.INSTANCE; } - return PrimitiveByteArrayType.BYTES_TYPE; + return BytesEventMeshDataType.INSTANCE; } case TINYINT: - return PrimitiveType.BYTE_TYPE; + return Int8EventMeshDataType.INSTANCE; case TINYINT_UNSIGNED: case SMALLINT: - return PrimitiveType.SHORT_TYPE; + return Int16EventMeshDataType.INSTANCE; case SMALLINT_UNSIGNED: case INT: case MEDIUMINT: case MEDIUMINT_UNSIGNED: - return PrimitiveType.INT_TYPE; + return Int32EventMeshDataType.INSTANCE; case INT_UNSIGNED: case BIGINT: - return PrimitiveType.LONG_TYPE; + return Int64EventMeshDataType.INSTANCE; case FLOAT: case FLOAT_UNSIGNED: - return PrimitiveType.FLOAT_TYPE; + return Float32EventMeshDataType.INSTANCE; case DOUBLE: case DOUBLE_UNSIGNED: - return PrimitiveType.DOUBLE_TYPE; + return Float64EventMeshDataType.INSTANCE; case TIME: - return CalendarType.LOCAL_TIME_TYPE; + return TimeEventMeshDataType.INSTANCE; case YEAR: + return YearEventMeshDataType.INSTANCE; case DATE: - return CalendarType.LOCAL_DATE_TYPE; + return DateEventMeshDataType.INSTANCE; case TIMESTAMP: case DATETIME: - return CalendarType.LOCAL_DATE_TIME_TYPE; + return DateTimeEventMeshDataType.INSTANCE; case CHAR: case VARCHAR: case TINYTEXT: @@ -149,7 +161,8 @@ public EventMeshDataType toEventMeshType(MysqlType connectorDataType, Map toEventMeshType(MysqlType connectorDataType, Map toEventMeshType(MysqlType connectorDataType, MapMysql doc-DECIMAL, NUMERIC */ if (dataTypeProperties == null) { - return new DecimalType(DEFAULT_PRECISION, DEFAULT_SCALE); + return new DecimalEventMeshDataType(DEFAULT_PRECISION, DEFAULT_SCALE); } Integer precision = (Integer) dataTypeProperties.getOrDefault(PRECISION, DEFAULT_PRECISION); Integer scale = (Integer) dataTypeProperties.getOrDefault(SCALE, DEFAULT_SCALE); - return new DecimalType(precision, scale); + return new DecimalEventMeshDataType(precision, scale); } default: throw new DataTypeConvertException(String.format("%s type is not supported", connectorDataType.getName())); @@ -218,8 +231,6 @@ public MysqlType toConnectorType(EventMeshDataType eventMeshDataType, Map snapshot private Callable createSnapshotDataEvent4TableCallable(Jc context, SnapshotContext snapshotContext, Queue connectionPool, String sql, TableId tableId) { UniversalJdbcContext universalJdbcContext = (UniversalJdbcContext) context; - universalJdbcContext.withTableId(tableId); + //universalJdbcContext.withTableId(tableId); return () -> { JdbcConnection connection = connectionPool.poll(); - MysqlSourceMateData sourceMateData = MysqlSourceMateData.newBuilder() - .name(sourceConnectorConfig.getName()) - .snapshot(true) - .withTableId(tableId) - .serverId(sourceConnectorConfig.getMysqlConfig().getServerId()) - .build(); + SourceMateData sourceMateData = buildSourceMateData(context, snapshotContext, tableId); TableSchema tableSchema = universalJdbcContext.getCatalogTableSet().getTableSchema(tableId); - Field field = new Field().withField("after").withType("field").withName("payload.after").withRequired(false); + Field field = new Field().withField("after").withName("payload.after").withRequired(false); List columns = tableSchema.getColumns(); if (CollectionUtils.isNotEmpty(columns)) { - List fields = columns.stream() - .map(col -> new Field(col.getDataType().getName(), col.isNotNull(), col.getName(), tableId.toString())) - .collect(Collectors.toList()); + List fields = columns.stream().map(col -> { + Column rebuild = Column.newBuilder().withName(col.getName()).withDataType(col.getDataType()) + .withJdbcType(col.getJdbcType()).withNativeType(col.getNativeType()).withOrder(col.getOrder()).build(); + return new Field(rebuild, col.isNotNull(), col.getName(), tableId.toString()); + }).collect(Collectors.toList()); field.withRequired(true).withFields(fields); } try (Statement statement = connection.createStatement(jdbcSourceConfig.getSourceConnectorConfig().getSnapshotFetchSize(), 100)) { @@ -239,13 +236,14 @@ private Callable createSnapshotDataEvent4TableCallable(Jc context, Snapsho while (resultSet.next()) { int columnCount = resultSet.getMetaData().getColumnCount(); InsertDataEvent event = new InsertDataEvent(tableId); - Payload payload = event.getJdbcConnectData().getPayload(); Map values = new HashMap<>(columnCount); for (int index = 1; index <= columnCount; ++index) { values.put(resultSet.getMetaData().getColumnName(index), resultSet.getObject(index)); } Builder builder = DataChanges.newBuilder(); builder.withAfter(values); + builder.withType(event.getDataChangeEventType().ofCode()); + final Payload payload = event.getJdbcConnectData().getPayload(); payload.withDataChanges(builder.build()); payload.withSource(sourceMateData); event.getJdbcConnectData().setSchema(new Schema(Collections.singletonList(field))); @@ -273,6 +271,16 @@ private Queue createConnectionPool(final SnapshotContext snapshotContext, TableId tableId); + /** * Pre-snapshot preparations. * diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/snapshot/SnapshotEngineFactory.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/snapshot/SnapshotEngineFactory.java index 164b5b97b4..d573d7081a 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/snapshot/SnapshotEngineFactory.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/snapshot/SnapshotEngineFactory.java @@ -17,8 +17,8 @@ package org.apache.eventmesh.connector.jdbc.source.dialect.snapshot; -import org.apache.eventmesh.connector.jdbc.DatabaseDialect; import org.apache.eventmesh.connector.jdbc.JdbcContext; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; import org.apache.eventmesh.connector.jdbc.source.config.JdbcSourceConfig; import org.apache.eventmesh.spi.EventMeshExtensionType; import org.apache.eventmesh.spi.EventMeshSPI; diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/snapshot/mysql/MysqlSnapshotEngine.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/snapshot/mysql/MysqlSnapshotEngine.java index 2d4a7c2d31..a707029012 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/snapshot/mysql/MysqlSnapshotEngine.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/snapshot/mysql/MysqlSnapshotEngine.java @@ -17,19 +17,25 @@ package org.apache.eventmesh.connector.jdbc.source.dialect.snapshot.mysql; +import org.apache.eventmesh.connector.jdbc.CatalogChanges; import org.apache.eventmesh.connector.jdbc.connection.mysql.MysqlJdbcConnection; import org.apache.eventmesh.connector.jdbc.context.mysql.MysqlOffsetContext; import org.apache.eventmesh.connector.jdbc.context.mysql.MysqlPartition; +import org.apache.eventmesh.connector.jdbc.dialect.mysql.MysqlDatabaseDialect; import org.apache.eventmesh.connector.jdbc.event.Event; import org.apache.eventmesh.connector.jdbc.event.EventConsumer; +import org.apache.eventmesh.connector.jdbc.event.SchemaChangeEventType; +import org.apache.eventmesh.connector.jdbc.source.SourceMateData; import org.apache.eventmesh.connector.jdbc.source.config.JdbcSourceConfig; import org.apache.eventmesh.connector.jdbc.source.config.MysqlConfig; import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlConstants; -import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlDatabaseDialect; import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlDialectSql; import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlJdbcContext; +import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlSourceMateData; import org.apache.eventmesh.connector.jdbc.source.dialect.snapshot.AbstractSnapshotEngine; +import org.apache.eventmesh.connector.jdbc.table.catalog.DefaultValueConvertor; import org.apache.eventmesh.connector.jdbc.table.catalog.TableId; +import org.apache.eventmesh.connector.jdbc.table.catalog.mysql.MysqlDefaultValueConvertorImpl; import org.apache.eventmesh.connector.jdbc.utils.MysqlUtils; import org.apache.commons.collections4.CollectionUtils; @@ -60,6 +66,8 @@ public class MysqlSnapshotEngine extends private MysqlJdbcConnection connection; + private DefaultValueConvertor defaultValueConvertor = new MysqlDefaultValueConvertorImpl(); + public MysqlSnapshotEngine(JdbcSourceConfig jdbcSourceConfig, MysqlDatabaseDialect databaseDialect, MysqlJdbcContext jdbcContext) { super(jdbcSourceConfig, databaseDialect, jdbcContext, jdbcContext.getPartition(), jdbcContext.getOffsetContext()); this.connection = databaseDialect.getConnection(); @@ -76,6 +84,29 @@ public void close() throws Exception { shutdown(); } + /** + * Builds the source metadata. + * + * @param context The context. + * @param snapshotContext The snapshot context. + * @param tableId The table id + * @return The source metadata. + */ + @Override + protected SourceMateData buildSourceMateData(MysqlJdbcContext context, SnapshotContext snapshotContext, + TableId tableId) { + + MysqlSourceMateData sourceMateData = MysqlSourceMateData.newBuilder() + .name(sourceConnectorConfig.getName()) + .withTableId(tableId) + .serverId(sourceConnectorConfig.getMysqlConfig().getServerId()) + .snapshot(true) + .position(context.getSourceInfo().getCurrentBinlogPosition()) + .build(); + + return sourceMateData; + } + @Override protected void preSnapshot(MysqlJdbcContext jdbcContext, SnapshotContext snapshotContext) { // nothing to do @@ -185,7 +216,17 @@ private void addParseDdlAndEvent(MysqlJdbcContext jdbcContext, String ddl, Table if (event == null) { return; } - event.getJdbcConnectData().getPayload().ofSourceMateData().setSnapshot(true); + // handle default value expression + if (event.getJdbcConnectData().isSchemaChanges()) { + CatalogChanges catalogChanges = event.getJdbcConnectData().getPayload().getCatalogChanges(); + SchemaChangeEventType schemaChangeEventType = SchemaChangeEventType.ofSchemaChangeEventType(catalogChanges.getType(), + catalogChanges.getOperationType()); + if (SchemaChangeEventType.TABLE_CREATE == schemaChangeEventType || SchemaChangeEventType.TABLE_ALERT == schemaChangeEventType) { + catalogChanges.getColumns().forEach( + column -> column.setDefaultValue(defaultValueConvertor.parseDefaultValue(column, column.getDefaultValueExpression()))); + } + } + event.getJdbcConnectData().getPayload().withDdl(ddl).ofSourceMateData().setSnapshot(true); eventQueue.put(event); } catch (InterruptedException e) { throw new RuntimeException(e); diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/snapshot/mysql/MysqlSnapshotEngineFactory.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/snapshot/mysql/MysqlSnapshotEngineFactory.java index 53c5f993da..210ded0edf 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/snapshot/mysql/MysqlSnapshotEngineFactory.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/source/dialect/snapshot/mysql/MysqlSnapshotEngineFactory.java @@ -17,10 +17,10 @@ package org.apache.eventmesh.connector.jdbc.source.dialect.snapshot.mysql; -import org.apache.eventmesh.connector.jdbc.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.dialect.mysql.MysqlDatabaseDialect; import org.apache.eventmesh.connector.jdbc.source.config.JdbcSourceConfig; import org.apache.eventmesh.connector.jdbc.source.dialect.antlr4.mysql.MysqlAntlr4DdlParser; -import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlDatabaseDialect; import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlJdbcContext; import org.apache.eventmesh.connector.jdbc.source.dialect.snapshot.SnapshotEngine; import org.apache.eventmesh.connector.jdbc.source.dialect.snapshot.SnapshotEngineFactory; diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/AbstractColumnEditorImpl.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/AbstractColumnEditorImpl.java index 146d232a2d..a3bcc97f8e 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/AbstractColumnEditorImpl.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/AbstractColumnEditorImpl.java @@ -20,6 +20,7 @@ import org.apache.eventmesh.connector.jdbc.table.type.EventMeshDataType; import java.sql.JDBCType; +import java.util.List; public abstract class AbstractColumnEditorImpl implements ColumnEditor { @@ -36,7 +37,7 @@ public abstract class AbstractColumnEditorImpl enumValues; + + private Options options; + public AbstractColumnEditorImpl(String name) { this.name = name; } @@ -132,7 +146,7 @@ public CE withJdbcType(JDBCType jdbcType) { */ @SuppressWarnings("unchecked") @Override - public CE withEventMeshType(EventMeshDataType eventMeshType) { + public CE withEventMeshType(EventMeshDataType eventMeshType) { this.eventMeshDataType = eventMeshType; return (CE) this; } @@ -158,7 +172,7 @@ public CE withOrder(int order) { */ @SuppressWarnings("unchecked") @Override - public CE length(int length) { + public CE length(long length) { this.columnLength = length; return (CE) this; } @@ -241,11 +255,47 @@ public CE notNull(boolean notNull) { return (CE) this; } - public EventMeshDataType ofEventMeshDataType() { + @SuppressWarnings("unchecked") + @Override + public CE charsetName(String charsetName) { + this.charsetName = charsetName; + return (CE) this; + } + + @SuppressWarnings("unchecked") + @Override + public CE enumValues(List enumValues) { + this.enumValues = enumValues; + return (CE) this; + } + + @SuppressWarnings("unchecked") + @Override + public CE withOption(String key, Object value) { + if (options == null) { + this.options = new Options(); + } + this.options.put(key, value); + return (CE) this; + } + + @SuppressWarnings("unchecked") + @Override + public CE withOptions(Options options) { + if (options != null) { + if (this.options == null) { + this.options = new Options(); + } + this.options.putAll(options); + } + return (CE) this; + } + + public EventMeshDataType ofEventMeshDataType() { return eventMeshDataType; } - public Integer ofColumnLength() { + public Long ofColumnLength() { return columnLength; } @@ -284,4 +334,16 @@ public boolean isOptional() { public int ofOrder() { return this.order; } + + public String ofCharsetName() { + return this.charsetName; + } + + public List ofEnumValues() { + return this.enumValues; + } + + public Options ofOptions() { + return this.options; + } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/AbstractTableEditorImpl.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/AbstractTableEditorImpl.java index 61a7c7bf3f..4d26b3cb85 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/AbstractTableEditorImpl.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/AbstractTableEditorImpl.java @@ -174,11 +174,6 @@ public TE withUniqueKeys(UniqueKey... uniqueKeys) { return (TE) this; } - /** - * @param key - * @param value - * @return - */ @Override @SuppressWarnings("unchecked") public TE withOption(String key, Object value) { diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/CatalogTableSet.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/CatalogTableSet.java index ed2b6dc6e4..16c083f06a 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/CatalogTableSet.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/CatalogTableSet.java @@ -51,6 +51,7 @@ public void overrideTable(TableSchema tableSchema) { return; } tableSchemaMap.putTableSchema(tableSchema); + tableIdSet.addTableId(tableSchema.getTableId()); } public TableSchema getTableSchema(TableId tableId) { @@ -65,6 +66,10 @@ public TableIdSet() { values = new HashSet<>(32); } + public void addTableId(TableId tableId) { + values.add(tableId); + } + public void removeDatabase(String catalogName, String schemaName) { values.removeIf( entry -> StringUtils.equals(entry.getCatalogName(), catalogName) && StringUtils.equals(entry.getSchemaName(), schemaName)); diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/Column.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/Column.java index 94ed25f24e..363a50c147 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/Column.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/Column.java @@ -22,6 +22,12 @@ import java.io.Serializable; import java.sql.JDBCType; import java.sql.Types; +import java.util.List; +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.AllArgsConstructor; import lombok.Data; @@ -33,7 +39,7 @@ @Data @AllArgsConstructor @NoArgsConstructor -public abstract class Column implements Serializable { +public class Column implements Serializable { /** * Name of the column @@ -43,7 +49,9 @@ public abstract class Column implements Serializable { /** * Data type of the column */ - protected EventMeshDataType dataType; + @JsonSerialize(using = EventMeshDataTypeJsonSerializer.class) + @JsonDeserialize(using = EventMeshDataTypeJsonDeserializer.class) + protected EventMeshDataType dataType; /** * {@link Types JDBC type} @@ -53,7 +61,7 @@ public abstract class Column implements Serializable { /** * Length of the column */ - protected Integer columnLength; + protected Long columnLength; /** * Decimal point of the column @@ -63,7 +71,7 @@ public abstract class Column implements Serializable { /** * Indicates if the column can be null or not */ - protected boolean notNull; + protected boolean notNull = false; /** * Comment for the column @@ -75,15 +83,202 @@ public abstract class Column implements Serializable { */ protected Object defaultValue; + @JsonIgnore protected String defaultValueExpression; - protected int order; + // order of the column in the table + protected int order = 1; + + protected String charsetName; + + // Use wrapper types to reduce data transmission during serialization + protected Boolean autoIncremented; + + protected Boolean generated; + + protected String collationName; + + protected List enumValues; + + // for mysql: varchar or json + protected String nativeType; + + protected Options options; + + public Column(String name, EventMeshDataType dataType, JDBCType jdbcType, Long columnLength, Integer decimal, boolean notNull, String comment, + Object defaultValue, String defaultValueExpression, int order, String charsetName, boolean autoIncremented, boolean generated, + String collationName) { + this.name = name; + this.dataType = dataType; + this.jdbcType = jdbcType; + this.columnLength = columnLength; + this.decimal = decimal; + this.notNull = notNull; + this.comment = comment; + this.defaultValue = defaultValue; + this.defaultValueExpression = defaultValueExpression; + this.order = order; + this.charsetName = charsetName; + this.autoIncremented = autoIncremented; + this.generated = generated; + this.collationName = collationName; + } + + private Column(Builder builder) { + this.name = builder.name; + this.dataType = builder.dataType; + this.jdbcType = builder.jdbcType; + this.columnLength = builder.columnLength; + this.decimal = builder.decimal; + this.notNull = builder.notNull; + this.comment = builder.comment; + this.defaultValue = builder.defaultValue; + this.defaultValueExpression = builder.defaultValueExpression; + this.order = builder.order; + this.charsetName = builder.charsetName; + this.autoIncremented = builder.autoIncremented; + this.generated = builder.generated; + this.collationName = builder.collationName; + this.enumValues = builder.enumValues; + this.nativeType = builder.nativeType; + this.options = builder.options; + } + + public boolean isAutoIncremented() { + return Optional.ofNullable(this.autoIncremented).orElse(false); + } + + public static Builder newBuilder() { + return new Builder(); + } /** * creates a clone of the Column * * @return clone of column */ - public abstract Col clone(); + public Col clone() { + return null; + } + + /** + * Builder for the Column class + */ + public static class Builder { + + protected String name; + protected EventMeshDataType dataType; + protected JDBCType jdbcType; + protected Long columnLength; + protected Integer decimal; + protected boolean notNull = false; + protected String comment; + protected Object defaultValue; + protected String defaultValueExpression; + protected int order = 1; + protected String charsetName; + protected Boolean autoIncremented; + protected Boolean generated; + protected String collationName; + protected List enumValues; + // for mysql: varchar or json + protected String nativeType; + + protected Options options; + + public Builder withName(String name) { + this.name = name; + return this; + } + + public Builder withDataType(EventMeshDataType dataType) { + this.dataType = dataType; + return this; + } + + public Builder withJdbcType(JDBCType jdbcType) { + this.jdbcType = jdbcType; + return this; + } + + public Builder withColumnLength(Long columnLength) { + this.columnLength = columnLength; + return this; + } + + public Builder withDecimal(Integer decimal) { + this.decimal = decimal; + return this; + } + + public Builder withNotNull(boolean notNull) { + this.notNull = notNull; + return this; + } + + public Builder withComment(String comment) { + this.comment = comment; + return this; + } + + public Builder withDefaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + public Builder withDefaultValueExpression(String defaultValueExpression) { + this.defaultValueExpression = defaultValueExpression; + return this; + } + + public Builder withOrder(int order) { + this.order = order; + return this; + } + + public Builder withCharsetName(String charsetName) { + this.charsetName = charsetName; + return this; + } + + public Builder withAutoIncremented(boolean autoIncremented) { + this.autoIncremented = autoIncremented; + return this; + } + + public Builder withGenerated(boolean generated) { + this.generated = generated; + return this; + } + + public Builder withCollationName(String collationName) { + this.collationName = collationName; + return this; + } + + public Builder withEnumValues(List enumValues) { + this.enumValues = enumValues; + return this; + } + + public Builder withNativeType(String nativeType) { + this.nativeType = nativeType; + return this; + } + + public Builder withOptions(Options options) { + this.options = options; + return this; + } + + /** + * Builds the Column instance. + * + * @return Column instance + */ + public Column build() { + return new Column(this); + } + } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/ColumnEditor.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/ColumnEditor.java index 2661c7db27..e99abaccd8 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/ColumnEditor.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/ColumnEditor.java @@ -20,6 +20,7 @@ import org.apache.eventmesh.connector.jdbc.table.type.EventMeshDataType; import java.sql.JDBCType; +import java.util.List; /** * An interface for building and configuring columns in a database table. @@ -66,7 +67,7 @@ public interface ColumnEditor { * @param eventMeshType The EventMesh data type of the column. * @return The column editor instance. */ - CE withEventMeshType(EventMeshDataType eventMeshType); + CE withEventMeshType(EventMeshDataType eventMeshType); /** * Sets the order or position of the column within a table. @@ -82,7 +83,7 @@ public interface ColumnEditor { * @param length The length of the column. * @return The column editor instance. */ - CE length(int length); + CE length(long length); /** * Sets the scale of the column (if applicable). @@ -132,10 +133,19 @@ public interface ColumnEditor { */ CE notNull(boolean notNull); + CE charsetName(String charsetName); + + CE enumValues(List enumValues); + + CE withOption(String key, Object value); + + CE withOptions(Options options); + /** * Builds and returns the configured column. * * @return The configured column. */ Col build(); + } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/DefaultColumn.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/DefaultColumn.java index f682ea9438..e0dae1e617 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/DefaultColumn.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/DefaultColumn.java @@ -28,26 +28,34 @@ @NoArgsConstructor public class DefaultColumn extends Column { - public DefaultColumn(String name, EventMeshDataType dataType, JDBCType jdbcType, Integer columnLength, Integer decimal, boolean notNull, - String comment, Object defaultValue, String defaultValueExpression) { - super(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, 0); + public DefaultColumn(String name, EventMeshDataType dataType, JDBCType jdbcType, Long columnLength, Integer decimal, boolean notNull, + String comment, Object defaultValue, String defaultValueExpression, String charsetName, boolean autoIncremented, boolean generated, + String collationName) { + super(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, 0, charsetName, + autoIncremented, generated, collationName); } - public DefaultColumn(String name, EventMeshDataType dataType, JDBCType jdbcType, Integer columnLength, Integer decimal, boolean notNull, - String comment, Object defaultValue, String defaultValueExpression, int order) { - super(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, order); + public DefaultColumn(String name, EventMeshDataType dataType, JDBCType jdbcType, Long columnLength, Integer decimal, boolean notNull, + String comment, Object defaultValue, String defaultValueExpression, int order, String charsetName, boolean autoIncremented, boolean generated, + String collationName) { + super(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, order, charsetName, + autoIncremented, generated, collationName); } public static DefaultColumn of( - String name, EventMeshDataType dataType, JDBCType jdbcType, Integer columnLength, Integer decimal, boolean notNull, - String comment, Object defaultValue, String defaultValueExpression) { - return new DefaultColumn(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression); + String name, EventMeshDataType dataType, JDBCType jdbcType, Long columnLength, Integer decimal, boolean notNull, + String comment, Object defaultValue, String defaultValueExpression, String charsetName, boolean autoIncremented, boolean generated, + String collationName) { + return new DefaultColumn(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, + charsetName, autoIncremented, generated, collationName); } public static DefaultColumn of( - String name, EventMeshDataType dataType, JDBCType jdbcType, Integer columnLength, Integer decimal, boolean notNull, - String comment, Object defaultValue, String defaultValueExpression, int order) { - return new DefaultColumn(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, order); + String name, EventMeshDataType dataType, JDBCType jdbcType, Long columnLength, Integer decimal, boolean notNull, + String comment, Object defaultValue, String defaultValueExpression, int order, String charsetName, boolean autoIncremented, boolean generated, + String collationName) { + return new DefaultColumn(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, order, + charsetName, autoIncremented, generated, collationName); } /** @@ -57,6 +65,7 @@ public static DefaultColumn of( */ @Override public DefaultColumn clone() { - return DefaultColumn.of(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, order); + return DefaultColumn.of(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, order, + charsetName, autoIncremented, generated, collationName); } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/DefaultValueConvertor.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/DefaultValueConvertor.java new file mode 100644 index 0000000000..9c3e6d4185 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/DefaultValueConvertor.java @@ -0,0 +1,34 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.table.catalog; + +/** + * Functional interface for converting default values. + */ +@FunctionalInterface +public interface DefaultValueConvertor { + + /** + * Parses the default value expression for a column. + * + * @param column The column for which the default value is being parsed. + * @param defaultValueExpression The expression representing the default value. + * @return The parsed default value. + */ + Object parseDefaultValue(Column column, String defaultValueExpression); +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/EventMeshDataTypeJsonDeserializer.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/EventMeshDataTypeJsonDeserializer.java new file mode 100644 index 0000000000..86b5530614 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/EventMeshDataTypeJsonDeserializer.java @@ -0,0 +1,53 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.table.catalog; + +import org.apache.eventmesh.connector.jdbc.table.type.EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.table.type.EventMeshTypeNameConverter; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.NullEventMeshDataType; + +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; + +public class EventMeshDataTypeJsonDeserializer extends JsonDeserializer { + + @Override + public EventMeshDataType deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException, JacksonException { + JsonNode treeNode = jsonParser.readValueAsTree(); + Iterator> fields = treeNode.fields(); + while (fields.hasNext()) { + Map.Entry field = fields.next(); + if (StringUtils.equals("eventMeshDataType", field.getKey())) { + String value = field.getValue().asText(); + return EventMeshTypeNameConverter.ofEventMeshDataType(value); + } + } + return NullEventMeshDataType.INSTANCE; + } +} \ No newline at end of file diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/EventMeshDataTypeJsonSerializer.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/EventMeshDataTypeJsonSerializer.java new file mode 100644 index 0000000000..68fd9a6954 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/EventMeshDataTypeJsonSerializer.java @@ -0,0 +1,36 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.table.catalog; + +import org.apache.eventmesh.connector.jdbc.table.type.EventMeshDataType; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +public class EventMeshDataTypeJsonSerializer extends JsonSerializer { + + @Override + public void serialize(EventMeshDataType value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeStartObject(); + gen.writeStringField("eventMeshDataType", value.getName()); + gen.writeEndObject(); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/Index.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/Index.java new file mode 100644 index 0000000000..751d26cb13 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/Index.java @@ -0,0 +1,135 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.table.catalog; + +import java.io.Serializable; +import java.util.List; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * This class represents an Index object with attributes such as name, column names, index type, index method, and comment. + * It provides a Builder pattern for creating Index objects. + */ +@Getter +@Setter +@NoArgsConstructor +public class Index implements Serializable { + + // Name of the index + private String name; + + // List of column names included in the index + private List columnNames; + + // Type of the index (e.g., unique, normal, etc.) + private String indexType; + + // Method used for the index (e.g., B-tree, Hash, etc.) + private String indexMethod; + + // Comment associated with the index + private String comment; + + protected Index(Builder builder) { + this.name = builder.name; + this.columnNames = builder.columnNames; + this.indexType = builder.indexType; + this.indexMethod = builder.indexMethod; + this.comment = builder.comment; + } + + public Index(List columnNames) { + this.columnNames = columnNames; + } + + public Index(String name, List columnNames) { + this.name = name; + this.columnNames = columnNames; + } + + public Index(String name, List columnNames, String comment) { + this.name = name; + this.columnNames = columnNames; + this.comment = comment; + } + + public void addColumnNames(String... columnNames) { + if (columnNames != null && columnNames.length > 0) { + for (String columnName : columnNames) { + this.columnNames.add(columnName); + } + } + } + + public Index(String name, List columnNames, String indexType, String indexMethod, String comment) { + this.name = name; + this.columnNames = columnNames; + this.indexType = indexType; + this.indexMethod = indexMethod; + this.comment = comment; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + protected String name; + protected List columnNames; + protected String indexType; + protected String indexMethod; + protected String comment; + + public static Builder newIndex() { + return new Builder(); + } + + public Builder withName(String name) { + this.name = name; + return this; + } + + public Builder withColumnNames(List columnNames) { + this.columnNames = columnNames; + return this; + } + + public Builder withIndexType(String indexType) { + this.indexType = indexType; + return this; + } + + public Builder withIndexMethod(String indexMethod) { + this.indexMethod = indexMethod; + return this; + } + + public Builder withComment(String comment) { + this.comment = comment; + return this; + } + + public Index build() { + return new Index(this); + } + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/PrimaryKey.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/PrimaryKey.java index 2095b30429..48fa27d936 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/PrimaryKey.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/PrimaryKey.java @@ -22,6 +22,7 @@ public class PrimaryKey extends UniqueKey { public PrimaryKey() { + } public PrimaryKey(String name, List columnNames, String comment) { diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/Table.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/Table.java index 34a44eedff..c475227248 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/Table.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/Table.java @@ -19,16 +19,14 @@ import java.util.List; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data -@AllArgsConstructor @NoArgsConstructor public class Table { - private String name; + private TableId tableId; private PrimaryKey primaryKey; @@ -36,4 +34,72 @@ public class Table { private String comment; + private Options options = new Options(); + + public Table(TableId tableId, PrimaryKey primaryKey, List uniqueKeys, String comment) { + this.tableId = tableId; + this.primaryKey = primaryKey; + this.uniqueKeys = uniqueKeys; + this.comment = comment; + } + + public Table(TableId tableId, PrimaryKey primaryKey, List uniqueKeys, String comment, Options options) { + this.tableId = tableId; + this.primaryKey = primaryKey; + this.uniqueKeys = uniqueKeys; + this.comment = comment; + if (null != options) { + this.options.putAll(options); + } + } + + public void put(String key, Object value) { + options.put(key, value); + } + + public void putAll(Options options) { + this.options.putAll(options); + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder { + + private TableId tableId; + private PrimaryKey primaryKey; + private List uniqueKeys; + private String comment; + private Options options; + + public Builder withTableId(TableId tableId) { + this.tableId = tableId; + return this; + } + + public Builder withPrimaryKey(PrimaryKey primaryKey) { + this.primaryKey = primaryKey; + return this; + } + + public Builder withUniqueKeys(List uniqueKeys) { + this.uniqueKeys = uniqueKeys; + return this; + } + + public Builder withComment(String comment) { + this.comment = comment; + return this; + } + + public Builder withOptions(Options options) { + this.options = options; + return this; + } + + public Table build() { + return new Table(tableId, primaryKey, uniqueKeys, comment, options); + } + } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/TableSchema.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/TableSchema.java index 3eadd78d56..fa4ed243d8 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/TableSchema.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/TableSchema.java @@ -40,14 +40,14 @@ public class TableSchema implements Serializable { /** * A map of column names to their respective column objects. */ - private Map columnMap; + private Map> columnMap; /** * A list of columns in the table. */ - private List columns; + private List> columns; - private Map orderColumnMap; + private Map> orderColumnMap; /** * The primary key of the table. @@ -58,8 +58,8 @@ public class TableSchema implements Serializable { private String comment; - public TableSchema(TableId tableId, Map columnMap, List columns, - Map orderColumnMap, PrimaryKey primaryKey, List uniqueKeys, String comment) { + public TableSchema(TableId tableId, Map> columnMap, List> columns, + Map> orderColumnMap, PrimaryKey primaryKey, List uniqueKeys, String comment) { this.tableId = tableId; this.columnMap = columnMap; this.columns = columns; @@ -88,9 +88,9 @@ public static TableSchemaBuilder newTableSchemaBuilder() { public static class TableSchemaBuilder { private TableId tableId; - private Map columnMap; - private Map orderColumnMap; - private List columns; + private Map> columnMap; + private Map> orderColumnMap; + private List> columns; private PrimaryKey primaryKey; private List uniqueKeys; private String comment; @@ -104,12 +104,12 @@ public TableSchemaBuilder withTableId(TableId tableId) { return this; } - public TableSchemaBuilder withColumns(Map columnMap) { + public TableSchemaBuilder withColumns(Map> columnMap) { this.columnMap = columnMap; return this; } - public TableSchemaBuilder withColumns(List columns) { + public TableSchemaBuilder withColumns(List> columns) { this.columns = columns; return this; } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/UniqueKey.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/UniqueKey.java index f44ed561f4..4677d43c62 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/UniqueKey.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/UniqueKey.java @@ -17,7 +17,6 @@ package org.apache.eventmesh.connector.jdbc.table.catalog; -import java.io.Serializable; import java.util.List; import lombok.Getter; @@ -29,44 +28,26 @@ */ @Setter @Getter -public class UniqueKey implements Serializable { +public class UniqueKey extends Index { - // The name of the unique key, if specified. - private String name; - - // The list of column names that make up the unique/primary key. - private List columnNames; - - // An optional comment or description for the unique/primary key. - private String comment; + private static final String INDEX_TYPE = "UNIQUE"; public UniqueKey() { } public UniqueKey(String name, List columnNames, String comment) { - this.name = name; - this.columnNames = columnNames; - this.comment = comment; + super(name, columnNames, INDEX_TYPE, null, comment); } public UniqueKey(String name, List columnNames) { - this.name = name; - this.columnNames = columnNames; + super(name, columnNames); } public UniqueKey(List columnNames) { - this.columnNames = columnNames; + super(columnNames); } public UniqueKey copy() { - return new UniqueKey(name, columnNames, comment); - } - - public void addColumnNames(String... columnNames) { - if (columnNames != null && columnNames.length > 0) { - for (String columnName : columnNames) { - this.columnNames.add(columnName); - } - } + return new UniqueKey(getName(), getColumnNames(), getComment()); } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlColumn.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlColumn.java index c0440a4d14..424f70f3a6 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlColumn.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlColumn.java @@ -18,36 +18,34 @@ package org.apache.eventmesh.connector.jdbc.table.catalog.mysql; import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.catalog.Options; import org.apache.eventmesh.connector.jdbc.table.type.EventMeshDataType; import java.sql.JDBCType; +import java.util.List; + +import lombok.Data; +import lombok.EqualsAndHashCode; /** * Represents a MySQL column in a database table. */ +@Data +@EqualsAndHashCode(callSuper = true) public class MysqlColumn extends Column { - private boolean autoIncremented; - - private boolean generated; - - private String collationName; - - public MysqlColumn(String name, EventMeshDataType dataType, JDBCType jdbcType, Integer columnLength, Integer decimal, boolean notNull, - String comment, Object defaultValue, String defaultValueExpression, boolean autoIncremented, boolean generated, String collationName) { - super(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, 0); - this.autoIncremented = autoIncremented; - this.generated = generated; - this.collationName = collationName; + public MysqlColumn(String name, EventMeshDataType dataType, JDBCType jdbcType, Long columnLength, Integer decimal, boolean notNull, + String comment, Object defaultValue, String defaultValueExpression, boolean autoIncremented, boolean generated, String collationName, + String charsetName, List enumValues, String nativeType, Options options) { + super(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, 0, charsetName, + autoIncremented, generated, collationName, enumValues, nativeType, options); } - public MysqlColumn(String name, EventMeshDataType dataType, JDBCType jdbcType, Integer columnLength, Integer decimal, boolean notNull, + public MysqlColumn(String name, EventMeshDataType dataType, JDBCType jdbcType, Long columnLength, Integer decimal, boolean notNull, String comment, Object defaultValue, String defaultValueExpression, boolean autoIncremented, boolean generated, String collationName, - int order) { - super(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, order); - this.autoIncremented = autoIncremented; - this.generated = generated; - this.collationName = collationName; + int order, String charsetName, List enumValues, String nativeType, Options options) { + super(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, order, charsetName, + autoIncremented, generated, collationName, enumValues, nativeType, options); } public MysqlColumn() { @@ -55,17 +53,19 @@ public MysqlColumn() { } public static MysqlColumn of( - String name, EventMeshDataType dataType, JDBCType jdbcType, Integer columnLength, Integer decimal, boolean notNull, - String comment, Object defaultValue, String defaultValueExpression, boolean autoIncremented, boolean generated, String collationName) { + String name, EventMeshDataType dataType, JDBCType jdbcType, Long columnLength, Integer decimal, boolean notNull, + String comment, Object defaultValue, String defaultValueExpression, boolean autoIncremented, boolean generated, String collationName, + String charsetName, List enumValues, String nativeType, Options options) { return new MysqlColumn(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, - autoIncremented, generated, collationName); + autoIncremented, generated, collationName, charsetName, enumValues, nativeType, options); } public static MysqlColumn of( - String name, EventMeshDataType dataType, JDBCType jdbcType, Integer columnLength, Integer decimal, boolean notNull, String comment, - Object defaultValue, String defaultValueExpression, boolean autoIncremented, boolean generated, String collationName, int order) { + String name, EventMeshDataType dataType, JDBCType jdbcType, Long columnLength, Integer decimal, boolean notNull, String comment, + Object defaultValue, String defaultValueExpression, boolean autoIncremented, boolean generated, String collationName, int order, + String charsetName, List enumValues, String nativeType, Options options) { return new MysqlColumn(name, dataType, jdbcType, columnLength, decimal, notNull, comment, defaultValue, defaultValueExpression, - autoIncremented, generated, collationName, order); + autoIncremented, generated, collationName, order, charsetName, enumValues, nativeType, options); } /** diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlColumnEditor.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlColumnEditor.java index cea5ea9ceb..470e31affd 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlColumnEditor.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlColumnEditor.java @@ -65,6 +65,6 @@ static MysqlColumnEditor ofEditor() { * @param collationName The name of the collation to set. * @return The column editor with the collation set. */ - MysqlColumnEditor collate(String collationName); + MysqlColumnEditor collation(String collationName); } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlColumnEditorImpl.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlColumnEditorImpl.java index c7b7648af2..9f6ac950d0 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlColumnEditorImpl.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlColumnEditorImpl.java @@ -17,7 +17,12 @@ package org.apache.eventmesh.connector.jdbc.table.catalog.mysql; +import org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlDataTypeConvertor; import org.apache.eventmesh.connector.jdbc.table.catalog.AbstractColumnEditorImpl; +import org.apache.eventmesh.connector.jdbc.table.type.EventMeshDataType; + +import java.util.HashMap; +import java.util.Map; public class MysqlColumnEditorImpl extends AbstractColumnEditorImpl implements MysqlColumnEditor { @@ -27,6 +32,8 @@ public class MysqlColumnEditorImpl extends AbstractColumnEditorImpl dataTypeProperties = new HashMap<>(); + if (ofColumnLength() != null) { + dataTypeProperties.put(MysqlDataTypeConvertor.PRECISION, ofColumnLength().intValue()); + } + dataTypeProperties.put(MysqlDataTypeConvertor.SCALE, ofScale()); + EventMeshDataType eventMeshType = convertor.toEventMeshType(ofJdbcType(), dataTypeProperties); + withEventMeshType(eventMeshType); + return MysqlColumn.of(ofName(), ofEventMeshDataType(), ofJdbcType(), ofColumnLength(), ofScale(), isNotNull(), ofComment(), ofDefaultValue(), - ofDefaultValueExpression(), autoIncremented, generated, collationName, ofOrder()); + ofDefaultValueExpression(), autoIncremented, generated, collationName, ofOrder(), ofCharsetName(), ofEnumValues(), ofTypeName(), + ofOptions()); } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlDefaultValueConvertorImpl.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlDefaultValueConvertorImpl.java new file mode 100644 index 0000000000..699f4fd4f5 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlDefaultValueConvertorImpl.java @@ -0,0 +1,218 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.table.catalog.mysql; + +import org.apache.eventmesh.common.Constants; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.catalog.DefaultValueConvertor; +import org.apache.eventmesh.connector.jdbc.utils.ByteArrayUtils; + +import org.apache.commons.lang3.StringUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.JDBCType; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class MysqlDefaultValueConvertorImpl implements DefaultValueConvertor { + + private static final String EPOCH_DATE = "1970-01-01"; + + // Time The range is '-838:59:59.000000' to '838:59:59.000000' + private static final Pattern TIME_PATTERN = Pattern.compile("(\\-?[0-9]*):([0-9]*)(:([0-9]*))?(\\.([0-9]*))?"); + + private static final Set NUMBER_DATA_TYPES = Collections.unmodifiableSet(new HashSet<>( + Arrays.asList(JDBCType.TINYINT, JDBCType.INTEGER, JDBCType.DATE, JDBCType.TIMESTAMP, JDBCType.TIMESTAMP_WITH_TIMEZONE, JDBCType.TIME, + JDBCType.BOOLEAN, JDBCType.BIT, JDBCType.NUMERIC, JDBCType.DECIMAL, JDBCType.FLOAT, JDBCType.DOUBLE, JDBCType.REAL))); + + private static final Set BINARY_DATA_TYPES = Collections.unmodifiableSet(new HashSet<>( + Arrays.asList(JDBCType.BINARY, JDBCType.VARBINARY))); + + @Override + public Object parseDefaultValue(Column column, String defaultValueExpression) { + if (null == defaultValueExpression) { + return null; + } + defaultValueExpression = defaultValueExpression.trim(); + + if (NUMBER_DATA_TYPES.contains(column.getJdbcType()) && StringUtils.equalsAnyIgnoreCase(defaultValueExpression, Boolean.TRUE.toString(), + Boolean.FALSE.toString())) { + /* + * These types are synonyms for DECIMAL: DEC[(M[,D])] [UNSIGNED] [ZEROFILL], NUMERIC[(M[,D])] [UNSIGNED] [ZEROFILL], FIXED[(M[,D])] + * [UNSIGNED] [ZEROFILL] + */ + if (column.getJdbcType() == JDBCType.DECIMAL || column.getJdbcType() == JDBCType.NUMERIC) { + return convert2Decimal(column, defaultValueExpression); + } + return StringUtils.equalsIgnoreCase(Boolean.TRUE.toString(), defaultValueExpression) ? 1 : 0; + } + + if (BINARY_DATA_TYPES.contains(column.getJdbcType()) && column.getDefaultValueExpression() != null) { + // https://dev.mysql.com/doc/refman/8.0/en/binary-varbinary.html + String cleanedDefaultValueExpression = StringUtils.replace(column.getDefaultValueExpression(), "\\0", ""); + return ByteArrayUtils.bytesToHexString(cleanedDefaultValueExpression.getBytes(Constants.DEFAULT_CHARSET)); + } + + switch (column.getDataType().getSQLType()) { + case DATE: + return convert2LocalDate(column, defaultValueExpression); + case TIMESTAMP: + return convertToLocalDateTime(column, defaultValueExpression); + case TIMESTAMP_WITH_TIMEZONE: + return convertToTimestamp(column, defaultValueExpression); + case TIME: + return convertToLocalTime(column, defaultValueExpression); + case BOOLEAN: + return convert2Boolean(column, defaultValueExpression); + case BIT: + return convertToBits(column, defaultValueExpression); + + case NUMERIC: + case DECIMAL: + return convert2Decimal(column, defaultValueExpression); + + case FLOAT: + case DOUBLE: + case REAL: + return Double.parseDouble(defaultValueExpression); + default: + } + return defaultValueExpression; + } + + private Object convert2Boolean(Column column, String value) { + // value maybe is numeric or string + if (StringUtils.isNumeric(value)) { + return Integer.parseInt(value) != 0; + } + return Boolean.parseBoolean(value); + } + + private Object convert2Decimal(Column column, String value) { + return Optional.ofNullable(column.getDecimal()).isPresent() ? new BigDecimal(value).setScale(column.getDecimal(), RoundingMode.HALF_UP) + : new BigDecimal(value); + } + + private Object convertToBits(Column column, String value) { + // value: '101010111' + if (column.getColumnLength() > 1) { + int nums = value.length() / Byte.SIZE + (value.length() % Byte.SIZE == 0 ? 0 : 1); + byte[] bytes = new byte[nums]; + int length = value.length(); + for (int i = 0; i < nums; i++) { + int size = value.length() - Byte.SIZE < 0 ? 0 : value.length() - Byte.SIZE; + bytes[nums - i - 1] = (byte) Integer.parseInt(value.substring(size, length), 2); + value = value.substring(0, size); + } + return bytes; + } + + // value: '1' or '0' parse to boolean + return Short.parseShort(value) != 0; + } + + private Object convertToLocalTime(Column column, String value) { + + Matcher matcher = TIME_PATTERN.matcher(value); + if (!matcher.matches()) { + throw new IllegalArgumentException("Unexpected format for TIME column: " + value); + } + + final int hours = Integer.parseInt(matcher.group(1)); + final int minutes = Integer.parseInt(matcher.group(2)); + final String secondsGroup = matcher.group(4); + int seconds = 0; + int nanoSeconds = 0; + + if (secondsGroup != null) { + seconds = Integer.parseInt(secondsGroup); + String microSecondsString = matcher.group(6); + if (microSecondsString != null) { + nanoSeconds = Integer.parseInt(microSecondsString) * 1000; + } + } + return LocalTime.of(hours, minutes, seconds, nanoSeconds); + } + + private Object convertToTimestamp(Column column, String value) { + // Mysql not support + return null; + } + + private Object convertToLocalDateTime(Column column, String value) { + if (StringUtils.containsAny(value, "CURRENT_TIMESTAMP", "current_timestamp")) { + return value; + } + // The TIMESTAMP data type is used for values that contain both date and time parts. + // TIMESTAMP has a range of '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC. + return LocalDateTime.from(timestampFormat(Optional.ofNullable(column.getColumnLength()).orElse(0L).intValue()).parse(value)); + } + + private Object convert2LocalDate(Column column, String value) { + // The DATE type is used for values with a date part but no time part. + // MySQL retrieves and displays DATE values in 'YYYY-MM-DD' format. + // The supported range is '1000-01-01' to '9999-12-31'. + + try { + if (StringUtils.contains(value, "-")) { + return LocalDate.parse(value); + } + // maybe is year, e.g. 2020 + if (StringUtils.isNumeric(value)) { + return LocalDate.parse(value + "-01-01"); + } + // format: 20200101 + return LocalDate.from(dateFormat().parse(value)); + } catch (Exception e) { + log.warn("Convert date error[value={}]", value); + return LocalDate.parse(EPOCH_DATE); + } + } + + private DateTimeFormatter timestampFormat(int length) { + final DateTimeFormatterBuilder dtf = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd").optionalStart().appendLiteral(" ") + .append(DateTimeFormatter.ISO_LOCAL_TIME).optionalEnd().parseDefaulting(ChronoField.HOUR_OF_DAY, 0) + .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0).parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0); + if (length > 0) { + dtf.appendFraction(ChronoField.MICRO_OF_SECOND, 0, length, true); + } + return dtf.toFormatter(); + } + + private DateTimeFormatter dateFormat() { + final DateTimeFormatterBuilder dtf = new DateTimeFormatterBuilder().appendValue(ChronoField.YEAR, 4).appendValue(ChronoField.MONTH_OF_YEAR, 2) + .optionalStart().appendValue(ChronoField.DAY_OF_YEAR, 2); + return dtf.toFormatter(); + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlOptions.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlOptions.java new file mode 100644 index 0000000000..dc82e6ee51 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlOptions.java @@ -0,0 +1,47 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.table.catalog.mysql; + +/** + * The MysqlOptions class provides the constants for configuring options in MySQL tables and columns. + */ +public class MysqlOptions { + + public static final class MysqlTableOptions { + + public static String ENGINE = "ENGINE"; + + public static String AUTO_INCREMENT = "AUTO_INCREMENT"; + + public static String CHARSET = "CHARSET"; + + public static String COLLATE = "COLLATE"; + + public static String COMMENT = "COMMENT"; + } + + public static final class MysqlColumnOptions { + + public static String SIGNED = "SIGNED"; + + public static String UNSIGNED = "UNSIGNED"; + + public static String ZEROFILL = "ZEROFILL"; + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlTableSchema.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlTableSchema.java index c26ca5c3ce..4cddc03a65 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlTableSchema.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/catalog/mysql/MysqlTableSchema.java @@ -26,8 +26,11 @@ import java.util.List; import java.util.Map; +import lombok.Getter; + public class MysqlTableSchema extends TableSchema { + @Getter private Options tableOptions; public MysqlTableSchema(TableId tableId, Map columnMap, List columns, Map orderColumnMap, diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/CalendarType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/CalendarType.java deleted file mode 100644 index 788b85ecd7..0000000000 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/CalendarType.java +++ /dev/null @@ -1,84 +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. - */ - -package org.apache.eventmesh.connector.jdbc.table.type; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.temporal.Temporal; -import java.util.Objects; - -public class CalendarType implements EventMeshDataType { - - // Constants for LocalDate, LocalTime, and LocalDateTime types - public static final CalendarType LOCAL_DATE_TYPE = new CalendarType<>(LocalDate.class, SQLType.DATE); - - public static final CalendarType LOCAL_TIME_TYPE = new CalendarType<>(LocalTime.class, SQLType.TIME); - - public static final CalendarType LOCAL_DATE_TIME_TYPE = new CalendarType<>(LocalDateTime.class, SQLType.TIMESTAMP); - - private final Class typeClass; - private final SQLType sqlType; - - private CalendarType(Class typeClass, SQLType sqlType) { - this.typeClass = typeClass; - this.sqlType = sqlType; - } - - /** - * Returns the type class of the data. - * - * @return the type class of the data. - */ - @Override - public Class getTypeClass() { - return typeClass; - } - - /** - * Returns the SQL type of the data. - * - * @return the SQL type of the data. - */ - @Override - public SQLType getSQLType() { - return sqlType; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof CalendarType)) { - return false; - } - CalendarType that = (CalendarType) o; - return Objects.equals(getTypeClass(), that.getTypeClass()) && sqlType == that.sqlType; - } - - @Override - public int hashCode() { - return Objects.hash(getTypeClass(), sqlType); - } - - @Override - public String toString() { - return typeClass.getName(); - } -} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/EventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/EventMeshDataType.java index 07da72bea7..110ef499df 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/EventMeshDataType.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/EventMeshDataType.java @@ -17,22 +17,26 @@ package org.apache.eventmesh.connector.jdbc.table.type; +import org.apache.eventmesh.connector.jdbc.type.Type; + /** - * Defines Event Mesh data type with methods to get the type class and SQL type of the data. + * An interface representing a data type used in an EventMesh. + * + * @param The type of the data. */ -public interface EventMeshDataType { +public interface EventMeshDataType extends Type { /** - * Gets the type class of the data. + * Gets the class representing the type of the data. * - * @return the type class of the data. + * @return The class representing the type of the data. */ Class getTypeClass(); /** * Gets the SQL type of the data. * - * @return the SQL type of the data. + * @return The SQL type of the data. */ SQLType getSQLType(); @@ -41,7 +45,5 @@ public interface EventMeshDataType { * * @return The name of the data type. */ - default String getName() { - return EventMeshTypeNameConverter.ofName(this); - } + String getName(); } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/EventMeshTypeNameConverter.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/EventMeshTypeNameConverter.java index 181e735a91..f78386d37e 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/EventMeshTypeNameConverter.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/EventMeshTypeNameConverter.java @@ -17,45 +17,45 @@ package org.apache.eventmesh.connector.jdbc.table.type; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.BooleanEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.BytesEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.DateEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.DateTimeEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.DecimalEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Float32EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Float64EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Int16EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Int32EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Int64EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.Int8EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.StringEventMeshDataType; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.TimeEventMeshDataType; + import java.util.HashMap; import java.util.Map; public final class EventMeshTypeNameConverter { - private static Map, String> PRIMITIVE_TYPE_MAP = new HashMap<>(32); + private static Map PRIMITIVE_TYPE_MAP = new HashMap<>(32); static { - PRIMITIVE_TYPE_MAP.put(PrimitiveType.STRING_TYPE, "string"); - PRIMITIVE_TYPE_MAP.put(PrimitiveType.BOOLEAN_TYPE, "bool"); - PRIMITIVE_TYPE_MAP.put(PrimitiveType.BYTE_TYPE, "byte"); - PRIMITIVE_TYPE_MAP.put(PrimitiveType.SHORT_TYPE, "short"); - PRIMITIVE_TYPE_MAP.put(PrimitiveType.INT_TYPE, "int"); - PRIMITIVE_TYPE_MAP.put(PrimitiveType.LONG_TYPE, "long"); - PRIMITIVE_TYPE_MAP.put(PrimitiveType.FLOAT_TYPE, "float"); - PRIMITIVE_TYPE_MAP.put(PrimitiveType.DOUBLE_TYPE, "double"); - PRIMITIVE_TYPE_MAP.put(PrimitiveType.VOID_TYPE, "void"); - PRIMITIVE_TYPE_MAP.put(CalendarType.LOCAL_DATE_TYPE, "LocalDate"); - PRIMITIVE_TYPE_MAP.put(CalendarType.LOCAL_TIME_TYPE, "LocalTime"); - PRIMITIVE_TYPE_MAP.put(CalendarType.LOCAL_DATE_TIME_TYPE, "LocalDateTime"); - PRIMITIVE_TYPE_MAP.put(PrimitiveByteArrayType.BYTES_TYPE, "bytes"); - - PRIMITIVE_TYPE_MAP.put(PrimitiveArrayType.STRING_ARRAY_TYPE, "string-array"); - PRIMITIVE_TYPE_MAP.put(PrimitiveArrayType.BOOLEAN_ARRAY_TYPE, "bool-array"); - PRIMITIVE_TYPE_MAP.put(PrimitiveArrayType.BYTE_ARRAY_TYPE, "byte-array"); - PRIMITIVE_TYPE_MAP.put(PrimitiveArrayType.SHORT_ARRAY_TYPE, "short-array"); - PRIMITIVE_TYPE_MAP.put(PrimitiveArrayType.INT_ARRAY_TYPE, "int-array"); - PRIMITIVE_TYPE_MAP.put(PrimitiveArrayType.LONG_ARRAY_TYPE, "long-array"); - PRIMITIVE_TYPE_MAP.put(PrimitiveArrayType.FLOAT_ARRAY_TYPE, "float-array"); - PRIMITIVE_TYPE_MAP.put(PrimitiveArrayType.DOUBLE_ARRAY_TYPE, "double-array"); + PRIMITIVE_TYPE_MAP.put(BooleanEventMeshDataType.INSTANCE.getName(), BooleanEventMeshDataType.INSTANCE); + PRIMITIVE_TYPE_MAP.put(Float32EventMeshDataType.INSTANCE.getName(), Float32EventMeshDataType.INSTANCE); + PRIMITIVE_TYPE_MAP.put(Float64EventMeshDataType.INSTANCE.getName(), Float64EventMeshDataType.INSTANCE); + PRIMITIVE_TYPE_MAP.put(Int8EventMeshDataType.INSTANCE.getName(), Int8EventMeshDataType.INSTANCE); + PRIMITIVE_TYPE_MAP.put(Int16EventMeshDataType.INSTANCE.getName(), Int16EventMeshDataType.INSTANCE); + PRIMITIVE_TYPE_MAP.put(Int32EventMeshDataType.INSTANCE.getName(), Int32EventMeshDataType.INSTANCE); + PRIMITIVE_TYPE_MAP.put(Int64EventMeshDataType.INSTANCE.getName(), Int64EventMeshDataType.INSTANCE); + PRIMITIVE_TYPE_MAP.put(StringEventMeshDataType.INSTANCE.getName(), StringEventMeshDataType.INSTANCE); + PRIMITIVE_TYPE_MAP.put(BytesEventMeshDataType.INSTANCE.getName(), BytesEventMeshDataType.INSTANCE); + PRIMITIVE_TYPE_MAP.put(DateEventMeshDataType.INSTANCE.getName(), DateEventMeshDataType.INSTANCE); + PRIMITIVE_TYPE_MAP.put(TimeEventMeshDataType.INSTANCE.getName(), TimeEventMeshDataType.INSTANCE); + PRIMITIVE_TYPE_MAP.put(DateTimeEventMeshDataType.INSTANCE.getName(), DateTimeEventMeshDataType.INSTANCE); + PRIMITIVE_TYPE_MAP.put(DecimalEventMeshDataType.INSTANCE.getName(), DecimalEventMeshDataType.INSTANCE); } - public static String ofName(EventMeshDataType type) { - String typeName = PRIMITIVE_TYPE_MAP.get(type); - if (typeName == null && (type instanceof DecimalType)) { - DecimalType decimalType = (DecimalType) type; - return String.format("decimal(%s,%s)", decimalType.getScale(), decimalType.getPrecision()); - } - return typeName; + public static EventMeshDataType ofEventMeshDataType(String dataType) { + return PRIMITIVE_TYPE_MAP.get(dataType); } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/MapType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/MapType.java index 5e33d818bf..f8a5fb530f 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/MapType.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/MapType.java @@ -19,10 +19,9 @@ import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Objects; -public class MapType implements EventMeshDataType> { +public class MapType { private static final List SUPPORTED_KEY_TYPES = Arrays.asList( @@ -40,11 +39,11 @@ public class MapType implements EventMeshDataType> { SQLType.STRING, SQLType.DECIMAL); - private final EventMeshDataType keyType; + private final EventMeshDataType keyType; - private final EventMeshDataType valueType; + private final EventMeshDataType valueType; - public MapType(EventMeshDataType keyType, EventMeshDataType valueType) { + public MapType(EventMeshDataType keyType, EventMeshDataType valueType) { Objects.requireNonNull(keyType, "The key type is required."); Objects.requireNonNull(valueType, "The value type is required."); @@ -56,27 +55,6 @@ public MapType(EventMeshDataType keyType, EventMeshDataType valueType) { this.valueType = valueType; } - /** - * Returns the type class of the data. - * - * @return the type class of the data. - */ - @SuppressWarnings("unchecked") - @Override - public Class> getTypeClass() { - return (Class>) (Class) Map.class; - } - - /** - * Returns the SQL type of the data. - * - * @return the SQL type of the data. - */ - @Override - public SQLType getSQLType() { - return SQLType.MAP; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -94,11 +72,11 @@ public int hashCode() { return Objects.hash(keyType, valueType); } - public EventMeshDataType keyType() { + public EventMeshDataType keyType() { return this.keyType; } - public EventMeshDataType valueType() { + public EventMeshDataType valueType() { return this.valueType; } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/PrimitiveArrayType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/PrimitiveArrayType.java deleted file mode 100644 index d7f9abeb00..0000000000 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/PrimitiveArrayType.java +++ /dev/null @@ -1,99 +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. - */ - -package org.apache.eventmesh.connector.jdbc.table.type; - -import java.util.Objects; - -public class PrimitiveArrayType implements EventMeshDataType { - - @SuppressWarnings("unchecked") - public static final PrimitiveArrayType STRING_ARRAY_TYPE = new PrimitiveArrayType(String[].class, PrimitiveType.STRING_TYPE); - - @SuppressWarnings("unchecked") - public static final PrimitiveArrayType BOOLEAN_ARRAY_TYPE = new PrimitiveArrayType(Boolean[].class, - PrimitiveType.BOOLEAN_TYPE); - - @SuppressWarnings("unchecked") - public static final PrimitiveArrayType BYTE_ARRAY_TYPE = new PrimitiveArrayType(Byte[].class, PrimitiveType.BYTE_TYPE); - - @SuppressWarnings("unchecked") - public static final PrimitiveArrayType SHORT_ARRAY_TYPE = new PrimitiveArrayType(Short[].class, PrimitiveType.SHORT_TYPE); - - @SuppressWarnings("unchecked") - public static final PrimitiveArrayType INT_ARRAY_TYPE = new PrimitiveArrayType(Integer[].class, PrimitiveType.INT_TYPE); - - @SuppressWarnings("unchecked") - public static final PrimitiveArrayType LONG_ARRAY_TYPE = new PrimitiveArrayType(Long[].class, PrimitiveType.LONG_TYPE); - - @SuppressWarnings("unchecked") - public static final PrimitiveArrayType FLOAT_ARRAY_TYPE = new PrimitiveArrayType(Float[].class, PrimitiveType.FLOAT_TYPE); - - @SuppressWarnings("unchecked") - public static final PrimitiveArrayType DOUBLE_ARRAY_TYPE = new PrimitiveArrayType(Double[].class, PrimitiveType.DOUBLE_TYPE); - - private final Class typeClass; - - private final PrimitiveType sqlType; - - private PrimitiveArrayType(Class arrayClass, PrimitiveType elementType) { - this.typeClass = arrayClass; - this.sqlType = elementType; - } - - /** - * Returns the type class of the data. - * - * @return the type class of the data. - */ - @Override - public Class getTypeClass() { - return typeClass; - } - - /** - * Returns the SQL type of the data. - * - * @return the SQL type of the data. - */ - @Override - public SQLType getSQLType() { - return SQLType.ARRAY; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof PrimitiveArrayType)) { - return false; - } - PrimitiveArrayType that = (PrimitiveArrayType) o; - return Objects.equals(getTypeClass(), that.getTypeClass()) && Objects.equals(sqlType, that.sqlType); - } - - @Override - public int hashCode() { - return Objects.hash(getTypeClass(), sqlType); - } - - @Override - public String toString() { - return typeClass.getName(); - } -} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/PrimitiveByteArrayType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/PrimitiveByteArrayType.java deleted file mode 100644 index eb4ee47bbf..0000000000 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/PrimitiveByteArrayType.java +++ /dev/null @@ -1,65 +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. - */ - -package org.apache.eventmesh.connector.jdbc.table.type; - -public class PrimitiveByteArrayType implements EventMeshDataType { - - public static final PrimitiveByteArrayType BYTES_TYPE = new PrimitiveByteArrayType(); - - private PrimitiveByteArrayType() { - - } - - /** - * Returns the type class of the data. - * - * @return the type class of the data. - */ - @Override - public Class getTypeClass() { - return byte[].class; - } - - /** - * Returns the SQL type of the data. - * - * @return the SQL type of the data. - */ - @Override - public SQLType getSQLType() { - return SQLType.BINARY; - } - - @Override - public int hashCode() { - return byte[].class.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - return obj instanceof PrimitiveByteArrayType; - } - - @Override - public String toString() { - return byte[].class.getName(); - } -} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/PrimitiveType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/PrimitiveType.java deleted file mode 100644 index d6e8bdd514..0000000000 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/PrimitiveType.java +++ /dev/null @@ -1,84 +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. - */ - -package org.apache.eventmesh.connector.jdbc.table.type; - -import java.util.Objects; - -public class PrimitiveType implements EventMeshDataType { - - public static final PrimitiveType STRING_TYPE = new PrimitiveType<>(String.class, SQLType.STRING); - public static final PrimitiveType BOOLEAN_TYPE = new PrimitiveType<>(Boolean.class, SQLType.BOOLEAN); - public static final PrimitiveType BYTE_TYPE = new PrimitiveType<>(Byte.class, SQLType.TINYINT); - public static final PrimitiveType SHORT_TYPE = new PrimitiveType<>(Short.class, SQLType.SMALLINT); - public static final PrimitiveType INT_TYPE = new PrimitiveType<>(Integer.class, SQLType.INTEGER); - public static final PrimitiveType LONG_TYPE = new PrimitiveType<>(Long.class, SQLType.BIGINT); - public static final PrimitiveType FLOAT_TYPE = new PrimitiveType<>(Float.class, SQLType.FLOAT); - public static final PrimitiveType DOUBLE_TYPE = new PrimitiveType<>(Double.class, SQLType.DOUBLE); - public static final PrimitiveType VOID_TYPE = new PrimitiveType<>(Void.class, SQLType.NULL); - - private final Class typeClass; - - private final SQLType sqlType; - - public PrimitiveType(Class typeClass, SQLType sqlType) { - this.typeClass = typeClass; - this.sqlType = sqlType; - } - - /** - * Returns the type class of the data. - * - * @return the type class of the data. - */ - @Override - public Class getTypeClass() { - return typeClass; - } - - /** - * Returns the SQL type of the data. - * - * @return the SQL type of the data. - */ - @Override - public SQLType getSQLType() { - return sqlType; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof PrimitiveType)) { - return false; - } - PrimitiveType that = (PrimitiveType) o; - return Objects.equals(getTypeClass(), that.getTypeClass()) && sqlType == that.sqlType; - } - - @Override - public int hashCode() { - return Objects.hash(getTypeClass(), sqlType); - } - - @Override - public String toString() { - return typeClass.getName(); - } -} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/SQLType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/SQLType.java index 203ceb84bb..d8a397c700 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/SQLType.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/SQLType.java @@ -17,80 +17,92 @@ package org.apache.eventmesh.connector.jdbc.table.type; +import java.sql.Types; + /** * see {@link java.sql.SQLType} */ public enum SQLType { + BIT(Types.BIT), + /** * Identifies the generic SQL type {@code TINYINT}. */ - TINYINT, + TINYINT(Types.TINYINT), /** * Identifies the generic SQL type {@code SMALLINT}. */ - SMALLINT, + SMALLINT(Types.SMALLINT), /** * Identifies the generic SQL type {@code INTEGER}. */ - INTEGER, + INTEGER(Types.INTEGER), /** * Identifies the generic SQL type {@code BIGINT}. */ - BIGINT, + BIGINT(Types.BIGINT), /** * Identifies the generic SQL type {@code FLOAT}. */ - FLOAT, + FLOAT(Types.FLOAT), /** * Identifies the generic SQL type {@code DOUBLE}. */ - DOUBLE, + DOUBLE(Types.DOUBLE), /** * Identifies the generic SQL type {@code DECIMAL}. */ - DECIMAL, + DECIMAL(Types.DECIMAL), + NUMERIC(Types.NUMERIC), + REAL(Types.REAL), /** * Identifies the generic SQL type {@code DATE}. */ - DATE, + DATE(Types.DATE), /** * Identifies the generic SQL type {@code TIME}. */ - TIME, + TIME(Types.TIME), /** * Identifies the generic SQL type {@code TIMESTAMP}. */ - TIMESTAMP, + TIMESTAMP(Types.TIMESTAMP), + + TIMESTAMP_WITH_TIMEZONE(Types.TIMESTAMP_WITH_TIMEZONE), + /** * Identifies the generic SQL type {@code BINARY}. */ - BINARY, + BINARY(Types.BINARY), /** * Identifies the generic SQL value {@code NULL}. */ - NULL, + NULL(Types.NULL), /** * Identifies the generic SQL type {@code ARRAY}. */ - ARRAY, + ARRAY(Types.ARRAY), /** * Identifies the generic SQL type {@code BOOLEAN}. */ - BOOLEAN, + BOOLEAN(Types.BOOLEAN), - /** - * EventMesh generic SQL type - */ - ROW, + STRING(Types.VARCHAR); + + private int type; - MAP, + SQLType(int type) { + this.type = type; + } - STRING + public int ofType() { + return type; + } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/AbstractType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/AbstractType.java new file mode 100644 index 0000000000..47b5c87896 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/AbstractType.java @@ -0,0 +1,89 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.EventMeshDataType; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; + +import java.util.Optional; + +import org.hibernate.dialect.Dialect; + +public abstract class AbstractType implements EventMeshDataType { + + protected Dialect hibernateDialect; + + protected DatabaseDialect eventMeshDialect; + + private final Class typeClass; + + private final SQLType sqlType; + + private final String name; + + public AbstractType(Class typeClass, SQLType sqlType, String name) { + this.typeClass = typeClass; + this.sqlType = sqlType; + this.name = name; + } + + @Override + public void configure(DatabaseDialect eventMeshDialect, Dialect hibernateDialect) { + this.hibernateDialect = hibernateDialect; + this.eventMeshDialect = eventMeshDialect; + } + + @Override + public String getTypeName(Column column) { + Long length = Optional.ofNullable(column.getColumnLength()).orElse(0L); + return hibernateDialect.getTypeName(column.getJdbcType().getVendorTypeNumber(), length, length.intValue(), + Optional.ofNullable(column.getDecimal()).orElse(0)); + } + + /** + * Returns the type class of the data. + * + * @return the type class of the data. + */ + @Override + public Class getTypeClass() { + return typeClass; + } + + /** + * Returns the SQL type of the data. + * + * @return the SQL type of the data. + */ + @Override + public SQLType getSQLType() { + return sqlType; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + return column.getDefaultValue() == null ? "NULL" : column.getDefaultValue().toString(); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/DatabaseTypeDialect.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/DatabaseTypeDialect.java new file mode 100644 index 0000000000..1496165dc8 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/DatabaseTypeDialect.java @@ -0,0 +1,125 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type; + +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.catalog.Table; + +import org.hibernate.dialect.Dialect; + +/** + * Interface for defining database type dialects. + */ +public interface DatabaseTypeDialect { + + String EMPTY_STRING = ""; + + /** + * Configures the given Hibernate dialect. + * + * @param hibernateDialect The Hibernate dialect to be configured. + */ + void configure(Dialect hibernateDialect); + + /** + * Gets type for the given column. + * + * @param column The column for which to get the type. + * @return The Hibernate type. + */ + Type getType(Column column); + + /** + * Gets the type name for the given column and Hibernate dialect. + * + * @param hibernateDialect The Hibernate dialect. + * @param column The column for which to get the type name. + * @return The type name. + */ + String getTypeName(Dialect hibernateDialect, Column column); + + /** + * Gets a formatted string for a boolean value. + * + * @param value The boolean value. + * @return The formatted string. + */ + default String getBooleanFormatted(boolean value) { + return value ? Boolean.TRUE.toString() : Boolean.FALSE.toString(); + } + + /** + * Gets a formatted string for an auto-incrementing column. + * + * @param column The auto-incrementing column. + * @return The formatted string. + */ + default String getAutoIncrementFormatted(Column column) { + return ""; + } + + /** + * Gets a formatted string for the default value of a column. + * + * @param column The column. + * @return The formatted string. + */ + default String getDefaultValueFormatted(Column column) { + return ""; + } + + /** + * Gets a formatted string for the character set or collation of a column. + * + * @param column The column. + * @return The formatted string. + */ + default String getCharsetOrCollateFormatted(Column column) { + return ""; + } + + /** + * Gets a formatted string for table options. + * + * @param table The table. + * @return The formatted string. + */ + default String getTableOptionsFormatted(Table table) { + return ""; + } + + /** + * Gets a formatted string for query binding with a value cast. + * + * @param column The column. + * @return The formatted string. + */ + default String getQueryBindingWithValueCast(Column column) { + return "?"; + } + + /** + * Gets a formatted string for the comment of a column. + * + * @param column The column. + * @return The formatted string. + */ + default String getCommentFormatted(Column column) { + return ""; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/Type.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/Type.java new file mode 100644 index 0000000000..6a903919c3 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/Type.java @@ -0,0 +1,101 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; + +import java.util.List; + +import org.hibernate.dialect.Dialect; +import org.hibernate.query.Query; + +/** + * top interface of jdbc type + */ +public interface Type { + + /** + * Configures the database dialects for the EventMesh and Hibernate. + * + * @param eventMeshDialect The database dialect for the EventMesh. + * @param hibernateDialect The database dialect for Hibernate. + */ + void configure(DatabaseDialect eventMeshDialect, Dialect hibernateDialect); + + /** + * Retrieves a list of registration keys. + * + * @return A list of registration keys as strings. + */ + List ofRegistrationKeys(); + + /** + * Retrieves the default value for a given database dialect and column. + * + * @param databaseDialect The specific database dialect. + * @param column The column for which to retrieve the default value. + * @return The default value for the specified database dialect and column. + */ + String getDefaultValue(DatabaseDialect databaseDialect, Column column); + + /** + * Returns the type name of the specified column. + * + * @param column the column object for which to retrieve the type name + * @return the type name of the column + */ + String getTypeName(Column column); + + /** + * Returns the query binding with value for the specified column and database dialect. + * + * @param databaseDialect the database dialect object to determine the query binding + * @param column the column object for which to retrieve the query binding with value + * @return the query binding with value for the column and database dialect + */ + default String getQueryBindingWithValue(DatabaseDialect databaseDialect, Column column) { + return databaseDialect.getQueryBindingWithValueCast(column); + } + + /** + * Converts the given value to the appropriate database type. + * + * @param value the value to be converted + * @return the converted value in the database type + */ + default Object convert2DatabaseTypeValue(Object value) { + return value; + } + + /** + * Binds the parameter value to the specified position in the query object and returns the number of bound parameters. + * + * @param startIndex The starting index for binding parameters + * @param value The value to bind + * @param query The query object to bind parameters to + * @return The number of bound parameters, Default is 1 + */ + default int bindValue(int startIndex, Object value, Query query) { + // Binds the parameter value to the specified index position in the query object + query.setParameter(startIndex, value); + // Returns the number of bound parameters, always 1 + return 1; + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/BooleanEventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/BooleanEventMeshDataType.java new file mode 100644 index 0000000000..a004dd2309 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/BooleanEventMeshDataType.java @@ -0,0 +1,39 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.List; + +public class BooleanEventMeshDataType extends AbstractType { + + public static final BooleanEventMeshDataType INSTANCE = new BooleanEventMeshDataType(); + + private BooleanEventMeshDataType() { + super(Boolean.class, SQLType.BOOLEAN, "BOOLEAN"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList("boolean", "bool", getName(), "BOOL"); + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/BytesEventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/BytesEventMeshDataType.java new file mode 100644 index 0000000000..301d58d632 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/BytesEventMeshDataType.java @@ -0,0 +1,49 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.Base64; +import java.util.List; + +public class BytesEventMeshDataType extends AbstractType { + + public static final BytesEventMeshDataType INSTANCE = new BytesEventMeshDataType(); + + public BytesEventMeshDataType() { + super(byte[].class, SQLType.BINARY, "BYTES"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList("bytes", getName()); + } + + @Override + public Object convert2DatabaseTypeValue(Object value) { + // Jackson default serialize byte[] as base64 + String strValue = (String) value; + if (strValue == null) { + return null; + } + return Base64.getDecoder().decode(strValue); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/DateEventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/DateEventMeshDataType.java new file mode 100644 index 0000000000..e2427c9348 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/DateEventMeshDataType.java @@ -0,0 +1,47 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; + +public class DateEventMeshDataType extends AbstractType { + + public static final DateEventMeshDataType INSTANCE = new DateEventMeshDataType(); + + private DateEventMeshDataType() { + super(LocalDate.class, SQLType.DATE, "DATE"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName()); + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + return column.getDefaultValue() == null ? "NULL" : "'" + column.getDefaultValue() + "'"; + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/DateTimeEventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/DateTimeEventMeshDataType.java new file mode 100644 index 0000000000..abb2eea520 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/DateTimeEventMeshDataType.java @@ -0,0 +1,55 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class DateTimeEventMeshDataType extends AbstractType { + + public static final DateTimeEventMeshDataType INSTANCE = new DateTimeEventMeshDataType(); + + private DateTimeEventMeshDataType() { + super(LocalDateTime.class, SQLType.TIMESTAMP, "DATETIME"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName()); + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + if (column.getDefaultValue() == null) { + return "NULL"; + } + LocalDateTime localDateTime = LocalDateTime.parse(column.getDefaultValue().toString()); + if (Optional.ofNullable(column.getColumnLength()).orElse(0L) > 0) { + return "'" + localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S")) + "'"; + } + return "'" + localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + "'"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/DecimalType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/DecimalEventMeshDataType.java similarity index 53% rename from eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/DecimalType.java rename to eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/DecimalEventMeshDataType.java index 44fed4a667..b7d40a33ae 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/DecimalType.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/DecimalEventMeshDataType.java @@ -15,49 +15,42 @@ * limitations under the License. */ -package org.apache.eventmesh.connector.jdbc.table.type; +package org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; import java.math.BigDecimal; -import java.util.Objects; +import java.util.Arrays; +import java.util.List; import lombok.Getter; +import lombok.Setter; + +public class DecimalEventMeshDataType extends AbstractType { -public class DecimalType extends PrimitiveType { + public static final DecimalEventMeshDataType INSTANCE = new DecimalEventMeshDataType(); @Getter - private final int scale; + @Setter + private Integer scale; @Getter - private final int precision; + @Setter + private Integer precision; - public DecimalType(int scale, int precision) { - super(BigDecimal.class, SQLType.DECIMAL); + public DecimalEventMeshDataType(Integer precision, Integer scale) { + this(); this.precision = precision; this.scale = scale; } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof DecimalType)) { - return false; - } - if (!super.equals(o)) { - return false; - } - DecimalType that = (DecimalType) o; - return precision == that.precision && scale == that.scale; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), precision, scale); + public DecimalEventMeshDataType() { + super(BigDecimal.class, SQLType.DECIMAL, "DECIMAL"); } @Override - public String toString() { - return getTypeClass().getName(); + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "decimal"); } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Float32EventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Float32EventMeshDataType.java new file mode 100644 index 0000000000..6cfacdd700 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Float32EventMeshDataType.java @@ -0,0 +1,38 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.List; + +public class Float32EventMeshDataType extends AbstractType { + + public static final Float32EventMeshDataType INSTANCE = new Float32EventMeshDataType(); + + private Float32EventMeshDataType() { + super(Float.class, SQLType.FLOAT, "FLOAT"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "FLOAT32", "float32", "float"); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Float64EventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Float64EventMeshDataType.java new file mode 100644 index 0000000000..938cb1f64a --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Float64EventMeshDataType.java @@ -0,0 +1,39 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.List; + +public class Float64EventMeshDataType extends AbstractType { + + public static final Float64EventMeshDataType INSTANCE = new Float64EventMeshDataType(); + + private Float64EventMeshDataType() { + super(Double.class, SQLType.DOUBLE, "FLOAT64"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "float64", "double"); + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Int16EventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Int16EventMeshDataType.java new file mode 100644 index 0000000000..87b4d00716 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Int16EventMeshDataType.java @@ -0,0 +1,39 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.List; + +public class Int16EventMeshDataType extends AbstractType { + + public static final Int16EventMeshDataType INSTANCE = new Int16EventMeshDataType(); + + private Int16EventMeshDataType() { + super(Short.class, SQLType.SMALLINT, "INT16"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "int16", "shot", "SHORT", "Short", "smallint"); + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Int32EventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Int32EventMeshDataType.java new file mode 100644 index 0000000000..e5c8f8146c --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Int32EventMeshDataType.java @@ -0,0 +1,39 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.List; + +public class Int32EventMeshDataType extends AbstractType { + + public static final Int32EventMeshDataType INSTANCE = new Int32EventMeshDataType(); + + private Int32EventMeshDataType() { + super(Integer.class, SQLType.INTEGER, "INT32"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "int32", "Integer", "INTEGER", "integer"); + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Int64EventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Int64EventMeshDataType.java new file mode 100644 index 0000000000..8b45479a0a --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Int64EventMeshDataType.java @@ -0,0 +1,39 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.List; + +public class Int64EventMeshDataType extends AbstractType { + + public static final Int64EventMeshDataType INSTANCE = new Int64EventMeshDataType(); + + private Int64EventMeshDataType() { + super(Long.class, SQLType.BIGINT, "INT64"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "int64", "long", "Long", "LONG", "bigint", "BIGINT"); + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Int8EventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Int8EventMeshDataType.java new file mode 100644 index 0000000000..f59a8ed594 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/Int8EventMeshDataType.java @@ -0,0 +1,39 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.List; + +public class Int8EventMeshDataType extends AbstractType { + + public static final Int8EventMeshDataType INSTANCE = new Int8EventMeshDataType(); + + private Int8EventMeshDataType() { + super(Byte.class, SQLType.TINYINT, "INT8"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList("INT8, int8"); + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/NullEventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/NullEventMeshDataType.java new file mode 100644 index 0000000000..21bdf7cf15 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/NullEventMeshDataType.java @@ -0,0 +1,39 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.List; + +public class NullEventMeshDataType extends AbstractType { + + public static final NullEventMeshDataType INSTANCE = new NullEventMeshDataType(); + + private NullEventMeshDataType() { + super(Void.class, SQLType.NULL, "NULL"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "null"); + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/StringEventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/StringEventMeshDataType.java new file mode 100644 index 0000000000..0b068d8f69 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/StringEventMeshDataType.java @@ -0,0 +1,45 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.List; + +public class StringEventMeshDataType extends AbstractType { + + public static final StringEventMeshDataType INSTANCE = new StringEventMeshDataType(); + + public StringEventMeshDataType() { + super(String.class, SQLType.STRING, "STRING"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList("string", getName()); + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + return column.getDefaultValue() == null ? "NULL" : "'" + column.getDefaultValue() + "'"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/TimeEventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/TimeEventMeshDataType.java new file mode 100644 index 0000000000..876753d8cf --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/TimeEventMeshDataType.java @@ -0,0 +1,49 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.text.SimpleDateFormat; +import java.time.LocalTime; +import java.util.Arrays; +import java.util.List; + +public class TimeEventMeshDataType extends AbstractType { + + public static final TimeEventMeshDataType INSTANCE = new TimeEventMeshDataType(); + + private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + private TimeEventMeshDataType() { + super(LocalTime.class, SQLType.TIME, "TIME"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "time"); + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + return column.getDefaultValue() == null ? "NULL" : "'" + column.getDefaultValue() + "'"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/YearEventMeshDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/YearEventMeshDataType.java new file mode 100644 index 0000000000..eac7fabf88 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/eventmesh/YearEventMeshDataType.java @@ -0,0 +1,38 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.eventmesh; + +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.List; + +public class YearEventMeshDataType extends AbstractType { + + public static final YearEventMeshDataType INSTANCE = new YearEventMeshDataType(); + + private YearEventMeshDataType() { + super(Integer.class, SQLType.INTEGER, "YEAR"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "year", "Year"); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/BitType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/BitType.java new file mode 100644 index 0000000000..ebe81152ae --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/BitType.java @@ -0,0 +1,60 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import java.util.Optional; + +public class BitType extends AbstractType { + + public static final BitType INSTANCE = new BitType(); + + public BitType() { + super(byte[].class, SQLType.BIT, "BIT"); + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + return column.getDefaultValue() == null ? " NULL " : String.format("b'%s'", column.getDefaultValue()); + } + + @Override + public String getTypeName(Column column) { + // https://dev.mysql.com/doc/refman/8.0/en/bit-type.html + Long columnLength = column.getColumnLength(); + return String.format("bit(%d)", Optional.ofNullable(columnLength).orElse(1L).intValue()); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList("BIT", "bit"); + } + + @Override + public Object convert2DatabaseTypeValue(Object value) { + String strValue = (String) value; + return Base64.getDecoder().decode(strValue); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/BooleanType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/BooleanType.java new file mode 100644 index 0000000000..90dec2de0a --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/BooleanType.java @@ -0,0 +1,60 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.List; + +public class BooleanType extends AbstractType { + + public static final BooleanType INSTANCE = new BooleanType(); + + public BooleanType() { + super(Boolean.class, SQLType.BOOLEAN, "BOOLEAN"); + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + return column.getDefaultValue() == null ? " NULL " : String.format("b'%s'", column.getDefaultValue()); + } + + @Override + public String getTypeName(Column column) { + return "bit(1)"; + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "bool", "boolean"); + } + + @Override + public Object convert2DatabaseTypeValue(Object value) { + if (value instanceof String) { + return StringUtils.equalsIgnoreCase("true", (String) value) ? 1 : 0; + } + return Long.parseLong(value.toString()) > 0 ? 1 : 0; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/BytesType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/BytesType.java new file mode 100644 index 0000000000..554e5aa6b3 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/BytesType.java @@ -0,0 +1,69 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.BytesEventMeshDataType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class BytesType extends BytesEventMeshDataType { + + public static final BytesType INSTANCE = new BytesType(); + + private static final List BINARY_REGISTRATION_KEYS = Arrays.asList("BINARY", "binary", "VARBINARY", "varbinary"); + + private static final List BLOB_REGISTRATION_KEYS = Arrays.asList("TINYBLOB", "tinyblob", "BLOB", "blob", "MEDIUMBLOB", "mediumblob", + "LONGBLOB", "longblob"); + + @Override + public List ofRegistrationKeys() { + List registrationCodes = new ArrayList<>(); + registrationCodes.addAll(super.ofRegistrationKeys()); + registrationCodes.addAll(BINARY_REGISTRATION_KEYS); + registrationCodes.addAll(BLOB_REGISTRATION_KEYS); + return registrationCodes; + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + // https://dev.mysql.com/doc/refman/8.0/en/blob.html + // BLOB and TEXT columns cannot have DEFAULT values + if (BLOB_REGISTRATION_KEYS.contains(column.getNativeType())) { + return ""; + } + // binary use hex string,e.g: 0x01020304 + return column.getDefaultValue() != null ? "0x" + column.getDefaultValue() : "NULL"; + } + + @Override + public String getTypeName(Column column) { + if (BLOB_REGISTRATION_KEYS.contains(column.getNativeType())) { + return column.getNativeType(); + } + if (BINARY_REGISTRATION_KEYS.contains(column.getNativeType())) { + final int lengthValue = Optional.ofNullable(column.getColumnLength()).orElse(1L).intValue(); + return String.format("%s(%d)", column.getNativeType(), lengthValue); + } + return "binary(1)"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/DecimalType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/DecimalType.java new file mode 100644 index 0000000000..14f75d3130 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/DecimalType.java @@ -0,0 +1,59 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class DecimalType extends NumberType { + + public static final DecimalType INSTANCE = new DecimalType(); + + public DecimalType() { + super(Double.class, SQLType.DOUBLE, "DECIMAL"); + } + + @Override + public String getTypeName(Column column) { + // DECIMAL[(M[,D])] [UNSIGNED] [ZEROFILL] + // A packed “exact” fixed-point number. M is the total number of digits (the precision) and D is the number of digits after the decimal point + // (the scale). The decimal point and (for negative numbers) the - sign are not counted in M. If D is 0, values have no decimal point or + // fractional part. The maximum number of digits (M) for DECIMAL is 65. + // The maximum number of supported decimals (D) is 30. If D is omitted, the default is 0. If M is omitted, the default is 10. + StringBuilder typeNameBuilder = new StringBuilder(); + Long length = Optional.ofNullable(column.getColumnLength()).orElse(10L); + if (column.getDecimal() == null) { + typeNameBuilder.append("DECIMAL(" + length + ")"); + } else { + String typeName = hibernateDialect.getTypeName(column.getJdbcType().getVendorTypeNumber(), length, length.intValue(), + Optional.ofNullable(column.getDecimal()).orElse(0)); + typeNameBuilder.append(typeName); + } + typeNameBuilder.append(convertOptions2Sql(column)); + return typeNameBuilder.toString(); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList("decimal", getName()); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/EnumType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/EnumType.java new file mode 100644 index 0000000000..4aa81fbb00 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/EnumType.java @@ -0,0 +1,64 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import org.apache.commons.collections4.CollectionUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class EnumType extends AbstractType { + + public static final EnumType INSTANCE = new EnumType(); + + private EnumType() { + super(byte[].class, SQLType.BIT, "ENUM"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList("enum", "ENUM"); + } + + @Override + public String getTypeName(Column column) { + // https://dev.mysql.com/doc/refman/8.0/en/enum.html + List enumValues = column.getEnumValues(); + if (CollectionUtils.isNotEmpty(enumValues)) { + return "ENUM(" + enumValues.stream().map(val -> "'" + val + "'").collect(Collectors.joining(", ")) + ")"; + } + return "ENUM()"; + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + if (column.getDefaultValue() == null) { + return "NULL"; + } + return "'" + column.getDefaultValue() + "'"; + } +} \ No newline at end of file diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/GeometryCollectionType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/GeometryCollectionType.java new file mode 100644 index 0000000000..58ccb8630c --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/GeometryCollectionType.java @@ -0,0 +1,48 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; + +import java.util.Arrays; +import java.util.List; + +public class GeometryCollectionType extends SpatialDataType { + + public static final GeometryCollectionType INSTANCE = new GeometryCollectionType(); + + public GeometryCollectionType() { + super("GEOMETRYCOLLECTION"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "geometrycollection"); + } + + @Override + public String getTypeName(Column column) { + return "geometrycollection"; + } + + @Override + public String getQueryBindingWithValue(DatabaseDialect databaseDialect, Column column) { + return "ST_GeomCollFromWKB(?,?)"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/GeometryType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/GeometryType.java new file mode 100644 index 0000000000..449b1adf94 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/GeometryType.java @@ -0,0 +1,52 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; + +import java.util.Arrays; +import java.util.List; + +public class GeometryType extends SpatialDataType { + + public static final GeometryType INSTANCE = new GeometryType(); + + public GeometryType() { + super("GEOMETRY"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "geometry"); + } + + @Override + public String getTypeName(Column column) { + return "geometry"; + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + return ""; + } + + public String getQueryBindingWithValue(DatabaseDialect databaseDialect, Column column) { + return "ST_GeomFromWKB(?,?)"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/IntType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/IntType.java new file mode 100644 index 0000000000..d14cd75ba0 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/IntType.java @@ -0,0 +1,49 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class IntType extends NumberType { + + public static final IntType INSTANCE = new IntType(); + + public IntType() { + super(Integer.class, SQLType.INTEGER, "INT"); + } + + @Override + public String getTypeName(Column column) { + Long length = Optional.ofNullable(column.getColumnLength()).orElse(0L); + String typeName = hibernateDialect.getTypeName(column.getJdbcType().getVendorTypeNumber(), length, length.intValue(), + Optional.ofNullable(column.getDecimal()).orElse(0)); + StringBuilder typeNameBuilder = new StringBuilder(length > 0 ? typeName + "(" + length + ")" : typeName); + typeNameBuilder.append(convertOptions2Sql(column)); + return typeNameBuilder.toString(); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList("INT32", getName(), "int"); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/JsonType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/JsonType.java new file mode 100644 index 0000000000..e230a06741 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/JsonType.java @@ -0,0 +1,50 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.List; + +public class JsonType extends AbstractType { + + public static final JsonType INSTANCE = new JsonType(); + + public JsonType() { + super(String.class, SQLType.STRING, "JSON"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList("json", getName(), "Json"); + } + + @Override + public String getTypeName(Column column) { + return "json"; + } + + @Override + public String getQueryBindingWithValue(DatabaseDialect databaseDialect, Column column) { + return "CAST(? AS JSON)"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/LineStringType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/LineStringType.java new file mode 100644 index 0000000000..1c610fd449 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/LineStringType.java @@ -0,0 +1,48 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; + +import java.util.Arrays; +import java.util.List; + +public class LineStringType extends SpatialDataType { + + public static final LineStringType INSTANCE = new LineStringType(); + + public LineStringType() { + super("LINESTRING"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "linestring"); + } + + @Override + public String getTypeName(Column column) { + return "linestring"; + } + + @Override + public String getQueryBindingWithValue(DatabaseDialect databaseDialect, Column column) { + return "ST_LineFromWKB(?,?)"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/MediumintType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/MediumintType.java new file mode 100644 index 0000000000..0c51082f04 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/MediumintType.java @@ -0,0 +1,45 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; + +import java.util.Arrays; +import java.util.List; + +public class MediumintType extends NumberType { + + public static final MediumintType INSTANCE = new MediumintType(); + + public MediumintType() { + super(Integer.class, SQLType.INTEGER, "MEDIUMINT"); + } + + @Override + public String getTypeName(Column column) { + StringBuilder typeNameBuilder = new StringBuilder(" mediumint "); + typeNameBuilder.append(convertOptions2Sql(column)); + return typeNameBuilder.toString(); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList("mediumint", getName()); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/MultiLineStringType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/MultiLineStringType.java new file mode 100644 index 0000000000..00b7a2f6c4 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/MultiLineStringType.java @@ -0,0 +1,48 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; + +import java.util.Arrays; +import java.util.List; + +public class MultiLineStringType extends SpatialDataType { + + public static final MultiLineStringType INSTANCE = new MultiLineStringType(); + + public MultiLineStringType() { + super("MULTILINESTRING"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "multilinestring"); + } + + @Override + public String getTypeName(Column column) { + return "multilinestring"; + } + + @Override + public String getQueryBindingWithValue(DatabaseDialect databaseDialect, Column column) { + return "ST_MLineFromWKB(?,?)"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/MultiPointType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/MultiPointType.java new file mode 100644 index 0000000000..1b9c3c87c5 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/MultiPointType.java @@ -0,0 +1,48 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; + +import java.util.Arrays; +import java.util.List; + +public class MultiPointType extends SpatialDataType { + + public static final MultiPointType INSTANCE = new MultiPointType(); + + public MultiPointType() { + super("MULTIPOINT"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "multipoint"); + } + + @Override + public String getTypeName(Column column) { + return "multipoint"; + } + + @Override + public String getQueryBindingWithValue(DatabaseDialect databaseDialect, Column column) { + return "ST_MPointFromWKB(?,?)"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/MultiPolygonType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/MultiPolygonType.java new file mode 100644 index 0000000000..c35d167a03 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/MultiPolygonType.java @@ -0,0 +1,48 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; + +import java.util.Arrays; +import java.util.List; + +public class MultiPolygonType extends SpatialDataType { + + public static final MultiPolygonType INSTANCE = new MultiPolygonType(); + + public MultiPolygonType() { + super("MULTIPOLYGON"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "multipolygon"); + } + + @Override + public String getTypeName(Column column) { + return "multipolygon"; + } + + @Override + public String getQueryBindingWithValue(DatabaseDialect databaseDialect, Column column) { + return "ST_MPolyFromWKB(?,?)"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/NumberType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/NumberType.java new file mode 100644 index 0000000000..84c1dec4eb --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/NumberType.java @@ -0,0 +1,57 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.catalog.mysql.MysqlOptions.MysqlColumnOptions; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import org.apache.commons.collections4.MapUtils; + +import java.util.Optional; + +public abstract class NumberType extends AbstractType { + + public NumberType(Class typeClass, SQLType sqlType, String name) { + super(typeClass, sqlType, name); + } + + @Override + public String getTypeName(Column column) { + Long length = Optional.ofNullable(column.getColumnLength()).orElse(0L); + String typeName = hibernateDialect.getTypeName(column.getJdbcType().getVendorTypeNumber(), length, length.intValue(), + Optional.ofNullable(column.getDecimal()).orElse(0)); + return typeName; + } + + protected String convertOptions2Sql(Column column) { + StringBuilder builder = new StringBuilder(); + if (MapUtils.isNotEmpty(column.getOptions())) { + String unsigned = (String) column.getOptions().get(MysqlColumnOptions.UNSIGNED); + if (unsigned != null) { + builder.append(" ").append(unsigned); + } + String zerofill = (String) column.getOptions().get(MysqlColumnOptions.ZEROFILL); + if (zerofill != null) { + builder.append(" ").append(zerofill); + } + } + return builder.toString(); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/EvetMeshRowType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/PointType.java similarity index 55% rename from eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/EvetMeshRowType.java rename to eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/PointType.java index ece432c28f..839df9de82 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/table/type/EvetMeshRowType.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/PointType.java @@ -15,35 +15,34 @@ * limitations under the License. */ -package org.apache.eventmesh.connector.jdbc.table.type; +package org.apache.eventmesh.connector.jdbc.type.mysql; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; + +import java.util.Arrays; import java.util.List; -public class EvetMeshRowType implements EventMeshDataType { +public class PointType extends SpatialDataType { + + public static final PointType INSTANCE = new PointType(); - private List>> fields; + public PointType() { + super("POINT"); + } - public EvetMeshRowType(List>> fields) { - this.fields = fields; + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "point"); } - /** - * Returns the type class of the data. - * - * @return the type class of the data. - */ @Override - public Class getTypeClass() { - return EventMeshRow.class; + public String getTypeName(Column column) { + return "point"; } - /** - * Returns the SQL type of the data. - * - * @return the SQL type of the data. - */ @Override - public SQLType getSQLType() { - return SQLType.ROW; + public String getQueryBindingWithValue(DatabaseDialect databaseDialect, Column column) { + return "ST_PointFromWKB(?,?)"; } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/PolygonType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/PolygonType.java new file mode 100644 index 0000000000..a26537580e --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/PolygonType.java @@ -0,0 +1,48 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; + +import java.util.Arrays; +import java.util.List; + +public class PolygonType extends SpatialDataType { + + public static final PolygonType INSTANCE = new PolygonType(); + + public PolygonType() { + super("POLYGON"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "polygon"); + } + + @Override + public String getTypeName(Column column) { + return "polygon"; + } + + @Override + public String getQueryBindingWithValue(DatabaseDialect databaseDialect, Column column) { + return "ST_PolyFromWKB(?,?)"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/SetType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/SetType.java new file mode 100644 index 0000000000..402045cdf6 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/SetType.java @@ -0,0 +1,61 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import org.apache.commons.collections4.CollectionUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class SetType extends AbstractType { + + public static final SetType INSTANCE = new SetType(); + + private SetType() { + super(String.class, SQLType.STRING, "SET"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "set"); + } + + @Override + public String getTypeName(Column column) { + // https://dev.mysql.com/doc/refman/8.0/en/set.html + List enumValues = column.getEnumValues(); + if (CollectionUtils.isNotEmpty(enumValues)) { + return "SET(" + enumValues.stream().map(val -> "'" + val + "'").collect(Collectors.joining(", ")) + ")"; + } + return "SET()"; + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + if (column.getDefaultValue() == null) { + return "NULL"; + } + return "'" + column.getDefaultValue() + "'"; + } +} \ No newline at end of file diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/SpatialDataType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/SpatialDataType.java new file mode 100644 index 0000000000..dac75ab453 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/SpatialDataType.java @@ -0,0 +1,81 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Base64; + +import org.hibernate.query.Query; + +/** + * The SpatialDataType class is an abstract class that extends the AbstractType class. It represents a spatial data type for storing byte arrays. + * + *

+ * The SpatialDataType class provides constructors for specifying the type class, SQL type, and name of the spatial data type. + *

+ * mysql gis-wkb-functions + */ +public abstract class SpatialDataType extends AbstractType { + + public SpatialDataType(String name) { + super(byte[].class, SQLType.BINARY, name); + } + + @Override + public int bindValue(int startIndex, Object value, Query query) { + + if (value == null) { + query.setParameter(startIndex, null); + return 1; + } + + //doc:https://dev.mysql.com/doc/refman/8.0/en/gis-wkb-functions.html + //Different data types have different methods. For example, + // the `Point` type uses a method like this: `ST_PointFromWKB(wkb [, srid [, options]])`. + //Special handling is required when binding data like this. + if (value instanceof byte[]) { + + ByteBuffer buf = ByteBuffer.wrap((byte[]) value); + buf.order(ByteOrder.LITTLE_ENDIAN); + // The first 4 bytes represent the SRID. + Integer srid = buf.getInt(); + // The remainder is the WKB (Well-Known Binary) data. + byte[] wkb = new byte[buf.remaining()]; + buf.get(wkb); + query.setParameter(startIndex, wkb); + query.setParameter(startIndex + 1, srid); + return 2; + } + + throw new RuntimeException(); + } + + @Override + public Object convert2DatabaseTypeValue(Object value) { + // Jackson default serialize byte[] as base64 + String strValue = (String) value; + if (strValue == null) { + return null; + } + return Base64.getDecoder().decode(strValue); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/TextType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/TextType.java new file mode 100644 index 0000000000..8611c1beb6 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/TextType.java @@ -0,0 +1,44 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.type.eventmesh.StringEventMeshDataType; + +import java.util.Arrays; +import java.util.List; + +public class TextType extends StringEventMeshDataType { + + public static final TextType INSTANCE = new TextType(); + + private static final List TEXT_REGISTRATION_KEYS = Arrays.asList("TEXT", "text", "MEDIUMTEXT", "mediumtext", "LONGTEXT", "longtext"); + + @Override + public List ofRegistrationKeys() { + return TEXT_REGISTRATION_KEYS; + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + // https://dev.mysql.com/doc/refman/8.0/en/blob.html + // BLOB and TEXT columns cannot have DEFAULT values + return ""; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/TinyIntType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/TinyIntType.java new file mode 100644 index 0000000000..1c0b597d10 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/TinyIntType.java @@ -0,0 +1,50 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public class TinyIntType extends AbstractType { + + public static final TinyIntType INSTANCE = new TinyIntType(); + + public TinyIntType() { + super(Byte.class, SQLType.TINYINT, "TINYINT"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "tinyint"); + } + + @Override + public String getTypeName(Column column) { + + final int size = Optional.ofNullable(column.getColumnLength()).orElse(0L).intValue(); + if (size > 0) { + return String.format("tinyint(%d)", size); + } + return "tinyint"; + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/YearType.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/YearType.java new file mode 100644 index 0000000000..2b5ef3452d --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/type/mysql/YearType.java @@ -0,0 +1,80 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.type.mysql; + +import org.apache.eventmesh.connector.jdbc.JdbcDriverMetaData; +import org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialect; +import org.apache.eventmesh.connector.jdbc.table.catalog.Column; +import org.apache.eventmesh.connector.jdbc.table.type.SQLType; +import org.apache.eventmesh.connector.jdbc.type.AbstractType; +import org.apache.eventmesh.connector.jdbc.utils.JdbcStringUtils; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; + +public class YearType extends AbstractType { + + public static final YearType INSTANCE = new YearType(); + + private YearType() { + super(Integer.class, SQLType.INTEGER, "YEAR"); + } + + @Override + public List ofRegistrationKeys() { + return Arrays.asList(getName(), "year", "Year"); + } + + @Override + public String getTypeName(Column column) { + JdbcDriverMetaData jdbcDriverMetaData = eventMeshDialect.getJdbcDriverMetaData(); + + // As of MySQL 8.0.19, the YEAR(4) data type with an explicit display width is deprecated; you should expect support for it to be removed in a + // future version of MySQL. + // Instead, use YEAR without a display width, which has the same meaning + if (JdbcStringUtils.compareVersion(jdbcDriverMetaData.getDatabaseProductVersion(), "8.0.19") >= 0) { + return "year"; + } + // MySQL 8.0 does not support the 2-digit YEAR(2) data type permitted in older versions of MySQL. For instructions on converting to 4-digit + // YEAR + if (column.getColumnLength() != null && column.getColumnLength() <= 2 + && JdbcStringUtils.compareVersion(jdbcDriverMetaData.getDatabaseProductVersion(), "8.0") >= 0) { + return "year(4)"; + } + return column.getColumnLength() == null ? "year" : "year(" + column.getColumnLength() + ")"; + } + + @Override + public String getDefaultValue(DatabaseDialect databaseDialect, Column column) { + + final Object defaultValue = column.getDefaultValue(); + if (defaultValue == null) { + return "NULL"; + } + return "'" + LocalDate.parse(defaultValue.toString()).getYear() + "'"; + } + + @Override + public Object convert2DatabaseTypeValue(Object value) { + if (value == null) { + return null; + } + return LocalDate.parse(value.toString()).getYear(); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/utils/ByteArrayUtils.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/utils/ByteArrayUtils.java new file mode 100644 index 0000000000..95f5d7a5e3 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/utils/ByteArrayUtils.java @@ -0,0 +1,78 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.utils; + +public class ByteArrayUtils { + + private static final char[] HEX_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * Converts a byte array into a hexadecimal string. + * + * @param bytes the byte array to be converted + * @return the hexadecimal string representation of the byte array + * @throws NullPointerException if the byte array is null + */ + public static String bytesToHexString(byte[] bytes) { + if (bytes == null) { + throw new NullPointerException("Parameter to be converted can not be null"); + } + + char[] converted = new char[bytes.length * 2]; + for (int i = 0; i < bytes.length; i++) { + byte b = bytes[i]; + converted[i * 2] = HEX_CHARS[b >> 4 & 0x0F]; + converted[i * 2 + 1] = HEX_CHARS[b & 0x0F]; + } + + return String.valueOf(converted); + } + + /** + * This method converts a hexadecimal string into an array of bytes. + * + * @param str the hexadecimal string to be converted + * @return the resulting byte array + * @throws IllegalArgumentException if the supplied character array contains an odd number of hex characters + */ + public static byte[] hexStringToBytes(String str) { + final char[] chars = str.toCharArray(); + if (chars.length % 2 != 0) { + throw new IllegalArgumentException("The supplied character array must contain an even number of hex chars."); + } + + byte[] response = new byte[chars.length / 2]; + + for (int i = 0; i < response.length; i++) { + int posOne = i * 2; + response[i] = (byte) (toByte(chars, posOne) << 4 | toByte(chars, posOne + 1)); + } + + return response; + } + + private static byte toByte(final char[] chars, final int pos) { + int response = Character.digit(chars[pos], 16); + if (response < 0 || response > 15) { + throw new IllegalArgumentException("Non-hex character '" + chars[pos] + "' at index=" + pos); + } + + return (byte) response; + } + +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/utils/JdbcStringUtils.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/utils/JdbcStringUtils.java index 98fb897a6c..49f372214e 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/utils/JdbcStringUtils.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/utils/JdbcStringUtils.java @@ -22,20 +22,21 @@ @UtilityClass public class JdbcStringUtils { + /** + * Checks whether the given string is wrapped with a specific set of characters. + * + * @param possiblyWrapped the string to check + * @return true if the string is wrapped with characters '`', "'", or "\""; false otherwise + */ public static boolean isWrapped(String possiblyWrapped) { - if (possiblyWrapped.length() < 2) { + if (possiblyWrapped == null || possiblyWrapped.length() < 2) { return false; } - if (possiblyWrapped.startsWith("`") && possiblyWrapped.endsWith("`")) { - return true; - } - if (possiblyWrapped.startsWith("'") && possiblyWrapped.endsWith("'")) { - return true; - } - if (possiblyWrapped.startsWith("\"") && possiblyWrapped.endsWith("\"")) { - return true; - } - return false; + char firstChar = possiblyWrapped.charAt(0); + char lastChar = possiblyWrapped.charAt(possiblyWrapped.length() - 1); + return (firstChar == '`' && lastChar == '`') + || (firstChar == '\'' && lastChar == '\'') + || (firstChar == '\"' && lastChar == '\"'); } public static boolean isWrapped(char c) { @@ -46,4 +47,29 @@ public static String withoutWrapper(String possiblyWrapped) { return isWrapped(possiblyWrapped) ? possiblyWrapped.substring(1, possiblyWrapped.length() - 1) : possiblyWrapped; } + /** + * Compares two version numbers and returns the result as an integer. + * + * @param versionX The first version number to compare. + * @param versionY The second version number to compare. + * @return An integer value representing the comparison result: -1 if versionX is less than versionY, 0 if versionX is equal to versionY, 1 if + * versionX is greater than versionY. + */ + public static int compareVersion(String versionX, String versionY) { + String[] firstVersionParts = versionX.split("\\."); + String[] secondVersionParts = versionY.split("\\."); + int maxLength = Math.max(firstVersionParts.length, secondVersionParts.length); + for (int i = 0; i < maxLength; i++) { + int firstVersionNumber = getPartAsNumber(firstVersionParts, i); + int secondVersionNumber = getPartAsNumber(secondVersionParts, i); + if (firstVersionNumber != secondVersionNumber) { + return Integer.signum(firstVersionNumber - secondVersionNumber); + } + } + return 0; + } + + private static int getPartAsNumber(String[] parts, int index) { + return index < parts.length ? Integer.parseInt(parts[index]) : 0; + } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/utils/MysqlUtils.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/utils/MysqlUtils.java index c272b1b879..70cbb50705 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/utils/MysqlUtils.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/java/org/apache/eventmesh/connector/jdbc/utils/MysqlUtils.java @@ -21,14 +21,29 @@ public class MysqlUtils { + /** + * Private constructor to prevent instantiation from outside the class. + */ private MysqlUtils() { } + /** + * Generates a wrapped name based on the TableId object. + * + * @param tableId The TableId object. + * @return The generated wrapped name. + */ public static String wrapper(TableId tableId) { - return wrapper(tableId.toString()); + return wrapper(tableId.getCatalogName()) + "." + wrapper(tableId.getTableName()); } + /** + * Generates a wrapped name based on the original string. + * + * @param original The original string. + * @return The generated wrapped name. + */ public static String wrapper(String original) { return "`" + original + "`"; } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.connector.jdbc.source.dialect.DatabaseDialectFactory b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialectFactory similarity index 89% rename from eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.connector.jdbc.source.dialect.DatabaseDialectFactory rename to eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialectFactory index 8524b42ac8..834b0bcf23 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.connector.jdbc.source.dialect.DatabaseDialectFactory +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.connector.jdbc.dialect.DatabaseDialectFactory @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -mysql=org.apache.eventmesh.connector.jdbc.source.dialect.mysql.MysqlDatabaseDialectFactory +mysql=org.apache.eventmesh.connector.jdbc.dialect.mysql.MysqlDatabaseDialectFactory diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/server-config.yml b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/server-config.yml index 5f66dd0f68..d9416e1ed2 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/server-config.yml +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/server-config.yml @@ -15,5 +15,5 @@ # limitations under the License. # -sourceEnable: true +sourceEnable: false sinkEnable: true diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/sink-config.yml b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/sink-config.yml new file mode 100644 index 0000000000..7896a08751 --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/sink-config.yml @@ -0,0 +1,34 @@ +# +# 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. +# + +pubSubConfig: + meshAddress: 127.0.0.1:10001 + subject: TopicTest + idc: FT + env: PRD + group: rocketmqSource + appId: 5032 + userName: jdbcSourceUser + passWord: jdbcPassWord +sinkConnectorConfig: + connectorName: "EventMesh-sink" + jdbcConfig: + hostname: localhost + port: 3406 + user: root + password: xxxx + url: "jdbc:mysql://localhost:3406" diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/source-config.yml b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/source-config.yml index 0ec393819a..6e966029ad 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/source-config.yml +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/main/resources/source-config.yml @@ -41,7 +41,7 @@ sourceConnectorConfig: hostname: localhost port: 3306 user: root - password: xxxxx + password: xxxx initialStatements: connectTimeout: 10 mysqlConfig: @@ -56,4 +56,4 @@ offsetStorageConfig: dataId: TopicTest, #same with group group: rocketmqSource - } \ No newline at end of file + } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/test/java/org/apache/eventmesh/connector/jdbc/utils/ByteArrayUtilsTest.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/test/java/org/apache/eventmesh/connector/jdbc/utils/ByteArrayUtilsTest.java new file mode 100644 index 0000000000..51d30726aa --- /dev/null +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/test/java/org/apache/eventmesh/connector/jdbc/utils/ByteArrayUtilsTest.java @@ -0,0 +1,63 @@ +/* + * 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 org.apache.eventmesh.connector.jdbc.utils; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +public class ByteArrayUtilsTest { + + @Test + public void testBytesToHexString() { + byte[] bytes = {(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE}; + String hexString = ByteArrayUtils.bytesToHexString(bytes); + assertEquals("cafebabe", hexString); + } + + @Test + public void testHexStringToBytes() { + String hexString = "cafebabe"; + byte[] bytes = ByteArrayUtils.hexStringToBytes(hexString); + assertArrayEquals(new byte[]{(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE}, bytes); + } + + @Test + public void testBytesToHexStringAndBack() { + byte[] originalBytes = {(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE}; + String hexString = ByteArrayUtils.bytesToHexString(originalBytes); + byte[] convertedBytes = ByteArrayUtils.hexStringToBytes(hexString); + assertArrayEquals(originalBytes, convertedBytes); + } + + @Test + public void testHexStringToBytesWithOddLength() { + assertThrows(IllegalArgumentException.class, () -> { + ByteArrayUtils.hexStringToBytes("cafebabe1"); // Odd-length hex string + }); + } + + @Test + public void testHexStringToBytesWithInvalidCharacter() { + assertThrows(IllegalArgumentException.class, () -> { + ByteArrayUtils.hexStringToBytes("cafebabeG"); // Invalid hex character + }); + } +} diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/test/java/org/apache/eventmesh/connector/jdbc/utils/JdbcStringUtilsTest.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/test/java/org/apache/eventmesh/connector/jdbc/utils/JdbcStringUtilsTest.java index 0b2e5121fa..5a97c21b10 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/test/java/org/apache/eventmesh/connector/jdbc/utils/JdbcStringUtilsTest.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/test/java/org/apache/eventmesh/connector/jdbc/utils/JdbcStringUtilsTest.java @@ -52,4 +52,44 @@ public void testIsWrappedWithChar() { assertTrue(JdbcStringUtils.isWrapped('\"')); assertFalse(JdbcStringUtils.isWrapped('A')); } + + @Test + public void testCompareVersion() { + // Test case 1: versionX is less than versionY + String versionX1 = "1.0.0"; + String versionY1 = "1.1.0"; + int expected1 = -1; + int result1 = JdbcStringUtils.compareVersion(versionX1, versionY1); + assertEquals(expected1, result1); + + // Test case 2: versionX is equal to versionY + String versionX2 = "1.2.3"; + String versionY2 = "1.2.3"; + int expected2 = 0; + int result2 = JdbcStringUtils.compareVersion(versionX2, versionY2); + assertEquals(expected2, result2); + + // Test case 3: versionX is greater than versionY + String versionX3 = "2.0.0"; + String versionY3 = "1.2.3"; + int expected3 = 1; + int result3 = JdbcStringUtils.compareVersion(versionX3, versionY3); + assertEquals(expected3, result3); + + assertEquals(0, JdbcStringUtils.compareVersion("1.0", "1.0")); + assertEquals(1, JdbcStringUtils.compareVersion("1.1", "1.0")); + assertEquals(-1, JdbcStringUtils.compareVersion("1.0", "1.1")); + assertEquals(1, JdbcStringUtils.compareVersion("1.10", "1.9")); + assertEquals(-1, JdbcStringUtils.compareVersion("1.9", "1.10")); + assertEquals(0, JdbcStringUtils.compareVersion("1.0.0", "1")); + assertEquals(0, JdbcStringUtils.compareVersion("1.0.0.0", "1.0")); + assertEquals(0, JdbcStringUtils.compareVersion("1.0.0.0", "1")); + assertEquals(-1, JdbcStringUtils.compareVersion("1.0.0.0", "1.1")); + assertEquals(1, JdbcStringUtils.compareVersion("1.1", "1.0.0.0")); + try { + assertEquals(0, JdbcStringUtils.compareVersion("1.1", "1.a")); + } catch (Exception e) { + assertTrue(e instanceof NumberFormatException); + } + } } diff --git a/eventmesh-connectors/eventmesh-connector-jdbc/src/test/java/org/apache/eventmesh/connector/jdbc/utils/MysqlUtilsTest.java b/eventmesh-connectors/eventmesh-connector-jdbc/src/test/java/org/apache/eventmesh/connector/jdbc/utils/MysqlUtilsTest.java index c3970f39fe..a84afd8570 100644 --- a/eventmesh-connectors/eventmesh-connector-jdbc/src/test/java/org/apache/eventmesh/connector/jdbc/utils/MysqlUtilsTest.java +++ b/eventmesh-connectors/eventmesh-connector-jdbc/src/test/java/org/apache/eventmesh/connector/jdbc/utils/MysqlUtilsTest.java @@ -30,11 +30,12 @@ public class MysqlUtilsTest { @Test public void testWrapperWithTableId() { TableId mockTableId = mock(TableId.class); - when(mockTableId.toString()).thenReturn("test_table"); + when(mockTableId.getTableName()).thenReturn("test_table"); + when(mockTableId.getCatalogName()).thenReturn("test_table"); String wrapped = MysqlUtils.wrapper(mockTableId); - assertEquals("`test_table`", wrapped); + assertEquals("`test_table`.`test_table`", wrapped); } @Test diff --git a/tools/dependency-check/known-dependencies.txt b/tools/dependency-check/known-dependencies.txt index 408c5c6208..ccf34353e3 100644 --- a/tools/dependency-check/known-dependencies.txt +++ b/tools/dependency-check/known-dependencies.txt @@ -1,3 +1,4 @@ +FastInfoset-1.2.15.jar ST4-4.3.4.jar accessors-smart-2.4.7.jar alibabacloud-gateway-spi-0.0.1.jar @@ -5,6 +6,7 @@ amqp-client-5.16.0.jar animal-sniffer-annotations-1.19.jar annotations-2.20.29.jar annotations-4.1.1.4.jar +antlr-2.7.7.jar antlr-runtime-3.5.3.jar antlr4-4.13.0.jar antlr4-runtime-4.13.0.jar @@ -38,6 +40,7 @@ bouncy-castle-bc-2.10.1-pkg.jar bouncy-castle-bc-2.11.1-pkg.jar bson-3.12.11.jar byte-buddy-1.11.0.jar +byte-buddy-1.12.18.jar cache-api-1.1.1.jar checker-qual-3.12.0.jar classmate-1.5.1.jar @@ -69,6 +72,7 @@ dingtalk-2.0.61.jar disruptor-3.4.2.jar dledger-0.3.1.2.jar dom4j-2.0.3.jar +druid-1.2.20.jar endpoint-util-0.0.7.jar endpoints-spi-2.20.29.jar error_prone_annotations-2.9.0.jar @@ -93,6 +97,8 @@ gson-2.8.2.jar guava-31.0.1-jre.jar guava-retrying-2.0.0.jar guice-4.2.2.jar +hibernate-commons-annotations-5.1.2.Final.jar +hibernate-core-5.6.15.Final.jar hibernate-validator-6.2.0.Final.jar http-client-spi-2.20.29.jar httpasyncclient-4.1.3.jar @@ -103,6 +109,7 @@ httpmime-4.5.13.jar icu4j-72.1.jar ini4j-0.5.4.jar ipaddress-5.3.3.jar +istack-commons-runtime-3.0.7.jar j2objc-annotations-1.3.jar jackson-annotations-2.13.0.jar jackson-core-2.13.0.jar @@ -111,18 +118,25 @@ jackson-dataformat-yaml-2.13.0.jar jackson-datatype-jsr310-2.13.0.jar jakarta.annotation-api-1.3.5.jar jakarta.validation-api-2.0.2.jar +jandex-2.4.2.Final.jar javassist-3.24.0-GA.jar javax.activation-1.2.0.jar +javax.activation-api-1.2.0.jar javax.annotation-api-1.3.2.jar javax.inject-1.jar +javax.persistence-api-2.2.jar javax.ws.rs-api-2.1.jar jaxb-api-2.3.0.jar +jaxb-api-2.3.1.jar jaxb-core-2.3.0.jar jaxb-impl-2.3.0.jar +jaxb-runtime-2.3.1.jar jaxen-1.1.6.jar jboss-logging-3.4.1.Final.jar +jboss-logging-3.4.3.Final.jar jboss-marshalling-2.0.11.Final.jar jboss-marshalling-river-2.0.11.Final.jar +jboss-transaction-api_1.2_spec-1.1.1.Final.jar jcip-annotations-1.0.jar jcommander-1.78.jar jcommander-1.82.jar @@ -144,8 +158,8 @@ kafka-clients-3.0.0.jar listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar log4j-api-2.22.1.jar log4j-core-2.22.1.jar -log4j-slf4j2-impl-2.22.1.jar log4j-slf4j-impl-2.22.1.jar +log4j-slf4j2-impl-2.22.1.jar logback-classic-1.2.10.jar logback-core-1.2.10.jar lz4-java-1.7.1.jar @@ -318,12 +332,14 @@ spring-expression-5.3.15.jar spring-jcl-5.3.20.jar spring-messaging-5.3.20.jar stax-api-1.0-2.jar +stax-ex-1.8.jar tea-1.2.7.jar tea-openapi-0.2.8.jar tea-util-0.2.21.jar tea-xml-0.1.5.jar third-party-jackson-core-2.20.29.jar tomcat-embed-el-9.0.56.jar +txw2-2.3.1.jar utils-2.20.29.jar validation-api-1.1.0.Final.jar vertx-auth-common-4.4.6.jar @@ -340,4 +356,4 @@ zipkin-sender-okhttp3-2.16.3.jar zookeeper-3.7.1.jar zookeeper-jute-3.7.1.jar zstd-jni-1.5.0-2.jar -zstd-jni-1.5.2-2.jar \ No newline at end of file +zstd-jni-1.5.2-2.jar