mirror of https://github.com/AMT-Cheif/drift.git
Option to generate functions for Dart placeholders
This commit is contained in:
parent
e87e4d7a7a
commit
eb362effe8
|
@ -13,6 +13,7 @@ targets:
|
||||||
generate_values_in_copy_with: true
|
generate_values_in_copy_with: true
|
||||||
named_parameters: true
|
named_parameters: true
|
||||||
new_sql_code_generation: true
|
new_sql_code_generation: true
|
||||||
|
scoped_dart_components: true
|
||||||
sqlite:
|
sqlite:
|
||||||
version: "3.35"
|
version: "3.35"
|
||||||
modules:
|
modules:
|
||||||
|
|
|
@ -1650,11 +1650,11 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Selectable<Config> readMultiple(List<String> var1,
|
Selectable<Config> readMultiple(List<String> var1,
|
||||||
{OrderBy clause = const OrderBy.nothing()}) {
|
{OrderBy Function(ConfigTable config) clause = _$moor$default$0}) {
|
||||||
var $arrayStartIndex = 1;
|
var $arrayStartIndex = 1;
|
||||||
final expandedvar1 = $expandVar($arrayStartIndex, var1.length);
|
final expandedvar1 = $expandVar($arrayStartIndex, var1.length);
|
||||||
$arrayStartIndex += var1.length;
|
$arrayStartIndex += var1.length;
|
||||||
final generatedclause = $write(clause);
|
final generatedclause = $write(clause(this.config));
|
||||||
$arrayStartIndex += generatedclause.amountOfVariables;
|
$arrayStartIndex += generatedclause.amountOfVariables;
|
||||||
return customSelect(
|
return customSelect(
|
||||||
'SELECT * FROM config WHERE config_key IN ($expandedvar1) ${generatedclause.sql}',
|
'SELECT * FROM config WHERE config_key IN ($expandedvar1) ${generatedclause.sql}',
|
||||||
|
@ -1668,17 +1668,18 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Selectable<Config> readDynamic(
|
Selectable<Config> readDynamic(
|
||||||
{Expression<bool> predicate = const CustomExpression('(TRUE)')}) {
|
{Expression<bool> Function(ConfigTable config) predicate =
|
||||||
final generatedpredicate = $write(predicate);
|
_$moor$default$1}) {
|
||||||
|
final generatedpredicate = $write(predicate(this.config));
|
||||||
return customSelect('SELECT * FROM config WHERE ${generatedpredicate.sql}',
|
return customSelect('SELECT * FROM config WHERE ${generatedpredicate.sql}',
|
||||||
variables: [...generatedpredicate.introducedVariables],
|
variables: [...generatedpredicate.introducedVariables],
|
||||||
readsFrom: {config}).map(config.mapFromRow);
|
readsFrom: {config}).map(config.mapFromRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
Selectable<String> typeConverterVar(SyncType? var1, List<SyncType?> var2,
|
Selectable<String> typeConverterVar(SyncType? var1, List<SyncType?> var2,
|
||||||
{Expression<bool> pred = const CustomExpression('(TRUE)')}) {
|
{Expression<bool> Function(ConfigTable config) pred = _$moor$default$2}) {
|
||||||
var $arrayStartIndex = 2;
|
var $arrayStartIndex = 2;
|
||||||
final generatedpred = $write(pred);
|
final generatedpred = $write(pred(this.config));
|
||||||
$arrayStartIndex += generatedpred.amountOfVariables;
|
$arrayStartIndex += generatedpred.amountOfVariables;
|
||||||
final expandedvar2 = $expandVar($arrayStartIndex, var2.length);
|
final expandedvar2 = $expandVar($arrayStartIndex, var2.length);
|
||||||
$arrayStartIndex += var2.length;
|
$arrayStartIndex += var2.length;
|
||||||
|
@ -1721,8 +1722,13 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Selectable<MultipleResult> multiple({required Expression<bool> predicate}) {
|
Selectable<MultipleResult> multiple(
|
||||||
final generatedpredicate = $write(predicate, hasMultipleTables: true);
|
{required Expression<bool> Function(WithDefaults d, WithConstraints c)
|
||||||
|
predicate}) {
|
||||||
|
final generatedpredicate = $write(
|
||||||
|
predicate(
|
||||||
|
alias(this.withDefaults, 'd'), alias(this.withConstraints, 'c')),
|
||||||
|
hasMultipleTables: true);
|
||||||
return customSelect(
|
return customSelect(
|
||||||
'SELECT d.*,"c"."a" AS "nested_0.a", "c"."b" AS "nested_0.b", "c"."c" AS "nested_0.c" FROM with_defaults AS d LEFT OUTER JOIN with_constraints AS c ON d.a = c.a AND d.b = c.b WHERE ${generatedpredicate.sql}',
|
'SELECT d.*,"c"."a" AS "nested_0.a", "c"."b" AS "nested_0.b", "c"."c" AS "nested_0.c" FROM with_defaults AS d LEFT OUTER JOIN with_constraints AS c ON d.a = c.a AND d.b = c.b WHERE ${generatedpredicate.sql}',
|
||||||
variables: [...generatedpredicate.introducedVariables],
|
variables: [...generatedpredicate.introducedVariables],
|
||||||
|
@ -1743,8 +1749,9 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
|
||||||
readsFrom: {email}).map(email.mapFromRow);
|
readsFrom: {email}).map(email.mapFromRow);
|
||||||
}
|
}
|
||||||
|
|
||||||
Selectable<ReadRowIdResult> readRowId({required Expression<int> expr}) {
|
Selectable<ReadRowIdResult> readRowId(
|
||||||
final generatedexpr = $write(expr);
|
{required Expression<int> Function(ConfigTable config) expr}) {
|
||||||
|
final generatedexpr = $write(expr(this.config));
|
||||||
return customSelect(
|
return customSelect(
|
||||||
'SELECT oid, * FROM config WHERE _rowid_ = ${generatedexpr.sql}',
|
'SELECT oid, * FROM config WHERE _rowid_ = ${generatedexpr.sql}',
|
||||||
variables: [...generatedexpr.introducedVariables],
|
variables: [...generatedexpr.introducedVariables],
|
||||||
|
@ -1837,6 +1844,12 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OrderBy _$moor$default$0(ConfigTable _) => const OrderBy.nothing();
|
||||||
|
Expression<bool> _$moor$default$1(ConfigTable _) =>
|
||||||
|
const CustomExpression('(TRUE)');
|
||||||
|
Expression<bool> _$moor$default$2(ConfigTable _) =>
|
||||||
|
const CustomExpression('(TRUE)');
|
||||||
|
|
||||||
class JsonResult extends CustomResultSet {
|
class JsonResult extends CustomResultSet {
|
||||||
final String key;
|
final String key;
|
||||||
final String? value;
|
final String? value;
|
||||||
|
|
|
@ -8,7 +8,7 @@ CREATE TABLE no_ids (
|
||||||
CREATE TABLE with_defaults (
|
CREATE TABLE with_defaults (
|
||||||
a TEXT DEFAULT 'something',
|
a TEXT DEFAULT 'something',
|
||||||
b INT UNIQUE
|
b INT UNIQUE
|
||||||
) ;
|
);
|
||||||
|
|
||||||
CREATE TABLE with_constraints (
|
CREATE TABLE with_constraints (
|
||||||
a TEXT,
|
a TEXT,
|
||||||
|
|
|
@ -103,7 +103,8 @@ void main() {
|
||||||
|
|
||||||
test('runs queries with arrays and Dart templates', () async {
|
test('runs queries with arrays and Dart templates', () async {
|
||||||
await db.readMultiple(['a', 'b'],
|
await db.readMultiple(['a', 'b'],
|
||||||
clause: OrderBy([OrderingTerm(expression: db.config.configKey)])).get();
|
clause: (config) =>
|
||||||
|
OrderBy([OrderingTerm(expression: config.configKey)])).get();
|
||||||
|
|
||||||
verify(mock.runSelect(
|
verify(mock.runSelect(
|
||||||
'SELECT * FROM config WHERE config_key IN (?1, ?2) '
|
'SELECT * FROM config WHERE config_key IN (?1, ?2) '
|
||||||
|
@ -118,7 +119,7 @@ void main() {
|
||||||
.thenAnswer((_) => Future.value([mockResponse]));
|
.thenAnswer((_) => Future.value([mockResponse]));
|
||||||
|
|
||||||
final parsed = await db
|
final parsed = await db
|
||||||
.readDynamic(predicate: db.config.configKey.equals('key'))
|
.readDynamic(predicate: (config) => config.configKey.equals('key'))
|
||||||
.getSingle();
|
.getSingle();
|
||||||
|
|
||||||
verify(
|
verify(
|
||||||
|
@ -133,9 +134,9 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('columns use table names in queries with multiple tables', () async {
|
test('columns use table names in queries with multiple tables', () async {
|
||||||
await db.multiple(predicate: db.withDefaults.a.equals('foo')).get();
|
await db.multiple(predicate: (d, c) => d.a.equals('foo')).get();
|
||||||
|
|
||||||
verify(mock.runSelect(argThat(contains('with_defaults.a')), any));
|
verify(mock.runSelect(argThat(contains('d.a = ?')), any));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('order by-params are ignored by default', () async {
|
test('order by-params are ignored by default', () async {
|
||||||
|
@ -156,8 +157,9 @@ void main() {
|
||||||
return Future.value([row]);
|
return Future.value([row]);
|
||||||
});
|
});
|
||||||
|
|
||||||
final result =
|
final result = await db
|
||||||
await db.multiple(predicate: const Constant(true)).getSingle();
|
.multiple(predicate: (_, __) => const Constant(true))
|
||||||
|
.getSingle();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
result,
|
result,
|
||||||
|
@ -183,8 +185,9 @@ void main() {
|
||||||
return Future.value([row]);
|
return Future.value([row]);
|
||||||
});
|
});
|
||||||
|
|
||||||
final result =
|
final result = await db
|
||||||
await db.multiple(predicate: const Constant(true)).getSingle();
|
.multiple(predicate: (_, __) => const Constant(true))
|
||||||
|
.getSingle();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
result,
|
result,
|
||||||
|
|
|
@ -91,6 +91,9 @@ class MoorOptions {
|
||||||
@JsonKey(name: 'new_sql_code_generation', defaultValue: false)
|
@JsonKey(name: 'new_sql_code_generation', defaultValue: false)
|
||||||
final bool newSqlCodeGeneration;
|
final bool newSqlCodeGeneration;
|
||||||
|
|
||||||
|
@JsonKey(name: 'scoped_dart_components', defaultValue: false)
|
||||||
|
final bool scopedDartComponents;
|
||||||
|
|
||||||
@internal
|
@internal
|
||||||
const MoorOptions.defaults({
|
const MoorOptions.defaults({
|
||||||
this.generateFromJsonStringConstructor = false,
|
this.generateFromJsonStringConstructor = false,
|
||||||
|
@ -108,6 +111,7 @@ class MoorOptions {
|
||||||
this.generateValuesInCopyWith = false,
|
this.generateValuesInCopyWith = false,
|
||||||
this.generateNamedParameters = false,
|
this.generateNamedParameters = false,
|
||||||
this.newSqlCodeGeneration = false,
|
this.newSqlCodeGeneration = false,
|
||||||
|
this.scopedDartComponents = false,
|
||||||
this.modules = const [],
|
this.modules = const [],
|
||||||
this.sqliteAnalysisOptions,
|
this.sqliteAnalysisOptions,
|
||||||
});
|
});
|
||||||
|
@ -128,6 +132,7 @@ class MoorOptions {
|
||||||
required this.generateValuesInCopyWith,
|
required this.generateValuesInCopyWith,
|
||||||
required this.generateNamedParameters,
|
required this.generateNamedParameters,
|
||||||
required this.newSqlCodeGeneration,
|
required this.newSqlCodeGeneration,
|
||||||
|
required this.scopedDartComponents,
|
||||||
required this.modules,
|
required this.modules,
|
||||||
required this.sqliteAnalysisOptions,
|
required this.sqliteAnalysisOptions,
|
||||||
}) {
|
}) {
|
||||||
|
|
|
@ -25,7 +25,8 @@ MoorOptions _$MoorOptionsFromJson(Map json) {
|
||||||
'apply_converters_on_variables',
|
'apply_converters_on_variables',
|
||||||
'generate_values_in_copy_with',
|
'generate_values_in_copy_with',
|
||||||
'named_parameters',
|
'named_parameters',
|
||||||
'new_sql_code_generation'
|
'new_sql_code_generation',
|
||||||
|
'scoped_dart_components'
|
||||||
]);
|
]);
|
||||||
final val = MoorOptions(
|
final val = MoorOptions(
|
||||||
generateFromJsonStringConstructor: $checkedConvert(
|
generateFromJsonStringConstructor: $checkedConvert(
|
||||||
|
@ -73,6 +74,9 @@ MoorOptions _$MoorOptionsFromJson(Map json) {
|
||||||
newSqlCodeGeneration:
|
newSqlCodeGeneration:
|
||||||
$checkedConvert(json, 'new_sql_code_generation', (v) => v as bool?) ??
|
$checkedConvert(json, 'new_sql_code_generation', (v) => v as bool?) ??
|
||||||
false,
|
false,
|
||||||
|
scopedDartComponents:
|
||||||
|
$checkedConvert(json, 'scoped_dart_components', (v) => v as bool?) ??
|
||||||
|
false,
|
||||||
modules: $checkedConvert(
|
modules: $checkedConvert(
|
||||||
json,
|
json,
|
||||||
'sqlite_modules',
|
'sqlite_modules',
|
||||||
|
@ -102,6 +106,7 @@ MoorOptions _$MoorOptionsFromJson(Map json) {
|
||||||
'generateValuesInCopyWith': 'generate_values_in_copy_with',
|
'generateValuesInCopyWith': 'generate_values_in_copy_with',
|
||||||
'generateNamedParameters': 'named_parameters',
|
'generateNamedParameters': 'named_parameters',
|
||||||
'newSqlCodeGeneration': 'new_sql_code_generation',
|
'newSqlCodeGeneration': 'new_sql_code_generation',
|
||||||
|
'scopedDartComponents': 'scoped_dart_components',
|
||||||
'modules': 'sqlite_modules',
|
'modules': 'sqlite_modules',
|
||||||
'sqliteAnalysisOptions': 'sqlite'
|
'sqliteAnalysisOptions': 'sqlite'
|
||||||
});
|
});
|
||||||
|
|
|
@ -278,7 +278,38 @@ class TypeMapper {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return FoundDartPlaceholder(type, name)..astNode = placeholder;
|
final availableResults =
|
||||||
|
placeholder.scope.allOf<ResultSetAvailableInStatement>();
|
||||||
|
final availableMoorResults = <AvailableMoorResultSet>[];
|
||||||
|
for (final available in availableResults) {
|
||||||
|
final aliasedResultSet = available.resultSet.resultSet;
|
||||||
|
final resultSet = aliasedResultSet?.unalias();
|
||||||
|
String name;
|
||||||
|
if (aliasedResultSet is NamedResultSet) {
|
||||||
|
name = aliasedResultSet.name;
|
||||||
|
} else {
|
||||||
|
// If we don't have a name we can't include this result set.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MoorEntityWithResultSet moorEntity;
|
||||||
|
|
||||||
|
if (resultSet is Table) {
|
||||||
|
moorEntity = tableToMoor(resultSet);
|
||||||
|
} else if (resultSet is View) {
|
||||||
|
moorEntity = viewToMoor(resultSet);
|
||||||
|
} else {
|
||||||
|
// If this result set is an inner select statement or anything else we
|
||||||
|
// can't represent it in Dart.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
availableMoorResults
|
||||||
|
.add(AvailableMoorResultSet(name, moorEntity, available));
|
||||||
|
}
|
||||||
|
|
||||||
|
return FoundDartPlaceholder(type, name, availableMoorResults)
|
||||||
|
..astNode = placeholder;
|
||||||
}
|
}
|
||||||
|
|
||||||
MoorTable tableToMoor(Table table) {
|
MoorTable tableToMoor(Table table) {
|
||||||
|
|
|
@ -36,4 +36,9 @@ abstract class MoorEntityWithResultSet extends MoorSchemaEntity {
|
||||||
/// The name of the Dart class storing additional properties like type
|
/// The name of the Dart class storing additional properties like type
|
||||||
/// converters.
|
/// converters.
|
||||||
String get entityInfoName;
|
String get entityInfoName;
|
||||||
|
|
||||||
|
/// The name of the Dart class storing the right column getters for this type.
|
||||||
|
///
|
||||||
|
/// This class is equal to, or a superclass of, [entityInfoName].
|
||||||
|
String get dslName => entityInfoName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:moor/moor.dart' show $mrjf, $mrjc, UpdateKind;
|
import 'package:moor/moor.dart' show $mrjf, $mrjc, UpdateKind;
|
||||||
|
import 'package:moor_generator/src/analyzer/options.dart';
|
||||||
import 'package:moor_generator/src/analyzer/runner/results.dart';
|
import 'package:moor_generator/src/analyzer/runner/results.dart';
|
||||||
import 'package:moor_generator/src/model/base_entity.dart';
|
import 'package:moor_generator/src/model/base_entity.dart';
|
||||||
import 'package:moor_generator/src/utils/hash.dart';
|
import 'package:moor_generator/src/utils/hash.dart';
|
||||||
|
@ -599,10 +600,41 @@ class InsertableDartPlaceholderType extends DartPlaceholderType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Dart placeholder that will be bound at runtime.
|
/// A Dart placeholder that will be bound to a dynamically-generated SQL node
|
||||||
|
/// at runtime.
|
||||||
|
///
|
||||||
|
/// Moor supports injecting expressions, order by terms and clauses and limit
|
||||||
|
/// clauses as placeholders. For insert statements, companions can be used
|
||||||
|
/// as a Dart placeholder too.
|
||||||
class FoundDartPlaceholder extends FoundElement {
|
class FoundDartPlaceholder extends FoundElement {
|
||||||
final DartPlaceholderType type;
|
final DartPlaceholderType type;
|
||||||
|
|
||||||
|
/// All result sets that are available for this Dart placeholder.
|
||||||
|
///
|
||||||
|
/// When queries are operating on multiple tables, especially if some of those
|
||||||
|
/// tables have aliases, it may be hard to reflect the name of those tables
|
||||||
|
/// at runtime.
|
||||||
|
/// For instance, consider this query:
|
||||||
|
///
|
||||||
|
/// ```sql
|
||||||
|
/// myQuery: SELECT a.**, b.** FROM users a
|
||||||
|
/// INNER JOIN friends f ON f.a_id = a.id
|
||||||
|
/// INNER JOIN users b ON b.id = f.b_id
|
||||||
|
/// WHERE $expression;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Here `$expression` is a Dart-defined expression evaluating to an sql
|
||||||
|
/// boolean.
|
||||||
|
/// Moor uses to add a `Expression<bool>` parameter to the generated query
|
||||||
|
/// method. Unfortunately, this puts the burden of picking the right table
|
||||||
|
/// name on the user. For instance, they may have to use
|
||||||
|
/// `alias('a', users).someColumn` to avoid getting an runtime exception.
|
||||||
|
/// With a new build option, moor instead generates a
|
||||||
|
/// `Expression<bool> Function(Users a, Users b, Friends f)` function as a
|
||||||
|
/// parameter. This allows users to access the right aliases right away,
|
||||||
|
/// reducing potential for misuse.
|
||||||
|
final List<AvailableMoorResultSet> availableResultSets;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final String name;
|
final String name;
|
||||||
DartPlaceholder astNode;
|
DartPlaceholder astNode;
|
||||||
|
@ -611,26 +643,67 @@ class FoundDartPlaceholder extends FoundElement {
|
||||||
type is ExpressionDartPlaceholderType &&
|
type is ExpressionDartPlaceholderType &&
|
||||||
(type as ExpressionDartPlaceholderType).defaultValue != null;
|
(type as ExpressionDartPlaceholderType).defaultValue != null;
|
||||||
|
|
||||||
FoundDartPlaceholder(this.type, this.name);
|
FoundDartPlaceholder(this.type, this.name, this.availableResultSets);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get dartParameterName => name;
|
String get dartParameterName => name;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => hashAll([type, name]);
|
int get hashCode => hashAll([type, name, ...availableResultSets]);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(dynamic other) {
|
bool operator ==(dynamic other) {
|
||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
other is FoundDartPlaceholder &&
|
other is FoundDartPlaceholder &&
|
||||||
other.type == type &&
|
other.type == type &&
|
||||||
other.name == name;
|
other.name == name &&
|
||||||
|
const ListEquality()
|
||||||
|
.equals(other.availableResultSets, availableResultSets);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String dartTypeCode([GenerationOptions options = const GenerationOptions()]) {
|
String dartTypeCode([GenerationOptions options = const GenerationOptions()]) {
|
||||||
return type.parameterTypeCode(options);
|
return type.parameterTypeCode(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether we should write this parameter as a function having available
|
||||||
|
/// result sets as parameters.
|
||||||
|
bool writeAsScopedFunction(MoorOptions options) {
|
||||||
|
return options.scopedDartComponents &&
|
||||||
|
availableResultSets.isNotEmpty &&
|
||||||
|
// Don't generate scoped functions for insertables, where the Dart type
|
||||||
|
// already defines which fields are available
|
||||||
|
type is! InsertableDartPlaceholderType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A table or view that is available in the position of a
|
||||||
|
/// [FoundDartPlaceholder].
|
||||||
|
///
|
||||||
|
/// For more information, see [FoundDartPlaceholder.availableResultSets].
|
||||||
|
class AvailableMoorResultSet {
|
||||||
|
/// The (potentially aliased) name of this result set.
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// The table or view that is available.
|
||||||
|
final MoorEntityWithResultSet entity;
|
||||||
|
|
||||||
|
final ResultSetAvailableInStatement source;
|
||||||
|
|
||||||
|
AvailableMoorResultSet(this.name, this.entity, [this.source]);
|
||||||
|
|
||||||
|
/// The argument type of this result set when used in a scoped function.
|
||||||
|
String get argumentType => entity.dslName;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => hashAll([name, entity]);
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is AvailableMoorResultSet &&
|
||||||
|
other.name == name &&
|
||||||
|
other.entity == entity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ResultColumnEquality implements Equality<ResultColumn> {
|
class _ResultColumnEquality implements Equality<ResultColumn> {
|
||||||
|
|
|
@ -41,6 +41,9 @@ class MoorTable implements MoorEntityWithResultSet {
|
||||||
|
|
||||||
String get _baseName => _overriddenName ?? fromClass.name;
|
String get _baseName => _overriddenName ?? fromClass.name;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get dslName => fromClass?.name ?? entityInfoName;
|
||||||
|
|
||||||
/// The columns declared in this table.
|
/// The columns declared in this table.
|
||||||
@override
|
@override
|
||||||
final List<MoorColumn> columns;
|
final List<MoorColumn> columns;
|
||||||
|
|
|
@ -281,6 +281,22 @@ class QueryWriter {
|
||||||
void _writeParameters() {
|
void _writeParameters() {
|
||||||
final namedElements = <FoundElement>[];
|
final namedElements = <FoundElement>[];
|
||||||
|
|
||||||
|
String typeFor(FoundElement element) {
|
||||||
|
var type = element.dartTypeCode(scope.generationOptions);
|
||||||
|
|
||||||
|
if (element is FoundDartPlaceholder &&
|
||||||
|
element.writeAsScopedFunction(options)) {
|
||||||
|
// Generate a function providing result sets that are in scope as args
|
||||||
|
|
||||||
|
final args = element.availableResultSets
|
||||||
|
.map((e) => '${e.argumentType} ${e.name}')
|
||||||
|
.join(', ');
|
||||||
|
type = '$type Function($args)';
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
var needsComma = false;
|
var needsComma = false;
|
||||||
for (final element in query.elements) {
|
for (final element in query.elements) {
|
||||||
// Placeholders with a default value generate optional (and thus, named)
|
// Placeholders with a default value generate optional (and thus, named)
|
||||||
|
@ -294,7 +310,7 @@ class QueryWriter {
|
||||||
} else {
|
} else {
|
||||||
if (needsComma) _buffer.write(', ');
|
if (needsComma) _buffer.write(', ');
|
||||||
|
|
||||||
final type = element.dartTypeCode(scope.generationOptions);
|
final type = typeFor(element);
|
||||||
_buffer.write('$type ${element.dartParameterName}');
|
_buffer.write('$type ${element.dartParameterName}');
|
||||||
needsComma = true;
|
needsComma = true;
|
||||||
}
|
}
|
||||||
|
@ -325,9 +341,37 @@ class QueryWriter {
|
||||||
kind.kind == SimpleDartPlaceholderKind.orderBy) {
|
kind.kind == SimpleDartPlaceholderKind.orderBy) {
|
||||||
defaultCode = 'const OrderBy.nothing()';
|
defaultCode = 'const OrderBy.nothing()';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the parameter is converted to a scoped function, we also need to
|
||||||
|
// generate a scoped function as a default value. Since defaults have
|
||||||
|
// to be constants, we generate a top-level function which is then
|
||||||
|
// used as a tear-off.
|
||||||
|
if (optional.writeAsScopedFunction(options) && defaultCode != null) {
|
||||||
|
final root = scope.root;
|
||||||
|
final counter = root.counter++;
|
||||||
|
// ignore: prefer_interpolation_to_compose_strings
|
||||||
|
final functionName = r'_$moor$default$' + counter.toString();
|
||||||
|
|
||||||
|
final buffer = root.leaf()
|
||||||
|
..write(optional.dartTypeCode(scope.generationOptions))
|
||||||
|
..write(' ')
|
||||||
|
..write(functionName)
|
||||||
|
..write('(');
|
||||||
|
var i = 0;
|
||||||
|
for (final arg in optional.availableResultSets) {
|
||||||
|
if (i != 0) buffer.write(', ');
|
||||||
|
|
||||||
|
buffer..write(arg.argumentType)..write(' ')..write('_' * (i + 1));
|
||||||
|
}
|
||||||
|
buffer..write(') => ')..write(defaultCode)..write(';');
|
||||||
|
|
||||||
|
// With the function being written, the default code is just a tear-
|
||||||
|
// off of that function
|
||||||
|
defaultCode = functionName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final type = optional.dartTypeCode(scope.generationOptions);
|
final type = typeFor(optional);
|
||||||
|
|
||||||
// No default value, this element is required if it's not nullable
|
// No default value, this element is required if it's not nullable
|
||||||
var isMarkedAsRequired = false;
|
var isMarkedAsRequired = false;
|
||||||
|
@ -436,6 +480,26 @@ class QueryWriter {
|
||||||
} else if (element is FoundDartPlaceholder) {
|
} else if (element is FoundDartPlaceholder) {
|
||||||
_writeIndexCounterIfNeeded();
|
_writeIndexCounterIfNeeded();
|
||||||
|
|
||||||
|
String useExpression() {
|
||||||
|
if (element.writeAsScopedFunction(scope.options)) {
|
||||||
|
// The parameter is a function type that needs to be evaluated first
|
||||||
|
final args = element.availableResultSets.map((e) {
|
||||||
|
final table = 'this.${e.entity.dbGetterName}';
|
||||||
|
final needsAlias = e.name != e.entity.displayName;
|
||||||
|
|
||||||
|
if (needsAlias) {
|
||||||
|
return 'alias($table, ${asDartLiteral(e.name)})';
|
||||||
|
} else {
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
}).join(', ');
|
||||||
|
return '${element.dartParameterName}($args)';
|
||||||
|
} else {
|
||||||
|
// We can just use the parameter directly
|
||||||
|
return element.dartParameterName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_buffer
|
_buffer
|
||||||
..write('final ')
|
..write('final ')
|
||||||
..write(placeholderContextName(element))
|
..write(placeholderContextName(element))
|
||||||
|
@ -449,10 +513,10 @@ class QueryWriter {
|
||||||
..write(r'$writeInsertable(this.')
|
..write(r'$writeInsertable(this.')
|
||||||
..write(table?.dbGetterName)
|
..write(table?.dbGetterName)
|
||||||
..write(', ')
|
..write(', ')
|
||||||
..write(element.dartParameterName)
|
..write(useExpression())
|
||||||
..write(');\n');
|
..write(');\n');
|
||||||
} else {
|
} else {
|
||||||
_buffer..write(r'$write(')..write(element.dartParameterName);
|
_buffer..write(r'$write(')..write(useExpression());
|
||||||
if (query.hasMultipleTables) {
|
if (query.hasMultipleTables) {
|
||||||
_buffer.write(', hasMultipleTables: true');
|
_buffer.write(', hasMultipleTables: true');
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,11 @@ class Scope extends _Node {
|
||||||
final DartScope scope;
|
final DartScope scope;
|
||||||
final Writer writer;
|
final Writer writer;
|
||||||
|
|
||||||
|
/// An arbitrary counter.
|
||||||
|
///
|
||||||
|
/// This can be used to generated methods which must have a unique name-
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
Scope({@required Scope parent, Writer writer})
|
Scope({@required Scope parent, Writer writer})
|
||||||
: scope = parent?.scope?.nextLevel ?? DartScope.library,
|
: scope = parent?.scope?.nextLevel ?? DartScope.library,
|
||||||
writer = writer ?? parent?.writer,
|
writer = writer ?? parent?.writer,
|
||||||
|
|
|
@ -96,10 +96,23 @@ class ProgrammingLanguages extends Table {
|
||||||
expect(importQuery.declaredInMoorFile, isFalse);
|
expect(importQuery.declaredInMoorFile, isFalse);
|
||||||
expect(importQuery.hasMultipleTables, isFalse);
|
expect(importQuery.hasMultipleTables, isFalse);
|
||||||
expect(
|
expect(
|
||||||
importQuery.placeholders,
|
importQuery.placeholders,
|
||||||
contains(equals(FoundDartPlaceholder(
|
contains(
|
||||||
|
equals(
|
||||||
|
FoundDartPlaceholder(
|
||||||
SimpleDartPlaceholderType(SimpleDartPlaceholderKind.orderBy),
|
SimpleDartPlaceholderType(SimpleDartPlaceholderKind.orderBy),
|
||||||
'o'))));
|
'o',
|
||||||
|
[
|
||||||
|
AvailableMoorResultSet(
|
||||||
|
'programming_languages',
|
||||||
|
database.tables
|
||||||
|
.firstWhere((e) => e.sqlName == 'programming_languages'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
final librariesQuery = database.queries
|
final librariesQuery = database.queries
|
||||||
.singleWhere((q) => q.name == 'findLibraries') as SqlSelectQuery;
|
.singleWhere((q) => q.name == 'findLibraries') as SqlSelectQuery;
|
||||||
|
|
|
@ -34,7 +34,7 @@ totalDurationByArtist:
|
||||||
FROM artists a
|
FROM artists a
|
||||||
INNER JOIN albums ON albums.artist = a.id
|
INNER JOIN albums ON albums.artist = a.id
|
||||||
INNER JOIN tracks ON tracks.album = albums.id
|
INNER JOIN tracks ON tracks.album = albums.id
|
||||||
GROUP BY artists.id;
|
GROUP BY a.id;
|
||||||
'''
|
'''
|
||||||
}, options: const MoorOptions.defaults());
|
}, options: const MoorOptions.defaults());
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ CREATE TABLE routes (
|
||||||
);
|
);
|
||||||
|
|
||||||
allRoutes: SELECT routes.*, "from".**, "to".**
|
allRoutes: SELECT routes.*, "from".**, "to".**
|
||||||
FROM routes r
|
FROM routes
|
||||||
INNER JOIN points "from" ON "from".id = routes.from
|
INNER JOIN points "from" ON "from".id = routes.from
|
||||||
INNER JOIN points "to" ON "to".id = routes."to";
|
INNER JOIN points "to" ON "to".id = routes."to";
|
||||||
''',
|
''',
|
||||||
|
|
Loading…
Reference in New Issue