Documentation
¶
Overview ¶
Package transform provides composable SQL query rewriting via AST manipulation.
This is GoSQLX's key differentiator — enabling safe, programmatic SQL modification without string concatenation. All transforms operate on AST nodes from pkg/sql/ast and preserve structural validity, meaning a roundtrip (parse -> transform -> format) always produces well-formed SQL. Transforms are defined by the Rule interface and applied individually or composed using Apply.
Available Transforms ¶
WHERE clause: AddWhere, AddWhereFromSQL, ReplaceWhere, RemoveWhere Columns: AddColumn, RemoveColumn JOINs: AddJoin, AddJoinFromSQL ORDER BY: AddOrderBy LIMIT/OFFSET: SetLimit, SetOffset (for pagination) Tables: RenameTable, AddTableAlias
WHERE Clause Transforms ¶
// Add a filter condition using an AST node (safe for untrusted column values)
rule := transform.AddWhere(&ast.BinaryExpression{
Left: &ast.Identifier{Name: "status"},
Operator: "=",
Right: &ast.LiteralValue{Value: "active"},
})
transform.Apply(stmt, rule)
// Add a filter from a trusted SQL string
rule := transform.AddWhereFromSQL("status = 'active'")
transform.Apply(stmt, rule)
Column Transforms ¶
// Add a column to SELECT
rule := transform.AddColumn(&ast.Identifier{Name: "email"})
transform.Apply(stmt, rule)
JOIN Transforms ¶
// Add a JOIN from SQL
rule := transform.AddJoinFromSQL("LEFT JOIN orders ON orders.user_id = users.id")
transform.Apply(stmt, rule)
Pagination ¶
// Set LIMIT and OFFSET for pagination
transform.Apply(stmt,
transform.SetLimit(20),
transform.SetOffset(40),
)
Security ¶
WARNING: Functions that accept raw SQL strings (AddWhereFromSQL, AddJoinFromSQL) must not receive untrusted user input. Passing unsanitized input could produce unintended query modifications. Use parameterized queries or construct AST nodes directly (AddWhere, AddJoin) for untrusted input.
Composability ¶
Multiple transforms can be chained in a single Apply call:
transform.Apply(stmt,
transform.AddWhereFromSQL("active = true"),
transform.SetLimit(10),
transform.AddOrderBy("created_at", true),
)
Index ¶
- func Apply(stmt ast.Statement, rules ...Rule) error
- func FormatSQL(stmt ast.Statement) string
- func ParseSQL(sql string) (*ast.AST, error)
- type ErrUnsupportedStatement
- type Rule
- func AddColumn(expr ast.Expression) Rule
- func AddJoin(joinType string, table string, condition ast.Expression) Rule
- func AddJoinFromSQL(sql string) Rule
- func AddOrderBy(column string, desc bool) Rule
- func AddSelectStar() Rule
- func AddTableAlias(tableName, alias string) Rule
- func AddWhere(condition ast.Expression) Rule
- func AddWhereFromSQL(sql string) Rule
- func QualifyColumns(tableName string) Rule
- func RemoveColumn(name string) Rule
- func RemoveJoin(tableName string) Rule
- func RemoveLimit() Rule
- func RemoveOffset() Rule
- func RemoveOrderBy() Rule
- func RemoveWhere() Rule
- func ReplaceColumn(oldName, newName string) Rule
- func ReplaceTable(oldName, newName string) Rule
- func ReplaceWhere(condition ast.Expression) Rule
- func SetLimit(n int) Rule
- func SetOffset(n int) Rule
- type RuleFunc
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Apply ¶
Apply executes one or more rules against an AST statement in the order they are provided. If any rule returns a non-nil error the function stops immediately and returns that error without applying subsequent rules.
This is the primary entry point for composing transforms:
err := transform.Apply(stmt,
transform.AddWhereFromSQL("active = true"),
transform.SetLimit(100),
transform.AddOrderBy("created_at", true),
)
func FormatSQL ¶
FormatSQL converts an AST statement back into a compact SQL string using the GoSQLX formatter. It is the inverse of ParseSQL and completes the parse-transform-format round-trip.
The output uses compact style with minimal whitespace. Use this after applying transforms to obtain the final SQL to execute or log.
Example:
sql := transform.FormatSQL(stmt) // "SELECT id, name FROM users WHERE active = true LIMIT 10"
func ParseSQL ¶
ParseSQL parses a SQL string into a full AST containing all statements. This is a convenience wrapper around the tokenizer and parser pipeline that handles resource pooling automatically.
Use this function when you need an AST for subsequent Apply calls:
tree, err := transform.ParseSQL("SELECT id, name FROM users WHERE active = true")
if err != nil {
log.Fatal(err)
}
stmt := tree.Statements[0]
transform.Apply(stmt, transform.SetLimit(10))
fmt.Println(transform.FormatSQL(stmt))
Returns a *ast.AST containing all parsed statements, or an error if tokenization or parsing fails.
Types ¶
type ErrUnsupportedStatement ¶
ErrUnsupportedStatement is returned when a transform rule is applied to a statement type it does not support. For example, AddColumn only supports SelectStatement; applying it to an InsertStatement will produce this error.
Fields:
- Transform: Name of the transform function that produced the error (e.g., "AddColumn")
- Got: Human-readable name of the statement type that was rejected (e.g., "INSERT")
func (*ErrUnsupportedStatement) Error ¶
func (e *ErrUnsupportedStatement) Error() string
type Rule ¶
Rule represents an AST rewrite rule that can be applied to a single SQL statement. Rules modify the AST in-place and return an error if the transform cannot be applied (e.g., applying a SELECT-only rule to an INSERT statement).
Implement this interface to create custom transform rules:
type MyRule struct{}
func (r MyRule) Apply(stmt ast.Statement) error {
sel, ok := stmt.(*ast.SelectStatement)
if !ok {
return nil // skip non-SELECT statements
}
// modify sel in-place
return nil
}
Built-in rules are created by the constructor functions in this package (AddWhere, AddColumn, AddJoin, SetLimit, etc.). Use Apply (the package-level function) to chain multiple rules together.
func AddColumn ¶
func AddColumn(expr ast.Expression) Rule
AddColumn returns a Rule that appends a column expression to the SELECT list of a SELECT statement. The expression may be any valid AST expression node such as *ast.Identifier, *ast.AliasedExpression, or *ast.FunctionCall.
Parameters:
- expr: The column expression to append to the SELECT list
Returns ErrUnsupportedStatement for non-SELECT statements.
func AddJoin ¶
func AddJoin(joinType string, table string, condition ast.Expression) Rule
AddJoin returns a Rule that appends a JOIN clause to the SELECT statement. The join type is validated against the set of supported types; an error is returned for any unrecognised value.
Parameters:
- joinType: One of INNER, LEFT, RIGHT, FULL, CROSS, or NATURAL (case-insensitive)
- table: Name of the table to join
- condition: AST expression for the ON condition (may be nil for CROSS/NATURAL joins)
Returns an error for unrecognised join types or ErrUnsupportedStatement for non-SELECT statements.
func AddJoinFromSQL ¶
AddJoinFromSQL returns a Rule that parses a JOIN clause from SQL and adds it to a SELECT statement. The sql parameter should be a complete JOIN clause, e.g. "LEFT JOIN orders ON orders.user_id = users.id".
WARNING: sql parameter must not contain untrusted user input. This function parses raw SQL — passing unsanitized input could produce unintended query modifications. Use parameterized queries or construct AST nodes directly for untrusted input.
func AddOrderBy ¶
AddOrderBy returns a Rule that appends an ORDER BY expression to a SELECT statement. Multiple calls append additional sort keys in the order they are applied.
Parameters:
- column: Name of the column (or expression alias) to sort by
- desc: When true the sort direction is DESC; when false it is ASC
Returns ErrUnsupportedStatement for non-SELECT statements.
func AddSelectStar ¶
func AddSelectStar() Rule
AddSelectStar returns a Rule that appends a wildcard * column to the SELECT list of a SELECT statement. This is a convenience wrapper around AddColumn with an *ast.Identifier{Name: "*"} argument.
Returns ErrUnsupportedStatement for non-SELECT statements.
func AddTableAlias ¶
AddTableAlias returns a Rule that assigns an alias to a specific table in a SELECT, UPDATE, or DELETE statement. For SELECT statements the alias is applied to the matching entry in the FROM clause; for UPDATE and DELETE it sets the statement-level alias field.
Parameters:
- tableName: The table to alias (case-insensitive match)
- alias: The alias to assign
Returns ErrUnsupportedStatement for INSERT or DDL statements.
func AddWhere ¶
func AddWhere(condition ast.Expression) Rule
AddWhere returns a Rule that appends a condition to the WHERE clause of a SELECT, UPDATE, or DELETE statement. If the statement already has a WHERE clause, the new condition is combined with AND. If there is no WHERE clause, the condition becomes the sole WHERE predicate.
Use this when you have a pre-built AST expression. For raw SQL strings use AddWhereFromSQL instead.
Parameters:
- condition: An AST expression node representing the filter predicate
Returns ErrUnsupportedStatement for INSERT or DDL statements.
func AddWhereFromSQL ¶
AddWhereFromSQL returns a Rule that parses a SQL condition string and adds it as an AND condition to the existing WHERE clause.
WARNING: sql parameter must not contain untrusted user input. This function parses raw SQL — passing unsanitized input could produce unintended query modifications. Use parameterized queries or construct AST nodes directly for untrusted input.
func QualifyColumns ¶
QualifyColumns returns a Rule that prefixes every unqualified column reference in a SELECT statement's column list and WHERE clause with tableName. Only identifiers that have no existing table qualifier and whose name is not the wildcard "*" are modified.
This is useful when merging standalone column lists into multi-table queries to avoid ambiguous column reference errors.
Parameters:
- tableName: The qualifier to prepend to unqualified identifiers
Returns ErrUnsupportedStatement for non-SELECT statements.
func RemoveColumn ¶
RemoveColumn returns a Rule that removes the first column in the SELECT list that matches name. Matching is case-insensitive and checks both identifier names and expression aliases.
Returns an error if no column matching name is found, or ErrUnsupportedStatement for non-SELECT statements.
func RemoveJoin ¶
RemoveJoin returns a Rule that removes all JOIN clauses whose right-hand table name or alias matches tableName (case-insensitive). If no matching JOIN exists the statement is returned unmodified without an error.
Parameters:
- tableName: Name or alias of the table to remove from the JOIN list
Returns ErrUnsupportedStatement for non-SELECT statements.
func RemoveLimit ¶
func RemoveLimit() Rule
RemoveLimit returns a Rule that removes the LIMIT clause from a SELECT statement, allowing the query to return an unbounded number of rows.
Returns ErrUnsupportedStatement for non-SELECT statements.
func RemoveOffset ¶
func RemoveOffset() Rule
RemoveOffset returns a Rule that removes the OFFSET clause from a SELECT statement, resetting pagination to start from the first row.
Returns ErrUnsupportedStatement for non-SELECT statements.
func RemoveOrderBy ¶
func RemoveOrderBy() Rule
RemoveOrderBy returns a Rule that removes the ORDER BY clause entirely from a SELECT statement, leaving the result set in an unspecified (engine-dependent) order. This is useful when rewriting queries for intermediate stages in a pipeline where ordering should be deferred to the final step, or when performance is preferred over a stable row order.
Returns ErrUnsupportedStatement for non-SELECT statements.
func RemoveWhere ¶
func RemoveWhere() Rule
RemoveWhere returns a Rule that removes the WHERE clause from a SELECT, UPDATE, or DELETE statement. After the rule is applied, the statement will match all rows in the target table(s). Use with care in production to avoid unintentional full table scans or mass updates.
Returns ErrUnsupportedStatement for INSERT or DDL statements.
func ReplaceColumn ¶
ReplaceColumn returns a Rule that replaces every column in the SELECT list that matches oldName with a bare *ast.Identifier for newName. Matching is case-insensitive against both identifier names and aliases.
Parameters:
- oldName: Name or alias of the column to replace
- newName: Replacement identifier name
Returns ErrUnsupportedStatement for non-SELECT statements.
func ReplaceTable ¶
ReplaceTable returns a Rule that replaces all occurrences of a table name throughout a SELECT, UPDATE, or DELETE statement. The replacement covers:
- FROM clause table references
- JOIN clause left and right table references
- Column qualifiers (table.column identifiers in SELECT list, WHERE, ORDER BY)
- The table name field of UPDATE and DELETE statements
- Subquery FROM/JOIN references (recursively)
Matching is case-insensitive. This is useful for renaming tables, routing queries to shards, or switching between environments.
Parameters:
- oldName: The table name to replace (case-insensitive)
- newName: The replacement table name
Returns ErrUnsupportedStatement for INSERT or DDL statements.
func ReplaceWhere ¶
func ReplaceWhere(condition ast.Expression) Rule
ReplaceWhere returns a Rule that unconditionally replaces the WHERE clause of a SELECT, UPDATE, or DELETE statement with the given condition. Unlike AddWhere, this discards any existing WHERE predicate instead of combining with AND.
Parameters:
- condition: The new AST expression to use as the WHERE predicate
Returns ErrUnsupportedStatement for INSERT or DDL statements.
func SetLimit ¶
SetLimit returns a Rule that sets (or replaces) the LIMIT clause of a SELECT statement. Any existing LIMIT value is overwritten.
Parameters:
- n: Number of rows to return; must be >= 0. Returns an error for negative values.
Returns ErrUnsupportedStatement for non-SELECT statements.
func SetOffset ¶
SetOffset returns a Rule that sets (or replaces) the OFFSET clause of a SELECT statement. Use together with SetLimit to implement pagination.
Parameters:
- n: Number of rows to skip; must be >= 0. Returns an error for negative values.
Returns ErrUnsupportedStatement for non-SELECT statements.
type RuleFunc ¶
RuleFunc is a function type that implements the Rule interface. It allows anonymous functions and closures to be used directly as transform rules without defining a named type. All built-in rule constructors (AddWhere, AddColumn, etc.) return a RuleFunc internally.
Example:
rule := transform.RuleFunc(func(stmt ast.Statement) error {
sel, ok := stmt.(*ast.SelectStatement)
if !ok {
return nil
}
sel.Distinct = true
return nil
})