fix: coerce nil Origins to empty slice at pg store boundary
Two bugs exposed by the integration suite:
1. (production) pins.origins is NOT NULL DEFAULT '{}', but pgx
serialises a Go nil []string as SQL NULL — so every Create/Replace
whose caller omitted origins (an optional field per the IPFS
Pinning Service spec) was 500ing on the NOT NULL constraint. The
openapi/pin-service paths pass origins through verbatim, so any
client POST without "origins" hit this. Normalise nil -> []string{}
at the store boundary in both pinStore.Create and pinStore.Replace.
2. (test I introduced last commit) SET LOCAL does not accept bound
parameters; the RLS integration test was getting a 42601 syntax
error. Switch to SELECT set_config('anchorage.org_id', $1, true),
which is parameterisable and keeps the value out of the SQL string.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -422,9 +422,16 @@ func (p pinStore) Create(ctx context.Context, pn *store.Pin) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal meta: %w", err)
|
||||
}
|
||||
// pins.origins is NOT NULL DEFAULT '{}'. pgx serialises a nil
|
||||
// []string as SQL NULL, which would fail the constraint, so coerce
|
||||
// nil to an empty slice at the boundary.
|
||||
origins := pn.Origins
|
||||
if origins == nil {
|
||||
origins = []string{}
|
||||
}
|
||||
_, err = p.s.queries().CreatePin(ctx, sqlc.CreatePinParams{
|
||||
RequestID: pn.RequestID, OrgID: pn.OrgID, Cid: pn.CID,
|
||||
Name: pn.Name, Meta: meta, Origins: pn.Origins,
|
||||
Name: pn.Name, Meta: meta, Origins: origins,
|
||||
})
|
||||
if isUniqueViolation(err) {
|
||||
return store.ErrConflict
|
||||
@@ -550,6 +557,9 @@ func (p pinStore) Replace(ctx context.Context, orgID ids.OrgID, rid ids.PinID, n
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if origins == nil {
|
||||
origins = []string{}
|
||||
}
|
||||
row, err := p.s.queries().ReplacePin(ctx, sqlc.ReplacePinParams{
|
||||
RequestID: rid, OrgID: orgID, Cid: newCID, Name: name, Meta: metaBytes, Origins: origins,
|
||||
})
|
||||
|
||||
@@ -80,7 +80,10 @@ func txWithOrg(ctx context.Context, pool *pgxpool.Pool, org string, fn func(cont
|
||||
}
|
||||
defer func() { _ = tx.Rollback(ctx) }()
|
||||
if org != "" {
|
||||
if _, err := tx.Exec(ctx, "SET LOCAL anchorage.org_id = $1", org); err != nil {
|
||||
// SET LOCAL does not accept bound parameters; use set_config,
|
||||
// which does, so the org id is never string-concatenated into
|
||||
// SQL.
|
||||
if _, err := tx.Exec(ctx, "SELECT set_config('anchorage.org_id', $1, true)", org); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user