Skip to content

Commit

Permalink
Add exception test on scanning vs parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
simolus3 committed Nov 11, 2024
1 parent c2b66de commit b2a0d50
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 0 deletions.
23 changes: 23 additions & 0 deletions sqlparser/lib/src/engine/sql_engine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ class SqlEngine {
/// Tokenizes the [source] into a list list [Token]s. Each [Token] contains
/// information about where it appears in the [source] and a [TokenType].
///
/// If an error occurs while tokenizing, e.g. because an invalid token is
/// found, throws a [CumulatedTokenizerException].
///
/// Note that the list might be tokens that should be
/// [Token.invisibleToParser], if you're passing them to a [Parser] directly,
/// you need to filter them. When using the methods in this class, this will
Expand Down Expand Up @@ -159,6 +162,11 @@ class SqlEngine {
}

/// Parses a single [sql] statement into an AST-representation.
///
/// This method generally doesn't throw, but instead collects parsing errors
/// through [ParseResult.errors]. Callers should check that value instead of
/// blindly trusting the returned AST, as it will be an approximation if
/// parsing errors were encountered.
ParseResult parse(String sql) {
final (tokens, parser, _) = _createParser(sql);

Expand All @@ -170,6 +178,11 @@ class SqlEngine {
///
/// You can use the [AstNode.childNodes] of the returned [ParseResult.rootNode]
/// to inspect the returned statements.
///
/// This method generally doesn't throw, but instead collects parsing errors
/// through [ParseResult.errors]. Callers should check that value instead of
/// blindly trusting the returned AST, as it will be an approximation if
/// parsing errors were encountered.
ParseResult parseMultiple(String sql) {
final (tokens, parser, _) = _createParser(sql);

Expand All @@ -181,6 +194,11 @@ class SqlEngine {
///
/// The [ParseResult.rootNode] will be a [ColumnDefinition] with the parsed
/// constraints.
///
/// This method generally doesn't throw, but instead collects parsing errors
/// through [ParseResult.errors]. Callers should check that value instead of
/// blindly trusting the returned AST, as it will be an approximation if
/// parsing errors were encountered.
ParseResult parseColumnConstraints(String sql) {
final (tokens, parser, _) = _createParser(sql, driftExtensions: false);

Expand All @@ -201,6 +219,11 @@ class SqlEngine {
///
/// The [ParseResult.rootNode] will either be a [TableConstraint] or an
/// [InvalidStatement] in case of parsing errors.
///
/// This method generally doesn't throw, but instead collects parsing errors
/// through [ParseResult.errors]. Callers should check that value instead of
/// blindly trusting the returned AST, as it will be an approximation if
/// parsing errors were encountered.
ParseResult parseTableConstraint(String sql) {
final (tokens, parser, _) = _createParser(sql, driftExtensions: false);

Expand Down
19 changes: 19 additions & 0 deletions sqlparser/test/scanner/scanner_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,23 @@ void main() {
);
});
});

test('does not crash on non-SQL input', () async {
// Regression test for https://github.com/simolus3/drift/issues/3273#issuecomment-2468988502
const badInput = '''
class Screen extends ConsumerStatefulWidget {
const Screen({super.key});
@override
ConsumerState<Screen> createState() => _ScreenState();
}
''';

expect(() => SqlEngine().tokenize(badInput),
throwsA(isA<CumulatedTokenizerException>()));

final parsed = SqlEngine().parse(badInput);
expect(parsed.errors, isNotEmpty);
expect(parsed.rootNode, isA<InvalidStatement>());
});
}

0 comments on commit b2a0d50

Please sign in to comment.