Skip to content

Commit c82623b

Browse files
authored
Fix: ARRAY_AGG (and other arrays) may return [null]. (#413)
1 parent 0ce72ec commit c82623b

File tree

4 files changed

+64
-6
lines changed

4 files changed

+64
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 3.5.1
4+
5+
- Fix: `ARRAY_AGG` (and other arrays) may return `[null]`.
6+
37
## 3.5.0
48

59
- Better exception stacktraces (in some cases) using `package:stack_trace`.

lib/src/types/binary_codec.dart

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -943,26 +943,36 @@ class PostgresBinaryDecoder {
943943
);
944944
}
945945

946-
static List<V> readListBytes<V>(Uint8List data,
947-
V Function(ByteDataReader reader, int length) valueDecoder) {
946+
static List<V?> readListBytes<V>(
947+
Uint8List data,
948+
V Function(ByteDataReader reader, int length) valueDecoder,
949+
) {
948950
if (data.length < 16) {
949951
return [];
950952
}
951953

952954
final reader = ByteDataReader()..add(data);
953955
reader.read(12); // header
954956

955-
final decoded = [].cast<V>();
957+
final decoded = <V?>[];
956958
final size = reader.readInt32();
957959

958960
reader.read(4); // index
959961

962+
bool hasNull = false;
960963
for (var i = 0; i < size; i++) {
961964
final len = reader.readInt32();
962-
decoded.add(valueDecoder(reader, len));
965+
if (len == -1) {
966+
decoded.add(null);
967+
hasNull = true;
968+
} else {
969+
final v = valueDecoder(reader, len);
970+
decoded.add(v);
971+
hasNull = hasNull || (v == null);
972+
}
963973
}
964974

965-
return decoded;
975+
return hasNull ? decoded : decoded.cast<V>();
966976
}
967977

968978
/// Decode numeric / decimal to String without loosing precision.

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: postgres
22
description: PostgreSQL database driver. Supports statement reuse and binary protocol and connection pooling.
3-
version: 3.5.0
3+
version: 3.5.1
44
homepage: https://github.com/isoos/postgresql-dart
55
topics:
66
- sql

test/not_enough_bytes_test.dart

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import 'package:postgres/postgres.dart';
2+
import 'package:test/test.dart';
3+
4+
import 'docker.dart';
5+
6+
void main() {
7+
withPostgresServer('not enough bytes to read', (server) {
8+
test('case #1', () async {
9+
final conn = await server.newConnection();
10+
await conn.execute(_testDdl, queryMode: QueryMode.simple);
11+
final rs1 = await conn.execute('SELECT l.id, bn.novel_id as novels '
12+
'FROM books l LEFT JOIN book_novel bn on l.id=bn.book_id;');
13+
expect(rs1.single, [359, null]);
14+
15+
final rs2 =
16+
await conn.execute('SELECT l.id, ARRAY_AGG(bn.novel_id) as novels '
17+
'FROM books l LEFT JOIN book_novel bn on l.id=bn.book_id '
18+
'GROUP BY l.id;');
19+
expect(rs2.single, [
20+
359,
21+
[null]
22+
]);
23+
});
24+
});
25+
}
26+
27+
final _testDdl = '''
28+
CREATE TABLE IF NOT EXISTS books (
29+
id INTEGER NOT NULL PRIMARY KEY,
30+
title TEXT NOT NULL,
31+
first_publication INTEGER,
32+
notes TEXT,
33+
opinion_id INTEGER NOT NULL
34+
);
35+
36+
CREATE TABLE IF NOT EXISTS book_novel (
37+
book_id INTEGER NOT NULL,
38+
novel_id INTEGER NOT NULL,
39+
PRIMARY KEY (book_id,novel_id)
40+
);
41+
42+
INSERT INTO books (id,title,first_publication,notes,opinion_id) VALUES (359,'The legacy of heorot',1987,NULL,0);
43+
INSERT INTO book_novel (book_id,novel_id) VALUES (1268,215);
44+
''';

0 commit comments

Comments
 (0)