diff --git a/sqlparser/lib/src/reader/parser.dart b/sqlparser/lib/src/reader/parser.dart index 905fb48e..561ff1d5 100644 --- a/sqlparser/lib/src/reader/parser.dart +++ b/sqlparser/lib/src/reader/parser.dart @@ -716,9 +716,8 @@ class Parser { final token = _peek; Literal? _parseInner() { - if (_matchOne(TokenType.numberLiteral)) { - final number = token as NumericToken; - return NumericLiteral(number.parsedNumber, token); + if (_check(TokenType.numberLiteral)) { + return _numericLiteral(); } if (_matchOne(TokenType.stringLiteral)) { return StringLiteral(token as StringLiteralToken); @@ -752,6 +751,12 @@ class Parser { return literal; } + NumericLiteral _numericLiteral() { + final number = _consume(TokenType.numberLiteral, 'Expected a number here') + as NumericToken; + return NumericLiteral(number.parsedNumber, number)..setSpan(number, number); + } + Expression _primary() { final literal = _literalOrNull(); if (literal != null) return literal; @@ -2442,10 +2447,17 @@ class Parser { return CheckColumn(resolvedName, expr)..setSpan(first, _previous); } if (_matchOne(TokenType.$default)) { - Expression? expr = _literalOrNull(); + Expression expr; - // when not a literal, expect an expression in parentheses - expr ??= _expressionInParentheses(); + if (_match(const [TokenType.plus, TokenType.minus])) { + // Signed number + final operator = _previous; + expr = UnaryExpression(operator, _numericLiteral()) + ..setSpan(operator, _previous); + } else { + // Literal or an expression in parentheses + expr = _literalOrNull() ?? _expressionInParentheses(); + } return Default(resolvedName, expr)..setSpan(first, _previous); } diff --git a/sqlparser/test/parser/create_table_test.dart b/sqlparser/test/parser/create_table_test.dart index acdafec6..9c13865e 100644 --- a/sqlparser/test/parser/create_table_test.dart +++ b/sqlparser/test/parser/create_table_test.dart @@ -300,4 +300,26 @@ void main() { ), ); }); + + test('parses DEFAULT with a negative literal', () { + // regression test for https://github.com/simolus3/moor/discussions/1550 + testStatement( + 'CREATE TABLE a (b INTEGER NOT NULL DEFAULT -1);', + CreateTableStatement( + tableName: 'a', + columns: [ + ColumnDefinition(columnName: 'b', typeName: 'INTEGER', constraints: [ + NotNull(null), + Default( + null, + UnaryExpression( + Token(TokenType.minus, fakeSpan('-')), + NumericLiteral(1, NumericToken(fakeSpan('1'))), + ), + ) + ]) + ], + ), + ); + }); } diff --git a/sqlparser/test/parser/utils.dart b/sqlparser/test/parser/utils.dart index cd6c0da9..e261c263 100644 --- a/sqlparser/test/parser/utils.dart +++ b/sqlparser/test/parser/utils.dart @@ -38,8 +38,11 @@ void testMoorFile(String moorFile, MoorFile expected) { } void testStatement(String sql, AstNode expected, {bool moorMode = false}) { - final parsed = - SqlEngine(EngineOptions(useMoorExtensions: moorMode)).parse(sql).rootNode; + final result = + SqlEngine(EngineOptions(useMoorExtensions: moorMode)).parse(sql); + expect(result.errors, isEmpty); + + final parsed = result.rootNode; enforceHasSpan(parsed); enforceEqual(parsed, expected); }