Deep Agents
AgentContextOrchestratorRetrievalText2SQLToolbox

Grounding

Configure database introspection to provide rich context for SQL generation

Grounding is Text2SQL's database introspection system. It extracts schema metadata, statistics, and relationships from your database to give the AI rich context for generating accurate SQL queries.

Why Grounding Matters

Without grounding, the AI would need to guess table names, column types, and relationships. Grounding provides:

  • Schema awareness - Tables, columns, and their types
  • Relationship mapping - Foreign keys and join paths
  • Performance hints - Indexes, row counts, and size classifications
  • Data patterns - Low cardinality values (enums) and column statistics

Quick Start

Each database adapter (PostgreSQL, SQLite, SQL Server, MySQL/MariaDB) exports grounding factory functions. Import them and pass to your adapter:

Reuse the shared model setup from Getting Started in the examples below.

import { Text2Sql } from '@deepagents/text2sql';
import { Postgres } from '@deepagents/text2sql/postgres';
import * as postgres from '@deepagents/text2sql/postgres';

const text2sql = new Text2Sql({
  version: 'v1',
  model,
  adapters: {
    main: new Postgres({
      execute: async (sql) => pool.query(sql).then(r => r.rows),
      grounding: [
        postgres.tables(),
        postgres.views(),
        postgres.info(),
        postgres.indexes(),
        postgres.constraints(),
        postgres.rowCount(),
        postgres.columnStats(),
        postgres.columnValues(),
      ],
    }),
  },
});
import { Text2Sql } from '@deepagents/text2sql';
import { Sqlite } from '@deepagents/text2sql/sqlite';
import * as sqlite from '@deepagents/text2sql/sqlite';

const text2sql = new Text2Sql({
  version: 'v1',
  model,
  adapters: {
    main: new Sqlite({
      execute: (sql) => db.prepare(sql).all(),
      grounding: [
        sqlite.tables(),
        sqlite.views(),
        sqlite.info(),
        sqlite.indexes(),
        sqlite.constraints(),
        sqlite.rowCount(),
        sqlite.columnStats(),
        sqlite.columnValues(),
      ],
    }),
  },
});
import { Text2Sql } from '@deepagents/text2sql';
import { SqlServer } from '@deepagents/text2sql/sqlserver';
import * as sqlserver from '@deepagents/text2sql/sqlserver';

const text2sql = new Text2Sql({
  version: 'v1',
  model,
  adapters: {
    main: new SqlServer({
      execute: async (sql) => {
        const result = await pool.request().query(sql);
        return result.recordset;
      },
      grounding: [
        sqlserver.tables(),
        sqlserver.views(),
        sqlserver.info(),
        sqlserver.indexes(),
        sqlserver.constraints(),
        sqlserver.rowCount(),
        sqlserver.columnStats(),
        sqlserver.columnValues(),
      ],
    }),
  },
});
import { Text2Sql } from '@deepagents/text2sql';
import { Mysql } from '@deepagents/text2sql/mysql';
import * as mysqlGrounding from '@deepagents/text2sql/mysql';

const text2sql = new Text2Sql({
  version: 'v1',
  model,
  adapters: {
    main: new Mysql({
      execute: async (sql) => {
        const [rows] = await pool.query(sql);
        return rows;
      },
      grounding: [
        mysqlGrounding.tables(),
        mysqlGrounding.views(),
        mysqlGrounding.info(),
        mysqlGrounding.indexes(),
        mysqlGrounding.constraints(),
        mysqlGrounding.rowCount(),
        mysqlGrounding.columnStats(),
        mysqlGrounding.columnValues(),
      ],
    }),
  },
});
import { BigQuery as BigQueryClient } from '@google-cloud/bigquery';

import { Text2Sql } from '@deepagents/text2sql';
import { BigQuery } from '@deepagents/text2sql/bigquery';
import * as bigquery from '@deepagents/text2sql/bigquery';

const client = new BigQueryClient({ projectId: process.env.BQ_PROJECT_ID });

const text2sql = new Text2Sql({
  version: 'v1',
  model,
  adapters: {
    main: new BigQuery({
      projectId: process.env.BQ_PROJECT_ID,
      datasets: ['analytics'],
      execute: async (sql) => {
        const [job] = await client.createQueryJob({ query: sql });
        const [rows] = await job.getQueryResults();
        return rows;
      },
      validate: async (sql) => {
        await client.createQueryJob({ query: sql, dryRun: true, useQueryCache: false });
      },
      grounding: [
        bigquery.tables(),
        bigquery.views(),
        bigquery.info(),
        bigquery.indexes(),
        bigquery.constraints(),
        bigquery.rowCount(),
      ],
    }),
  },
});

Available Groundings

Core Groundings

These groundings discover your database structure:

tables(config?)

Discovers tables and their relationships via foreign key traversal. This is the foundational grounding that other groundings depend on.

tables({
  // Filter tables - array of names, regex, or predicate function
  filter: ['users', 'orders', 'products'],
  // or
  filter: /^app_/,
  // or
  filter: (name) => !name.startsWith('_'),

  // Traverse forward (child → parent) following FK direction
  // true = unlimited depth, number = max depth
  forward: 2,

  // Traverse backward (parent → child) finding referencing tables
  backward: 1,
})
OptionTypeDescription
filterstring[] | RegExp | (name: string) => booleanSelect which tables to include
forwardboolean | numberFollow FKs from child to parent tables
backwardboolean | numberFind tables that reference these tables

views(config?)

Discovers database views and their columns.

views({
  // Same filter options as tables()
  filter: ['active_users', 'sales_summary'],
})

info()

Collects database dialect, version, and connection info. Helps the AI understand database-specific SQL syntax.

info()
// Returns: { dialect: 'postgresql', version: '15.0', database: 'mydb' }

Metadata Groundings

These groundings enhance the schema with performance-relevant metadata. They run after tables() and annotate the discovered tables.

indexes()

Marks which columns have indexes. Helps the AI generate queries that use indexed columns for filtering and sorting.

indexes()
// Annotates columns with isIndexed: true

constraints()

Collects CHECK, UNIQUE, NOT NULL, DEFAULT, PRIMARY KEY, and FOREIGN KEY constraints.

constraints()
// Annotates tables with constraint information

rowCount()

Counts rows per table and classifies by size. Helps the AI avoid expensive full table scans on large tables.

rowCount()
// Annotates tables with rowCount and sizeHint

Size classifications:

Size HintRow Count
tiny< 100
small100 - 999
medium1,000 - 9,999
large10,000 - 99,999
huge>= 100,000

columnStats()

Collects min/max values and null fraction for numeric and date columns.

columnStats()
// Annotates columns with stats: { min, max, nullFraction }

columnValues()

Detects enum-like and low-cardinality columns (like status, type, or category columns) and caches their values.

columnValues()
// Annotates columns with kind: 'LowCardinality' and values: ['active', 'inactive', ...]

Execution Order

Groundings execute in the order you specify. Some groundings depend on others:

  1. tables() must run first - discovers the base schema
  2. views() can run independently
  3. info() can run independently
  4. indexes(), constraints(), rowCount() - run after tables(), annotate discovered tables
  5. columnStats(), columnValues() - run after tables(), annotate columns

Filtering Tables

All filter options work the same way across tables() and views():

// Include specific tables by name
tables({ filter: ['users', 'orders', 'products'] })

// Include tables matching a pattern
tables({ filter: /^public\./ })

// Custom predicate
tables({ filter: (name) => !name.includes('_archive') })

Relationship Traversal

The forward and backward options control how tables() discovers related tables:

// Start with 'orders' and follow FKs to find 'users', 'products', etc.
tables({
  filter: ['orders'],
  forward: 2,  // Follow FKs up to 2 levels deep
})

// Start with 'users' and find all tables that reference it
tables({
  filter: ['users'],
  backward: 1,  // Find immediate children only
})

// Both directions
tables({
  filter: ['orders'],
  forward: true,   // Unlimited forward traversal
  backward: true,  // Unlimited backward traversal
})

Minimal Configuration

For simple use cases, you can use a minimal grounding setup:

import * as postgres from '@deepagents/text2sql/postgres';

grounding: [
  postgres.tables(),
  postgres.info(),
]
import * as sqlite from '@deepagents/text2sql/sqlite';

grounding: [
  sqlite.tables(),
  sqlite.info(),
]
import * as sqlserver from '@deepagents/text2sql/sqlserver';

grounding: [
  sqlserver.tables(),
  sqlserver.info(),
]
import * as mysqlGrounding from '@deepagents/text2sql/mysql';

grounding: [
  mysqlGrounding.tables(),
  mysqlGrounding.info(),
]
import * as bigquery from '@deepagents/text2sql/bigquery';

grounding: [
  bigquery.tables(),
  bigquery.info(),
]

Empty Grounding

Pass an empty array to disable all automatic introspection:

grounding: []

This is useful when providing custom introspection via the adapter's introspect option.

Next Steps