Skip to content

Commit 6307dd1

Browse files
committed
fix(query): fix arrow reversal tagging the subrelation incorrectly
1 parent b275f07 commit 6307dd1

File tree

3 files changed

+17
-9
lines changed

3 files changed

+17
-9
lines changed

pkg/query/arrow.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,14 @@ func (a *ArrowIterator) checkRightToLeft(ctx *Context, resources []Object, subje
142142
}
143143
rightPathCount++
144144

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

153155
leftSeq, err := ctx.Check(a.left, resources, intermediateAsSubject)

pkg/query/datastore.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -449,9 +449,15 @@ func (r *DatastoreIterator) IterResourcesImpl(ctx *Context, subject ObjectAndRel
449449
return r.iterResourcesWildcardImpl(ctx, subject)
450450
}
451451

452+
// An empty Relation is always a bug in the caller: it must be either tuple.Ellipsis
453+
// for direct membership or a specific subrelation string.
454+
if subject.Relation == "" {
455+
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)
456+
}
457+
452458
// Check if subject relation matches what this iterator expects.
453-
// Both the schema's expected subrelation and the query's subject relation must match exactly.
454-
// Ellipsis is a specific relation value, not a wildcard.
459+
// When the schema uses ellipsis ("..."), callers should pass tuple.Ellipsis — the
460+
// MustBugf above ensures "" never reaches here — but we match on the schema value directly.
455461
if r.base.Subrelation() != subject.Relation {
456462
return EmptyPathSeq(), nil
457463
}
@@ -712,12 +718,12 @@ func (r *DatastoreIterator) SubjectTypes() ([]ObjectType, error) {
712718
}}, nil
713719
}
714720

715-
// For ellipsis, return the base type with empty subrelation
716-
// Ellipsis means "any relation on this type"
721+
// For ellipsis, preserve the ellipsis subrelation so callers that construct
722+
// ObjectAndRelation values from SubjectTypes get the correct relation to query with.
717723
if r.base.Subrelation() == tuple.Ellipsis {
718724
return []ObjectType{{
719725
Type: r.base.Type(),
720-
Subrelation: "",
726+
Subrelation: tuple.Ellipsis,
721727
}}, nil
722728
}
723729

pkg/query/datastore_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,6 @@ func TestDatastoreIterator_Types(t *testing.T) {
414414
require.NoError(err)
415415
require.Len(subjectTypes, 1)
416416
require.Equal("user", subjectTypes[0].Type)
417-
require.Empty(subjectTypes[0].Subrelation) // Ellipsis returns empty subrelation
417+
require.Equal(tuple.Ellipsis, subjectTypes[0].Subrelation) // Ellipsis is preserved as-is
418418
})
419419
}

0 commit comments

Comments
 (0)