Formatting directives and prefixes
On this page
On SQLite:
- directives.prefixes: [sgl, seagull] makes sgl canonical and seagull an alias; require_canonical: true reports each alias
- fmt -l and --check list files; -w rewrites them; without either fmt prints the formatted file
- adopt.yaml lists the other tool's prefix as an alias, so sgl -c adopt.yaml fmt -w rewrites it to sgl
Directives live in SQL comments, so nothing stops them drifting: one person writes seagull.query, another sgl.query, spacing wanders, and queries copied from another tool arrive with that tool's prefix. sgl fmt is to directives what gofmt is to Go. It rewrites every directive to one canonical spelling and spacing, and it never touches the SQL itself. This example formats a small project, makes the canonical prefix a rule, and adopts a query written for another tool.
Prefixes
Which prefixes sgl reads is set in sgl.yaml:
sgl.yaml
# Copyright 2026 PolitePixels Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This project stands against fascism, authoritarianism, and all forms of
# oppression. We built this to empower people, not to enable those who would
# strip others of their rights and dignity.
version: 1
directives:
prefixes: [sgl, seagull]
require_canonical: true
databases:
- name: shop
dialect: sqlite
migrations: db/migrations
queries: db/queries
go:
package: store
out: internal/store
directives.prefixes lists the accepted prefixes. The first, sgl, is canonical; the others are aliases, read exactly the same way, and sgl fmt rewrites them to the canonical one. require_canonical: true asks sgl vet to point out each alias with a Q050 hint.
A prefix that sgl knows belongs to another tool, such as piko, is different. Unless it is listed, sgl does not read it at all, and warns with Q051 rather than ignore it in silence.
The queries
customers.sql uses the seagull alias, with spacing nobody would choose:
db/queries/customers.sql
-- seagull.query( GetCustomer ,one )
SELECT id, name, email FROM customers WHERE id = ?;
-- seagull.query(ListCustomers,many)
SELECT id, name, email FROM customers ORDER BY name;
imported.sql came from another tool. Its query header uses sgl, but its parameter directive still uses that tool's piko:
db/queries/imported.sql
-- sgl.query(OrdersForCustomer, many)
-- ?1 as piko.param(customer)
SELECT id, total FROM orders WHERE customer_id = ? ORDER BY id;
orders.sql is already canonical.
The session
sgl fmt -l lists each file whose directives are not in canonical form, and exits 0. sgl fmt --check lists the same files and exits 1 when there are any, which is the one to put in CI or a pre-commit hook.
sgl vet reports two Q050 hints in customers.sql, with a caret under each seagull and a suggestion of sgl. In imported.sql it reports a Q051 warning on piko.param. Because that directive is not read, the parameter does not get the name it asks for: it is named customer_id, after the column it is compared with, instead of customer. The warning says what to do: add piko to directives.prefixes to accept it, then run sgl fmt -w to rewrite it to sgl. Hints never fail a run, even with --strict, so sgl fmt --check is the way to enforce the canonical prefix.
sgl fmt db/queries/customers.sql, with neither -l nor -w, prints the file as it would write it: the sgl prefix, no stray spaces, and a space after each comma.
sgl fmt -w rewrites the files in place. It leaves imported.sql alone, because piko is not one of its prefixes.
sgl -c adopt.yaml fmt -w follows the warning's advice for a single run. adopt.yaml is sgl.yaml with piko added to the list:
adopt.yaml
# Copyright 2026 PolitePixels Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This project stands against fascism, authoritarianism, and all forms of
# oppression. We built this to empower people, not to enable those who would
# strip others of their rights and dignity.
version: 1
directives:
prefixes: [sgl, seagull, piko]
databases:
- name: shop
dialect: sqlite
migrations: db/migrations
queries: db/queries
Listing a foreign prefix accepts it as an alias, so this run rewrites piko.param to sgl.param, and cat shows the result. You could add piko to sgl.yaml itself instead, if queries in both spellings will keep arriving; a separate file keeps the project's own configuration strict.
sgl fmt --check and sgl vet now pass with nothing to report.
sgl fmt --rewrite-prefix seagull db/queries/orders.sql goes the other way, rewriting every directive to the prefix you name. Without -w it only prints the result; with -w it switches a whole project to a new spelling.
Here is the session as recorded. Each $ line is a command, the lines after it are what it printed, and # exit N is its exit status.
# customers.sql uses the seagull. alias and loose spacing. fmt -l lists every file
# whose directives are not in canonical form.
$ sgl fmt -l
db/queries/customers.sql
# exit 0
# --check lists the same files and exits 1, for CI.
$ sgl fmt --check
db/queries/customers.sql
# exit 1
# sgl.yaml sets directives.require_canonical, so vet reports each alias with a Q050
# hint. imported.sql came from another tool: sgl does not parse a directive written with
# that tool's prefix, so it warns with Q051, and the parameter is named customer_id,
# after the column, instead of customer.
$ sgl vet
db/queries/customers.sql:1:6: hint[Q050]: directive prefix "seagull" is an alias; write "sgl"
|
1 | -- seagull.query( GetCustomer ,one )
| ^^^^^^^
help: did you mean "sgl"?
db/queries/customers.sql:4:4: hint[Q050]: directive prefix "seagull" is an alias; write "sgl"
|
4 | -- seagull.query(ListCustomers,many)
| ^^^^^^^
help: did you mean "sgl"?
db/queries/imported.sql:2:10: warning[Q051]: -- piko.param is not enabled here; add piko to directives.prefixes to accept it, then optionally format the directives to rewrite it to the canonical prefix
|
2 | -- ?1 as piko.param(customer)
| ^^^^^^^^^^
help: once the prefix is accepted, `sgl fmt -w` rewrites it to the canonical prefix
0 error(s), 1 warning(s), 2 hint(s)
# exit 0
# Without -w, fmt prints a file as it would write it.
$ sgl fmt db/queries/customers.sql
-- sgl.query(GetCustomer, one)
SELECT id, name, email FROM customers WHERE id = ?;
-- sgl.query(ListCustomers, many)
SELECT id, name, email FROM customers ORDER BY name;
# exit 0
# -w rewrites the files in place.
$ sgl fmt -w
# exit 0
# The other tool's prefix is not an alias, so fmt left imported.sql alone. adopt.yaml
# accepts it as one for a single run, which rewrites it to sgl.
$ sgl -c adopt.yaml fmt -w
# exit 0
$ cat db/queries/imported.sql
-- sgl.query(OrdersForCustomer, many)
-- ?1 as sgl.param(customer)
SELECT id, total FROM orders WHERE customer_id = ? ORDER BY id;
# exit 0
# Every directive is canonical now, and vet is clean.
$ sgl fmt --check
# exit 0
$ sgl vet
0 error(s), 0 warning(s), 0 hint(s)
# exit 0
# --rewrite-prefix rewrites directives to another accepted prefix, here printing
# orders.sql in the seagull. spelling.
$ sgl fmt --rewrite-prefix seagull db/queries/orders.sql
-- seagull.query(ListOrders, many)
SELECT id, customer_id, total FROM orders ORDER BY id;
# exit 0
sgl fmt runs seagull.FormatDirectives, which a tool that embeds Seagull can call itself, as White-label presets does to move a project to its own prefix.
Run it locally
From the directory you cloned the Seagull repository into:
cd seagull/examples/tooling/044_fmt_and_prefixes && go run seagull.sh/seagull/cmd/sgl vet
The files
Every file of the example, as it is in the Seagull repository.
sgl.yaml
# Copyright 2026 PolitePixels Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This project stands against fascism, authoritarianism, and all forms of
# oppression. We built this to empower people, not to enable those who would
# strip others of their rights and dignity.
version: 1
directives:
prefixes: [sgl, seagull]
require_canonical: true
databases:
- name: shop
dialect: sqlite
migrations: db/migrations
queries: db/queries
go:
package: store
out: internal/store
db/migrations/0001_init.up.sql
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers (id),
total INTEGER NOT NULL
);
db/migrations/0001_init.down.sql
DROP TABLE orders;
DROP TABLE customers;
db/queries/customers.sql
-- seagull.query( GetCustomer ,one )
SELECT id, name, email FROM customers WHERE id = ?;
-- seagull.query(ListCustomers,many)
SELECT id, name, email FROM customers ORDER BY name;
db/queries/imported.sql
-- sgl.query(OrdersForCustomer, many)
-- ?1 as piko.param(customer)
SELECT id, total FROM orders WHERE customer_id = ? ORDER BY id;
db/queries/orders.sql
-- sgl.query(ListOrders, many)
SELECT id, customer_id, total FROM orders ORDER BY id;
adopt.yaml
# Copyright 2026 PolitePixels Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This project stands against fascism, authoritarianism, and all forms of
# oppression. We built this to empower people, not to enable those who would
# strip others of their rights and dignity.
version: 1
directives:
prefixes: [sgl, seagull, piko]
databases:
- name: shop
dialect: sqlite
migrations: db/migrations
queries: db/queries
transcript.txt
# customers.sql uses the seagull. alias and loose spacing. fmt -l lists every file
# whose directives are not in canonical form.
$ sgl fmt -l
db/queries/customers.sql
# exit 0
# --check lists the same files and exits 1, for CI.
$ sgl fmt --check
db/queries/customers.sql
# exit 1
# sgl.yaml sets directives.require_canonical, so vet reports each alias with a Q050
# hint. imported.sql came from another tool: sgl does not parse a directive written with
# that tool's prefix, so it warns with Q051, and the parameter is named customer_id,
# after the column, instead of customer.
$ sgl vet
db/queries/customers.sql:1:6: hint[Q050]: directive prefix "seagull" is an alias; write "sgl"
|
1 | -- seagull.query( GetCustomer ,one )
| ^^^^^^^
help: did you mean "sgl"?
db/queries/customers.sql:4:4: hint[Q050]: directive prefix "seagull" is an alias; write "sgl"
|
4 | -- seagull.query(ListCustomers,many)
| ^^^^^^^
help: did you mean "sgl"?
db/queries/imported.sql:2:10: warning[Q051]: -- piko.param is not enabled here; add piko to directives.prefixes to accept it, then optionally format the directives to rewrite it to the canonical prefix
|
2 | -- ?1 as piko.param(customer)
| ^^^^^^^^^^
help: once the prefix is accepted, `sgl fmt -w` rewrites it to the canonical prefix
0 error(s), 1 warning(s), 2 hint(s)
# exit 0
# Without -w, fmt prints a file as it would write it.
$ sgl fmt db/queries/customers.sql
-- sgl.query(GetCustomer, one)
SELECT id, name, email FROM customers WHERE id = ?;
-- sgl.query(ListCustomers, many)
SELECT id, name, email FROM customers ORDER BY name;
# exit 0
# -w rewrites the files in place.
$ sgl fmt -w
# exit 0
# The other tool's prefix is not an alias, so fmt left imported.sql alone. adopt.yaml
# accepts it as one for a single run, which rewrites it to sgl.
$ sgl -c adopt.yaml fmt -w
# exit 0
$ cat db/queries/imported.sql
-- sgl.query(OrdersForCustomer, many)
-- ?1 as sgl.param(customer)
SELECT id, total FROM orders WHERE customer_id = ? ORDER BY id;
# exit 0
# Every directive is canonical now, and vet is clean.
$ sgl fmt --check
# exit 0
$ sgl vet
0 error(s), 0 warning(s), 0 hint(s)
# exit 0
# --rewrite-prefix rewrites directives to another accepted prefix, here printing
# orders.sql in the seagull. spelling.
$ sgl fmt --rewrite-prefix seagull db/queries/orders.sql
-- seagull.query(ListOrders, many)
SELECT id, customer_id, total FROM orders ORDER BY id;
# exit 0