Open-source project
posit-dev/ggsql avatar
posit-dev/ggsql

ggsql: a SQL extension that draws the chart as part of the query

A SQL extension for declarative data visualisation based on the Grammar of Graphics.

567 stars31 forksRustMIT

At a glance

What is it?
ggsql extends SQL with a VISUALISE and DRAW syntax based on the Grammar of Graphics, so a query can specify the marks, the scales and the labels in the same statement that fetches the data. It is written in Rust by Posit, it compiles to WASM so it runs in a browser playground, and it is still in alpha with the reader and writer support deliberately narrow for now.
Who is it for?
Adopt ggsql if you are a data analyst who works in SQL and wants a chart specification in the same statement as the query, especially if you use notebooks, since the Jupyter kernel removes the extract-then-import round trip. Do not adopt it for saved, long-lived queries yet, because the release is alpha and the syntax is explicitly subject to change, and do not expect it to read your database today, since the readers are DuckDB and SQLite with more planned.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What it is, and the idea in one example

ggsql is a SQL extension for declarative data visualization based on the Grammar of Graphics, and the one-line description of what it does is that it allows you to write queries that combine SQL data retrieval with visualization specifications in a single, composable syntax.

The example in the README is the clearest possible demonstration:

ggsql
SELECT date, revenue, region
FROM sales
WHERE year = 2024

VISUALISE date AS x, revenue AS y, region AS color
DRAW line
SCALE x
  SETTING breaks => 'month'
LABEL title => 'Sales by Region'

Everything above the VISUALISE keyword is ordinary SQL. Everything below it is the visualisation. The three clauses that follow SELECT date, revenue, region map the columns onto visual roles, x for the horizontal position, y for the vertical and color for the grouping. The DRAW line chooses the mark type, so a line rather than points or bars. The SCALE x SETTING breaks => 'month' says how the x axis should be broken up, and the LABEL clause sets the title.

There is no separate specification document, no layer object, no chart configuration file. The visualisation is part of the query, and it is composed in the same way SQL is composed, with keywords and clauses rather than nested objects.

That last point is the design decision that makes the project different from every other declarative charting library. A typical approach has a language where you build a chart with function calls or a nested structure, and the data is passed in separately. Here the visualisation language is an extension of a language you already write, so the whole thing is one statement and one thing to read.

The primary language is Rust, the licence is MIT, the default branch is main, and the homepage is ggsql.org. The recent releases are v0.5.2 from 2026-09-11, v0.5.1 from 2026-09-11 and v0.5.0 from 2026-09-11, all on the same day, which is the pattern of a release prepared and then published in stages. The last push was on 2026-10-16 with the project not archived.

Why SQL, and the Grammar of Graphics

The Why section is about the people using it rather than about the technology.

Many data analysts are naturally at home in SQL and spend more time there than in a programming language like Python or R. Having to extract data, context switch to a new programming language, import data and so on is cumbersome when all you want to do is understand the data you are working with right now.

That is a real and under-discussed problem. The workflow of a data analyst in a notebook is often extract, then paste into another environment, then import, then plot, and the round trip through a language boundary is pure friction when the question is a simple one about the shape of the data.

ggsql is built for immediate familiarity and alignment with the SQL language, and it is built on the foundation of the grammar of graphics known from ggplot2, which affords a composable syntax capable of simple as well as arbitrarily complex visualizations.

The ggplot2 reference is doing specific work here. The Grammar of Graphics is the idea that a plot is a small number of independent decisions: what data, what marks, what aesthetic mappings, what scales, what coordinate system, what faceting. Once you separate a plot into those parts, you can compose them, and the same set of rules covers a scatter plot and a faceted small-multiple dashboard.

So the vocabulary of ggsql, the aesthetic mappings, the scales and the marks, is not invented for SQL. It is the vocabulary that a large number of people already know from R, translated into a query language.

The syntax has also been designed to be easy to learn, read and write, and the README makes a further point about that: it is a great fit for AI agents to produce, as the output query is immediately easy to understand and validate by the user, so that you can have certainty in its validity.

That claim is worth taking seriously. A chart specification written as a function call tree is hard for a person to check at a glance. A chart specification written as a short block of SQL clauses is something a person can read and confirm in seconds, which matters more for a generated query than the elegance of the generated code.

Project status, and the honest scope of what works today

The Project status section is short and unusually honest about the limits.

The current release is in alpha, with the main architectural parts finished but still subject to change. Future development will focus on adding new readers, which is database support, and writers, which is output types, to complement the DuckDB and SQLite plus Vega-Lite setup the project has focused on during early development.

So the current scope is a deliberately small closed loop. DuckDB and SQLite are the two readers, and Vega-Lite is the output format. That combination is a sensible one to start with, because DuckDB and SQLite are both embedded and dependency-free, which means a reader can be added to the workspace without pulling in a server, and Vega-Lite is a JSON specification for charts that most visualisation tools can consume.

The readers and writers vocabulary is the useful part of that sentence, and it tells you how the project thinks about extensibility. A reader is a source of data, a writer is a destination for the chart. Anything that can be turned into Arrow tables is a candidate reader, and anything that can be expressed as a chart specification is a candidate writer. The architecture is described as finished, which means the extension points are in place and adding a database is a matter of implementing one interface.

The alpha warning is not a formality in a project where the whole point is the syntax. If the clause names change, your saved queries change meaning, and a project that asks people to write a new kind of query has to be honest about that.

The installation section points at the website rather than giving commands, asking you to follow the instructions on the website for up to date information on how to install ggsql. That is a deliberate choice for a project with several install targets and a Rust toolchain, and it also means the README does not carry the commands themselves.

The four ways to run it, including a browser playground

The Try it out section contains the most immediately useful fact in the README.

ggsql compiles to WASM and can thus be embedded in a website, and you can try it out on the playground with no installation required.

That is a significant design choice, and it is more than a convenience. A query language for visualisation needs to be runnable somewhere to be worth learning, and the alternatives are a local install of a Rust-built binary, which is a barrier for a data analyst, or a hosted service, which is a barrier for anyone who does not want their data uploaded. Compiling to WASM means the whole engine, including the SQL parsing and the chart generation, runs in the browser.

The repository layout shows how many front ends there are. The workspace members are tree-sitter-ggsql, src, ggsql-cli, ggsql-jupyter and ggsql-wasm, and the default members are the first four, which means the WASM crate is built separately rather than by default.

The front ends are the interesting part. ggsql-cli is the command line tool, the natural way to use it against a local database. ggsql-jupyter is a Jupyter kernel, which is the answer for the analyst in a notebook who wants a chart without leaving the notebook. ggsql-wasm is the browser build. And ggsql-vscode is a separate top-level directory rather than a workspace member, so the editor extension is versioned separately from the core.

The tree-sitter-ggsql member explains how the syntax gets tooling support. Tree-sitter is a parser generator that produces a syntax tree usable by editors, and by generating one for ggsql you get syntax highlighting, bracket matching and structural editing in any editor with Tree-sitter support, including VS Code through the extension. That is the mechanism behind the claim that the syntax is easy to read and write, since a language with a grammar can be highlighted properly rather than left as plain text.

The Jupyter kernel and the VS Code extension together are what turn a query language into something an analyst actually uses, and the playground is what lets a curious person try it with no commitment at all.

The dependency choices, and the Rust version that is not a bump

The workspace manifest is where the technical decisions show, and one of them is remarkable enough to deserve its own section.

The Rust version is pinned to 1.86, and the comment explaining why is unusually informative. It is CRAN's maximum Rust version, which the R package's vendored copy of ggsql has to build against, and the instruction is to only bump this when CRAN does. Declaring it there also points clippy's MSRV-aware lints at 1.86, so an accidental use of a 1.87 or newer standard library API is a clear lint rather than a cryptic E0658 at vendoring time. The comment calls that guard the main reason not to raise it to match a dependency.

That is a good example of a version constraint explained by its purpose rather than asserted. The floor is not the newest Rust, it is the oldest Rust that CRAN's toolchain allows, and the comment explains how the project keeps itself honest against that floor without giving up the lints.

There is a second, sharper problem described right below. The parley dependency, which is non-optional for the default svg, pdf and hep writers, declares 1.88 while compiling fine on 1.86, so cargo's floor check refuses to build on 1.86 until you pass --ignore-rust-version. The comment notes that CI proves the code itself still builds there, and points at the CRAN-toolchain step in build.yaml.

So the project is running a deliberately old Rust floor, has a dependency that disagrees with it, and works around that with a flag, and has a CI job that verifies the floor actually holds. That combination of careful comment and CI check is what separates a deliberate constraint from a neglected one.

The dependencies themselves describe the architecture. tree-sitter at 0.26 for parsing. Arrow at 58 with default features off, which is the data container, so tables move between readers and writers as Arrow rather than as something bespoke. duckdb at about 1.10502 with the bundled and vtab-arrow features, and rusqlite at 0.38 with bundled, chrono and load_extension, which are the two current readers. parquet at 58 with the arrow and snap features is a file format, and bytes and csscolorparser are supporting pieces.

The workspace also holds tree-sitter-ggsql and the core ggsql package as path dependencies at the same version, 0.5.2.

How to evaluate it, and what to check first

There are four things to establish.

First, whether the alpha status matters for your use. The main architectural parts are finished but subject to change, and the change that would hurt is a change to the visualisation clause names, because that changes the meaning of queries you have saved. For a scratch analysis, that is irrelevant. For a stored query in a dashboard, it is the whole question. Read the CHANGELOG.md in the repository and see how the syntax has moved between the 0.5.0, 0.5.1 and 0.5.2 releases before you commit.

Second, whether DuckDB, SQLite and Vega-Lite are enough for you. The plan is to add more readers and writers, but today those are the three you have. If your data sits in Postgres or BigQuery, the current release cannot read it, and that is a hard limit rather than a configuration detail.

Third, how you will run it. The CLI against a local database is the most direct route. The Jupyter kernel is the route for a notebook-based analyst, and it removes the context switch that the Why section complains about. The WASM playground costs nothing and is the right place to learn the syntax. And the VS Code extension is the one to install if you intend to write these queries by hand regularly, since it gives you the syntax highlighting that the Tree-sitter grammar exists to provide.

Fourth, what the AI agent claim means in practice. The README argues the syntax is a good fit for agents because a person can read and validate the output query quickly. That is a reasonable argument, and it is easier to check a query than to check a chart specification written as function calls. But an agent generating ggsql still needs the correct column names and the right value ranges, and nothing in the syntax solves that.

What you get in exchange is a genuinely different way to do exploratory visualisation, one where the query and the chart are the same artefact. For a data analyst who lives in SQL, that removes a step from a task that happens many times a day.

The most interesting thing about the project, though, is the Rust version comment. A tool that pins its compiler floor to the maximum CRAN allows, explains why in the manifest, works around a dependency that disagrees, and adds a CI job to prove the floor holds, is a project that has thought carefully about its constraints. That is a good sign for the architecture being finished, even while the syntax is still moving.

Editorial conclusion

Adopt ggsql if you are a data analyst who works in SQL and wants a chart specification in the same statement as the query, especially if you use notebooks, since the Jupyter kernel removes the extract-then-import round trip. Do not adopt it for saved, long-lived queries yet, because the release is alpha and the syntax is explicitly subject to change, and do not expect it to read your database today, since the readers are DuckDB and SQLite with more planned. Before you commit, read the CHANGELOG to see how the clause names have moved between the 0.5 releases, confirm your data source is one of the current readers, and try the WASM playground first, since it costs nothing and teaches the syntax without a Rust toolchain, then add the VS Code extension if you intend to write these queries by hand.

Frequently asked questions

What is ggsql?

It is a SQL extension for declarative data visualization based on the Grammar of Graphics, written in Rust and MIT licensed by Posit. It lets you write queries that combine SQL data retrieval with visualization specifications in a single composable syntax, using VISUALISE to map columns to aesthetic roles and clauses such as DRAW and SCALE to choose marks and scales.

Which databases and output formats does ggsql support?

The current release focuses on a DuckDB and SQLite setup with Vega-Lite output. The project status notes that future development will focus on adding new readers, meaning database support, and writers, meaning output types, to complement that setup, with the main architectural parts already finished.

Can I try ggsql without installing it?

Yes. ggsql compiles to WASM and can be embedded in a website, and there is a playground that requires no installation. The repository also contains a command line tool, a Jupyter kernel, a WASM crate and a separate VS Code extension directory.

Why is ggsql built on the Grammar of Graphics?

The project is built on the foundation of the grammar of graphics known from ggplot2, which affords a composable syntax capable of simple as well as arbitrarily complex visualizations. The vocabulary of aesthetic mappings, scales and marks is therefore one many analysts already know, translated into a query language rather than a function-call API.

What is ggsql's project status?

The current release is in alpha, with the main architectural parts finished but still subject to change. The recent releases are 0.5.0, 0.5.1 and 0.5.2, all published on 2026-09-11, and the last push to the repository was on 2026-10-16.

Why does the ggsql workspace pin Rust 1.86?

The manifest explains that 1.86 is CRAN's maximum Rust version, which the R package's vendored copy of ggsql has to build against, and that it should only be bumped when CRAN does. Declaring it also points clippy's MSRV-aware lints at 1.86, so using a newer standard library API becomes a clear lint rather than a cryptic error at vendoring time.

Official sources

  1. License: MIT
  2. posit-dev/ggsql on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/posit-dev-ggsql.svg)](https://hysenlabs.com/projects/posit-dev-ggsql)

Was this useful?