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), refusewp_users/wp_usermeta, force a non-empty WHERE on update/delete, accept only real column names, and record each touched row in History so the write can be undone. 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.
Read tools (enabled by default)
Section titled “Read tools (enabled by default)”emcp-tools/list-tables
Section titled “emcp-tools/list-tables”List all tables in the WordPress database with row counts and sizes. Read-only (manage_options).
emcp-tools/describe-table
Section titled “emcp-tools/describe-table”Return the column definitions and indexes for a table. Read-only (manage_options).
emcp-tools/query
Section titled “emcp-tools/query”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), returning 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, since 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.
Write tools (disabled by default)
Section titled “Write tools (disabled by default)”Every write tool uses $wpdb with parameterized values (no raw write-SQL or DDL) and refuses wp_users/wp_usermeta. 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. Since 3.20.0 every column name in data and where must be a real column of the table, or the call is refused with unknown_column (read describe-table for the names).
Undo from History (3.20.0). Each write locks the rows it touches and records them by primary key, with their previous values, in the same transaction as the write. History can then undo it: deleted rows come back under their original ids, an inserted row is removed, and a row changed since is a conflict that only Undo anyway overwrites. Every answer includes undoable and change_id, plus not_undoable with the reason when History cannot undo it: a table without a primary key, a table that is not InnoDB, or an update that changes primary key values. Those writes still run. A write that would change more than 500 rows is refused with too_many_rows, and nothing is changed.
emcp-tools/insert-row
Section titled “emcp-tools/insert-row”Insert a row into a table using $wpdb. Returns insert_id, affected and the undo fields (manage_options).
emcp-tools/update-rows
Section titled “emcp-tools/update-rows”Update rows matching a forced non-empty equality WHERE. Undoable from History unless the update changes primary key values; refused above 500 rows (manage_options).
emcp-tools/delete-rows
Section titled “emcp-tools/delete-rows”Delete rows matching a forced non-empty equality WHERE. Requires an explicit confirm:true. Undoable from History, with rows restored under their original keys; refused above 500 rows (manage_options).
Enabling the write tools
Section titled “Enabling the write tools”insert-row, update-rows, and delete-rows are off until you enable them. Open EMCP Tools → Tools in wp-admin, go to the WordPress tab, find the Database section, switch on the ones you want, then choose Save changes in the save bar at the bottom of the screen. See Disabling tools.
