From bf19399e43b93771cadf81145fd70740c733e32a Mon Sep 17 00:00:00 2001 From: Istvan Soos Date: Sat, 8 Feb 2025 11:47:17 +0100 Subject: [PATCH] Fix: ARRAY_AGG (and other arrays) may return [null]. --- CHANGELOG.md | 4 +++ lib/src/types/binary_codec.dart | 20 +++++++++++---- pubspec.yaml | 2 +- test/not_enough_bytes_test.dart | 44 +++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 test/not_enough_bytes_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index dab156b..fb36338 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 3.5.1 + +- Fix: `ARRAY_AGG` (and other arrays) may return `[null]`. + ## 3.5.0 - Better exception stacktraces (in some cases) using `package:stack_trace`. diff --git a/lib/src/types/binary_codec.dart b/lib/src/types/binary_codec.dart index 7a690f6..1ca5a34 100644 --- a/lib/src/types/binary_codec.dart +++ b/lib/src/types/binary_codec.dart @@ -943,8 +943,10 @@ class PostgresBinaryDecoder { ); } - static List readListBytes(Uint8List data, - V Function(ByteDataReader reader, int length) valueDecoder) { + static List readListBytes( + Uint8List data, + V Function(ByteDataReader reader, int length) valueDecoder, + ) { if (data.length < 16) { return []; } @@ -952,17 +954,25 @@ class PostgresBinaryDecoder { final reader = ByteDataReader()..add(data); reader.read(12); // header - final decoded = [].cast(); + final decoded = []; final size = reader.readInt32(); reader.read(4); // index + bool hasNull = false; for (var i = 0; i < size; i++) { final len = reader.readInt32(); - decoded.add(valueDecoder(reader, len)); + if (len == -1) { + decoded.add(null); + hasNull = true; + } else { + final v = valueDecoder(reader, len); + decoded.add(v); + hasNull = hasNull || (v == null); + } } - return decoded; + return hasNull ? decoded : decoded.cast(); } /// Decode numeric / decimal to String without loosing precision. diff --git a/pubspec.yaml b/pubspec.yaml index b2dc9e3..f480196 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: postgres description: PostgreSQL database driver. Supports statement reuse and binary protocol and connection pooling. -version: 3.5.0 +version: 3.5.1 homepage: https://github.com/isoos/postgresql-dart topics: - sql diff --git a/test/not_enough_bytes_test.dart b/test/not_enough_bytes_test.dart new file mode 100644 index 0000000..61b7d8f --- /dev/null +++ b/test/not_enough_bytes_test.dart @@ -0,0 +1,44 @@ +import 'package:postgres/postgres.dart'; +import 'package:test/test.dart'; + +import 'docker.dart'; + +void main() { + withPostgresServer('not enough bytes to read', (server) { + test('case #1', () async { + final conn = await server.newConnection(); + await conn.execute(_testDdl, queryMode: QueryMode.simple); + final rs1 = await conn.execute('SELECT l.id, bn.novel_id as novels ' + 'FROM books l LEFT JOIN book_novel bn on l.id=bn.book_id;'); + expect(rs1.single, [359, null]); + + final rs2 = + await conn.execute('SELECT l.id, ARRAY_AGG(bn.novel_id) as novels ' + 'FROM books l LEFT JOIN book_novel bn on l.id=bn.book_id ' + 'GROUP BY l.id;'); + expect(rs2.single, [ + 359, + [null] + ]); + }); + }); +} + +final _testDdl = ''' +CREATE TABLE IF NOT EXISTS books ( + id INTEGER NOT NULL PRIMARY KEY, + title TEXT NOT NULL, + first_publication INTEGER, + notes TEXT, + opinion_id INTEGER NOT NULL +); + +CREATE TABLE IF NOT EXISTS book_novel ( + book_id INTEGER NOT NULL, + novel_id INTEGER NOT NULL, + PRIMARY KEY (book_id,novel_id) +); + +INSERT INTO books (id,title,first_publication,notes,opinion_id) VALUES (359,'The legacy of heorot',1987,NULL,0); +INSERT INTO book_novel (book_id,novel_id) VALUES (1268,215); +''';