r3sql

package
v0.0.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 9, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package r3sql translates r3 query types into SQL clauses.

Category: Data store dialect.

It converts r3.Filters to WHERE clauses with positional placeholders, r3.Sorts to ORDER BY expressions, and r3.PaginationSpec to LIMIT/OFFSET. This package is used by the SQL engine and drivers; most users do not import it directly.

Index

Constants

This section is empty.

Variables

View Source
var ErrSQLDialectorFailed = errors.New("sql dialector failed")

ErrSQLDialectorFailed is a generic error for r3sql dialector failures.

Functions

func AggregateExpr added in v0.0.3

func AggregateExpr(a *r3.AggregateSpec) (string, error)

AggregateExpr returns the bare SQL expression for an aggregate spec (no alias): COUNT(*), COUNT("col"), COUNT(DISTINCT "col"), SUM("col"), ... Field names are validated and quoted via SafeColumnExpr.

The expression form (rather than the alias) is what HAVING must use: PostgreSQL does not allow select-list aliases in HAVING, expressions are portable everywhere.

func AggregateToSQL added in v0.0.3

func AggregateToSQL(a *r3.AggregateSpec) (string, error)

AggregateToSQL returns the select-list item for an aggregate spec: `COUNT(*) AS "alias"`. The alias is validated and quoted like any identifier.

func GroupByToSQL added in v0.0.3

func GroupByToSQL(fields r3.Fields) ([]string, error)

GroupByToSQL validates and quotes the group-by fields, returning one quoted column expression per field.

func IsErrDialectorFailure

func IsErrDialectorFailure(err error) bool

IsErrDialectorFailure checks if the given error is a r3sql failure.

func QuoteDottedIdentifier

func QuoteDottedIdentifier(path string) string

QuoteDottedIdentifier quotes a dotted identifier path (e.g., "user.name") by quoting each segment individually and joining with dots.

Examples:

QuoteDottedIdentifier("id")            => `"id"`
QuoteDottedIdentifier("user.name")     => `"user"."name"`
QuoteDottedIdentifier("a.b.c")         => `"a"."b"."c"`

func QuoteIdentifier

func QuoteIdentifier(name string) string

QuoteIdentifier wraps a single SQL identifier in double quotes, escaping any embedded double quotes by doubling them (ANSI SQL standard).

This produces identifiers safe for PostgreSQL, SQLite, and MySQL (in ANSI mode). MySQL's default backtick quoting also accepts double-quoted identifiers when ANSI_QUOTES mode is enabled, but all major ORMs (GORM, Bun, go-pg) already handle this correctly when receiving double-quoted identifiers.

Examples:

QuoteIdentifier("id")          => `"id"`
QuoteIdentifier("user_name")   => `"user_name"`
QuoteIdentifier(`col"name`)    => `"col""name"` (escaped)

func SQLToField

func SQLToField(col SQLColumn) *r3.FieldSpec

SQLToField converts an SQLColumn to a FieldSpec.

func SQLToOperator

func SQLToOperator(op SQLClauseOperator) (r3.FilterOperatorSpec, error)

SQLToOperator converts an SQLClauseOperator back to a FilterOperatorSpec.

func SQLToPagination

func SQLToPagination(p *SQLPagination) *r3.PaginationSpec

SQLToPagination converts SQLPagination to PaginationSpec.

func SQLToSort

func SQLToSort(s SQLSort) (*r3.SortSpec, error)

SQLToSort converts an SQLSort string back to a SortSpec.

func SafeColumnExpr

func SafeColumnExpr(f *r3.FieldSpec) (string, error)

SafeColumnExpr validates a FieldSpec as a safe SQL identifier and returns the quoted SQL expression. This is the primary function used by FilterToSQL and SortToSQL to prevent SQL injection via field names.

It performs two layers of defense:

  1. Validates that the field name contains only safe identifier characters (letters, digits, underscores, dots) via r3.ValidateIdentifier.
  2. Quotes each identifier segment with double quotes to prevent any remaining edge cases.

Returns an error if the field name is invalid.

Types

type SQLClause

type SQLClause struct {
	// Clause is the raw safe SQL string (e.g. "name = ?")
	Clause string

	// Args is a slice of arguments to be bound to the SQL clause.
	// Len of arguments must match the number of placeholders in the clause.
	Args []any

	// Joins is a slice of joins that are required for the clause.
	Joins []SQLColumn
}

SQLClause represents a r3.Filter that can be converted into SQL Clause (used by gorm, sqlx, squirrel, etc.)

func CursorToSQLClause

func CursorToSQLClause(values r3.CursorValues, sorts r3.Sorts, direction r3.CursorDirection) (SQLClause, error)

CursorToSQLClause generates a keyset pagination WHERE clause from decoded cursor values and the current sort specification.

For a single sort column (e.g., created_at DESC), it produces:

WHERE "created_at" < ?

For multiple sort columns (e.g., created_at DESC, id ASC), it produces a compound condition:

WHERE ("created_at" < ?) OR ("created_at" = ? AND "id" > ?)

The direction parameter controls whether comparison operators are > (forward) or < (backward), flipped based on the sort direction of each column.

func FilterToSQL

func FilterToSQL(cf *r3.FilterSpec) (SQLClause, error)

FilterToSQL converts a FilterSpec to an SQLClause.

func HavingToSQL added in v0.0.3

func HavingToSQL(having r3.Filters, exprByName map[string]string) (SQLClause, error)

HavingToSQL converts Having filters (implicitly ANDed) into one SQLClause. Each leaf's field name is resolved through exprByName — aggregate aliases map to their full expressions (SUM("col")), group fields to their quoted columns — because HAVING cannot reference select-list aliases portably. A name absent from the map is an error (the caller validates first via r3.Schema.ValidateAggregateQuery, so this is a belt-and-braces check).

type SQLClauseOperator

type SQLClauseOperator string

SQLClauseOperator represents a SQL operator. e.g. "=", "LIKE", "IN".

const (
	SQLClauseOperatorEq      SQLClauseOperator = "="
	SQLClauseOperatorNe      SQLClauseOperator = "!="
	SQLClauseOperatorGt      SQLClauseOperator = ">"
	SQLClauseOperatorGte     SQLClauseOperator = ">="
	SQLClauseOperatorLt      SQLClauseOperator = "<"
	SQLClauseOperatorLte     SQLClauseOperator = "<="
	SQLClauseOperatorLike    SQLClauseOperator = "LIKE"
	SQLClauseOperatorNotLike SQLClauseOperator = "NOT LIKE"
	SQLClauseOperatorILike   SQLClauseOperator = "ILIKE"
	SQLClauseOperatorIn      SQLClauseOperator = "IN"
	SQLClauseOperatorNotIn   SQLClauseOperator = "NOT IN"
)

List of supported SQL operators.

func OperatorToSQL

func OperatorToSQL(op r3.FilterOperatorSpec) (SQLClauseOperator, error)

OperatorToSQL converts a FilterOperatorSpec to an SQLClauseOperator.

func (SQLClauseOperator) String

func (op SQLClauseOperator) String() string

String so we implement fmt.Stringer.

func (SQLClauseOperator) ToSQL

func (op SQLClauseOperator) ToSQL() string

ToSQL is the string representation that will be used inside generated SQL.

type SQLClauses

type SQLClauses []SQLClause

SQLClauses is a slice of SQLClause.

func FiltersToSQL

func FiltersToSQL(filters r3.Filters) (SQLClauses, error)

FiltersToSQL translates a list of r3.Filters into SQLClauses.

func FiltersToSQLClauses deprecated

func FiltersToSQLClauses(filters r3.Filters) (SQLClauses, error)

FiltersToSQLClauses is an alias for FiltersToSQL for backward compatibility.

Deprecated: Use FiltersToSQL instead.

func (SQLClauses) Joins

func (cs SQLClauses) Joins() []SQLColumn

Joins is a combined getter for each SQLClause.Joins It will return duplicates-free list.

type SQLColumn

type SQLColumn string

SQLColumn stands for the string type that holds name of the column.

func FieldToSQL

func FieldToSQL(f *r3.FieldSpec) SQLColumn

FieldToSQL converts a FieldSpec to an SQLColumn (raw, no quoting). This does NOT validate or quote the identifier. For safe SQL generation from user-supplied field names, use SafeColumnExpr instead.

func (SQLColumn) String

func (sc SQLColumn) String() string

String returns the column name as a string.

type SQLPagination

type SQLPagination struct {
	Limit  int
	Offset int
}

SQLPagination represents pagination in SQL format using LIMIT and OFFSET.

func NewSQLPagination

func NewSQLPagination(limit, offset int) *SQLPagination

NewSQLPagination creates a new SQLPagination with given limit and offset.

func PaginationToSQL

func PaginationToSQL(p *r3.PaginationSpec) *SQLPagination

PaginationToSQL converts a PaginationSpec to SQLPagination.

func (*SQLPagination) String

func (sp *SQLPagination) String() string

String returns the string representation of SQL pagination.

func (*SQLPagination) ToLimitOffset

func (sp *SQLPagination) ToLimitOffset() (int, int)

ToLimitOffset returns the limit and offset values.

type SQLSort

type SQLSort string

SQLSort stands for the string type that holds a SQL sort clause. Example: "name ASC", "created_at DESC NULLS LAST".

func SortToSQL

func SortToSQL(s *r3.SortSpec) (SQLSort, error)

SortToSQL converts a SortSpec to an SQLSort.

func SortsToSQL

func SortsToSQL(ss r3.Sorts) ([]SQLSort, error)

SortsToSQL converts a slice of SortSpec to a slice of SQLSort.

func (SQLSort) String

func (ss SQLSort) String() string

String returns the SQL sort expression as a string.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL