pgxcel

package module
v0.0.0-...-43e4afd Latest Latest
Warning

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

Go to latest
Published: Apr 30, 2026 License: MIT Imports: 7 Imported by: 0

README

pgxcel

Go Reference License Go

pgxcel converts a checked CEL AST into a Postgres WHERE fragment with positional bind placeholders. It is deliberately small: one walker over the standard CEL expression protobuf, a fail-closed identifier allow-list, and time.Time / time.Duration bindings for the timestamp(...) / duration(...) literals.

The package accepts any *cel.Ast regardless of how it was produced. That includes ASTs translated back from an AIP-160 filter via cel.CheckedExprToAst, so the same transpiler powers both direct CEL and AIP filtering on top of Postgres.

Installation

go get github.com/pgx-contrib/pgxcel

Usage

env, _ := cel.NewEnv(
    cel.Variable("name", cel.StringType),
    cel.Variable("age", cel.IntType),
)
ast, iss := env.Compile(`name == "Alice" && age > 30`)
if iss.Err() != nil {
    return iss.Err()
}

columns := map[string]string{
    "name": "users.name",
    "age":  "users.age",
}
where, args, err := pgxcel.Transpile(ast, pgxcel.WithColumns(columns))
// where: ("users"."name" = $1 AND "users"."age" > $2)
// args:  []any{"Alice", int64(30)}
Options
  • pgxcel.WithColumns(map[string]string) — the path → DB-column allow-list. Lookup is fail-closed: any identifier the AST references that is not in the map causes Transpile to return an error. When omitted, every ident in the AST errors. Never feed user input as a column name; the value of each map entry is emitted into the SQL after only identifier quoting.
  • pgxcel.WithFunctions(map[string]string) — alias → canonical function-name map applied before dispatch. Use it to feed in ASTs produced by parsers other than cel-go (for example einride/aip-go emits "=" / "AND" / "NOT" instead of the cel-go operator names). Unknown aliases pass through unchanged.
  • pgxcel.WithParamOffset(int) — the first placeholder number. Defaults to 1. Use a higher value when splicing the fragment into a query that already has bound values.

A nil ast returns ("", nil, nil). An unchecked ast (ast.IsChecked() == false) returns an error.

Operator coverage

CEL Postgres fragment
==, !=, <, <=, >, >= col op $N (or col op col)
&&, || (lhs AND rhs) / (lhs OR rhs)
! (NOT expr)
x in [a, b, c] x IN ($1, $2, $3) (empty → FALSE)
s.contains(x) s LIKE '%' || $N || '%'
s.startsWith(x) s LIKE $N || '%'
s.endsWith(x) s LIKE '%' || $N
s.matches(re) s ~ $N (POSIX regex)
timestamp("2025-01-02T03:04:05Z") $N bound as time.Time
duration("1h30m") $N bound as time.Duration
unary -<literal> bound as signed numeric literal

Development

go test ./...
go vet ./...

License

MIT

Documentation

Overview

Package pgxcel converts checked CEL expressions into PostgreSQL WHERE fragments with positional bind placeholders.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Transpile

func Transpile(ast *cel.Ast, opts ...Option) (string, []any, error)

Transpile turns ast into a Postgres WHERE fragment (no enclosing parentheses) plus the bound positional args.

ast must be checked (ast.IsChecked() == true). An unchecked AST returns an error. A nil ast returns ("", nil, nil) — the caller decides whether to omit the WHERE keyword.

Configure resolution and placeholder numbering via WithColumns and WithParamOffset.

Types

type Option

type Option func(*config)

Option configures a Transpile call.

func WithColumns

func WithColumns(columns map[string]string) Option

WithColumns supplies the fail-closed AIP-path → DB-column allow-list. Any ident in the AST that is absent from columns causes an error. Dotted paths (e.g. "address.city") are looked up by their full path. When omitted, every ident in the AST errors.

func WithFunctions

func WithFunctions(functions map[string]string) Option

WithFunctions registers aliases that are normalized to canonical CEL function names before dispatch. Use this to feed in ASTs produced by parsers other than cel-go — for example einride/aip-go emits "=" / "AND" / "NOT" instead of operators.Equals / LogicalAnd / LogicalNot.

Each map entry is alias → canonical, where canonical is one of the names recognized by Transpile (typically a value from the cel-go operators package). Unknown aliases are passed through unchanged.

func WithParamOffset

func WithParamOffset(n int) Option

WithParamOffset sets the number of the first emitted placeholder. Placeholders are then numbered offset, offset+1, ... so callers can splice the fragment into a query that already has earlier bound values. Defaults to 1.

Jump to

Keyboard shortcuts

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