Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,97 @@ private Object convertPrimitive(String text, String type) {
return text;
}
}

/**
* Formats a complex type JSON string into a consistent string format. This is primarily used when
* complex datatype support is disabled.
*
* @param jsonString The JSON string representation of the complex type
* @param complexType The type of complex data (MAP, ARRAY, STRUCT)
* @param typeMetadata The metadata for the type (e.g., "MAP<INT,INT>")
* @return A consistently formatted string representation
*/
public String formatComplexTypeString(
String jsonString, String complexType, String typeMetadata) {
if (jsonString == null || complexType == null) {
return jsonString;
}

try {
if (complexType.equals(DatabricksTypeUtil.MAP)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not for this PR but could there be other types where we need to format them in a different format than JSON?

Comment thread
madhav-db marked this conversation as resolved.
return formatMapString(jsonString, typeMetadata);
}
} catch (Exception e) {
LOGGER.warn("Failed to format complex type representation: {}", e.getMessage());
}

return jsonString;
}

/**
* Formats a map JSON string into the standard {key:value} format.
*
* @param jsonString The JSON string representation of the map
* @param mapMetadata The metadata for the map type (e.g., "MAP<INT,INT>")
* @return A map string in the format {key:value,key:value}
*/
public String formatMapString(String jsonString, String mapMetadata) {
try {
JsonNode node = JsonUtil.getMapper().readTree(jsonString);
if (node.isArray() && node.size() > 0 && node.get(0).has("key")) {
String[] kv = new String[] {"STRING", "STRING"};
if (mapMetadata != null && mapMetadata.startsWith(DatabricksTypeUtil.MAP)) {
kv = MetadataParser.parseMapMetadata(mapMetadata).split(",", 2);
}

String keyType = kv[0].trim();
String valueType = kv[1].trim();
Comment thread
madhav-db marked this conversation as resolved.
boolean isStringKey = keyType.equalsIgnoreCase(DatabricksTypeUtil.STRING);
Comment thread
madhav-db marked this conversation as resolved.
boolean isStringValue = valueType.equalsIgnoreCase(DatabricksTypeUtil.STRING);

StringBuilder result = new StringBuilder("{");

for (int i = 0; i < node.size(); i++) {
JsonNode entry = node.get(i);
JsonNode keyNode = entry.get("key");
Comment thread
madhav-db marked this conversation as resolved.
JsonNode valueNode = entry.get("value");
Comment thread
madhav-db marked this conversation as resolved.

// Throw error if keyNode is null
if (keyNode == null || keyNode.isNull()) {
throw new DatabricksParsingException(
"Map entry found with null key in JSON: " + entry.toString(),
DatabricksDriverErrorCode.JSON_PARSING_ERROR);
}

if (i > 0) {
result.append(",");
}

if (isStringKey) {
result.append("\"").append(keyNode.asText()).append("\"");
} else {
result.append(keyNode.asText());
}

result.append(":");

// Handle null valueNode
if (valueNode == null || valueNode.isNull()) {
result.append("null");
} else if (isStringValue) {
result.append("\"").append(valueNode.asText()).append("\"");
} else {
result.append(valueNode.asText());
}
}

result.append("}");
return result.toString();
}
} catch (Exception e) {
LOGGER.warn("Failed to format map representation: {}", e.getMessage());
}

return jsonString;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static com.databricks.jdbc.common.util.DatabricksThriftUtil.getTypeFromTypeDesc;

import com.databricks.jdbc.api.impl.ComplexDataTypeParser;
import com.databricks.jdbc.api.impl.IExecutionResult;
import com.databricks.jdbc.api.internal.IDatabricksSession;
import com.databricks.jdbc.api.internal.IDatabricksStatementInternal;
Expand Down Expand Up @@ -138,8 +139,12 @@ public Object getObject(int columnIndex) throws DatabricksSQLException {
this.session.getConnectionContext().isComplexDatatypeSupportEnabled();
if (!isComplexDatatypeSupportEnabled && isComplexType(requiredType)) {
LOGGER.debug("Complex datatype support is disabled, converting complex type to STRING");
requiredType = ColumnInfoTypeName.STRING;
arrowMetadata = "STRING";

Object result =
chunkIterator.getColumnObjectAtCurrentRow(
columnIndex, ColumnInfoTypeName.STRING, "STRING");
ComplexDataTypeParser parser = new ComplexDataTypeParser();
return parser.formatComplexTypeString(result.toString(), requiredType.name(), arrowMetadata);
}

return chunkIterator.getColumnObjectAtCurrentRow(columnIndex, requiredType, arrowMetadata);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,48 @@ void testComplexPrimitiveConversions() throws DatabricksParsingException {
fail("Should not throw: " + e.getMessage());
}
}

@Test
void testFormatComplexTypeString_withMapType() {
String jsonString = "[{\"key\":1,\"value\":2},{\"key\":3,\"value\":4}]";
String expected = "{1:2,3:4}";

String result = parser.formatComplexTypeString(jsonString, "MAP", "MAP<INT,INT>");
assertEquals(expected, result);
}

@Test
void testFormatComplexTypeString_withNonMapType() {
String jsonString = "[1,2,3]";

String result = parser.formatComplexTypeString(jsonString, "ARRAY", "ARRAY<INT>");
assertEquals(jsonString, result);
}

@Test
void testFormatMapString_withIntKeyAndValue() throws DatabricksParsingException {
String jsonString = "[{\"key\":1,\"value\":2},{\"key\":3,\"value\":4}]";
String expected = "{1:2,3:4}";

String result = parser.formatMapString(jsonString, "MAP<INT,INT>");
assertEquals(expected, result);
}

@Test
void testFormatMapString_withStringKeyAndValue() throws DatabricksParsingException {
String jsonString = "[{\"key\":\"a\",\"value\":\"b\"},{\"key\":\"c\",\"value\":\"d\"}]";
String expected = "{\"a\":\"b\",\"c\":\"d\"}";

String result = parser.formatMapString(jsonString, "MAP<STRING,STRING>");
assertEquals(expected, result);
}

@Test
void testFormatMapString_withMixedTypes() throws DatabricksParsingException {
String jsonString = "[{\"key\":\"a\",\"value\":100},{\"key\":\"b\",\"value\":200}]";
String expected = "{\"a\":100,\"b\":200}";

String result = parser.formatMapString(jsonString, "MAP<STRING,INT>");
assertEquals(expected, result);
}
}
Loading