diff --git a/sqlparser/CHANGELOG.md b/sqlparser/CHANGELOG.md index 70723712..5f55cbe8 100644 --- a/sqlparser/CHANGELOG.md +++ b/sqlparser/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.15.1-dev + +- New analysis checks for `RETURNING`: Disallow `table.*` syntax and aggregate expressions + ## 0.15.0 - __Breaking__: Change `InsertStatement.upsert` to a list of upsert clauses diff --git a/sqlparser/lib/src/analysis/steps/linting_visitor.dart b/sqlparser/lib/src/analysis/steps/linting_visitor.dart index f66ea8ee..2db3fbe3 100644 --- a/sqlparser/lib/src/analysis/steps/linting_visitor.dart +++ b/sqlparser/lib/src/analysis/steps/linting_visitor.dart @@ -128,6 +128,29 @@ class LintingVisitor extends RecursiveVisitor { } } + for (final column in e.columns) { + // Table wildcards are not currently allowed, see + // https://www.sqlite.org/src/info/132994c8b1063bfb + if (column is StarResultColumn && column.tableName != null) { + context.reportError(AnalysisError( + type: AnalysisErrorType.synctactic, + message: 'Columns in RETURNING may not use the TABLE.* syntax', + relevantNode: column, + )); + } else if (column is ExpressionResultColumn) { + // While we're at it, window expressions aren't allowed either + if (column.expression is AggregateExpression) { + context.reportError( + AnalysisError( + type: AnalysisErrorType.illegalUseOfReturning, + message: 'Aggregate expressions are not allowed in RETURNING', + relevantNode: column.expression, + ), + ); + } + } + } + visitChildren(e, arg); } diff --git a/sqlparser/test/analysis/errors/returning_test.dart b/sqlparser/test/analysis/errors/returning_test.dart index a8e89b56..e9cdca94 100644 --- a/sqlparser/test/analysis/errors/returning_test.dart +++ b/sqlparser/test/analysis/errors/returning_test.dart @@ -30,4 +30,36 @@ void main() { expect(result.errors, hasLength(1)); expect(result.errors.single.type, AnalysisErrorType.illegalUseOfReturning); }); + + test('does not allow star columns with an associated table', () { + final result = engine.analyze(''' + UPDATE t SET id = t.id + 1 + FROM (SELECT * FROM t) AS old + RETURNING old.*; + '''); + + expect(result.errors, hasLength(1)); + expect( + result.errors.single, + isA() + .having((e) => e.source!.span!.text, 'source.span.text', 'old.*') + .having((e) => e.message, 'message', + contains('RETURNING may not use the TABLE.* syntax')), + ); + }); + + test('does not allow aggregate expressions', () { + final result = engine.analyze('INSERT INTO t DEFAULT VALUES RETURNING ' + 'MAX(id) OVER (PARTITION BY c2)'); + + expect(result.errors, hasLength(1)); + expect( + result.errors.single, + isA() + .having((e) => e.source!.span!.text, 'source.span.text', + 'MAX(id) OVER (PARTITION BY c2)') + .having((e) => e.message, 'message', + 'Aggregate expressions are not allowed in RETURNING'), + ); + }); }