---
title: "MariaDB Python Connector 2.0.0rc2"
publish_date: 2026-03-23
author: "Diego Dupin"
channel:
  - name: "Product"
    url: "/resources/blog/channel/product.md"
tags:
  - name: "C++"
    url: "/resources/blog/tag/c.md"
  - name: "Connector"
    url: "/resources/blog/tag/connector.md"
  - name: "MariaDB Connector/Python"
    url: "/resources/blog/tag/mariadb-connector-python.md"
  - name: "performance"
    url: "/resources/blog/tag/performance.md"
  - name: "Python"
    url: "/resources/blog/tag/python.md"
---

# MariaDB Python Connector 2.0.0rc2

 Table of contents

1. [Get Started](#get-started)
2. [Why a Major Version?](#why-a-major-version)
3. [What's New](#whats-new)
4. [Performance: Measured, Not Claimed](#performance-measured-not-claimed)
5. [Upgrading from 1.1](#upgrading-from-1-1)
6. [Summary](#summary)
 
 ## Key takeaways

- This major update to the MariaDB Python Connector is a total ground-up rewrite that makes it much easier to install and significantly faster to run. Here are the key takeaways from the version 2.0 release:
- **Simplified installation** eliminates the old requirement for pre-installed C libraries, offering a pure Python version that works anywhere and a “plug-and-play” binary option for maximum speed.
- **Native async/await support** and full type hinting allow the connector to fit perfectly into modern Python frameworks like FastAPI and Starlette without extra workarounds.
- **Massive performance gains** make this the fastest MariaDB driver available, with speeds up to 64 times faster than competitors when handling complex data and large-scale queries.
- **Modern connection features** like standard URI strings (`mariadb://...`) and intelligent statement caching simplify your code while automatically reducing database overhead.
 
 Today’s release candidate for MariaDB Connector/Python 2.0 marks a transition from the architecture used in version 1.1. Although the previous version was fast, it came with technical friction – specifically the requirement for a pre-installed C/C++ connector, a lack of async and type hint support, and CPython-only compatibility.  
In version 2.0, we’ve rebuilt the connector to remove those dependencies and add the missing features, all while making the underlying C extension faster. We invite the community to test this preview before the final 2.0.0 release.





## Get Started

```
pip install mariadb[binary,pool]
```





## Why a Major Version?

Three forces drove the rewrite:

1. **The Python ecosystem moved on.** async/await is mainstream, type hints are expected, and URI-based configuration is standard. The 1.1 API predates all of this.
2. **Installation was a barrier.** Requiring users to install the MariaDB C/C Connector before pip install mariadb was a constant source of friction, especially in containers and CI pipelines.
3. **The performance had headroom.** The 1.1 C extension was already fast, but internal data structures — particularly metadata handling and parameter binding — left significant performance on the table.

## What’s New

### Flexible Distribution (CONPY-324)

Version 1.1 had a single installation path: compiling the C extension against a locally installed MariaDB C Connector. If the C library wasn’t present, pip install failed.

Version 2.0 introduces three mutually exclusive backends, plus an optional add-on:

| **Extra** | **What you get** | **Requirements** |
|---|---|---|
| *(none)* | Pure Python implementation | Nothing   works everywhere, any interpreter |
| \[c\] | C extension | Local MariaDB C Connector + compiler |
| \[binary\] | Pre-compiled C extension wheel | Nothing   compiler and C Connector bundled |
| \[pool\] | Connection pooling add-on | Can be combined with any backend above |

Pick your backend:

```
pip install --pre mariadb # Pure Python -- CPython, PyPy, any platform pip install --pre mariadb[c] # C extension -- maximum performance pip install --pre mariadb[binary] # Pre-compiled wheel -- performance, no setup
```

Add connection pooling to any of the above:

```
pip install --pre mariadb[pool] pip install --pre mariadb[c,pool] pip install --pre mariadb[binary,pool]
```

All backends share the same API

**Note:** `--pre` is required for now, as 2.0 is currently a release candidate.

### Dedicated Connection Pool Package (CONPY-324) – Breaking Change

The pool package was fully rewritten for 2.0: it now works with both the pure Python and C extension implementations, and adds a new asynchronous API that did not exist in 1.1. Extracting it into a separate package reflects its expanded scope and keeps it optional for applications that do not need pooling.

**Before (1.1):**

```
pip install mariadb # pooling included automatically
```

**After (2.0):**

```
pip install mariadb[pool] # explicit opt-in for pooling
```

The API itself is unchanged, only the installation step differs. The pool package is deliberately optional: applications that never use connection pooling no longer carry the dependency.

Synchronous pool:

```
import mariadbpool = mariadb.create_pool(    host="localhost",    port=3306,    user="user",    password="password",    database="mydb",    min_size=5,    max_size=10,    ping_threshold=0.25)with pool.acquire() as conn:    with conn.cursor() as cursor:        cursor.execute("SELECT COUNT(*) FROM users")        count = cursor.fetchone()[0]        print(f"Total users: {count}")
```

Asynchronous pool:

```
import asyncioimport mariadbasync def main():    pool = await mariadb.create_async_pool(        host="localhost",        user="user",        password="password",        database="mydb",        min_size=5,        max_size=10    )    async with await pool.acquire() as conn:        async with conn.cursor() as cursor:            await cursor.execute("SELECT * FROM users WHERE id = ?", (1,))            row = await cursor.fetchone()            print(f"User: {row}")    await pool.close()asyncio.run(main())
```

### Native Async Support (CONPY-2, CONPY-327)

Version 1.1 had no async support. Applications using asyncio, FastAPI, or Starlette had to wrap synchronous calls in thread pools, adding latency and complexity.

Version 2.0 provides first-class async/await support for both the pure Python and C extension implementations:

```
import asyncioimport mariadbasync def main():    # Single connection    async with await mariadb.asyncConnect("mariadb://user:pass@host/db") as conn:        async with conn.cursor() as cursor:            await cursor.execute("SELECT * FROM users WHERE id = ?", (1,))            row = await cursor.fetchone()            print(row)    # With connection pool (requires mariadb[pool])    pool = await mariadb.create_async_pool(        "mariadb://user:pass@host/db", min_size=1, max_size=20    )    async with await pool.acquire() as conn:        async with conn.cursor() as cursor:            await cursor.execute("SELECT 1")            result = await cursor.fetchone()    await pool.close()asyncio.run(main())
```

The pure Python async path uses native asyncio socket I/O — no threads, no GIL contention. The C extension async path uses libmariadb’s non-blocking API, yielding to the event loop during network waits while keeping C-speed parsing.

### Connection URI Support (CONPY-326)

Version 1.1 required keyword arguments for every connection parameter:

```
# 1.1 -- verboseconn = mariadb.connect(host="localhost", user="root", password="secret", database="mydb")
```

Version 2.0 supports standard URI syntax:

```
# 2.0 -- conciseconn = mariadb.connect("mariadb://root:secret@localhost:3306/mydb")
```

URIs make configuration simpler: a single DATABASE\_URL environment variable replaces multiple connection parameters. Keyword arguments still work for backward compatibility and can override URI values when both are provided.

### Comprehensive Type Hints (CONPY-325)

Every public API surface is now fully annotated. mypy –strict and pyright pass cleanly. IDEs provide accurate autocomplete, and type errors are caught at development time rather than in production.

```
conn: mariadb.Connection = mariadb.connect(    host="localhost",    port=3306,    user="root",    password="secret",    database="mydb") cursor: mariadb.Cursor = conn.cursor()cursor.execute("SELECT * FROM users WHERE id = ?", (1,))row: tuple | None = cursor.fetchone()rows: list[tuple] = cursor.fetchall()cursor.close()conn.close()
```

### Unified Binary Protocol Control (CONPY-338)

In 1.1, the C extension silently auto-promoted certain parameter types (bytes, datetime, etc.) to the binary protocol, even when the user expected text protocol.

Version 2.0 makes protocol selection explicit and consistent across both implementations:

- **Text protocol by default**: predictable, debuggable
- `binary=True` at connection or cursor level for prepared statements
- **Dict parameters** always use text protocol (named parameter substitution)

```
# Text protocol (default)cursor.execute("SELECT ?", (b'\xde\xad',))# sends: SELECT _binary'\xde\xad' # Binary protocol (explicit)cursor = conn.cursor(binary=True)cursor.execute("SELECT ?", (b'\xde\xad',))# uses COM_STMT_PREPARE + COM_STMT_EXECUTE
```

No more hidden protocol switches. The behavior is identical whether you use the pure Python or C implementation.

## Performance: Measured, Not Claimed

Every architectural change was benchmarked. All results below were measured on the same machine with pytest-benchmark (1000+ rounds). Results are in operations per second (higher is better).

### C Extension: 1.1 vs 2.0 (Text Protocol)

Both 1.1 and 2.0 use the same text protocol path (COM\_QUERY), so these benchmarks are directly comparable:

| **Benchmark** | **1.1 (ops/s)** | **2.0 C (ops/s)** | **Change** |
|---|---|---|---|
| DO 1 | 65,789 | 70,922 | +8% |
| SELECT 1 | 34,364 | 39,683 | +15% |
| SELECT 100 columns | 14,104 | 14,124 | ~0% |
| DO 1000 params | 2,117 | 3,239 | +53% |

Text parameter binding is 53% faster on 1000 parameters and simple queries are 8–15% faster. Wide result sets remain on par.

**What Drove the Text Protocol Improvement**

- **Columnar metadata storage (CONPY-335):** replaced the traditional array-of-objects layout with parallel arrays, eliminating per-column Python object dereferencing during row parsing and improving cache locality.
- **C-level metadata reading (CONPY-333):** moved column definition parsing from Python into C. In 1.1, the C extension was actually slower than the pure Python connector on wide binary result sets due to metadata overhead. This fix eliminated that anomaly.

### New in 2.0: Prepared Statement Caching

Version 1.1 had no statement caching — every binary protocol execution required a full COM\_STMT\_PREPARE + COM\_STMT\_EXECUTE + COM\_STMT\_CLOSE round-trip. To show the impact fairly, here are three columns: text protocol, binary protocol without caching (equivalent to 1.1’s behavior), and binary protocol with caching (2.0 default):

| **Benchmark** | **Text (ops/s)** | **Binary no cache** | **Binary cached** | **Cache speedup** |
|---|---|---|---|---|
| DO 1000 params | 3,152 | 2,175 | 4,472 | 2.1× |
| SELECT 1000 rows | 4,571 | 4,500 | 4,943 | 1.1× |
| SELECT 100 columns | 11,862 | 10,000 | 16,920 | 1.7× |

Without caching, the binary protocol is actually slower than text on parameter-heavy queries (2,175 vs 3,152 ops/s) — the round-trip overhead of PREPARE + EXECUTE + CLOSE exceeds any benefit from server-side parsing. This was the situation in 1.1.

Version 2.0’s prepared statement cache eliminates this overhead. MYSQL\_STMT\* handles are reused across executions of the same SQL using a checkout/return pattern at the connection level. Combined with MariaDB’s MARIADB\_CLIENT\_CACHE\_METADATA capability, the binary protocol now outperforms text across the board.

**Configuration**

Caching is controlled by two connection options:

- **cache\_prep\_stmts** (default: True) — enables or disables the prepared statement cache.
- **prep\_stmt\_cache\_size** (default: 100) — maximum number of cached statements per connection. When the cache is full, the least recently used statement is evicted.

```
# Default: caching enabled with up to 100 statementsconn = mariadb.connect("mariadb://localhost/mydb") # Larger cache for applications with many distinct queriesconn = mariadb.connect("mariadb://localhost/mydb?prep_stmt_cache_size=500") # Disable caching entirely (1.1-like behavior)conn = mariadb.connect("mariadb://localhost/mydb?cache_prep_stmts=false")
```

**When to use binary protocol**

The cache benefits applications that repeatedly execute the same SQL statements — the first execution pays the PREPARE cost, but all subsequent executions skip it entirely.

- **Connection-level (binary=True):** best when the application mostly runs the same set of parameterized queries. All cursors default to binary protocol, and the cache handles the rest.

```
conn = mariadb.connect("mariadb://localhost/mydb?binary=true")cur = conn.cursor()cur.execute("SELECT * FROM users WHERE id = ?", (user_id,))  # binary
```

- **Per-cursor (cursor(binary=True)):** best for mixed workloads where only specific hot queries benefit from binary protocol.

```
conn = mariadb.connect("mariadb://localhost/mydb") # Hot path -- binary with cachingcur = conn.cursor(binary=True)cur.execute("SELECT * FROM users WHERE id = ?", (user_id,)) # Ad-hoc query -- text protocol, no PREPARE overheadcur2 = conn.cursor()cur2.execute("SHOW PROCESSLIST")
```

### Synchronous: vs the Competition

How does 2.0 compare to the most popular Python MySQL/MariaDB drivers? We benchmarked against PyMySQL 1.1.2 (pure Python) and mysql-connector-python 9.6.0 (Oracle’s C extension and pure Python variants):

| **Benchmark** | **mariadb\_c 2.0 (ops/s)** | **mariadb 2.0   (ops/s)** | **PyMySQL 1.1.2 (ops/s)** | **mysql-conn (c ) (ops/s)** | **mysql-conn (pure) (ops/s)** |
|---|---|---|---|---|---|
| DO 1 | 68,493 | 62,112 | 62,500 | 23,364 | 15,649 |
| SELECT 1 | 53,763 | 33,113 | 30,864 | 16,367 | 10,799 |
| SELECT 1000 rows (text) | 4,831 | 802 | 416 | 1,188 | 272 |
| SELECT 1000 rows (binary) | 5,089 | 680 | — | 1,041 | 22 |
| SELECT 100 cols (text) | 14,065 | 4,975 | 1,952 | 4,310 | 1,791 |
| SELECT 100 cols (binary) | 21,231 | 10,846 | — | 2,866 | 24 |
| DO 1000 params (text) | 3,272 | 3,142 | 1,681 | 2,300 | 775 |
| DO 1000 params (binary) | 4,533 | 2,133 | — | 71 | 17 |
| Batch INSERT | 5,876 | 4,486 | 1,784 | 1,791 | 1,455 |

mariadb\_c wins all 9 benchmarks. The gaps are huge on data-heavy workloads:

- **SELECT 1000 rows:** mariadb\_c delivers 4,831 ops/s vs mysql-connector (C)’s 1,188 and PyMySQL’s 416 — 4× and 12× faster
- **Binary protocol params:** mariadb\_c at 4,533 ops/s vs mysql-connector (C)’s 71 — 64× faster. Oracle’s prepared statement implementation has severe overhead at high parameter counts
- **100-column binary:** mariadb\_c at 21,231 ops/s vs mysql-connector pure Python’s 24 — a 895× difference

Even the pure Python mariadb 2.0 holds its own: it matches or beats mysql-connector (C) on parameter binding and is 2–3× faster than PyMySQL on most benchmarks.

### Asynchronous: vs aiomysql and asyncmy

Version 2.0 introduces native async support — so how does it compare to the existing async MySQL drivers, aiomysql (async wrapper around PyMySQL) and asyncmy (async client with C speedups)?

| **Benchmark** | **mariadb\_c async (ops/s)** | **mariadb async (ops/s)** | **Aiomysql (ops/s)** | **Asyncmy (ops/s)** |
|---|---|---|---|---|
| DO 1 | 21,786 | 24,631 | 24,331 | 21,552 |
| SELECT 1 | 21,142 | 17,391 | 15,974 | 14,265 |
| SELECT 1000 rows | 1,799 | 765 | 379 | 339 |
| SELECT 100 cols | 10,515 | 4,365 | 1,764 | 1,938 |
| DO 1000 params | 2,899 | 2,808 | 2,033 | 2,142 |
| Batch INSERT | 5,444 | 3,738 | 1,650 | 1,617 |

mariadb\_c async wins 5 out of 6 benchmarks, with mariadb async taking DO 1. Key results:

- **SELECT 1000 rows:** mariadb\_c async at 1,799 ops/s vs aiomysql’s 379 and asyncmy’s 339 — 4.7× and 5.3× faster
- **SELECT 100 cols:** mariadb\_c async at 10,515 ops/s vs aiomysql’s 1,764 — 6× faster
- **Batch INSERT:** both mariadb variants are 3× faster than aiomysql/asyncmy
- **Pure Python mariadb async** is 2× faster than aiomysql on SELECT 1000 rows

For FastAPI, Starlette, or any asyncio-based application, mariadb 2.0 delivers async performance that no other Python MySQL/MariaDB driver can match, with or without the C extension.

## Upgrading from 1.1

The mariadb.connect() API remains largely backward compatible — most keyword-argument connections work without changes. However, several options have been removed, changed, or added.

### Removed Options

- **reconnect / auto\_reconnect removed:** In 1.1, setting reconnect=True enabled automatic reconnection on lost connections. This was silently hiding failures and causing unpredictable behavior: lost session state, uncommitted transactions, broken transaction isolation. In 2.0, there is no automatic reconnection. The reconnect() method still exists for explicit manual reconnection, but the automatic behavior is gone. For production use, a connection pool is the recommended approach.
- **plugin\_dir removed in pure Python:** The C extension still supports it, but the pure Python driver does not load native authentication plugins from disk.
- **cursor\_type removed:** In 1.1, cursor\_type=CURSOR.READ\_ONLY opened a server-side cursor. In 2.0, use buffered=False instead: this reads rows from the network on demand rather than fetching the entire result set upfront.
- **prepared removed:** In 2.0, use binary=True instead: it provides the same prepared statement behavior with a clearer name.

### Changed Behavior

- **binary is now a connection-level option:** In 1.1, binary=True could only be set per cursor. In 2.0, you can set it at connection level to default all cursors to the binary protocol:

```
# 2.0 -- all cursors use binary protocol by defaultconn = mariadb.connect("mariadb://root:secret@localhost/mydb?binary=true")
```

- **No more automatic protocol promotion:** In 1.1, passing bytes, datetime, or similar types as parameters silently switched the cursor to binary protocol. In 2.0, the text protocol is always used unless binary=True is explicitly set.
- **pip install mariadb now installs pure Python:** For the C extension, use pip install mariadb\[c\]. For pre-compiled wheels, use pip install mariadb\[binary\].
- **Connection pooling is now a separate package:** Use pip install mariadb\[pool\] to get pooling support. In 1.1, pooling was built into the mariadb package.

### New Options

- **URI connection strings:** mariadb.connect(“mariadb://user:pass@host/db”) in addition to keyword arguments.
- **binary at connection level:** defaults all cursors to prepared statements.
- **cache\_prep\_stmts / prep\_stmt\_cache\_size:** controls the prepared statement cache (C extension).
- **pipeline:** enables pipelining for batch operations.
- **Async API:** AsyncConnection, AsyncCursor, asyncConnect(), AsyncConnectionPool are entirely new.

## Summary

MariaDB Connector/Python 2.0 is faster, more flexible, and more Pythonic than 1.1:

- **Pure Python option:** install anywhere, no compiler required
- **Native async/await:** for both pure Python and C implementations
- **URI connections:** standard mariadb:// scheme
- **Full type hints:** mypy –strict compatible
- **2× faster parameter binding** and 2× faster wide result sets in the C extension
- **Explicit protocol control:** no hidden behavior differences between implementations

[Download Python Connector](/downloads/connectors/connectors-data-access/python-connector)