Semantic aliases
================

A **semantic alias** is a named, parameterized SQL query paired with a
natural-language description. The description is embedded, so an alias
can be found by meaning — the same way schema metadata is. Aliases are
the reusable, governed layer of text-to-SQL: instead of generating fresh
SQL for every question, a recurring question resolves to a query you’ve
already written and reviewed.

Each alias is a member of *every* semantic KB that owns the schema its
SQL reads — not just one. AIDB infers the owning KB(s) from the query
and embeds the alias’s description once per KB, using that KB’s model,
so each embedding stays comparable to that KB’s schema vectors. A schema
owned by two KBs yields two embeddings, and the alias is then findable —
with :ref:`aidb.semantic_kb_search() <aidb.semantic_kb_search()>`  — from either KB.

!!!note Semantic aliases are managed and run with the ``aidb`` SQL
functions below. Unlike the KB *search* functions, alias functions are
**not** exposed as :ref:`agent tools <Knowledge bases reference>`  — by product decision, an agent
discovers and reads schema, but doesn’t create or execute aliases
itself.

Creating an alias
-----------------

``aidb.create_semantic_alias()`` takes a unique name, a description to
embed, the SQL query text, and its parameter definitions. Placeholders
in the query use ``${name}`` syntax. The query must be a single
read-only ``SELECT`` .

.. code:: sql

   SELECT aidb.create_semantic_alias(
       name        => monthly_revenue,
       description => Total revenue for a given month and year,
       query_text  => $$
           SELECT SUM(amount) AS total
           FROM sales.orders
           WHERE EXTRACT(MONTH FROM order_date) = ${month}
             AND EXTRACT(YEAR  FROM order_date) = ${year}
       $$,
       params => [
           {"name": "month", "param_type": "integer", "description": "Month number (1-12)"},
           {"name": "year",  "param_type": "integer", "description": "Four-digit year"}
       ]
   );

.. csv-table::
  :header: Parameter,Type,Default,Description
  :widths: 15,12,12,20
  :align: left
  :class: longtable

  `name`,`text`,—,Unique alias name.
  `description`,`text`,—,Human-readable description — this is what gets embedded for search.
  `query_text`,`text`,—,"A single read-only `SELECT`, with `${name}` placeholders for parameters."
  `params`,`jsonb`,`NULL`,Array of parameter definitions (see below).
  `kb_name`,`text`,`NULL`,Optional; narrows the alias to just this KB. Omit to embed it for every KB that owns the query's schema(s).

Each entry in the ``params`` JSONB array has:

.. csv-table::
  :header: Field,Required,Description
  :widths: 12,15,15
  :align: left
  :class: longtable

  `name`,Yes,Matches a `${name}` placeholder in `query_text`.
  `param_type`,Yes,"PostgreSQL type: `text`, `integer`, `numeric`, `date`, and so on."
  `description`,No,Human-readable description of the parameter.
  `enum_values`,No,"Allowed values, when the parameter is constrained to a set."

If the query’s schema maps to more than one KB, the alias is embedded
once for each of them — creation never fails on ambiguity; pass
``kb_name`` only to narrow it to a single KB. A schema not yet owned by
any KB leaves the alias unembedded, until a KB covering it is created or
adopts it later.

Finding an alias
----------------

``aidb.search_semantic_aliases()`` searches aliases by natural-language
query. Without ``kb_name`` , it searches ``default_semkb`` — the name a
KB gets when created without an explicit one — if that KB exists, or
every KB with stored alias embeddings otherwise; the query is embedded
once per distinct model among those KBs, so the search stays
dimension-safe, and results are de-duplicated to the best match per
alias.

.. code:: sql

   SELECT name, query_text, similarity
   FROM aidb.search_semantic_aliases(
       query_text => how much money did we make last month,
       kb_name    => analytics_kb,
       top_k      => 5
   );

.. csv-table::
  :header: Parameter,Type,Default,Description
  :widths: 15,12,12,20
  :align: left
  :class: longtable

  `query_text`,`text`,—,Natural-language query.
  `min_similarity`,`double precision`,`NULL`,Optional similarity floor.
  `top_k`,`int`,`10`,Maximum results.
  `offset`,`int`,`0`,Paging offset.
  `kb_name`,`text`,`NULL`,"Optional; narrows the search to this KB. Omit to resolve `default_semkb`, or all KBs with alias embeddings, per the precedence above."

It returns ``name`` , ``query_text`` , and ``similarity`` . Aliases also
appear in :ref:`aidb.semantic_kb_search() <aidb.semantic_kb_search()>`  results with ``source_type = 'alias'`` , so a
single composite search finds both schema and aliases at once.

Running an alias
----------------

``aidb.execute_semantic_alias()`` runs an alias by name, substituting
its parameters. It returns a set of ``result`` rows, each a JSONB
object:

.. code:: sql

   SELECT result
   FROM aidb.execute_semantic_alias(
       alias_name => monthly_revenue,
       args       => {"month": 3, "year": 2025}
   );

.. csv-table::
  :header: Parameter,Type,Default,Description
  :widths: 15,12,12,20
  :align: left
  :class: longtable

  `alias_name`,`text`,—,Alias to run.
  `args`,`jsonb`,`NULL`,Object of parameter values keyed by name.
  `execute_role`,`text`,`NULL`,PostgreSQL role to run the query as — requires the appropriate `SET ROLE` grants.

Use ``execute_role`` to run an alias under a least-privilege reporting
role rather than the connecting user. Because every alias is validated
as a single read-only ``SELECT`` at creation and re-validated at
execution, an alias can’t be used to run writes.

Managing aliases
----------------

.. csv-table::
  :header: Function,Purpose
  :widths: 10,30
  :align: left
  :class: longtable

  `aidb.get_semantic_aliases()`,"List all aliases with `name`, `description`, `query_text`, and `param_count`."
  `aidb.get_semantic_alias(alias_name)`,"Get one alias's `name`, `description`, `query_text`, and `params`."
  "`aidb.update_semantic_alias(name, description, query_text, params, kb_name)`",Update an alias; changing `query_text` or `kb_name` re-infers its owning KB(s) and reconciles embeddings — adding KBs it newly belongs to and dropping ones it no longer does. Changing `description` re-embeds it in every KB it's already in.
  `aidb.delete_semantic_alias(name)`,Delete an alias by name.

Deleting a KB removes only that KB’s embedding of each alias — the alias
definition, and its embeddings in any other owning KBs, survive. A later
KB covering the same schema adopts the alias, adding a fresh embedding
for it, even if the alias is already embedded for other KBs.

Next steps
----------

- See how an agent combines schema search and aliases into an end-to-end
  :ref:`Example: a text-to-SQL agent <Example: a text-to-SQL agent>`  workflow.

- Walk through aliases and search together in the :ref:`Examples <Examples>`  .
