# How GreptimeDB Handles Protocol Compatibility with MySQL and PostgreSQL Clients

> GreptimeDB achieves MySQL and PostgreSQL protocol compatibility using independent wire-protocol servers, allowing native clients to connect directly without special drivers. Discover how.

- Repository: [Greptime/greptimedb](https://github.com/greptimeteam/greptimedb)
- Tags: deep-dive
- Published: 2026-03-02

---

**GreptimeDB implements independent wire-protocol servers using the `opensrv-mysql` and `pgwire` crates, enabling native MySQL and PostgreSQL clients to connect without custom drivers or protocol adapters.**

GreptimeDB achieves seamless protocol compatibility with MySQL and PostgreSQL clients through dedicated wire-protocol implementations that translate native client commands into the database's internal query engine. According to the `greptimeteam/greptimedb` source code, the system runs independent TCP servers for each protocol, handling authentication, SQL dialect parsing, and result formatting to ensure native client compatibility.

## MySQL Protocol Implementation

### Server Architecture and Connection Handling

GreptimeDB's MySQL compatibility layer builds on the **`opensrv-mysql`** crate, implementing the **`AsyncMysqlShim`** trait to handle the MySQL wire protocol. The server initialization occurs in **`MysqlServer::create_server`** ([`src/servers/src/mysql/server.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/servers/src/mysql/server.rs)), which constructs a `BaseTcpServer` named `"MySQL"` and stores the `MysqlSpawnRef` containing the query handler and optional user provider.

For each incoming TCP connection, the server spawns a task that constructs a **`MysqlInstanceShim`** ([`src/servers/src/mysql/handler.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/servers/src/mysql/handler.rs)). This shim manages the connection lifecycle, implementing authentication via the optional `UserProvider` and executing queries through **`MysqlInstanceShim::do_query`**.

### SQL Dialect and Parsing

MySQL-specific SQL syntax is handled by the **`MySqlDialect`** from the `sqlparser` crate, re-exported in [`src/sql/src/dialect.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/sql/src/dialect.rs). The shim parses statements using **`sql::parser::ParserContext`** configured for the MySQL dialect.

Protocol-specific quirks include MySQL-style **`KILL QUERY`** statements, parsed in [`src/sql/src/parser.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/sql/src/parser.rs), and the **`SHOW TABLES`** command, which renames the output column from `table_name` to **`Tables_in_{schema}`** in [`src/query/src/sql.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/query/src/sql.rs) to match MySQL client expectations.

### Error Mapping and Result Formatting

Error translation occurs in **`writer::handle_err`** ([`src/servers/src/mysql/writer.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/servers/src/mysql/writer.rs)). This function extracts the GreptimeDB `StatusCode`, determines the appropriate MySQL `ErrorKind`, and prefixes the error message with the status code (e.g., `"(StatusCode): message"`).

Result streaming is handled by **`writer::write_output`**, which builds a `MysqlResultWriter` to stream Arrow record batches as MySQL result sets. The writer handles affected-row responses and warning accumulation, ensuring MySQL clients receive properly formatted tabular data.

### Session Management

The global **`Session`** struct ([`src/session/src/lib.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/session/src/lib.rs)) stores a **`Channel::Mysql`** value, allowing the query engine to identify the client type for protocol-specific behaviors such as warning handling and dialect selection.

## PostgreSQL Protocol Implementation

### Server Architecture and Connection Handling

PostgreSQL compatibility leverages the **`pgwire`** crate, implementing **`SimpleQueryHandler`** and **`ExtendedQueryHandler`** traits. Server initialization occurs in **`PostgresServer::new`** ([`src/servers/src/postgres/server.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/servers/src/postgres/server.rs)), which builds a `MakePostgresServerHandler` containing the query handler and TLS configuration.

Connection handling uses **`pgwire::tokio::process_socket`** to accept TCP connections, creating a **`PostgresServerHandlerInner`** ([`src/servers/src/postgres/handler.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/servers/src/postgres/handler.rs)) for each client. This handler manages the PostgreSQL startup sequence, authentication via `UserProvider`, and query execution.

### SQL Dialect and Extended Parsing

PostgreSQL syntax is supported through the **`PostgreSqlDialect`** from `sqlparser` ([`src/sql/src/dialect.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/sql/src/dialect.rs)). The handler uses **`PostgresCompatibilityParser`** (part of `datafusion_pg_catalog`) to recognize PostgreSQL-specific extensions such as **`COPY TO STDOUT`** and catalog queries.

### Error Mapping and Result Formatting

Error conversion occurs in **`postgres::utils::convert_err`** ([`src/servers/src/postgres/utils.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/servers/src/postgres/utils.rs)). This utility maps GreptimeDB `StatusCode` values to appropriate PostgreSQL error fields including SQLSTATE codes, severity levels, and detailed messages.

Result formatting uses **`handler::output_to_query_response`** to build PostgreSQL `Response` objects (e.g., `QueryResponse`, `Execution`). Row streaming employs **`DataRowEncoder`** to convert Arrow record batches into PostgreSQL wire format, ensuring `psql` and other PostgreSQL clients receive properly formatted result sets.

### Extended Query Flow and Parameter Handling

The PostgreSQL implementation fully supports the extended query protocol. **`PostgresServerHandlerInner`** implements **`ExtendedQueryHandler`**, processing the `Parse → Bind → Execute` message flow for prepared statements.

Prepared statements are cached within the handler, and parameter type checking occurs during the bind phase. Type mismatches or execution errors are converted to `PgWireError` via `convert_err`, maintaining protocol compliance.

### Session Management

Similar to the MySQL implementation, the **`Session`** struct stores **`Channel::Postgres`** ([`src/session/src/lib.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/session/src/lib.rs)), enabling the engine to apply PostgreSQL-specific behaviors for warnings and dialect features.

## Configuration and Deployment Examples

### Enabling Both Protocol Listeners

GreptimeDB exposes MySQL and PostgreSQL options via `MysqlOptions` and `PostgresOptions` in [`src/standalone/src/options.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/standalone/src/options.rs). Enable both protocols in your configuration file:

```toml

# config/example.toml

[mysql]
listen_addr = "0.0.0.0:4002"
tls_mode = "Disabled"

[postgres]
listen_addr = "0.0.0.0:4001"
tls_mode = "Disabled"

```

Running the binary spins up two independent TCP listeners—port 4002 for MySQL and port 4001 for PostgreSQL.

### Connecting with Native Clients

**MySQL client connection:**

```bash
$ mysql -h 127.0.0.1 -P 4002 -u root -p
Enter password: ********
Welcome to the GreptimeDB monitor...
mysql> SHOW TABLES;
+-------------------+
| Tables_in_public |
+-------------------+
| demo              |
+-------------------+

```

The column name `Tables_in_public` is produced by the MySQL-specific rename logic in [`src/query/src/sql.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/query/src/sql.rs).

**PostgreSQL client connection:**

```bash
$ psql -h 127.0.0.1 -p 4001 -U greptime
Password for user greptime:
greptime=# SELECT * FROM demo LIMIT 5;

 id | name | value
----+------+-------
 1  | foo  | 10.0
 2  | bar  | 20.0
(2 rows)

```

The response is generated by `handler::output_to_query_response` in the PostgreSQL handler.

### Executing Protocol-Specific Commands

**MySQL-style `KILL QUERY`:**

```sql
-- From a MySQL client
KILL QUERY 3;

```

The parser in [`src/sql/src/parser.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/sql/src/parser.rs) recognizes this MySQL-specific syntax, translating the connection ID to an internal process ID for query termination.

**PostgreSQL extended query flow:**

```sql
PREPARE stmt AS SELECT * FROM demo WHERE id = $1;
EXECUTE stmt USING 1;

```

`PostgresServerHandlerInner` implements `ExtendedQueryHandler` to process the `Parse`, `Bind`, and `Execute` messages, caching the prepared statement and type-checking parameters.

## Summary

- **Dual Server Architecture**: GreptimeDB runs independent TCP servers for MySQL (`opensrv-mysql`) and PostgreSQL (`pgwire`), allowing native clients to connect on separate ports without custom drivers.
- **Dialect-Aware Parsing**: The system uses `MySqlDialect` and `PostgreSqlDialect` from `sqlparser` to handle protocol-specific syntax, including MySQL `KILL QUERY` and PostgreSQL `COPY TO STDOUT`.
- **Protocol-Specific Output**: MySQL results rename columns (e.g., `Tables_in_{schema}`) and map errors to MySQL error codes via `writer::handle_err`, while PostgreSQL returns standard wire-protocol responses with SQLSTATE codes via `convert_err`.
- **Session Differentiation**: The `Session` struct tracks `Channel::Mysql` or `Channel::Postgres`, enabling the query engine to apply protocol-specific behaviors for warnings and result formatting.
- **Extended Query Support**: The PostgreSQL implementation fully supports prepared statements through the `ExtendedQueryHandler` trait, processing `Parse`, `Bind`, and `Execute` messages.

## Frequently Asked Questions

### Does GreptimeDB require custom drivers to connect with MySQL or PostgreSQL clients?

No. GreptimeDB implements native wire-protocol compatibility using the `opensrv-mysql` and `pgwire` crates, allowing standard MySQL and PostgreSQL clients (such as `mysql`, `psql`, JDBC drivers, and libpq-based tools) to connect directly without modification.

### How does GreptimeDB handle SQL dialect differences between MySQL and PostgreSQL clients?

GreptimeDB uses protocol-specific SQL dialects from the `sqlparser` crate. For MySQL connections, it applies `MySqlDialect` and handles MySQL-specific syntax like `KILL QUERY` and `SHOW TABLES` column renaming. For PostgreSQL, it uses `PostgreSqlDialect` and `PostgresCompatibilityParser` to support PostgreSQL extensions such as `COPY TO STDOUT` and catalog queries.

### What happens to GreptimeDB-specific errors when using MySQL or PostgreSQL clients?

Errors are mapped to protocol-native formats. For MySQL, `writer::handle_err` in [`src/servers/src/mysql/writer.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/servers/src/mysql/writer.rs) converts GreptimeDB `StatusCode` values to MySQL `ErrorKind` codes with prefixed status messages. For PostgreSQL, `postgres::utils::convert_err` in [`src/servers/src/postgres/utils.rs`](https://github.com/greptimeteam/greptimedb/blob/main/src/servers/src/postgres/utils.rs) maps errors to `PgWireError` with appropriate SQLSTATE codes, severity levels, and detailed messages.

### Can I use prepared statements with PostgreSQL clients connecting to GreptimeDB?

Yes. The PostgreSQL protocol implementation fully supports the extended query flow for prepared statements. The `PostgresServerHandlerInner` struct implements `ExtendedQueryHandler`, processing `Parse`, `Bind`, and `Execute` messages. Statements are cached within the handler session, and parameters are type-checked during the bind phase, allowing safe execution of queries like `PREPARE stmt AS SELECT * FROM table WHERE id = $1`.