Ask MySQL Questions in Plain English
Ask MySQL questions in plain English: the SELECT-only user, MySQL 8 TLS auth, the server defaults that quietly change generated SQL, and reading from a replica.
By the Datarelix Team · Published
You can ask MySQL questions in plain English, and the safety story is short: a MySQL user granted SELECT and nothing else cannot write, and every generated statement is validated again before it executes. What MySQL adds is operational — version-8 authentication wants TLS, the database is the entire scope, and a handful of server defaults quietly shape both the generated SQL and the answers it returns.
Who this is for, and what it assumes
Engineers and analysts who run MySQL — RDS, Cloud SQL, Azure Database for MySQL, PlanetScale, or self-managed 5.7+/8.x — and are weighing whether natural-language querying is safe to point at it. It assumes you can run CREATE USER and GRANT. The field-by-field connection form is in the MySQL connection guide.
One user, one grant, one database
MySQL has no schema layer separate from the database, so the database is the scope — and the whole privilege model for read-only analytics fits in three statements:
CREATE USER 'datarelix_reader'@'%' IDENTIFIED BY 'choose-a-strong-password';
GRANT SELECT ON your_db.* TO 'datarelix_reader'@'%';
FLUSH PRIVILEGES;
The '%' host wildcard accepts connections from any address; tighten it to a CIDR if your server enforces host-based access control, remembering that MySQL matches user plus host as a pair. The full privilege model is in the MySQL GRANT reference.
Prove the boundary before moving on, connected as the new user:
-- Should list exactly one privilege: SELECT ON `your_db`.*
SHOW GRANTS FOR CURRENT_USER();
-- Should fail with "command denied", which is the point:
CREATE TABLE should_not_work (id INT);
That grant is the boundary you own. The second one sits in front of your server regardless: a generated statement is parsed into a full syntax tree and rejected unless it is a read, so INSERT, UPDATE, DELETE, DDL, and MySQL-side escapes have no path through even if a privileged credential were ever supplied. The mechanism and its threat model are in read-only AI analytics.
MySQL 8 authentication needs TLS
MySQL 8 made caching_sha2_password the default authentication plugin, and it refuses to send a password over an insecure channel — the error names the plugin and says “requires secure connection”. Managed MySQL serves TLS out of the box, so in practice this only bites self-managed servers without certificates. Connect over TLS; falling back to mysql_native_password works but fixes the symptom rather than the transport. One Azure note: Flexible Server takes the plain username — the user@servername suffix belonged to the retired Single Server and now only causes login failures.
The SQL that comes back is MySQL’s dialect, not generic
Against a fictional retailer’s schema, “How did monthly revenue develop over the last six months?” generates something like:
SELECT
DATE_FORMAT(o.ordered_at, '%Y-%m') AS month,
SUM(oi.quantity * oi.unit_price) AS revenue
FROM orders AS o
JOIN order_items AS oi ON oi.order_id = o.order_id
WHERE o.status = 'completed'
AND o.ordered_at >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
GROUP BY DATE_FORMAT(o.ordered_at, '%Y-%m')
ORDER BY month;
The markers of real MySQL: DATE_SUB/CURDATE date arithmetic, DATE_FORMAT for the month grain, LIMIT rather than TOP or FETCH FIRST, backticks only where an identifier needs them, and a GROUP BY that satisfies strict grouping mode. How a question becomes this query at all is covered in text-to-SQL explained — and the decisions embedded here (completed orders only, gross of refunds, calendar months) are exactly what checking the query is for.
Server defaults that quietly change answers
| Server reality | How it surfaces | What to know |
|---|---|---|
ONLY_FULL_GROUP_BY — on by default since 5.7 | On servers where someone disabled it, a non-aggregated column in a grouped query returns an arbitrary row value — silently. | Generated SQL aggregates every selected column, which is correct on both settings. Legacy hand-written reports are where the silent version of this bug lives. |
TIMESTAMP vs DATETIME | TIMESTAMP converts through the session time zone; DATETIME is stored as-is with no zone. Mixed use skews time-window answers. | Know which your schema uses before trusting an hour-grain number. |
CONVERT_TZ with named zones | Returns NULL until the server’s time zone tables are loaded — a classic source of suddenly-empty results. | Load them with mysql_tzinfo_to_sql, per MySQL time zone support. |
| Mixed collations | Joining a utf8mb3 column to a utf8mb4 one can throw “Illegal mix of collations” or quietly stop using an index. | Surfaces most often on older databases upgraded in place. |
lower_case_table_names | Table names are case-sensitive on Linux by default, case-insensitive on Windows and macOS. | A name that worked against a dev laptop can miss the table in production. Discovery surfaces names exactly as the server stores them. |
No FULL OUTER JOIN | MySQL simply lacks it. | The generated equivalent is a UNION of left and right joins — more verbose, same result. |
| MyISAM tables | The legacy engine has no foreign-key support at all, so old tables carry no declared relationships. | Join paths get inferred from naming — see below. |
Point it at a replica, not the primary
Analytical questions never need a write path, which makes a read replica the natural target: the primary keeps serving the application while ad hoc scans land elsewhere, and the reader user’s grant replicates with everything else. RDS, Cloud SQL, and Azure all offer replicas as a checkbox. The one caveat is replication lag — an answer can trail the primary by seconds. For analytical questions that rarely matters; for “what is happening right now” questions, it is worth knowing which endpoint you connected.
Connecting MySQL to Datarelix
Datarelix connects to MySQL 5.7+ and 8.x with username/password or a mysql:// connection string; the password is stored encrypted and every other field as plain configuration. Both mysql_native_password and caching_sha2_password work. Introspection walks INFORMATION_SCHEMA for tables, columns, primary keys, and declared foreign keys — on Vitess-backed databases such as PlanetScale, where foreign keys are typically absent, discovery finds no declared relationships and join paths are inferred from column naming, so check the joins on early answers and add descriptions where names are cryptic — why semantic context matters.
Every query runs under a server-side execution-time cap of 30 seconds, results are capped at 5,000 rows, and a missing LIMIT is appended automatically. Setup and the troubleshooting table are in the MySQL connection guide.
The limits on MySQL
One database per connection — cross-database joins in a single question are out, even though MySQL’s SQL could address other_db.table. There is no write path and none is planned. The row cap makes bulk export the wrong tool. And answer quality follows schema quality: a well-named schema with declared keys answers well; a legacy schema of cryptic columns, MyISAM tables, and undocumented status codes yields fluent SQL against the wrong column until you curate descriptions.
Provenance
Privilege behavior follows the MySQL GRANT reference; authentication-plugin behavior follows caching_sha2_password documentation; time-zone behavior follows MySQL time zone support. Product statements describe Datarelix as shipped in August 2026 and are stated canonically on the security page. The SQL example uses a fictional demo schema and is illustrative, not captured output.
Create the reader and re-ask a number you already trust
Run the three-statement grant above — against a replica if you have one — then follow the quickstart and ask a question your existing reporting already answers. Compare the generated SQL’s definitions to the report’s before comparing the numbers. Your first 14 days run at Pro limits, no credit card required.