Documentation
¶
Overview ¶
Package sqldsl provides a domain-specific SQL DSL for generating Melange authorization queries. It models authorization concepts directly rather than generic SQL syntax.
Index ¶
- Variables
- func Ident(name string) string
- func IndentLines(input, indent string) string
- func ListObjectsFunctionName(objectType, relation string) string
- func ListSubjectsFunctionName(objectType, relation string) string
- func Optf(cond bool, format string, args ...any) string
- func RenderBlocks(blocks []QueryBlock) string
- func RenderUnionBlocks(blocks []QueryBlock) string
- func Sqlf(format string, args ...any) string
- func WrapWithPagination(query, idColumn string) string
- func WrapWithPaginationWildcardFirst(query string) string
- type Add
- type Alias
- type AndExpr
- type ArrayAppend
- type ArrayContains
- type ArrayLength
- type ArrayLiteral
- type Bool
- type CTEDef
- type CaseExpr
- type CaseWhen
- type Col
- type CommentedSQL
- type Concat
- type EmptyArray
- type Eq
- type Exists
- type Expr
- type Func
- type FuncCallEq
- func InternalCheckCall(subjectType, subjectID Expr, relation string, ...) FuncCallEq
- func InternalPermissionCheckCall(relation, objectType string, objectID, visited Expr) FuncCallEq
- func NoWildcardPermissionCheckCall(relation, objectType string, subjectID, objectID Expr) FuncCallEq
- func SpecializedCheckCall(funcName string, subjectType, subjectID, objectID, visited Expr) FuncCallEq
- type FuncCallNe
- type FunctionCallExpr
- type Gt
- type Gte
- type HasUserset
- type In
- type InFunctionSelect
- type Int
- type IntersectSubquery
- type IsNotNull
- type IsNull
- type IsWildcard
- type JoinClause
- type LateralFunction
- type Lit
- type Lt
- type Lte
- type Ne
- type NoUserset
- type NotExists
- type NotExpr
- type NotIn
- type Null
- type ObjectRef
- type OrExpr
- type Param
- type Paren
- type Position
- type QueryBlock
- type Raw
- type SQLer
- type SelectIntoVar
- type SelectStmt
- type Sub
- type SubjectRef
- type Substring
- type SubstringUsersetRelation
- type TableExpr
- type TableRef
- type TypedValuesTable
- type UnionAll
- type UsersetNormalized
- type UsersetObjectID
- type UsersetRelation
- type ValuesRow
- type ValuesTable
- type WithCTE
Constants ¶
This section is empty.
Variables ¶
var ( SubjectType = Param("p_subject_type") SubjectID = Param("p_subject_id") ObjectType = Param("p_object_type") ObjectID = Param("p_object_id") Visited = Param("p_visited") )
Common parameter constants.
Functions ¶
func Ident ¶
Ident sanitizes an identifier for use in SQL. Replaces non-alphanumeric characters with underscores.
func IndentLines ¶
IndentLines adds the given indent prefix to each line of input.
func ListObjectsFunctionName ¶
ListObjectsFunctionName generates a list_TYPE_RELATION_objects function name.
func ListSubjectsFunctionName ¶
ListSubjectsFunctionName generates a list_TYPE_RELATION_subjects function name.
func Optf ¶
Optf returns formatted string if condition is true, empty string otherwise. Useful for optional SQL clauses.
func RenderBlocks ¶
func RenderBlocks(blocks []QueryBlock) string
RenderBlocks renders multiple query blocks sequentially. Each block is indented and comments are rendered as SQL comments.
func RenderUnionBlocks ¶
func RenderUnionBlocks(blocks []QueryBlock) string
RenderUnionBlocks renders query blocks joined with UNION. Each block is indented and comments are rendered as SQL comments.
func Sqlf ¶
Sqlf formats SQL with automatic dedenting and blank line removal. The SQL shape is visible in the format string.
func WrapWithPagination ¶
WrapWithPagination wraps a query with cursor-based pagination. The idColumn parameter specifies which column to use for ordering and cursoring.
func WrapWithPaginationWildcardFirst ¶
WrapWithPaginationWildcardFirst wraps a query for list_subjects with wildcard-first ordering. Wildcards ('*') are sorted before all other subject IDs to ensure consistent pagination. Uses a compound sort key: (is_not_wildcard, subject_id) where is_not_wildcard is 0 for '*', 1 otherwise.
Types ¶
type Alias ¶
Alias wraps an expression with an alias (expr AS alias).
type AndExpr ¶
type AndExpr struct {
Exprs []Expr
}
AndExpr represents a logical AND of multiple expressions.
type ArrayAppend ¶
type ArrayAppend struct {
Array Expr // The base array expression
Values []Expr // Values to append (rendered as ARRAY[...])
}
ArrayAppend represents array concatenation: arr || ARRAY[values]. Used for building the visited array in recursive check functions.
Example:
ArrayAppend{
Array: Param("p_visited"),
Values: []Expr{Concat{Parts: []Expr{Lit("doc:"), ObjectID, Lit(":viewer")}}},
}
Renders: p_visited || ARRAY['doc:' || p_object_id || ':viewer']
func VisitedWithKey ¶
func VisitedWithKey(objectType, relation string, objectID Expr) ArrayAppend
VisitedWithKey creates the p_visited || ARRAY[key] expression. This is the standard pattern for passing updated visited arrays to recursive calls.
Example:
VisitedWithKey("document", "viewer", ObjectID)
Renders: p_visited || ARRAY['document:' || p_object_id || ':viewer']
type ArrayContains ¶
ArrayContains represents the ANY() check: value = ANY(array).
type ArrayLength ¶
ArrayLength represents array_length(arr, dim).
type ArrayLiteral ¶
type ArrayLiteral struct {
Values []Expr
}
ArrayLiteral represents a SQL array literal: ARRAY[values].
type CTEDef ¶
type CTEDef struct {
Name string // CTE name (e.g., "accessible", "base_results")
Columns []string // Optional column names (e.g., ["object_id", "depth"])
Query SQLer // The CTE query body
}
CTEDef represents a single Common Table Expression definition. Used within WithCTE to define named subqueries.
type CommentedSQL ¶
type CommentedSQL struct {
Comment string // Comment text (without -- prefix)
Query SQLer // The query to render after the comment
}
CommentedSQL wraps a query with a SQL comment prefix. Useful for adding descriptive comments to parts of a UNION or CTE body.
Example:
CommentedSQL{Comment: "Base case: seed with starting value", Query: baseQuery}
Renders:
-- Base case: seed with starting value <base query>
func MultiLineComment ¶
func MultiLineComment(comments []string, query SQLer) CommentedSQL
MultiLineComment creates a CommentedSQL with multiple comment lines. The query follows after all comment lines.
func (CommentedSQL) SQL ¶
func (c CommentedSQL) SQL() string
SQL renders the comment followed by the query.
type EmptyArray ¶
type EmptyArray struct{}
EmptyArray represents an empty text array (ARRAY[]::TEXT[]).
type Exists ¶
type Exists struct {
Query interface{ SQL() string }
}
Exists represents an EXISTS subquery.
func ExistsExpr ¶
func ExistsExpr(stmt SelectStmt) Exists
ExistsExpr creates an Exists expression from a SelectStmt.
type Expr ¶
type Expr interface {
SQL() string
}
Expr is the interface that all SQL expression types implement.
func NormalizedUsersetSubject ¶
NormalizedUsersetSubject combines the object_id from a userset with a new relation. Example: split_part(subject_id, '#', 1) || '#' || v_filter_relation
func SubjectIDMatch ¶
SubjectIDMatch creates a condition for matching subject IDs. When allowWildcard is true, matches exact ID or wildcard tuples. When allowWildcard is false, matches exact ID and excludes wildcard tuples.
func VisitedKey ¶
VisitedKey creates the standard visited key expression for cycle detection. The key format is: 'objectType:' || objectID || ':relation'
Example:
VisitedKey("document", "viewer", ObjectID)
Renders the expression: 'document:' || p_object_id || ':viewer'
type FuncCallEq ¶
type FuncCallEq struct {
FuncName string
Args []Expr
Value Expr // The value to compare against (typically Int(1) or Int(0))
}
FuncCallEq compares a function call result to a value. Used for authorization check expressions like "check_permission(...) = 1".
Example:
FuncCallEq{
FuncName: "check_doc_viewer",
Args: []Expr{SubjectType, SubjectID, ObjectID, Visited},
Value: Int(1),
}
Renders: check_doc_viewer(p_subject_type, p_subject_id, p_object_id, p_visited) = 1
func InternalCheckCall ¶
func InternalCheckCall(subjectType, subjectID Expr, relation string, parentType, parentID, visited Expr) FuncCallEq
InternalCheckCall creates a check_permission_internal call with explicit subject/object types. Used in parent relation (TTU) checks where the linking tuple provides the parent object.
Example:
InternalCheckCall(SubjectType, SubjectID, "viewer", Col{Table: "link", Column: "subject_type"}, Col{Table: "link", Column: "subject_id"}, visited)
Renders: check_permission_internal(p_subject_type, p_subject_id, 'viewer', link.subject_type, link.subject_id, <visited>) = 1
func InternalPermissionCheckCall ¶
func InternalPermissionCheckCall(relation, objectType string, objectID, visited Expr) FuncCallEq
InternalPermissionCheckCall creates a check_permission_internal function call comparison. This is the most common pattern in the codebase for recursive permission checks.
Example:
InternalPermissionCheckCall("viewer", "document", Col{Table: "t", Column: "object_id"}, Visited)
Renders: check_permission_internal(p_subject_type, p_subject_id, 'viewer', 'document', t.object_id, p_visited) = 1
func NoWildcardPermissionCheckCall ¶
func NoWildcardPermissionCheckCall(relation, objectType string, subjectID, objectID Expr) FuncCallEq
NoWildcardPermissionCheckCall creates a check_permission_no_wildcard function call.
func SpecializedCheckCall ¶
func SpecializedCheckCall(funcName string, subjectType, subjectID, objectID, visited Expr) FuncCallEq
SpecializedCheckCall creates a call to a specialized check function. Used for implied relations and parent relation checks.
Example:
SpecializedCheckCall("check_doc_owner", SubjectType, SubjectID, ObjectID, Visited)
Renders: check_doc_owner(p_subject_type, p_subject_id, p_object_id, p_visited) = 1
type FuncCallNe ¶
FuncCallNe compares a function call result for inequality. Used for negative authorization checks like "check_permission(...) <> 1".
func (FuncCallNe) SQL ¶
func (f FuncCallNe) SQL() string
SQL renders the function call not-equal comparison.
type FunctionCallExpr ¶
type FunctionCallExpr struct {
Name string // Function name
Args []Expr // Function arguments
Alias string // Table alias for the result
}
FunctionCallExpr represents a function call that can be used as a table expression. Used for LATERAL joins with table-returning functions.
func (FunctionCallExpr) TableAlias ¶
func (f FunctionCallExpr) TableAlias() string
TableAlias implements TableExpr.
func (FunctionCallExpr) TableSQL ¶
func (f FunctionCallExpr) TableSQL() string
TableSQL implements TableExpr.
type HasUserset ¶
type HasUserset struct {
Source Expr
}
HasUserset checks if an expression contains a userset marker (#).
func (HasUserset) SQL ¶
func (h HasUserset) SQL() string
type InFunctionSelect ¶
type InFunctionSelect struct {
Expr Expr // The expression to check (left side of IN)
FuncName string // The function to call
Args []Expr // Function arguments
Alias string // Alias for the function result
SelectCol string // Column to select from the function result
}
InFunctionSelect represents "expr IN (SELECT column FROM func(args...) alias)". Used for checking membership against results of a list function.
Example:
InFunctionSelect{
Expr: Col{Table: "t", Column: "subject_id"},
FuncName: "list_doc_viewer_objects",
Args: []Expr{SubjectType, SubjectID, Null{}, Null{}},
Alias: "obj",
SelectCol: "object_id",
}
Renders: t.subject_id IN (SELECT obj.object_id FROM list_doc_viewer_objects(p_subject_type, p_subject_id, NULL, NULL) obj)
func (InFunctionSelect) SQL ¶
func (i InFunctionSelect) SQL() string
SQL renders the IN subquery expression.
type IntersectSubquery ¶
type IntersectSubquery struct {
Queries []SelectStmt
Alias string
}
IntersectSubquery represents an INTERSECT of multiple queries as a subquery. Used for intersection groups where all parts must be satisfied.
Example: IntersectSubquery{Queries: [q1, q2], Alias: "ig"} Renders: (q1 INTERSECT q2) AS ig
func (IntersectSubquery) TableAlias ¶
func (i IntersectSubquery) TableAlias() string
TableAlias implements TableExpr.
func (IntersectSubquery) TableSQL ¶
func (i IntersectSubquery) TableSQL() string
TableSQL renders the INTERSECT subquery as a FROM clause table expression.
type IsWildcard ¶
type IsWildcard struct {
Source Expr
}
IsWildcard checks if an expression equals the wildcard value "*".
func (IsWildcard) SQL ¶
func (w IsWildcard) SQL() string
type JoinClause ¶
type JoinClause struct {
Type string // "INNER", "LEFT", etc.
Table string // Deprecated: use TableExpr instead
TableExpr TableExpr // Preferred: typed table expression
Alias string // Deprecated: use TableExpr's alias instead
On Expr
}
JoinClause represents a SQL JOIN clause.
type LateralFunction ¶
LateralFunction represents a LATERAL function call in a JOIN. Example: LateralFunction{Name: "list_doc_viewer_subjects", Args: []Expr{...}, Alias: "s"} Renders: LATERAL list_doc_viewer_subjects(...) AS s
func (LateralFunction) SQL ¶
func (l LateralFunction) SQL() string
SQL renders the LATERAL function expression.
func (LateralFunction) TableAlias ¶
func (l LateralFunction) TableAlias() string
TableAlias implements TableExpr.
func (LateralFunction) TableSQL ¶
func (l LateralFunction) TableSQL() string
TableSQL implements TableExpr.
type Lit ¶
type Lit string
Lit represents a literal string value (auto-quoted with single quotes).
type NoUserset ¶
type NoUserset struct {
Source Expr
}
NoUserset checks if an expression does NOT contain a userset marker (#).
type NotExists ¶
type NotExists struct {
Query interface{ SQL() string }
}
NotExists represents a NOT EXISTS subquery.
type ObjectRef ¶
ObjectRef represents an object reference (type + id).
func LiteralObject ¶
LiteralObject creates an ObjectRef with literal type and expression ID.
type OrExpr ¶
type OrExpr struct {
Exprs []Expr
}
OrExpr represents a logical OR of multiple expressions.
type Param ¶
type Param string
Param represents a function parameter (e.g., p_subject_type, p_object_id).
type Position ¶
Position represents SQL position(needle in haystack). Returns the position of the first occurrence of needle in haystack.
type QueryBlock ¶
type QueryBlock struct {
Comments []string // Comment lines (without -- prefix)
Query SQLer // The query as typed DSL (SelectStmt, Raw, etc.)
}
QueryBlock represents a query with optional comments. Used to build UNION queries with descriptive comments for each branch.
type SQLer ¶
type SQLer interface {
SQL() string
}
SQLer is an interface for types that can render SQL. SelectStmt, Raw, and other query types implement this interface.
type SelectIntoVar ¶
type SelectIntoVar struct {
Query SQLer // The query (e.g., a CTE)
Variable string // Variable name to select into
}
SelectIntoVar wraps a query for use with PL/pgSQL SELECT INTO. Appends "INTO <variable>" after the query.
Example:
SelectIntoVar{Query: cteQuery, Variable: "v_max_depth"}
Renders:
<query> INTO v_max_depth
func (SelectIntoVar) SQL ¶
func (s SelectIntoVar) SQL() string
SQL renders the query with INTO clause appended.
type SelectStmt ¶
type SelectStmt struct {
Distinct bool
Columns []string // Deprecated: use ColumnExprs instead
ColumnExprs []Expr // Preferred: typed column expressions
From string // Deprecated: use FromExpr instead
FromExpr TableExpr // Preferred: typed table expression
Alias string // Deprecated: use FromExpr's alias instead
Joins []JoinClause
Where Expr
Limit int
}
SelectStmt represents a SELECT query.
func (SelectStmt) NotExists ¶
func (s SelectStmt) NotExists() string
NotExists wraps a query in NOT EXISTS(...).
type SubjectRef ¶
SubjectRef represents a subject reference (type + id).
func SubjectParams ¶
func SubjectParams() SubjectRef
SubjectParams creates a SubjectRef from the standard function parameters.
type Substring ¶
Substring represents SQL substring(source from start [for length]). If For is nil, renders substring(source from start). If For is provided, renders substring(source from start for length).
type SubstringUsersetRelation ¶
type SubstringUsersetRelation struct {
Source Expr
}
SubstringUsersetRelation extracts the relation portion after the '#' marker. Used when the input contains a userset marker and we need just the relation.
func (SubstringUsersetRelation) SQL ¶
func (s SubstringUsersetRelation) SQL() string
type TableExpr ¶
type TableExpr interface {
// TableSQL returns the SQL for use in FROM/JOIN clauses.
TableSQL() string
// TableAlias returns the alias if any (empty string if none).
TableAlias() string
}
TableExpr is the interface for table expressions in FROM and JOIN clauses. Types that can be used as table sources implement this interface.
func ClosureTable ¶
ClosureTable returns a typed closure VALUES table.
func UsersetTable ¶
UsersetTable returns a typed userset VALUES table.
type TableRef ¶
TableRef wraps a raw table name for use as a TableExpr.
type TypedValuesTable ¶
type TypedValuesTable struct {
Rows []ValuesRow // Typed expression rows
Alias string // Table alias
Columns []string // Column names
}
TypedValuesTable represents a VALUES clause with typed expression rows. Unlike ValuesTable which uses a pre-formatted string, TypedValuesTable uses structured ValuesRow elements that render via the Expr DSL.
Example:
TypedValuesTable{
Rows: []ValuesRow{{Lit("doc"), Lit("viewer"), Lit("editor")}},
Alias: "c",
Columns: []string{"object_type", "relation", "satisfying_relation"},
}
Renders: (VALUES ('doc', 'viewer', 'editor')) AS c(object_type, relation, satisfying_relation)
func TypedClosureValuesTable ¶
func TypedClosureValuesTable(rows []ValuesRow, alias string) TypedValuesTable
TypedClosureValuesTable creates a typed closure VALUES table. The table has columns: object_type, relation, satisfying_relation
func TypedUsersetValuesTable ¶
func TypedUsersetValuesTable(rows []ValuesRow, alias string) TypedValuesTable
TypedUsersetValuesTable creates a typed userset VALUES table. The table has columns: object_type, relation, subject_type, subject_relation
func (TypedValuesTable) SQL ¶
func (v TypedValuesTable) SQL() string
SQL renders the typed VALUES table expression.
func (TypedValuesTable) TableAlias ¶
func (v TypedValuesTable) TableAlias() string
TableAlias implements TableExpr.
func (TypedValuesTable) TableSQL ¶
func (v TypedValuesTable) TableSQL() string
TableSQL implements TableExpr.
type UnionAll ¶
type UnionAll struct {
Queries []SQLer
Indent string // Optional indent prefix for each query (default: empty)
}
UnionAll represents multiple queries combined with UNION ALL. Each query is rendered and joined with UNION ALL.
Example:
UnionAll{Queries: []SQLer{query1, query2}}
Renders:
query1 UNION ALL query2
type UsersetNormalized ¶
UsersetNormalized replaces the relation in a userset with a new relation. Example: "group:1#admin" with relation "member" -> "group:1#member"
func (UsersetNormalized) SQL ¶
func (u UsersetNormalized) SQL() string
type UsersetObjectID ¶
type UsersetObjectID struct {
Source Expr
}
UsersetObjectID extracts the object ID: "group:1#member" -> "group:1"
func (UsersetObjectID) SQL ¶
func (u UsersetObjectID) SQL() string
type UsersetRelation ¶
type UsersetRelation struct {
Source Expr
}
UsersetRelation extracts the relation: "group:1#member" -> "member"
func (UsersetRelation) SQL ¶
func (u UsersetRelation) SQL() string
type ValuesRow ¶
type ValuesRow []Expr
ValuesRow represents a single row in a VALUES clause as typed expressions. Each element in the slice corresponds to a column value.
type ValuesTable ¶
type ValuesTable struct {
Values string // The VALUES content (e.g., "('a', 'b'), ('c', 'd')")
Alias string // Table alias (e.g., "c")
Columns []string // Column names (e.g., ["object_type", "relation"])
}
ValuesTable represents a VALUES clause as a table expression. Used to inline data like closure values without database tables.
Example: ValuesTable{Values: "('doc', 'viewer', 'editor')", Alias: "c", Columns: []string{"object_type", "relation", "satisfying_relation"}} Renders: (VALUES ('doc', 'viewer', 'editor')) AS c(object_type, relation, satisfying_relation)
func (ValuesTable) TableAlias ¶
func (v ValuesTable) TableAlias() string
TableAlias implements TableExpr.
type WithCTE ¶
type WithCTE struct {
Recursive bool // If true, renders WITH RECURSIVE
CTEs []CTEDef // One or more CTE definitions
Query SQLer // The final SELECT that uses the CTEs
}
WithCTE represents a WITH clause wrapping a final query. Supports both regular and recursive CTEs.
Example:
WithCTE{
Recursive: true,
CTEs: []CTEDef{{Name: "accessible", Columns: []string{"object_id", "depth"}, Query: cteQuery}},
Query: finalSelect,
}
Renders:
WITH RECURSIVE accessible(object_id, depth) AS (
<cte query>
)
<final query>
func MultiCTE ¶
MultiCTE creates a WITH clause with multiple CTEs. Useful for complex queries with subject_pool, base_results, has_wildcard, etc.
func RecursiveCTE ¶
RecursiveCTE is a convenience constructor for a single recursive CTE.