gocql2

package module
v0.10.4 Latest Latest
Warning

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

Go to latest
Published: Aug 27, 2026 License: MIT Imports: 3 Imported by: 0

README

gocql2 - OGC CQL2 parser with SQL generation

codecov

gocql2 is a Go library for parsing OGC Common Query Language 2 (CQL2) filters and, when needed, compiling them into safe parameterized SQL fragments.

Use it when you accept CQL2 from API clients, such as OGC API Features filter parameters, and need to validate the filter against your queryable fields before applying it to a datastore.

Install

go get github.com/cwygoda/gocql2

Quick start: parse CQL2 Text

package main

import (
    "fmt"
    "log"

    gocql2 "github.com/cwygoda/gocql2"
    "github.com/cwygoda/gocql2/api"
)

func main() {
    expr, err := gocql2.NewParser().
        WithAllowedProperties(
            api.PropertyDefinition{Name: "name", Type: api.PropertyTypeString},
            api.PropertyDefinition{Name: "height", Type: api.PropertyTypeNumber},
        ).
        ParseText("name = 'Oak' AND height >= 10")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%T\n", expr) // *api.LogicalExpression
}

For parsing without schema validation, still create an explicit parser with gocql2.NewParser() and then call ParseText, ParseJSON, or Parse.

Parse CQL2 JSON

expr, err := gocql2.NewParser().
    WithAllowedProperties(
        api.PropertyDefinition{Name: "name", Type: api.PropertyTypeString},
        api.PropertyDefinition{Name: "height", Type: api.PropertyTypeNumber},
    ).
    ParseJSON([]byte(`{
        "op": "and",
        "args": [
            {"op": "=", "args": [{"property": "name"}, "Oak"]},
            {"op": ">=", "args": [{"property": "height"}, 10]}
        ]
    }`))

Serialize CQL2

Parsed ASTs can be serialized back to CQL2 Text or CQL2 JSON. Output is canonicalized for safe round-tripping; it is structurally equivalent, not byte-for-byte identical to the original input.

text, err := gocql2.SerializeText(expr)
jsonBytes, err := gocql2.SerializeJSON(expr)

Compile CQL2 to SQL

The sql package turns a parsed AST into a parameterized SQL expression. Property mappings are fail-closed by default: every CQL2 property must be explicitly mapped to trusted application-authored SQL.

package main

import (
    "fmt"
    "log"

    gocql2 "github.com/cwygoda/gocql2"
    "github.com/cwygoda/gocql2/api"
    cql2sql "github.com/cwygoda/gocql2/sql"
)

func main() {
    props := []cql2sql.Property{
        {Name: "name", Type: api.PropertyTypeString, Expr: cql2sql.Column("assets", "name")},
        {Name: "height", Type: api.PropertyTypeNumber, Expr: cql2sql.Column("assets", "height")},
    }

    expr, err := gocql2.NewParser().
        WithConformance(
            api.ConformanceAdvancedComparisonOperators,
            api.ConformanceCaseInsensitiveComparison,
        ).
        WithAllowedProperties(cql2sql.PropertyDefinitions(props...)...).
        ParseText("CASEI(name) LIKE casei('oak%') AND height >= 10")
    if err != nil {
        log.Fatal(err)
    }

    where, err := cql2sql.ToSQL(
        expr,
        cql2sql.PostGISDialect(),
        cql2sql.WithSQLProperties(props...),
    )
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(where.Text)
    fmt.Printf("%#v\n", where.Args)
}

Output:

(((lower("assets"."name") LIKE lower($1))) AND (("assets"."height" >= CAST($2 AS numeric))))
[]interface {}{"oak%", "10"}

You can then compose where.Text into your query and pass where.Args to your database driver.

Validate queryables and functions

A reusable parser can be configured before concurrent use:

  • WithAllowedProperties rejects unknown properties and validates property types in scalar, comparison, temporal, spatial, array, and function contexts.
  • WithAllowedFunctions rejects unknown functions and validates function signatures.
  • WithConformance records CQL2 conformance classes and enables standard CQL2 functions implied by those classes, such as CASEI, spatial predicates, temporal predicates, and array predicates.
  • WithMaxDepth limits recursive parse depth for defensive parsing.

SQL dialects

gocql2 includes:

  • cql2sql.BaseDialect for ANSI-style placeholders and identifier quoting.
  • cql2sql.PostGISDialect for PostgreSQL/PostGIS placeholders, case/accent functions, spatial predicates, temporal predicates, array predicates, and geometry literals.

Implement cql2sql.Dialect or embed cql2sql.BaseDialect to customize database-specific rendering.

Error handling

Parser errors are returned as *api.ParseError and include source language plus either text position or JSON path information.

_, err := gocql2.NewParser().ParseText("name =")
if err != nil {
    var parseErr *api.ParseError
    if errors.As(err, &parseErr) {
        log.Printf(
            "bad CQL2 at line %d, column %d",
            parseErr.Location.Line,
            parseErr.Location.Column,
        )
    }
}

SQL generation errors are regular Go errors, for example when a property has no SQL mapping or a dialect does not support a requested function.

Supported input and features

  • CQL2 Text and CQL2 JSON parsing.
  • Logical expressions, comparisons, LIKE, BETWEEN, IN, IS NULL, arithmetic, and boolean/null/string/number literals.
  • Standard CQL2 spatial, temporal, and array predicates when enabled by conformance.
  • Typed public AST in the api package.
  • Parameterized SQL fragment generation with explicit property mapping.

See REFERENCES.md for CQL2 references and DEVELOPMENT.md for contributor setup.

Documentation

Overview

Package gocql2 parses OGC CQL2 filters.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SerializeJSON added in v0.10.0

func SerializeJSON(expr api.Expression) ([]byte, error)

SerializeJSON serializes a CQL2 AST to CQL2 JSON.

func SerializeText added in v0.10.0

func SerializeText(expr api.Expression) (string, error)

SerializeText serializes a CQL2 AST to CQL2 Text.

Types

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

Parser parses CQL2 and exposes the capabilities it was configured with.

func NewParser

func NewParser() *Parser

NewParser builds a reusable parser. Chain setup methods before concurrent use.

func (*Parser) ConformanceClasses

func (p *Parser) ConformanceClasses() []string

ConformanceClasses returns the advertised conformance class IDs.

func (*Parser) Parse

func (p *Parser) Parse(input []byte, lang api.Language) (api.Expression, error)

Parse parses input in the requested CQL2 language.

func (*Parser) ParseJSON

func (p *Parser) ParseJSON(input []byte) (api.Expression, error)

ParseJSON parses CQL2 JSON into an AST.

func (*Parser) ParseText

func (p *Parser) ParseText(input string) (api.Expression, error)

ParseText parses CQL2 Text into an AST.

func (*Parser) SupportedFunctionDefinitions

func (p *Parser) SupportedFunctionDefinitions() []api.FunctionDefinition

SupportedFunctionDefinitions returns the configured allowed functions.

func (*Parser) SupportedFunctions

func (p *Parser) SupportedFunctions() []string

SupportedFunctions returns the advertised function names.

func (*Parser) SupportedProperties

func (p *Parser) SupportedProperties() []string

SupportedProperties returns the advertised property names.

func (*Parser) SupportedPropertyDefinitions

func (p *Parser) SupportedPropertyDefinitions() []api.PropertyDefinition

SupportedPropertyDefinitions returns the configured allowed properties.

func (*Parser) WithAllowedFunctions

func (p *Parser) WithAllowedFunctions(defs ...api.FunctionDefinition) *Parser

WithAllowedFunctions adds function definitions to the fail-closed function registry. Any function reference not present in the registry is rejected, and registered signatures are used to validate argument counts, argument types, and return-type contexts. Definitions added later override earlier definitions with the same normalized name.

func (*Parser) WithAllowedProperties

func (p *Parser) WithAllowedProperties(defs ...api.PropertyDefinition) *Parser

WithAllowedProperties configures a fail-closed property registry. Any property reference not present in the registry is rejected, and registered types are used to validate character, numeric, comparison, and IN-list contexts.

func (*Parser) WithConformance

func (p *Parser) WithConformance(classes ...string) *Parser

WithConformance records CQL2 conformance classes and configures the standard functions required by those classes. Arguments may be api conformance constants, full CQL2 conformance/requirements URIs, /conf/<class> fragments, or class slugs such as "case-insensitive-comparison".

The Functions conformance class does not define any concrete function names; combine it with WithAllowedFunctions to advertise implementation-specific functions.

func (*Parser) WithConformanceClasses

func (p *Parser) WithConformanceClasses(classes ...string) *Parser

WithConformanceClasses records the parser's advertised conformance classes.

func (*Parser) WithMaxDepth

func (p *Parser) WithMaxDepth(n int) *Parser

WithMaxDepth limits recursive parse depth.

Directories

Path Synopsis
Package api contains public CQL2 AST and schema building blocks.
Package api contains public CQL2 AST and schema building blocks.
internal
ats
Package ats contains ATS-inspired, fixture-backed CQL2 integration tests.
Package ats contains ATS-inspired, fixture-backed CQL2 integration tests.
parser
Package parser contains the internal CQL2 parser implementation.
Package parser contains the internal CQL2 parser implementation.
serializer
Package serializer renders parsed CQL2 AST nodes as CQL2 Text or JSON.
Package serializer renders parsed CQL2 AST nodes as CQL2 Text or JSON.
Package sql compiles parsed CQL2 expressions to SQL fragments.
Package sql compiles parsed CQL2 expressions to SQL fragments.

Jump to

Keyboard shortcuts

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