Skip to content

Commit e5c01b5

Browse files
authored
Fixed scale column for the getColumns function to match the simba output. (#989)
## Description <!-- Provide a brief summary of the changes made and the issue they aim to address.--> Fixed the DECIMAL_DIGITS column to return non zero values for timestamp, decimal, numeric datatype. For the rest of the datatypes, the value returned is zero. ## Testing <!-- Describe how the changes have been tested--> Added unit tests ## Additional Notes to the Reviewer <!-- Share any additional context or insights that may help the reviewer understand the changes better. This could include challenges faced, limitations, or compromises made during the development process. Also, mention any areas of the code that you would like the reviewer to focus on specifically. --> [PECOBLR-840](https://databricks.atlassian.net/browse/PECOBLR-840) Fixes [#960](#960) [PECOBLR-840]: https://databricks.atlassian.net/browse/PECOBLR-840?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
1 parent 43ac32b commit e5c01b5

4 files changed

Lines changed: 108 additions & 5 deletions

File tree

NEXT_CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- Fixed volume operations not completing unless the ResultSet is fully iterated.
2424
- Fixed `connection.getMetadata().getColumns()` to return the correct SQL data type code for complex type columns.
2525
- Fixed a bug in the JDBC driver's metadata parsing for nested decimal fields within struct types.
26-
- Fix case sensitive table search in `connection.getMetadata().getTables()`
26+
- Fixed case sensitive table search in `connection.getMetadata().getTables()`
27+
- Fixed `connection.getMetadata().getColumns()` to return the correct scale.
2728
---
2829
*Note: When making changes, please add your change under the appropriate section with a brief description.*

src/main/java/com/databricks/jdbc/dbclient/impl/common/MetadataResultSetBuilder.java

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static com.databricks.jdbc.common.MetadataResultConstants.*;
44
import static com.databricks.jdbc.common.util.DatabricksTypeUtil.INTERVAL;
5+
import static com.databricks.jdbc.common.util.WildcardUtil.isNullOrEmpty;
56
import static com.databricks.jdbc.dbclient.impl.common.CommandConstants.*;
67
import static com.databricks.jdbc.dbclient.impl.common.TypeValConstants.*;
78

@@ -212,8 +213,13 @@ List<List<Object>> getRows(
212213
} else {
213214
object = "NO";
214215
}
215-
} else if (mappedColumn.getColumnName().equals(DECIMAL_DIGITS_COLUMN.getColumnName())
216-
|| mappedColumn.getColumnName().equals(NUM_PREC_RADIX_COLUMN.getColumnName())) {
216+
} else if (mappedColumn
217+
.getColumnName()
218+
.equals(DECIMAL_DIGITS_COLUMN.getColumnName())) {
219+
object = getUpdatedDecimalDigits(stripBaseTypeName(typeVal), object);
220+
} else if (mappedColumn
221+
.getColumnName()
222+
.equals(NUM_PREC_RADIX_COLUMN.getColumnName())) {
217223
if (object == null) {
218224
object = 0;
219225
}
@@ -424,6 +430,37 @@ int getBufferLength(String typeVal) {
424430
return getSizeInBytes(sqlType);
425431
}
426432

433+
/**
434+
* Overrides DECIMAL_DIGITS value for specific data types. Returns non-zero only for DECIMAL,
435+
* NUMERIC, TIMESTAMP, and TIMESTAMP_NTZ.
436+
*
437+
* @param baseTypeVal the column type name
438+
* @param scaleObject the original scale value (can be null)
439+
* @return scale value for DECIMAL/NUMERIC, 9 for TIMESTAMP types, 0 otherwise
440+
* @example
441+
* <pre>
442+
* getUpdatedDecimalDigits("DECIMAL", 2) → 2
443+
* getUpdatedDecimalDigits("TIMESTAMP", 6) → 9
444+
* getUpdatedDecimalDigits("FLOAT", 7) → 0
445+
* </pre>
446+
*/
447+
int getUpdatedDecimalDigits(String baseTypeVal, Object scaleObject) {
448+
if (scaleObject == null) {
449+
return 0;
450+
}
451+
int scale = (int) scaleObject;
452+
if (isNullOrEmpty(baseTypeVal)) {
453+
return 0;
454+
}
455+
if (baseTypeVal.contains(DECIMAL_TYPE) || baseTypeVal.contains(NUMERIC_TYPE)) {
456+
return scale;
457+
}
458+
if (baseTypeVal.contains(TIMESTAMP_TYPE) || baseTypeVal.contains(TIMESTAMP_NTZ_TYPE)) {
459+
return 9;
460+
}
461+
return 0;
462+
}
463+
427464
/**
428465
* Extracts the character octet length from a given SQL type definition. For example, for input
429466
* "VARCHAR(100)", it returns 100. For inputs without a specified length or invalid inputs, it
@@ -891,8 +928,10 @@ List<List<Object>> getThriftRows(List<List<Object>> rows, List<ResultColumn> col
891928
object = "YES";
892929
}
893930
}
894-
if (column.getColumnName().equals(DECIMAL_DIGITS_COLUMN.getColumnName())
895-
|| column.getColumnName().equals(NUM_PREC_RADIX_COLUMN.getColumnName())) {
931+
if (column.getColumnName().equals(DECIMAL_DIGITS_COLUMN.getColumnName())) {
932+
object = getUpdatedDecimalDigits(stripBaseTypeName(typeVal), object);
933+
}
934+
if (column.getColumnName().equals(NUM_PREC_RADIX_COLUMN.getColumnName())) {
896935
if (object == null) {
897936
object = 0;
898937
}

src/main/java/com/databricks/jdbc/dbclient/impl/common/TypeValConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public class TypeValConstants {
88
static final String BOOLEAN_TYPE = "BOOLEAN";
99
static final String DATE_TYPE = "DATE";
1010
static final String TIMESTAMP_TYPE = "TIMESTAMP";
11+
static final String TIMESTAMP_NTZ_TYPE = "TIMESTAMP_NTZ";
1112
static final String DECIMAL_TYPE = "DECIMAL";
1213
static final String BINARY_TYPE = "BINARY";
1314
static final String ARRAY_TYPE = "ARRAY";

src/test/java/com/databricks/jdbc/dbclient/impl/common/MetadataResultSetBuilderTest.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,4 +531,66 @@ void testComplexTypesReturnActualCodesWhenSupportEnabled() throws SQLException {
531531
assertEquals(1111, variantRow.get(0));
532532
assertEquals(1111, variantRow.get(2));
533533
}
534+
535+
@Test
536+
void testDecimalDigitsColumnInGetThriftRows() {
537+
List<ResultColumn> columns = Arrays.asList(COLUMN_TYPE_COLUMN, DECIMAL_DIGITS_COLUMN);
538+
539+
List<List<Object>> rows =
540+
Arrays.asList(
541+
Arrays.asList("DECIMAL(10,2)", 2),
542+
Arrays.asList("TIMESTAMP", 6),
543+
Arrays.asList("INT", 0),
544+
Arrays.asList("VARCHAR(100)", 0),
545+
Arrays.asList("DECIMAL(15,5)", 5),
546+
Arrays.asList("TIMESTAMP_NTZ", 6),
547+
Arrays.asList("DECIMAL", 0));
548+
549+
List<List<Object>> updatedRows = metadataResultSetBuilder.getThriftRows(rows, columns);
550+
551+
assertEquals(2, updatedRows.get(0).get(1), "DECIMAL(10,2) should have scale 2");
552+
assertEquals(9, updatedRows.get(1).get(1), "TIMESTAMP should have scale 9");
553+
assertEquals(0, updatedRows.get(2).get(1), "INT should have scale 0");
554+
assertEquals(0, updatedRows.get(3).get(1), "VARCHAR should have scale 0");
555+
assertEquals(5, updatedRows.get(4).get(1), "DECIMAL(15,5) should have scale 5");
556+
assertEquals(9, updatedRows.get(5).get(1), "TIMESTAMP_NTZ should have scale 9");
557+
assertEquals(0, updatedRows.get(6).get(1), "DECIMAL should have scale 0");
558+
}
559+
560+
private static Stream<Arguments> provideDecimalDigitsArguments() {
561+
return Stream.of(
562+
Arguments.of("DECIMAL(10,2)", 2, 2, "DECIMAL(10,2) should have scale 2"),
563+
Arguments.of("TIMESTAMP", 6, 9, "TIMESTAMP should have scale 9"),
564+
Arguments.of("INT", 0, 0, "INT should have scale 0"),
565+
Arguments.of("VARCHAR(100)", 0, 0, "VARCHAR should have scale 0"),
566+
Arguments.of("DECIMAL(15,5)", 5, 5, "DECIMAL(15,5) should have scale 5"),
567+
Arguments.of("TIMESTAMP_NTZ", 6, 9, "TIMESTAMP_NTZ should have scale 9"),
568+
Arguments.of("DECIMAL", 0, 0, "DECIMAL should have scale 0"));
569+
}
570+
571+
@ParameterizedTest
572+
@MethodSource("provideDecimalDigitsArguments")
573+
void testDecimalDigitsColumnInGetRows(
574+
String typeName, int inputScale, int expectedScale, String message) throws SQLException {
575+
DatabricksResultSet resultSet = mock(DatabricksResultSet.class);
576+
when(resultSet.next()).thenReturn(true).thenReturn(false);
577+
578+
for (ResultColumn resultColumn : COLUMN_COLUMNS) {
579+
if (resultColumn.getResultSetColumnName().equals("SQLDataType")) {
580+
// Special handling begins from SQLDataType columns onward; getObject is no longer invoked.
581+
break;
582+
}
583+
when(resultSet.getObject(resultColumn.getResultSetColumnName())).thenReturn(null);
584+
}
585+
when(resultSet.getObject(IS_NULLABLE_COLUMN.getResultSetColumnName())).thenReturn("true");
586+
when(resultSet.getString(COLUMN_TYPE_COLUMN.getResultSetColumnName())).thenReturn(typeName);
587+
when(resultSet.getObject(DECIMAL_DIGITS_COLUMN.getResultSetColumnName()))
588+
.thenReturn(inputScale);
589+
590+
List<List<Object>> rows =
591+
metadataResultSetBuilder.getRows(
592+
resultSet, COLUMN_COLUMNS, new DefaultDatabricksResultSetAdapter());
593+
594+
assertEquals(expectedScale, rows.get(0).get(8), message);
595+
}
534596
}

0 commit comments

Comments
 (0)