Skip to content

Commit 3daf1fd

Browse files
authored
Merge pull request #2 from isoos/update
Updated query API methods to always return non-null values.
2 parents 6bf7456 + ea547e9 commit 3daf1fd

13 files changed

+81
-74
lines changed

CHANGELOG.md

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

3+
## 2.3.0-null-safety.1
4+
5+
- Updated public API to always return non-nullable results.
6+
- **BREAKING CHANGE**: unknown mapped table name is no longer `null`, it is empty string (`''`).
7+
38
## 2.3.0-null-safety.0
49

510
- Migrate to null safety. (Thanks to [j4qfrost](https://github.com/j4qfrost), [#153](https://github.com/stablekernel/postgresql-dart/pull/153)).

lib/src/connection.dart

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -390,10 +390,6 @@ class _OidCache {
390390
resolveOids: false,
391391
);
392392

393-
if (orderedTableNames == null) {
394-
return;
395-
}
396-
397393
final iterator = oids.iterator;
398394
orderedTableNames.forEach((tableName) {
399395
iterator.moveNext();
@@ -416,7 +412,7 @@ abstract class _PostgreSQLExecutionContextMixin
416412
int get queueSize => _queue.length;
417413

418414
@override
419-
Future<PostgreSQLResult?> query(
415+
Future<PostgreSQLResult> query(
420416
String fmtString, {
421417
Map<String, dynamic>? substitutionValues,
422418
bool allowReuse = true,
@@ -429,7 +425,7 @@ abstract class _PostgreSQLExecutionContextMixin
429425
timeoutInSeconds: timeoutInSeconds,
430426
);
431427

432-
Future<PostgreSQLResult?> _query(
428+
Future<PostgreSQLResult> _query(
433429
String fmtString, {
434430
Map<String, dynamic>? substitutionValues,
435431
required bool allowReuse,
@@ -458,18 +454,16 @@ abstract class _PostgreSQLExecutionContextMixin
458454
}
459455
final metaData = _PostgreSQLResultMetaData(columnDescriptions!);
460456

461-
return queryResult != null
462-
? _PostgreSQLResult(
463-
queryResult.affectedRowCount,
464-
metaData,
465-
queryResult.value!
466-
.map((columns) => _PostgreSQLResultRow(metaData, columns))
467-
.toList())
468-
: null;
457+
return _PostgreSQLResult(
458+
queryResult.affectedRowCount,
459+
metaData,
460+
queryResult.value!
461+
.map((columns) => _PostgreSQLResultRow(metaData, columns))
462+
.toList());
469463
}
470464

471465
@override
472-
Future<List<Map<String?, Map<String, dynamic>>>> mappedResultsQuery(
466+
Future<List<Map<String, Map<String, dynamic>>>> mappedResultsQuery(
473467
String fmtString,
474468
{Map<String, dynamic> substitutionValues = const {},
475469
bool allowReuse = false,
@@ -481,14 +475,11 @@ abstract class _PostgreSQLExecutionContextMixin
481475
timeoutInSeconds: timeoutInSeconds,
482476
);
483477

484-
if (rs == null) {
485-
return Future.value();
486-
}
487478
return rs.map((row) => row.toTableColumnMap()).toList();
488479
}
489480

490481
@override
491-
Future<int?> execute(String fmtString,
482+
Future<int> execute(String fmtString,
492483
{Map<String, dynamic> substitutionValues = const {},
493484
int? timeoutInSeconds}) async {
494485
timeoutInSeconds ??= _connection.queryTimeoutInSeconds;
@@ -502,13 +493,13 @@ abstract class _PostgreSQLExecutionContextMixin
502493
onlyReturnAffectedRowCount: true);
503494

504495
final result = await _enqueue(query, timeoutInSeconds: timeoutInSeconds);
505-
return result != null ? result.affectedRowCount : null;
496+
return result.affectedRowCount;
506497
}
507498

508499
@override
509-
void cancelTransaction({String reason});
500+
void cancelTransaction({String? reason});
510501

511-
Future<QueryResult<T>?> _enqueue<T>(Query<T> query,
502+
Future<QueryResult<T>> _enqueue<T>(Query<T> query,
512503
{int timeoutInSeconds = 30}) async {
513504
if (_queue.add(query)) {
514505
_connection._transitionToState(_connection._connectionState.awake());
@@ -518,7 +509,7 @@ abstract class _PostgreSQLExecutionContextMixin
518509
await query.future.timeout(Duration(seconds: timeoutInSeconds));
519510
_connection._cache.add(query);
520511
_queue.remove(query);
521-
return result;
512+
return result!;
522513
} catch (e, st) {
523514
_queue.remove(query);
524515
await _onQueryError(query, e, st);
@@ -529,20 +520,20 @@ abstract class _PostgreSQLExecutionContextMixin
529520
// the caller behaves correctly in this condition. otherwise,
530521
// the caller would complete synchronously. This future
531522
// will always complete as a cancellation error.
532-
return Future(() async => query.future);
523+
return Future(() async => (await query.future)!);
533524
}
534525
}
535526

536527
Future _onQueryError(Query query, dynamic error, [StackTrace? trace]) async {}
537528
}
538529

539530
class _PostgreSQLResultMetaData {
540-
final List<ColumnDescription?> columnDescriptions;
531+
final List<ColumnDescription> columnDescriptions;
541532
late List<String?> _tableNames;
542533

543534
_PostgreSQLResultMetaData(this.columnDescriptions) {
544535
_tableNames =
545-
columnDescriptions.map((column) => column!.tableName).toSet().toList();
536+
columnDescriptions.map((column) => column.tableName).toSet().toList();
546537
}
547538

548539
List<String?> get tableNames {
@@ -561,7 +552,7 @@ class _PostgreSQLResult extends UnmodifiableListView<PostgreSQLResultRow>
561552
: super(rows);
562553

563554
@override
564-
List<ColumnDescription?> get columnDescriptions =>
555+
List<ColumnDescription> get columnDescriptions =>
565556
_metaData.columnDescriptions;
566557
}
567558

@@ -572,18 +563,18 @@ class _PostgreSQLResultRow extends UnmodifiableListView
572563
_PostgreSQLResultRow(this._metaData, List columns) : super(columns);
573564

574565
@override
575-
List<ColumnDescription?> get columnDescriptions =>
566+
List<ColumnDescription> get columnDescriptions =>
576567
_metaData.columnDescriptions;
577568

578569
@override
579-
Map<String?, Map<String, dynamic>> toTableColumnMap() {
580-
final rowMap = <String?, Map<String, dynamic>>{};
570+
Map<String, Map<String, dynamic>> toTableColumnMap() {
571+
final rowMap = <String, Map<String, dynamic>>{};
581572
_metaData.tableNames.forEach((tableName) {
582-
rowMap[tableName] = <String, dynamic>{};
573+
rowMap[tableName ?? ''] = <String, dynamic>{};
583574
});
584575
for (var i = 0; i < _metaData.columnDescriptions.length; i++) {
585576
final col = _metaData.columnDescriptions[i];
586-
rowMap[col!.tableName]![col.columnName] = this[i];
577+
rowMap[col.tableName]![col.columnName] = this[i];
587578
}
588579
return rowMap;
589580
}
@@ -593,7 +584,7 @@ class _PostgreSQLResultRow extends UnmodifiableListView
593584
final rowMap = <String, dynamic>{};
594585
for (var i = 0; i < _metaData.columnDescriptions.length; i++) {
595586
final col = _metaData.columnDescriptions[i];
596-
rowMap[col!.columnName] = this[i];
587+
rowMap[col.columnName] = this[i];
597588
}
598589
return rowMap;
599590
}

lib/src/execution_context.dart

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ abstract class PostgreSQLExecutionContext {
2929
/// By default, instances of this class will reuse queries. This allows significantly more efficient transport to and from the database. You do not have to do
3030
/// anything to opt in to this behavior, this connection will track the necessary information required to reuse queries without intervention. (The [fmtString] is
3131
/// the unique identifier to look up reuse information.) You can disable reuse by passing false for [allowReuse].
32-
Future<PostgreSQLResult?> query(String fmtString,
32+
Future<PostgreSQLResult> query(String fmtString,
3333
{Map<String, dynamic> substitutionValues,
3434
bool allowReuse,
3535
int? timeoutInSeconds});
@@ -41,14 +41,14 @@ abstract class PostgreSQLExecutionContext {
4141
/// This method returns the number of rows affected and no additional information. This method uses the least efficient and less secure command
4242
/// for executing queries in the PostgreSQL protocol; [query] is preferred for queries that will be executed more than once, will contain user input,
4343
/// or return rows.
44-
Future<int?> execute(String fmtString,
44+
Future<int> execute(String fmtString,
4545
{Map<String, dynamic> substitutionValues, int? timeoutInSeconds});
4646

4747
/// Cancels a transaction on this context.
4848
///
4949
/// If this context is an instance of [PostgreSQLConnection], this method has no effect. If the context is a transaction context (passed as the argument
5050
/// to [PostgreSQLConnection.transaction]), this will rollback the transaction.
51-
void cancelTransaction({String reason});
51+
void cancelTransaction({String? reason});
5252

5353
/// Executes a query on this connection and returns each row as a [Map].
5454
///
@@ -80,7 +80,7 @@ abstract class PostgreSQLExecutionContext {
8080
/// "company: {"name": "stable|kernel"}
8181
/// }
8282
/// ]
83-
Future<List<Map<String?, Map<String, dynamic>>>> mappedResultsQuery(
83+
Future<List<Map<String, Map<String, dynamic>>>> mappedResultsQuery(
8484
String fmtString,
8585
{Map<String, dynamic> substitutionValues,
8686
bool allowReuse,
@@ -93,18 +93,18 @@ abstract class ColumnDescription {
9393
String get columnName;
9494

9595
/// The resolved name of the referenced table.
96-
String? get tableName;
96+
String get tableName;
9797
}
9898

9999
/// A single row of a query result.
100100
///
101101
/// Column values can be accessed through the `[]` operator.
102102
abstract class PostgreSQLResultRow implements List {
103-
List<ColumnDescription?> get columnDescriptions;
103+
List<ColumnDescription> get columnDescriptions;
104104

105105
/// Returns a two-level map that on the first level contains the resolved
106106
/// table name, and on the second level the column name (or its alias).
107-
Map<String?, Map<String, dynamic>> toTableColumnMap();
107+
Map<String, Map<String, dynamic>> toTableColumnMap();
108108

109109
/// Returns a single-level map that maps the column name (or its alias) to the
110110
/// value returned on that position.
@@ -117,5 +117,5 @@ abstract class PostgreSQLResultRow implements List {
117117
abstract class PostgreSQLResult implements List<PostgreSQLResultRow> {
118118
/// How many rows did this query affect?
119119
int get affectedRowCount;
120-
List<ColumnDescription?> get columnDescriptions;
120+
List<ColumnDescription> get columnDescriptions;
121121
}

lib/src/query.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ class FieldDescription implements ColumnDescription {
244244
final int formatCode;
245245

246246
@override
247-
final String? tableName;
247+
final String tableName;
248248

249249
FieldDescription._(
250250
this.converter,
@@ -281,7 +281,7 @@ class FieldDescription implements ColumnDescription {
281281
return FieldDescription._(
282282
converter, fieldName, tableID, columnID, typeID,
283283
dataTypeSize, typeModifier, formatCode,
284-
null, // tableName
284+
'', // tableName
285285
);
286286
}
287287

pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
name: postgres
22
description: PostgreSQL database driver. Supports statement reuse and binary protocol.
3-
version: 2.3.0-null-safety.0
3+
version: 2.3.0-null-safety.1
44
homepage: https://github.com/isoos/postgresql-dart
55

66
environment:
77
sdk: '>=2.12.0 <3.0.0'
88

99
dependencies:
10-
buffer: ^1.0.6
10+
buffer: ^1.1.0
1111
crypto: ^3.0.0
1212
collection: ^1.15.0-nullsafety.4
1313

test/connection_test.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,11 @@ void main() {
105105
username: 'darttrust');
106106
await conn.open();
107107

108+
final rs = await conn.query('select 1');
108109
final errors = [];
109110
final catcher = (e) {
110111
errors.add(e);
111-
return null;
112+
return rs;
112113
};
113114
final futures = [
114115
conn.query('select 1', allowReuse: false).catchError(catcher),
@@ -131,11 +132,12 @@ void main() {
131132
conn = PostgreSQLConnection('localhost', 5432, 'dart_test',
132133
username: 'darttrust', useSSL: true);
133134
await conn.open();
135+
final rs = await conn.query('select 1');
134136

135137
final errors = [];
136138
final catcher = (e) {
137139
errors.add(e);
138-
return null;
140+
return rs;
139141
};
140142
final futures = [
141143
conn.query('select 1', allowReuse: false).catchError(catcher),

test/decode_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ void main() {
4040
test('Fetch em', () async {
4141
final res = await connection.query('select * from t');
4242

43-
final row1 = res![0];
43+
final row1 = res[0];
4444
final row2 = res[1];
4545
final row3 = res[2];
4646

test/encoding_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ Future expectInverse(dynamic value, PostgreSQLDataType dataType) async {
393393
final result = await conn.query(
394394
'INSERT INTO t (v) VALUES (${PostgreSQLFormat.id('v', type: dataType)}) RETURNING v',
395395
substitutionValues: {'v': value});
396-
expect(result!.first.first, equals(value));
396+
expect(result.first.first, equals(value));
397397

398398
final encoder = PostgresBinaryEncoder(dataType);
399399
final encodedValue = encoder.convert(value);

test/map_return_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ void main() {
125125
final result = await connection.mappedResultsQuery('SELECT 1');
126126
expect(result, [
127127
{
128-
null: {'?column?': 1}
128+
'': {'?column?': 1}
129129
}
130130
]);
131131
});

test/query_reuse_test.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -547,23 +547,24 @@ void main() {
547547
test(
548548
'Send two queries that will be the same prepared statement async, first one fails on bind',
549549
() async {
550+
final rs = await connection.query('SELECT 1');
550551
await connection.query(
551552
'insert into u (i1, i2) values (@i1:int4, @i2:int4) returning i1, i2',
552553
substitutionValues: {'i1': 1, 'i2': 2},
553554
allowReuse: false);
554555

555556
final string = 'select i1, i2 from u where i1 = @i:int4';
556557
// ignore: unawaited_futures
557-
connection
558-
.query(string, substitutionValues: {'i': 'foo'}).catchError((e) {});
558+
connection.query(string,
559+
substitutionValues: {'i': 'foo'}).catchError((e) => rs);
559560

560561
final results =
561562
await connection.query(string, substitutionValues: {'i': 1});
562563

563564
expect(results, [
564565
[1, 2]
565566
]);
566-
expect(getQueryCache(connection).length, 1);
567+
expect(getQueryCache(connection).length, 2); // 1: SELECT 1
567568
expect(hasCachedQueryNamed(connection, string), true);
568569
});
569570
});

test/query_test.dart

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ void main() {
3838
expect(result, [expectedRow]);
3939

4040
result = await connection.query('select t from t');
41-
expect(result!.columnDescriptions, hasLength(1));
42-
expect(result.columnDescriptions.single!.tableName, 't');
43-
expect(result.columnDescriptions.single!.columnName, 't');
41+
expect(result.columnDescriptions, hasLength(1));
42+
expect(result.columnDescriptions.single.tableName, 't');
43+
expect(result.columnDescriptions.single.columnName, 't');
4444
expect(result, [expectedRow]);
4545
});
4646

@@ -154,11 +154,11 @@ void main() {
154154
{'a': 'b'},
155155
'01234567-89ab-cdef-0123-0123456789ab'
156156
];
157-
expect(result!.columnDescriptions, hasLength(14));
158-
expect(result.columnDescriptions.first!.tableName, 't');
159-
expect(result.columnDescriptions.first!.columnName, 'i');
160-
expect(result.columnDescriptions.last!.tableName, 't');
161-
expect(result.columnDescriptions.last!.columnName, 'u');
157+
expect(result.columnDescriptions, hasLength(14));
158+
expect(result.columnDescriptions.first.tableName, 't');
159+
expect(result.columnDescriptions.first.columnName, 'i');
160+
expect(result.columnDescriptions.last.tableName, 't');
161+
expect(result.columnDescriptions.last.columnName, 'u');
162162
expect(result, [expectedRow]);
163163
result = await connection.query(
164164
'select i,s, bi, bs, bl, si, t, f, d, dt, ts, tsz, j, u from t');
@@ -363,13 +363,13 @@ void main() {
363363
() async {
364364
final rs1 = await connection
365365
.query('SELECT * FROM (VALUES (\'user@domain.com\')) t1 (col1)');
366-
expect(rs1!.first.toColumnMap(), {'col1': 'user@domain.com'});
366+
expect(rs1.first.toColumnMap(), {'col1': 'user@domain.com'});
367367

368368
final rs2 = await connection.query(
369369
'SELECT * FROM (VALUES (\'user@domain.com\')) t1 (col1) WHERE col1 > @u1',
370370
substitutionValues: {'u1': 'hello@domain.com'},
371371
);
372-
expect(rs2!.first.toColumnMap(), {'col1': 'user@domain.com'});
372+
expect(rs2.first.toColumnMap(), {'col1': 'user@domain.com'});
373373
});
374374

375375
test('Wrong type for parameter in substitution values fails', () async {

0 commit comments

Comments
 (0)