chore(datastore): add the maven remote create and details reads

Why

The management API's remote-repository CRUD needs per-format store plumbing. npm's create and details reads are merged, and Maven had only finders, credential writes, and health CAS: no insert path and no management projection. This MR lands the Maven remote child-row create, the details reads (single and page-bounded batch), and the write-error classifier that the create-dispatch, list-serialization, and update steps consume. Plan: 2026-08-13-s17-phase6-virtual-remote-repositories.md, Step 4. Spec: S17-rest-management-api.md.

What's non-obvious

  • The new store wraps are identifier-free per database-query-patterns.md "Identifier-free error strings", deliberately diverging from the npm mirror's grandfathered identifier-carrying wraps ("Do not copy them into a new store method").
  • Plan deviation: the plan made CreateMavenRemoteRepository the first caller of maven.ResolveCacheValidityHours, but internal/format/maven imports internal/datastore, so that call is an import cycle. The helper is retyped to the int16 column shape (zero production callers today, the management layer is its expected consumer), the create uses a datastore-local pin twin (npm's exact topology), and a pin-parity test in package datastore_test holds the two copies to the same verdicts behind a one-file depguard negation in .golangci.yaml (the import lives only in datastore's test binary, so the production fence stands).
  • NewMavenRemoteRepositoryStore gains the remote.AuditSink because the create is this store's first credential write, and every credential write is audited. wire_maven.go and the existing test call sites carry the mechanical constructor-arg addition.
  • FindRemoteDetailsByRepositoryIDs is the remote family's first batch read (npm's lands with its own completion step). Bound 100 is the management list's server maximum, tied in prose because managementapi imports datastore, not the reverse.
  • One pinned semantic divergence from npm: Maven always binds a concrete cache_validity_hours (the resolver materializes the default 24), npm renders SQL DEFAULT.

Size

Production +787/-22 (create 333, details 309, errors 99, the rest small edits to the store file, the credentials file, the maven helper, and cmd wiring), tests +1,945/-30, config +10 (.golangci.yaml). Splitting create from details would halve the production LOC per MR but sever the shared error classifier and the npm-mirror suite diff from the code they review. The merged plan fixes create-plus-details as one step, with the update as its own stacked step.

Test plan

Unit suites (insert-stmt pin matrix, credential-pair 3x3 matrix, classifier, guards, constructor panics) and the integration battery (go test -tags=integration -count=1 ./internal/datastore/ -run 'TestMavenRemote...'): creates under caller-owned transactions with rollback and 25P02 recovery, Central-pin persistence on both hosts with explicit-wins, credential-column invisibility plus JSON-leak assertions, batch present/missing/duplicate/cross-namespace walks, partition-pruning EXPLAIN pins, and the credential-audit event set. golangci-lint runs clean in both modes on branch-owned lines except the package-endemic contextcheck class in integration files (the npm mirror files carry the same class).

Spec coverage

Spec: docs/specs/S17-rest-management-api.md Plan: docs/plans/2026-08-13-s17-phase6-virtual-remote-repositories.md, Step 4

Scope: the internal/datastore Maven remote-repository create and details reads. Acceptance criteria are numbered by position (123 total); the Phase 6 block opens at 68.

Acceptance criteria

# Criterion (store-layer share) Tests
AC-69 A remote create inserts the per-format remote child row in one transaction that rolls back fully on any failure TestMavenRemoteRepositoryStore_CreateMavenRemoteRepository ("creates the binding with defaults for a non-central upstream", "a rolled-back transaction persists nothing"), ..._AbortedTransaction (a classified refusal leaves the caller's tx in 25P02 and rollback recovers it). The parent row, collection link, 201, and response body are Steps 10, 12, and 24
AC-70 Out-of-range cache_validity_hours / metadata_cache_validity_hours return 400 Store share: ..._CreateMavenRemoteRepository_Refusals ("maps a CHECK refusal without leaking the failing row", "maps a metadata-window CHECK refusal to the same sentinel") maps both to ErrMavenRemoteRepositoryCheckViolation; TestInsertMavenRemoteRepositoryStmt (negative and maximum values bind through for the schema to own). The 400 mapping and the missing-settings.url rule are Steps 11 and 24
AC-73 Credentials store and surface only as has_credentials; a one-sided pair and an over-2048 value are refused ..._CreateMavenRemoteRepository ("stores a supplied credential pair"), ..._Refusals (over-cap, username-only, password-only), TestValidateMavenRemoteCredentialPair (full 3x3 nil/empty/set matrix plus rune-counted cap), ..._FindRemoteDetailsByRepositoryID (presence flag; NULL, empty, and half-empty pairs read false; JSON leak assertion). The 400/has_credentials serialization is Steps 11 and 12
AC-74 Detail and list serialize remote settings; the list's fetch is page-bounded, one child-table lookup set per format ..._FindRemoteDetailsByRepositoryID (13 subtests: values, gates, tx visibility), ..._FindRemoteDetailsByRepositoryIDs (present/missing mix, empty result, duplicates collapse, per-row parent gate, cross-namespace), ..._FindRemoteDetailsByRepositoryIDs_ArgumentGuards (empty input runs no query, the bound is 100 with the at-bound batch passing, zero-element guard), and both _PrunesToOnePartition pins. Serialization is Step 12
AC-79 Create, update, and delete each emit one audit event; every credential write emits S13's credential event Store share (the create's credential event): TestMavenRemoteRepositoryStore_CredentialWriteAudit ("a create with a credential pair emits a set", "a create without credentials emits nothing", "a refused create emits nothing"). The repository-write events with the actor are Steps 3 and 10; the update-side credential events are Step 5
AC-83 An invalid url returns 400 on create, and create issues no DNS resolution and no upstream request Store share: the create rejects through remote.ParseUpstreamBase before any DB round trip (..._CreateMavenRemoteRepository_ArgumentGuards "invalid url" with the recordingDB assertion); the reject set and its no-resolution property are remote.ParseUpstreamBase's own merged suite. The 400 is Steps 11 and 24
AC-84 A Maven remote for Maven Central without an explicit cache_validity_hours stores 0; an explicit value wins ..._CreateMavenRemoteRepository ("pins repo1.maven.org and persists the normalized url", "pins repo.maven.apache.org", "an explicit cache validity wins over the pin"), TestInsertMavenRemoteRepositoryStmt (both hosts, respelled/http/odd-port spellings still pin, subdomain and suffix lookalikes fall off the pin, explicit zero/negative/maximum pass through). The npm half is the merged npm suite
AC-1 to AC-68, AC-71, AC-72, AC-75 to AC-78, AC-80 to AC-82, AC-85 to AC-123 Phases 1-4, the Phase 6 OpenAPI document, virtual creates, the every-kind cap, PATCH, eviction on URL change, associations, connection test, remote reads and evictions, and Phase 8 statistics Not this step. Step 4 is the Maven remote datastore create and details reads only; the update half is Step 5, the container twins Steps 6-7, npm completion Step 8, handlers Steps 10-12 and 23-24

Error cases

Condition Tests
Create/Update: invalid url, a one-sided or over-long credential value, an out-of-range cache window return 400 Store share: the sentinels the handler maps (remote.ErrUpstreamBaseURL, remote.ErrIncompleteCredentials, remote.ErrCredentialTooLong, ErrMavenRemoteRepositoryCheckViolation), covered by the guard, validation, and refusal suites above. The envelope and status are Steps 11 and 24
Every other row (auth, routing, list, delete, PATCH, tag upsert, bulk, associations, connection test, statistics) Not this step: HTTP-layer conditions owned by their route steps

Security considerations

Concern Tests
Injection: all queries use the Jet builder with bound parameters TestInsertMavenRemoteRepositoryStmt asserts every value binds as an ordered arg
Tenant isolation: every query scoped by namespace_id, the partition key "does not serve details from a different namespace", "does not serve another namespace's bindings", both _PrunesToOnePartition pins (single partition on both tables, IN-list batch included)
Upstream credentials: write-only, surfaced only as has_credentials, audited on every write, absent from logs and error bodies The details struct carries no credential fields plus JSON leak assertions; TestMavenRemoteRepositoryWriteError and TestClassifyMavenRemoteRepositoryWriteError (a PgError carrying row data never travels the chain; retryable states stay matchable); TestMavenRemoteRepositoryStore_CredentialWriteAudit
SSRF: the static covers run at create through remote.ParseUpstreamBase Create guard "invalid url" (rejected before persistence); the cover set itself is the merged remote suite's
Auth, existence hiding, write authorization, destructive-route posture, statistics authorization Handler and platform layers; not this step
Context for LLM agents

Rationale

  • Shared leaf package for the Central pin, instead of the datastore-local twin. Rejected because no third consumer exists yet, and the parity test pins equivalence.
  • Deferring the audit sink to the update step. Rejected because the create already writes credentials, and every credential write is audited.

Non-goals

  • Handler or route wiring. Later steps consume these stores.
  • The update path. It stacks on this branch as its own MR.
  • npm's batch read. It belongs to npm's completion step.
  • The plan's Status-table row. A dedicated single-writer MR fills it for the whole fan-out.
  • The endemic contextcheck class in shared seed helpers. It stays untouched.
  • Pre-existing comment citations on untouched lines. They stay.

Database Review Evidence

Queries

Note

Plans are from EXPLAIN (ANALYZE, BUFFERS) against an ephemeral PostgreSQL 17.10 container (matching GL_PG_CURR_VERSION from .gitlab-ci-other-versions.yml), with synthesized seed data rolled back per query and the container torn down at the end of the run. Numbers reflect moderate cardinality and do not capture production-scale effects. See Database review evidence for seed sizing, methodology, and the anomalies the skill flags. Expand each row's details for the seed shape, rendered SQL, bound args, and raw plan.

Method Plan node Index Rows (plan / actual) Cost Time Buffers (hit / read) Partitions
datastore.CreateMavenRemoteRepository.AllSet Insert n/a 0 / 0 0.01 0.410ms 33 / 2 1/64 (routed)
datastore.CreateMavenRemoteRepository.Defaults Insert n/a 0 / 0 0.01 0.182ms 49 / 0 1/64 (routed)
datastore.FindRemoteDetailsByRepositoryID Limit maven_remote_repositories_p56_namespace_id_repository_id_idx, repositories_p56_pkey 1 / 1 16.63 0.036ms 6 / 0 1/64, 1/64
datastore.FindRemoteDetailsByRepositoryIDs Nested Loop maven_remote_repositories_p56_namespace_id_repository_id_idx, repositories_p56_pkey 10 / 10 138.00 0.036ms 33 / 0 1/64, 1/64
datastore.parentRepositoryIsActive.mavenRemoteParent Limit repositories_p56_pkey 1 / 1 8.31 0.011ms 3 / 0 1/64
datastore.CreateMavenRemoteRepository.AllSet

Summary: Single-row VALUES insert. Tuple routing lands the row in one partition of 64 (verified inside the transaction via tableoid: partitions.maven_remote_repositories_p56), and both FK triggers fire once against that partition only. All nine columns bind as ordered args, credential pair included. No anomalies.

Seed shape: namespaces=1, repositories=1 (the write target starts empty)

Rendered SQL:

INSERT INTO public.maven_remote_repositories (id, namespace_id, repository_id, url, tmp_plaintext_username, tmp_plaintext_password, cache_validity_hours, metadata_cache_validity_hours, snapshot_metadata_always_revalidate)
VALUES ($1::uuid, $2::uuid, $3::uuid, $4::text, $5::text, $6::text, $7, $8, $9::boolean);

Bound args: [018f00ff-0000-7000-8000-000000000001, 018f0000-0000-7000-8000-aaaaaaaaaaaa, 018f0000-0000-7000-8000-0000000009c4, https://repo.example.com/maven2, review-prep-username, review-prep-password, 12, 6, true]

Plan (EXPLAIN (ANALYZE, BUFFERS) output):

 Insert on maven_remote_repositories  (cost=0.00..0.01 rows=0 width=0) (actual time=0.410..0.410 rows=0 loops=1)
   Buffers: shared hit=33 read=2 dirtied=5 written=3
   ->  Result  (cost=0.00..0.01 rows=1 width=159) (actual time=0.002..0.003 rows=1 loops=1)
 Planning:
   Buffers: shared hit=2
 Planning Time: 0.116 ms
 Trigger for constraint fk_maven_remote_repositories_namespace_id_namespaces on maven_remote_repositories_p56: time=0.659 calls=1
 Trigger for constraint fk_maven_remote_repositories_repository_id_repositories on maven_remote_repositories_p56: time=3.381 calls=1
 Execution Time: 5.604 ms

Partition routing: SELECT tableoid::regclass FROM maven_remote_repositories WHERE id = $1 inside the same transaction returned partitions.maven_remote_repositories_p56 (exactly one child).

Timings: planning 0.116ms, execution 5.604ms, total 5.720ms.

datastore.CreateMavenRemoteRepository.Defaults

Summary: Same plan shape as AllSet with the anonymous-create rendering: NULL credentials inline and the two DEFAULT keywords leave the schema defaults the only copy of the unset windows, while cache_validity_hours binds the resolved 24. Routed to the same single partition, FK triggers once each. No anomalies.

Seed shape: namespaces=1, repositories=1 (the write target starts empty)

Rendered SQL:

INSERT INTO public.maven_remote_repositories (id, namespace_id, repository_id, url, tmp_plaintext_username, tmp_plaintext_password, cache_validity_hours, metadata_cache_validity_hours, snapshot_metadata_always_revalidate)
VALUES ($1::uuid, $2::uuid, $3::uuid, $4::text, NULL, NULL, $5, DEFAULT, DEFAULT);

Bound args: [018f00ff-0000-7000-8000-000000000002, 018f0000-0000-7000-8000-aaaaaaaaaaaa, 018f0000-0000-7000-8000-0000000009c4, https://repo.example.com/maven2, 24]

Plan (EXPLAIN (ANALYZE, BUFFERS) output):

 Insert on maven_remote_repositories  (cost=0.00..0.01 rows=0 width=0) (actual time=0.182..0.182 rows=0 loops=1)
   Buffers: shared hit=49
   ->  Result  (cost=0.00..0.01 rows=1 width=159) (actual time=0.002..0.002 rows=1 loops=1)
 Planning:
   Buffers: shared hit=2
 Planning Time: 0.051 ms
 Trigger for constraint fk_maven_remote_repositories_namespace_id_namespaces on maven_remote_repositories_p56: time=0.129 calls=1
 Trigger for constraint fk_maven_remote_repositories_repository_id_repositories on maven_remote_repositories_p56: time=0.704 calls=1
 Execution Time: 1.116 ms

Partition routing: SELECT tableoid::regclass FROM maven_remote_repositories WHERE id = $1 inside the same transaction returned partitions.maven_remote_repositories_p56 (exactly one child).

Timings: planning 0.051ms, execution 1.116ms, total 1.167ms.

datastore.FindRemoteDetailsByRepositoryID

Summary: Plan matches the read's intent: the unique (namespace_id, repository_id) index on the child table drives a one-row nested loop into the parent's PK, with both partition keys bound explicitly so each table prunes to one partition of 64. Estimate matches actual (1 / 1) and execution stays at 0.074 ms over 5000 seeded pairs. No anomalies.

Seed shape: namespaces=1, repositories=5000, maven_remote_repositories=5000 (single namespace, so every row lands in one partition per table; credential pair set on even rows, NULL/NULL on odd)

Rendered SQL:

SELECT maven_remote_repositories.id AS "maven_remote_repositories.id",
     maven_remote_repositories.repository_id AS "maven_remote_repositories.repository_id",
     maven_remote_repositories.url AS "maven_remote_repositories.url",
     ((((maven_remote_repositories.tmp_plaintext_username IS NOT NULL) AND (maven_remote_repositories.tmp_plaintext_username != $1::text)) AND (maven_remote_repositories.tmp_plaintext_password IS NOT NULL)) AND (maven_remote_repositories.tmp_plaintext_password != $2::text)) AS "has_credentials",
     maven_remote_repositories.cache_validity_hours AS "maven_remote_repositories.cache_validity_hours",
     maven_remote_repositories.metadata_cache_validity_hours AS "maven_remote_repositories.metadata_cache_validity_hours",
     maven_remote_repositories.snapshot_metadata_always_revalidate AS "maven_remote_repositories.snapshot_metadata_always_revalidate",
     maven_remote_repositories.last_health_status AS "maven_remote_repositories.last_health_status",
     maven_remote_repositories.last_health_checked_at AS "maven_remote_repositories.last_health_checked_at"
FROM public.maven_remote_repositories
     INNER JOIN public.repositories ON ((repositories.id = maven_remote_repositories.repository_id) AND (repositories.namespace_id = maven_remote_repositories.namespace_id))
WHERE ((maven_remote_repositories.namespace_id = $3::uuid) AND (maven_remote_repositories.repository_id = $4::uuid)) AND ((((repositories.namespace_id = $5::uuid) AND (repositories.format = $6)) AND (repositories.kind = $7)) AND (repositories.soft_deleted_at IS NULL))
LIMIT $8;

Bound args: ['', '', 018f0000-0000-7000-8000-aaaaaaaaaaaa, 018f0000-0000-7000-8000-0000000009c4, 018f0000-0000-7000-8000-aaaaaaaaaaaa, 1, 2, 1]

Plan (EXPLAIN (ANALYZE, BUFFERS) output):

 Limit  (cost=0.56..16.63 rows=1 width=84) (actual time=0.035..0.036 rows=1 loops=1)
   Buffers: shared hit=6
   ->  Nested Loop  (cost=0.56..16.63 rows=1 width=84) (actual time=0.034..0.034 rows=1 loops=1)
         Buffers: shared hit=6
         ->  Index Scan using maven_remote_repositories_p56_namespace_id_repository_id_idx on maven_remote_repositories_p56 maven_remote_repositories  (cost=0.28..8.30 rows=1 width=141) (actual time=0.017..0.018 rows=1 loops=1)
               Index Cond: ((namespace_id = '018f0000-0000-7000-8000-aaaaaaaaaaaa'::uuid) AND (repository_id = '018f0000-0000-7000-8000-0000000009c4'::uuid))
               Buffers: shared hit=3
         ->  Index Scan using repositories_p56_pkey on repositories_p56 repositories  (cost=0.28..8.31 rows=1 width=32) (actual time=0.013..0.013 rows=1 loops=1)
               Index Cond: ((id = '018f0000-0000-7000-8000-0000000009c4'::uuid) AND (namespace_id = '018f0000-0000-7000-8000-aaaaaaaaaaaa'::uuid))
               Filter: ((soft_deleted_at IS NULL) AND (format = '1'::bigint) AND (kind = '2'::bigint))
               Buffers: shared hit=3
 Planning:
   Buffers: shared hit=285
 Planning Time: 2.265 ms
 Execution Time: 0.074 ms

Timings: planning 2.265ms, execution 0.074ms, total 2.339ms.

datastore.FindRemoteDetailsByRepositoryIDs

Summary: The IN-list read stays on the same two indexes: one probe over the unique (namespace_id, repository_id) index returns the 10 requested ids, then the PK join gates each parent row (10 loops). Estimate matches actual (10 / 10) and both tables prune to one partition of 64. No LIMIT is needed: the unique index yields at most one row per id, and FindRemoteDetailsByRepositoryIDs bounds the id list at 100 before building the statement. No anomalies.

Seed shape: namespaces=1, repositories=5000, maven_remote_repositories=5000 (single namespace, so every row lands in one partition per table; credential pair set on even rows, NULL/NULL on odd)

Rendered SQL:

SELECT maven_remote_repositories.id AS "maven_remote_repositories.id",
     maven_remote_repositories.repository_id AS "maven_remote_repositories.repository_id",
     maven_remote_repositories.url AS "maven_remote_repositories.url",
     ((((maven_remote_repositories.tmp_plaintext_username IS NOT NULL) AND (maven_remote_repositories.tmp_plaintext_username != $1::text)) AND (maven_remote_repositories.tmp_plaintext_password IS NOT NULL)) AND (maven_remote_repositories.tmp_plaintext_password != $2::text)) AS "has_credentials",
     maven_remote_repositories.cache_validity_hours AS "maven_remote_repositories.cache_validity_hours",
     maven_remote_repositories.metadata_cache_validity_hours AS "maven_remote_repositories.metadata_cache_validity_hours",
     maven_remote_repositories.snapshot_metadata_always_revalidate AS "maven_remote_repositories.snapshot_metadata_always_revalidate",
     maven_remote_repositories.last_health_status AS "maven_remote_repositories.last_health_status",
     maven_remote_repositories.last_health_checked_at AS "maven_remote_repositories.last_health_checked_at"
FROM public.maven_remote_repositories
     INNER JOIN public.repositories ON ((repositories.id = maven_remote_repositories.repository_id) AND (repositories.namespace_id = maven_remote_repositories.namespace_id))
WHERE ((maven_remote_repositories.namespace_id = $3::uuid) AND (maven_remote_repositories.repository_id IN ($4::uuid, $5::uuid, $6::uuid, $7::uuid, $8::uuid, $9::uuid, $10::uuid, $11::uuid, $12::uuid, $13::uuid))) AND ((((repositories.namespace_id = $14::uuid) AND (repositories.format = $15)) AND (repositories.kind = $16)) AND (repositories.soft_deleted_at IS NULL));

Bound args: ['', '', 018f0000-0000-7000-8000-aaaaaaaaaaaa, 018f0000-0000-7000-8000-0000000007d1 ... 018f0000-0000-7000-8000-0000000007da (10 consecutive seeded ids), 018f0000-0000-7000-8000-aaaaaaaaaaaa, 1, 2]

Plan (EXPLAIN (ANALYZE, BUFFERS) output):

 Nested Loop  (cost=0.56..138.00 rows=10 width=84) (actual time=0.022..0.036 rows=10 loops=1)
   Buffers: shared hit=33
   ->  Index Scan using maven_remote_repositories_p56_namespace_id_repository_id_idx on maven_remote_repositories_p56 maven_remote_repositories  (cost=0.28..58.78 rows=10 width=141) (actual time=0.012..0.014 rows=10 loops=1)
         Index Cond: ((namespace_id = '018f0000-0000-7000-8000-aaaaaaaaaaaa'::uuid) AND (repository_id = ANY ('{018f0000-0000-7000-8000-0000000007d1,018f0000-0000-7000-8000-0000000007d2,018f0000-0000-7000-8000-0000000007d3,018f0000-0000-7000-8000-0000000007d4,018f0000-0000-7000-8000-0000000007d5,018f0000-0000-7000-8000-0000000007d6,018f0000-0000-7000-8000-0000000007d7,018f0000-0000-7000-8000-0000000007d8,018f0000-0000-7000-8000-0000000007d9,018f0000-0000-7000-8000-0000000007da}'::uuid[])))
         Buffers: shared hit=3
   ->  Index Scan using repositories_p56_pkey on repositories_p56 repositories  (cost=0.28..7.91 rows=1 width=32) (actual time=0.002..0.002 rows=1 loops=10)
         Index Cond: ((id = maven_remote_repositories.repository_id) AND (namespace_id = '018f0000-0000-7000-8000-aaaaaaaaaaaa'::uuid))
         Filter: ((soft_deleted_at IS NULL) AND (format = '1'::bigint) AND (kind = '2'::bigint))
         Buffers: shared hit=30
 Planning:
   Buffers: shared hit=298 read=1
 Planning Time: 1.278 ms
 Execution Time: 0.060 ms

Timings: planning 1.278ms, execution 0.060ms, total 1.338ms.

datastore.parentRepositoryIsActive.mavenRemoteParent

Summary: Point lookup on the parent PK, with the namespace literal pruning to one partition of 64; format, kind, and the soft-delete arm apply as filters on the single matched row. The statement lives in repository_parent_gate.go (unchanged by this MR, shared with npm); this MR's create path adds the first dispatch with the maven spec (format=1, kind=2), so it is included here. No anomalies.

Seed shape: namespaces=1, repositories=5000 (single namespace, one partition)

Rendered SQL:

SELECT repositories.id AS "repositories.id"
FROM public.repositories
WHERE ((((repositories.namespace_id = $1::uuid) AND (repositories.format = $2)) AND (repositories.kind = $3)) AND (repositories.soft_deleted_at IS NULL)) AND (repositories.id = $4::uuid)
LIMIT $5;

Bound args: [018f0000-0000-7000-8000-aaaaaaaaaaaa, 1, 2, 018f0000-0000-7000-8000-0000000009c4, 1]

Plan (EXPLAIN (ANALYZE, BUFFERS) output):

 Limit  (cost=0.28..8.31 rows=1 width=16) (actual time=0.010..0.011 rows=1 loops=1)
   Buffers: shared hit=3
   ->  Index Scan using repositories_p56_pkey on repositories_p56 repositories  (cost=0.28..8.31 rows=1 width=16) (actual time=0.010..0.010 rows=1 loops=1)
         Index Cond: ((id = '018f0000-0000-7000-8000-0000000009c4'::uuid) AND (namespace_id = '018f0000-0000-7000-8000-aaaaaaaaaaaa'::uuid))
         Filter: ((soft_deleted_at IS NULL) AND (format = '1'::bigint) AND (kind = '2'::bigint))
         Buffers: shared hit=3
 Planning:
   Buffers: shared hit=362
 Planning Time: 1.441 ms
 Execution Time: 0.024 ms

Timings: planning 1.441ms, execution 0.024ms, total 1.465ms.

Related to #314

Edited by Hayley Swimelar

Merge request reports

Loading
Loading