Command line interface
The ggsql command line interface lets you execute queries and validate syntax directly from the terminal. While it may not be the most ergonomic way to interact directly with ggsql, it is useful for scripting, automation, and building tools around ggsql.
Installation
Install the ggsql CLI on your system using the standard installation instructions.
Executing a query
You can execute a ggsql query from a string with ggsql exec:
ggsql exec "VISUALISE species AS fill FROM ggsql:penguins DRAW bar"Or run a .gsql file:
ggsql run my_query.gsqlIn both cases, output is written to stdout as a Vega-Lite JSON spec. You can redirect it to a file:
ggsql run my_query.gsql > chart.vl.jsonSuch files can be rendered as images using tools that work with Vega-Lite specs, such as the Online Vega Editor or vl-convert command line tool. For example,
vl-convert vl2png -i chart.vl.json -o chart.pngggsql can also render an image itself, without a second tool — see Output format below.
A standard SQL query can also be provided to ggsql. If the query returns a table, the resulting values will be written to stdout.
Validating a query
If you only want to check that a query is syntactically valid without executing it, use ggsql validate:
$ ggsql validate "VISUALISE x, y FROM table DRAW point"
✓ Query syntax is validDatabase connections
ggsql exec, ggsql run and ggsql view all accept a --reader flag (short -r) that can be used to specify a connection string to be used when executing the query. If not provided, ggsql will use an empty in-memory duckdb connection, equivalent to --reader duckdb://memory.
$ ggsql exec --reader sqlite://sample/ggsql_test.sqlite \
"SELECT * FROM test_table LIMIT 3"
col_a, col_b, col_c
215.87, 75.11, delta
418.78, 71.75, delta
495.75, 12.55, delta
$ ggsql exec --reader odbc://DSN=ggsql-pg-test \
"SELECT * FROM test_table LIMIT 3"
col_a, col_b, col_c
319.34, 91.45, gamma
299.08, 49.36, epsilon
12.5, 29.48, gammaCaching reads
A remote or write-constrained database can be wrapped in an in-memory cache with --cache, naming the backend to cache into (duckdb or sqlite). It is off by default. Repeated reads of the same query — several layers over one table, or the same plot re-run while you iterate on it — are then served from memory instead of the remote:
$ ggsql view --reader odbc://DSN=ggsql-pg-test --cache duckdb \
"SELECT * FROM test_table VISUALISE col_a AS x, col_b AS y DRAW point"The same thing can be spelled as a composite connection string, <cache>+<primary>://…, which is what --cache is rewritten to:
$ ggsql exec --reader duckdb+odbc://DSN=ggsql-pg-test \
"SELECT * FROM test_table LIMIT 3"The two forms cannot be combined — there would be no saying which cache was meant.
Output format
ggsql exec and ggsql run render with the writer named by --writer (short -w), defaulting to --writer vegalite — the Vega-Lite JSON above. A standard build also writes three other formats directly, with nothing to enable:
--writer |
Output | Needs a GPU |
|---|---|---|
vegalite |
A Vega-Lite JSON spec | no |
svg |
An SVG image; text stays selectable and editable | no |
pdf |
A one-page PDF with embedded subset fonts; text stays selectable | no |
hep |
A .hep plot document — the resolved plot rather than a picture |
no |
png |
A PNG image | yes |
jpeg (jpg) |
A JPEG image | yes |
tiff |
A TIFF image | yes |
webp |
A lossless WebP image | yes |
--output picks the writer for you. If you name an output file and leave --writer off, the extension decides — so -o chart.svg writes SVG and -o chart.pdf writes a PDF, with no -w needed:
ggsql exec -o chart.pdf "VISUALISE species AS fill FROM ggsql:penguins DRAW bar"json and vl.json mean Vega-Lite; svg, pdf, hep, png, jpg/jpeg, tif/tiff and webp indicate file extensions. An extension ggsql doesn’t recognise, or no --output at all, falls back to Vega-Lite. An explicit --writer always wins: if it disagrees with the extension, the file holds the format --writer named — a .png written by -w svg is an SVG — and the mismatch is noted on stderr.
A binary format needs somewhere to go. ggsql will not print the bytes of a pdf, hep or raster format to a terminal: it says so on stderr and exits non-zero. Give the bytes a destination with --output, or pipe them somewhere; to just look at the plot, ggsql view draws it in a window and writes no file at all. A query with no VISUALISE prints its table, and honours --output the same way.
The four raster formats render through the GPU, so they need a graphics adapter — hardware or software — available where ggsql runs, and they are not compiled into every build. svg and pdf need neither, which makes them the ones to reach for on a server, in a container or in CI. Run ggsql exec --help to see which writers your build has; asking for one it doesn’t says so, and names the feature that would add it.
hep is the odd one out: it produces no picture at all. It captures the resolved plot — scales, breaks, labels, theme, geometry and data — so a host can draw it itself at any size, and redraw it on resize, without re-running the query.
Writer settings
A writer is configured with --writer-option key=value, repeated once per setting:
ggsql exec --writer png \
--writer-option width=6 \
--writer-option height=4 \
--writer-option units=in \
--writer-option dpi=150 \
--output chart.png \
"VISUALISE species AS fill FROM ggsql:penguins DRAW bar"Several settings can also be collapsed into one flag, separated by ;. With -D short for --writer-option (and --writer-options accepted as well), plus -w for --writer and -o for --output, the same call reads:
ggsql exec -w png -D 'width=6;height=4;units=in;dpi=150' -o chart.png \
"VISUALISE species AS fill FROM ggsql:penguins DRAW bar"Quote the collapsed form. Most shells — bash, zsh, PowerShell — read ; as a command separator, so unquoted it silently runs something else rather than failing. Single quotes, double quotes and \; all work. The two forms mix freely, and a repeated key takes its last value.
; is the only separator; , is not, because values contain commas — background='rgb(255, 0, 0)' has to survive intact.
The canvas
Every writer above except vegalite takes the same five canvas settings:
| Option | Value | Default |
|---|---|---|
width |
Canvas width, in units |
1500 (px) |
height |
Canvas height, in units |
1000 (px) |
units |
px, in, cm, mm, or pt — how width and height are read |
px |
dpi |
Pixels per inch. Sets the print resolution of a physical size, and how large text and other chrome are relative to the canvas | 300 |
background |
Any CSS color, e.g. white, #faf3e0, rgb(0 0 0 / 50%), or transparent |
white |
units applies to the width and height you supply — the defaults are pixel counts either way, so -D 'width=6;units=in' gives a canvas 6 inches wide and 1000 pixels tall.
svg and pdf honour a physical size in the file itself, so -D 'width=6;height=4;units=in;dpi=300' yields a file that prints six inches wide as well as carrying an 1800-pixel coordinate space. For hep, the canvas is recorded as a hint — the aspect and resolution a consumer should default to — rather than a fixed size.
Per-format settings
Each format offers additional settings:
| Writer | Option | Value | Default |
|---|---|---|---|
png |
compression |
none, fast, balanced, small |
balanced |
jpeg |
quality |
1–100 |
90 |
tiff |
compression |
none, deflate, lzw, packbits |
deflate |
svg |
text |
text keeps real text; outline converts it to paths |
text |
svg |
embed-fonts |
Inline font faces so the file stands alone | false |
svg |
id-prefix |
Prefix for generated element ids | none |
pdf |
compress |
Compress the content stream; false makes it readable |
true |
pdf |
links |
Turn markdown links into PDF link annotations | true |
hep |
lossy |
Allow writing a plot the format cannot fully express | false |
hep |
embed-fonts |
Embed the fonts the plot uses | false |
jpeg has no alpha channel, so it refuses background=transparent rather than quietly compositing onto black. svg’s id-prefix matters when two SVGs are inlined in one page: without it, both define the same gradient ids and each resolves to the other’s.
webp is lossless, with no rate control to set, so it takes the canvas settings and nothing else. The Vega-Lite writer takes no options at all: its output is resolution-independent, so size, resolution and background belong to whatever renders the spec. Passing an option a writer doesn’t understand or can’t honour is an error rather than a setting quietly ignored. Such a misunderstanding is reported before the query runs rather than after.
When a plot asks for a graphic a format cannot express, ggsql says so on stderr and still writes the file. This is a defect in the file you produced, so the defect is reported whether or not --verbose is set, and it stays out of a piped svg.
Viewing a plot
ggsql view shows a query’s plot in a native window instead of writing a file, and blocks until the window is closed:
ggsql view "VISUALISE species AS fill FROM ggsql:penguins DRAW bar"Resizing the window re-lays-out the plot rather than stretching it, so labels and gridlines stay correct at any size. -D (--viewer-option) takes width, height, background and title; units and dpi are refused, since a window is sized in logical pixels and its resolution belongs to the display it is on.
Like the raster writers, the viewer renders through the GPU and is not in every build. The subcommand exists either way and says what would enable it.
Documentation
The ggsql CLI has built-in documentation for ggsql syntax and usage. Run ggsql docs for an overview of available documentation topics, and ggsql docs [topic] to read about a specific topic.
$ ggsql docs draw
DRAW is perhaps the most important clause in ggsql as it defines a layer in your
visualisation. A layer is a single instance of a visual representation of a dataset.
[...]A ggsql skill, a usage guide intended for AI assistants and humans, can also be output using the ggsql skill command (also available as ggsql agent-info).
$ ggsql skill
ggsql Query Writer
ggsql is a SQL extension for declarative data visualization based on
Grammar of Graphics principles.
[...]