Make order by placeholders optional (#998)

This commit is contained in:
Simon Binder 2021-01-08 22:30:17 +01:00
parent 5e8cd53bc6
commit b8eed2f75b
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
8 changed files with 50 additions and 33 deletions

View File

@ -437,14 +437,7 @@ abstract class DatabaseConnectionUser {
if (hasMultipleTables != null) {
context.hasMultipleTables = hasMultipleTables;
}
// we don't want ORDER BY clauses to write the ORDER BY tokens because those
// are already declared in sql
if (component is OrderBy) {
component.writeInto(context, writeOrderBy: false);
} else {
component.writeInto(context);
}
return context;
}

View File

@ -56,14 +56,18 @@ class OrderBy extends Component {
final List<OrderingTerm> terms;
/// Constructs an order by clause by the [terms].
OrderBy(this.terms);
const OrderBy(this.terms);
/// Orders by nothing.
///
/// In this case, the ordering of result rows is undefined.
const OrderBy.nothing() : this(const []);
@override
void writeInto(GenerationContext context, {bool writeOrderBy = true}) {
if (writeOrderBy) {
context.buffer.write('ORDER BY ');
}
void writeInto(GenerationContext context) {
if (terms.isEmpty) return;
context.buffer.write('ORDER BY ');
_writeCommaSeparated(context, terms);
}
}

View File

@ -46,6 +46,9 @@ part 'migration.dart';
/// A component is anything that can appear in a sql query.
abstract class Component {
/// Default, constant constructor.
const Component();
/// Writes this component into the [context] by writing to its
/// [GenerationContext.buffer] or by introducing bound variables. When writing
/// into the buffer, no whitespace around the this component should be

View File

@ -1589,14 +1589,14 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
}
Selectable<Config> readMultiple(List<String> var1,
{required OrderBy clause}) {
{OrderBy clause = const OrderBy.nothing()}) {
var $arrayStartIndex = 1;
final expandedvar1 = $expandVar($arrayStartIndex, var1.length);
$arrayStartIndex += var1.length;
final generatedclause = $write(clause);
$arrayStartIndex += generatedclause.amountOfVariables;
return customSelect(
'SELECT * FROM config WHERE config_key IN ($expandedvar1) ORDER BY ${generatedclause.sql}',
'SELECT * FROM config WHERE config_key IN ($expandedvar1) ${generatedclause.sql}',
variables: [
for (var $ in var1) Variable<String>($),
...generatedclause.introducedVariables

View File

@ -131,6 +131,11 @@ void main() {
verify(mock.runSelect(argThat(contains('with_defaults.a')), any));
});
test('order by-params are ignored by default', () async {
await db.readMultiple(['foo']).get();
verify(mock.runSelect(argThat(isNot(contains('with_defaults.a'))), any));
});
test('runs queries with nested results', () async {
const row = {
'a': 'text for a',

View File

@ -93,6 +93,7 @@ MoorOptions _$MoorOptionsFromJson(Map<String, dynamic> json) {
'modules': 'sqlite_modules',
'rawResultSetData': 'raw_result_set_data',
'generateValuesInCopyWith': 'generate_values_in_copy_with',
'generateNamedParameters': 'named_parameters',
});
}

View File

@ -250,7 +250,7 @@ class QueryWriter {
var needsComma = false;
for (final element in query.elements) {
// Placeholders with a default value generate optional (and thus, named)
// parameters. Since moor 3.5, we have an option to also generate named
// parameters. Since moor 4, we have an option to also generate named
// parameters for named variables.
final isNamed =
(element is FoundDartPlaceholder && element.defaultValue != null) ||
@ -277,23 +277,34 @@ class QueryWriter {
if (needsComma) _buffer.write(', ');
needsComma = true;
final type = optional.dartTypeCode(scope.generationOptions);
if (optional is FoundDartPlaceholder && optional.defaultValue != null) {
// Wrap the default expression in parentheses to avoid issues with the
// surrounding precedence in SQL.
String defaultCode;
if (optional is FoundDartPlaceholder) {
final kind = optional.type;
if (kind == DartPlaceholderType.expression &&
optional.defaultValue != null) {
// Wrap the default expression in parentheses to avoid issues with
// the surrounding precedence in SQL.
final defaultSql =
"'(${escapeForDart(optional.defaultValue.toSql())})'";
_buffer.write('$type ${optional.dartParameterName} = '
'const CustomExpression($defaultSql)');
} else {
defaultCode = 'const CustomExpression($defaultSql)';
} else if (kind == DartPlaceholderType.orderBy) {
defaultCode = 'const OrderBy.nothing()';
}
}
final type = optional.dartTypeCode(scope.generationOptions);
// No default value, this element is required if it's not nullable
final isNullable =
optional is FoundVariable && optional.nullableInDart;
if (!isNullable) {
final isNullable = optional is FoundVariable && optional.nullableInDart;
final isRequired = !isNullable && defaultCode == null;
if (isRequired) {
_buffer..write(scope.required)..write(' ');
}
_buffer.write('$type ${optional.dartParameterName}');
if (defaultCode != null) {
_buffer..write(' = ')..write(defaultCode);
}
}

View File

@ -478,7 +478,7 @@ mixin CrudParser on ParserBase {
if (terms.length == 1 && terms.single is DartOrderingTermPlaceholder) {
final termPlaceholder = terms.single as DartOrderingTermPlaceholder;
return DartOrderByPlaceholder(name: termPlaceholder.name)
..setSpan(termPlaceholder.first!, termPlaceholder.last!);
..setSpan(orderToken, termPlaceholder.last!);
}
return OrderBy(terms: terms)..setSpan(orderToken, _previous);