Connector

MariaDB access governance — every statement reviewed before it runs

Last updated

AccessFlow governs MariaDB as a full query proxy: your team connects to AccessFlow, not to the database, and every statement is parsed, checked against a schema allow-list, risk-scored, routed to an approver, and only then executed — under column masking, row-level security and a row cap. The pipeline itself — parse, risk-score, route, approve, execute, audit — is the same for every relational engine and is described in full on the database access governance page. What follows is what is specific to MariaDB.

Family
Relational
Query language
SQL
Connects via
Database driver
Default port
3306
Default SSL mode
REQUIRE
Install
One click — checksum-verified download

The driver and the connection

MariaDB has its own driver, org.mariadb.jdbc:mariadb-java-client — it is a first-class connector rather than an alias pointed at the MySQL one, so the driver, the connection handling and the catalog entry are MariaDB's own.

The datasource takes host, port (3306 by default), database, username and password — encrypted at rest with AES-256-GCM, decrypted once when AccessFlow opens its connections, or held as a secret-manager reference instead. SSL defaults to REQUIRE.

AccessFlow builds the connection string from the datasource fields using the template the connector manifest declares, so what actually reaches the driver is predictable and reviewable:

connector manifest
jdbc:mariadb://{host}:{port}/{database_name}
org.mariadb.jdbc.Driver

The driver is fetched the first time a datasource of this type is created or tested, and every driver AccessFlow loads is kept separate from every other one. Two datasources on different driver versions — even two MariaDB datasources — never share a copy and cannot interfere with each other.

What AccessFlow parses

Statements are parsed before anything else happens, and the allow-list is checked against the tables the statement really touches rather than against its text. Backtick identifiers are understood as identifiers, and the classification onto read, write and DDL capabilities is what drives which approval chain the request enters.

What it refuses

Multi-statement input is refused, except for a BEGIN; … COMMIT; envelope around a homogeneous batch of writes, which executes in one transaction and rolls back as a unit if any statement in it fails. Inside that envelope a read, a DDL statement, a savepoint or a nested transaction each get their own rejection message rather than a generic one.

Row-level security and masking

Row-level security becomes an extra bound predicate in the WHERE clause. Shapes it cannot provably narrow fail closed with HTTP 422, which is the deliberate choice: a filter that silently does not apply is worse than a query that does not run. Masking is applied to the fetched result.

What the rewrite actually does

A policy restricting shipments to the caller's warehouse narrows the read before MariaDB sees it:

SQL
-- submitted
SELECT id, carrier FROM shipments WHERE dispatched_at IS NULL;

-- executed (policy: shipments.warehouse = the caller's warehouse)
SELECT id, carrier FROM shipments WHERE (dispatched_at IS NULL) AND shipments.warehouse = ?;

The same rewrite governs updates and deletes, not only reads — anything with a WHERE clause to narrow. An INSERT into a policied table is rejected instead, because there is no clause on which to apply a filter.

Introspection, dry-run and cost estimates

A dry-run plans the governed statement with EXPLAIN FORMAT=JSON and maps the output into a plan tree without executing. The automatic submission estimate uses the same path, and a write additionally gets an exact affected-row count from a governed SELECT COUNT(*) rewrite.