Skip to content

Commit b1ee65c

Browse files
Bruce Irschickbirschick-bq
andauthored
[AD-780] Add full support for MongoDB Timestamp data type. (#448)
* [AD-780] Add full support for MongoDB Timestamp data type. * [AD-780] Add more testing coverage. * Commit Code Coverage Badge * [AD-780] Improved data conversion for getObject so that Bson types don't leak. * Commit Code Coverage Badge * [AD-780] Update dependencies * Commit Code Coverage Badge Co-authored-by: birschick-bq <[email protected]>
1 parent 1edd9e2 commit b1ee65c

19 files changed

+269
-123
lines changed

.github/badges/branches.svg

Lines changed: 1 addition & 1 deletion
Loading

build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ dependencies {
318318
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.14.1'
319319
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-guava', version: '2.14.1'
320320
implementation group: 'com.google.guava', name: 'guava', version: '31.1-jre'
321-
implementation group: 'org.slf4j', name: 'slf4j-log4j12', version: '2.0.3'
321+
implementation group: 'org.slf4j', name: 'slf4j-log4j12', version: '2.0.5'
322322
implementation group: 'org.apache.commons', name: 'commons-text', version: '1.10.0'
323323
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.19.0'
324324
implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.19.0'
@@ -329,7 +329,7 @@ dependencies {
329329
implementation 'io.github.hakky54:sslcontext-kickstart:7.4.7'
330330

331331
compileOnly group: 'org.immutables', name: 'value', version: '2.9.2'
332-
compileOnly group: 'com.puppycrawl.tools', name: 'checkstyle', version: '10.3.4'
332+
compileOnly group: 'com.puppycrawl.tools', name: 'checkstyle', version: '10.5.0'
333333
compileOnly 'org.projectlombok:lombok:1.18.24'
334334
annotationProcessor 'org.projectlombok:lombok:1.18.24'
335335

@@ -340,14 +340,14 @@ dependencies {
340340
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.9.1'
341341
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.9.1'
342342
testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.8.0'
343-
testRuntimeOnly group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '3.5.1'
343+
testRuntimeOnly group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '3.5.3'
344344
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-api:5.9.1'
345345
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
346346
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.9.1'
347347

348348
spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.12.0'
349349

350-
testFixturesCompileOnly group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '3.4.11'
350+
testFixturesCompileOnly group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '3.5.3'
351351
testFixturesCompileOnly 'org.junit.jupiter:junit-jupiter-api:5.9.1'
352352
testFixturesImplementation group: 'com.google.guava', name: 'guava', version: '29.0-jre'
353353
testFixturesImplementation group: 'org.mongodb', name: 'mongodb-driver-sync', version: '4.7.2'

src/main/java/software/amazon/documentdb/jdbc/DocumentDbAbstractResultSet.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,11 @@ public Object getObject(final int columnIndex) throws SQLException {
287287
verifyState(columnIndex);
288288
final Object o = getValue(columnIndex);
289289
wasNull = (o == null);
290-
return o;
290+
if (!wasNull) {
291+
// Use default converter for the class. As we don't want the Bson types to leak.
292+
return TypeConverters.get(o.getClass(), Object.class).convert(null, o);
293+
}
294+
return null;
291295
}
292296

293297
@Override

src/main/java/software/amazon/documentdb/jdbc/DocumentDbResultSet.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import com.google.common.collect.ImmutableList;
2020
import com.mongodb.client.MongoCursor;
2121
import org.bson.Document;
22-
import org.bson.types.Binary;
2322
import org.slf4j.Logger;
2423
import org.slf4j.LoggerFactory;
2524
import software.amazon.documentdb.jdbc.common.utilities.JdbcColumnMetaData;
@@ -137,9 +136,6 @@ protected Object getValue(final int columnIndex) throws SQLException {
137136
segmentValue = ((Document) segmentValue).get(segmentedPath[j]);
138137
}
139138
// Apache converters cannot handle the following types, must be specifically converted.
140-
if (segmentValue instanceof Binary) {
141-
return ((Binary) segmentValue).getData();
142-
}
143139
if (segmentValue instanceof Document) {
144140
return ((Document) segmentValue).toJson();
145141
}
@@ -150,6 +146,7 @@ protected Object getValue(final int columnIndex) throws SQLException {
150146
.collect(Collectors.toList());
151147
return modifiedList.toString();
152148
}
149+
153150
return segmentValue;
154151
}
155152
}

src/main/java/software/amazon/documentdb/jdbc/common/utilities/TypeConverters.java

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
package software.amazon.documentdb.jdbc.common.utilities;
1818

1919
import com.google.common.collect.ImmutableMap;
20+
import com.google.common.io.BaseEncoding;
21+
import lombok.SneakyThrows;
22+
import org.apache.commons.beanutils.Converter;
2023
import org.apache.commons.beanutils.converters.AbstractConverter;
2124
import org.apache.commons.beanutils.converters.ArrayConverter;
2225
import org.apache.commons.beanutils.converters.BigDecimalConverter;
@@ -34,16 +37,19 @@
3437
import org.apache.commons.beanutils.converters.StringConverter;
3538
import org.bson.BsonRegularExpression;
3639
import org.bson.BsonTimestamp;
40+
import org.bson.types.Binary;
3741
import org.bson.types.Decimal128;
3842
import org.bson.types.MaxKey;
3943
import org.bson.types.MinKey;
4044
import org.bson.types.ObjectId;
4145
import org.slf4j.Logger;
4246
import org.slf4j.LoggerFactory;
47+
4348
import java.math.BigDecimal;
4449
import java.sql.SQLException;
4550
import java.sql.Timestamp;
4651
import java.util.Date;
52+
import java.util.concurrent.TimeUnit;
4753

4854
/**
4955
* Provides a map of type converters.
@@ -81,6 +87,7 @@ public class TypeConverters {
8187
.put(Timestamp.class, new SqlTimestampConverter())
8288
.put(Byte[].class, new ArrayConverter(Byte[].class, new ByteConverter(), -1))
8389
.put(byte[].class, new ArrayConverter(byte[].class, new ByteConverter(), -1))
90+
.put(Binary.class, new BsonBinaryConverter(byte[].class, new ByteConverter()))
8491
.build();
8592
}
8693

@@ -156,7 +163,7 @@ protected String convertToString(final Object value) throws Throwable {
156163

157164
@Override
158165
protected Class<?> getDefaultType() {
159-
return Decimal128.class;
166+
return String.class;
160167
}
161168
}
162169

@@ -181,22 +188,68 @@ public BsonTimestampConverter(final Object defaultValue) {
181188
@Override
182189
protected <T> T convertToType(final Class<T> targetType, final Object value) throws Exception {
183190
if (value instanceof BsonTimestamp) {
184-
return super.convertToType(targetType, ((BsonTimestamp) value).getValue());
191+
final long timeInMillisSinceEpoch = getTimeInMillisSinceEpoch((BsonTimestamp) value);
192+
return super.convertToType(targetType, timeInMillisSinceEpoch);
185193
}
186194
return super.convertToType(targetType, value);
187195
}
188196

189197
@Override
190198
protected String convertToString(final Object value) throws Throwable {
191199
if (value instanceof BsonTimestamp) {
192-
return super.convertToString(((BsonTimestamp) value).getValue());
200+
final long timeInMillisSinceEpoch = getTimeInMillisSinceEpoch((BsonTimestamp) value);
201+
return super.convertToString(super.convertToType(getDefaultType(), timeInMillisSinceEpoch));
202+
}
203+
return super.convertToString(value);
204+
}
205+
206+
private static long getTimeInMillisSinceEpoch(final BsonTimestamp value) {
207+
// This returns time in seconds since epoch.
208+
final int timeInSecsSinceEpoch = value.getTime();
209+
return TimeUnit.SECONDS.toMillis(timeInSecsSinceEpoch);
210+
}
211+
212+
@Override
213+
protected Class<?> getDefaultType() {
214+
return Timestamp.class;
215+
}
216+
}
217+
218+
private static class BsonBinaryConverter extends ArrayConverter {
219+
220+
public BsonBinaryConverter(final Class<?> defaultType, final Converter elementConverter) {
221+
super(defaultType, elementConverter);
222+
}
223+
224+
public BsonBinaryConverter(final Class<?> defaultType, final Converter elementConverter, final int defaultSize) {
225+
super(defaultType, elementConverter, defaultSize);
226+
}
227+
228+
@SneakyThrows
229+
@Override
230+
@SuppressWarnings("unchecked")
231+
public <T> T convertToType(final Class<T> type, final Object value) {
232+
final Class<?> targetType = type == null ? getDefaultType() : type;
233+
if (value instanceof Binary) {
234+
if (targetType.isAssignableFrom(String.class)) {
235+
return (T) convertToString(value);
236+
}
237+
return (T) super.convertToType(targetType, ((Binary) value).getData());
238+
}
239+
return (T) super.convertToType(targetType, value);
240+
}
241+
242+
@Override
243+
protected String convertToString(final Object value) throws Throwable {
244+
if (value instanceof Binary) {
245+
return BaseEncoding.base16().encode(((Binary) value).getData());
193246
}
194247
return super.convertToString(value);
195248
}
196249

197250
@Override
198251
protected Class<?> getDefaultType() {
199-
return BsonTimestamp.class;
252+
return byte[].class;
200253
}
201254
}
202255
}

src/main/java/software/amazon/documentdb/jdbc/metadata/DocumentDbTableSchemaGeneratorHelper.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public class DocumentDbTableSchemaGeneratorHelper {
5656
.put(new SimpleEntry<>(JdbcType.NULL, BsonType.DOUBLE), JdbcType.DOUBLE)
5757
.put(new SimpleEntry<>(JdbcType.NULL, BsonType.INT32), JdbcType.INTEGER)
5858
.put(new SimpleEntry<>(JdbcType.NULL, BsonType.INT64), JdbcType.BIGINT)
59+
.put(new SimpleEntry<>(JdbcType.NULL, BsonType.TIMESTAMP), JdbcType.TIMESTAMP)
5960
.put(new SimpleEntry<>(JdbcType.NULL, BsonType.MAX_KEY), JdbcType.VARCHAR)
6061
.put(new SimpleEntry<>(JdbcType.NULL, BsonType.MIN_KEY), JdbcType.VARCHAR)
6162
.put(new SimpleEntry<>(JdbcType.NULL, BsonType.NULL), JdbcType.NULL)
@@ -70,6 +71,7 @@ public class DocumentDbTableSchemaGeneratorHelper {
7071
.put(new SimpleEntry<>(JdbcType.ARRAY, BsonType.DOUBLE), JdbcType.VARCHAR)
7172
.put(new SimpleEntry<>(JdbcType.ARRAY, BsonType.INT32), JdbcType.VARCHAR)
7273
.put(new SimpleEntry<>(JdbcType.ARRAY, BsonType.INT64), JdbcType.VARCHAR)
74+
.put(new SimpleEntry<>(JdbcType.ARRAY, BsonType.TIMESTAMP), JdbcType.VARCHAR)
7375
.put(new SimpleEntry<>(JdbcType.ARRAY, BsonType.MAX_KEY), JdbcType.VARCHAR)
7476
.put(new SimpleEntry<>(JdbcType.ARRAY, BsonType.MIN_KEY), JdbcType.VARCHAR)
7577
.put(new SimpleEntry<>(JdbcType.ARRAY, BsonType.NULL), JdbcType.ARRAY)
@@ -84,6 +86,7 @@ public class DocumentDbTableSchemaGeneratorHelper {
8486
.put(new SimpleEntry<>(JdbcType.JAVA_OBJECT, BsonType.DOUBLE), JdbcType.VARCHAR)
8587
.put(new SimpleEntry<>(JdbcType.JAVA_OBJECT, BsonType.INT32), JdbcType.VARCHAR)
8688
.put(new SimpleEntry<>(JdbcType.JAVA_OBJECT, BsonType.INT64), JdbcType.VARCHAR)
89+
.put(new SimpleEntry<>(JdbcType.JAVA_OBJECT, BsonType.TIMESTAMP), JdbcType.VARCHAR)
8790
.put(new SimpleEntry<>(JdbcType.JAVA_OBJECT, BsonType.MAX_KEY), JdbcType.VARCHAR)
8891
.put(new SimpleEntry<>(JdbcType.JAVA_OBJECT, BsonType.MIN_KEY), JdbcType.VARCHAR)
8992
.put(new SimpleEntry<>(JdbcType.JAVA_OBJECT, BsonType.NULL), JdbcType.JAVA_OBJECT)
@@ -98,6 +101,7 @@ public class DocumentDbTableSchemaGeneratorHelper {
98101
.put(new SimpleEntry<>(JdbcType.BOOLEAN, BsonType.DOUBLE), JdbcType.DOUBLE)
99102
.put(new SimpleEntry<>(JdbcType.BOOLEAN, BsonType.INT32), JdbcType.INTEGER)
100103
.put(new SimpleEntry<>(JdbcType.BOOLEAN, BsonType.INT64), JdbcType.BIGINT)
104+
.put(new SimpleEntry<>(JdbcType.BOOLEAN, BsonType.TIMESTAMP), JdbcType.VARCHAR)
101105
.put(new SimpleEntry<>(JdbcType.BOOLEAN, BsonType.MAX_KEY), JdbcType.VARCHAR)
102106
.put(new SimpleEntry<>(JdbcType.BOOLEAN, BsonType.MIN_KEY), JdbcType.VARCHAR)
103107
.put(new SimpleEntry<>(JdbcType.BOOLEAN, BsonType.NULL), JdbcType.BOOLEAN)
@@ -112,6 +116,7 @@ public class DocumentDbTableSchemaGeneratorHelper {
112116
.put(new SimpleEntry<>(JdbcType.BIGINT, BsonType.DOUBLE), JdbcType.DECIMAL)
113117
.put(new SimpleEntry<>(JdbcType.BIGINT, BsonType.INT32), JdbcType.BIGINT)
114118
.put(new SimpleEntry<>(JdbcType.BIGINT, BsonType.INT64), JdbcType.BIGINT)
119+
.put(new SimpleEntry<>(JdbcType.BIGINT, BsonType.TIMESTAMP), JdbcType.VARCHAR)
115120
.put(new SimpleEntry<>(JdbcType.BIGINT, BsonType.MAX_KEY), JdbcType.VARCHAR)
116121
.put(new SimpleEntry<>(JdbcType.BIGINT, BsonType.MIN_KEY), JdbcType.VARCHAR)
117122
.put(new SimpleEntry<>(JdbcType.BIGINT, BsonType.NULL), JdbcType.BIGINT)
@@ -126,6 +131,7 @@ public class DocumentDbTableSchemaGeneratorHelper {
126131
.put(new SimpleEntry<>(JdbcType.DECIMAL, BsonType.DOUBLE), JdbcType.DECIMAL)
127132
.put(new SimpleEntry<>(JdbcType.DECIMAL, BsonType.INT32), JdbcType.DECIMAL)
128133
.put(new SimpleEntry<>(JdbcType.DECIMAL, BsonType.INT64), JdbcType.DECIMAL)
134+
.put(new SimpleEntry<>(JdbcType.DECIMAL, BsonType.TIMESTAMP), JdbcType.VARCHAR)
129135
.put(new SimpleEntry<>(JdbcType.DECIMAL, BsonType.MAX_KEY), JdbcType.VARCHAR)
130136
.put(new SimpleEntry<>(JdbcType.DECIMAL, BsonType.MIN_KEY), JdbcType.VARCHAR)
131137
.put(new SimpleEntry<>(JdbcType.DECIMAL, BsonType.NULL), JdbcType.DECIMAL)
@@ -140,6 +146,7 @@ public class DocumentDbTableSchemaGeneratorHelper {
140146
.put(new SimpleEntry<>(JdbcType.DOUBLE, BsonType.DOUBLE), JdbcType.DOUBLE)
141147
.put(new SimpleEntry<>(JdbcType.DOUBLE, BsonType.INT32), JdbcType.DOUBLE)
142148
.put(new SimpleEntry<>(JdbcType.DOUBLE, BsonType.INT64), JdbcType.DECIMAL)
149+
.put(new SimpleEntry<>(JdbcType.DOUBLE, BsonType.TIMESTAMP), JdbcType.VARCHAR)
143150
.put(new SimpleEntry<>(JdbcType.DOUBLE, BsonType.MAX_KEY), JdbcType.VARCHAR)
144151
.put(new SimpleEntry<>(JdbcType.DOUBLE, BsonType.MIN_KEY), JdbcType.VARCHAR)
145152
.put(new SimpleEntry<>(JdbcType.DOUBLE, BsonType.NULL), JdbcType.DOUBLE)
@@ -154,6 +161,7 @@ public class DocumentDbTableSchemaGeneratorHelper {
154161
.put(new SimpleEntry<>(JdbcType.INTEGER, BsonType.DOUBLE), JdbcType.DOUBLE)
155162
.put(new SimpleEntry<>(JdbcType.INTEGER, BsonType.INT32), JdbcType.INTEGER)
156163
.put(new SimpleEntry<>(JdbcType.INTEGER, BsonType.INT64), JdbcType.BIGINT)
164+
.put(new SimpleEntry<>(JdbcType.INTEGER, BsonType.TIMESTAMP), JdbcType.VARCHAR)
157165
.put(new SimpleEntry<>(JdbcType.INTEGER, BsonType.MAX_KEY), JdbcType.VARCHAR)
158166
.put(new SimpleEntry<>(JdbcType.INTEGER, BsonType.MIN_KEY), JdbcType.VARCHAR)
159167
.put(new SimpleEntry<>(JdbcType.INTEGER, BsonType.NULL), JdbcType.INTEGER)
@@ -168,6 +176,7 @@ public class DocumentDbTableSchemaGeneratorHelper {
168176
.put(new SimpleEntry<>(JdbcType.TIMESTAMP, BsonType.DOUBLE), JdbcType.VARCHAR)
169177
.put(new SimpleEntry<>(JdbcType.TIMESTAMP, BsonType.INT32), JdbcType.VARCHAR)
170178
.put(new SimpleEntry<>(JdbcType.TIMESTAMP, BsonType.INT64), JdbcType.VARCHAR)
179+
.put(new SimpleEntry<>(JdbcType.TIMESTAMP, BsonType.TIMESTAMP), JdbcType.TIMESTAMP)
171180
.put(new SimpleEntry<>(JdbcType.TIMESTAMP, BsonType.MAX_KEY), JdbcType.VARCHAR)
172181
.put(new SimpleEntry<>(JdbcType.TIMESTAMP, BsonType.MIN_KEY), JdbcType.VARCHAR)
173182
.put(new SimpleEntry<>(JdbcType.TIMESTAMP, BsonType.NULL), JdbcType.TIMESTAMP)
@@ -182,6 +191,7 @@ public class DocumentDbTableSchemaGeneratorHelper {
182191
.put(new SimpleEntry<>(JdbcType.VARBINARY, BsonType.DOUBLE), JdbcType.VARBINARY)
183192
.put(new SimpleEntry<>(JdbcType.VARBINARY, BsonType.INT32), JdbcType.VARBINARY)
184193
.put(new SimpleEntry<>(JdbcType.VARBINARY, BsonType.INT64), JdbcType.VARBINARY)
194+
.put(new SimpleEntry<>(JdbcType.VARBINARY, BsonType.TIMESTAMP), JdbcType.VARBINARY)
185195
.put(new SimpleEntry<>(JdbcType.VARBINARY, BsonType.MAX_KEY), JdbcType.VARBINARY)
186196
.put(new SimpleEntry<>(JdbcType.VARBINARY, BsonType.MIN_KEY), JdbcType.VARBINARY)
187197
.put(new SimpleEntry<>(JdbcType.VARBINARY, BsonType.NULL), JdbcType.VARBINARY)
@@ -196,6 +206,7 @@ public class DocumentDbTableSchemaGeneratorHelper {
196206
.put(new SimpleEntry<>(JdbcType.VARCHAR, BsonType.DOUBLE), JdbcType.VARCHAR)
197207
.put(new SimpleEntry<>(JdbcType.VARCHAR, BsonType.INT32), JdbcType.VARCHAR)
198208
.put(new SimpleEntry<>(JdbcType.VARCHAR, BsonType.INT64), JdbcType.VARCHAR)
209+
.put(new SimpleEntry<>(JdbcType.VARCHAR, BsonType.TIMESTAMP), JdbcType.VARCHAR)
199210
.put(new SimpleEntry<>(JdbcType.VARCHAR, BsonType.MAX_KEY), JdbcType.VARCHAR)
200211
.put(new SimpleEntry<>(JdbcType.VARCHAR, BsonType.MIN_KEY), JdbcType.VARCHAR)
201212
.put(new SimpleEntry<>(JdbcType.VARCHAR, BsonType.NULL), JdbcType.VARCHAR)
Loading
Loading

0 commit comments

Comments
 (0)