Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions pkg/query/arrow.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,14 @@ func (a *ArrowIterator) checkRightToLeft(ctx *Context, resources []Object, subje
}
rightPathCount++

// rightPath.Resource is an intermediate object from the right side
// Now check if any of our input resources connect to this intermediate via left
// rightPath.Resource is an intermediate object from the right side.
// Now check if any of our input resources connect to this intermediate via left.
// Use tuple.Ellipsis as the relation since the left side stores subjects with "..."
// (the canonical relation for direct membership with no subrelation).
intermediateAsSubject := ObjectAndRelation{
ObjectType: rightPath.Resource.ObjectType,
ObjectID: rightPath.Resource.ObjectID,
Relation: "",
Relation: tuple.Ellipsis,
}

leftSeq, err := ctx.Check(a.left, resources, intermediateAsSubject)
Expand Down
16 changes: 11 additions & 5 deletions pkg/query/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,15 @@
return r.iterResourcesWildcardImpl(ctx, subject)
}

// An empty Relation is always a bug in the caller: it must be either tuple.Ellipsis
// for direct membership or a specific subrelation string.
if subject.Relation == "" {
return nil, spiceerrors.MustBugf("IterResources called with empty subject.Relation for %s:%s; caller must use tuple.Ellipsis or a specific subrelation", subject.ObjectType, subject.ObjectID)
}

Check warning on line 456 in pkg/query/datastore.go

View check run for this annotation

Codecov / codecov/patch

pkg/query/datastore.go#L454-L456

Added lines #L454 - L456 were not covered by tests

// Check if subject relation matches what this iterator expects.
// Both the schema's expected subrelation and the query's subject relation must match exactly.
// Ellipsis is a specific relation value, not a wildcard.
// When the schema uses ellipsis ("..."), callers should pass tuple.Ellipsis — the
// MustBugf above ensures "" never reaches here — but we match on the schema value directly.
if r.base.Subrelation() != subject.Relation {
return EmptyPathSeq(), nil
}
Expand Down Expand Up @@ -712,12 +718,12 @@
}}, nil
}

// For ellipsis, return the base type with empty subrelation
// Ellipsis means "any relation on this type"
// For ellipsis, preserve the ellipsis subrelation so callers that construct
// ObjectAndRelation values from SubjectTypes get the correct relation to query with.
if r.base.Subrelation() == tuple.Ellipsis {
return []ObjectType{{
Type: r.base.Type(),
Subrelation: "",
Subrelation: tuple.Ellipsis,
}}, nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/query/datastore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,6 @@ func TestDatastoreIterator_Types(t *testing.T) {
require.NoError(err)
require.Len(subjectTypes, 1)
require.Equal("user", subjectTypes[0].Type)
require.Empty(subjectTypes[0].Subrelation) // Ellipsis returns empty subrelation
require.Equal(tuple.Ellipsis, subjectTypes[0].Subrelation) // Ellipsis is preserved as-is
})
}
Loading