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';
@UseMoor(include: {'tables.moor'})
@UseMoor(
include: {'tables.moor'},
queries: {'writeConfig': 'REPLACE INTO config VALUES (:key, :value)'},
)
class CustomTablesDb extends _$CustomTablesDb {
CustomTablesDb(QueryExecutor e) : super(e);

View File

@ -812,6 +812,21 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
Config get config => _config ??= Config(this);
Mytable _mytable;
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
List<TableInfo> get allTables =>
[noIds, withDefaults, withConstraints, config, mytable];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -123,7 +123,13 @@ abstract class ParserBase {
_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;
}

View File

@ -268,6 +268,11 @@ class IdentifierToken extends Token {
/// Whether this identifier was escaped by putting it in "double ticks".
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 {
if (escaped) {
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);
}
@ -295,7 +300,16 @@ class InlineDartToken extends Token {
/// additional properties to ease syntax highlighting, as it allows us to find
/// the keywords easily.
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);
IdentifierToken convertToIdentifier() {
isIdentifier = true;
return IdentifierToken(false, span, synthetic: false);
}
}
class TokenizerError {