MySQL Data Sources
The MySQL sources let a chart build itself from read-only SQL queries — run against your WordPress site's own database (Local MySQL) or against another MySQL server entirely (Remote MySQL). They are part of the External Data Sources system, so everything on that page about syncing, the locked spreadsheet, and status indicators applies here too, and like all External Data they work on Chart.js charts only.
Overview
- Local MySQL — queries the WordPress database itself. Zero configuration: no credentials, no connection settings.
- Remote MySQL — connects to any MySQL server you provide credentials for, over SSL by default.
Both work the same way once connected: one query produces one sheet of data, and the query's column names become the labels.
Administrators only
A SQL source is direct database access, so only administrators (users with the manage_options capability) can create or edit MySQL settings. Everyone else sees the two MySQL options in the Source menu, but the fields are disabled, with a note explaining why:
Only administrators can edit MySQL data source settings, because they provide direct database access
Non-administrators can still click Sync Now — the sync runs against whatever settings an administrator has saved.
Writing queries
Each query is a single read-only SELECT statement, entered in a syntax-highlighted SQL editor. For example:
SELECT label, value FROM my_table ORDER BY value DESC
How many queries you get depends on the chart type:
- On single-sheet chart types there is exactly one field, labeled
Query. - On multi-sheet chart types you can add up to 10 queries with the
Add querybutton (each field is labeledQuery 1,Query 2, …, and the X icon removes one). Each query gets aNamefield —Sheet 1,Sheet 2, … by default — which becomes that sheet's tab name in the chart.
How results become chart data
The query's column names become the header row — the labels M Chart reads — and the result rows fill in below, exactly as if you'd imported the result as a CSV. Ragged results are squared off automatically.
Alias your columns
Column names show up as chart labels, so alias them into something presentable: SELECT country AS Country, SUM(sales) AS Sales ….
Local MySQL
Local MySQL queries the WordPress database using the site's own database credentials — no setup at all. Pick Local MySQL as the Source, write your queries, and sync.
Queries can read anything the site's database user can read, including other plugins' tables — which is exactly why configuring it is limited to administrators. There are no connection fields and no Test Connection button; if the site works, the connection works.
Under the hood it opens its own read-only connection rather than borrowing WordPress's, so every query runs inside a READ ONLY transaction (see Safe by design).
Remote MySQL
Pick Remote MySQL as the Source and two tabs appear: Queries (or Query on single-sheet chart types) and MySQL Server.
Connection settings
The MySQL Server tab holds the connection fields:
Host— the server's hostname, e.g.db.example.comPort— default3306Database— the database (schema) nameUsernamePassword— write-only; once saved the field shows Saved, leave blank to keepRequire SSL— on by defaultVerify server certificate— on by default; only available whileRequire SSLis checkedCA certificate (PEM, optional)— only shown while both SSL checkboxes are on; see CA certificate below
Use a read-only account
As the field note says: use a read-only database account with access to only the data you need. M Chart enforces read-only queries on its side too, but a least-privilege account is your real safety net — and it limits what's exposed if the credentials ever leak.
Hosting firewalls
Web hosts can be picky about outgoing MySQL connections — many block outbound traffic on 3306 and other database ports by default. If Test Connection can't reach a server you know is up, ask your host to open outgoing connections on the port the remote MySQL server expects.
CA certificate
If the server uses a certificate the system trust store doesn't know — a self-signed certificate or a private CA — paste the CA certificate (PEM format, starting with -----BEGIN CERTIFICATE-----) into the CA certificate field. As the field's note says, it's only needed when the server uses a self-signed or private-CA certificate; leave it empty to verify against the system trust store. The field only appears while both Require SSL and Verify server certificate are on, and it's ignored whenever certificate verification is off.
Like the password, the certificate is write-only: once saved, the paste box collapses to a check-mark summary showing the certificate's subject and expiry date, with a Remove button. Remove reveals the paste box again — the saved certificate is deleted when you save the chart, unless you paste a replacement, which always wins over a pending removal.
Pasting something invalid never silently clobbers a saved certificate: bad PEM is rejected with an admin notice ("The pasted Remote MySQL CA certificate was not valid PEM and was not saved. Any previously saved certificate is unchanged."), private keys are refused outright ("The pasted text contains a private key — paste only the CA certificate"), and bundles are capped at 64 KB and 10 certificates.
The certificate is stored the same way as the password — encrypted, in a hidden per-chart option, deleted with the chart — so everything in How the password is stored applies to it too, including the AUTH_SALT caveat.
Test Connection
Administrators get a Test Connection button next to the connection fields (it reads Testing… while it runs). It performs a real connection to the server and reports back via a status icon:
Connection successfulConnection failed— with the actual driver error (this is the one place errors aren't genericized) plus a targeted hint for the failure: an unreachable server suggests checking the host and port ("managed databases often use a non-standard port") and verifying the port isn't blocked by a firewall, access denied suggests checking the username, password, and database name, and a TLS failure points at the CA certificate and theVerify server certificatesettingConnection not tested yet
Editing any connection field resets the status to untested.
How the password is stored
The password field is write-only: type a password to set or replace it, leave it blank to keep the saved one. It is never stored in the chart's post meta and never sent back to the browser. On the server it's encrypted (keyed off your site's AUTH_SALT) in a hidden per-chart option, and it's deleted when the chart is deleted.
AUTH_SALT rotation
Because the encryption key derives from AUTH_SALT, rotating your WordPress salts makes saved passwords and CA certificates unreadable. If that happens an admin notice appears — "A chart's stored Remote MySQL password could not be decrypted (the WordPress AUTH_SALT may have changed). Please re-enter the password on that chart." (with a matching notice asking you to re-paste the CA certificate) — and syncs fail until you re-enter them on the affected chart.
Syncing
One important difference from CSV and Google Sheets: MySQL sources never sync automatically while you edit. Queries only run when you ask. A sync happens:
- When you click
Sync Now. This works with unsaved edits — the sync uses the queries, password, or CA certificate you just typed, so whatTest Connectionverified is exactly what syncs, no save required first. - When you save the post, if the queries, host, or database changed. (Changing only the port, username, password, or SSL settings doesn't trigger a sync on save — use
Sync Nowor wait for the schedule.) - On the scheduled
Refreshinterval, same options as the other sources.
Everything else works like the rest of External Data: the spreadsheet is locked while the source is active, a failed sync leaves your existing chart data untouched and retries on the next interval, and after 3 consecutive failures an admin notice flags the failing chart.
Safe by design
- SELECT-only, twice over — every query must pass a SQL validator that accepts exactly one
SELECT(orWITH … SELECT) statement and rejects stacked statements and write/DDL keywords. And even if something slipped past, every query runs inside a server-sideREAD ONLYtransaction, so the database itself refuses writes. - Timeouts — connections time out after 5 seconds, statements after 10 seconds.
- Result caps — results are capped at 1,000 rows, 50 columns, 8 MB total, and 64 KB per cell; anything beyond is dropped. Developers can adjust the caps — see the
m_chart_pro_sql_*filters. - No internal hosts — a Remote MySQL host must resolve to a public IP address. Private, loopback, and link-local addresses are rejected (
The database host is not allowed), so a chart can't be used to probe your internal network. - Sanitized errors — database errors shown in the sync status are generic and never leak schema or SQL. Real driver errors appear only on the admin-only
Test Connectionbutton.
Troubleshooting
| Error message | What to do |
|---|---|
No queries are configured for this chart | Add at least one query and save. |
The query is empty | A query field was left blank — fill it in or remove it. |
The query is too long (limit 65535 characters) | Shorten the query. |
Only a single SELECT statement is allowed | The query contains multiple statements. Remove everything after the first SELECT — one statement per query. |
Only a single read-only SELECT statement is allowed | The query doesn't start with SELECT (or WITH … SELECT). Rewrite it as a plain read. |
The query has an unterminated string or comment | A quote or comment is opened but never closed. Balance the quotes and /* … */ markers. |
The query contains a disallowed keyword: … / The query contains a disallowed function: … | The validator found something that could write or leak data. Rewrite the query as a plain read. |
The query attempted a write, which is not allowed. | The query tried to write and the read-only transaction blocked it. Rewrite it as a read. |
The query did not return any data. | The query ran but returned zero rows. Check your WHERE clause against the data. |
The query failed to run. | Usually a SQL syntax error or a missing table or column. Test the query in a MySQL client first. |
Could not establish a read-only database session | The read-only transaction that guards every query couldn't be opened. Check that the database user can start transactions, and check the server logs. |
No database host is configured for this chart | Fill in Host on the MySQL Server tab (administrators only). |
The database host could not be resolved | The hostname has no DNS entry — check for typos. |
The database host is not allowed | The host resolves to a private, loopback, or link-local address. Only publicly-resolvable hosts are accepted — use Local MySQL for the WordPress database itself. |
Could not connect to the remote database | Check host, port, and firewall rules, and confirm the server accepts connections from your web server's IP. Your own web host may also block outgoing database ports — see Hosting firewalls above. Admins: Test Connection shows the underlying driver error. |
Database access was denied. | Wrong username or password, or the account lacks access to that database. Re-enter the password — it can't be read back once saved. |
Limits and gotchas
- Chart.js charts only, like all External Data.
- Up to 10 queries on multi-sheet chart types; exactly one on single-sheet types.
- Results are capped at 1,000 rows × 50 columns, 8 MB per result, 64 KB per cell, and queries time out after 10 seconds. Developers can raise the caps with the
m_chart_pro_sql_*filters. - Only administrators can edit MySQL settings; other roles can still
Sync Now. - No automatic sync while editing — click
Sync Nowto see the results of query changes. - Remote hosts must be publicly resolvable. You can't point Remote MySQL at
localhostor a private network address — use Local MySQL for the site's own database. - Rotating
AUTH_SALTinvalidates saved Remote MySQL passwords and CA certificates; re-enter them when the admin notices appear. - Duplicating a chart copies its MySQL configuration but not the password or CA certificate — re-enter them on the copy before it can sync.