Properly unify result classes with list columns

This commit is contained in:
Simon Binder 2023-01-31 22:58:30 +01:00
parent b935d27248
commit f9912d5455
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
2 changed files with 89 additions and 22 deletions

View File

@ -1,3 +1,5 @@
import 'dart:collection';
import 'results/results.dart';
/// Transforms queries given in [inputs] so that their result sets respect
@ -66,38 +68,71 @@ Map<SqlSelectQuery, SqlSelectQuery> transformCustomResultClasses(
}
final referenceResult = queries.first.resultSet;
final dartNames = {
for (final column in referenceResult.columns)
column: referenceResult.dartNameFor(column),
};
var isFirst = true;
for (final query in queries) {
final newResultSet = InferredResultSet(
null,
query.resultSet.columns,
resultClassName: resultSetName,
// Only generate a result class for the first query in the group
dontGenerateResultClass: !isFirst,
);
// Make sure compatible columns in the two result sets have the same
// Dart name.
newResultSet.forceDartNames({
for (final entry in dartNames.entries)
newResultSet.columns.singleWhere((e) => e.isCompatibleTo(entry.key)):
entry.value,
});
final newResultSet =
_makeResultSetsCompatible(query.resultSet, referenceResult);
final newQuery = query.replaceResultSet(newResultSet);
replacements[query] = newQuery;
isFirst = false;
}
}
return replacements;
}
InferredResultSet _makeResultSetsCompatible(
InferredResultSet target, InferredResultSet reference) {
var columns = target.columns;
Map<ResultColumn, String>? newNames;
if (target != reference) {
// Make sure the result sets agree on the Dart column names to use.
final remainingColumns = LinkedHashSet.of(target.columns);
newNames = <ResultColumn, String>{};
columns = [];
for (final column in reference.columns) {
var columnFromThisResultSet =
remainingColumns.firstWhere((e) => e.isCompatibleTo(column));
remainingColumns.remove(columnFromThisResultSet);
newNames[columnFromThisResultSet] = reference.dartNameFor(column);
// For list columns, we need to apply the same unification to the
// result set of this column as well.
if (columnFromThisResultSet is NestedResultQuery) {
final nested = columnFromThisResultSet.query.resultSet;
if (nested.needsOwnClass) {
final transformed = _makeResultSetsCompatible(
nested,
(column as NestedResultQuery).query.resultSet,
);
columnFromThisResultSet = NestedResultQuery(
from: columnFromThisResultSet.from,
query: columnFromThisResultSet.query.replaceResultSet(transformed),
);
}
}
columns.add(columnFromThisResultSet);
}
}
final newResultSet = InferredResultSet(
null,
columns,
resultClassName: reference.resultClassName,
// Only generate a result class for the first query in the group
dontGenerateResultClass: target != reference,
);
if (newNames != null) {
newResultSet.forceDartNames(newNames);
}
return newResultSet;
}
bool _resultSetsCompatible(Iterable<InferredResultSet> resultSets) {
InferredResultSet? last;

View File

@ -115,4 +115,36 @@ getTitlesWithGroupOther AS GroupWithTitles: SELECT group_name, LIST(SELECT title
)),
}, results.dartOutputs, results);
});
test('supports query with two list columns', () async {
var queries = await analyzeQueries('''
CREATE TABLE books (
id INT NOT NULL PRIMARY KEY,
title TEXT NULL,
group_name TEXT NULL
);
a AS TitleList: SELECT LIST(SELECT title FROM books), LIST(SELECT id, title FROM books) FROM books;
b AS TitleList: SELECT LIST(SELECT title FROM books), LIST(SELECT id, title FROM books) FROM books;
''');
final errors = <String>[];
queries = transformCustomResultClasses(queries, errors.add).values;
expect(errors, isEmpty);
final a = queries.first;
final b = queries.last;
expect(a.name, 'a');
expect(b.name, 'b');
expect(a.resultClassName, 'TitleList');
expect(b.resultClassName, 'TitleList');
expect(a.resultSet?.dontGenerateResultClass, isFalse);
expect(b.resultSet?.dontGenerateResultClass, isTrue);
final nestedA = a.resultSet?.nestedResults.last as NestedResultQuery;
final nestedB = b.resultSet?.nestedResults.last as NestedResultQuery;
expect(nestedA.query.resultSet.needsOwnClass, isTrue);
expect(nestedB.query.resultSet.needsOwnClass, isFalse);
});
}