Core Concepts
ASTQL has five primitives: tables, fields, params, conditions, and builders. Understanding these unlocks the full API.
Tables
A table represents a database table. Create tables through an ASTQL instance:
users := instance.T("users")
Tables can have single-letter aliases for JOINs:
users := instance.T("users", "u") // "users" aliased as "u"
posts := instance.T("posts", "p") // "posts" aliased as "p"
Aliases must be single lowercase letters (a-z). This restriction prevents injection through alias names.
instance.T("users", "u") // Valid
instance.T("users", "users_alias") // Panics: invalid alias
Fields
A field represents a column. Create fields through an ASTQL instance:
email := instance.F("email")
username := instance.F("username")
Fields can be prefixed with a table alias:
userEmail := instance.WithTable(instance.F("email"), "u")
// Renders as: u."email"
Field Validation
Fields are validated against the schema. Any field name must exist in at least one table:
instance.F("email") // Valid if "email" exists in any table
instance.F("nonexistent") // Panics: field not found in schema
Params
A param represents a query parameter. All user values flow through params:
emailParam := instance.P("email_value")
activeParam := instance.P("is_active")
Params are rendered as named placeholders:
// In SQL: WHERE "email" = :email_value
Parameter Validation
Parameter names must be valid SQL identifiers:
instance.P("user_id") // Valid
instance.P("id; DROP TABLE users--") // Panics: invalid parameter
Conditions
Conditions represent WHERE clause predicates. Create conditions with instance.C:
condition := instance.C(
instance.F("email"), // Field
astql.EQ, // Operator
instance.P("email_val"), // Param
)
Operators
| Constant | SQL | Description |
|---|---|---|
EQ | = | Equals |
NE | != | Not equals |
GT | > | Greater than |
GE | >= | Greater than or equal |
LT | < | Less than |
LE | <= | Less than or equal |
LIKE | LIKE | Pattern match |
ILIKE | ILIKE | Case-insensitive pattern match |
IN | = ANY() | In array |
NotIn | != ALL() | Not in array |
IsNull | IS NULL | Null check |
IsNotNull | IS NOT NULL | Not null check |
See Operators Reference for the complete list.
Combining Conditions
Use And and Or to combine conditions:
// AND: both must be true
instance.And(
instance.C(instance.F("active"), astql.EQ, instance.P("is_active")),
instance.C(instance.F("verified"), astql.EQ, instance.P("is_verified")),
)
// Renders: ("active" = :is_active AND "verified" = :is_verified)
// OR: either must be true
instance.Or(
instance.C(instance.F("role"), astql.EQ, instance.P("admin_role")),
instance.C(instance.F("role"), astql.EQ, instance.P("mod_role")),
)
// Renders: ("role" = :admin_role OR "role" = :mod_role)
Nested Conditions
Conditions can be nested arbitrarily:
instance.And(
instance.C(instance.F("active"), astql.EQ, instance.P("is_active")),
instance.Or(
instance.C(instance.F("role"), astql.EQ, instance.P("admin")),
instance.C(instance.F("role"), astql.EQ, instance.P("mod")),
),
)
// Renders: ("active" = :is_active AND ("role" = :admin OR "role" = :mod))
NULL Conditions
Use Null and NotNull for NULL checks:
instance.Null(instance.F("deleted_at")) // "deleted_at" IS NULL
instance.NotNull(instance.F("verified_at")) // "verified_at" IS NOT NULL
Field Comparisons
Compare two fields directly with CF:
astql.CF(
instance.F("created_at"),
astql.LT,
instance.F("updated_at"),
)
// Renders: "created_at" < "updated_at"
Builders
Builders construct queries through method chaining. Each operation type has its own entry point:
astql.Select(table) // SELECT query
astql.Insert(table) // INSERT query
astql.Update(table) // UPDATE query
astql.Delete(table) // DELETE query
astql.Count(table) // SELECT COUNT(*) query
Fluent API
Methods return the builder for chaining:
result, err := astql.Select(instance.T("users")).
Fields(instance.F("username"), instance.F("email")).
Where(condition).
OrderBy(instance.F("username"), astql.ASC).
Limit(10).
Offset(20).
Render(postgres.New())
Build vs Render
Build()returns the AST for inspection or modificationRender(provider)produces the final SQL and parameter list using the specified provider
// Get the AST
ast, err := query.Build()
// Get SQL output
result, err := query.Render(postgres.New())
Must Variants
MustBuild and MustRender panic on error instead of returning it:
result := query.MustRender(postgres.New()) // Panics if invalid
Try Variants
All instance methods have Try variants that return errors instead of panicking:
// Panics on invalid input
table := instance.T("users")
field := instance.F("email")
param := instance.P("value")
// Returns error on invalid input
table, err := instance.TryT("users")
field, err := instance.TryF("email")
param, err := instance.TryP("value")
Use Try variants when handling user input or dynamic field names.
Query Result
Render() returns a QueryResult:
type QueryResult struct {
SQL string // The rendered SQL query
RequiredParams []string // Parameter names that must be provided
}
The RequiredParams slice lists all parameters in order of appearance. Use this to validate that all required values are provided before execution.