-- name: CreatePlacement :one
INSERT INTO domains.placements (claim_id, fqdn, reversed_labels, provider, resource_ref, servable)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *;

-- name: GetPlacementByFQDN :one
-- The serving path (/domains/ask). Deliberately unconditional: a row with
-- servable = false is a local refusal, distinct from a registry miss, and
-- the authorizer needs to tell those apart (design D5).
SELECT * FROM domains.placements
WHERE fqdn = $1;

-- name: GetPlacementByID :one
-- The release path reads the placement before deleting it: the claim_id it
-- carries is what the auto-release re-count needs.
SELECT * FROM domains.placements
WHERE placement_id = $1;

-- name: GetPlacementByResource :one
SELECT * FROM domains.placements
WHERE provider = $1 AND resource_ref = $2;

-- name: ListPlacementsByClaim :many
SELECT * FROM domains.placements
WHERE claim_id = $1
ORDER BY fqdn;

-- name: CountPlacementsByClaim :one
-- Re-read under the registry advisory lock before auto-releasing a claim,
-- so a release racing a placement cannot strand a live placement in a
-- released claim (design D3).
SELECT COUNT(*) FROM domains.placements
WHERE claim_id = $1;

-- name: ListPlacementsByFQDNs :many
-- Ancestor walk on placements: an operator placement above a candidate root
-- blocks carving it. Exact equality keeps this on the fqdn unique index.
SELECT * FROM domains.placements
WHERE fqdn = ANY(@fqdns::text[])
ORDER BY length(fqdn) DESC;

-- name: ListPlacementsInSubtree :many
-- Descendant scan via the reversed-label prefix; excludes the subtree root
-- itself (an exact hit is the unique index's business).
SELECT * FROM domains.placements
WHERE reversed_labels LIKE @reversed_root::text || '.%'
ORDER BY reversed_labels;

-- name: SetPlacementServableByResource :execrows
-- Provider lifecycle transitions address placements by their own resource
-- identity, not by name (fedwiki: archive => FALSE, restore => TRUE).
UPDATE domains.placements
SET servable = $3, updated_at = NOW()
WHERE provider = $1 AND resource_ref = $2;

-- name: DeletePlacement :execrows
DELETE FROM domains.placements
WHERE placement_id = $1;

-- name: ListAllPlacementsForOperator :many
-- The operator placements section (ux-ia-naming task 9.1): every exact name
-- actually being served, deployment-wide, each with the provider resource it
-- is wired to. Claims and placements are never conflated
-- (docs/models/domains-registry.md, "Claim vs placement") -- a claim can hold
-- a name that serves nothing, and this is the query that answers what
-- actually serves. Ordered by fqdn so the list reads in the same namespace
-- order as the claims table above it.
SELECT placement_id, claim_id, fqdn, provider, resource_ref, servable, created_at
FROM domains.placements
ORDER BY fqdn;
