# Mariadb Connector Odbc Usage

> Explains MariaDB Connector/ODBC-specific behavior for application code, including DSN-less connection strings, positional parameter markers, prepared statement execution modes, autocommit defaults, TLS auto-enablement, and error handling. Use when writing or reviewing ODBC-based code (C/C++ or pyodbc) that talks to.

- Skill: `mariadb-corporation/mariadb-connector-odbc-usage` (Agent Skill)
- Install (CLI): `npx skillmds add mariadb-corporation/mariadb-connector-odbc-usage`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mariadb-corporation/mariadb-connector-odbc-usage/raw
- Safety review: PASS (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs, REST & GraphQL APIs
- Tags: Connector, Database, Mariadb, Odbc, Pyodbc, Sql
- Author: mariadb-corporation (https://skillmd.com/u/mariadb-corporation)
- Updated: 2026-08-22
- Page: https://skillmd.com/skills/mariadb-corporation/mariadb-connector-odbc-usage

---


# MariaDB Connector/ODBC

*Last updated: 2026-08-10*

MariaDB Connector/ODBC is an ODBC 3.8-compliant driver for MariaDB and MySQL, built on top of **MariaDB Connector/C** (`libmariadb`) for the actual client-server protocol work. Applications never link against it directly — they go through the standard **ODBC API** (`SQLConnect`/`SQLDriverConnect`, `SQLExecDirect`, `SQLBindParameter`, ...) mediated by a **Driver Manager** (unixODBC on Linux, iODBC on macOS, the built-in ODBC Driver Manager on Windows), or through a language wrapper on top of that API — most commonly **pyodbc** from Python. This skill covers the connector-specific behavior and the traps that bite generated application code. For installing the driver, registering it with the driver manager, and defining a DSN, see **`mariadb-connector-odbc-install`**.

> **Default context:** Assume the **3.2** stable release series (currently 3.2.10 GA) unless the user states otherwise. Connector/ODBC's version is independent of the MariaDB server version it connects to — behavior below applies across the 3.x line unless annotated with a specific version.

## What LLMs Often Miss

| If the agent writes / assumes… | …prefer the MariaDB form |
|---|---|
| Guesses the driver name, or copies one from another database's ODBC setup | On Windows the registered name is **`{MariaDB ODBC 3.2 Driver}`** (version number changes per series). On Linux/macOS with unixODBC/iODBC, `Driver=` is either a path to `libmaodbc.so`/`libmaodbc.dylib` or the driver name you registered in `odbcinst.ini` — there is no fixed string, it must match what `odbcinst -i -d` installed |
| Named or `%s`/`:name` parameter placeholders | ODBC only has **positional `?` markers**, bound in order with `SQLBindParameter` (or passed positionally through a wrapper, e.g. `cursor.execute(sql, params)` in pyodbc). No named-parameter support |
| Builds SQL with string formatting/concatenation | Never. Bind values through `SQLBindParameter`/the wrapper's parameter API — the driver sends them as protocol-level parameters, not as text spliced into the query |
| Assumes a separate ANSI driver package is needed for legacy apps | MariaDB Connector/ODBC ships **one Unicode (`SQLWCHAR`) driver binary** that serves both ANSI and Unicode applications; the Driver Manager sets `SQL_ATTR_ANSI_APP` and the driver converts transparently — no separate ANSI-only driver package to install |
| Expects `SQLExecDirect` to always use server-side prepared statements | `SQLExecDirect` defaults to **client-side prepare** (`SQL_ATTR_EXECDIRECT_ON_SERVER` = `SQL_FALSE` by default); set that attribute (or the `EDSERVER` connection-string option, *since 3.2.5*) to force server-side prepare/binary protocol for one-shot execution |
| Expects `SQLPrepare`+`SQLExecute` to use the text protocol | The opposite default: `SQLPrepare` uses **server-side prepared statements** (binary protocol) unless `SQL_ATTR_PREPARE_ON_CLIENT` is `SQL_TRUE` (or the `PREPONCLIENT` connection-string option, *since 3.2.0*) forces client-side prepare |
| Assumes autocommit is off until told otherwise | **Autocommit is ON by default** — the connector sends `SET autocommit=1` at connect time, matching the ODBC standard default (`SQL_AUTOCOMMIT_ON`). Turn it off with `SQLSetConnectAttr(dbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, 0)` before starting manual transaction control, then call `SQLEndTran(SQL_HANDLE_DBC, dbc, SQL_COMMIT)` / `SQL_ROLLBACK` |
| Ignores the legacy `OPTION`/`OPTIONS` bitmask connection parameter | It is still live and stacks with named keywords: bit 2 = return matched (not changed) rows, bit 2048 = compression, bit 4194304 = `AUTO_RECONNECT`, bit 67108864 = allow multiple statements per query (needed to run a batch of `;`-separated statements). Prefer the equivalent named keyword (`AUTO_RECONNECT=1`, etc.) when one exists — it is more readable than computing bit sums |
| Assumes TLS needs an explicit "enable SSL" switch before certs matter | Setting **any** of `SSLCA`/`SSLCERT`/`SSLKEY`/`SSLCIPHER`/`SSLCAPATH` auto-enables TLS enforcement. Use **`FORCETLS=1`** to require TLS without supplying certificate material, and **`SSLVERIFY=1`** separately to verify the server certificate against `SSLCA` |
| Treats `SQLGetDiagRec`'s native-error field as an ODBC-defined code | It is the raw **MariaDB server error number** (`mysql_errno()`), e.g. `1146` for "table doesn't exist" — cross-reference it against server error codes, not an ODBC error table. `SQLSTATE` is the server's mapped 5-character state |
| Runs several statements at once on one connection without buffering results first | The MariaDB protocol allows only one command in flight per connection; fetch or discard a statement's result set before issuing another on the **same connection** (no MARS-style interleaving). Use separate statement handles sequentially, a connection pool, or `OPTION` bit 67108864 only for literal multi-statement *batches* (`stmt1; stmt2;`) in a single `SQLExecDirect` call, which has its own limitations for cross-statement dependencies |
| Connects with `mariadb.connect(...)` (the Python `mariadb` module) when the app is ODBC-based | From Python via ODBC, use **pyodbc**: `pyodbc.connect("DRIVER={MariaDB ODBC 3.2 Driver};SERVER=...;...")`. pyodbc is a generic ODBC wrapper — it is not MariaDB-specific, but it inherits every behavior above (autocommit-on default, `?` markers, native error = server error number) |
| Loops `SQLExecute` once per row for a bulk insert | Bind arrays instead: set **`SQL_ATTR_PARAMSET_SIZE`** to the row count, point each `SQLBindParameter` at an array, and add **`SQL_ATTR_PARAM_STATUS_PTR`** to see which rows succeeded. One execution covers the whole set |
| Stops after the first result set from a `CALL` | Loop **`SQLMoreResults(stmt)`** until it returns `SQL_NO_DATA`. A stored procedure returns one result set per `SELECT` plus a final status result |
| Reads a long `TEXT` or `BLOB` column with a fixed-size bound buffer | Either bind a buffer large enough, or leave the column unbound and pull it in chunks with **`SQLGetData`**, which returns `SQL_SUCCESS_WITH_INFO` and SQLSTATE `01004` while data remains |
| Trusts `SQLRowCount` after a `SELECT` | It is defined for `INSERT`, `UPDATE`, and `DELETE`. After a `SELECT` it may report `-1`; count rows by fetching them |
| Assumes pyodbc commits automatically after `execute()` | pyodbc's `Connection.autocommit` defaults to **`False`** at the *pyodbc* layer (distinct from the ODBC/server-level default above) — call `conn.commit()` explicitly, or construct with `pyodbc.connect(..., autocommit=True)` |

## Connection essentials

```c
/* DSN-less connection string, C/C++ via SQLDriverConnect */
SQLWCHAR *ConnStr = L"DRIVER={MariaDB ODBC 3.2 Driver};"
                     L"SERVER=127.0.0.1;PORT=3306;"
                     L"DATABASE=appdb;UID=app;PASSWORD=secret;"
                     L"SSLCA=/etc/mysql/certs/ca.pem;SSLVERIFY=1";
```

The same keywords work in a DSN — see **`mariadb-connector-odbc-install`** for the `odbc.ini` form and for registering the driver in the first place.

```python
import pyodbc

conn = pyodbc.connect(
    "DRIVER={MariaDB ODBC 3.2 Driver};"
    "SERVER=127.0.0.1;PORT=3306;DATABASE=appdb;UID=app;PWD=secret",
    autocommit=False,   # pyodbc's own default; shown for clarity
)
cur = conn.cursor()
cur.execute(
    "INSERT INTO t (name, qty) VALUES (?, ?)",  # positional ? markers only
    ("widget", 5),
)
conn.commit()   # required: neither pyodbc nor a manual OFF toggle auto-commits

cur.execute("SELECT id, name FROM t WHERE qty > ?", (0,))
for row in cur.fetchall():
    print(row.id, row.name)

cur.close()
conn.close()
```

## Bulk insert with array-bound parameters

```c
#define ROWS 3
SQLINTEGER  ids[ROWS]        = { 1, 2, 3 };
SQLCHAR     names[ROWS][64]  = { "widget", "gadget", "doohickey" };
SQLLEN      name_len[ROWS]   = { SQL_NTS, SQL_NTS, SQL_NTS };
SQLUSMALLINT status[ROWS];

SQLSetStmtAttr(stmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)ROWS, 0);
SQLSetStmtAttr(stmt, SQL_ATTR_PARAM_STATUS_PTR, status, 0);

SQLPrepare(stmt, (SQLCHAR *)"INSERT INTO t (id, name) VALUES (?, ?)", SQL_NTS);
SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
                 0, 0, ids, 0, NULL);
SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR,
                 64, 0, names, sizeof(names[0]), name_len);

SQLExecute(stmt);        /* all three rows in one execution */
```

## See Also

- **`mariadb-connector-odbc-install`** — installing the driver, registering it with the driver manager, DSNs, and the TLS keywords
- **`mariadb-connector-c-usage`** — the underlying `libmariadb` client library that does the actual protocol work
- **`mariadb-transactions`** — server-side semantics behind `SQLEndTran`/`conn.commit()` and isolation levels (`SQL_ATTR_TXN_ISOLATION` maps directly to `SET SESSION TRANSACTION ISOLATION LEVEL`)
- **`mariadb-prepare`** — server-side prepared statements, what `SQLPrepare`'s default (and `EDSERVER`) use under the hood
- Canonical reference on `mariadb.com/docs`, consult for edge cases not covered here: <https://mariadb.com/docs/connectors/mariadb-connector-odbc>

<sub>_This page is: Copyright © 2026 MariaDB. All rights reserved._</sub>

