-- name: CreateOrganization :one -- Identity is org_id, presentation is name, and organization names are -- deliberately not unique. `key` is the optional declarative address -- (entity-keys §1 to §4): a caller outside the database -- a seed, a -- configuration loader, an API client -- supplies it to name this row -- literally. UI-created organizations leave it NULL, and a personal -- organization never gets one: a key is never derived from a person's login -- or display name (§4). INSERT INTO core.organizations (name, org_type, owner_person_id, key) VALUES ($1, $2, $3, sqlc.narg(key)) RETURNING *; -- name: GetOrganizationByKey :one -- Root-scoped key resolver (entity-keys §5): the query that makes `key` a -- whole feature rather than a seed-only column. uq_organizations_key -- guarantees at most one row matches; a NULL key never matches because the -- parameter is compared with `=`. SELECT * FROM core.organizations WHERE key = $1; -- name: GetOrganizationByID :one SELECT * FROM core.organizations WHERE org_id = $1; -- name: GetOrganizationsByOwner :many SELECT * FROM core.organizations WHERE owner_person_id = $1 ORDER BY name; -- name: ListOrganizations :many SELECT * FROM core.organizations ORDER BY name; -- name: ListOrganizationsPage :many -- Organizations list operator surface (operator-list-scale UX-4): the -- paged, searched, org-type-filtered successor to ListOrganizationsWithOwner -- -- each row still carries its owner's display name so no organization can -- read as unowned (ux-honest-surfaces UX-16). owner_person_id is NOT NULL -- (every organization has an owner person, the System tenant included -- -- 00001_init.sql), so the LEFT JOIN is defensive rather than expected to -- ever drop the owner's name. -- -- sqlc.narg(q): NULL matches every row; set, a case-insensitive substring -- match against the organization's name, or its owner's display -- name or primary email (operator-list-scale: "carries a scoped -- server-side search" -- "organizations: name and owner name or -- email"). -- sqlc.narg(org_type): NULL matches every org type; set, an exact facet -- match (operator-list-scale: "Status filters exist ... organizations: org -- type"). -- count(*) OVER() carries the true total for the filtered set alongside -- the page's rows (design D2), so the stated total never reflects only the -- page. SELECT o.org_id, o.name, o.org_type, o.owner_person_id, o.status, o.created_at, o.updated_at, p.display_name AS owner_display_name, count(*) OVER() AS total_count FROM core.organizations o LEFT JOIN core.persons p ON p.person_id = o.owner_person_id WHERE (sqlc.narg(q)::text IS NULL OR o.name ILIKE '%' || sqlc.narg(q)::text || '%' OR p.display_name ILIKE '%' || sqlc.narg(q)::text || '%' OR p.primary_email ILIKE '%' || sqlc.narg(q)::text || '%') AND (sqlc.narg(org_type)::text IS NULL OR o.org_type = sqlc.narg(org_type)::text) ORDER BY o.name LIMIT sqlc.arg(page_limit)::int OFFSET sqlc.arg(page_offset)::int; -- name: SearchOrganizationsByName :many -- Landing lookup's organization branch (entity-keys). Runs only -- when no person matches the lookup term; a dedicated query rather than -- ListOrganizationsPage because that query's owner-email term would blur -- the person/organization split the lookup depends on. Active -- organizations only, name substring match, bounded by LIMIT so a short -- term cannot return the whole table. SELECT o.org_id, o.name, o.org_type, p.display_name AS owner_display_name FROM core.organizations o LEFT JOIN core.persons p ON p.person_id = o.owner_person_id WHERE o.status = 'active' AND o.name ILIKE '%' || sqlc.arg(name)::text || '%' ORDER BY o.name LIMIT sqlc.arg(row_limit)::int; -- name: ListOrganizationsByType :many SELECT * FROM core.organizations WHERE org_type = $1 ORDER BY name; -- name: CountTeamOrganizations :one -- Operator overview headline. Personal orgs exist one-per-member by -- structural convention (design/organization/architecture.md §2), so an -- organizations count that includes them tracks the people count and reads -- as an error. Negative-space predicate instead of org_type = 'team': -- everything active that is neither the auto-created personal type nor a -- reserved type counts, so team, enterprise, and deployment-defined types -- need no code change here and the reserved system org never appears. SELECT COUNT(*) FROM core.organizations o JOIN core.org_types t ON t.org_type = o.org_type WHERE o.status = 'active' AND o.org_type <> 'personal' AND NOT t.is_reserved; -- name: EnsureSystemOrganization :exec -- Idempotently insert the singleton System organization. The partial unique -- index uq_organizations_one_system enforces "exactly one System tenant", and -- the conflict target repeats the index predicate so Postgres can infer it. -- Read back with GetSystemOrganization. -- -- `system` is one of the two keys the system writes for itself (entity-keys -- §4): a fixed constant for a row the console creates by design, not a value -- derived from anybody's data. It is written here rather than by the Go -- caller so the only path that creates the System tenant is also the only -- path that names it. INSERT INTO core.organizations (name, org_type, owner_person_id, key) VALUES ($1, $2, $3, 'system') ON CONFLICT (org_type) WHERE org_type = 'system' DO NOTHING; -- name: GetSystemOrganization :one -- The singleton System organization, resolved by its natural key. Paired -- with EnsureSystemOrganization; uq_organizations_one_system guarantees at -- most one row matches. SELECT * FROM core.organizations WHERE org_type = 'system';