diff --git a/sqlparser/lib/src/analysis/error.dart b/sqlparser/lib/src/analysis/error.dart index 5c52e282..48b378d3 100644 --- a/sqlparser/lib/src/analysis/error.dart +++ b/sqlparser/lib/src/analysis/error.dart @@ -62,6 +62,7 @@ enum AnalysisErrorType { compoundColumnCountMismatch, cteColumnCountMismatch, valuesSelectCountMismatch, + viewColumnNamesMismatch, rowValueMisuse, other, } diff --git a/sqlparser/lib/src/analysis/steps/linting_visitor.dart b/sqlparser/lib/src/analysis/steps/linting_visitor.dart index e7b40b6b..c467e357 100644 --- a/sqlparser/lib/src/analysis/steps/linting_visitor.dart +++ b/sqlparser/lib/src/analysis/steps/linting_visitor.dart @@ -8,6 +8,28 @@ class LintingVisitor extends RecursiveVisitor { LintingVisitor(this.options, this.context); + @override + void visitCreateViewStatement(CreateViewStatement e, void arg) { + final resolvedColumns = e.query.resolvedColumns; + if (e.columns == null || resolvedColumns == null) { + return super.visitCreateViewStatement(e, arg); + } + + final amountOfNames = e.columns.length; + final amountOfColumns = resolvedColumns.length; + + if (amountOfNames != amountOfColumns) { + context.reportError(AnalysisError( + type: AnalysisErrorType.viewColumnNamesMismatch, + relevantNode: e, + message: 'This view declares $amountOfNames column names, but the ' + 'inner select statement returns $amountOfColumns', + )); + } + + visitChildren(e, arg); + } + @override void visitInvocation(SqlInvocation e, void arg) { final lowercaseCall = e.name.toLowerCase(); diff --git a/sqlparser/test/analysis/errors/view_column_names_mismatch_test.dart b/sqlparser/test/analysis/errors/view_column_names_mismatch_test.dart new file mode 100644 index 00000000..191826fc --- /dev/null +++ b/sqlparser/test/analysis/errors/view_column_names_mismatch_test.dart @@ -0,0 +1,17 @@ +import 'package:sqlparser/sqlparser.dart'; +import 'package:test/test.dart'; + +import '../data.dart'; + +void main() { + test('reports column name mismatches in CREATE VIEW statements', () { + final engine = SqlEngine()..registerTable(demoTable); + final result = engine.analyze('CREATE VIEW my_view (foo) AS ' + 'SELECT * FROM demo;'); + + expect(result.errors, hasLength(1)); + final error = result.errors.single; + + expect(error.type, AnalysisErrorType.viewColumnNamesMismatch); + }); +}