diff --git a/sqlparser/lib/src/analysis/steps/linting_visitor.dart b/sqlparser/lib/src/analysis/steps/linting_visitor.dart index 38ff2cea..219d9169 100644 --- a/sqlparser/lib/src/analysis/steps/linting_visitor.dart +++ b/sqlparser/lib/src/analysis/steps/linting_visitor.dart @@ -153,6 +153,22 @@ class LintingVisitor extends RecursiveVisitor { visitChildren(e, arg); } + @override + void visitDefaultValues(DefaultValues e, void arg) { + // `DEFAULT VALUES` is not supported in a trigger, see https://www.sqlite.org/lang_insert.html + if (!_isTopLevelStatement) { + context.reportError( + AnalysisError( + type: AnalysisErrorType.synctactic, + message: '`DEFAULT VALUES` cannot be used in a trigger.', + relevantNode: e, + ), + ); + } + + visitChildren(e, arg); + } + @override void visitInsertStatement(InsertStatement e, void arg) { for (final target in e.targetColumns) { @@ -295,6 +311,25 @@ class LintingVisitor extends RecursiveVisitor { visitChildren(e, arg); } + @override + void visitTableReference(TableReference e, void arg) { + final parent = e.parent; + if (parent is HasPrimarySource && parent.table == e) { + // The source of a `INSERT`, `UPDATE` or `DELETE` statement must not have + // an alias in `CREATE TRIGGER` statements. + if (!_isTopLevelStatement && e.as != null) { + context.reportError(AnalysisError( + type: AnalysisErrorType.synctactic, + message: + 'The source must not have an `AS` alias when used in a trigger.', + relevantNode: e, + )); + } + } + + visitChildren(e, arg); + } + @override void visitTuple(Tuple e, void arg) { if (!e.usedAsRowValue) return; diff --git a/sqlparser/test/analysis/errors/syntax_error_test.dart b/sqlparser/test/analysis/errors/syntax_error_test.dart index 5e5b592a..6490294f 100644 --- a/sqlparser/test/analysis/errors/syntax_error_test.dart +++ b/sqlparser/test/analysis/errors/syntax_error_test.dart @@ -4,6 +4,7 @@ import 'package:sqlparser/sqlparser.dart'; import 'package:test/test.dart'; import '../data.dart'; +import 'utils.dart'; void main() { group('DO UPDATE clause without conflict target', () { @@ -66,4 +67,46 @@ void main() { contains('Only column names can be used in a PRIMARY KEY clause')) ]); }); + + group('illegal constructs in triggers', () { + final engine = SqlEngine()..registerTable(demoTable); + + test('DEFAULT VALUES', () { + engine.analyze('INSERT INTO demo DEFAULT VALUES').expectNoError(); + engine + .analyze('CREATE TRIGGER tgr AFTER DELETE ON demo BEGIN ' + 'INSERT INTO demo DEFAULT VALUES;' + 'END;') + .expectError('DEFAULT VALUES', type: AnalysisErrorType.synctactic); + }); + + group('aliased source tables', () { + test('insert', () { + engine.analyze('INSERT INTO demo AS d VALUES (?, ?)').expectNoError(); + engine + .analyze('CREATE TRIGGER tgr AFTER DELETE ON demo BEGIN ' + 'INSERT INTO demo AS d VALUES (?, ?);' + 'END;') + .expectError('demo AS d', type: AnalysisErrorType.synctactic); + }); + + test('update', () { + engine.analyze('UPDATE demo AS d SET id = id + 1;').expectNoError(); + engine + .analyze('CREATE TRIGGER tgr AFTER DELETE ON demo BEGIN ' + 'UPDATE demo AS d SET id = id + 1;' + 'END;') + .expectError('demo AS d', type: AnalysisErrorType.synctactic); + }); + + test('delete', () { + engine.analyze('DELETE FROM demo d;').expectNoError(); + engine + .analyze('CREATE TRIGGER tgr AFTER DELETE ON demo BEGIN ' + 'DELETE FROM demo d;' + 'END;') + .expectError('demo d', type: AnalysisErrorType.synctactic); + }); + }); + }); }