mirror of https://github.com/AMT-Cheif/drift.git
Fix tests, parse delete statements
This commit is contained in:
parent
ff530dd4ea
commit
3f0776faf8
|
@ -18,6 +18,7 @@ part 'expressions/simple.dart';
|
|||
part 'expressions/subquery.dart';
|
||||
part 'expressions/variables.dart';
|
||||
|
||||
part 'statements/delete.dart';
|
||||
part 'statements/select.dart';
|
||||
part 'statements/statement.dart';
|
||||
|
||||
|
@ -122,6 +123,7 @@ abstract class AstNode {
|
|||
abstract class AstVisitor<T> {
|
||||
T visitSelectStatement(SelectStatement e);
|
||||
T visitResultColumn(ResultColumn e);
|
||||
T visitDeleteStatement(DeleteStatement e);
|
||||
|
||||
T visitOrderBy(OrderBy e);
|
||||
T visitOrderingTerm(OrderingTerm e);
|
||||
|
@ -204,6 +206,9 @@ class RecursiveVisitor<T> extends AstVisitor<T> {
|
|||
@override
|
||||
T visitSelectStatement(SelectStatement e) => visitChildren(e);
|
||||
|
||||
@override
|
||||
T visitDeleteStatement(DeleteStatement e) => visitChildren(e);
|
||||
|
||||
@override
|
||||
T visitUnaryExpression(UnaryExpression e) => visitChildren(e);
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
part of '../ast.dart';
|
||||
|
||||
class DeleteStatement extends Statement {
|
||||
final TableReference from;
|
||||
final Expression where;
|
||||
|
||||
DeleteStatement({@required this.from, this.where});
|
||||
|
||||
@override
|
||||
T accept<T>(AstVisitor<T> visitor) => visitor.visitDeleteStatement(this);
|
||||
|
||||
@override
|
||||
Iterable<AstNode> get childNodes => [from, if (where != null) where];
|
||||
|
||||
@override
|
||||
bool contentEquals(DeleteStatement other) => true;
|
||||
}
|
|
@ -102,7 +102,8 @@ class Parser {
|
|||
}
|
||||
|
||||
Statement statement() {
|
||||
final stmt = select();
|
||||
final stmt = select() ?? _deleteStmt();
|
||||
|
||||
_matchOne(TokenType.semicolon);
|
||||
return stmt;
|
||||
}
|
||||
|
@ -218,12 +219,9 @@ class Parser {
|
|||
TableOrSubquery _tableOrSubquery() {
|
||||
// this is what we're parsing: https://www.sqlite.org/syntax/table-or-subquery.html
|
||||
// we currently only support regular tables and nested selects
|
||||
if (_matchOne(TokenType.identifier)) {
|
||||
// ignore the schema name, it's not supported. Besides that, we're on the
|
||||
// first branch in the diagram here
|
||||
final tableName = (_previous as IdentifierToken).identifier;
|
||||
final alias = _as();
|
||||
return TableReference(tableName, alias?.identifier);
|
||||
final tableRef = _tableReference();
|
||||
if (tableRef != null) {
|
||||
return tableRef;
|
||||
} else if (_matchOne(TokenType.leftParen)) {
|
||||
final innerStmt = select();
|
||||
_consume(TokenType.rightParen,
|
||||
|
@ -237,6 +235,17 @@ class Parser {
|
|||
_error('Expected a table name or a nested select statement');
|
||||
}
|
||||
|
||||
TableReference _tableReference() {
|
||||
if (_matchOne(TokenType.identifier)) {
|
||||
// ignore the schema name, it's not supported. Besides that, we're on the
|
||||
// first branch in the diagram here
|
||||
final tableName = (_previous as IdentifierToken).identifier;
|
||||
final alias = _as();
|
||||
return TableReference(tableName, alias?.identifier);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
JoinClause _joinClause(TableOrSubquery start) {
|
||||
var operator = _parseJoinOperatorNoComma();
|
||||
if (operator == null) {
|
||||
|
@ -375,7 +384,7 @@ class Parser {
|
|||
/// Parses a [Limit] clause, or returns null if there is no limit token after
|
||||
/// the current position.
|
||||
Limit _limit() {
|
||||
if (!_match(const [TokenType.limit])) return null;
|
||||
if (!_matchOne(TokenType.limit)) return null;
|
||||
|
||||
final count = expression();
|
||||
Token offsetSep;
|
||||
|
@ -389,6 +398,23 @@ class Parser {
|
|||
return Limit(count: count, offsetSeparator: offsetSep, offset: offset);
|
||||
}
|
||||
|
||||
DeleteStatement _deleteStmt() {
|
||||
if (!_matchOne(TokenType.delete)) return null;
|
||||
_consume(TokenType.from, 'Expected a FROM here');
|
||||
|
||||
final table = _tableReference();
|
||||
Expression where;
|
||||
if (table == null) {
|
||||
_error('Expected a table reference');
|
||||
}
|
||||
|
||||
if (_matchOne(TokenType.where)) {
|
||||
where = expression();
|
||||
}
|
||||
|
||||
return DeleteStatement(from: table, where: where);
|
||||
}
|
||||
|
||||
/* We parse expressions here.
|
||||
* Operators have the following precedence:
|
||||
* - + ~ NOT (unary)
|
||||
|
|
|
@ -141,9 +141,10 @@ class Scanner {
|
|||
_numeric(char);
|
||||
} else if (canStartColumnName(char)) {
|
||||
_identifier();
|
||||
} else {
|
||||
errors.add(TokenizerError(
|
||||
'Unexpected character.', SourceLocation(_currentOffset)));
|
||||
}
|
||||
errors.add(TokenizerError(
|
||||
'Unexpected character.', SourceLocation(_currentOffset)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ enum TokenType {
|
|||
identifier,
|
||||
|
||||
select,
|
||||
delete,
|
||||
distinct,
|
||||
all,
|
||||
from,
|
||||
|
@ -94,6 +95,7 @@ const Map<String, TokenType> keywords = {
|
|||
'AND': TokenType.and,
|
||||
'OR': TokenType.or,
|
||||
'BETWEEN': TokenType.between,
|
||||
'DELETE': TokenType.delete,
|
||||
'FROM': TokenType.from,
|
||||
'NATURAL': TokenType.natural,
|
||||
'LEFT': TokenType.leftParen,
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import 'package:sqlparser/src/reader/tokenizer/token.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
import 'package:sqlparser/src/utils/ast_equality.dart';
|
||||
|
||||
import 'utils.dart';
|
||||
|
||||
void main() {
|
||||
test('parses delete statements', () {
|
||||
final parsed = SqlEngine().parse('DELETE FROM table WHERE id = 5').rootNode;
|
||||
|
||||
enforceEqual(
|
||||
parsed,
|
||||
DeleteStatement(
|
||||
from: TableReference('table', null),
|
||||
where: BinaryExpression(
|
||||
Reference(columnName: 'id'),
|
||||
token(TokenType.equal),
|
||||
NumericLiteral(
|
||||
5,
|
||||
token(TokenType.numberLiteral),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
|
@ -16,14 +16,16 @@ void _enforceFrom(SelectStatement stmt, List<Queryable> expected) {
|
|||
void main() {
|
||||
group('from', () {
|
||||
test('a simple table', () {
|
||||
final stmt = SqlEngine().parse('SELECT * FROM table') as SelectStatement;
|
||||
final stmt =
|
||||
SqlEngine().parse('SELECT * FROM table').rootNode as SelectStatement;
|
||||
|
||||
enforceEqual(stmt.from.single, TableReference('table', null));
|
||||
});
|
||||
|
||||
test('from more than one table', () {
|
||||
final stmt = SqlEngine().parse('SELECT * FROM table AS test, table2')
|
||||
as SelectStatement;
|
||||
final stmt = SqlEngine()
|
||||
.parse('SELECT * FROM table AS test, table2')
|
||||
.rootNode as SelectStatement;
|
||||
|
||||
_enforceFrom(
|
||||
stmt,
|
||||
|
@ -35,9 +37,10 @@ void main() {
|
|||
});
|
||||
|
||||
test('from inner select statements', () {
|
||||
final stmt = SqlEngine().parse(
|
||||
final stmt = SqlEngine()
|
||||
.parse(
|
||||
'SELECT * FROM table1, (SELECT * FROM table2 WHERE a) as "inner"')
|
||||
as SelectStatement;
|
||||
.rootNode as SelectStatement;
|
||||
|
||||
_enforceFrom(
|
||||
stmt,
|
||||
|
@ -56,9 +59,11 @@ void main() {
|
|||
});
|
||||
|
||||
test('from a join', () {
|
||||
final stmt = SqlEngine().parse('SELECT * FROM table1 '
|
||||
'INNER JOIN table2 USING (test) '
|
||||
'LEFT OUTER JOIN table3 ON TRUE') as SelectStatement;
|
||||
final stmt = SqlEngine()
|
||||
.parse('SELECT * FROM table1 '
|
||||
'INNER JOIN table2 USING (test) '
|
||||
'LEFT OUTER JOIN table3 ON TRUE')
|
||||
.rootNode as SelectStatement;
|
||||
|
||||
_enforceFrom(stmt, [
|
||||
JoinClause(
|
||||
|
|
|
@ -7,9 +7,9 @@ import '../utils.dart';
|
|||
|
||||
void main() {
|
||||
test('parses group by statements', () {
|
||||
final stmt = SqlEngine().parse(
|
||||
"SELECT * FROM test GROUP BY country HAVING country LIKE '%G%'")
|
||||
as SelectStatement;
|
||||
final stmt = SqlEngine()
|
||||
.parse("SELECT * FROM test GROUP BY country HAVING country LIKE '%G%'")
|
||||
.rootNode as SelectStatement;
|
||||
|
||||
return enforceEqual(
|
||||
stmt.groupBy,
|
||||
|
|
|
@ -8,8 +8,9 @@ import '../utils.dart';
|
|||
void main() {
|
||||
group('limit clauses', () {
|
||||
test('with just a limit', () {
|
||||
final select = SqlEngine().parse('SELECT * FROM test LIMIT 5 * 3')
|
||||
as SelectStatement;
|
||||
final select = SqlEngine()
|
||||
.parse('SELECT * FROM test LIMIT 5 * 3')
|
||||
.rootNode as SelectStatement;
|
||||
|
||||
enforceEqual(
|
||||
select.limit,
|
||||
|
@ -24,8 +25,9 @@ void main() {
|
|||
});
|
||||
|
||||
test('with offset', () {
|
||||
final select = SqlEngine().parse('SELECT * FROM test LIMIT 10 OFFSET 2')
|
||||
as SelectStatement;
|
||||
final select = SqlEngine()
|
||||
.parse('SELECT * FROM test LIMIT 10 OFFSET 2')
|
||||
.rootNode as SelectStatement;
|
||||
|
||||
enforceEqual(
|
||||
select.limit,
|
||||
|
@ -38,8 +40,9 @@ void main() {
|
|||
});
|
||||
|
||||
test('with offset as comma', () {
|
||||
final select = SqlEngine().parse('SELECT * FROM test LIMIT 10, 2')
|
||||
as SelectStatement;
|
||||
final select = SqlEngine()
|
||||
.parse('SELECT * FROM test LIMIT 10, 2')
|
||||
.rootNode as SelectStatement;
|
||||
|
||||
enforceEqual(
|
||||
select.limit,
|
||||
|
|
|
@ -7,8 +7,9 @@ import '../utils.dart';
|
|||
|
||||
void main() {
|
||||
test('parses order by clauses', () {
|
||||
final parsed = SqlEngine().parse('SELECT * FROM table ORDER BY -a, b DESC')
|
||||
as SelectStatement;
|
||||
final parsed = SqlEngine()
|
||||
.parse('SELECT * FROM table ORDER BY -a, b DESC')
|
||||
.rootNode as SelectStatement;
|
||||
|
||||
enforceEqual(
|
||||
parsed.orderBy,
|
||||
|
|
Loading…
Reference in New Issue