From a1bbf043559e632f09e798c1fdec4dad7612f38a Mon Sep 17 00:00:00 2001 From: Andrey Kuzin Date: Tue, 2 May 2023 15:21:49 +0000 Subject: [PATCH 1/4] Adding Struct Support Signed-off-by: Andrey Kuzin --- .../org/opensearch/jdbc/ConnectionImpl.java | 3 +- .../java/org/opensearch/jdbc/StructImpl.java | 33 +++++++++++++++ .../jdbc/types/BaseTypeConverter.java | 4 ++ .../opensearch/jdbc/types/OpenSearchType.java | 3 ++ .../org/opensearch/jdbc/types/StructType.java | 42 +++++++++++++++++++ .../opensearch/jdbc/types/TypeConverters.java | 26 ++++++++++++ 6 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/opensearch/jdbc/StructImpl.java create mode 100644 src/main/java/org/opensearch/jdbc/types/StructType.java diff --git a/src/main/java/org/opensearch/jdbc/ConnectionImpl.java b/src/main/java/org/opensearch/jdbc/ConnectionImpl.java index e6a8587..430e659 100644 --- a/src/main/java/org/opensearch/jdbc/ConnectionImpl.java +++ b/src/main/java/org/opensearch/jdbc/ConnectionImpl.java @@ -41,6 +41,7 @@ import java.sql.Savepoint; import java.sql.Statement; import java.sql.Struct; +import java.util.Arrays; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; @@ -436,7 +437,7 @@ public Array createArrayOf(String typeName, Object[] elements) throws SQLExcepti @Override public Struct createStruct(String typeName, Object[] attributes) throws SQLException { - throw new SQLFeatureNotSupportedException("Struct is not supported."); + return new StructImpl(typeName, attributes); } @Override diff --git a/src/main/java/org/opensearch/jdbc/StructImpl.java b/src/main/java/org/opensearch/jdbc/StructImpl.java new file mode 100644 index 0000000..538d768 --- /dev/null +++ b/src/main/java/org/opensearch/jdbc/StructImpl.java @@ -0,0 +1,33 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + + +package org.opensearch.jdbc; + +import java.sql.SQLException; +import java.sql.Struct; +import java.util.Map; + +public class StructImpl implements Struct{ + private final String typeName; + private final Object[] attributes; + + public StructImpl(String typeName, Object[] attributes) { + this.typeName = typeName; + this.attributes = attributes; + } + + public String getSQLTypeName() throws SQLException { + return this.typeName; + } + + public Object[] getAttributes() throws SQLException { + return attributes; + } + + public Object[] getAttributes(Map> map) throws SQLException { + throw new java.lang.UnsupportedOperationException("Not supported yet."); + } +} diff --git a/src/main/java/org/opensearch/jdbc/types/BaseTypeConverter.java b/src/main/java/org/opensearch/jdbc/types/BaseTypeConverter.java index bbc114a..abf2b50 100644 --- a/src/main/java/org/opensearch/jdbc/types/BaseTypeConverter.java +++ b/src/main/java/org/opensearch/jdbc/types/BaseTypeConverter.java @@ -8,6 +8,7 @@ import java.sql.Date; import java.sql.SQLException; +import java.sql.Struct; import java.sql.Time; import java.sql.Timestamp; import java.util.HashMap; @@ -35,6 +36,9 @@ public abstract class BaseTypeConverter implements TypeConverter { typeHandlerMap.put(Date.class, DateType.INSTANCE); typeHandlerMap.put(Time.class, TimeType.INSTANCE); + // Adding Struct Support + typeHandlerMap.put(Struct.class, StructType.INSTANCE); + } @Override diff --git a/src/main/java/org/opensearch/jdbc/types/OpenSearchType.java b/src/main/java/org/opensearch/jdbc/types/OpenSearchType.java index e7329a7..4d4ee2c 100644 --- a/src/main/java/org/opensearch/jdbc/types/OpenSearchType.java +++ b/src/main/java/org/opensearch/jdbc/types/OpenSearchType.java @@ -90,6 +90,9 @@ public enum OpenSearchType { jdbcTypeToOpenSearchTypeMap.put(JDBCType.TIME, TIME); jdbcTypeToOpenSearchTypeMap.put(JDBCType.DATE, DATE); jdbcTypeToOpenSearchTypeMap.put(JDBCType.VARBINARY, BINARY); + + // Adding Struct Support + jdbcTypeToOpenSearchTypeMap.put(JDBCType.STRUCT, OBJECT); } /** diff --git a/src/main/java/org/opensearch/jdbc/types/StructType.java b/src/main/java/org/opensearch/jdbc/types/StructType.java new file mode 100644 index 0000000..cadd58c --- /dev/null +++ b/src/main/java/org/opensearch/jdbc/types/StructType.java @@ -0,0 +1,42 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +package org.opensearch.jdbc.types; + +import java.sql.Struct; +import java.util.Map; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.LinkedHashMap; + + + +import org.opensearch.jdbc.StructImpl; + +public class StructType implements TypeHelper { + + public static final StructType INSTANCE = new StructType(); + + private StructType() { + + } + + @Override + public String getTypeName() { + return "Struct"; + } + + @Override + public Struct fromValue(Object value, Map conversionParams) { + int i = 0; + Map structKeyValues = (Map) value; + Object[] attributes = new Map.Entry[structKeyValues.size()]; + + for(Map.Entry entry: structKeyValues.entrySet()) { + attributes[i++] = entry; + } + return new StructImpl(getTypeName(), attributes); + } + } diff --git a/src/main/java/org/opensearch/jdbc/types/TypeConverters.java b/src/main/java/org/opensearch/jdbc/types/TypeConverters.java index 189e970..9b78ac4 100644 --- a/src/main/java/org/opensearch/jdbc/types/TypeConverters.java +++ b/src/main/java/org/opensearch/jdbc/types/TypeConverters.java @@ -9,6 +9,7 @@ import java.sql.Date; import java.sql.JDBCType; import java.sql.SQLException; +import java.sql.Struct; import java.sql.Time; import java.sql.Timestamp; import java.util.Arrays; @@ -56,12 +57,37 @@ public class TypeConverters { tcMap.put(JDBCType.BINARY, new BinaryTypeConverter()); tcMap.put(JDBCType.NULL, new NullTypeConverter()); + + // Adding Struct Support + tcMap.put(JDBCType.STRUCT, new StructTypeConverter()); } public static TypeConverter getInstance(JDBCType jdbcType) { return tcMap.get(jdbcType); } + public static class StructTypeConverter extends BaseTypeConverter { + + private static final Set supportedJavaClasses = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList( + Struct.class + ))); + + StructTypeConverter() { + + } + + @Override + public Class getDefaultJavaClass() { + return Struct.class; + } + + @Override + public Set getSupportedJavaClasses() { + return supportedJavaClasses; + } + } + public static class TimestampTypeConverter extends BaseTypeConverter { private static final Set supportedJavaClasses = Collections.unmodifiableSet( From a58060b57160f78f5fd02c050054f192cee84f78 Mon Sep 17 00:00:00 2001 From: Andrey Kuzin Date: Thu, 4 May 2023 21:08:35 +0000 Subject: [PATCH 2/4] Added unit tests for Struct type Signed-off-by: Andrey Kuzin --- .../java/org/opensearch/jdbc/StructImpl.java | 23 ++++++++++ .../org/opensearch/jdbc/types/StructType.java | 12 +++-- .../opensearch/jdbc/types/TypeConverters.java | 5 +-- .../org/opensearch/jdbc/ResultSetTests.java | 32 +++++++++++++ .../jdbc/types/StructTypeTests.java | 45 +++++++++++++++++++ .../json/queryresponse_nullablefields.json | 33 +++++++++++++- 6 files changed, 137 insertions(+), 13 deletions(-) create mode 100644 src/test/java/org/opensearch/jdbc/types/StructTypeTests.java diff --git a/src/main/java/org/opensearch/jdbc/StructImpl.java b/src/main/java/org/opensearch/jdbc/StructImpl.java index 538d768..25dd96c 100644 --- a/src/main/java/org/opensearch/jdbc/StructImpl.java +++ b/src/main/java/org/opensearch/jdbc/StructImpl.java @@ -8,7 +8,9 @@ import java.sql.SQLException; import java.sql.Struct; +import java.util.Arrays; import java.util.Map; +import java.util.List; public class StructImpl implements Struct{ private final String typeName; @@ -30,4 +32,25 @@ public Object[] getAttributes() throws SQLException { public Object[] getAttributes(Map> map) throws SQLException { throw new java.lang.UnsupportedOperationException("Not supported yet."); } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Struct)) { + return false; + } + if (obj == this) { + return true; + } + Struct other = (Struct) obj; + try { + if (!typeName.equals(other.getSQLTypeName())) { + return false; + } + List otherAttributes = Arrays.asList(other.getAttributes()); + return otherAttributes.containsAll(Arrays.asList(attributes)); + } + catch (Exception e) { + return false; + } + } } diff --git a/src/main/java/org/opensearch/jdbc/types/StructType.java b/src/main/java/org/opensearch/jdbc/types/StructType.java index cadd58c..d090e3f 100644 --- a/src/main/java/org/opensearch/jdbc/types/StructType.java +++ b/src/main/java/org/opensearch/jdbc/types/StructType.java @@ -10,6 +10,7 @@ import java.util.Arrays; import java.util.List; import java.util.LinkedHashMap; +import java.util.Collections; @@ -30,13 +31,10 @@ public String getTypeName() { @Override public Struct fromValue(Object value, Map conversionParams) { - int i = 0; - Map structKeyValues = (Map) value; - Object[] attributes = new Map.Entry[structKeyValues.size()]; - - for(Map.Entry entry: structKeyValues.entrySet()) { - attributes[i++] = entry; + if (value == null) { + return null; } - return new StructImpl(getTypeName(), attributes); + Map structKeyValues = (Map) value; + return new StructImpl(getTypeName(), structKeyValues.entrySet().toArray()); } } diff --git a/src/main/java/org/opensearch/jdbc/types/TypeConverters.java b/src/main/java/org/opensearch/jdbc/types/TypeConverters.java index 9b78ac4..847e264 100644 --- a/src/main/java/org/opensearch/jdbc/types/TypeConverters.java +++ b/src/main/java/org/opensearch/jdbc/types/TypeConverters.java @@ -68,10 +68,7 @@ public static TypeConverter getInstance(JDBCType jdbcType) { public static class StructTypeConverter extends BaseTypeConverter { - private static final Set supportedJavaClasses = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList( - Struct.class - ))); + private static final Set supportedJavaClasses = Collections.singleton(Struct.class); StructTypeConverter() { diff --git a/src/test/java/org/opensearch/jdbc/ResultSetTests.java b/src/test/java/org/opensearch/jdbc/ResultSetTests.java index 0ef1753..60733db 100644 --- a/src/test/java/org/opensearch/jdbc/ResultSetTests.java +++ b/src/test/java/org/opensearch/jdbc/ResultSetTests.java @@ -13,6 +13,7 @@ import org.opensearch.jdbc.test.TestResources; import org.opensearch.jdbc.test.mocks.MockOpenSearch; import org.opensearch.jdbc.types.OpenSearchType; +import org.opensearch.jdbc.types.StructType; import org.opensearch.jdbc.test.PerTestWireMockServerExtension; import org.opensearch.jdbc.test.WireMockServerHelpers; import org.opensearch.jdbc.test.mocks.MockResultSet; @@ -32,6 +33,8 @@ import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; import java.util.stream.Stream; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; @@ -176,6 +179,18 @@ void testNullableFieldsQuery(WireMockServer mockServer) throws SQLException, IOE Statement st = con.createStatement(); ResultSet rs = assertDoesNotThrow(() -> st.executeQuery(queryMock.getSql())); + Map attributes = new HashMap() {{ + put("attribute1", "value1"); + put("attribute2", 2); + put("attribute3", 15.0); + }}; + + Map nestedAttributes = new HashMap() {{ + put("struct", attributes); + put("string", "hello"); + put("int", 1); + }}; + assertNotNull(rs); MockResultSetMetaData mockResultSetMetaData = MockResultSetMetaData.builder() @@ -191,6 +206,7 @@ void testNullableFieldsQuery(WireMockServer mockServer) throws SQLException, IOE .column("testKeyword", OpenSearchType.KEYWORD) .column("testText", OpenSearchType.TEXT) .column("testDouble", OpenSearchType.DOUBLE) + .column("testStruct", OpenSearchType.OBJECT) .build(); MockResultSetRows mockResultSetRows = MockResultSetRows.builder() @@ -207,6 +223,21 @@ void testNullableFieldsQuery(WireMockServer mockServer) throws SQLException, IOE .column("Test String", false) .column("document3", false) .column((double) 0, true) + .column(StructType.INSTANCE.fromValue(attributes, null), false) + .row() + .column(true, false) + .column("1", false) + .column((byte) 126, false) + .column((float) 0, true) + .column((long) 32000320003200030L, false) + .column((short) 29000, false) + .column((float) 0, true) + .column(null, true) + .column((double) 0, true) + .column(null, true) + .column(null, true) + .column((double) 22.312423148903218, false) + .column(null, true) .row() .column(true, false) .column("1", false) @@ -220,6 +251,7 @@ void testNullableFieldsQuery(WireMockServer mockServer) throws SQLException, IOE .column(null, true) .column(null, true) .column((double) 22.312423148903218, false) + .column(StructType.INSTANCE.fromValue(nestedAttributes, null), false) .build(); MockResultSet mockResultSet = new MockResultSet(mockResultSetMetaData, mockResultSetRows); diff --git a/src/test/java/org/opensearch/jdbc/types/StructTypeTests.java b/src/test/java/org/opensearch/jdbc/types/StructTypeTests.java new file mode 100644 index 0000000..a7f189c --- /dev/null +++ b/src/test/java/org/opensearch/jdbc/types/StructTypeTests.java @@ -0,0 +1,45 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + + + package org.opensearch.jdbc.types; + + import static org.junit.jupiter.api.Assertions.*; + import org.junit.jupiter.api.Test; + import org.opensearch.jdbc.types.StructType; + import org.opensearch.jdbc.StructImpl; + + import java.sql.Struct; + import java.sql.SQLException; + import java.util.Arrays; + import java.util.Map; + import java.util.HashMap; + + public class StructTypeTests { + + @Test + public void testStructTypeFromValue() throws SQLException { + Map attributes = new HashMap() {{ + put("attribute1", "value1"); + put("attribute2", 2); + put("attribute3", 15.0); + }}; + + Struct actualStruct = StructType.INSTANCE.fromValue(attributes, null); + assertTrue(Arrays.equals(actualStruct.getAttributes(), attributes.entrySet().toArray())); + assertEquals(actualStruct.getAttributes().length, 3); + assertEquals(actualStruct, new StructImpl(StructType.INSTANCE.getTypeName(), attributes.entrySet().toArray())); + + Map nestedAttributes = new HashMap() {{ + put("struct", attributes); + put("string", "hello"); + put("int", 1); + }}; + + Struct actualNestedStruct = StructType.INSTANCE.fromValue(nestedAttributes, null); + assertTrue(Arrays.equals(actualNestedStruct.getAttributes(), nestedAttributes.entrySet().toArray())); + assertEquals(actualNestedStruct, new StructImpl(StructType.INSTANCE.getTypeName(), nestedAttributes.entrySet().toArray())); + } + } diff --git a/src/test/resources/mock/protocol/json/queryresponse_nullablefields.json b/src/test/resources/mock/protocol/json/queryresponse_nullablefields.json index 1068e6c..932ab8d 100644 --- a/src/test/resources/mock/protocol/json/queryresponse_nullablefields.json +++ b/src/test/resources/mock/protocol/json/queryresponse_nullablefields.json @@ -47,6 +47,10 @@ { "name": "testDouble", "type": "double" + }, + { + "name": "testStruct", + "type": "object" } ], "total": 2, @@ -63,6 +67,26 @@ 24.324234543532153, "Test String", "document3", + null, + { + "attribute1": "value1", + "attribute2": 2, + "attribute3": 15.0 + } + ], + [ + true, + "1", + 126, + null, + 32000320003200030, + 29000, + null, + null, + null, + null, + null, + 22.312423148903218, null ], [ @@ -77,9 +101,14 @@ null, null, null, - 22.312423148903218 + 22.312423148903218, + { + "struct": {"attribute1": "value1", "attribute2": 2, "attribute3": 15.0}, + "string": "hello", + "int": 1 + } ] ], "size": 2, "status": 200 -} \ No newline at end of file +} From edc3f47a301e8b2873e8ca117bce0801d7272a6e Mon Sep 17 00:00:00 2001 From: Andrey Kuzin Date: Fri, 5 May 2023 21:49:42 +0000 Subject: [PATCH 3/4] Minor refactoring changes Signed-off-by: Andrey Kuzin --- src/main/java/org/opensearch/jdbc/ConnectionImpl.java | 1 - src/main/java/org/opensearch/jdbc/StructImpl.java | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opensearch/jdbc/ConnectionImpl.java b/src/main/java/org/opensearch/jdbc/ConnectionImpl.java index 430e659..6ad7e47 100644 --- a/src/main/java/org/opensearch/jdbc/ConnectionImpl.java +++ b/src/main/java/org/opensearch/jdbc/ConnectionImpl.java @@ -41,7 +41,6 @@ import java.sql.Savepoint; import java.sql.Statement; import java.sql.Struct; -import java.util.Arrays; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; diff --git a/src/main/java/org/opensearch/jdbc/StructImpl.java b/src/main/java/org/opensearch/jdbc/StructImpl.java index 25dd96c..0ed0b57 100644 --- a/src/main/java/org/opensearch/jdbc/StructImpl.java +++ b/src/main/java/org/opensearch/jdbc/StructImpl.java @@ -12,7 +12,7 @@ import java.util.Map; import java.util.List; -public class StructImpl implements Struct{ +public class StructImpl implements Struct { private final String typeName; private final Object[] attributes; @@ -49,7 +49,7 @@ public boolean equals(Object obj) { List otherAttributes = Arrays.asList(other.getAttributes()); return otherAttributes.containsAll(Arrays.asList(attributes)); } - catch (Exception e) { + catch (SQLException e) { return false; } } From bd51760da0bd951a49275e20f68638a78d26d76d Mon Sep 17 00:00:00 2001 From: Andrey Kuzin Date: Mon, 8 May 2023 21:16:54 +0000 Subject: [PATCH 4/4] Added Java docs for StructImpl Signed-off-by: Andrey Kuzin --- .../java/org/opensearch/jdbc/StructImpl.java | 44 ++++++++++++++++++- .../jdbc/types/BaseTypeConverter.java | 1 - .../opensearch/jdbc/types/OpenSearchType.java | 2 - .../org/opensearch/jdbc/types/StructType.java | 2 +- .../jdbc/types/StructTypeTests.java | 12 +++++ 5 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/opensearch/jdbc/StructImpl.java b/src/main/java/org/opensearch/jdbc/StructImpl.java index 0ed0b57..6ae2831 100644 --- a/src/main/java/org/opensearch/jdbc/StructImpl.java +++ b/src/main/java/org/opensearch/jdbc/StructImpl.java @@ -12,27 +12,69 @@ import java.util.Map; import java.util.List; + +/** + * This class implements the {@link java.sql.Struct} interface. + *

+ * {@code StructImpl} provides a simple implementation of a struct data type. + *

+ */ public class StructImpl implements Struct { private final String typeName; private final Object[] attributes; + /** + * Constructs a new {@code StructImpl} object with the specified parameter values. + * + * @param typeName the SQL type name of the struct + * @param attributes the attributes of the struct, each attribute is a {@code Map.Entry}(key-value pair) + */ public StructImpl(String typeName, Object[] attributes) { this.typeName = typeName; this.attributes = attributes; } + /** + * Returns the SQL type name of the struct. + * + * @return the SQL type name of the struct + * @throws SQLException if a database access error occurs + */ + @Override public String getSQLTypeName() throws SQLException { return this.typeName; } + /** + * Returns an array containing the attributes of the struct. + * + * @return an array containing the attribute values of the struct + * @throws SQLException if a database access error occurs + */ + @Override public Object[] getAttributes() throws SQLException { return attributes; } + /** + * @throws java.lang.UnsupportedOperationException because functionality is not supported yet + */ + @Override public Object[] getAttributes(Map> map) throws SQLException { throw new java.lang.UnsupportedOperationException("Not supported yet."); } + /** + * Compares this StructImpl object with the specified object for equality. + * + *

+ * Two StructImpl objects are considered equal if they have the same typeName, same number of attributes, + * and contain the same attributes. + *

+ * + * @param obj the object to compare with this StructImpl object for equality. + * @return {@code true} if the specified object is equal to this StructImpl object, {@code false} otherwise. + */ @Override public boolean equals(Object obj) { if (!(obj instanceof Struct)) { @@ -43,7 +85,7 @@ public boolean equals(Object obj) { } Struct other = (Struct) obj; try { - if (!typeName.equals(other.getSQLTypeName())) { + if (!typeName.equals(other.getSQLTypeName()) || attributes.length != other.getAttributes().length) { return false; } List otherAttributes = Arrays.asList(other.getAttributes()); diff --git a/src/main/java/org/opensearch/jdbc/types/BaseTypeConverter.java b/src/main/java/org/opensearch/jdbc/types/BaseTypeConverter.java index abf2b50..7e286ed 100644 --- a/src/main/java/org/opensearch/jdbc/types/BaseTypeConverter.java +++ b/src/main/java/org/opensearch/jdbc/types/BaseTypeConverter.java @@ -36,7 +36,6 @@ public abstract class BaseTypeConverter implements TypeConverter { typeHandlerMap.put(Date.class, DateType.INSTANCE); typeHandlerMap.put(Time.class, TimeType.INSTANCE); - // Adding Struct Support typeHandlerMap.put(Struct.class, StructType.INSTANCE); } diff --git a/src/main/java/org/opensearch/jdbc/types/OpenSearchType.java b/src/main/java/org/opensearch/jdbc/types/OpenSearchType.java index 4d4ee2c..36f6623 100644 --- a/src/main/java/org/opensearch/jdbc/types/OpenSearchType.java +++ b/src/main/java/org/opensearch/jdbc/types/OpenSearchType.java @@ -90,8 +90,6 @@ public enum OpenSearchType { jdbcTypeToOpenSearchTypeMap.put(JDBCType.TIME, TIME); jdbcTypeToOpenSearchTypeMap.put(JDBCType.DATE, DATE); jdbcTypeToOpenSearchTypeMap.put(JDBCType.VARBINARY, BINARY); - - // Adding Struct Support jdbcTypeToOpenSearchTypeMap.put(JDBCType.STRUCT, OBJECT); } diff --git a/src/main/java/org/opensearch/jdbc/types/StructType.java b/src/main/java/org/opensearch/jdbc/types/StructType.java index d090e3f..faee44b 100644 --- a/src/main/java/org/opensearch/jdbc/types/StructType.java +++ b/src/main/java/org/opensearch/jdbc/types/StructType.java @@ -31,7 +31,7 @@ public String getTypeName() { @Override public Struct fromValue(Object value, Map conversionParams) { - if (value == null) { + if (value == null || !(value instanceof Map)) { return null; } Map structKeyValues = (Map) value; diff --git a/src/test/java/org/opensearch/jdbc/types/StructTypeTests.java b/src/test/java/org/opensearch/jdbc/types/StructTypeTests.java index a7f189c..6280358 100644 --- a/src/test/java/org/opensearch/jdbc/types/StructTypeTests.java +++ b/src/test/java/org/opensearch/jdbc/types/StructTypeTests.java @@ -27,10 +27,21 @@ public void testStructTypeFromValue() throws SQLException { put("attribute3", 15.0); }}; + Map attributes2 = new HashMap() {{ + put("attribute1", "value1"); + put("attribute2", 2); + put("attribute3", 15.0); + put("attribute4", "value4"); + }}; + Struct actualStruct = StructType.INSTANCE.fromValue(attributes, null); + Struct actualStruct2 = StructType.INSTANCE.fromValue(attributes2, null); + assertTrue(Arrays.equals(actualStruct.getAttributes(), attributes.entrySet().toArray())); assertEquals(actualStruct.getAttributes().length, 3); + assertEquals(actualStruct2.getAttributes().length, 4); assertEquals(actualStruct, new StructImpl(StructType.INSTANCE.getTypeName(), attributes.entrySet().toArray())); + assertNotEquals(actualStruct, actualStruct2); Map nestedAttributes = new HashMap() {{ put("struct", attributes); @@ -41,5 +52,6 @@ public void testStructTypeFromValue() throws SQLException { Struct actualNestedStruct = StructType.INSTANCE.fromValue(nestedAttributes, null); assertTrue(Arrays.equals(actualNestedStruct.getAttributes(), nestedAttributes.entrySet().toArray())); assertEquals(actualNestedStruct, new StructImpl(StructType.INSTANCE.getTypeName(), nestedAttributes.entrySet().toArray())); + assertNotEquals(actualStruct, actualNestedStruct); } }