What Is SQLv2? The SQL Dialect That Treats AI as a First-Class Citizen

Published on May 27, 2026

SQLv2: AI functions as first-class SQL primitives

When we shipped the first internal build of what would eventually become SynapCores, the query that convinced me we were on the right track was three lines long:

SELECT GENERATE('Summarize: ' || body)
FROM tickets
WHERE COSINE_SIMILARITY(embedding, EMBED('shipping delays')) > 0.7;

That query does in one SQL statement what would take a Python service, a vector database, and an LLM API in the traditional stack. The reason it can do that isn't because we wrote a magic optimizer — it's because we extended SQL itself to treat embedding, similarity, and generation as first-class operations rather than as foreign function calls.

That extension is what we call SQLv2. This post is the explanation of what SQLv2 is, why we think SQL needed a v2 in the first place, and what the dialect actually contains. The examples are SynapCores' implementation, but the broader argument — that SQL is the right query language for AI workloads if you grow it correctly — applies whether you adopt our dialect or someone else's.

SQL is 50 years old. Why now?

SQL was published in 1974, codified in 1986, and has been the most widely deployed query language on Earth ever since. It survived OOP, NoSQL, MapReduce, and the cloud. Every generation of "SQL is dead" predictions has been wrong because SQL — declarative set-oriented queries over typed tabular data — turns out to be a remarkably durable abstraction.

What's changed is the kind of data we put in those tables and the kind of operations we want to do on them. The original SQL surface had:

  • Numeric and text types.
  • Set operations (joins, unions, intersections).
  • Aggregations.
  • Sorting and projection.

Modern AI workloads add:

  • Vector types (768-dimensional floats, not just scalars).
  • Semantic similarity (not just equality and ranges).
  • Embedding generation (compute the vector from text).
  • Text generation (compute text from text via an LLM).
  • Graph traversal (follow typed edges, not just JOIN keys).
  • ML training and inference (CREATE MODEL, PREDICT).

You can graft these onto classic SQL via UDFs and extensions, and people have — pgvector, MADlib, PostGIS as a precedent for spatial types. But the grafting shows. Vector operators look like punctuation (<->, <=>). LLM calls become procedural Python wrappers. The optimizer doesn't understand any of it, so you can't fuse operations across the AI/SQL boundary.

SQLv2 is the position that AI primitives deserve to be part of the language, not extensions bolted on. The grammar gets a few new tokens; the optimizer gets new operator costs; the type system gets new primitives. That's it. It's still SQL — declarative, set-oriented, planner-driven. It just admits that 2026 SQL has different built-ins than 1986 SQL did.

The four pillars of SQLv2

The four pillars of SQLv2

The extensions are smaller than people expect. There are four conceptual additions:

1. Vector types and operators

A VECTOR(n) is a fixed-dimension float vector. Functions over it:

EMBED(text)                       -- text → vector (uses configured embedding model)
COSINE_SIMILARITY(v1, v2)         -- vector × vector → float [-1, 1]
EUCLIDEAN_DISTANCE(v1, v2)        -- vector × vector → float
DOT_PRODUCT(v1, v2)               -- vector × vector → float

These behave like any other scalar function: planner-aware, indexable (via HNSW — see HNSW Explained), composable in WHERE, ORDER BY, SELECT, JOIN ON.

2. Generation and prediction functions

These are the big leap from classic SQL:

GENERATE(prompt, [model_name])         -- text → text via an LLM
PREDICT(model, features)               -- features → prediction via in-DB model
EXTRACT_ENTITIES(text)                 -- text → set of (entity, type)
CLASSIFY(text, label_set)              -- zero-shot classifier

The reason they belong in SQL is that they're just functions — pure-ish, side-effect-free (with caching), composable. The reason this is a big deal is that the planner can now reason about LLM calls: which rows really need them, can the call be batched, can we cache identical prompts.

For the deeper take on what changes when ML primitives are SQL functions, see What Is In-Database Machine Learning?.

3. Graph clauses in SQL

The third pillar is the addition of GRAPH MATCH (or MATCH ... CONNECTED BY, the syntax varies by implementation) as a clause that walks typed edges:

SELECT t.body, a.name, o.id
FROM tickets t
GRAPH MATCH (t)-[:FILED_BY]->(a:Account)-[:BELONGS_TO]->(o:Org)
WHERE o.industry = 'healthcare';

The grammar exposes the graph engine inside SQL. You can mix GRAPH MATCH with JOIN, WHERE, ORDER BY, LIMIT — the planner treats the graph walk as another join shape and optimizes accordingly. This is what makes GraphRAG expressible as one query instead of an orchestration of three.

4. Model DDL

CREATE MODEL is the DDL counterpart to CREATE TABLE:

CREATE MODEL fraud_scorer
TYPE classification
TARGET is_fraud
FROM transactions
WHERE created_at < '2026-05-01'
ALGORITHM autoML
WITH (max_trials = 50);

ALTER MODEL fraud_scorer RETRAIN
FROM transactions
WHERE created_at < CURRENT_DATE;

DROP MODEL fraud_scorer;

The model is a database object. It's versioned, audited, replicated, backed up, GRANT/REVOKE-able. This is what makes the model lifecycle survivable in production — see the in-DB ML post for the longer treatment.

A side-by-side: same query, classic SQL vs SQLv2

A small but realistic question: "For each open ticket about MRI integration, find the closest past resolution from any account in the same parent org, and let the LLM summarize what we did."

Classic SQL + extensions + Python service

-- Step 1: vector search in DB (pgvector style)
SELECT t.id, t.body
FROM tickets t
WHERE t.status = 'open'
  AND t.embedding <-> (
    SELECT embedding FROM keywords WHERE term = 'MRI integration'
  ) < 0.4
ORDER BY t.embedding <-> ... LIMIT 20;
# Step 2: pull rows, do graph traversal manually
for row in rows:
    # query Neo4j for parent org
    parent = neo4j.run("MATCH (a:Account {id: $a})-[:BELONGS_TO]->(o) RETURN o", a=row['account_id'])
    # query past tickets in same org
    past = neo4j.run("MATCH (o)<-[:BELONGS_TO]-(a2)<-[:FILED_BY]-(t2) WHERE o.id = $o RETURN t2", o=parent['id'])
    # call vector DB again for closest past
    closest = pinecone.query(vector=row['embedding'], filter={'id': [p['id'] for p in past]})
    # call LLM
    summary = openai.chat.completions.create(...)
    # write back to DB
    db.execute("INSERT INTO ticket_suggestions ...")

Three services, two languages, six round-trips per ticket. The Python is fine; the assembly is the bug surface.

SQLv2

WITH open_tickets AS (
  SELECT t.id, t.body, t.embedding, t.account_id
  FROM tickets t
  WHERE t.status = 'open'
    AND COSINE_SIMILARITY(t.embedding, EMBED('MRI integration')) > 0.6
)
INSERT INTO ticket_suggestions (ticket_id, summary)
SELECT ot.id,
       GENERATE(
         'Summarize what was done in this past resolution: ' || past_r.body
       )
FROM open_tickets ot
GRAPH MATCH
  (Account {id: ot.account_id})-[:BELONGS_TO]->(o:Org)
  <-[:BELONGS_TO]-(:Account)
  <-[:FILED_BY]-(past_t:Ticket)
  -[:HAS_RESOLUTION]->(past_r:Resolution)
ORDER BY COSINE_SIMILARITY(past_t.embedding, ot.embedding) DESC
LIMIT 1;

One query, one transaction, one round-trip from the application. The query is longer than a classic SQL statement because it's doing more — but it's all in one engine, which means it can be optimized as one plan.

What the planner can do that a pipeline can't

The reason putting these in SQL matters isn't ergonomic. It's that the planner can now reason about the whole operation.

Three concrete optimizations the SynapCores planner does that a multi-service pipeline can't:

  1. Predicate pushdown into the embedding step. If your query has WHERE created_at > NOW() - INTERVAL '7 days', the planner can filter rows before calling EMBED(), saving thousands of embedding calls per query. A pipeline would call EMBED on every row, then filter.

  2. Batching across rows. When GENERATE() is called over a set, the planner batches the LLM calls (5-50 per request) instead of issuing one HTTP per row. Latency drops by an order of magnitude for batch inference.

  3. Vector + graph fusion. When GRAPH MATCH is followed by ORDER BY COSINE_SIMILARITY(...), the planner can use the vector index during the graph walk instead of after, drastically reducing the candidate set.

None of these are exotic. They're standard planner optimizations applied to operations the planner wouldn't have known about in classic SQL.

What SQLv2 is not

A few things I want to make explicit because the phrase gets misused:

  • It's not a replacement for SQL. Every classic SQL statement still works. SELECT, JOIN, WHERE, GROUP BY, HAVING, window functions — all unchanged. SQLv2 adds; it doesn't subtract.
  • It's not vendor lock-in by another name. The four pillars (vector types, generation/prediction functions, graph clauses, model DDL) are conceptually present in multiple engines now — BigQuery ML, Redshift ML, Snowflake Cortex, Databricks SQL functions, our own SynapCores. The syntax varies; the category is converging.
  • It's not "SQL with AI buzzwords." It's a small grammar extension and a real planner update. If a vendor claims SQLv2 features but the AI calls are just opaque UDFs the planner can't reason about, they're claiming the marketing without the engineering.

How we got the dialect

The SynapCores SQLv2 dialect was built around four design rules:

  1. If a real engineer would reach for a UDF, make it a function. Don't invent new syntax when a function call works.
  2. If the optimizer needs to know about it, make it a typed primitive. That's why VECTOR(n) is a type, not a JSONB blob.
  3. If it's a new lifecycle, make it DDL. Models get CREATE MODEL, not procedural API calls, because lifecycles need ALTER/DROP/RENAME.
  4. Stay set-oriented. Every new operator works over sets, not single rows. This is what keeps SQL recognizable as SQL.

The complete reference lives in our AI-Native Database SQL Reference post — that's the long-form spec. This post is the why.

For the comparative angle against vanilla Postgres, see SQLv2 vs PostgreSQL.

Adoption path

If you want to try SQLv2 without changing your stack: install the SynapCores Community Edition, point a DBeaver/psql client at it, and translate one of your existing AI queries. The translation is usually obvious — EMBED(text) replaces a Python service call; GRAPH MATCH replaces a recursive CTE or a Neo4j roundtrip; GENERATE() replaces an OpenAI HTTP call.

The honest version of this is: the queries get shorter, the deployment gets simpler, and the gotchas move. Instead of fighting four services to stay in sync, you fight one engine to express what you want. Different problem, smaller surface area.

The honest version

We don't have customer logos yet. SQLv2 is the dialect we built for ourselves because we got tired of writing the same pipeline glue across products. If you want help porting your AI queries into SQLv2 on your real data, the Agent Memory JumpStart is the 2-4 week sprint where we wire SynapCores into your workflow — including translating your existing Python orchestration into queries the planner can actually optimize. Free Design Partner track or fixed-fee Paid Pilot from $5,000.

For where SQLv2 stops being theoretical: download the CE and run SELECT GENERATE('Hello') once. That's the smallest, most boring SQLv2 query, and it should not work in classic SQL. That it does — without a Python service — is the whole point.