From 1c7a8c979d5062825dc1630771e15f104016ffb1 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Thu, 15 Oct 2026 07:58:21 +0300 Subject: [PATCH] perf(postgres): hex-encode bytea and cut date-only cells without temporary strings postgresByteaHexDisplay made toRadixString(16).padLeft(2, '0') for every byte (two strings per byte), and date-only cells ran toIso8601String().split('T') .first, which builds a list for every date cell of every row. Bytea is written as code units from a hex digit table into one buffer. A date cell takes the ISO text up to the first T by index, which also keeps the sign and six digits of years outside 0..9999. Closes #1529 Closes #1521 --- lib/core/database/postgres_result_cells.dart | 21 ++++-- .../postgres_result_cells_scanner_test.dart | 73 +++++++++++++++++++ 2 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 test/core/database/postgres_result_cells_scanner_test.dart diff --git a/lib/core/database/postgres_result_cells.dart b/lib/core/database/postgres_result_cells.dart index 7c1f89d3..bbe0d65c 100644 --- a/lib/core/database/postgres_result_cells.dart +++ b/lib/core/database/postgres_result_cells.dart @@ -51,10 +51,14 @@ String postgresResultCellToDisplayString( } if (value is DateTime) { + final iso = value.toIso8601String(); if (_isDateOnly(typeOid, dataTypeName)) { - return value.toIso8601String().split('T').first; + // The text before `T` (also right for years outside 0..9999, which + // carry a sign and six digits), without a split() list (#1529). + final t = iso.indexOf('T'); + return t < 0 ? iso : iso.substring(0, t); } - return value.toIso8601String(); + return iso; } if (value is bool) return value ? 'true' : 'false'; @@ -77,11 +81,18 @@ String postgresResultCellToDisplayString( /// PostgreSQL bytea text `\xdeadbeef`. String postgresByteaHexDisplay(Uint8List bytes) { - final out = StringBuffer(r'\x'); + // Two lower-case hex digits per byte, written as code units into one buffer: + // no toRadixString / padLeft string per byte (#1529, #1521). + const hexDigits = '0123456789abcdef'; + final out = Uint8List(2 + bytes.length * 2); + out[0] = 0x5C; // \ + out[1] = 0x78; // x + var k = 2; for (final b in bytes) { - out.write(b.toRadixString(16).padLeft(2, '0')); + out[k++] = hexDigits.codeUnitAt(b >> 4); + out[k++] = hexDigits.codeUnitAt(b & 0x0F); } - return out.toString(); + return String.fromCharCodes(out); } String postgresByteaHexDisplayFromCell(Object value) { diff --git a/test/core/database/postgres_result_cells_scanner_test.dart b/test/core/database/postgres_result_cells_scanner_test.dart new file mode 100644 index 00000000..cf9cbdcf --- /dev/null +++ b/test/core/database/postgres_result_cells_scanner_test.dart @@ -0,0 +1,73 @@ +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:postgres/postgres.dart'; +import 'package:querya_desktop/core/database/postgres_result_cells.dart'; + +void main() { + group('postgresByteaHexDisplay (#1529, #1521)', () { + test('writes \\x and two lower-case hex digits per byte', () { + expect(postgresByteaHexDisplay(Uint8List(0)), r'\x'); + expect( + postgresByteaHexDisplay(Uint8List.fromList([0, 1, 15, 16, 171, 255])), + r'\x00010f10abff', + ); + }); + + test('every byte value maps to its two digits', () { + final all = Uint8List.fromList(List.generate(256, (i) => i)); + final text = postgresByteaHexDisplay(all); + expect(text.length, 2 + 512); + for (var i = 0; i < 256; i++) { + expect(text.substring(2 + i * 2, 4 + i * 2), + i.toRadixString(16).padLeft(2, '0')); + } + }); + + test('cell display goes through the same encoder', () { + expect( + postgresResultCellToDisplayString(Uint8List.fromList([222, 173])), + r'\xdead', + ); + }); + }); + + group('date-only cells (#1529, #1521)', () { + test('a date column shows only the date', () { + expect( + postgresResultCellToDisplayString( + DateTime.utc(2026, 1, 5), + typeOid: Type.date.oid, + ), + '2026-01-05', + ); + expect( + postgresResultCellToDisplayString( + DateTime(2026, 1, 5, 13, 30), + dataTypeName: 'date', + ), + '2026-01-05', + ); + }); + + test('years outside 0..9999 keep their sign and digits', () { + expect( + postgresResultCellToDisplayString( + DateTime.utc(12000, 3, 4), + typeOid: Type.date.oid, + ), + '+012000-03-04', + ); + }); + + test('timestamps keep the full ISO text', () { + expect( + postgresResultCellToDisplayString( + DateTime.utc(2026, 1, 5, 7, 8, 9), + typeOid: Type.timestamp.oid, + ), + '2026-01-05T07:08:09.000Z', + ); + }); + }); +}