snapshot of https://github.com/ory/keto.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
CI / Run lints and checks (push) Failing after 47s
CI / Run tests (push) Failing after 1s
CI / Run race tests (push) Failing after 5s
CI / Build CLI docs (push) Has been skipped
CI / Run buf actions (push) Failing after 5s
CodeQL / Analyze (go) (push) Failing after 3s
CodeQL / Analyze (javascript) (push) Failing after 2s
Docker Image Scanners / scanners (push) Failing after 3s
Format / format (push) Failing after 3s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Draft newsletter (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-16 01:08:34 +05:00
commit 9456a4e922
2560 changed files with 120911 additions and 0 deletions

View File

@@ -0,0 +1,218 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package sql
import (
"context"
"fmt"
"strings"
"github.com/gofrs/uuid"
"github.com/ory/x/otelx"
"github.com/ory/x/sqlcon"
"github.com/pkg/errors"
"go.opentelemetry.io/otel/attribute"
"github.com/ory/keto/internal/relationtuple"
"github.com/ory/keto/ketoapi"
)
type (
subjectExpandedRelationTupleRow struct {
RelationTuple
Found bool `db:"found"`
}
)
func whereSubject(sub relationtuple.Subject) (sqlFragment string, args []any, err error) {
switch s := sub.(type) {
case *relationtuple.SubjectID:
sqlFragment = "subject_id = ? AND subject_set_namespace IS NULL AND subject_set_object IS NULL AND subject_set_relation IS NULL"
args = []any{s.ID}
case *relationtuple.SubjectSet:
sqlFragment = "subject_id IS NULL AND subject_set_namespace = ? AND subject_set_object = ? AND subject_set_relation = ?"
args = []any{s.Namespace, s.Object, s.Relation}
case nil:
return "", nil, errors.WithStack(ketoapi.ErrNilSubject())
}
return sqlFragment, args, nil
}
// buildSubjectSetTypeSQL builds an optional SQL fragment and args to filter
// subject-set tuples by (namespace, relation) pairs. Returns empty string and
// nil args when allowedSubjectSets is nil (no filter applied).
func buildSubjectSetTypeSQL(allowedSubjectSets []relationtuple.SubjectSetType) (string, []any) {
if len(allowedSubjectSets) == 0 {
return "", nil
}
args := make([]any, 0, len(allowedSubjectSets)*2)
for _, f := range allowedSubjectSets {
args = append(args, f.Namespace, f.Relation)
}
return "\nAND (current.subject_set_namespace, current.subject_set_relation) IN (" + tuplePlaceholders(len(allowedSubjectSets)) + ")", args
}
// tuplePlaceholders returns n comma-separated "(?, ?)" pairs for use in a
// row-value IN list.
func tuplePlaceholders(n int) string {
return strings.TrimSuffix(strings.Repeat("(?, ?), ", n), ", ")
}
// buildFoundExpr builds the SQL expression for the found column.
//
// When allowedSubjectSets is empty, found is the result of the EXISTS subquery for every row.
// When non-empty, AllowsDirect on each entry controls whether found can be true for that type:
// - no entries have AllowsDirect: found is always false (literal, no subquery).
// - all entries have AllowsDirect: found is the EXISTS result for every row.
// - mixed: found can be true only for rows whose type is in the AllowsDirect set
func buildFoundExpr(allowedSubjectSets []relationtuple.SubjectSetType, subject relationtuple.Subject) (string, []any, error) {
var directTypes []relationtuple.SubjectSetType
if len(allowedSubjectSets) > 0 {
for _, t := range allowedSubjectSets {
if t.AllowsDirect {
directTypes = append(directTypes, t)
}
}
if len(directTypes) == 0 {
return "false", nil, nil
}
}
subjectSQL, subjectArgs, err := whereSubject(subject)
if err != nil {
return "", nil, err
}
existsQuery := fmt.Sprintf(`EXISTS(
SELECT 1 FROM keto_relation_tuples
WHERE nid = current.nid AND
namespace = current.subject_set_namespace AND
object = current.subject_set_object AND
relation = current.subject_set_relation AND
%s
)`, subjectSQL)
// No filter, or all types allow direct membership: EXISTS runs for every matched row.
if len(allowedSubjectSets) == 0 || len(directTypes) == len(allowedSubjectSets) {
return existsQuery, subjectArgs, nil
}
// Only some types allow direct membership: gate the EXISTS result to those
// types. The EXISTS subquery may still be evaluated for every returned row.
args := make([]any, 0, len(subjectArgs)+len(directTypes)*2)
args = append(args, subjectArgs...)
for _, t := range directTypes {
args = append(args, t.Namespace, t.Relation)
}
return fmt.Sprintf("(%s AND (current.subject_set_namespace, current.subject_set_relation) IN (%s))", existsQuery, tuplePlaceholders(len(directTypes))), args, nil
}
// TraverseSubjectSetExpansion gets all subject-set pointers for the object#relation and
// checks whether the requested subject is a direct member of each. When allowedSubjectSets
// is non-empty, only pointers matching the declared (namespace, relation) pairs are returned,
// and AllowsDirect on each type controls whether a direct membership check(subquery) is performed.
func (t *Persister) TraverseSubjectSetExpansion(ctx context.Context, start *relationtuple.RelationTuple, allowedSubjectSets []relationtuple.SubjectSetType) (res []*relationtuple.TraversalResult, err error) {
ctx, span := t.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.TraverseSubjectSetExpansion")
defer otelx.End(span, &err)
var rowCount int
defer func() { span.SetAttributes(attribute.Int("tuples_loaded", rowCount)) }()
foundExpr, foundArgs, err := buildFoundExpr(allowedSubjectSets, start.Subject)
if err != nil {
return nil, err
}
subjectSetFilterSQL, subjectSetFilterArgs := buildSubjectSetTypeSQL(allowedSubjectSets)
shardID := uuid.Nil
for {
var (
rows []*subjectExpandedRelationTupleRow
limit = 1000
)
queryArgs := make([]any, 0)
queryArgs = append(queryArgs, foundArgs...)
queryArgs = append(queryArgs, t.NetworkID(ctx), shardID, start.Namespace, start.Object, start.Relation)
queryArgs = append(queryArgs, subjectSetFilterArgs...)
queryArgs = append(queryArgs, limit)
err = t.Connection(ctx).RawQuery(fmt.Sprintf(`
SELECT current.shard_id AS shard_id,
current.subject_set_namespace AS namespace,
current.subject_set_object AS object,
current.subject_set_relation AS relation,
%s AS found
FROM keto_relation_tuples AS current
WHERE current.nid = ? AND
current.shard_id > ? AND
current.namespace = ? AND
current.object = ? AND
current.relation = ? AND
current.subject_id IS NULL
%s
ORDER BY current.shard_id
LIMIT ?
`, foundExpr, subjectSetFilterSQL),
queryArgs...,
).All(&rows)
if err != nil {
return nil, sqlcon.HandleError(err)
}
rowCount += len(rows)
for _, r := range rows {
to := r.ToInternal()
to.Subject = start.Subject
res = append(res, &relationtuple.TraversalResult{
From: start,
To: to,
Via: relationtuple.TraversalSubjectSetExpand,
Found: r.Found,
})
if r.Found {
return res, nil
}
}
if len(rows) == limit {
shardID = rows[limit-1].ID
} else {
break
}
}
return res, nil
}
func (t *Persister) FindTupleWithRelations(ctx context.Context, tuple *relationtuple.RelationTuple, relations []string) (_ *relationtuple.RelationTuple, err error) {
ctx, span := t.d.Tracer(ctx).Tracer().Start(ctx, "persistence.sql.FindTupleWithRelations")
defer otelx.End(span, &err)
if len(relations) == 0 {
return nil, nil
}
var rows relationTuples
query := t.queryWithNetwork(ctx)
if err := t.whereQuery(ctx, query, &relationtuple.RelationQuery{
Namespace: &tuple.Namespace,
Object: &tuple.Object,
Subject: tuple.Subject,
}); err != nil {
return nil, err
}
err = query.Where("relation IN (?)", relations).Limit(1).All(&rows)
if err != nil {
return nil, sqlcon.HandleError(err)
}
// If we got any rows back, success!
if len(rows) > 0 {
return rows[0].ToInternal(), nil
}
return nil, nil
}