How GreptimeDB Handles Protocol Compatibility with MySQL and PostgreSQL Clients

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), 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). 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. 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, and the SHOW TABLES command, which renames the output column from table_name to Tables_in_{schema} in 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). 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) 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), 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) 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). 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). 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), 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. Enable both protocols in your configuration file:


# 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:

$ 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.

PostgreSQL client connection:

$ 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:

-- From a MySQL client
KILL QUERY 3;

The parser in 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:

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 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →