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 @@ -6,6 +6,7 @@ import MonacoEditor, { IEditorIns } from '@/components/MonacoEditor';
interface IProps {
id: string;
value: string;
resetViewRevision: number;
readOnly: boolean;
onChange: (value: string) => void;
onJsonChange: (isJson: boolean) => void;
Expand All @@ -16,7 +17,7 @@ interface JsonValidationWorker {
parseJSONDocument: (uri: string) => Promise<monaco.languages.json.JSONDocument | null>;
}

const JsonAwareMonacoEditor = ({ id, value, readOnly, onChange, onJsonChange }: IProps) => {
const JsonAwareMonacoEditor = ({ id, value, resetViewRevision, readOnly, onChange, onJsonChange }: IProps) => {
const editorRef = useRef<IEditorIns | null>(null);
const changeDisposerRef = useRef<{ dispose: () => void } | null>(null);
const validationModelRef = useRef<monaco.editor.ITextModel | null>(null);
Expand Down Expand Up @@ -107,6 +108,15 @@ const JsonAwareMonacoEditor = ({ id, value, readOnly, onChange, onJsonChange }:
editorRef.current?.updateOptions({ readOnly });
}, [readOnly]);

useEffect(() => {
const editor = editorRef.current;
if (!editor) {
return;
}
editor.setPosition({ lineNumber: 1, column: 1 });
editor.setScrollPosition({ scrollTop: 0, scrollLeft: 0 });
}, [resetViewRevision]);

useEffect(() => {
return () => {
validationVersionRef.current += 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export interface ViewDataRef {
const ViewData = forwardRef((_props: IProps, ref: ForwardedRef<ViewDataRef>) => {
const [viewData, setViewData] = useState<IViewData | null>(null);
const [editorValue, setEditorContent] = useState('');
const [editorViewRevision, setEditorViewRevision] = useState(0);
const [isJsonContent, setIsJsonContent] = useState(false);
const activeViewDataRef = useRef<IViewData | null>(null);
const editorValueRef = useRef('');
Expand Down Expand Up @@ -414,23 +415,31 @@ const ViewData = forwardRef((_props: IProps, ref: ForwardedRef<ViewDataRef>) =>
applyEditorValue(value);
};

const applyJsonPresentation = (value: string) => {
setEditorContent(value);
setEditorViewRevision((revision) => revision + 1);
if (!isLargeValue) {
applyEditorValue(value);
}
};

const formatJson = () => {
try {
const parsed = JSON.parse(editorValue);
const formatted = JSON.stringify(parsed, null, 2);
handleEditorValueChange(formatted);
applyJsonPresentation(formatted);
} catch (err) {
console.error('无效的 JSON 格式,请检查语法', err);
console.error('Invalid JSON format. Check the syntax.', err);
}
};

const compressJson = () => {
try {
const parsed = JSON.parse(editorValue);
const compressed = JSON.stringify(parsed);
handleEditorValueChange(compressed);
applyJsonPresentation(compressed);
} catch (err) {
console.error('无效的 JSON 格式,请检查语法', err);
console.error('Invalid JSON format. Check the syntax.', err);
}
};

Expand Down Expand Up @@ -528,6 +537,7 @@ const ViewData = forwardRef((_props: IProps, ref: ForwardedRef<ViewDataRef>) =>
<JsonAwareMonacoEditor
id={uuid}
value={editorValue}
resetViewRevision={editorViewRevision}
readOnly={!editorCanEdit}
onChange={handleEditorValueChange}
onJsonChange={setIsJsonContent}
Expand All @@ -551,6 +561,7 @@ const ViewData = forwardRef((_props: IProps, ref: ForwardedRef<ViewDataRef>) =>
largeValueStatus,
displayMode,
editorValue,
editorViewRevision,
editorCanEdit,
isJsonContent,
viewerMode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
@AllArgsConstructor
public class JDBCDataValue {
private static final Logger log = LoggerFactory.getLogger(JDBCDataValue.class);
private static final int LARGE_VALUE_THRESHOLD_BYTES = 10 * 1024;
private static final int LARGE_VALUE_PREVIEW_CHARS = 200;
private static final Pattern SUMMARY_SIZE_PATTERN = Pattern.compile("(\\d+(?:\\.\\d+)?)\\s*(B|KB|MB|GB)\\s*$",
Pattern.CASE_INSENSITIVE);
private ResultSet resultSet;
Expand Down Expand Up @@ -267,19 +269,21 @@ public ResultCell buildResultCell(String value) {
int sqlType = getSqlType();
long displayBytes = value == null ? 0L : value.getBytes(StandardCharsets.UTF_8).length;
long displayChars = value == null ? 0L : value.length();
LargeValueInfo largeValueInfo = detectLargeValue(value, columnType, sqlType);
Object rawValue = getRawCellValue(largeValueInfo);
LargeValueInfo largeValueInfo = detectLargeValue(value, columnType, sqlType, displayBytes);
String displayValue = previewValue(value, largeValueInfo);
Object rawValue = getRawCellValue(value, largeValueInfo);
return ResultCell.builder()
.value(value)
.value(displayValue)
.rawValue(rawValue)
.largeValue(largeValueInfo.largeValue)
.valueType(largeValueInfo.valueType.code())
.sqlType(sqlType)
.columnType(columnType)
.sizeBytes(largeValueInfo.sizeBytes)
.sizeChars(largeValueInfo.sizeChars)
.loadedBytes(largeValueInfo.largeValue ? displayBytes : null)
.loadedChars(largeValueInfo.largeValue ? displayChars : null)
.loadedBytes(largeValueInfo.largeValue && displayValue != null
? (long) displayValue.getBytes(StandardCharsets.UTF_8).length : null)
.loadedChars(largeValueInfo.largeValue && displayValue != null ? (long) displayValue.length() : null)
.truncated(largeValueInfo.largeValue)
.build();
}
Expand Down Expand Up @@ -319,7 +323,7 @@ public String getBinaryDataString() {
}
}

private LargeValueInfo detectLargeValue(String value, String columnType, int sqlType) {
private LargeValueInfo detectLargeValue(String value, String columnType, int sqlType, long valueBytes) {
LargeValueInfo info = new LargeValueInfo();
info.valueType = LargeValueTypeEnum.resolve(columnType, sqlType);
if (!limitSize || value == null) {
Expand All @@ -328,6 +332,7 @@ private LargeValueInfo detectLargeValue(String value, String columnType, int sql
Long summaryBytes = parseSummaryBytes(value);
if (summaryBytes != null) {
info.largeValue = true;
info.summary = true;
info.sizeBytes = summaryBytes;
if (info.valueType == LargeValueTypeEnum.BINARY && isImageSummary(value)) {
info.valueType = LargeValueTypeEnum.IMAGE;
Expand All @@ -337,9 +342,10 @@ private LargeValueInfo detectLargeValue(String value, String columnType, int sql
}
return info;
}
if (LargeValueTypeEnum.isPotentialLargeType(columnType, sqlType) && value.length() > LobUnitEnum.M.getSize()) {
if (LargeValueTypeEnum.isPotentialLargeType(columnType, sqlType)
&& valueBytes > LARGE_VALUE_THRESHOLD_BYTES) {
info.largeValue = true;
info.sizeBytes = (long) value.getBytes(StandardCharsets.UTF_8).length;
info.sizeBytes = valueBytes;
info.sizeChars = (long) value.length();
}
return info;
Expand All @@ -349,10 +355,18 @@ private boolean isImageSummary(String value) {
return value != null && value.toUpperCase(Locale.ROOT).contains(" IMAGE");
}

private Object getRawCellValue(LargeValueInfo largeValueInfo) {
private String previewValue(String value, LargeValueInfo largeValueInfo) {
if (value == null || !largeValueInfo.largeValue || largeValueInfo.summary
|| value.length() <= LARGE_VALUE_PREVIEW_CHARS) {
return value;
}
return value.substring(0, LARGE_VALUE_PREVIEW_CHARS);
}

private Object getRawCellValue(String value, LargeValueInfo largeValueInfo) {
try {
if (largeValueInfo.valueType == LargeValueTypeEnum.JSON) {
return getJsonString();
return value;
}
if (!largeValueInfo.largeValue) {
return getObject();
Expand Down Expand Up @@ -422,6 +436,7 @@ private String getJsonString() {

private static class LargeValueInfo {
private boolean largeValue;
private boolean summary;
private LargeValueTypeEnum valueType = LargeValueTypeEnum.UNKNOWN;
private Long sizeBytes;
private Long sizeChars;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,24 @@ void leavesSmallStringsEditableInlineValues() {
assertEquals("small value", cell.getValue());
}

@Test
void truncatesJsonValuesLargerThanTenKbForLazyLoading() {
String json = "{\"payload\":\"" + "x".repeat(11 * 1024) + "\"}";
JDBCDataValue value = new JDBCDataValue(resultSet(), metaData("jsonb", Types.OTHER), 1, true);

ResultCell cell = value.buildResultCell(json);

assertTrue(cell.isLargeValue());
assertTrue(cell.isTruncated());
assertEquals("JSON", cell.getValueType());
assertEquals(200, cell.getValue().length());
assertEquals(json, cell.getRawValue());
assertEquals((long) json.getBytes(java.nio.charset.StandardCharsets.UTF_8).length, cell.getSizeBytes());
assertEquals((long) json.length(), cell.getSizeChars());
assertEquals(200L, cell.getLoadedBytes());
assertEquals(200L, cell.getLoadedChars());
}

@Test
void nullClobReturnsNullInsteadOfDereferencingClob() {
ResultSet resultSet = resultSet("getClob", null, "getString", null);
Expand Down
Loading