This is an automated email from the ASF dual-hosted git repository.
shuwenwei pushed a commit to branch flink-iotdb-table-connector
in repository https://gitbox.apache.org/repos/asf/iotdb-extras.git
The following commit(s) were added to refs/heads/flink-iotdb-table-connector by
this push:
new 991ac3d feat: support configurable timestamp precision
991ac3d is described below
commit 991ac3d7e1ec29f193b257419d4ec7713e38ca3c
Author: shuwenwei <[email protected]>
AuthorDate: Thu Oct 8 11:36:02 2026 +0800
feat: support configurable timestamp precision
Add iotdb.timestamp-precision (ms/us/ns) and convert timestamps on write,
CDC read, catalog mapping and pushed literals; add iotdb.cdc.end-time with
start/end-time validation; drop iotdb.cdc.topic and iotdb.cdc.consumer-group in
favor of derived names.
---
.../iotdb/relational/flink/cfg/IoTDBOptions.java | 74 +++++++------
.../flink/sink/RowDataSinkDataConverter.java | 38 +++++--
.../relational/flink/sink/SinkDataConverter.java | 8 +-
.../flink/source/cdc/SubscriptionDataIterator.java | 9 +-
.../source/cdc/client/IoTDBSubscriptionClient.java | 58 +++++++++-
.../flink/source/lookup/IoTDBLookupReader.java | 4 +-
.../scan/pushdown/IoTDBExpressionVisitor.java | 9 +-
.../iotdb/relational/flink/utils/IoTDBUtils.java | 50 +++++----
.../relational/flink/utils/TimestampPrecision.java | 120 +++++++++++++++++++++
.../flink/sink/RowDataSinkDataConverterTest.java | 15 ++-
.../flink/source/lookup/IoTDBLookupReaderTest.java | 2 +-
.../relational/flink/catalog/IoTDBCatalog.java | 3 +-
.../flink/catalog/IoTDBCatalogFactory.java | 4 +-
.../relational/flink/sink/IoTDBSinkWriter.java | 11 +-
.../source/cdc/IoTDBSubscriptionSourceReader.java | 5 +-
.../table/IoTDBRelationalDynamicTableFactory.java | 8 +-
.../table/IoTDBRelationalDynamicTableSink.java | 3 +-
.../table/IoTDBRelationalDynamicTableSource.java | 9 +-
18 files changed, 345 insertions(+), 85 deletions(-)
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/cfg/IoTDBOptions.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/cfg/IoTDBOptions.java
index 1463679..75ae43c 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/cfg/IoTDBOptions.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/cfg/IoTDBOptions.java
@@ -19,6 +19,8 @@
package org.apache.iotdb.relational.flink.cfg;
+import org.apache.iotdb.relational.flink.utils.TimestampPrecision;
+
import org.apache.flink.configuration.ConfigOption;
import org.apache.flink.configuration.ConfigOptions;
@@ -76,24 +78,26 @@ public class IoTDBOptions implements Serializable {
.enumType(ScanMode.class)
.defaultValue(ScanMode.SNAPSHOT);
- public static final ConfigOption<String> CDC_TOPIC =
- ConfigOptions.key("iotdb.cdc.topic").stringType().noDefaultValue();
-
- public static final ConfigOption<String> CDC_CONSUMER_GROUP =
-
ConfigOptions.key("iotdb.cdc.consumer-group").stringType().noDefaultValue();
-
public static final ConfigOption<String> CDC_MODE =
ConfigOptions.key("iotdb.cdc.mode").stringType().defaultValue("live");
public static final ConfigOption<String> CDC_START_TIME =
ConfigOptions.key("iotdb.cdc.start-time").stringType().noDefaultValue();
+ public static final ConfigOption<String> CDC_END_TIME =
+ ConfigOptions.key("iotdb.cdc.end-time").stringType().noDefaultValue();
+
public static final ConfigOption<Long> CDC_POLL_TIMEOUT_MS =
ConfigOptions.key("iotdb.cdc.poll-timeout-ms").longType().defaultValue(1000L);
public static final ConfigOption<Boolean> CDC_AUTO_COMMIT =
ConfigOptions.key("iotdb.cdc.auto-commit").booleanType().defaultValue(true);
+ public static final ConfigOption<TimestampPrecision> TIMESTAMP_PRECISION =
+ ConfigOptions.key("iotdb.timestamp-precision")
+ .enumType(TimestampPrecision.class)
+ .defaultValue(TimestampPrecision.MS);
+
private static final String CDC_TOPIC_PREFIX = "flink_iotdb_table_";
/** Scan mode of the table source. */
@@ -114,12 +118,12 @@ public class IoTDBOptions implements Serializable {
private final boolean lookupAsync;
private final int lookupThreadSize;
private final ScanMode scanMode;
- private final String cdcTopic;
- private final String cdcConsumerGroup;
private final String cdcMode;
private final String cdcStartTime;
+ private final String cdcEndTime;
private final long cdcPollTimeoutMs;
private final boolean cdcAutoCommit;
+ private final TimestampPrecision timestampPrecision;
private IoTDBOptions(Builder builder) {
this.nodeUrls = builder.nodeUrls;
@@ -134,12 +138,12 @@ public class IoTDBOptions implements Serializable {
this.lookupAsync = builder.lookupAsync;
this.lookupThreadSize = builder.lookupThreadSize;
this.scanMode = builder.scanMode;
- this.cdcTopic = builder.cdcTopic;
- this.cdcConsumerGroup = builder.cdcConsumerGroup;
this.cdcMode = builder.cdcMode;
this.cdcStartTime = builder.cdcStartTime;
+ this.cdcEndTime = builder.cdcEndTime;
this.cdcPollTimeoutMs = builder.cdcPollTimeoutMs;
this.cdcAutoCommit = builder.cdcAutoCommit;
+ this.timestampPrecision = builder.timestampPrecision;
}
/**
@@ -205,6 +209,13 @@ public class IoTDBOptions implements Serializable {
return attributeColumns;
}
+ /**
+ * @return the timestamp precision of the target IoTDB server.
+ */
+ public TimestampPrecision getTimestampPrecision() {
+ return timestampPrecision;
+ }
+
/**
* @return whether an asynchronous lookup function is used for lookup joins.
*/
@@ -234,23 +245,17 @@ public class IoTDBOptions implements Serializable {
}
/**
- * @return the effective CDC topic name. When unset, it is derived from the
connector marker and
- * the database/table names so the topic origin is recognizable.
+ * @return the CDC topic name, derived from the connector marker and the
database/table names so
+ * the topic origin is recognizable.
*/
public String getCdcTopic() {
- if (cdcTopic != null && !cdcTopic.isEmpty()) {
- return cdcTopic;
- }
return CDC_TOPIC_PREFIX + sanitize(database) + "_" + sanitize(table);
}
/**
- * @return the effective CDC consumer group id. When unset, it is derived
from the topic name.
+ * @return the CDC consumer group id, derived from the topic name.
*/
public String getCdcConsumerGroup() {
- if (cdcConsumerGroup != null && !cdcConsumerGroup.isEmpty()) {
- return cdcConsumerGroup;
- }
return getCdcTopic() + "_group";
}
@@ -268,6 +273,13 @@ public class IoTDBOptions implements Serializable {
return cdcStartTime;
}
+ /**
+ * @return the CDC history end time, or {@code null} when unset.
+ */
+ public String getCdcEndTime() {
+ return cdcEndTime;
+ }
+
/**
* @return the CDC poll timeout in milliseconds.
*/
@@ -308,12 +320,12 @@ public class IoTDBOptions implements Serializable {
private boolean lookupAsync = false;
private int lookupThreadSize = 5;
private ScanMode scanMode = ScanMode.SNAPSHOT;
- private String cdcTopic;
- private String cdcConsumerGroup;
private String cdcMode = "live";
private String cdcStartTime;
+ private String cdcEndTime;
private long cdcPollTimeoutMs = 1000L;
private boolean cdcAutoCommit = true;
+ private TimestampPrecision timestampPrecision = TimestampPrecision.MS;
public Builder withNodeUrls(List<String> nodeUrls) {
this.nodeUrls = nodeUrls;
@@ -375,16 +387,6 @@ public class IoTDBOptions implements Serializable {
return this;
}
- public Builder withCdcTopic(String cdcTopic) {
- this.cdcTopic = cdcTopic;
- return this;
- }
-
- public Builder withCdcConsumerGroup(String cdcConsumerGroup) {
- this.cdcConsumerGroup = cdcConsumerGroup;
- return this;
- }
-
public Builder withCdcMode(String cdcMode) {
this.cdcMode = cdcMode;
return this;
@@ -395,6 +397,11 @@ public class IoTDBOptions implements Serializable {
return this;
}
+ public Builder withCdcEndTime(String cdcEndTime) {
+ this.cdcEndTime = cdcEndTime;
+ return this;
+ }
+
public Builder withCdcPollTimeoutMs(long cdcPollTimeoutMs) {
this.cdcPollTimeoutMs = cdcPollTimeoutMs;
return this;
@@ -405,6 +412,11 @@ public class IoTDBOptions implements Serializable {
return this;
}
+ public Builder withTimestampPrecision(TimestampPrecision
timestampPrecision) {
+ this.timestampPrecision = timestampPrecision;
+ return this;
+ }
+
/**
* @return the built options
*/
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/sink/RowDataSinkDataConverter.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/sink/RowDataSinkDataConverter.java
index 8e378a1..976afdd 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/sink/RowDataSinkDataConverter.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/sink/RowDataSinkDataConverter.java
@@ -9,22 +9,26 @@
*
* 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.
+ * 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.iotdb.relational.flink.sink;
import org.apache.flink.table.data.RowData;
+import org.apache.flink.table.types.DataType;
+import org.apache.flink.table.types.logical.LogicalType;
+import org.apache.flink.table.types.logical.TimestampType;
import org.apache.flink.types.RowKind;
import org.apache.tsfile.utils.Binary;
import java.io.IOException;
+import java.sql.Timestamp;
import java.time.LocalDate;
+import java.util.List;
/**
* {@link SinkDataConverter} over Flink {@link RowData}.
@@ -36,17 +40,30 @@ public class RowDataSinkDataConverter implements
SinkDataConverter<RowData> {
private static final long serialVersionUID = 1L;
+ private final int[] timestampPrecisions;
+
+ public RowDataSinkDataConverter(DataType rowDataType) {
+ List<DataType> fieldTypes = DataType.getFieldDataTypes(rowDataType);
+ this.timestampPrecisions = new int[fieldTypes.size()];
+ for (int i = 0; i < fieldTypes.size(); i++) {
+ LogicalType logicalType = fieldTypes.get(i).getLogicalType();
+ timestampPrecisions[i] =
+ logicalType instanceof TimestampType ? ((TimestampType)
logicalType).getPrecision() : -1;
+ }
+ }
+
@Override
public Iterator getIterator(RowData record) throws IOException {
- return new RowDataIterator(record);
+ return new RowDataIterator(record, timestampPrecisions);
}
private static class RowDataIterator implements SinkDataConverter.Iterator {
private final RowData current;
+ private final int[] timestampPrecisions;
private boolean consumed;
- private RowDataIterator(RowData record) throws IOException {
+ private RowDataIterator(RowData record, int[] timestampPrecisions) throws
IOException {
if (record != null && record.getRowKind() != RowKind.INSERT) {
throw new IOException(
"The IoTDB table sink only accepts INSERT records, but got "
@@ -54,6 +71,7 @@ public class RowDataSinkDataConverter implements
SinkDataConverter<RowData> {
+ ".");
}
this.current = record;
+ this.timestampPrecisions = timestampPrecisions;
}
@Override
@@ -111,8 +129,8 @@ public class RowDataSinkDataConverter implements
SinkDataConverter<RowData> {
}
@Override
- public long getTimestamp(int columnIndex) {
- return current.getTimestamp(columnIndex, 3).getMillisecond();
+ public Timestamp getTimestamp(int columnIndex) {
+ return current.getTimestamp(columnIndex,
timestampPrecisions[columnIndex]).toTimestamp();
}
}
}
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/sink/SinkDataConverter.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/sink/SinkDataConverter.java
index a7bbbe4..0963588 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/sink/SinkDataConverter.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/sink/SinkDataConverter.java
@@ -23,6 +23,7 @@ import org.apache.tsfile.utils.Binary;
import java.io.IOException;
import java.io.Serializable;
+import java.sql.Timestamp;
import java.time.LocalDate;
/**
@@ -72,8 +73,11 @@ public interface SinkDataConverter<IN> extends Serializable {
LocalDate getDate(int columnIndex) throws IOException;
- /** Returns the TIME value of the current row as epoch milliseconds. */
- long getTimestamp(int columnIndex) throws IOException;
+ /**
+ * Returns the timestamp value of the current row, keeping the
sub-millisecond part. The sink
+ * converts it to the target IoTDB timestamp precision.
+ */
+ Timestamp getTimestamp(int columnIndex) throws IOException;
@Override
default void close() throws IOException {}
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/cdc/SubscriptionDataIterator.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/cdc/SubscriptionDataIterator.java
index bee63a7..3e36c4e 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/cdc/SubscriptionDataIterator.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/cdc/SubscriptionDataIterator.java
@@ -20,6 +20,7 @@
package org.apache.iotdb.relational.flink.source.cdc;
import org.apache.iotdb.relational.flink.source.common.IoTDBDataIterator;
+import org.apache.iotdb.relational.flink.utils.TimestampPrecision;
import
org.apache.iotdb.session.subscription.payload.SubscriptionRecordHandler.SubscriptionResultSet;
import java.io.IOException;
@@ -44,12 +45,16 @@ public class SubscriptionDataIterator implements
IoTDBDataIterator {
private final SubscriptionResultSet resultSet;
private final List<String> columnNames;
private final int[] sourceIndexes;
+ private final TimestampPrecision timestampPrecision;
public SubscriptionDataIterator(
- SubscriptionResultSet resultSet, List<String> projectedColumns) {
+ SubscriptionResultSet resultSet,
+ List<String> projectedColumns,
+ TimestampPrecision timestampPrecision) {
this.resultSet = resultSet;
this.columnNames = Collections.unmodifiableList(new
ArrayList<>(projectedColumns));
this.sourceIndexes = resolveSourceIndexes(resultSet.getColumnNames(),
projectedColumns);
+ this.timestampPrecision = timestampPrecision;
}
private static int[] resolveSourceIndexes(
@@ -133,6 +138,6 @@ public class SubscriptionDataIterator implements
IoTDBDataIterator {
@Override
public Timestamp getTimestamp(int columnIndex) {
- return new Timestamp(resultSet.getLong(sourceIndexes[columnIndex]));
+ return
timestampPrecision.toTimestamp(resultSet.getLong(sourceIndexes[columnIndex]));
}
}
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/cdc/client/IoTDBSubscriptionClient.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/cdc/client/IoTDBSubscriptionClient.java
index e76b8ed..6c15dfd 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/cdc/client/IoTDBSubscriptionClient.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/cdc/client/IoTDBSubscriptionClient.java
@@ -26,6 +26,8 @@ import
org.apache.iotdb.session.subscription.SubscriptionTableSessionBuilder;
import
org.apache.iotdb.session.subscription.consumer.ISubscriptionTablePullConsumer;
import
org.apache.iotdb.session.subscription.consumer.table.SubscriptionTablePullConsumerBuilder;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.Properties;
@@ -47,9 +49,13 @@ public final class IoTDBSubscriptionClient {
properties.setProperty(TopicConstant.TABLE_KEY, options.getTable());
properties.setProperty(TopicConstant.MODE_KEY, options.getCdcMode());
properties.setProperty(TopicConstant.FORMAT_KEY,
TopicConstant.FORMAT_RECORD_HANDLER_VALUE);
- if (options.getCdcStartTime() != null &&
!options.getCdcStartTime().isEmpty()) {
- properties.setProperty(TopicConstant.START_TIME_KEY,
options.getCdcStartTime());
- }
+ setTimeProperty(
+ properties,
+ TopicConstant.START_TIME_KEY,
+ "iotdb.cdc.start-time",
+ options.getCdcStartTime());
+ setTimeProperty(
+ properties, TopicConstant.END_TIME_KEY, "iotdb.cdc.end-time",
options.getCdcEndTime());
try (ISubscriptionTableSession session =
new SubscriptionTableSessionBuilder()
@@ -74,6 +80,52 @@ public final class IoTDBSubscriptionClient {
.build();
}
+ private static void setTimeProperty(
+ Properties properties, String key, String optionName, String value) {
+ if (value == null || value.trim().isEmpty()) {
+ return;
+ }
+ properties.setProperty(key, validateTimeValue(value.trim(), optionName));
+ }
+
+ private static String validateTimeValue(String value, String optionName) {
+ if ("now".equalsIgnoreCase(value)) {
+ return value;
+ }
+ try {
+ Long.parseLong(value);
+ return value;
+ } catch (NumberFormatException ignored) {
+ // Not a raw long timestamp; fall through to the datetime formats.
+ }
+ if (isIsoDateTime(value)) {
+ return value;
+ }
+ throw new IllegalArgumentException(
+ "Invalid value for '"
+ + optionName
+ + "': "
+ + value
+ + ". Expected 'now', an ISO datetime (e.g. 2011-12-03T10:15:30),
or a raw long"
+ + " timestamp in the IoTDB timestamp precision.");
+ }
+
+ private static boolean isIsoDateTime(String value) {
+ return parseable(value, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
+ || parseable(value, DateTimeFormatter.ISO_OFFSET_DATE_TIME)
+ || parseable(value, DateTimeFormatter.ISO_LOCAL_DATE)
+ || parseable(value.replace(' ', 'T'),
DateTimeFormatter.ISO_LOCAL_DATE_TIME);
+ }
+
+ private static boolean parseable(String value, DateTimeFormatter formatter) {
+ try {
+ formatter.parse(value);
+ return true;
+ } catch (DateTimeParseException e) {
+ return false;
+ }
+ }
+
private static String[] splitNodeUrl(String nodeUrl) {
String[] parts = nodeUrl.split(":");
if (parts.length != 2) {
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/lookup/IoTDBLookupReader.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/lookup/IoTDBLookupReader.java
index c92e6c6..44df320 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/lookup/IoTDBLookupReader.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/lookup/IoTDBLookupReader.java
@@ -117,7 +117,9 @@ public class IoTDBLookupReader implements AutoCloseable {
List<String> predicates = new ArrayList<>(keyIndices.length);
for (int position = 0; position < keyIndices.length; position++) {
int fieldIndex = keyIndices[position];
- String literal = IoTDBUtils.renderLiteral(keyRow, position,
fieldTypes.get(fieldIndex));
+ String literal =
+ IoTDBUtils.renderLiteral(
+ keyRow, position, fieldTypes.get(fieldIndex),
options.getTimestampPrecision());
if (literal == null) {
return null;
}
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/scan/pushdown/IoTDBExpressionVisitor.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/scan/pushdown/IoTDBExpressionVisitor.java
index 4884e39..432bb61 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/scan/pushdown/IoTDBExpressionVisitor.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/source/scan/pushdown/IoTDBExpressionVisitor.java
@@ -20,6 +20,7 @@
package org.apache.iotdb.relational.flink.source.scan.pushdown;
import org.apache.iotdb.relational.flink.utils.IoTDBUtils;
+import org.apache.iotdb.relational.flink.utils.TimestampPrecision;
import org.apache.flink.table.expressions.CallExpression;
import org.apache.flink.table.expressions.Expression;
@@ -99,6 +100,12 @@ public class IoTDBExpressionVisitor implements
ExpressionVisitor<String> {
FLINK_TO_IOTDB_FUNCTION_NAMES = Collections.unmodifiableMap(functionNames);
}
+ private final TimestampPrecision timestampPrecision;
+
+ public IoTDBExpressionVisitor(TimestampPrecision timestampPrecision) {
+ this.timestampPrecision = timestampPrecision;
+ }
+
@Override
public String visit(CallExpression call) {
if (call == null || call.getFunctionDefinition().getKind() !=
FunctionKind.SCALAR) {
@@ -114,7 +121,7 @@ public class IoTDBExpressionVisitor implements
ExpressionVisitor<String> {
@Override
public String visit(ValueLiteralExpression valueLiteral) {
- return IoTDBUtils.renderLiteral(valueLiteral);
+ return IoTDBUtils.renderLiteral(valueLiteral, timestampPrecision);
}
@Override
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/utils/IoTDBUtils.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/utils/IoTDBUtils.java
index 183b1ee..fbb5ba0 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/utils/IoTDBUtils.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/utils/IoTDBUtils.java
@@ -103,7 +103,8 @@ public final class IoTDBUtils {
}
}
- public static DataType toFlinkDataType(TSDataType dataType) {
+ public static DataType toFlinkDataType(
+ TSDataType dataType, TimestampPrecision timestampPrecision) {
switch (dataType) {
case BOOLEAN:
return DataTypes.BOOLEAN();
@@ -123,7 +124,7 @@ public final class IoTDBUtils {
case DATE:
return DataTypes.DATE();
case TIMESTAMP:
- return DataTypes.TIMESTAMP(3);
+ return DataTypes.TIMESTAMP(timestampPrecision.getFlinkPrecision());
default:
throw new CatalogException("Unsupported IoTDB data type: " + dataType);
}
@@ -299,7 +300,8 @@ public final class IoTDBUtils {
* Renders a Flink planner literal as an IoTDB SQL literal. Returns {@code
null} if the literal
* type or value cannot be represented in IoTDB SQL.
*/
- public static String renderLiteral(ValueLiteralExpression literal) {
+ public static String renderLiteral(
+ ValueLiteralExpression literal, TimestampPrecision timestampPrecision) {
if (literal == null || literal.isNull()) {
return null;
}
@@ -340,16 +342,18 @@ public final class IoTDBUtils {
.getValueAs(LocalDate.class)
.map(value -> "CAST('" + value + "' AS DATE)")
.orElse(null);
- case TIMESTAMP_WITHOUT_TIME_ZONE:
- return literal
- .getValueAs(LocalDateTime.class)
- .map(value -> "CAST('" + value + "' AS TIMESTAMP)")
- .orElse(null);
- case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
- return literal
- .getValueAs(Instant.class)
- .map(value -> "CAST('" + value + "' AS TIMESTAMP)")
- .orElse(null);
+ case TIMESTAMP_WITHOUT_TIME_ZONE: {
+ LocalDateTime value =
literal.getValueAs(LocalDateTime.class).orElse(null);
+ return value == null
+ ? null
+ : renderTimestampLiteral(TimestampData.fromLocalDateTime(value),
timestampPrecision);
+ }
+ case TIMESTAMP_WITH_LOCAL_TIME_ZONE: {
+ Instant value = literal.getValueAs(Instant.class).orElse(null);
+ return value == null
+ ? null
+ : renderTimestampLiteral(TimestampData.fromInstant(value),
timestampPrecision);
+ }
default:
return null;
}
@@ -362,7 +366,8 @@ public final class IoTDBUtils {
* Renders a runtime {@link RowData} field as an IoTDB SQL literal. Returns
{@code null} when the
* value is {@code null} or cannot be represented in IoTDB SQL.
*/
- public static String renderLiteral(RowData row, int position, DataType
dataType) {
+ public static String renderLiteral(
+ RowData row, int position, DataType dataType, TimestampPrecision
timestampPrecision) {
if (row == null || row.isNullAt(position) || dataType == null) {
return null;
}
@@ -395,11 +400,8 @@ public final class IoTDBUtils {
case DATE:
return "CAST('" + LocalDate.ofEpochDay(row.getInt(position)) + "' AS
DATE)";
case TIMESTAMP_WITHOUT_TIME_ZONE:
- return "CAST('"
- + getTimestamp(row, position, dataType).toLocalDateTime()
- + "' AS TIMESTAMP)";
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
- return "CAST('" + getTimestamp(row, position, dataType).toInstant() +
"' AS TIMESTAMP)";
+ return renderTimestampLiteral(getTimestamp(row, position, dataType),
timestampPrecision);
default:
return null;
}
@@ -417,7 +419,8 @@ public final class IoTDBUtils {
List<int[]> groupingSets,
List<AggregateExpression> aggregateExpressions,
DataType sourceRowDataType,
- DataType producedDataType) {
+ DataType producedDataType,
+ TimestampPrecision timestampPrecision) {
if (groupingSets == null
|| groupingSets.size() != 1
|| aggregateExpressions == null
@@ -450,7 +453,7 @@ public final class IoTDBUtils {
selectExpressions.add(column);
}
- IoTDBExpressionVisitor visitor = new IoTDBExpressionVisitor();
+ IoTDBExpressionVisitor visitor = new
IoTDBExpressionVisitor(timestampPrecision);
for (int i = 0; i < aggregateExpressions.size(); i++) {
int producedIndex = grouping.length + i;
if (producedIndex >= producedFieldTypes.size()) {
@@ -566,6 +569,13 @@ public final class IoTDBUtils {
return row.getTimestamp(position, precision);
}
+ private static String renderTimestampLiteral(
+ TimestampData timestamp, TimestampPrecision timestampPrecision) {
+ long units =
+ timestampPrecision.toUnits(timestamp.getMillisecond(),
timestamp.getNanoOfMillisecond());
+ return "CAST(" + units + " AS TIMESTAMP)";
+ }
+
private static boolean isFinite(float value) {
return !Float.isNaN(value) && !Float.isInfinite(value);
}
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/utils/TimestampPrecision.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/utils/TimestampPrecision.java
new file mode 100644
index 0000000..bed585c
--- /dev/null
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/main/java/org/apache/iotdb/relational/flink/utils/TimestampPrecision.java
@@ -0,0 +1,120 @@
+/*
+ * 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.iotdb.relational.flink.utils;
+
+import java.sql.Timestamp;
+import java.util.Locale;
+
+/**
+ * Timestamp precision used by the target IoTDB server.
+ *
+ * <p>The values mirror IoTDB's {@code timestamp_precision}: {@code ms},
{@code us} and {@code ns}.
+ * The connector converts between Flink's epoch-millisecond based timestamps
and the precision of
+ * the IoTDB server with the helpers below.
+ */
+public enum TimestampPrecision {
+ MS("ms", 1L, 3),
+ US("us", 1_000L, 6),
+ NS("ns", 1_000_000L, 9);
+
+ private final String value;
+ private final long timeFactor;
+ private final int flinkPrecision;
+
+ TimestampPrecision(String value, long timeFactor, int flinkPrecision) {
+ this.value = value;
+ this.timeFactor = timeFactor;
+ this.flinkPrecision = flinkPrecision;
+ }
+
+ /** @return the option value, e.g. {@code ms}. */
+ public String getValue() {
+ return value;
+ }
+
+ /** @return the number of units in one millisecond. */
+ public long getTimeFactor() {
+ return timeFactor;
+ }
+
+ /** @return the equivalent Flink {@code TIMESTAMP(p)} precision. */
+ public int getFlinkPrecision() {
+ return flinkPrecision;
+ }
+
+ /**
+ * Converts an epoch-millisecond value (with its sub-millisecond
nanoseconds) into this
+ * precision's units.
+ *
+ * @param epochMillis milliseconds since the epoch
+ * @param nanoOfMilli nanoseconds within the millisecond, in {@code [0,
1_000_000)}
+ * @return the value in this precision's units
+ */
+ public long toUnits(long epochMillis, int nanoOfMilli) {
+ long nanoPerUnit = 1_000_000L / timeFactor;
+ return epochMillis * timeFactor + nanoOfMilli / nanoPerUnit;
+ }
+
+ /**
+ * Converts a value in this precision's units into epoch milliseconds,
truncating the
+ * sub-millisecond part.
+ *
+ * @param raw value in this precision's units
+ * @return milliseconds since the epoch
+ */
+ public long toEpochMillis(long raw) {
+ return Math.floorDiv(raw, timeFactor);
+ }
+
+ /**
+ * Converts a value in this precision's units into a {@link Timestamp},
keeping the
+ * sub-millisecond part.
+ *
+ * @param raw value in this precision's units
+ * @return the corresponding timestamp
+ */
+ public Timestamp toTimestamp(long raw) {
+ long millis = Math.floorDiv(raw, timeFactor);
+ long sub = Math.floorMod(raw, timeFactor);
+ long nanoPerUnit = 1_000_000L / timeFactor;
+ Timestamp timestamp = new Timestamp(millis);
+ timestamp.setNanos((int) (sub * nanoPerUnit));
+ return timestamp;
+ }
+
+ /**
+ * Parses a precision value, e.g. {@code ms}, case-insensitively.
+ *
+ * @param value option value
+ * @return the matching precision
+ * @throws IllegalArgumentException when the value is {@code null} or
unsupported
+ */
+ public static TimestampPrecision fromString(String value) {
+ if (value == null) {
+ throw new IllegalArgumentException("Timestamp precision must not be
null.");
+ }
+ String normalized = value.trim().toLowerCase(Locale.ROOT);
+ for (TimestampPrecision precision : values()) {
+ if (precision.value.equals(normalized)) {
+ return precision;
+ }
+ }
+ throw new IllegalArgumentException("Unsupported timestamp precision: " +
value);
+ }
+}
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/test/java/org/apache/iotdb/relational/flink/sink/RowDataSinkDataConverterTest.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/test/java/org/apache/iotdb/relational/flink/sink/RowDataSinkDataConverterTest.java
index d4e93c6..7ac51b0 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/test/java/org/apache/iotdb/relational/flink/sink/RowDataSinkDataConverterTest.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/test/java/org/apache/iotdb/relational/flink/sink/RowDataSinkDataConverterTest.java
@@ -19,9 +19,11 @@
package org.apache.iotdb.relational.flink.sink;
+import org.apache.flink.table.api.DataTypes;
import org.apache.flink.table.data.GenericRowData;
import org.apache.flink.table.data.StringData;
import org.apache.flink.table.data.TimestampData;
+import org.apache.flink.table.types.DataType;
import org.apache.flink.types.RowKind;
import org.apache.tsfile.utils.Binary;
import org.junit.Test;
@@ -38,7 +40,16 @@ import static org.junit.Assert.fail;
public class RowDataSinkDataConverterTest {
- private final RowDataSinkDataConverter converter = new
RowDataSinkDataConverter();
+ private static final DataType ROW_TYPE =
+ DataTypes.ROW(
+ DataTypes.FIELD("time", DataTypes.TIMESTAMP(3)),
+ DataTypes.FIELD("s", DataTypes.STRING()),
+ DataTypes.FIELD("i", DataTypes.INT()),
+ DataTypes.FIELD("d", DataTypes.DOUBLE()),
+ DataTypes.FIELD("date", DataTypes.DATE()),
+ DataTypes.FIELD("b", DataTypes.BYTES()));
+
+ private final RowDataSinkDataConverter converter = new
RowDataSinkDataConverter(ROW_TYPE);
@Test
public void testYieldsOneRowPerInput() throws IOException {
@@ -57,7 +68,7 @@ public class RowDataSinkDataConverterTest {
try (SinkDataConverter.Iterator iterator =
converter.getIterator(rowData())) {
assertTrue(iterator.next());
- assertEquals(1000L, iterator.getTimestamp(0));
+ assertEquals(1000L, iterator.getTimestamp(0).getTime());
assertEquals("t", iterator.getString(1));
assertEquals(7, iterator.getInt(2));
assertEquals(2.5d, iterator.getDouble(3), 0.0d);
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/test/java/org/apache/iotdb/relational/flink/source/lookup/IoTDBLookupReaderTest.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/test/java/org/apache/iotdb/relational/flink/source/lookup/IoTDBLookupReaderTest.java
index f2b6134..b773791 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/test/java/org/apache/iotdb/relational/flink/source/lookup/IoTDBLookupReaderTest.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-base/src/test/java/org/apache/iotdb/relational/flink/source/lookup/IoTDBLookupReaderTest.java
@@ -61,7 +61,7 @@ public class IoTDBLookupReaderTest {
assertEquals(
"SELECT \"time\", \"device_id\", \"temperature\" FROM \"sensor\" "
- + "WHERE \"device_id\" = 'd1' AND \"time\" =
CAST('2024-01-01T12:30' AS TIMESTAMP)",
+ + "WHERE \"device_id\" = 'd1' AND \"time\" = CAST(1704112200000 AS
TIMESTAMP)",
reader.buildLookupSql(keyRow));
}
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/catalog/IoTDBCatalog.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/catalog/IoTDBCatalog.java
index 25f4ebd..a31cd32 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/catalog/IoTDBCatalog.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/catalog/IoTDBCatalog.java
@@ -418,7 +418,8 @@ public class IoTDBCatalog extends AbstractCatalog {
for (int i = 0; i < columns.size(); i++) {
IMeasurementSchema column = columns.get(i);
schemaBuilder.column(
- column.getMeasurementName(),
IoTDBUtils.toFlinkDataType(column.getType()));
+ column.getMeasurementName(),
+ IoTDBUtils.toFlinkDataType(column.getType(),
options.getTimestampPrecision()));
switch (categories.get(i)) {
case TIME:
timeColumn = column.getMeasurementName();
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/catalog/IoTDBCatalogFactory.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/catalog/IoTDBCatalogFactory.java
index b29fde5..553b945 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/catalog/IoTDBCatalogFactory.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/catalog/IoTDBCatalogFactory.java
@@ -50,7 +50,8 @@ public class IoTDBCatalogFactory implements CatalogFactory {
Arrays.asList(
IoTDBOptions.USER,
IoTDBOptions.PASSWORD,
- IoTDBOptions.DEFAULT_DATABASE));
+ IoTDBOptions.DEFAULT_DATABASE,
+ IoTDBOptions.TIMESTAMP_PRECISION));
}
@Override
@@ -68,6 +69,7 @@ public class IoTDBCatalogFactory implements CatalogFactory {
.withUsername(config.get(IoTDBOptions.USER))
.withPassword(config.get(IoTDBOptions.PASSWORD))
.withDefaultDatabase(config.get(IoTDBOptions.DEFAULT_DATABASE))
+ .withTimestampPrecision(config.get(IoTDBOptions.TIMESTAMP_PRECISION))
.build();
}
}
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/sink/IoTDBSinkWriter.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/sink/IoTDBSinkWriter.java
index 0422e34..ee8cddd 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/sink/IoTDBSinkWriter.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/sink/IoTDBSinkWriter.java
@@ -22,6 +22,7 @@ package org.apache.iotdb.relational.flink.sink;
import org.apache.iotdb.isession.ITableSession;
import org.apache.iotdb.relational.flink.cfg.IoTDBOptions;
import org.apache.iotdb.relational.flink.utils.IoTDBUtils;
+import org.apache.iotdb.relational.flink.utils.TimestampPrecision;
import org.apache.iotdb.session.TableSessionBuilder;
import org.apache.flink.api.connector.sink2.SinkWriter;
@@ -32,6 +33,7 @@ import org.apache.tsfile.enums.TSDataType;
import org.apache.tsfile.write.record.Tablet;
import java.io.IOException;
+import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -241,7 +243,7 @@ public class IoTDBSinkWriter<IN> implements SinkWriter<IN> {
}
int row = buffer.getRowSize();
- buffer.addTimestamp(row, iterator.getTimestamp(timeColumnIndex));
+ buffer.addTimestamp(row, toUnits(iterator.getTimestamp(timeColumnIndex)));
for (int i = 0; i < columnNames.size(); i++) {
buffer.addValue(
columnNames.get(i), row, readValue(iterator, columnIndexes[i],
columnTsTypes.get(i)));
@@ -273,12 +275,17 @@ public class IoTDBSinkWriter<IN> implements
SinkWriter<IN> {
case DATE:
return iterator.getDate(columnIndex);
case TIMESTAMP:
- return iterator.getTimestamp(columnIndex);
+ return toUnits(iterator.getTimestamp(columnIndex));
default:
throw new IOException("Unsupported IoTDB data type for sink: " +
dataType);
}
}
+ private long toUnits(Timestamp timestamp) {
+ TimestampPrecision precision = options.getTimestampPrecision();
+ return precision.toUnits(timestamp.getTime(), timestamp.getNanos() %
1_000_000);
+ }
+
private static Set<String> normalizeColumns(List<String> columns) {
if (columns == null) {
return Collections.emptySet();
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/source/cdc/IoTDBSubscriptionSourceReader.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/source/cdc/IoTDBSubscriptionSourceReader.java
index 878759d..8673275 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/source/cdc/IoTDBSubscriptionSourceReader.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/source/cdc/IoTDBSubscriptionSourceReader.java
@@ -176,7 +176,10 @@ public class IoTDBSubscriptionSourceReader<OUT>
}
for (ResultSet resultSet : message.getResultSets()) {
SubscriptionDataIterator iterator =
- new SubscriptionDataIterator((SubscriptionResultSet) resultSet,
projectedColumns);
+ new SubscriptionDataIterator(
+ (SubscriptionResultSet) resultSet,
+ projectedColumns,
+ options.getTimestampPrecision());
while (iterator.next()) {
OUT record = deserializer.deserialize(iterator);
if (record != null) {
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/table/IoTDBRelationalDynamicTableFactory.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/table/IoTDBRelationalDynamicTableFactory.java
index 22cd95d..7ffa32d 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/table/IoTDBRelationalDynamicTableFactory.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/table/IoTDBRelationalDynamicTableFactory.java
@@ -86,12 +86,12 @@ public class IoTDBRelationalDynamicTableFactory
IoTDBOptions.LOOKUP_ASYNC,
IoTDBOptions.LOOKUP_THREAD_SIZE,
IoTDBOptions.SCAN_MODE,
- IoTDBOptions.CDC_TOPIC,
- IoTDBOptions.CDC_CONSUMER_GROUP,
IoTDBOptions.CDC_MODE,
IoTDBOptions.CDC_START_TIME,
+ IoTDBOptions.CDC_END_TIME,
IoTDBOptions.CDC_POLL_TIMEOUT_MS,
IoTDBOptions.CDC_AUTO_COMMIT,
+ IoTDBOptions.TIMESTAMP_PRECISION,
LookupOptions.CACHE_TYPE,
LookupOptions.PARTIAL_CACHE_MAX_ROWS,
LookupOptions.PARTIAL_CACHE_EXPIRE_AFTER_WRITE,
@@ -112,12 +112,12 @@ public class IoTDBRelationalDynamicTableFactory
.withLookupAsync(config.get(IoTDBOptions.LOOKUP_ASYNC))
.withLookupThreadSize(config.get(IoTDBOptions.LOOKUP_THREAD_SIZE))
.withScanMode(config.get(IoTDBOptions.SCAN_MODE))
- .withCdcTopic(config.get(IoTDBOptions.CDC_TOPIC))
- .withCdcConsumerGroup(config.get(IoTDBOptions.CDC_CONSUMER_GROUP))
.withCdcMode(config.get(IoTDBOptions.CDC_MODE))
.withCdcStartTime(config.get(IoTDBOptions.CDC_START_TIME))
+ .withCdcEndTime(config.get(IoTDBOptions.CDC_END_TIME))
.withCdcPollTimeoutMs(config.get(IoTDBOptions.CDC_POLL_TIMEOUT_MS))
.withCdcAutoCommit(config.get(IoTDBOptions.CDC_AUTO_COMMIT))
+ .withTimestampPrecision(config.get(IoTDBOptions.TIMESTAMP_PRECISION))
.build();
}
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/table/IoTDBRelationalDynamicTableSink.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/table/IoTDBRelationalDynamicTableSink.java
index eb52dfe..d42994a 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/table/IoTDBRelationalDynamicTableSink.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/table/IoTDBRelationalDynamicTableSink.java
@@ -53,7 +53,8 @@ public class IoTDBRelationalDynamicTableSink implements
DynamicTableSink {
@Override
public SinkRuntimeProvider getSinkRuntimeProvider(Context context) {
- SinkDataConverter<RowData> converter = new RowDataSinkDataConverter();
+ SinkDataConverter<RowData> converter =
+ new RowDataSinkDataConverter(schema.toPhysicalRowDataType());
return SinkV2Provider.of(
new IoTDBSink<>(options, schema.toPhysicalRowDataType(), converter));
}
diff --git
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/table/IoTDBRelationalDynamicTableSource.java
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/table/IoTDBRelationalDynamicTableSource.java
index b7cbaac..c424c88 100644
---
a/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/table/IoTDBRelationalDynamicTableSource.java
+++
b/connectors/flink-iotdb-table-connector/flink-iotdb-table-connector-flink1/src/main/java/org/apache/iotdb/relational/flink/table/IoTDBRelationalDynamicTableSource.java
@@ -179,7 +179,8 @@ public class IoTDBRelationalDynamicTableSource
List<ResolvedExpression> acceptedFilters = new ArrayList<>();
List<ResolvedExpression> remainingFilters = new ArrayList<>();
- IoTDBExpressionVisitor expressionVisitor = new IoTDBExpressionVisitor();
+ IoTDBExpressionVisitor expressionVisitor =
+ new IoTDBExpressionVisitor(options.getTimestampPrecision());
for (ResolvedExpression filter : filters) {
String filterQuery = filter.accept(expressionVisitor);
if (filterQuery == null || filterQuery.trim().isEmpty()) {
@@ -205,7 +206,11 @@ public class IoTDBRelationalDynamicTableSource
// after any projection that has already been pushed into this source.
AggregateSpec spec =
IoTDBUtils.translateAggregate(
- groupingSets, aggregateExpressions, physicalRowDataType,
producedDataType);
+ groupingSets,
+ aggregateExpressions,
+ physicalRowDataType,
+ producedDataType,
+ options.getTimestampPrecision());
if (spec == null) {
return false;
}