# PostgreSQL Extensions Design §8.3, §21, §30. What the platform uses beyond portable JPA, and what each is guarded by. Everything here is core PostgreSQL. No server extension is required. ## Locking `SELECT ... FOR UPDATE` with a finite bound, always. `PostgreSqlLockOptions` refuses an unbounded lock request because it waits as long as the holder holds it, turning one slow transaction into a pile-up of blocked connections. `NOWAIT` and a wait timeout are separate requests, not two spellings of one — modelling them as a single field with a magic zero is how "no wait" becomes "wait forever". `55P03` (lock not available) and `40P01` (deadlock) drive opposite recovery and are never collapsed: the first leaves the transaction alive and the caller in control; the second has already been rolled back by the server. ## Work claims `FOR UPDATE SKIP LOCKED` is reachable only through a registered `WorkQueueName`, never as a repository flag. It deliberately returns an incomplete view of the table: correct for handing disjoint work to competing workers, silently wrong for anything that needs to see every matching row. A registered claim statement must skip locked rows and impose a deterministic `ORDER BY`. ## Upserts `INSERT ... ON CONFLICT ... RETURNING` under a registered `NativeWriteName` with a fixed conflict target and update column set. The conflict target cannot be a bound parameter, so accepting one from a caller would mean building SQL from input. An upsert is the correct answer to a create race precisely because the database decides. Read-then-write cannot be made correct: another transaction can commit between the read and the write. `(xmax = 0) AS inserted` in the `RETURNING` list is what lets the platform report insert-versus-update without a second query. The executor flushes before and clears after: a native write is invisible to the Persistence Context, so a pending managed change would otherwise overwrite it, and a managed entity loaded beforehand would keep serving pre-upsert values. ## JSONB `JsonDocument` carries a schema name and version alongside the payload. A JSONB column is schemaless at the database level, so without an envelope the only record of what a stored document means is the code that wrote it — and a document written two releases ago is indistinguishable from a current one. The payload never carries a Java class name. Type metadata in a JSONB column is a deserialization gadget: whoever can write a row chooses the class the reader instantiates. Query paths are registered. A JSON path is part of the SQL text and cannot be bound, so forwarding a request field into one is concatenating untrusted input into a statement. Values are always bound. ## Arrays and ranges Arrays are built with `Connection.createArrayOf`, never by formatting a literal — hand-formatting is where quoting bugs live, and a tag containing a comma changes the array's shape rather than its content. `PgRange` models both endpoints as independently optional and independently inclusive, because that is what a PostgreSQL range is. Whether `[09:00, 10:00)` and `[10:00, 11:00)` overlap depends on the bracket, not the values, and a pair of `timestamptz` columns cannot express it. ## COPY (J4 admin) `COPY` bypasses the Persistence Context, entity callbacks, version checks, and Envers entirely. That is why it is fast and why it is an admin capability with a registered statement, a bounded stream, a row and byte cap, a finite server-side `statement_timeout`, and a named operator. The registry accepts only `COPY ... FROM STDIN`. `COPY ... FROM '/path'` reads a file on the *database server* as the server's OS user; it is superuser-only for exactly that reason and does not belong behind an application API.