Paranoid SQL Execution on Postgres
ardentperf.com
ardentperf.com
Especially, definately, also DDL (where possible). There are only few DDL statements that cannot be run transactionally (most of which is [CREATE / RE]INDEX CONCURRENTLY), but the rest of DDL can best be tested before committing to the result, especially when migrating between versioned schemas: you don't want half-applied migrations.
How do you test what wasn't changed, how does that work. Do you select before and after and compare certain rows or tables and if so random ones or which ones? A small example would be awesome.
Or just use PL/pgSQL stored functions which are inherently transactions[1]
No bad thing anyway as it has a side-benefit of helping the fight against SQL injection attacks etc.
[1] https://www.postgresql.org/docs/14/plpgsql-transactions.html
What I personally like about RLS is that I can test every access scenario for a user's session in unit tests, and I don't have to worry about application bugs allowing one user to access another user's data or accessing tables that their session token/api key doesn't allow them access to.
Overall I agree with you but the process/tooling is really the issue. Not inherently, it's just less developed and less standardized than for other kinds of code. DBAs aren't necessarily a silver bullet here either. They have a different, somewhat overlapping focus and while they have tools for managing changes to SQL-as-code, they're usually not built around the assumption that devs are shipping diffs to SQL as part of their normal day-to-day work.
Not insurmountable complexities by any means but unless you are or work closely with an expert in your chosen DB, it might be difficult to accurately estimate what you're getting into.
---
This probably is overall more secure but not in a magical way. There's nothing really special about a user here, something will eventually have to have a role that can read that column. For example a classic trap that has caused plenty of security breaches is needing to write a function as `security definer` to account for RLS policies but not remembering to exclude user-writeable schemas from search_path. No users necessary for that leak.
Parse the SQL and make sure it is what you think it is. Some parsers will give you a list of all tables accessed. I use node-sql-parser.
Run in a read-only transaction: SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY;
- qualify all type names that you might be using in casts
If so, that is very surprising. Any ideas why that is slow?
It's quite similar to why you fully qualify table names and column names.
Not per se, but it might just as well be that you share the database with several other applications, which might do DDL. Fully qualifying all identifiers is the easiest way to guarantee that you don't have negative dependencies to worry about (i.e. depending on the non-existance of some identifier in a certain schema); the issue will most likely happen only when a) an adversary gets CREATE TYPES access to the database, or when you use custom types and a name that's in use starts to be shadowed.
Examples:
CREATE DOMAIN "bigint" AS pg_catalog.text;
CREATE TYPE "text" AS ENUM ();
Though, in all earnesty, this is also an issue with custom operators:
CREATE OPERATOR = (function = always_false, left_arg = int, right_arg = int);
You can schema-qualify operators ( Col1 OPERATOR(pg_catalog.=) Col2 ), but in doing that you lose operator precedence.
> To be able to create an operator, you must have USAGE privilege on the argument types and the return type, as well as EXECUTE privilege on the underlying function. If a commutator or negator operator is specified, you must own these operators.
(https://www.postgresql.org/docs/14/sql-createoperator.html)