Reference manual¶
Introduction¶
Quick reference for the pyadql command:
pyadql [-h] [-v] [--level {INFO,DEBUG,WARNING,ERROR,CRITICAL,TRACE}]
[-f PATH] [--tree] [--json] [-q] [query]
| Option | Effect |
|---|---|
query (positional, optional) |
ADQL query to parse; if omitted, read from -f or standard input |
-f, --file PATH |
Read the ADQL query from a file |
--tree |
Print the raw Lark parse tree instead of the typed AST |
--json |
Print the AST as JSON (each node carries a "_type" key) instead of a Python repr |
--level {INFO,DEBUG,WARNING,ERROR,CRITICAL,TRACE} |
Set the logging verbosity (default: ERROR) |
-q, --quiet |
Only print errors (overrides --level) |
-v, --version |
Print the installed pyadql version and exit |
-h, --help |
Print the full help text and exit |
For the corresponding Python API reference, see the auto-generated API
documentation on this site (built from the docstrings in src/pyadql/
via mkdocstrings) and docs/AST.md for a guided tour of the
AST node types.
Help method¶
pyadql --helpprints the full option list with a short description of each (generated byargparsefrom the definitions insrc/pyadql/__main__.py).pyadql --version(or-v) prints the installed version string.- Installation instructions are in
operation_manual.md; if installation itself fails, the error comes frompip/uvdirectly (missing Python version, network issue, ...), not from PyADQL. - Parsing failures are diagnosed by re-running with
--level DEBUGor--level TRACE; see "Error messages" below andoperation_manual.md.
Screen definitions and operations¶
Not applicable: PyADQL has no graphical interface. It is a text-in /
text-out command-line tool. Input is the ADQL query (as a CLI argument, a
file via -f, or standard input); output is the parsed AST on stdout
and diagnostic logs on stderr, kept on separate streams so they can be
redirected independently.
Commands and operations¶
The pyadql command is the package's only executable entry point
(declared under [project.scripts] in pyproject.toml). Its operations
are fully described in operation_manual.md;
the equivalent Python API is pyadql.parse_adql(query) (query text ->
typed AST) and pyadql.parse_tree(query) (query text -> raw Lark tree,
mainly for debugging the grammar itself).
ADQL language coverage¶
What's supported by the grammar (src/pyadql/grammar/*.lark, transcribed
from Annex A of IVOA Recommendation ADQL 2.1):
SELECT(DISTINCT,TOP n,*, column aliases)WITH(common table expressions, ADQL 2.1)FROMwith every join type (INNER,LEFT/RIGHT/FULL [OUTER],NATURAL,ON,USING), derived tables(SELECT ...) AS aliasWHERE,GROUP BY,HAVINGORDER BY [ASC|DESC],OFFSET(ADQL 2.1) -- applying to the whole combined query, per Annex A (seeast_nodes.SelectExpression)- Set operators
UNION/EXCEPT/INTERSECT(+ALL) - Predicates: comparisons,
BETWEEN,IN(list or subquery),LIKEandILIKE(case-insensitive, ADQL 2.1),IS [NOT] NULL,EXISTS - Arithmetic expressions (
+ - * /),||concatenation,NOT/AND/OR - Scalar and correlated subqueries
CAST(expr AS type)with ADQL 2.1's enumerated target types (CHAR/VARCHAR(n),SMALLINT/INTEGER/BIGINT/REAL/DOUBLE PRECISION,TIMESTAMP,POINT/CIRCLE/POLYGON)COALESCE(a, b, ...)(ADQL 2.1)- Aggregate functions:
COUNT,SUM,AVG,MIN,MAX(+DISTINCT) - ADQL numeric functions:
ABS,CEILING,FLOOR,ROUND,SQRT,POWER,MOD,LOG,LOG10,EXP,PI,RAND,SIGN,TRUNCATE, trigonometric functions (SIN,COS,TAN,ASIN,ACOS,ATAN,ATAN2,COT),DEGREES,RADIANS,IN_UNIT(ADQL 2.1) - String functions:
LOWER,UPPER - ADQL geometry:
POINT(optionalcoordsys, ADQL 2.1),CIRCLE,BOX,POLYGON(all three accepting either a rawra, decpair or a ready-made point-valued expression as center/vertex, ADQL 2.1),REGION,CONTAINS,INTERSECTS,AREA,CENTROID,COORD1,COORD2,COORDSYS,DISTANCE(both the two-point and the 2.1-added 4-number overload) - User-defined functions (any unrecognized
name(...)call is captured asUserFunctionCall, to cover TAP-service-specific extensions) - Double-quoted identifiers (
"My Column", with""-escaped literal quotes),--comments
Known limitations (feel free to extend, see grammar/core.lark):
TIMESTAMP/INTERVALliterals (as opposed toCAST(... AS TIMESTAMP), which is supported) are not yet in the grammar; Annex A itself barely defines them.- No specific handling of
TAP_UPLOAD(a TAP-protocol detail, outside the ADQL grammar proper). - Earley's ambiguity resolution (
ambiguity='resolve') picks the "most likely" derivation; for very unusual queries, double-check the resulting AST. One concrete case:POINT(a, b, c)where none ofa/b/cfitPOINT's expected shape (e.g.aisn't a string literal coordsys) falls back to a genericUserFunctionCallnamed"POINT"rather than being rejected outright -- a harmless permissive superset, but worth knowing about if you're validating strict spec compliance rather than just parsing.
To cover a missing case: add the rule in the relevant
src/pyadql/grammar/*.lark file (following Annex A, see the
[AnnexA #x] cross-references already there), then add the matching
method in ADQLTransformer (src/pyadql/parser.py) that builds the AST
node (src/pyadql/ast_nodes.py).
Validation against a real production log¶
The grammar was run against ~2584 unique ADQL queries extracted from a production log of a TAP service (DaCHS, EPN-TAP): 2533 parsed successfully, 51 fail, and break down as:
LIMIT(31 queries): generic SQL syntax never standardized in ADQL (the standard usesTOP) -- correctly rejected, a standard-compliant TAP service rejects it too,- one SQL injection attempt by a bot (correctly rejected),
- truncated log lines or client queries that are plainly malformed (missing commas, unclosed quotes).
Error messages¶
PyADQL reports failures through two channels:
- Exit code (CLI) / exception (library) -- see the table in
operation_manual.md:0success,1ADQL parsing failure,2usage error. - A
loguru-formatted error message onstderr, always shown regardless of--level, explicitly tagged as either: - a grammar error -- the query does not conform to ADQL 2.1's Annex A grammar (a real syntax mistake, or unsupported syntax, see "Known limitations" above),
- a transformer error -- the grammar accepted the query but
ADQLTransformercould not build the AST for it (report this as a bug, seeCONTRIBUTING.md).
Re-running the failing command with --level DEBUG adds per-step timing
and an AST summary on success, or the point of failure with more context
on error; --level TRACE additionally prints the raw Lark parse tree,
which is the most direct way to see exactly how far the grammar got
before failing.