Connect to MySQL
besu_mysql::Sqlx wraps a sqlx MySqlPool. The only interesting decision is whether to let
the driver pin the session to UTF-8 and UTC, which is what new does and connect does not.
A connection string
Sqlx::connect parses the URL and changes nothing else, so a charset or timezone parameter in
the URL is the one that applies.
The recommended constructor
Sqlx::new parses the same URL and then sets charset=utf8mb4 and timezone=+00:00. The
first matters because utf8mb4 is the only character set that stores every Unicode character,
including emoji; the second because Besu encodes chrono timestamps as UTC, and a session in
another timezone would silently shift a TIMESTAMP column on the way in or out.
Your own pool
from_pool is the way to change the connection limit or the timeouts. This needs sqlx as a
direct dependency:
Sqlx::new does exactly this, minus the connection limit and the timeout.
Two options worth knowing
test_before_acquire defaults to true, which sends a COM_PING round trip on every
acquire. That is by far the largest per-call cost in this driver: switching it off measured
18% off a 1,000-row select and 15% off a single insert. It is left on because a connection
dropped while idle then surfaces as a transparent reconnect instead of a failed query — but if
you would rather have the 18%, from_pool is the way:
statement_cache_capacity is sqlx's default of 100 prepared statements per connection. Besu
builds a distinct SQL string per query shape, so an application with many shapes will evict;
raise it on the pool if that shows up in SHOW GLOBAL STATUS LIKE 'Prepared_stmt%'.
The pool underneath
pool() returns the MySqlPool for anything Besu does not expose, such as closing it at
shutdown. db.driver() is the Sqlx.
Transactions
begin borrows the database and keeps every statement on one connection; commit and
rollback consume the handle. db.transaction(..) commits on Ok and rolls back on Err,
and tx.savepoint("name", ..) nests.
No connection at all
besu::dry gives the full builder and an exact to_sql() with no server. Executing one
returns an error that points at to_sql.
Gotchas
- MySQL has no
RETURNING.Insert::returningcompiles and then fails when the SQL is built, with "mysqldoes not supportRETURNING". Read the row back by a unique column, or useMySqlQueryResult::last_insert_id()from the insert's output. - A statement that is not pinned to
utf8mb4can mangle text and reject an emoji. UseSqlx::new, or set the charset on a pool you build yourself. - A session timezone is not the same thing as a timezone-aware column. MySQL's
DATETIME(6)stores no offset; Besu sends and reads UTC and relies on the pinned session. - DDL causes an implicit commit on MySQL, so wrapping a schema change in
db.transaction(..)does not make it atomic. That is the server's rule, not the driver's. OFFSETwithoutLIMITis not grammatical; the builder supplies an unboundedLIMITwhen you set only an offset.FULL OUTER JOINhas no MySQL spelling, and the builder rejects it rather than emitting something that will not parse;NULLS FIRST/NULLS LASTare emulated by ordering onexpr IS NULLfirst.- MySQL identifiers are quoted with backticks and a backtick inside one is doubled, so a table or column with an unusual name round-trips through DDL.
pushis not transactional, and a failed statement leaves the earlier ones applied.
