Skip to main content

Module mutate

Module mutate 

Source
Expand description

Builds and runs parameterized INSERT/UPDATE/DELETE against an already-introspected table, plus the deliberately-unrestricted raw-SQL path the dashboard’s own “Run SQL” page uses.

Two different security postures, on purpose. The structured functions (insert_row, update_row, delete_row, fetch_row) only ever interpolate a table or column name into SQL text after validating it against that table’s own freshly-introspected column list (introspect::table_columns) - every value is always a bound parameter via [codec::bind_any], never interpolated as text. Column-name validation is enforced inside this module, not left to the caller: values/settable are filtered against columns before use, and pk is checked by [require_known_pk_columns] - the latter closes a real SQL-injection gap found in a security review (pk’s column names previously reached a WHERE clause unchecked, since they originate from a request’s raw field names, not values, which nothing upstream ever validated). The raw functions (run_raw, run_script) are the deliberate opposite: they execute whatever SQL text they’re given, unrestricted, the same way phpMyAdmin’s own SQL tab and Import feature are - their safety is the dashboard’s existing double gate (DB_DASHBOARD_PASSWORD + APP_DEBUG-gated registration), not query validation, because restricting either would defeat the entire point of those features.

Functions§

delete_row
Deletes one row identified by pk (composite-safe, same shape as update_row).
fetch_row
Fetches one row identified by pk, if it still exists - used to prefill an edit form. pk originates from a query string a user can tamper with, so both its column names ([require_known_pk_columns]) and its values (bind_any) are validated/bound before any SQL is built - unlike run_raw’s deliberately-unrestricted user-typed SQL.
insert_row
Inserts one row. values keys not found in columns are silently ignored (a form field the caller shouldn’t have sent) rather than erroring - the caller (the HTTP handler) only ever builds values from columns in the first place, so this is a defense-in-depth backstop, not the primary guard.
run_raw
Runs arbitrary, user-typed SQL and renders whatever comes back - see this module’s own doc comment for why this one is deliberately unrestricted. fetch_all works uniformly for both queries and statements: a SELECT returns real rows; INSERT/UPDATE/DELETE/ DDL return zero rows on success, so the same call reports “0 rows” rather than needing to sniff the statement’s kind from its text first.
run_script
Runs an uploaded .sql file’s full contents (the SQL Import feature) - unlike run_raw, this doesn’t return or render rows, because an imported file is typically many statements, not one query to display. Uses sqlx::raw_sql rather than fetch_all, the same primitive larust_orm::migrate::run already relies on for exactly this reason: it delegates statement parsing to the backend itself, so multi-statement files, comments, and string literals containing semicolons all work correctly (splitting on ; by hand does not - a real bug this framework already hit once in the migration runner). Same deliberately- unrestricted security posture as run_raw.
update_row
Updates one row identified by pk (composite-safe - pass more than one entry for a composite key). Any entry in values whose column is also part of pk is skipped for the SET clause (the primary key itself is never rewritten by an edit form).