Table of contents generated with markdown-toc
Most of the heavy lifting features are provided by / built into sqlc, so I do not aim to take credit for them.
β
Supports Sqlite, PostgreSQL and MySQL.
β
Named parameters.*1
β
Multiple queries per file.
β
Database client agnostic.
β
Utility wrappers for popular gleam database libraries (lpil/sqlight, lpil/pog).
β
Automatically pulls schema of your database.
β
Automatically downloads sqlc binary.
*1: meaning that it infers the names of the parameters from your sql queries in the gleam function you
call. f.e. WHERE username = $1
can yield sql.get_user(username:)
. if you have multiple parameters of the same
data types this can avoid confusion and bugs.
$ gleam add parrot
So here is the catch: you can only execute parrot in an erlang gleam application. However the generated code will also run in a javascript environment. So if you need parrot for a javascript project, you can create a separate package and copy over the generated module and that will work.
- Parrot will look for all *.sql files in any sql directory under your project's src directory.
- Each *.sql file can contain as many SQL queries as you want.
- All of the queries will compile into a single
src/[project name]/sql.gleam
module.
Here are some links to help you start out, if you are unfamiliar with the sqlc annotation syntax:
Here is an example of the file structure:
βββ gleam.toml
βββ README.md
βββ src
βΒ Β βββ app.gleam
βΒ Β βββ sql
βΒ Β βββ auth.sql
βΒ Β βββ posts.sql
βββ test
βββ app_test.gleam
# automatically detects database & engine from env (DATABASE_URL by default)
$ gleam run -m parrot
# provide connection string from different environment variable
$ gleam run -m parrot -- -e PG_DATABASE_URL
# specify sqlite file
$ gleam run -m parrot -- --sqlite <file_path>
# see all options
$ gleam run -m parrot help
If you use MySQL, you also need mysqldump (comes by default if you have a mysql client installed)
If you use PostgreSQL, you also need pg_dump (comes by default if you have a postgresql client installed)
You now have type safe access to your sql queries. You might have to write 1-2 wrapper functions for the database client library of your choice.
If you are using lpil/pog or lpil/sqlight, you are in luck! You can find functions to copy & paste into your codebase here: wrappers
An example with lpil/sqlight:
import app/sql
import parrot/dev
fn parrot_to_sqlight(param: dev.Param) -> sqlight.Value {
// ...
}
pub fn main() {
// ...
let #(sql, with, expecting) = sql.get_user_by_username("alice")
let with = parrot_to_sqlight(with)
let row = sqlight.query(sql, on:, with:, expecting:)
// ...
}
If you want to see how this library works in action, take a look at the integration tests:
- PostgreSQL: ./integration/psql
- MySQL: ./integration/mysql
- Sqlite: ./integration/sqlite
just is used to run project commands.
There are scripts to spawn a MySQL or PostgreSQL docker container:
For example:
$ ./bin/mysql.sh
# or
$ ./bin/psql.sh
$ just test-sqlite
$ just test-mysql
$ just test-psql
As with everything in software, there are some quirks with this library, due to the nature of your database of choice and sqlc.
If you have an INTEGER[][]
column in Postgres, pg_dump
does not correctly identify
the column as a two-dimensional array and thereby only give you a List(Int)
instead
of a List(List(Int))
. If this is a problem for you, you can raise an issue and
we might come up with a solution or workaround.
There are a couple of complex data types that are explictly made dynamic
since they are too complex to handle with the current implementation.
There is a plan for a better and more flexible implementation. Until then,
it will be wrapped in a dynamic type.
This library supports everything that sqlc supports. As the time of this writing that would be MySQL, PostgreSQL and Sqlite.
You can read more on language & SQL support here: https://docs.sqlc.dev/en/stable/reference/language-support.html
-
embeddeding structs (https://docs.sqlc.dev/en/stable/howto/embedding.html)
-
Certain query annotations are not supported and will panic the process:
:execrows
,:execlastid
,:batchexec
,:batchone
,:batchmany
,:copyfrom
. You can read more about it here: https://docs.sqlc.dev/en/stable/reference/query-annotations.html
Ideas and actionable tasks are collected and organised here: https://github.com/daniellionel01/parrot/issues
Contributions are welcomed!
- This project was heavily inspired by
squirrel
(Hex, GitHub). Thank you @giacomocavalieri! - Thank you to
sqlc
(GitHub, Website)