|
26 | 26 | import io.trino.plugin.jdbc.BaseJdbcClient; |
27 | 27 | import io.trino.plugin.jdbc.BaseJdbcConfig; |
28 | 28 | import io.trino.plugin.jdbc.BooleanWriteFunction; |
| 29 | +import io.trino.plugin.jdbc.CaseSensitivity; |
29 | 30 | import io.trino.plugin.jdbc.ColumnMapping; |
30 | 31 | import io.trino.plugin.jdbc.ConnectionFactory; |
31 | 32 | import io.trino.plugin.jdbc.DoubleWriteFunction; |
|
81 | 82 | import java.sql.DatabaseMetaData; |
82 | 83 | import java.sql.PreparedStatement; |
83 | 84 | import java.sql.ResultSet; |
| 85 | +import java.sql.ResultSetMetaData; |
84 | 86 | import java.sql.SQLException; |
85 | 87 | import java.sql.Types; |
86 | 88 | import java.time.Instant; |
|
108 | 110 | import static com.google.common.base.Verify.verify; |
109 | 111 | import static io.airlift.slice.Slices.utf8Slice; |
110 | 112 | import static io.airlift.slice.Slices.wrappedBuffer; |
| 113 | +import static io.trino.plugin.jdbc.CaseSensitivity.CASE_INSENSITIVE; |
| 114 | +import static io.trino.plugin.jdbc.CaseSensitivity.CASE_SENSITIVE; |
111 | 115 | import static io.trino.plugin.jdbc.JdbcErrorCode.JDBC_ERROR; |
112 | 116 | import static io.trino.plugin.jdbc.PredicatePushdownController.DISABLE_PUSHDOWN; |
113 | 117 | import static io.trino.plugin.jdbc.PredicatePushdownController.FULL_PUSHDOWN; |
|
162 | 166 | import static java.lang.Math.toIntExact; |
163 | 167 | import static java.lang.String.format; |
164 | 168 | import static java.lang.String.join; |
| 169 | +import static java.sql.Types.FLOAT; |
165 | 170 | import static java.util.Locale.ENGLISH; |
166 | 171 | import static java.util.concurrent.TimeUnit.DAYS; |
167 | 172 |
|
@@ -393,6 +398,67 @@ public Optional<String> getTableComment(ResultSet resultSet) |
393 | 398 | return Optional.ofNullable(emptyToNull(resultSet.getString("REMARKS"))); |
394 | 399 | } |
395 | 400 |
|
| 401 | + @Override |
| 402 | + protected List<JdbcColumnHandle> getColumns(ConnectorSession session, Connection connection, ResultSetMetaData metadata) |
| 403 | + throws SQLException |
| 404 | + { |
| 405 | + ImmutableList.Builder<JdbcColumnHandle> columns = ImmutableList.builder(); |
| 406 | + for (int column = 1; column <= metadata.getColumnCount(); column++) { |
| 407 | + JdbcTypeHandle jdbcTypeHandle = getJdbcTypeHandle(metadata, column); |
| 408 | + |
| 409 | + // Use getColumnLabel method because query pass-through table function may contain column aliases |
| 410 | + String name = metadata.getColumnLabel(column); |
| 411 | + Type type = toColumnMapping(session, connection, jdbcTypeHandle) |
| 412 | + .orElseThrow(() -> new UnsupportedOperationException(format("Unsupported type: %s of column: %s", jdbcTypeHandle, name))) |
| 413 | + .getType(); |
| 414 | + columns.add(new JdbcColumnHandle(name, jdbcTypeHandle, type)); |
| 415 | + } |
| 416 | + return columns.build(); |
| 417 | + } |
| 418 | + |
| 419 | + private static JdbcTypeHandle getJdbcTypeHandle(ResultSetMetaData metadata, int column) |
| 420 | + throws SQLException |
| 421 | + { |
| 422 | + int columnType = metadata.getColumnType(column); |
| 423 | + int columnSize = metadata.getPrecision(column); |
| 424 | + int scale = metadata.getScale(column); |
| 425 | + CaseSensitivity caseSensitive = metadata.isCaseSensitive(column) ? CASE_SENSITIVE : CASE_INSENSITIVE; |
| 426 | + if (columnType == Types.NUMERIC) { |
| 427 | + String columnClassName = metadata.getColumnClassName(column); |
| 428 | + int precision = columnSize + max(-scale, 0); |
| 429 | + // Oracle NUMBER are expected "java.math.BigDecimal", when the class name is "java.lang.Double" it means |
| 430 | + // it's actual a FLOAT type in Oracle side |
| 431 | + if ("java.lang.Double".equals(columnClassName)) { |
| 432 | + // we just handle the precision here is not able to handle by `toColumnMapping` method |
| 433 | + if (!isAllowedNumber(precision, columnSize)) { |
| 434 | + return new JdbcTypeHandle(FLOAT, Optional.of("FLOAT"), Optional.of(columnSize), Optional.of(scale), Optional.empty(), Optional.of(caseSensitive)); |
| 435 | + } |
| 436 | + } |
| 437 | + else { |
| 438 | + verify("java.math.BigDecimal".equals(columnClassName), "Unexpected class name %s for Oracle NUMBER type", columnClassName); |
| 439 | + } |
| 440 | + } |
| 441 | + |
| 442 | + return new JdbcTypeHandle( |
| 443 | + columnType, |
| 444 | + Optional.ofNullable(metadata.getColumnTypeName(column)), |
| 445 | + Optional.of(columnSize), |
| 446 | + Optional.of(scale), |
| 447 | + Optional.empty(), // TODO support arrays |
| 448 | + Optional.of(caseSensitive)); |
| 449 | + } |
| 450 | + |
| 451 | + private static boolean isAllowedNumber(int precision, int columnSize) |
| 452 | + { |
| 453 | + if (precision > Decimals.MAX_PRECISION) { |
| 454 | + return false; |
| 455 | + } |
| 456 | + if (columnSize <= 0) { |
| 457 | + return false; |
| 458 | + } |
| 459 | + return true; |
| 460 | + } |
| 461 | + |
396 | 462 | // Iterates over filtered schemas to fetch column details per schema, preventing unnecessary columns for Oracle's internal schemas |
397 | 463 | @Override |
398 | 464 | public Iterator<RelationColumnsMetadata> getAllTableColumns(ConnectorSession session, Optional<String> schema) |
@@ -515,7 +581,7 @@ else if (numberDefaultScale.isPresent() && precision == PRECISION_OF_UNSPECIFIED |
515 | 581 | precision = Decimals.MAX_PRECISION; |
516 | 582 | scale = numberDefaultScale.get(); |
517 | 583 | } |
518 | | - else if (precision > Decimals.MAX_PRECISION || actualPrecision <= 0) { |
| 584 | + else if (!isAllowedNumber(precision, actualPrecision)) { |
519 | 585 | break; |
520 | 586 | } |
521 | 587 | DecimalType decimalType = createDecimalType(precision, scale); |
|
0 commit comments