|
| 1 | +"""Functionality for performing checks on SPARQL queries.""" |
| 2 | + |
| 3 | +import logging |
| 4 | + |
| 5 | +from rdfproxy.utils._exceptions import UnsupportedQueryException |
| 6 | +from rdfproxy.utils._types import ParsedSPARQL, _TQuery |
| 7 | +from rdfproxy.utils.utils import compose_left |
| 8 | + |
| 9 | + |
| 10 | +logger = logging.getLogger(__name__) |
| 11 | + |
| 12 | + |
| 13 | +def _check_select_query(parsed_sparql: ParsedSPARQL) -> ParsedSPARQL: |
| 14 | + """Check if a SPARQL query is a SELECT query. |
| 15 | +
|
| 16 | + This is meant to run as a component in check_query. |
| 17 | + """ |
| 18 | + logger.debug("Running SELECT query check.") |
| 19 | + |
| 20 | + if parsed_sparql.parse_object.name != "SelectQuery": |
| 21 | + raise UnsupportedQueryException("Only SELECT queries are applicable.") |
| 22 | + return parsed_sparql |
| 23 | + |
| 24 | + |
| 25 | +def _check_solution_modifiers(parsed_sparql: ParsedSPARQL) -> ParsedSPARQL: |
| 26 | + """Check if a SPARQL query has a solution modifier. |
| 27 | +
|
| 28 | + This is meant to run as a component in check_query. |
| 29 | + """ |
| 30 | + logger.debug("Running solution modifier check.") |
| 31 | + |
| 32 | + def _has_modifier(): |
| 33 | + for mod_name in ["limitoffset", "groupby", "having", "orderby"]: |
| 34 | + if (mod := getattr(parsed_sparql.parse_object, mod_name)) is not None: |
| 35 | + return mod |
| 36 | + return False |
| 37 | + |
| 38 | + if mod := _has_modifier(): |
| 39 | + logger.critical("Detected solution modifier '%s' in outer query.", mod) |
| 40 | + raise UnsupportedQueryException( |
| 41 | + "Solution modifiers for top-level queries are currently not supported." |
| 42 | + ) |
| 43 | + |
| 44 | + return parsed_sparql |
| 45 | + |
| 46 | + |
| 47 | +def check_query(query: _TQuery) -> _TQuery: |
| 48 | + """Check a SPARQL query by running a compose pipeline of checks. |
| 49 | +
|
| 50 | + The pipeline expects a SPARQL query string and |
| 51 | + will return that string if all checks pass. |
| 52 | +
|
| 53 | + _parse_query is meant to be the first component |
| 54 | + and _get_query_string is meant to be the last component. |
| 55 | + """ |
| 56 | + logger.debug("Running query check pipeline on '%s'", query) |
| 57 | + parsed_sparql = ParsedSPARQL(query=query) |
| 58 | + |
| 59 | + result: ParsedSPARQL = compose_left( |
| 60 | + _check_select_query, |
| 61 | + _check_solution_modifiers, |
| 62 | + )(parsed_sparql) |
| 63 | + |
| 64 | + return result.data |
0 commit comments