mirror of https://github.com/AMT-Cheif/drift.git
Support custom fts5 queries
This commit is contained in:
parent
da9ca61e0c
commit
94634bd48b
|
@ -60,6 +60,11 @@ mixin TableInfo<TableDsl extends Table, D extends DataClass> on Table {
|
|||
/// Maps the given row returned by the database into the fitting data class.
|
||||
D map(Map<String, dynamic> data, {String tablePrefix});
|
||||
|
||||
/// Like [map], but from a [row] instead of the low-level map.
|
||||
D mapFromRow(QueryRow row, {String tablePrefix}) {
|
||||
return map(row.data, tablePrefix: tablePrefix);
|
||||
}
|
||||
|
||||
TableInfo<TableDsl, D> createAlias(String alias);
|
||||
|
||||
@override
|
||||
|
|
|
@ -1113,10 +1113,7 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
|
|||
Email _email;
|
||||
Email get email => _email ??= Email(this);
|
||||
Config _rowToConfig(QueryRow row) {
|
||||
return Config(
|
||||
configKey: row.readString('config_key'),
|
||||
configValue: row.readString('config_value'),
|
||||
);
|
||||
return config.mapFromRow(row);
|
||||
}
|
||||
|
||||
Selectable<Config> readConfig(String var1) {
|
||||
|
@ -1157,6 +1154,17 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
|
|||
readsFrom: {config}).map(_rowToConfig);
|
||||
}
|
||||
|
||||
EMail _rowToEMail(QueryRow row) {
|
||||
return email.mapFromRow(row);
|
||||
}
|
||||
|
||||
Selectable<EMail> searchEmails(String term) {
|
||||
return customSelectQuery(
|
||||
'SELECT * FROM email WHERE email MATCH :term ORDER BY rank',
|
||||
variables: [Variable.withString(term)],
|
||||
readsFrom: {email}).map(_rowToEMail);
|
||||
}
|
||||
|
||||
ReadRowIdResult _rowToReadRowIdResult(QueryRow row) {
|
||||
return ReadRowIdResult(
|
||||
rowid: row.readInt('rowid'),
|
||||
|
|
|
@ -34,6 +34,8 @@ readMultiple: SELECT * FROM config WHERE config_key IN ? ORDER BY $clause;
|
|||
readDynamic: SELECT * FROM config WHERE $predicate;
|
||||
findValidJsons: SELECT * FROM config WHERE json_valid(config_value);
|
||||
|
||||
searchEmails: SELECT * FROM email WHERE email MATCH :term ORDER BY rank;
|
||||
|
||||
readRowId: SELECT oid, * FROM config WHERE _rowid_ = $expr;
|
||||
|
||||
cfeTest: WITH RECURSIVE
|
||||
|
|
|
@ -1372,13 +1372,7 @@ abstract class _$TodoDb extends GeneratedDatabase {
|
|||
}
|
||||
|
||||
TodoEntry _rowToTodoEntry(QueryRow row) {
|
||||
return TodoEntry(
|
||||
id: row.readInt('id'),
|
||||
title: row.readString('title'),
|
||||
content: row.readString('content'),
|
||||
targetDate: row.readDateTime('target_date'),
|
||||
category: row.readInt('category'),
|
||||
);
|
||||
return todosTable.mapFromRow(row);
|
||||
}
|
||||
|
||||
Selectable<TodoEntry> withInQuery(String var1, String var2, List<int> var3) {
|
||||
|
@ -1499,13 +1493,7 @@ mixin _$SomeDaoMixin on DatabaseAccessor<TodoDb> {
|
|||
$SharedTodosTable get sharedTodos => db.sharedTodos;
|
||||
$TodosTableTable get todosTable => db.todosTable;
|
||||
TodoEntry _rowToTodoEntry(QueryRow row) {
|
||||
return TodoEntry(
|
||||
id: row.readInt('id'),
|
||||
title: row.readString('title'),
|
||||
content: row.readString('content'),
|
||||
targetDate: row.readDateTime('target_date'),
|
||||
category: row.readInt('category'),
|
||||
);
|
||||
return todosTable.mapFromRow(row);
|
||||
}
|
||||
|
||||
Selectable<TodoEntry> todosForUserQuery(int user) {
|
||||
|
|
|
@ -23,6 +23,8 @@ class CreateTableReader {
|
|||
final primaryKey = <SpecifiedColumn>{};
|
||||
|
||||
for (final column in table.resolvedColumns) {
|
||||
if (!column.includedInResults) continue;
|
||||
|
||||
var isPrimaryKey = false;
|
||||
final features = <ColumnFeature>[];
|
||||
final sqlName = column.name;
|
||||
|
|
|
@ -16,6 +16,7 @@ class TypeMapper {
|
|||
Table extractStructure(SpecifiedTable table) {
|
||||
final existingTable = table.declaration?.tableFromSqlParser;
|
||||
if (existingTable != null) {
|
||||
_engineTablesToSpecified[existingTable] = table;
|
||||
return existingTable;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:moor_generator/src/analyzer/options.dart';
|
||||
import 'package:moor_generator/src/analyzer/sql_queries/meta/declarations.dart';
|
||||
import 'package:moor_generator/src/model/specified_column.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:moor_generator/src/model/used_type_converter.dart';
|
||||
import 'package:recase/recase.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
@ -34,6 +34,7 @@ class SpecifiedTable {
|
|||
/// The name for the data class associated with this table
|
||||
final String dartTypeName;
|
||||
|
||||
/// The getter name used for this table in a generated database or dao class.
|
||||
String get tableFieldName => _dbFieldName(_baseName);
|
||||
String get tableInfoName {
|
||||
// if this table was parsed from sql, a user might want to refer to it
|
||||
|
|
|
@ -96,15 +96,25 @@ class QueryWriter {
|
|||
if (!_writtenMappingMethods.contains(_nameOfMappingMethod())) {
|
||||
_buffer
|
||||
..write('${_select.resultClassName} ${_nameOfMappingMethod()}')
|
||||
..write('(QueryRow row) {\n')
|
||||
..write('return ${_select.resultClassName}(');
|
||||
..write('(QueryRow row) {\n');
|
||||
|
||||
// If we're matching an existing table from moor - let's just use the
|
||||
// mapping method we're generating for each table!
|
||||
if (_select.resultSet.matchingTable != null) {
|
||||
final table = _select.resultSet.matchingTable;
|
||||
_buffer.write('return ${table.tableFieldName}.mapFromRow(row);\n');
|
||||
} else {
|
||||
// For more complex results, generate a custom constructor call
|
||||
_buffer.write('return ${_select.resultClassName}(');
|
||||
for (final column in _select.resultSet.columns) {
|
||||
final fieldName = _select.resultSet.dartNameFor(column);
|
||||
_buffer.write('$fieldName: ${_readingCode(column)},');
|
||||
}
|
||||
|
||||
_buffer.write(');\n}\n');
|
||||
_buffer.write(');\n');
|
||||
}
|
||||
_buffer.write('}\n');
|
||||
|
||||
_writtenMappingMethods.add(_nameOfMappingMethod());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,12 @@ abstract class Column with Referencable, HasMetaMixin implements Typeable {
|
|||
/// The name of this column in the result set.
|
||||
String get name;
|
||||
|
||||
/// Whether this column is included in results when running a select query
|
||||
/// like `SELECT * FROM table`.
|
||||
///
|
||||
/// Some columns, notably the rowid aliases, are exempt from this.
|
||||
bool get includedInResults => true;
|
||||
|
||||
Column();
|
||||
}
|
||||
|
||||
|
@ -85,6 +91,9 @@ class RowId extends TableColumn {
|
|||
// note that such alias is always called "rowid" in the result set -
|
||||
// "SELECT oid FROM table" yields a sinle column called "rowid"
|
||||
RowId() : super('rowid', const ResolvedType(type: BasicType.int));
|
||||
|
||||
@override
|
||||
bool get includedInResults => false;
|
||||
}
|
||||
|
||||
/// A column that is created by an expression. For instance, in the select
|
||||
|
|
|
@ -131,6 +131,8 @@ class ColumnResolver extends RecursiveVisitor<void> {
|
|||
// result, but also expressions that appear as result columns
|
||||
for (final resultColumn in s.columns) {
|
||||
if (resultColumn is StarResultColumn) {
|
||||
Iterable<Column> visibleColumnsForStar;
|
||||
|
||||
if (resultColumn.tableName != null) {
|
||||
final tableResolver = scope
|
||||
.resolve<ResolvesToResultSet>(resultColumn.tableName, orElse: () {
|
||||
|
@ -141,11 +143,15 @@ class ColumnResolver extends RecursiveVisitor<void> {
|
|||
));
|
||||
});
|
||||
|
||||
usedColumns.addAll(tableResolver.resultSet.resolvedColumns);
|
||||
visibleColumnsForStar = tableResolver.resultSet.resolvedColumns;
|
||||
} else {
|
||||
// we have a * column, that would be all available columns
|
||||
usedColumns.addAll(availableColumns);
|
||||
// we have a * column without a table, that resolves to every columns
|
||||
// available
|
||||
visibleColumnsForStar = availableColumns;
|
||||
}
|
||||
|
||||
usedColumns
|
||||
.addAll(visibleColumnsForStar.where((e) => e.includedInResults));
|
||||
} else if (resultColumn is ExpressionResultColumn) {
|
||||
final expression = resultColumn.expression;
|
||||
Column column;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import 'package:meta/meta.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
final _rankColumn = _Fts5RankColumn();
|
||||
|
||||
class Fts5Extension implements Extension {
|
||||
const Fts5Extension();
|
||||
|
||||
|
@ -42,17 +40,40 @@ class _Fts5Table extends Table {
|
|||
{@required String name,
|
||||
List<TableColumn> columns,
|
||||
CreateVirtualTableStatement definition})
|
||||
: super(name: name, resolvedColumns: columns, definition: definition);
|
||||
|
||||
@override
|
||||
Column findColumn(String name) {
|
||||
if (name == 'rank') {
|
||||
return _rankColumn;
|
||||
}
|
||||
return super.findColumn(name);
|
||||
}
|
||||
: super(
|
||||
name: name,
|
||||
resolvedColumns: [
|
||||
...columns,
|
||||
_Fts5RankColumn(),
|
||||
_Fts5TableColumn(name),
|
||||
],
|
||||
definition: definition,
|
||||
);
|
||||
}
|
||||
|
||||
/// The rank column, which we introduce to support queries like
|
||||
/// ```
|
||||
/// SELECT * FROM my_fts_table WHERE my_fts_table MATCH 'foo' ORDER BY rank;
|
||||
/// ```
|
||||
class _Fts5RankColumn extends TableColumn {
|
||||
@override
|
||||
bool get includedInResults => false;
|
||||
|
||||
_Fts5RankColumn() : super('rank', const ResolvedType(type: BasicType.int));
|
||||
}
|
||||
|
||||
/// A column that has the same name as the fts5 it's from. We introduce this
|
||||
/// column to support constructs like
|
||||
/// ```
|
||||
/// CREATE VIRTUAL TABLE foo USING fts5(bar, baz);
|
||||
/// query: SELECT * FROM foo WHERE foo MATCH 'something';
|
||||
/// ```
|
||||
/// The easiest way to support that is to just make "foo" a column on that
|
||||
/// table.
|
||||
class _Fts5TableColumn extends TableColumn {
|
||||
@override
|
||||
bool get includedInResults => false;
|
||||
|
||||
_Fts5TableColumn(String name)
|
||||
: super(name, const ResolvedType(type: BasicType.text));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue