From fbe061c84d7e607ae15ef2308a36052dd4148fec Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Sat, 1 Feb 2020 13:18:53 +0100 Subject: [PATCH] Support current time literals in the sql parser --- sqlparser/CHANGELOG.md | 1 + .../lib/src/analysis/types/resolver.dart | 2 ++ .../analysis/types2/resolving_visitor.dart | 9 ++++---- .../lib/src/ast/expressions/literals.dart | 21 +++++++++++++++++++ sqlparser/lib/src/ast/visitor.dart | 6 ++++++ .../lib/src/reader/parser/expressions.dart | 13 +++++++++++- .../test/analysis/types/resolver_test.dart | 2 ++ .../test/analysis/types2/misc_cases_test.dart | 1 + sqlparser/test/parser/expression_test.dart | 6 ++++++ 9 files changed, 56 insertions(+), 5 deletions(-) diff --git a/sqlparser/CHANGELOG.md b/sqlparser/CHANGELOG.md index 97bea167..2be0cefa 100644 --- a/sqlparser/CHANGELOG.md +++ b/sqlparser/CHANGELOG.md @@ -4,6 +4,7 @@ - __Breaking__: Removed the `enableJson1` parameter on `EngineOptions`. Add a `Json1Extension` instance to `enabledExtensions` instead. - Parse `rowid` as a valid reference when needed (`SELECT rowid FROM tbl` is now parsed correctly) +- Parse `CURRENT_TIME`, `CURRENT_DATE` and `CURRENT_TIMESTAMP` ## 0.6.0 diff --git a/sqlparser/lib/src/analysis/types/resolver.dart b/sqlparser/lib/src/analysis/types/resolver.dart index ca3bccda..fff09278 100644 --- a/sqlparser/lib/src/analysis/types/resolver.dart +++ b/sqlparser/lib/src/analysis/types/resolver.dart @@ -161,6 +161,8 @@ class TypeResolver { } else if (l is NullLiteral) { return const ResolveResult( ResolvedType(type: BasicType.nullType, nullable: true)); + } else if (l is TimeConstantLiteral) { + return const ResolveResult(ResolvedType(type: BasicType.text)); } throw StateError('Unknown literal $l'); diff --git a/sqlparser/lib/src/analysis/types2/resolving_visitor.dart b/sqlparser/lib/src/analysis/types2/resolving_visitor.dart index afe5f2ae..2f5f48bc 100644 --- a/sqlparser/lib/src/analysis/types2/resolving_visitor.dart +++ b/sqlparser/lib/src/analysis/types2/resolving_visitor.dart @@ -144,21 +144,22 @@ class TypeResolver extends RecursiveVisitor { @override void visitLiteral(Literal e, TypeExpectation arg) { ResolvedType type; + var nullable = false; if (e is NullLiteral) { type = const ResolvedType(type: BasicType.nullType, nullable: true); - session._hintNullability(e, true); + nullable = true; } else if (e is StringLiteral) { type = e.isBinary ? const ResolvedType(type: BasicType.blob) : _textType; - session._hintNullability(e, false); } else if (e is BooleanLiteral) { type = const ResolvedType.bool(); - session._hintNullability(e, false); } else if (e is NumericLiteral) { type = e.isInt ? _intType : _realType; - session._hintNullability(e, false); + } else if (e is TimeConstantLiteral) { + type = _textType; } + session._hintNullability(e, nullable); session._checkAndResolve(e, type, arg); } diff --git a/sqlparser/lib/src/ast/expressions/literals.dart b/sqlparser/lib/src/ast/expressions/literals.dart index 676b1e3b..72c939ba 100644 --- a/sqlparser/lib/src/ast/expressions/literals.dart +++ b/sqlparser/lib/src/ast/expressions/literals.dart @@ -89,3 +89,24 @@ class StringLiteral extends Literal { return other.isBinary == isBinary && other.value == value; } } + +enum TimeConstantKind { currentTime, currentDate, currentTimestamp } + +class TimeConstantLiteral extends Literal { + final TimeConstantKind kind; + + TimeConstantLiteral(this.kind, Token keyword) : super(keyword); + + @override + R accept(AstVisitor visitor, A arg) { + return visitor.visitTimeConstantLiteral(this, arg); + } + + @override + bool contentEquals(TimeConstantLiteral other) { + return other.kind == kind; + } + + @override + dynamic get value => throw UnimplementedError('TimeConstantLiteral.value'); +} diff --git a/sqlparser/lib/src/ast/visitor.dart b/sqlparser/lib/src/ast/visitor.dart index 430881e1..d7944111 100644 --- a/sqlparser/lib/src/ast/visitor.dart +++ b/sqlparser/lib/src/ast/visitor.dart @@ -34,6 +34,7 @@ abstract class AstVisitor { R visitNullLiteral(NullLiteral e, A arg); R visitBooleanLiteral(BooleanLiteral e, A arg); R visitStringLiteral(StringLiteral e, A arg); + R visitTimeConstantLiteral(TimeConstantLiteral e, A arg); R visitCastExpression(CastExpression e, A arg); R visitBinaryExpression(BinaryExpression e, A arg); @@ -325,6 +326,11 @@ class RecursiveVisitor implements AstVisitor { return visitLiteral(e, arg); } + @override + R visitTimeConstantLiteral(TimeConstantLiteral e, A arg) { + return visitLiteral(e, arg); + } + @override R visitReference(Reference e, A arg) { return visitExpression(e, arg); diff --git a/sqlparser/lib/src/reader/parser/expressions.dart b/sqlparser/lib/src/reader/parser/expressions.dart index 92a26fd3..76958366 100644 --- a/sqlparser/lib/src/reader/parser/expressions.dart +++ b/sqlparser/lib/src/reader/parser/expressions.dart @@ -265,7 +265,18 @@ mixin ExpressionParser on ParserBase { if (_matchOne(TokenType.$false)) { return BooleanLiteral.withFalse(token); } - // todo CURRENT_TIME, CURRENT_DATE, CURRENT_TIMESTAMP + + const timeLiterals = { + TokenType.currentTime: TimeConstantKind.currentTime, + TokenType.currentDate: TimeConstantKind.currentDate, + TokenType.currentTimestamp: TimeConstantKind.currentTimestamp, + }; + + if (_match(timeLiterals.keys)) { + final token = _previous; + return TimeConstantLiteral(timeLiterals[token.type], token); + } + return null; } diff --git a/sqlparser/test/analysis/types/resolver_test.dart b/sqlparser/test/analysis/types/resolver_test.dart index e32b6b75..94d6af7a 100644 --- a/sqlparser/test/analysis/types/resolver_test.dart +++ b/sqlparser/test/analysis/types/resolver_test.dart @@ -34,6 +34,8 @@ Map _types = { const ResolveResult(ResolvedType(type: BasicType.text)), 'SELECT (3 * 4) = ?': const ResolveResult(ResolvedType(type: BasicType.int)), 'SELECT (3 / 4) = ?': const ResolveResult(ResolvedType(type: BasicType.int)), + 'SELECT CURRENT_TIME = ?': + const ResolveResult(ResolvedType(type: BasicType.text)), }; void main() { diff --git a/sqlparser/test/analysis/types2/misc_cases_test.dart b/sqlparser/test/analysis/types2/misc_cases_test.dart index 4510f518..f9b08297 100644 --- a/sqlparser/test/analysis/types2/misc_cases_test.dart +++ b/sqlparser/test/analysis/types2/misc_cases_test.dart @@ -29,6 +29,7 @@ const Map _types = { 'SELECT CAST(3 AS TEXT) = ?': ResolvedType(type: BasicType.text), 'SELECT (3 * 4) = ?': ResolvedType(type: BasicType.int), 'SELECT (3 / 4) = ?': ResolvedType(type: BasicType.int), + 'SELECT CURRENT_TIMESTAMP = ?': ResolvedType(type: BasicType.text), }; SqlEngine _spawnEngine() { diff --git a/sqlparser/test/parser/expression_test.dart b/sqlparser/test/parser/expression_test.dart index 3f2a911b..b21315f8 100644 --- a/sqlparser/test/parser/expression_test.dart +++ b/sqlparser/test/parser/expression_test.dart @@ -146,6 +146,12 @@ final Map _testCases = { ), 'foo ISNULL': IsNullExpression(Reference(columnName: 'foo')), 'foo NOTNULL': IsNullExpression(Reference(columnName: 'foo'), true), + 'CURRENT_TIME': TimeConstantLiteral( + TimeConstantKind.currentTime, token(TokenType.currentTime)), + 'CURRENT_TIMESTAMP': TimeConstantLiteral( + TimeConstantKind.currentTimestamp, token(TokenType.currentTimestamp)), + 'CURRENT_DATE': TimeConstantLiteral( + TimeConstantKind.currentDate, token(TokenType.currentDate)), }; void main() {