fix(agent): cache JDBC column metadata
This commit is contained in:
1 parent
c1d3f9423d
commit
fb4bce90d6
3 files changed
+154
-13
No files matched your search
@@ -113,24 +113,28 @@ public final class JdbcExecutor {
|
||||
return unchecked(() -> {
|
||||
ResultSetMetaData meta = rs.getMetaData();
|
||||
int colCount = meta.getColumnCount();
|
||||
List<String> columns = new ArrayList<>();
|
||||
List<String> columnTypes = new ArrayList<>();
|
||||
List<String> columns = new ArrayList<>(colCount);
|
||||
List<String> columnTypes = new ArrayList<>(colCount);
|
||||
// Cache JDBC column metadata once; some drivers resolve it lazily,
|
||||
// and row reading is the hot path.
|
||||
int[] sqlTypeByIndex = new int[colCount];
|
||||
String[] typeNameByIndex = new String[colCount];
|
||||
for (int i = 1; i <= colCount; i++) {
|
||||
columns.add(meta.getColumnLabel(i));
|
||||
sqlTypeByIndex[i - 1] = safeColumnSqlType(meta, i);
|
||||
String typeName = safeColumnTypeName(meta, i);
|
||||
columnTypes.add(typeName);
|
||||
typeNameByIndex[i - 1] = typeName;
|
||||
}
|
||||
|
||||
List<List<Object>> rows = new ArrayList<>();
|
||||
List<List<Object>> rows = new ArrayList<>(initialRowCapacity(maxRows));
|
||||
boolean truncated = false;
|
||||
while (rs.next()) {
|
||||
if (rows.size() >= maxRows) {
|
||||
truncated = true;
|
||||
break;
|
||||
}
|
||||
rows.add(rowValues(rs, valueReader, typeNameByIndex));
|
||||
rows.add(rowValues(rs, valueReader, sqlTypeByIndex, typeNameByIndex));
|
||||
}
|
||||
|
||||
return new QueryResult(columns, columnTypes, rows, 0L, executionTimeMs, truncated);
|
||||
@@ -221,9 +225,12 @@ public final class JdbcExecutor {
|
||||
int colCount = meta.getColumnCount();
|
||||
List<String> columns = new ArrayList<>(colCount);
|
||||
List<String> columnTypes = new ArrayList<>(colCount);
|
||||
// Cache JDBC column metadata once; fetching a page should only read row values.
|
||||
int[] sqlTypeByIndex = new int[colCount];
|
||||
String[] typeNameByIndex = new String[colCount];
|
||||
for (int i = 1; i <= colCount; i++) {
|
||||
columns.add(meta.getColumnLabel(i));
|
||||
sqlTypeByIndex[i - 1] = safeColumnSqlType(meta, i);
|
||||
String typeName = safeColumnTypeName(meta, i);
|
||||
columnTypes.add(typeName);
|
||||
typeNameByIndex[i - 1] = typeName;
|
||||
@@ -234,6 +241,7 @@ public final class JdbcExecutor {
|
||||
rs,
|
||||
columns,
|
||||
columnTypes,
|
||||
sqlTypeByIndex,
|
||||
typeNameByIndex,
|
||||
Math.max(options.getMaxRows(), 1),
|
||||
valueReader
|
||||
@@ -449,7 +457,7 @@ public final class JdbcExecutor {
|
||||
return unchecked(() -> {
|
||||
session.lastAccessedAtMillis = System.currentTimeMillis();
|
||||
int effectivePageSize = Math.max(pageSize, 1);
|
||||
List<List<Object>> rows = new ArrayList<>();
|
||||
List<List<Object>> rows = new ArrayList<>(initialRowCapacity(effectivePageSize));
|
||||
|
||||
if (session.pendingRow != null) {
|
||||
rows.add(session.pendingRow);
|
||||
@@ -461,7 +469,7 @@ public final class JdbcExecutor {
|
||||
closeSession(targetSessions, session.id);
|
||||
return new QueryPageResult(session.columns, session.columnTypes, rows, 0L, executionTimeMs, false, null, false);
|
||||
}
|
||||
rows.add(rowValues(session.resultSet, session.valueReader, session.typeNameByIndex));
|
||||
rows.add(rowValues(session.resultSet, session.valueReader, session.sqlTypeByIndex, session.typeNameByIndex));
|
||||
session.rowsRead += 1;
|
||||
}
|
||||
|
||||
@@ -477,7 +485,7 @@ public final class JdbcExecutor {
|
||||
return new QueryPageResult(session.columns, session.columnTypes, rows, 0L, executionTimeMs, false, null, false);
|
||||
}
|
||||
|
||||
session.pendingRow = rowValues(session.resultSet, session.valueReader, session.typeNameByIndex);
|
||||
session.pendingRow = rowValues(session.resultSet, session.valueReader, session.sqlTypeByIndex, session.typeNameByIndex);
|
||||
session.rowsRead += 1;
|
||||
return new QueryPageResult(session.columns, session.columnTypes, rows, 0L, executionTimeMs, false, session.id, true);
|
||||
});
|
||||
@@ -517,17 +525,29 @@ public final class JdbcExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
private List<Object> rowValues(ResultSet rs, ResultValueReader valueReader, String[] typeNameByIndex) throws SQLException {
|
||||
ResultSetMetaData meta = rs.getMetaData();
|
||||
int colCount = meta.getColumnCount();
|
||||
static int safeColumnSqlType(ResultSetMetaData meta, int columnIndex) {
|
||||
try {
|
||||
return meta.getColumnType(columnIndex);
|
||||
} catch (SQLException ignored) {
|
||||
return Types.OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Object> rowValues(
|
||||
ResultSet rs,
|
||||
ResultValueReader valueReader,
|
||||
int[] sqlTypeByIndex,
|
||||
String[] typeNameByIndex
|
||||
) throws SQLException {
|
||||
int colCount = sqlTypeByIndex.length;
|
||||
List<Object> row = new ArrayList<>(colCount);
|
||||
for (int i = 1; i <= colCount; i++) {
|
||||
int sqlType = meta.getColumnType(i);
|
||||
int sqlType = sqlTypeByIndex[i - 1];
|
||||
Object value;
|
||||
if (valueReader instanceof ColumnAwareResultValueReader) {
|
||||
String typeName = typeNameByIndex != null && i - 1 < typeNameByIndex.length
|
||||
? typeNameByIndex[i - 1]
|
||||
: safeColumnTypeName(meta, i);
|
||||
: "";
|
||||
value = ((ColumnAwareResultValueReader) valueReader).read(rs, i, sqlType, typeName);
|
||||
} else {
|
||||
value = valueReader.read(rs, i, sqlType);
|
||||
@@ -537,6 +557,13 @@ public final class JdbcExecutor {
|
||||
return row;
|
||||
}
|
||||
|
||||
private static int initialRowCapacity(int requestedRows) {
|
||||
if (requestedRows <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return Math.min(requestedRows, 1024);
|
||||
}
|
||||
|
||||
private void applySchema(Connection conn, String schema, Function<String, String> setSchemaSql) throws SQLException {
|
||||
if (schema == null || schema.trim().isEmpty()) {
|
||||
return;
|
||||
@@ -664,6 +691,7 @@ public final class JdbcExecutor {
|
||||
private final ResultSet resultSet;
|
||||
private final List<String> columns;
|
||||
private final List<String> columnTypes;
|
||||
private final int[] sqlTypeByIndex;
|
||||
private final String[] typeNameByIndex;
|
||||
private final int maxRows;
|
||||
private final ResultValueReader valueReader;
|
||||
@@ -677,6 +705,7 @@ public final class JdbcExecutor {
|
||||
ResultSet resultSet,
|
||||
List<String> columns,
|
||||
List<String> columnTypes,
|
||||
int[] sqlTypeByIndex,
|
||||
String[] typeNameByIndex,
|
||||
int maxRows,
|
||||
ResultValueReader valueReader
|
||||
@@ -686,6 +715,7 @@ public final class JdbcExecutor {
|
||||
this.resultSet = resultSet;
|
||||
this.columns = columns;
|
||||
this.columnTypes = columnTypes;
|
||||
this.sqlTypeByIndex = sqlTypeByIndex;
|
||||
this.typeNameByIndex = typeNameByIndex;
|
||||
this.maxRows = maxRows;
|
||||
this.valueReader = valueReader;
|
||||
|
||||
@@ -99,7 +99,7 @@ public final class QueryResult {
|
||||
if (input == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<List<Object>> normalized = new ArrayList<>();
|
||||
List<List<Object>> normalized = new ArrayList<>(input.size());
|
||||
for (List<?> row : input) {
|
||||
normalized.add(row == null ? Collections.emptyList() : new ArrayList<Object>(row));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user