From 591e1b2bff43f06ce475b617a35e2d2a087bd301 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sun, 27 Oct 2019 10:47:21 +0100 Subject: [PATCH] Support WITH clause for inner select statements --- .../lib/src/ast/expressions/subquery.dart | 4 ++-- sqlparser/lib/src/reader/parser/crud.dart | 20 +++++++++++++------ .../lib/src/reader/parser/expressions.dart | 8 +++++--- sqlparser/lib/src/reader/parser/parser.dart | 9 +++++++++ 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/sqlparser/lib/src/ast/expressions/subquery.dart b/sqlparser/lib/src/ast/expressions/subquery.dart index ccb14fc6..ff2baccc 100644 --- a/sqlparser/lib/src/ast/expressions/subquery.dart +++ b/sqlparser/lib/src/ast/expressions/subquery.dart @@ -3,7 +3,7 @@ part of '../ast.dart'; /// A subquery, which is an expression. It is expected that the inner query /// only returns one column and one row. class SubQuery extends Expression { - final SelectStatement select; + final BaseSelectStatement select; SubQuery({this.select}); @@ -18,7 +18,7 @@ class SubQuery extends Expression { } class ExistsExpression extends Expression { - final SelectStatement select; + final BaseSelectStatement select; ExistsExpression({@required this.select}); diff --git a/sqlparser/lib/src/reader/parser/crud.dart b/sqlparser/lib/src/reader/parser/crud.dart index 19bab1b6..5982b3eb 100644 --- a/sqlparser/lib/src/reader/parser/crud.dart +++ b/sqlparser/lib/src/reader/parser/crud.dart @@ -66,11 +66,8 @@ mixin CrudParser on ParserBase { ..withToken = withToken; } - /// Parses a select statement as defined in [the sqlite documentation][s-d], - /// which means that compound selects and a with clause is supported. - /// - /// [s-d]: https://sqlite.org/syntax/select-stmt.html - BaseSelectStatement _defaultSelect() { + @override + BaseSelectStatement _fullSelect() { final clause = _withClause(); return select(withClause: clause); } @@ -82,6 +79,17 @@ mixin CrudParser on ParserBase { } else { final firstTokenOfBase = _peek; final first = _selectNoCompound(withClause); + + if (first == null) { + // _selectNoCompound returns null if there's no select statement at the + // current position. That's fine if we didn't encounter an with clause + // already + if (withClause != null) { + _error('Expected a SELECT statement to follow the WITH clause here'); + } + return null; + } + final parts = []; while (true) { @@ -631,7 +639,7 @@ mixin CrudParser on ParserBase { return const DefaultValues(); } else { return SelectInsertSource( - _defaultSelect() ?? _error('Expeced a select statement')); + _fullSelect() ?? _error('Expeced a select statement')); } } diff --git a/sqlparser/lib/src/reader/parser/expressions.dart b/sqlparser/lib/src/reader/parser/expressions.dart index 51dc7851..51e2961d 100644 --- a/sqlparser/lib/src/reader/parser/expressions.dart +++ b/sqlparser/lib/src/reader/parser/expressions.dart @@ -194,7 +194,7 @@ mixin ExpressionParser on ParserBase { final existsToken = _previous; _consume( TokenType.leftParen, 'Expected opening parenthesis after EXISTS'); - final selectStmt = select(noCompound: true) as SelectStatement; + final selectStmt = _fullSelect() ?? _error('Expected a select statement'); _consume(TokenType.rightParen, 'Expected closing paranthesis to finish EXISTS expression'); return ExistsExpression(select: selectStmt) @@ -263,7 +263,9 @@ mixin ExpressionParser on ParserBase { if (_matchOne(TokenType.leftParen)) { final left = _previous; - if (_peek.type == TokenType.select) { + + final selectStmt = _fullSelect(); // returns null if there's no select + if (selectStmt != null) { final stmt = select(noCompound: true) as SelectStatement; _consume(TokenType.rightParen, 'Expected a closing bracket'); return SubQuery(select: stmt)..setSpan(left, _previous); @@ -380,7 +382,7 @@ mixin ExpressionParser on ParserBase { _consume(TokenType.leftParen, 'Expected opening parenthesis for tuple'); final expressions = []; - final subQuery = select(noCompound: true) as SelectStatement; + final subQuery = _fullSelect(); if (subQuery == null) { // no sub query found. read expressions that form the tuple. // tuples can be empty `()`, so only start parsing values when it's not diff --git a/sqlparser/lib/src/reader/parser/parser.dart b/sqlparser/lib/src/reader/parser/parser.dart index c083eb9b..4152e3dd 100644 --- a/sqlparser/lib/src/reader/parser/parser.dart +++ b/sqlparser/lib/src/reader/parser/parser.dart @@ -169,10 +169,19 @@ abstract class ParserBase { /// [CompoundSelectStatement]. If [noCompound] is set to true, the parser will /// only attempt to parse a [SelectStatement]. /// + /// This method doesn't parse WITH clauses, most users would probably want to + /// use [_fullSelect] instead. + /// /// See also: /// https://www.sqlite.org/lang_select.html BaseSelectStatement select({bool noCompound}); + /// Parses a select statement as defined in [the sqlite documentation][s-d], + /// which means that compound selects and a with clause is supported. + /// + /// [s-d]: https://sqlite.org/syntax/select-stmt.html + BaseSelectStatement _fullSelect(); + Literal _literalOrNull(); OrderingMode _orderingModeOrNull();