2019-06-15 14:01:10 -07:00
|
|
|
# sqlparser
|
|
|
|
|
2023-11-01 02:27:01 -07:00
|
|
|
SQL parser and static analyzer written in Dart. At the moment, this library targets the
|
|
|
|
SQLite dialect only.
|
2019-06-22 13:35:34 -07:00
|
|
|
|
2019-06-29 03:48:09 -07:00
|
|
|
## Features
|
2020-01-21 08:55:41 -08:00
|
|
|
|
2023-11-01 02:27:01 -07:00
|
|
|
This library aims to support every SQLite feature, which includes parsing and detailed
|
2020-01-21 08:55:41 -08:00
|
|
|
static analysis.
|
|
|
|
We can resolve what type a column in a `SELECT` statement has, infer types for variables,
|
|
|
|
find semantic errors and more.
|
2019-09-25 11:19:39 -07:00
|
|
|
|
2023-11-01 02:27:01 -07:00
|
|
|
This library supports most SQLite features:
|
2021-01-11 11:15:10 -08:00
|
|
|
- DQL: Full support, including joins, `group by`, nested and compound selects, `WITH` clauses
|
2019-12-23 03:52:46 -08:00
|
|
|
and window functions
|
2020-01-21 08:55:41 -08:00
|
|
|
- DDL: Supports `CREATE TABLE` statements, including advanced features like foreign keys or
|
|
|
|
virtual tables (when a matching module like `fts5` is enabled). This library also supports
|
|
|
|
`CREATE TRIGGER` and `CREATE INDEX` statements.
|
2019-12-23 03:52:46 -08:00
|
|
|
|
2019-09-25 11:19:39 -07:00
|
|
|
### Using the parser
|
2023-11-01 02:27:01 -07:00
|
|
|
To obtain an abstract syntax tree from an SQL statement, use `SqlEngine.parse`.
|
2019-06-22 13:35:34 -07:00
|
|
|
```dart
|
|
|
|
import 'package:sqlparser/sqlparser.dart';
|
|
|
|
|
|
|
|
final engine = SqlEngine();
|
2019-06-29 13:29:16 -07:00
|
|
|
final result = engine.parse('''
|
2019-06-22 13:35:34 -07:00
|
|
|
SELECT f.* FROM frameworks f
|
|
|
|
INNER JOIN uses_language ul ON ul.framework = f.id
|
|
|
|
INNER JOIN languages l ON l.id = ul.language
|
|
|
|
WHERE l.name = 'Dart'
|
|
|
|
ORDER BY f.name ASC, f.popularity DESC
|
|
|
|
LIMIT 5 OFFSET 5 * 3
|
|
|
|
''');
|
2019-06-29 13:29:16 -07:00
|
|
|
// result.rootNode contains the select statement in tree form
|
2019-06-26 14:07:30 -07:00
|
|
|
```
|
|
|
|
|
2019-06-29 03:48:09 -07:00
|
|
|
### Analysis
|
2023-11-01 02:27:01 -07:00
|
|
|
Given information about all tables and an SQL statement, this library can:
|
2019-06-26 14:07:30 -07:00
|
|
|
|
2019-06-29 03:48:09 -07:00
|
|
|
1. Determine which result columns a query is going to have, including types and nullability
|
|
|
|
2. Make an educated guess about what type the variables in the query should have (it's not really
|
2023-11-01 02:27:01 -07:00
|
|
|
possible to be 100% accurate about this because SQLite is very flexible at types, but this library
|
2019-07-01 06:04:17 -07:00
|
|
|
gets it mostly right)
|
2019-07-01 12:20:59 -07:00
|
|
|
3. Issue basic warnings about queries that are syntactically valid but won't run (references unknown
|
2019-07-01 06:04:17 -07:00
|
|
|
tables / columns, uses undefined functions, etc.)
|
2019-06-29 03:48:09 -07:00
|
|
|
|
|
|
|
To use the analyzer, first register all known tables via `SqlEngine.registerTable`. Then,
|
2019-09-25 11:19:39 -07:00
|
|
|
`SqlEngine.analyze(sql)` gives you an `AnalysisContext` which contains an annotated AST and information
|
2019-06-29 03:48:09 -07:00
|
|
|
about errors. The type of result columns and expressions can be inferred by using
|
2019-07-01 06:04:17 -07:00
|
|
|
`AnalysisContext.typeOf()`. Here's an example:
|
2019-06-29 03:48:09 -07:00
|
|
|
|
2020-01-21 08:55:41 -08:00
|
|
|
```dart
|
2019-06-29 03:48:09 -07:00
|
|
|
final id = TableColumn('id', const ResolvedType(type: BasicType.int));
|
|
|
|
final content = TableColumn('content', const ResolvedType(type: BasicType.text));
|
|
|
|
final demoTable = Table(
|
|
|
|
name: 'demo',
|
|
|
|
resolvedColumns: [id, content],
|
|
|
|
);
|
|
|
|
final engine = SqlEngine()..registerTable(demoTable);
|
|
|
|
|
|
|
|
final context =
|
|
|
|
engine.analyze('SELECT id, d.content, *, 3 + 4 FROM demo AS d');
|
|
|
|
|
|
|
|
final select = context.root as SelectStatement;
|
|
|
|
final resolvedColumns = select.resolvedColumns;
|
|
|
|
|
2020-05-03 13:21:29 -07:00
|
|
|
resolvedColumns.map((c) => c.name); // id, content, id, content, 3 + 4
|
|
|
|
resolvedColumns.map((c) => context.typeOf(c).type.type); // int, text, int, text, int, int
|
2019-06-29 03:48:09 -07:00
|
|
|
```
|
|
|
|
|
2019-06-30 03:01:46 -07:00
|
|
|
## But why?
|
2022-02-26 12:38:28 -08:00
|
|
|
[Drift](https://pub.dev/packages/drift), a persistence library for Dart apps, uses this
|
2023-11-01 02:27:01 -07:00
|
|
|
package to generate type-safe methods from SQL.
|
2019-06-30 03:01:46 -07:00
|
|
|
|
2019-06-29 03:48:09 -07:00
|
|
|
## Thanks
|
|
|
|
- To [Bob Nystrom](https://github.com/munificent) for his amazing ["Crafting Interpreters"](https://craftinginterpreters.com/)
|
|
|
|
book, which was incredibly helpful when writing the parser.
|