Generate query implementations for insert statements

This commit is contained in:
Simon Binder 2019-08-29 16:27:02 +02:00
parent a4b256f8a5
commit 241baed0c3
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
9 changed files with 65 additions and 9 deletions

View File

@ -2,7 +2,10 @@ import 'package:moor/moor.dart';
part 'custom_tables.g.dart'; part 'custom_tables.g.dart';
@UseMoor(include: {'tables.moor'}) @UseMoor(
include: {'tables.moor'},
queries: {'writeConfig': 'REPLACE INTO config VALUES (:key, :value)'},
)
class CustomTablesDb extends _$CustomTablesDb { class CustomTablesDb extends _$CustomTablesDb {
CustomTablesDb(QueryExecutor e) : super(e); CustomTablesDb(QueryExecutor e) : super(e);

View File

@ -812,6 +812,21 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
Config get config => _config ??= Config(this); Config get config => _config ??= Config(this);
Mytable _mytable; Mytable _mytable;
Mytable get mytable => _mytable ??= Mytable(this); Mytable get mytable => _mytable ??= Mytable(this);
Future<int> writeConfig(
String key,
String value,
{@Deprecated('No longer needed with Moor 1.6 - see the changelog for details')
QueryEngine operateOn}) {
return (operateOn ?? this).customInsert(
'REPLACE INTO config VALUES (:key, :value)',
variables: [
Variable.withString(key),
Variable.withString(value),
],
updates: {config},
);
}
@override @override
List<TableInfo> get allTables => List<TableInfo> get allTables =>
[noIds, withDefaults, withConstraints, config, mytable]; [noIds, withDefaults, withConstraints, config, mytable];

View File

@ -51,9 +51,11 @@ class SqlSelectQuery extends SqlQuery {
class UpdatingQuery extends SqlQuery { class UpdatingQuery extends SqlQuery {
final List<SpecifiedTable> updates; final List<SpecifiedTable> updates;
final bool isInsert;
UpdatingQuery(String name, AnalysisContext fromContext, UpdatingQuery(String name, AnalysisContext fromContext,
List<FoundVariable> variables, this.updates) List<FoundVariable> variables, this.updates,
{this.isInsert = false})
: super(name, fromContext, variables); : super(name, fromContext, variables);
} }

View File

@ -52,4 +52,10 @@ class UpdatedTablesVisitor extends RecursiveVisitor<void> {
_addIfResolved(e.table); _addIfResolved(e.table);
visitChildren(e); visitChildren(e);
} }
@override
void visitInsertStatement(InsertStatement e) {
_addIfResolved(e.table);
visitChildren(e);
}
} }

View File

@ -26,7 +26,9 @@ class QueryHandler {
if (root is SelectStatement) { if (root is SelectStatement) {
return _handleSelect(); return _handleSelect();
} else if (root is UpdateStatement || root is DeleteStatement) { } else if (root is UpdateStatement ||
root is DeleteStatement ||
root is InsertStatement) {
return _handleUpdate(); return _handleUpdate();
} else { } else {
throw StateError( throw StateError(
@ -39,8 +41,11 @@ class QueryHandler {
context.root.accept(updatedFinder); context.root.accept(updatedFinder);
_foundTables = updatedFinder.foundTables; _foundTables = updatedFinder.foundTables;
final isInsert = context.root is InsertStatement;
return UpdatingQuery(name, context, _foundVariables, return UpdatingQuery(name, context, _foundVariables,
_foundTables.map(mapper.tableToMoor).toList()); _foundTables.map(mapper.tableToMoor).toList(),
isInsert: isInsert);
} }
SqlSelectQuery _handleSelect() { SqlSelectQuery _handleSelect() {

View File

@ -155,6 +155,8 @@ class QueryWriter {
return customUpdate('', variables: [], updates: {}); return customUpdate('', variables: [], updates: {});
} }
*/ */
final implName = _update.isInsert ? 'customInsert' : 'customUpdate';
buffer.write('Future<int> ${query.name}('); buffer.write('Future<int> ${query.name}(');
_writeParameters(buffer); _writeParameters(buffer);
buffer.write(') {\n'); buffer.write(') {\n');
@ -162,7 +164,7 @@ class QueryWriter {
_writeExpandedDeclarations(buffer); _writeExpandedDeclarations(buffer);
buffer buffer
..write('return (operateOn ?? this).') ..write('return (operateOn ?? this).')
..write('customUpdate(${_queryCode()},'); ..write('$implName(${_queryCode()},');
_writeVariables(buffer); _writeVariables(buffer);
buffer.write(','); buffer.write(',');

View File

@ -326,8 +326,10 @@ mixin ExpressionParser on ParserBase {
break; break;
case TokenType.colon: case TokenType.colon:
final colon = token; final colon = token;
final identifier = _consume(TokenType.identifier, final identifier = _consumeIdentifier(
'Expected an identifier for the named variable') as IdentifierToken; 'Expected an identifier for the named variable',
lenient: true);
final content = identifier.identifier; final content = identifier.identifier;
return ColonNamedVariable(':$content')..setSpan(colon, identifier); return ColonNamedVariable(':$content')..setSpan(colon, identifier);
default: default:
@ -392,6 +394,7 @@ mixin ExpressionParser on ParserBase {
)..setSpan(name, _previous); )..setSpan(name, _previous);
} }
@override
TupleExpression _consumeTuple() { TupleExpression _consumeTuple() {
final firstToken = final firstToken =
_consume(TokenType.leftParen, 'Expected opening parenthesis for tuple'); _consume(TokenType.leftParen, 'Expected opening parenthesis for tuple');

View File

@ -123,7 +123,13 @@ abstract class ParserBase {
_error(message); _error(message);
} }
IdentifierToken _consumeIdentifier(String message) { /// Consumes an identifier. If [lenient] is true and the next token is not
/// an identifier but rather a [KeywordToken], that token will be converted
/// to an identifier.
IdentifierToken _consumeIdentifier(String message, {bool lenient = false}) {
if (lenient && _peek is KeywordToken) {
return (_advance() as KeywordToken).convertToIdentifier();
}
return _consume(TokenType.identifier, message) as IdentifierToken; return _consume(TokenType.identifier, message) as IdentifierToken;
} }

View File

@ -268,6 +268,11 @@ class IdentifierToken extends Token {
/// Whether this identifier was escaped by putting it in "double ticks". /// Whether this identifier was escaped by putting it in "double ticks".
final bool escaped; final bool escaped;
/// Whether this identifier token is synthetic. We sometimes convert
/// [KeywordToken]s to identifiers if they're unambiguous, in which case
/// [synthetic] will be true on this token because it was not scanned as such.
final bool synthetic;
String get identifier { String get identifier {
if (escaped) { if (escaped) {
return lexeme.substring(1, lexeme.length - 1); return lexeme.substring(1, lexeme.length - 1);
@ -276,7 +281,7 @@ class IdentifierToken extends Token {
} }
} }
const IdentifierToken(this.escaped, FileSpan span) const IdentifierToken(this.escaped, FileSpan span, {this.synthetic = false})
: super(TokenType.identifier, span); : super(TokenType.identifier, span);
} }
@ -295,7 +300,16 @@ class InlineDartToken extends Token {
/// additional properties to ease syntax highlighting, as it allows us to find /// additional properties to ease syntax highlighting, as it allows us to find
/// the keywords easily. /// the keywords easily.
class KeywordToken extends Token { class KeywordToken extends Token {
/// Whether this token has been used as an identifier while parsing.
bool isIdentifier;
KeywordToken(TokenType type, FileSpan span) : super(type, span); KeywordToken(TokenType type, FileSpan span) : super(type, span);
IdentifierToken convertToIdentifier() {
isIdentifier = true;
return IdentifierToken(false, span, synthetic: false);
}
} }
class TokenizerError { class TokenizerError {