mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2025-12-26 08:00:01 +00:00
75 lines
4.0 KiB
Markdown
75 lines
4.0 KiB
Markdown
This document introduces how to implement SQL statements in GreptimeDB.
|
|
|
|
The execution entry point for SQL statements locates at Frontend Instance. You can see it has
|
|
implemented `SqlQueryHandler`:
|
|
|
|
```rust
|
|
impl SqlQueryHandler for Instance {
|
|
type Error = Error;
|
|
|
|
async fn do_query(&self, query: &str, query_ctx: QueryContextRef) -> Vec<Result<Output>> {
|
|
// ...
|
|
}
|
|
}
|
|
```
|
|
|
|
Normally, when a SQL query arrives at GreptimeDB, the `do_query` method will be called. After some parsing work, the SQL
|
|
will be feed into `StatementExecutor`:
|
|
|
|
```rust
|
|
// in Frontend Instance:
|
|
self.statement_executor.execute_sql(stmt, query_ctx).await
|
|
```
|
|
|
|
That's where we handle our SQL statements. You can just create a new match arm for your statement there, then the
|
|
statement is implemented for both GreptimeDB Standalone and Cluster. You can see how `DESCRIBE TABLE` is implemented as
|
|
an example.
|
|
|
|
Now, what if the statements should be handled differently for GreptimeDB Standalone and Cluster? You can see there's
|
|
a `SqlStatementExecutor` field in `StatementExecutor`. Each GreptimeDB Standalone and Cluster has its own implementation
|
|
of `SqlStatementExecutor`. If you are going to implement the statements differently in the two mode (
|
|
like `CREATE TABLE`), you have to implement them in their own `SqlStatementExecutor`s.
|
|
|
|
Summarize as the diagram below:
|
|
|
|
```text
|
|
SQL query
|
|
|
|
|
v
|
|
+---------------------------+
|
|
| SqlQueryHandler::do_query |
|
|
+---------------------------+
|
|
|
|
|
| SQL parsing
|
|
v
|
|
+--------------------------------+
|
|
| StatementExecutor::execute_sql |
|
|
+--------------------------------+
|
|
|
|
|
| SQL execution
|
|
v
|
|
+----------------------------------+
|
|
| commonly handled statements like |
|
|
| "plan_exec" for selection or |
|
|
+----------------------------------+
|
|
| |
|
|
For Standalone | | For Cluster
|
|
v v
|
|
+---------------------------+ +---------------------------+
|
|
| SqlStatementExecutor impl | | SqlStatementExecutor impl |
|
|
| in Datanode Instance | | in Frontend DistInstance |
|
|
+---------------------------+ +---------------------------+
|
|
```
|
|
|
|
Note that some SQL statements can be executed in our QueryEngine, in the form of `LogicalPlan`. You can follow the
|
|
invocation path down to the `QueryEngine` implementation from `StatementExecutor::plan_exec`. For now, there's only
|
|
one `DatafusionQueryEngine` for both GreptimeDB Standalone and Cluster. That lone query engine works for both modes is
|
|
because GreptimeDB read/write data through `Table` trait, and each mode has its own `Table` implementation.
|
|
|
|
We don't have any bias towards whether statements should be handled in query engine or `StatementExecutor`. You can
|
|
implement one kind of statement in both places. For example, `Insert` with selection is handled in query engine, because
|
|
we can easily do the query part there. However, `Insert` without selection is not, for the cost of parsing statement
|
|
to `LogicalPlan` is not neglectable. So generally if the SQL query is simple enough, you can handle it
|
|
in `StatementExecutor`; otherwise if it is complex or has some part of selection, it should be parsed to `LogicalPlan`
|
|
and handled in query engine.
|