Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Added support for the `name` type, represented as a Gleam `String`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))

## 4.5.0 - 2025-10-24

- Squirrel will no longer perform code generation if there's any errors in the
Expand Down
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,20 +191,20 @@ This is needed in two places:

The types that are currently supported are:

| postgres type | encoded as | decoded as |
| ------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| `bool` | `Bool` | `Bool` |
| `text`, `char`, `bpchar`, `varchar`, `citext` | `String` | `String` |
| `float4`, `float8`, `numeric` | `Float` | `Float` |
| `int2`, `int4`, `int8` | `Int` | `Int` |
| `json`, `jsonb` | [`json.Json`](https://hexdocs.pm/gleam_json/gleam/json.html#Json) | `String` |
| `uuid` | [`uuid.Uuid`](https://hexdocs.pm/youid/youid/uuid.html#Uuid) | [`uuid.Uuid`](https://hexdocs.pm/youid/youid/uuid.html#Uuid) |
| `bytea` | `BitArray` | `BitArray` |
| `date` | [`calendar.Date`](https://hexdocs.pm/gleam_time/gleam/time/calendar.html#Date) | [`calendar.Date`](https://hexdocs.pm/gleam_time/gleam/time/calendar.html#Date) |
| `time` | [`calendar.TimeOfDay`](https://hexdocs.pm/gleam_time/gleam/time/calendar.html#TimeOfDay) | [`calendar.TimeOfDay`](https://hexdocs.pm/gleam_time/gleam/time/calendar.html#TimeOfDay) |
| `timestamp` | [`timestamp.Timestamp`](https://hexdocs.pm/gleam_time/gleam/time/timestamp.html#Timestamp) | [`timestamp.Timestamp`](https://hexdocs.pm/gleam_time/gleam/time/timestamp.html#Timestamp) |
| `<type>[]` (where `<type>` is any supported type) | `List(<type>)` | `List(<type>)` |
| user-defined enum | [Gleam custom type](https://tour.gleam.run/data-types/custom-types/) | [Gleam custom type](https://tour.gleam.run/data-types/custom-types/) |
| postgres type | encoded as | decoded as |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| `bool` | `Bool` | `Bool` |
| `text`, `char`, `bpchar`, `varchar`, `citext`, `name` | `String` | `String` |
| `float4`, `float8`, `numeric` | `Float` | `Float` |
| `int2`, `int4`, `int8` | `Int` | `Int` |
| `json`, `jsonb` | [`json.Json`](https://hexdocs.pm/gleam_json/gleam/json.html#Json) | `String` |
| `uuid` | [`uuid.Uuid`](https://hexdocs.pm/youid/youid/uuid.html#Uuid) | [`uuid.Uuid`](https://hexdocs.pm/youid/youid/uuid.html#Uuid) |
| `bytea` | `BitArray` | `BitArray` |
| `date` | [`calendar.Date`](https://hexdocs.pm/gleam_time/gleam/time/calendar.html#Date) | [`calendar.Date`](https://hexdocs.pm/gleam_time/gleam/time/calendar.html#Date) |
| `time` | [`calendar.TimeOfDay`](https://hexdocs.pm/gleam_time/gleam/time/calendar.html#TimeOfDay) | [`calendar.TimeOfDay`](https://hexdocs.pm/gleam_time/gleam/time/calendar.html#TimeOfDay) |
| `timestamp` | [`timestamp.Timestamp`](https://hexdocs.pm/gleam_time/gleam/time/timestamp.html#Timestamp) | [`timestamp.Timestamp`](https://hexdocs.pm/gleam_time/gleam/time/timestamp.html#Timestamp) |
| `<type>[]` (where `<type>` is any supported type) | `List(<type>)` | `List(<type>)` |
| user-defined enum | [Gleam custom type](https://tour.gleam.run/data-types/custom-types/) | [Gleam custom type](https://tour.gleam.run/data-types/custom-types/) |

### Enums

Expand Down
28 changes: 28 additions & 0 deletions birdie_snapshots/array_of_names_decoding.accepted
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
version: 1.4.0
title: array of names decoding
file: ./test/squirrel_test.gleam
test_name: array_of_names_decoding_test
---

import gleam/dynamic/decode
import pog

pub type QueryRow {
QueryRow(res: List(String))
}

pub fn query(
db: pog.Connection,
) -> Result(pog.Returned(QueryRow), pog.QueryError) {
let decoder = {
use res <- decode.field(0, decode.list(decode.string))
decode.success(QueryRow(res:))
}

"select '{}'::name[] as res"
|> pog.query
|> pog.returning(decoder)
|> pog.execute(db)
}

30 changes: 30 additions & 0 deletions birdie_snapshots/array_of_names_encoding.accepted
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
version: 1.4.0
title: array of names encoding
file: ./test/squirrel_test.gleam
test_name: array_of_names_encoding_test
---

import gleam/dynamic/decode
import pog

pub type QueryRow {
QueryRow(res: Int)
}

pub fn query(
db: pog.Connection,
arg_1: List(String),
) -> Result(pog.Returned(QueryRow), pog.QueryError) {
let decoder = {
use res <- decode.field(0, decode.int)
decode.success(QueryRow(res:))
}

"select 1 as res where $1 = '{}'::name[]"
|> pog.query
|> pog.parameter(pog.array(fn(value) { pog.text(value) }, arg_1))
|> pog.returning(decoder)
|> pog.execute(db)
}

28 changes: 28 additions & 0 deletions birdie_snapshots/name_decoding.accepted
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
version: 1.4.0
title: name decoding
file: ./test/squirrel_test.gleam
test_name: name_decoding_test
---

import gleam/dynamic/decode
import pog

pub type QueryRow {
QueryRow(res: String)
}

pub fn query(
db: pog.Connection,
) -> Result(pog.Returned(QueryRow), pog.QueryError) {
let decoder = {
use res <- decode.field(0, decode.string)
decode.success(QueryRow(res:))
}

"select 'name'::name as res"
|> pog.query
|> pog.returning(decoder)
|> pog.execute(db)
}

30 changes: 30 additions & 0 deletions birdie_snapshots/name_encoding.accepted
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
version: 1.4.0
title: name encoding
file: ./test/squirrel_test.gleam
test_name: name_encoding_test
---

import gleam/dynamic/decode
import pog

pub type QueryRow {
QueryRow(res: Int)
}

pub fn query(
db: pog.Connection,
arg_1: String,
) -> Result(pog.Returned(QueryRow), pog.QueryError) {
let decoder = {
use res <- decode.field(0, decode.int)
decode.success(QueryRow(res:))
}

"select 1 as res where $1 = 'name'::name"
|> pog.query
|> pog.parameter(pog.text(arg_1))
|> pog.returning(decoder)
|> pog.execute(db)
}

1 change: 1 addition & 0 deletions gleam.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ gleam_erlang = ">= 1.0.0 and < 2.0.0"
gleeunit = ">= 1.0.0 and < 2.0.0"
shellout = ">= 1.6.0 and < 2.0.0"
temporary = ">= 1.0.0 and < 2.0.0"
global_value = ">= 1.0.0 and < 2.0.0"
2 changes: 2 additions & 0 deletions manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ packages = [
{ name = "gleam_time", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "DCDDC040CE97DA3D2A925CDBBA08D8A78681139745754A83998641C8A3F6587E" },
{ name = "gleeunit", version = "1.6.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "FDC68A8C492B1E9B429249062CD9BAC9B5538C6FBF584817205D0998C42E1DAC" },
{ name = "glexer", version = "2.2.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glexer", source = "hex", outer_checksum = "5C235CBDF4DA5203AD5EAB1D6D8B456ED8162C5424FE2309CFFB7EF438B7C269" },
{ name = "global_value", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "global_value", source = "hex", outer_checksum = "23F74C91A7B819C43ABCCBF49DAD5BB8799D81F2A3736BA9A534BD47F309FF4F" },
{ name = "justin", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "justin", source = "hex", outer_checksum = "7FA0C6DB78640C6DC5FBFD59BF3456009F3F8B485BF6825E97E1EB44E9A1E2CD" },
{ name = "mug", version = "3.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "mug", source = "hex", outer_checksum = "49AD2B71690C6A615453D272300951C4BDE19FBF55B167D9C951F5CD89FEC820" },
{ name = "non_empty_list", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "non_empty_list", source = "hex", outer_checksum = "3BF1496B475F3F2CE2EA18157587329812493369B2A7C5B9DCFEA5C007670A3B" },
Expand Down Expand Up @@ -57,6 +58,7 @@ gleam_stdlib = { version = ">= 0.51.0 and < 2.0.0" }
gleam_time = { version = ">= 1.2.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
glexer = { version = ">= 2.1.0 and < 3.0.0" }
global_value = { version = ">= 1.0.0 and < 2.0.0" }
justin = { version = ">= 1.0.1 and < 2.0.0" }
mug = { version = ">= 3.0.0 and < 4.0.0" }
non_empty_list = { version = ">= 2.1.0 and < 3.0.0" }
Expand Down
68 changes: 34 additions & 34 deletions src/squirrel.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -63,52 +63,52 @@ const squirrel_version = "v4.5.0"
/// > that dependency to your project.
///
pub fn main() {
case parse_cli_args(), connection_options() {
Error(Nil), _ -> {
case parse_cli_args() {
Error(Nil) -> {
help_text()
|> doc.to_string(term_width())
|> io.println

exit(1)
}

// In case we cannot read the connection options we just immediately fail.
_, Error(error) -> {
error.to_doc(error)
|> doc.to_string(term_width())
|> io.println
Ok(mode) ->
case result.try(connection_options(), postgres.connect_and_authenticate) {
Error(error) -> {
error.to_doc(error)
|> doc.to_string(term_width())
|> io.println

exit(1)
}
exit(1)
}

// Otherwise we can walk through the file system and type all the queries,
// connecting to the database.
Ok(mode), Ok(options) -> {
let generated_queries =
walk(project.src())
|> dict.merge(walk(project.test_()))
|> dict.merge(walk(project.dev()))
|> generate_queries(options)

let #(report, status_code) = case mode {
GenerateCode ->
case ensure_no_errors(generated_queries) {
Error(errors) -> report_errors(errors)
Ok(generated_queries) ->
Ok(connection) -> {
let generated_queries =
walk(project.src())
|> dict.merge(walk(project.test_()))
|> dict.merge(walk(project.dev()))
|> generate_queries(connection)

let #(report, status_code) = case mode {
GenerateCode ->
case ensure_no_errors(generated_queries) {
Error(errors) -> report_errors(errors)
Ok(generated_queries) ->
generated_queries
|> write_queries
|> report_written_queries
}

CheckGeneratedCode ->
generated_queries
|> write_queries
|> report_written_queries
|> check_queries
|> report_checked_queries
}

CheckGeneratedCode ->
generated_queries
|> check_queries
|> report_checked_queries
io.println(report)
exit(status_code)
}
}

io.println(report)
exit(status_code)
}
}
}

Expand Down Expand Up @@ -340,7 +340,7 @@ fn walk(from: String) -> Dict(String, List(String)) {
///
fn generate_queries(
directories: Dict(String, List(String)),
connection: postgres.ConnectionOptions,
connection: postgres.Context,
) -> Dict(String, #(List(TypedQuery), List(Error))) {
use _directory, files <- dict.map_values(directories)

Expand Down
Loading