Concept ยท OrionBelt® Analytics

OBQC: Ontology-Based Query Check

A deterministic, rule-based SQL validator that checks every generated query against the ontology before it reaches the database. Errors block execution. No LLM in the loop.

Definition

OBQC stands for Ontology-Based Query Check. It is the deterministic safety layer in OrionBelt® Analytics (OBA), and it validates SQL against the loaded RDF/OWL ontology before the query is allowed to reach the database.

There is no LLM inside OBQC and no probabilistic reasoning. It parses the SQL with sqlglot, compares what the query actually does against the ontology's oba: annotations (tables, columns, SQL types, primary and foreign keys, join conditions, relationship direction), and hands back structured errors or warnings the calling assistant can act on.

In one sentence: OBQC gives LLM-generated SQL a hard structural check against the ontology. Errors block execution before the query reaches the database, warnings travel back with the result.

The Problem: Plausible SQL That Is Still Wrong

An LLM will happily write SQL that reads perfectly and computes the wrong thing. The same handful of failures keep coming back, and all of them are structural:

The first three fail loudly sooner or later. The last one never does. It gives you a confident, wrong answer that looks exactly like a correct one, and that is where a deterministic check earns its keep, because a language model cannot help you here.

How OBQC Works

OBQC runs inside the session, against the ontology OBA generated or loaded for the connected database:

  1. OBA connects to a database and discovers the schema.
  2. generate_ontology() or load_my_ontology() creates or loads an ontology carrying oba: annotations for tables, columns, SQL types, primary keys, foreign keys, join conditions, and relationship direction.
  3. OBA creates a session-local validator from that ontology.
  4. When execute_sql_query() receives SQL, OBQC parses it with sqlglot.
  5. OBQC extracts the referenced tables, columns, joins, aggregations, aliases, and join anchors.
  6. Those parts are validated against the ontology cache.
  7. If there are no blocking errors, OBA executes the query and attaches any warnings to the response.
connect_database()
  -> discover_schema()
  -> generate_ontology()
  -> execute_sql_query()
       -> OBQC parses SQL
       -> OBQC checks ontology rules
       -> errors block, warnings attach
       -> database execution only if valid

execute_sql_query() is the only entry point that runs OBQC. If OBQC returns an error, OBA answers with an obqc_error response instead of executing anything. The database never sees the query.

Severity: What Blocks and What Warns

Severity Effect on execution
Error Query is blocked and never sent to the database. The response carries "success": false and the issue details.
Warning Query executes normally. Warnings are attached to the response so the assistant can self-correct or tell the user.
Info Informational note, no effect on execution.

The overall is_valid flag follows one rule: any ERROR-level issue makes the result invalid.

What OBQC Checks

Check Severity Purpose
Table existenceErrorEvery table in FROM and JOIN must exist in the ontology.
Column existenceErrorQualified and unqualified columns must resolve to real columns.
Ambiguous columnWarningAn unqualified name that exists in more than one referenced table.
Missing join conditionErrorMultiple tables in one SELECT with no JOIN ... ON, which is a Cartesian product.
Non-matching joinWarningA join condition that matches no declared foreign-key relationship.
Type compatibilityWarningWHERE and ON comparisons should use compatible types.
Aggregation correctnessErrorNon-aggregated selected columns must appear in GROUP BY.
Fan-trap riskWarningAggregation across two or more fan-out joins.

Table and column existence

When a table is not found, OBQC lists the table names the ontology does know about, so the assistant can correct the name instead of guessing again.

-- ERROR: Table 'cusotmers' not found in ontology
SELECT * FROM cusotmers;

-- ERROR: Column 'emial' not found in table 'customers'
SELECT emial FROM customers;

Qualified references (table.column) are checked against that specific table. Unqualified references resolve within their own scope: the SELECT they appear in, plus any enclosing ones, so a correlated subquery can still reach the outer query's tables. It does not work the other way round. A subquery's tables never resolve names in the outer query.

Join validity

-- ERROR: Multiple tables without explicit JOIN (Cartesian product)
SELECT * FROM customers, orders;

-- WARNING: JOIN condition may not match declared FK relationship
-- Suggested: orders.customer_id = customers.id
SELECT * FROM customers JOIN orders ON customers.name = orders.note;

The Cartesian-product rule is judged per SELECT. That makes SELECT id FROM customers WHERE id IN (SELECT customer_id FROM orders) perfectly fine, since each scope only ever sees one table.

Aggregation correctness

-- ERROR: Column 'name' in SELECT with aggregation but no GROUP BY
SELECT name, COUNT(*) FROM customers;

-- ERROR: Column 'status' not in GROUP BY clause
SELECT status, region, SUM(amount) FROM orders GROUP BY region;

Grouping by an alias satisfies the check on the column behind it. The rule is again evaluated per SELECT, so an aggregate inside a subquery belongs to that subquery and nowhere else.

Fan-Trap Protection

A fan-trap happens when a query aggregates after joining across several one-to-many paths. The SQL runs fine. The totals are simply too high, because rows were multiplied before the aggregation ran.

-- WARNING: Potential fan-trap: 2 one-to-many joins with aggregation
SELECT c.name, SUM(o.amount), COUNT(r.id)
FROM customers c
JOIN orders o ON c.id = o.customer_id
JOIN reviews r ON c.id = r.customer_id
GROUP BY c.name;

OBQC catches this in two ways. First it looks for ontology axioms such as owl:disjointWith on sibling fact tables that share a dimension. Where those axioms are missing, it falls back to relationship metadata and join direction, and counts the fan-out joins in the query itself.

Direction is what makes the check precise. Joining from a fact table to a dimension is a lookup and multiplies nothing. Joining from a dimension out to several fact tables can multiply rows. So OBQC judges fan-out per join, against the table that join's ON condition attaches to, rather than asking whether a table happens to sit on the many side of some relationship elsewhere in the schema. Walking a chain of many-to-one lookups (sales to clients to countries) is never flagged, however many foreign keys those dimensions carry.

When a fan-trap is flagged, OBQC suggests aggregating each fact table separately with UNION ALL, or pre-aggregating in CTEs before joining. That is the same correction the Composite Fact Layer (CFL) applies automatically in the OrionBelt® Semantic Layer, where such queries come out correct by construction rather than being checked after the fact.

What OBQC Deliberately Does Not Flag

Three things sit outside the rules, because treating them as violations blocked SQL that was perfectly correct:

A validator that cries wolf gets switched off within a week. These exemptions exist so that every issue OBQC does report is worth reading.

How the AI Agent Uses OBQC

OBQC results come back as structured data in the tool response. They are not rendered for the user. The calling LLM receives:

When a query is blocked, the agent gets the error details and the suggestions, revises the SQL and tries again, usually without the user ever seeing the failed attempt. When warnings come back alongside a successful result, it is up to the agent whether to surface them ("this query joins across two one-to-many relationships, which may inflate the totals").

Requirements

OBQC relies on oba: namespace annotations in the ontology. generate_ontology writes them automatically, and load_my_ontology brings them along for any OrionBelt®-generated ontology:

If a loaded ontology lacks these annotations, a generic OWL ontology for instance, OBQC reports ontology_compatible: false and falls back to basic syntax checking only. The full annotation vocabulary is published at the oba: namespace.

Related OrionBelt® Concepts

Frequently Asked Questions

What does OBQC stand for?

Ontology-Based Query Check. It is the deterministic safety layer in OrionBelt® Analytics (OBA), and it checks SQL against the loaded RDF/OWL ontology before the query reaches the database.

Does OBQC use an LLM?

No. OBQC makes no LLM calls and does no probabilistic reasoning. It parses the SQL with sqlglot, compares the structure against the ontology, and returns structured errors and warnings. The guarantees it gives are structural, which is exactly what LLM-generated SQL lacks.

What does OBQC check?

Table existence, column existence, ambiguous unqualified columns, join validity (missing join conditions and joins that do not match a declared foreign key), type compatibility in WHERE and ON comparisons, aggregation correctness, and fan-trap risk when a query aggregates across two or more one-to-many joins.

What is the difference between an error and a warning?

An error blocks execution: the query is never sent to the database and the tool returns the issue details instead. A warning lets the query run and is attached to the response, so the agent can revise it or explain the caveat. Info issues are informational only.

How does OBQC detect a fan-trap?

First through ontology axioms such as owl:disjointWith on sibling fact tables that share a dimension. Where those are absent, through relationship metadata and join direction: fan-out is judged per join, against the table that join's ON condition attaches to, so a chain of many-to-one lookups is not flagged.

What does OBQC need in order to run?

An ontology carrying oba: namespace annotations, produced by generate_ontology or loaded with load_my_ontology. Without one, or with a generic OWL ontology lacking those annotations, OBQC reports ontology_compatible: false and falls back to basic syntax checking only.

Wrong SQL never reaches your database

OBQC ships with OrionBelt® Analytics, the open-source ontology-based MCP server for Text-to-SQL across 8 database connectors.

OrionBelt® Analytics on GitHub oba: Namespace Contact RALFORION