mirror of https://github.com/AMT-Cheif/drift.git
Correctly write variables in analyzed queries
This commit is contained in:
parent
807d1ddff0
commit
646987ad69
|
@ -56,9 +56,16 @@ class TableWithoutPK extends Table {
|
|||
tables: [TodosTable, Categories, Users, SharedTodos, TableWithoutPK],
|
||||
queries: [
|
||||
Sql(
|
||||
'allTodosWithCategory',
|
||||
'SELECT t.*, c.id as catId, c."desc" as catDesc '
|
||||
'FROM todos t INNER JOIN categories c ON c.id = t.category'),
|
||||
'allTodosWithCategory',
|
||||
'SELECT t.*, c.id as catId, c."desc" as catDesc '
|
||||
'FROM todos t INNER JOIN categories c ON c.id = t.category',
|
||||
),
|
||||
Sql(
|
||||
'todosForUser',
|
||||
'SELECT t.* FROM todos t '
|
||||
'INNER JOIN shared_todos st ON st.todo = t.id '
|
||||
'INNER JOIN users u ON u.id = st.user '
|
||||
'WHERE u.id = :user'),
|
||||
],
|
||||
)
|
||||
class TodoDb extends _$TodoDb {
|
||||
|
|
|
@ -1024,6 +1024,21 @@ class AllTodosWithCategoryResult {
|
|||
});
|
||||
}
|
||||
|
||||
class TodosForUserResult {
|
||||
final int id;
|
||||
final String title;
|
||||
final String content;
|
||||
final DateTime targetDate;
|
||||
final int category;
|
||||
TodosForUserResult({
|
||||
this.id,
|
||||
this.title,
|
||||
this.content,
|
||||
this.targetDate,
|
||||
this.category,
|
||||
});
|
||||
}
|
||||
|
||||
abstract class _$TodoDb extends GeneratedDatabase {
|
||||
_$TodoDb(QueryExecutor e) : super(const SqlTypeSystem.withDefaults(), e);
|
||||
$TodosTableTable _todosTable;
|
||||
|
@ -1051,16 +1066,44 @@ abstract class _$TodoDb extends GeneratedDatabase {
|
|||
|
||||
Future<List<AllTodosWithCategoryResult>> allTodosWithCategory() {
|
||||
return customSelect(
|
||||
'SELECT t.*, c.id as catId, c."desc" as catDesc FROM todos t INNER JOIN categories c ON c.id = t.category')
|
||||
'SELECT t.*, c.id as catId, c."desc" as catDesc FROM todos t INNER JOIN categories c ON c.id = t.category',
|
||||
variables: [])
|
||||
.then((rows) => rows.map(_rowToAllTodosWithCategoryResult).toList());
|
||||
}
|
||||
|
||||
Stream<List<AllTodosWithCategoryResult>> watchAllTodosWithCategory() {
|
||||
return customSelectStream(
|
||||
'SELECT t.*, c.id as catId, c."desc" as catDesc FROM todos t INNER JOIN categories c ON c.id = t.category')
|
||||
'SELECT t.*, c.id as catId, c."desc" as catDesc FROM todos t INNER JOIN categories c ON c.id = t.category',
|
||||
variables: [])
|
||||
.map((rows) => rows.map(_rowToAllTodosWithCategoryResult).toList());
|
||||
}
|
||||
|
||||
TodosForUserResult _rowToTodosForUserResult(QueryRow row) {
|
||||
return TodosForUserResult(
|
||||
id: row.readInt('id'),
|
||||
title: row.readString('title'),
|
||||
content: row.readString('content'),
|
||||
targetDate: row.readDateTime('target_date'),
|
||||
category: row.readInt('category'),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<TodosForUserResult>> todosForUser(int user) {
|
||||
return customSelect(
|
||||
'SELECT t.* FROM todos t INNER JOIN shared_todos st ON st.todo = t.id INNER JOIN users u ON u.id = st.user WHERE u.id = :user',
|
||||
variables: [
|
||||
Variable.withInt(user),
|
||||
]).then((rows) => rows.map(_rowToTodosForUserResult).toList());
|
||||
}
|
||||
|
||||
Stream<List<TodosForUserResult>> watchTodosForUser(int user) {
|
||||
return customSelectStream(
|
||||
'SELECT t.* FROM todos t INNER JOIN shared_todos st ON st.todo = t.id INNER JOIN users u ON u.id = st.user WHERE u.id = :user',
|
||||
variables: [
|
||||
Variable.withInt(user),
|
||||
]).map((rows) => rows.map(_rowToTodosForUserResult).toList());
|
||||
}
|
||||
|
||||
@override
|
||||
List<TableInfo> get allTables =>
|
||||
[todosTable, categories, users, sharedTodos, tableWithoutPK];
|
||||
|
|
|
@ -49,6 +49,17 @@ const Map<ColumnType, String> readFromMethods = {
|
|||
ColumnType.real: 'readDouble',
|
||||
};
|
||||
|
||||
/// Maps from a column type to code that can be used to create a variable of the
|
||||
/// respective type.
|
||||
const Map<ColumnType, String> createVariable = {
|
||||
ColumnType.boolean: 'Variable.withBool',
|
||||
ColumnType.text: 'Variable.withString',
|
||||
ColumnType.integer: 'Variable.withInt',
|
||||
ColumnType.datetime: 'Variable.withDateTime',
|
||||
ColumnType.blob: 'Variable.withBlob',
|
||||
ColumnType.real: 'Variable.withReal',
|
||||
};
|
||||
|
||||
/// A column, as specified by a getter in a table.
|
||||
class SpecifiedColumn {
|
||||
/// The getter name of this column in the table class. It will also be used
|
||||
|
|
|
@ -8,8 +8,9 @@ final _leadingDigits = RegExp(r'^\d*');
|
|||
abstract class SqlQuery {
|
||||
final String name;
|
||||
final String sql;
|
||||
final List<FoundVariable> variables;
|
||||
|
||||
SqlQuery(this.name, this.sql);
|
||||
SqlQuery(this.name, this.sql, this.variables);
|
||||
}
|
||||
|
||||
class SqlSelectQuery extends SqlQuery {
|
||||
|
@ -18,8 +19,9 @@ class SqlSelectQuery extends SqlQuery {
|
|||
|
||||
String get resultClassName => '${ReCase(name).pascalCase}Result';
|
||||
|
||||
SqlSelectQuery(String name, String sql, this.readsFrom, this.resultSet)
|
||||
: super(name, sql);
|
||||
SqlSelectQuery(String name, String sql, List<FoundVariable> variables,
|
||||
this.readsFrom, this.resultSet)
|
||||
: super(name, sql, variables);
|
||||
}
|
||||
|
||||
class InferredResultSet {
|
||||
|
@ -70,3 +72,14 @@ class ResultColumn {
|
|||
|
||||
ResultColumn(this.name, this.type, this.nullable);
|
||||
}
|
||||
|
||||
class FoundVariable {
|
||||
int index;
|
||||
String name;
|
||||
final ColumnType type;
|
||||
|
||||
FoundVariable(this.index, this.name, this.type);
|
||||
|
||||
String get dartParameterName =>
|
||||
name?.replaceAll(_illegalChars, '') ?? 'var$index';
|
||||
}
|
||||
|
|
|
@ -121,7 +121,33 @@ class SqlParser {
|
|||
}
|
||||
|
||||
final resultSet = InferredResultSet(null, moorColumns);
|
||||
foundQueries.add(
|
||||
SqlSelectQuery(queryName, ctx.sql, moorTables.toList(), resultSet));
|
||||
final foundVars = _extractVariables(ctx);
|
||||
foundQueries.add(SqlSelectQuery(
|
||||
queryName, ctx.sql, foundVars, moorTables.toList(), resultSet));
|
||||
}
|
||||
|
||||
List<FoundVariable> _extractVariables(AnalysisContext ctx) {
|
||||
// this contains variable references. For instance, SELECT :a = :a would
|
||||
// contain two entries, both referring to the same variable. To do that,
|
||||
// we use the fact that each variable has a unique index.
|
||||
final usedVars = ctx.root.allDescendants.whereType<Variable>().toList()
|
||||
..sort((a, b) => a.resolvedIndex.compareTo(b.resolvedIndex));
|
||||
|
||||
final foundVariables = <FoundVariable>[];
|
||||
var currentIndex = 0;
|
||||
|
||||
for (var used in usedVars) {
|
||||
if (used.resolvedIndex == currentIndex) {
|
||||
continue; // already handled
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
final name = (used is ColonNamedVariable) ? used.name : null;
|
||||
final type = _resolvedToMoor(ctx.typeOf(used).type);
|
||||
|
||||
foundVariables.add(FoundVariable(currentIndex, name, type));
|
||||
}
|
||||
|
||||
return foundVariables;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,24 +11,6 @@ class QueryWriter {
|
|||
|
||||
QueryWriter(this.query);
|
||||
|
||||
/* The generated code will look like:
|
||||
Future<List<AllTodosWithCategoryResult>> allTodosWithCategory() {
|
||||
return customSelect('', variables: [])
|
||||
.then((rows) => rows.map(_rowToAllT).toList());
|
||||
}
|
||||
|
||||
Stream<List<AllTodosWithCategoryResult>> watchAllTodosWithCategory() {
|
||||
return customSelectStream('', variables: [])
|
||||
.map((rows) => rows.map(_rowToAllT).toList());
|
||||
}
|
||||
|
||||
AllTodosWithCategoryResult _rowToAllT(QueryRow row) {
|
||||
return AllTodosWithCategoryResult(
|
||||
id: row.readInt('id'),
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
void writeInto(StringBuffer buffer) {
|
||||
if (query is SqlSelectQuery) {
|
||||
_writeSelect(buffer);
|
||||
|
@ -63,9 +45,14 @@ class QueryWriter {
|
|||
}
|
||||
|
||||
void _writeOneTimeReader(StringBuffer buffer) {
|
||||
buffer.write('Future<List<${_select.resultClassName}>> ${query.name}(');
|
||||
_writeParameters(buffer);
|
||||
buffer
|
||||
..write('Future<List<${_select.resultClassName}>> ${query.name}() {\n')
|
||||
..write('return customSelect(${asDartLiteral(query.sql)})')
|
||||
..write(') {\n')
|
||||
..write('return customSelect(${asDartLiteral(query.sql)},');
|
||||
_writeVariables(buffer);
|
||||
buffer
|
||||
..write(')')
|
||||
..write(
|
||||
'.then((rows) => rows.map(${_nameOfMappingMethod()}).toList());\n')
|
||||
..write('\n}\n');
|
||||
|
@ -73,11 +60,36 @@ class QueryWriter {
|
|||
|
||||
void _writeStreamReader(StringBuffer buffer) {
|
||||
final upperQueryName = ReCase(query.name).pascalCase;
|
||||
buffer.write(
|
||||
'Stream<List<${_select.resultClassName}>> watch$upperQueryName(');
|
||||
_writeParameters(buffer);
|
||||
buffer
|
||||
..write('Stream<List<${_select.resultClassName}>> watch$upperQueryName')
|
||||
..write('() {\n')
|
||||
..write('return customSelectStream(${asDartLiteral(query.sql)})')
|
||||
..write(') {\n')
|
||||
..write('return customSelectStream(${asDartLiteral(query.sql)},');
|
||||
_writeVariables(buffer);
|
||||
buffer
|
||||
..write(')')
|
||||
..write('.map((rows) => rows.map(${_nameOfMappingMethod()}).toList());\n')
|
||||
..write('\n}\n');
|
||||
}
|
||||
|
||||
void _writeParameters(StringBuffer buffer) {
|
||||
final paramList = query.variables
|
||||
.map((v) => '${dartTypeNames[v.type]} ${v.dartParameterName}')
|
||||
.join(', ');
|
||||
|
||||
buffer.write(paramList);
|
||||
}
|
||||
|
||||
void _writeVariables(StringBuffer buffer) {
|
||||
buffer..write('variables: [');
|
||||
|
||||
for (var variable in query.variables) {
|
||||
buffer
|
||||
..write(createVariable[variable.type])
|
||||
..write('(${variable.dartParameterName}),');
|
||||
}
|
||||
|
||||
buffer..write(']');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue