See whether your warehouse can produce customer-level margin— without connecting it
Run one metadata-only query. Usually takes under two minutes. NemulAI never receives warehouse credentials or customer records.
What actually leaves your systems
Sent to NemulAI
- ✓Table names
- ✓Column names
- ✓Column types (e.g. BIGINT, TIMESTAMPTZ)
Never sent, never asked for
- ✕Database credentials or connection strings
- ✕OAuth grants or production access
- ✕An SDK, agent, or proxy in your stack
- ✕Customer names, prompts, or any row of data
Nothing is stored until you choose to save it — the analysis below runs anonymously. When you do run the audit, you run our generated read-only SQL yourself, in your own warehouse, and upload the results. We never connect to your database.
Metadata only. NemulAI receives table names, column names, data types and relationships — never rows, values or credentials, and never a connection to your database.
Choosing a database does not connect NemulAI to it. It only determines which query we generate.
Comma-separated. Defaults to public. Most warehouses keep this data somewhere else — analytics, dbt_prod, raw — and only the schemas you name here are exported.
Your metadata query
One row per column: schema, table, column, data type, whether it is nullable, and any declared primary or foreign key.
-- NemulAI schema metadata.
-- Read-only: catalog reads only, no DML. Review it before running.
-- Scanning schema(s): public
WITH fk AS (
-- One row per (referencing column -> referenced column).
-- The constraint is identified by catalog + schema + name, never name
-- alone, or identically named constraints in two schemas collide.
-- The referenced column is matched POSITIONALLY, so a composite key
-- yields ordered pairs instead of a cartesian product.
SELECT
src.table_schema,
src.table_name,
src.column_name,
tgt.table_schema AS foreign_table_schema,
tgt.table_name AS foreign_table_name,
tgt.column_name AS foreign_column_name
FROM information_schema.key_column_usage src
JOIN information_schema.referential_constraints rc
ON rc.constraint_catalog = src.constraint_catalog
AND rc.constraint_schema = src.constraint_schema
AND rc.constraint_name = src.constraint_name
JOIN information_schema.key_column_usage tgt
ON tgt.constraint_catalog = rc.unique_constraint_catalog
AND tgt.constraint_schema = rc.unique_constraint_schema
AND tgt.constraint_name = rc.unique_constraint_name
AND tgt.ordinal_position = src.position_in_unique_constraint
),
pk AS (
SELECT kcu.table_schema, kcu.table_name, kcu.column_name
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
ON kcu.constraint_catalog = tc.constraint_catalog
AND kcu.constraint_schema = tc.constraint_schema
AND kcu.constraint_name = tc.constraint_name
WHERE tc.constraint_type = 'PRIMARY KEY'
)
SELECT
c.table_schema AS table_schema,
c.table_name AS table_name,
c.column_name AS column_name,
c.data_type AS data_type,
c.is_nullable AS is_nullable,
CASE WHEN pk.column_name IS NOT NULL
THEN 'YES' ELSE 'NO' END AS is_primary_key,
fk.foreign_table_schema AS foreign_table_schema,
fk.foreign_table_name AS foreign_table,
fk.foreign_column_name AS foreign_column
FROM information_schema.columns c
LEFT JOIN pk
ON pk.table_schema = c.table_schema
AND pk.table_name = c.table_name
AND pk.column_name = c.column_name
LEFT JOIN fk
ON fk.table_schema = c.table_schema
AND fk.table_name = c.table_name
AND fk.column_name = c.column_name
WHERE c.table_schema IN ('public')
ORDER BY c.table_schema, c.table_name, c.ordinal_position;Where to run it: psql, TablePlus, DBeaver, pgAdmin — anywhere you normally run a SELECT.
Paste your query results
Copy straight out of your warehouse — comma, tab or pipe separated all work.