Parse MAPPED BY constraints for moor files

This commit is contained in:
Simon Binder 2019-08-27 12:33:48 +02:00
parent aa13aad276
commit 5d2149d727
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
6 changed files with 62 additions and 3 deletions

View File

@ -40,6 +40,7 @@ abstract class ColumnConstraint extends AstNode {
T Function(Default) isDefault,
T Function(CollateConstraint) collate,
T Function(ForeignKeyColumnConstraint) foreignKey,
T Function(MappedBy) mappedBy,
}) {
if (this is NotNull) {
return notNull?.call(this as NotNull);
@ -55,6 +56,8 @@ abstract class ColumnConstraint extends AstNode {
return collate?.call(this as CollateConstraint);
} else if (this is ForeignKeyColumnConstraint) {
return foreignKey?.call(this as ForeignKeyColumnConstraint);
} else if (this is MappedBy) {
return mappedBy?.call(this as MappedBy);
} else {
throw Exception('Did not expect $runtimeType as a ColumnConstraint');
}
@ -164,3 +167,20 @@ class ForeignKeyColumnConstraint extends ColumnConstraint {
@override
Iterable<AstNode> get childNodes => [clause];
}
/// A `MAPPED BY` constraint, which is only parsed for moor files. It can be
/// used to declare a type converter for this column.
class MappedBy extends ColumnConstraint {
/// The Dart expression creating the type converter we use to map this token.
final InlineDartToken mapper;
MappedBy(String name, this.mapper) : super(name);
@override
bool _equalToConstraint(MappedBy other) {
return other.mapper.dartCode == mapper.dartCode;
}
@override
final Iterable<AstNode> childNodes = const [];
}

View File

@ -46,7 +46,7 @@ class SqlEngine {
/// Parses the [sql] statement into an AST-representation.
ParseResult parse(String sql) {
final tokens = tokenize(sql);
final parser = Parser(tokens);
final parser = Parser(tokens, useMoor: useMoorExtensions);
final stmt = parser.statement();
return ParseResult._(stmt, parser.errors, sql);

View File

@ -43,9 +43,13 @@ class ParsingError implements Exception {
abstract class ParserBase {
final List<Token> tokens;
final List<ParsingError> errors = [];
/// Whether to enable the extensions moor makes to the sql grammar.
final bool enableMoorExtensions;
int _current = 0;
ParserBase(this.tokens);
ParserBase(this.tokens, this.enableMoorExtensions);
bool get _isAtEnd => _peek.type == TokenType.eof;
Token get _peek => tokens[_current];
@ -145,7 +149,7 @@ abstract class ParserBase {
class Parser extends ParserBase
with ExpressionParser, SchemaParser, CrudParser {
Parser(List<Token> tokens) : super(tokens);
Parser(List<Token> tokens, {bool useMoor = false}) : super(tokens, useMoor);
Statement statement({bool expectEnd = true}) {
final first = _peek;

View File

@ -153,6 +153,15 @@ mixin SchemaParser on ParserBase {
return ForeignKeyColumnConstraint(resolvedName, clause)
..setSpan(first, _previous);
}
if (enableMoorExtensions && _matchOne(TokenType.mapped)) {
_consume(TokenType.by, 'Expected a MAPPED BY constraint');
final dartExpr = _consume(
TokenType.inlineDart, 'Expected Dart expression in backticks');
return MappedBy(resolvedName, dartExpr as InlineDartToken)
..setSpan(first, _previous);
}
// no known column constraint matched. If orNull is set and we're not
// guaranteed to be in a constraint clause (started with CONSTRAINT), we

View File

@ -1,5 +1,6 @@
import 'package:sqlparser/sqlparser.dart';
import 'package:sqlparser/src/ast/ast.dart';
import 'package:sqlparser/src/utils/ast_equality.dart';
import 'package:test_core/test_core.dart';
import '../common_data.dart';
@ -115,4 +116,24 @@ void main() {
),
);
});
test('parses MAPPED BY expressions when in moor mode', () {
const stmt = 'CREATE TABLE a (b NOT NULL MAPPED BY `Mapper()` PRIMARY KEY)';
final parsed = SqlEngine(useMoorExtensions: true).parse(stmt).rootNode;
enforceEqual(
parsed,
CreateTableStatement(tableName: 'a', columns: [
ColumnDefinition(
columnName: 'b',
typeName: null,
constraints: [
NotNull(null),
MappedBy(null, inlineDart('Mapper()')),
PrimaryKeyColumn(null),
],
),
]),
);
});
}

View File

@ -10,6 +10,11 @@ Token token(TokenType type) {
return Token(type, null);
}
InlineDartToken inlineDart(String dartCode) {
final fakeFile = SourceFile.fromString('`$dartCode`');
return InlineDartToken(fakeFile.span(0));
}
IdentifierToken identifier(String content) {
final fakeFile = SourceFile.fromString(content);
return IdentifierToken(false, fakeFile.span(0));