Skip to content

Database

Inspect the WordPress database with flexible read-only SQL, and, when you opt in, perform structured, parameterized writes. The 3 read tools are enabled by default; the 3 write tools ship disabled-by-default (an admin opts in on EMCP Tools → Tools). All six require manage_options.

Direct DB access. The read path (query) is bounded by a read-only-SQL validator that rejects writes, DDL, stacked statements, MySQL /*! … */ executable comments, and file-access SQL (INTO OUTFILE / LOAD_FILE). Writes are structured and parameterized via $wpdb (no raw write-SQL, so no DDL), refuse wp_users/wp_usermeta, force a non-empty WHERE on update/delete, and snapshot a before-image to an audit log. Arbitrary DB access is contrary to WordPress.org plugin guidelines; this feature exists per an explicit project decision and stays off unless you enable it.

List all tables in the WordPress database with row counts and sizes. Read-only (manage_options).

Return the column definitions and indexes for a table. Read-only (manage_options).

Run a read-only SQL query: SELECT / SHOW / DESCRIBE / EXPLAIN only; results are capped at 1,000 rows. Writes, DDL, stacked statements, /*! executable comments, and file-access SQL are rejected. It also refuses reads that touch wp_users/wp_usermeta (password hashes, session tokens, activation keys) — a protected_read error — even for a plain SELECT; use list-users/get-user for user data instead. Read-only (manage_options).

Hardened in v3.13.0. The guard was rebuilt around a real SQL tokenizer, not a string pattern-match, closing several bypasses a security review found (a comment trick, a backslash inside a string, a backquoted alias, a double-quoted identifier — each could hide a table name from the old check). A query is analyzed under every way the server could read it and refused if any reading is unsafe. Since 3.13.0, query also refuses:

  • The server’s own system schemas: mysql, information_schema, performance_schema, sys (protected_read, same error code and filter, emcp_tools_db_protected_tables, as the user-table refusal above).
  • Variable assignment (:=) inside the query — that’s a write in disguise (not_read_only).
  • Delay/lock functions: SLEEP, BENCHMARK, GET_LOCK, and similar (unsafe_function_blocked).

Row limiting is enforced by the database itself now, not by fetching everything and slicing in PHP: a query with no top-level LIMIT gets one added; a LIMIT larger than 1,000 is refused (limit_too_large) rather than silently capped; and a LIMIT that only appears inside a subquery doesn’t count as bounding the outer query. Each query also runs under a 10-second server-side statement timeout.

Every write tool uses $wpdb with parameterized values (no raw write-SQL or DDL), refuses wp_users/wp_usermeta, and records a before-image snapshot to an audit log. The refused-table list (wp_users/wp_usermeta by default) is the same one the read-only query tool checks above, both filterable via emcp_tools_db_protected_tables.

Insert a row into a table using $wpdb (manage_options).

Update rows matching a forced non-empty WHERE clause; captures a before-image (manage_options).

Delete rows matching a forced non-empty WHERE clause. Requires an explicit confirm:true; captures a before-image (manage_options).

insert-row, update-rows, and delete-rows are off until you enable them. Open EMCP Tools → Tools in wp-admin, find the Database category on the WordPress tab, and toggle the ones you want. See Disabling tools.