⌘+k ctrl+k
Search Shortcut cmd + k | ctrl + k
MySQL Extension

The mysql extension allows DuckDB to directly read and write data from/to a running MySQL instance. The data can be queried directly from the underlying MySQL database. Data can be loaded from MySQL tables into DuckDB tables, or vice versa.

Installing and Loading

To install the mysql extension, run:

INSTALL mysql;

The extension is loaded automatically upon first use. If you prefer to load it manually, run:

LOAD mysql;

Reading Data from MySQL

To make a MySQL database accessible to DuckDB use the ATTACH command with the mysql or the mysql_scanner type:

ATTACH 'host=localhost user=root port=0 database=mysql' AS mysqldb (TYPE mysql);
USE mysqldb;

Configuration

The connection string determines the parameters for how to connect to MySQL as a set of key=value pairs. Any options not provided are replaced by their default values, as per the table below. Connection information can also be specified with environment variables. If no option is provided explicitly, the MySQL extension tries to read it from an environment variable.

Setting Default Environment variable
database NULL MYSQL_DATABASE
host localhost MYSQL_HOST
password   MYSQL_PWD
port 0 MYSQL_TCP_PORT
socket NULL MYSQL_UNIX_PORT
user current user MYSQL_USER
ssl_mode preferred  
ssl_ca    
ssl_capath    
ssl_cert    
ssl_cipher    
ssl_crl    
ssl_crlpath    
ssl_key    

Configuring via Secrets

MySQL connection information can also be specified with secrets. The following syntax can be used to create a secret.

CREATE SECRET (
    TYPE mysql,
    HOST '127.0.0.1',
    PORT 0,
    DATABASE mysql,
    USER 'mysql',
    PASSWORD ''
);

The information from the secret will be used when ATTACH is called. We can leave the connection string empty to use all of the information stored in the secret.

ATTACH '' AS mysql_db (TYPE mysql);

We can use the connection string to override individual options. For example, to connect to a different database while still using the same credentials, we can override only the database name in the following manner.

ATTACH 'database=my_other_db' AS mysql_db (TYPE mysql);

By default, created secrets are temporary. Secrets can be persisted using the CREATE PERSISTENT SECRET command. Persistent secrets can be used across sessions.

Managing Multiple Secrets

Named secrets can be used to manage connections to multiple MySQL database instances. Secrets can be given a name upon creation.

CREATE SECRET mysql_secret_one (
    TYPE mysql,
    HOST '127.0.0.1',
    PORT 0,
    DATABASE mysql,
    USER 'mysql',
    PASSWORD ''
);

The secret can then be explicitly referenced using the SECRET parameter in the ATTACH.

ATTACH '' AS mysql_db_one (TYPE mysql, SECRET mysql_secret_one);

SSL Connections

The ssl connection parameters can be used to make SSL connections. Below is a description of the supported parameters.

Setting Description
ssl_mode The security state to use for the connection to the server: disabled, required, verify_ca, verify_identity or preferred (default: preferred)
ssl_ca The path name of the Certificate Authority (CA) certificate file
ssl_capath The path name of the directory that contains trusted SSL CA certificate files
ssl_cert The path name of the client public key certificate file
ssl_cipher The list of permissible ciphers for SSL encryption
ssl_crl The path name of the file containing certificate revocation lists
ssl_crlpath The path name of the directory that contains files containing certificate revocation lists
ssl_key The path name of the client private key file

Reading MySQL Tables

The tables in the MySQL database can be read as if they were normal DuckDB tables, but the underlying data is read directly from MySQL at query time.

SHOW ALL TABLES;
name
signed_integers
SELECT * FROM signed_integers;
t s m i b
-128 -32768 -8388608 -2147483648 -9223372036854775808
127 32767 8388607 2147483647 9223372036854775807
NULL NULL NULL NULL NULL

It might be desirable to create a copy of the MySQL databases in DuckDB to prevent the system from re-reading the tables from MySQL continuously, particularly for large tables.

Data can be copied over from MySQL to DuckDB using standard SQL, for example:

CREATE TABLE duckdb_table AS FROM mysqlscanner.mysql_table;

Writing Data to MySQL

In addition to reading data from MySQL, create tables, ingest data into MySQL and make other modifications to a MySQL database using standard SQL queries.

This allows you to use DuckDB to, for example, export data that is stored in a MySQL database to Parquet, or read data from a Parquet file into MySQL.

Below is a brief example of how to create a new table in MySQL and load data into it.

ATTACH 'host=localhost user=root port=0 database=mysqlscanner' AS mysql_db (TYPE mysql);
CREATE TABLE mysql_db.tbl (id INTEGER, name VARCHAR);
INSERT INTO mysql_db.tbl VALUES (42, 'DuckDB');

Many operations on MySQL tables are supported. All these operations directly modify the MySQL database, and the result of subsequent operations can then be read using MySQL. Note that if modifications are not desired, ATTACH can be run with the READ_ONLY property which prevents making modifications to the underlying database. For example:

ATTACH 'host=localhost user=root port=0 database=mysqlscanner' AS mysql_db (TYPE mysql, READ_ONLY);

Supported Operations

Below is a list of supported operations.

CREATE TABLE

CREATE TABLE mysql_db.tbl (id INTEGER, name VARCHAR);

INSERT INTO

INSERT INTO mysql_db.tbl VALUES (42, 'DuckDB');

SELECT

SELECT * FROM mysql_db.tbl;
id name
42 DuckDB

COPY

COPY mysql_db.tbl TO 'data.parquet';
COPY mysql_db.tbl FROM 'data.parquet';

You may also create a full copy of the database using the COPY FROM DATABASE statement:

COPY FROM DATABASE mysql_db TO my_duckdb_db;

UPDATE

UPDATE mysql_db.tbl
SET name = 'Woohoo'
WHERE id = 42;

DELETE

DELETE FROM mysql_db.tbl
WHERE id = 42;

ALTER TABLE

ALTER TABLE mysql_db.tbl
ADD COLUMN k INTEGER;

DROP TABLE

DROP TABLE mysql_db.tbl;

CREATE VIEW

CREATE VIEW mysql_db.v1 AS SELECT 42;

CREATE SCHEMA and DROP SCHEMA

CREATE SCHEMA mysql_db.s1;
CREATE TABLE mysql_db.s1.integers (i INTEGER);
INSERT INTO mysql_db.s1.integers VALUES (42);
SELECT * FROM mysql_db.s1.integers;
i
42
DROP SCHEMA mysql_db.s1;

Transactions

CREATE TABLE mysql_db.tmp (i INTEGER);
BEGIN;
INSERT INTO mysql_db.tmp VALUES (42);
SELECT * FROM mysql_db.tmp;

This returns:

i
42
ROLLBACK;
SELECT * FROM mysql_db.tmp;

This returns an empty table.

The DDL statements are not transactional in MySQL.

Running SQL Queries in MySQL

The mysql_query Table Function

The mysql_query table function allows you to run arbitrary read queries within an attached database. mysql_query takes the name of the attached MySQL database to execute the query in, as well as the SQL query to execute. The result of the query is returned. Single-quote strings are escaped by repeating the single quote twice.

mysql_query(attached_database::VARCHAR, query::VARCHAR)

For example:

ATTACH 'host=localhost database=mysql' AS mysqldb (TYPE mysql);
SELECT * FROM mysql_query('mysqldb', 'SELECT * FROM cars LIMIT 3');

The mysql_execute Function

The mysql_execute function allows running arbitrary queries within MySQL, including statements that update the schema and content of the database.

ATTACH 'host=localhost database=mysql' AS mysqldb (TYPE mysql);
CALL mysql_execute('mysqldb', 'CREATE TABLE my_table (i INTEGER)');

Settings

Name Description Default
mysql_bit1_as_boolean Whether or not to convert BIT(1) columns to BOOLEAN true
mysql_debug_show_queries DEBUG SETTING: print all queries sent to MySQL to stdout false
mysql_enable_filter_pushdown Whether or not to use filter pushdown (without predicate analyzer) true
mysql_enable_transactions Whether to run START TRANSACTION / COMMIT / ROLLBACK on MySQL connections true
mysql_incomplete_dates_as_nulls Whether to return DATEs with a zero month or day as NULLs false
mysql_pool_acquire_mode How to acquire connections from the pool: force (always connect, ignoring the pool limit), wait (block until one is available), or try (fail immediately if none is available) force
mysql_pool_connection_idle_timeout_millis Maximum time in milliseconds a connection can sit idle in the cache before being closed 60000
mysql_pool_connection_max_lifetime_millis Maximum age in milliseconds of a pooled connection since it was first opened; when exceeded, the connection is closed instead of being returned to the cache (0 disables it) 0
mysql_pool_enable_reaper_thread Whether to run a dedicated thread that periodically scans the pool and removes expired connections true
mysql_pool_enable_thread_local_cache Enable thread-local connection caching for faster same-thread connection reuse false
mysql_pool_size Maximum number of connections per MySQL catalog automatic (based on the CPU count)
mysql_pool_wait_timeout_millis Timeout in milliseconds when waiting for a connection from the pool 30000
mysql_session_time_zone Session time zone to set for newly opened connections to the MySQL server ''
mysql_time_as_time Whether or not to convert MySQL's TIME columns to DuckDB's TIME false
mysql_tinyint1_as_boolean Whether or not to convert TINYINT(1) columns to BOOLEAN true

Schema Cache

To avoid having to continuously fetch schema data from MySQL, DuckDB keeps schema information – such as the names of tables, their columns, etc. – cached. If changes are made to the schema through a different connection to the MySQL instance, such as new columns being added to a table, the cached schema information might be outdated. In this case, the function mysql_clear_cache can be executed to clear the internal caches.

CALL mysql_clear_cache();
© 2026 DuckDB Foundation, Amsterdam NL
DuckDB Home Code of Conduct Trademark Use Blog