func (ctq TupleQuery) ReverseQueryTuplesFromSubjectNamespace(subjectNamespace string) datastore.ReverseTupleQuery {
	ctq.TracerAttributes = append(ctq.TracerAttributes, SubNamespaceNameKey.String(subjectNamespace))
	ctq.InitialQuery = ctq.InitialQuery.Where(sq.Eq{
		ctq.Schema.ColUsersetNamespace: subjectNamespace,
	})
	return ReverseTupleQuery{ctq}
}
ReverseQueryTuplesFromSubjectNamespace constructs a ReverseTupleQuery from this tuple query.
package common
import (
	"context"
	"fmt"
	"runtime"
	sq "github.com/Masterminds/squirrel"
	"github.com/alecthomas/units"
	v0 "github.com/authzed/authzed-go/proto/authzed/api/v0"
	v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
	"github.com/jackc/pgx/v4"
	"github.com/jackc/pgx/v4/pgxpool"
	"github.com/jzelinskie/stringz"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/trace"
	"github.com/authzed/spicedb/internal/datastore"
)
const (
	errUnableToQueryTuples = "unable to query tuples: %w"
)
var (
	ObjNamespaceNameKey = attribute.Key("authzed.com/spicedb/sql/objNamespaceName")
	ObjRelationNameKey = attribute.Key("authzed.com/spicedb/sql/objRelationName")
	ObjIDKey = attribute.Key("authzed.com/spicedb/sql/objId")
	SubNamespaceNameKey = attribute.Key("authzed.com/spicedb/sql/subNamespaceName")
	SubRelationNameKey = attribute.Key("authzed.com/spicedb/sql/subRelationName")
	SubObjectIDKey = attribute.Key("authzed.com/spicedb/sql/subObjectId")
	limitKey = attribute.Key("authzed.com/spicedb/sql/limit")
)
const DefaultSplitAtEstimatedQuerySize = 8 * units.MiB
type SchemaInformation struct {
	TableTuple          string
	ColNamespace        string
	ColObjectID         string
	ColRelation         string
	ColUsersetNamespace string
	ColUsersetObjectID  string
	ColUsersetRelation  string
}
type TransactionPreparer func(ctx context.Context, tx pgx.Tx, revision datastore.Revision) error
type TupleQuery struct {
	Conn               *pgxpool.Pool
	Schema             SchemaInformation
	PrepareTransaction TransactionPreparer
	InitialQuery             sq.SelectBuilder
	InitialQuerySizeEstimate int
	Revision                 datastore.Revision
	Tracer           trace.Tracer
	TracerAttributes []attribute.KeyValue
	DebugName                 string
	SplitAtEstimatedQuerySize units.Base2Bytes
	limit         *uint64
	usersetFilter *v1.SubjectFilter
	usersets      *[]*v0.ObjectAndRelation
}
func (ctq TupleQuery) Limit(limit uint64) datastore.CommonTupleQuery {
	if ctq.limit != nil {
		panic("Called Limit twice")
	}
	ctq.TracerAttributes = append(ctq.TracerAttributes, limitKey.Int64(int64(limit)))
	ctq.limit = &limit
	return ctq
}
func (ctq TupleQuery) WithUsersets(usersets []*v0.ObjectAndRelation) datastore.TupleQuery {
	if len(usersets) == 0 {
		panic("cannot send nil or empty usersets into query")
	}
	if ctq.usersetFilter != nil {
		panic("cannot call WithUsersets after WithSubjectFilter")
	}
	if ctq.usersets != nil {
		panic("called WithUsersets twice")
	}
	ctq.usersets = &usersets
	return ctq
}
func (ctq TupleQuery) WithSubjectFilter(filter *v1.SubjectFilter) datastore.TupleQuery {
	if filter == nil {
		panic("cannot call WithSubjectFilter with a nil filter")
	}
	if ctq.usersets != nil {
		panic("cannot call WithSubjectFilter after WithUsersets")
	}
	if ctq.usersetFilter != nil {
		panic("called WithSubjectFilter twice")
	}
	ctq.TracerAttributes = append(ctq.TracerAttributes, SubNamespaceNameKey.String(filter.SubjectType))
	ctq.TracerAttributes = append(ctq.TracerAttributes, SubObjectIDKey.String(filter.OptionalSubjectId))
	if filter.OptionalRelation != nil {
		ctq.TracerAttributes = append(ctq.TracerAttributes, SubRelationNameKey.String(filter.OptionalRelation.Relation))
	}
	ctq.usersetFilter = filter
	return ctq
}
func (ctq TupleQuery) Execute(ctx context.Context) (datastore.TupleIterator, error) {
	query := ctq.InitialQuery
	baseEstimatedDataSize := ctq.InitialQuerySizeEstimate
	if filter := ctq.usersetFilter; filter != nil {
		query = query.Where(sq.Eq{ctq.Schema.ColUsersetNamespace: filter.SubjectType})
		baseEstimatedDataSize += len(filter.SubjectType)
		if filter.OptionalSubjectId != "" {
			query = query.Where(sq.Eq{ctq.Schema.ColUsersetObjectID: filter.OptionalSubjectId})
			baseEstimatedDataSize += len(filter.OptionalSubjectId)
		}
		if filter.OptionalRelation != nil {
			normalizedRelation := stringz.DefaultEmpty(filter.OptionalRelation.Relation, datastore.Ellipsis)
			query = query.Where(sq.Eq{ctq.Schema.ColUsersetRelation: normalizedRelation})
			baseEstimatedDataSize += len(normalizedRelation)
		}
	}
	queries := []sq.SelectBuilder{}
	if ctq.usersets != nil {
		splitIndexes := []int{}
		usersets := *ctq.usersets
		currentEstimatedDataSize := baseEstimatedDataSize
		currentUsersetCount := 0
		for index, userset := range usersets {
			estimatedUsersetSize := len(userset.Namespace) + len(userset.ObjectId) + len(userset.Relation)
			if currentUsersetCount > 0 && estimatedUsersetSize+currentEstimatedDataSize >= int(ctq.SplitAtEstimatedQuerySize) {
				currentEstimatedDataSize = baseEstimatedDataSize
				splitIndexes = append(splitIndexes, index)
			}
			currentUsersetCount++
			currentEstimatedDataSize += estimatedUsersetSize
		}
		addQueryWithUsersets := func(usersets []*v0.ObjectAndRelation) {
			orClause := sq.Or{}
			if len(usersets) == 0 {
				panic("Got empty sub usersets")
			}
			for _, userset := range usersets {
				orClause = append(orClause, sq.Eq{
					ctq.Schema.ColUsersetNamespace: userset.Namespace,
					ctq.Schema.ColUsersetObjectID:  userset.ObjectId,
					ctq.Schema.ColUsersetRelation:  userset.Relation,
				})
			}
			queries = append(queries, query.Where(orClause))
		}
		startIndex := 0
		for _, splitIndex := range splitIndexes {
			addQueryWithUsersets(usersets[startIndex:splitIndex])
			startIndex = splitIndex
		}
		addQueryWithUsersets(usersets[startIndex:])
	} else {
		queries = append(queries, query)
	}
	name := fmt.Sprintf("Execute%s", ctq.DebugName)
	ctx, span := ctq.Tracer.Start(ctx, name)
	defer span.End()
	var tuples []*v0.RelationTuple
	for index, query := range queries {
		var newLimit uint64
		if ctq.limit != nil {
			newLimit = *ctq.limit - uint64(len(tuples))
			if newLimit <= 0 {
				break
			}
			query = query.Limit(newLimit)
		}
		foundTuples, err := ctq.executeQuery(ctx, query, index, newLimit)
		if err != nil {
			return nil, err
		}
		tuples = append(tuples, foundTuples...)
	}
	iter := datastore.NewSliceTupleIterator(tuples)
	runtime.SetFinalizer(iter, datastore.BuildFinalizerFunction())
	return iter, nil
}
func (ctq TupleQuery) executeQuery(ctx context.Context, query sq.SelectBuilder, index int, limit uint64) ([]*v0.RelationTuple, error) {
	ctx = datastore.SeparateContextWithTracing(ctx)
	name := fmt.Sprintf("Query-%d", index)
	ctx, span := ctq.Tracer.Start(ctx, name)
	defer span.End()
	span.SetAttributes(ctq.TracerAttributes...)
	sql, args, err := query.ToSql()
	if err != nil {
		return nil, fmt.Errorf(errUnableToQueryTuples, err)
	}
	span.AddEvent("Query converted to SQL")
	tx, err := ctq.Conn.BeginTx(ctx, pgx.TxOptions{AccessMode: pgx.ReadOnly})
	if err != nil {
		return nil, fmt.Errorf(errUnableToQueryTuples, err)
	}
	defer tx.Rollback(ctx)
	span.AddEvent("DB transaction established")
	if ctq.PrepareTransaction != nil {
		err = ctq.PrepareTransaction(ctx, tx, ctq.Revision)
		if err != nil {
			return nil, fmt.Errorf(errUnableToQueryTuples, err)
		}
		span.AddEvent("Transaction prepared")
	}
	rows, err := tx.Query(ctx, sql, args...)
	if err != nil {
		return nil, fmt.Errorf(errUnableToQueryTuples, err)
	}
	defer rows.Close()
	span.AddEvent("Query issued to database")
	var tuples []*v0.RelationTuple
	for rows.Next() {
		if limit > 0 && len(tuples) >= int(limit) {
			return tuples, nil
		}
		nextTuple := &v0.RelationTuple{
			ObjectAndRelation: &v0.ObjectAndRelation{},
			User: &v0.User{
				UserOneof: &v0.User_Userset{
					Userset: &v0.ObjectAndRelation{},
				},
			},
		}
		userset := nextTuple.User.GetUserset()
		err := rows.Scan(
			&nextTuple.ObjectAndRelation.Namespace,
			&nextTuple.ObjectAndRelation.ObjectId,
			&nextTuple.ObjectAndRelation.Relation,
			&userset.Namespace,
			&userset.ObjectId,
			&userset.Relation,
		)
		if err != nil {
			return nil, fmt.Errorf(errUnableToQueryTuples, err)
		}
		tuples = append(tuples, nextTuple)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf(errUnableToQueryTuples, err)
	}
	span.AddEvent("Tuples loaded", trace.WithAttributes(attribute.Int("tupleCount", len(tuples))))
	return tuples, nil
}
func (ctq TupleQuery) ReverseQueryTuplesFromSubjectRelation(subjectNamespace, subjectRelation string) datastore.ReverseTupleQuery {
	ctq.TracerAttributes = append(ctq.TracerAttributes, SubNamespaceNameKey.String(subjectNamespace))
	ctq.TracerAttributes = append(ctq.TracerAttributes, SubRelationNameKey.String(subjectRelation))
	ctq.InitialQuery = ctq.InitialQuery.Where(sq.Eq{
		ctq.Schema.ColUsersetNamespace: subjectNamespace,
		ctq.Schema.ColUsersetRelation:  subjectRelation,
	})
	return ReverseTupleQuery{ctq}
}
func (ctq TupleQuery) ReverseQueryTuplesFromSubject(subject *v0.ObjectAndRelation) datastore.ReverseTupleQuery {
	ctq.TracerAttributes = append(ctq.TracerAttributes, SubNamespaceNameKey.String(subject.Namespace))
	ctq.TracerAttributes = append(ctq.TracerAttributes, SubObjectIDKey.String(subject.ObjectId))
	ctq.TracerAttributes = append(ctq.TracerAttributes, SubRelationNameKey.String(subject.Relation))
	ctq.InitialQuery = ctq.InitialQuery.Where(sq.Eq{
		ctq.Schema.ColUsersetNamespace: subject.Namespace,
		ctq.Schema.ColUsersetObjectID:  subject.ObjectId,
		ctq.Schema.ColUsersetRelation:  subject.Relation,
	})
	return ReverseTupleQuery{ctq}
}