protected ResultSetValueSelector determineValueSelector()

in solr/solrj-streaming/src/java/org/apache/solr/client/solrj/io/stream/JDBCStream.java [389:615]


  protected ResultSetValueSelector determineValueSelector(int columnIdx, ResultSetMetaData metadata)
      throws SQLException {
    final int columnNumber = columnIdx + 1; // cause it starts at 1
    // Use getColumnLabel instead of getColumnName to make sure fields renamed with AS are picked up
    // properly
    final String columnName = metadata.getColumnLabel(columnNumber);
    final int jdbcType = metadata.getColumnType(columnNumber);
    final String className = metadata.getColumnClassName(columnNumber);
    ResultSetValueSelector valueSelector = null;

    // Directly supported types can be just directly returned - no conversion really necessary
    if (directSupportedTypes.contains(className)) {
      valueSelector =
          new ResultSetValueSelector() {
            @Override
            public Object selectValue(ResultSet resultSet) throws SQLException {
              Object obj = resultSet.getObject(columnNumber);
              if (resultSet.wasNull()) {
                return null;
              }
              if (obj instanceof String s) {
                if (s.contains(sep)) {
                  s = s.substring(1);
                  return s.split(sep);
                }
              }

              return obj;
            }

            @Override
            public String getColumnName() {
              return columnName;
            }
          };
    }
    // We're checking the Java class names because there are lots of SQL types across
    // lots of database drivers that can be mapped to standard Java types. Basically,
    // this makes it easier, and we don't have to worry about esoteric type names in the
    // JDBC family of types
    else if (Short.class.getName().equals(className)) {
      valueSelector =
          new ResultSetValueSelector() {
            @Override
            public Object selectValue(ResultSet resultSet) throws SQLException {
              Short obj = resultSet.getShort(columnNumber);
              if (resultSet.wasNull()) {
                return null;
              }
              return obj.longValue();
            }

            @Override
            public String getColumnName() {
              return columnName;
            }
          };
    } else if (Integer.class.getName().equals(className)) {
      valueSelector =
          new ResultSetValueSelector() {
            @Override
            public Object selectValue(ResultSet resultSet) throws SQLException {
              Integer obj = resultSet.getInt(columnNumber);
              if (resultSet.wasNull()) {
                return null;
              }
              return obj.longValue();
            }

            @Override
            public String getColumnName() {
              return columnName;
            }
          };
    } else if (Float.class.getName().equals(className)) {
      valueSelector =
          new ResultSetValueSelector() {
            @Override
            public Object selectValue(ResultSet resultSet) throws SQLException {
              Float obj = resultSet.getFloat(columnNumber);
              if (resultSet.wasNull()) {
                return null;
              }
              return obj.doubleValue();
            }

            @Override
            public String getColumnName() {
              return columnName;
            }
          };
    }
    // Here we are switching to check against the SQL type because date/times are
    // notorious for not being consistent. We don't know if the driver is mapping
    // to a java.time.* type or some old-school type.
    else if (jdbcType == Types.DATE) {
      valueSelector =
          new ResultSetValueSelector() {
            @Override
            public Object selectValue(ResultSet resultSet) throws SQLException {
              Date sqlDate = resultSet.getDate(columnNumber);
              return resultSet.wasNull() ? null : sqlDate.toString();
            }

            @Override
            public String getColumnName() {
              return columnName;
            }
          };
    } else if (jdbcType == Types.TIME) {
      valueSelector =
          new ResultSetValueSelector() {
            @Override
            public Object selectValue(ResultSet resultSet) throws SQLException {
              Time sqlTime = resultSet.getTime(columnNumber);
              return resultSet.wasNull() ? null : sqlTime.toString();
            }

            @Override
            public String getColumnName() {
              return columnName;
            }
          };
    } else if (jdbcType == Types.TIMESTAMP) {
      valueSelector =
          new ResultSetValueSelector() {
            @Override
            public Object selectValue(ResultSet resultSet) throws SQLException {
              Timestamp sqlTimestamp = resultSet.getTimestamp(columnNumber);
              return resultSet.wasNull() ? null : sqlTimestamp.toInstant().toString();
            }

            @Override
            public String getColumnName() {
              return columnName;
            }
          };
    } else if (Object.class.getName().equals(className)) {
      // Calcite SQL type ANY comes across as generic Object (for multi-valued fields)
      valueSelector =
          new ResultSetValueSelector() {
            @Override
            public Object selectValue(ResultSet resultSet) throws SQLException {
              Object obj = resultSet.getObject(columnNumber);
              return resultSet.wasNull() ? null : obj;
            }

            @Override
            public String getColumnName() {
              return columnName;
            }
          };
    }
    // Now we're going to start seeing if things are assignable from the returned type
    // to a more general type - this allows us to cover cases where something we weren't
    // explicitly expecting, but can handle, is being returned.
    else {
      Class<?> clazz;
      try {
        clazz = Class.forName(className, false, getClass().getClassLoader());
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
      final int scale = metadata.getScale(columnNumber);
      if (Number.class.isAssignableFrom(clazz)) {
        if (scale > 0) {
          valueSelector =
              new ResultSetValueSelector() {
                @Override
                public Object selectValue(ResultSet resultSet) throws SQLException {
                  BigDecimal bd = resultSet.getBigDecimal(columnNumber);
                  return resultSet.wasNull() ? null : bd.doubleValue();
                }

                @Override
                public String getColumnName() {
                  return columnName;
                }
              };
        } else {
          valueSelector =
              new ResultSetValueSelector() {
                @Override
                public Object selectValue(ResultSet resultSet) throws SQLException {
                  BigDecimal bd = resultSet.getBigDecimal(columnNumber);
                  return resultSet.wasNull() ? null : bd.longValue();
                }

                @Override
                public String getColumnName() {
                  return columnName;
                }
              };
        }
      } else if (Clob.class.isAssignableFrom(clazz)) {
        valueSelector =
            new ResultSetValueSelector() {
              @Override
              public Object selectValue(ResultSet resultSet) throws SQLException {
                Clob c = resultSet.getClob(columnNumber);
                if (resultSet.wasNull()) {
                  return null;
                }
                long length = c.length();
                int lengthInt = (int) length;
                if (length != lengthInt) {
                  throw new SQLException(
                      String.format(
                          Locale.ROOT,
                          "Encountered a clob of length #%d in column '%s' (col #%d).  Max supported length is #%d.",
                          length,
                          columnName,
                          columnNumber,
                          Integer.MAX_VALUE));
                }
                return c.getSubString(1, lengthInt);
              }

              @Override
              public String getColumnName() {
                return columnName;
              }
            };
      }
    }
    return valueSelector;
  }