Skip to content

Commit

Permalink
✨ Add support for json and jsonb types
Browse files Browse the repository at this point in the history
  • Loading branch information
giacomocavalieri committed Aug 13, 2024
1 parent 58aa524 commit a35968c
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 13 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# CHANGELOG

## Unreleased

- Added support for the `jsonb` type, encoded as a Gleam `String` and decoded
as a Gleam `BitArray`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))

- Added support for the `json` type, encoded as a Gleam `String` and decoded
as a Gleam `BitArray`.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))

## v1.2.0 - 2024-08-12

- Squirrel now supports the `SCRAM-SHA-256` authentication.
Expand Down
36 changes: 36 additions & 0 deletions birdie_snapshots/json_decoding.accepted
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
version: 1.1.8
title: json decoding
file: ./test/squirrel_test.gleam
test_name: json_decoding_test
---
import decode
import gleam/pgo

/// A row you get from running the `query` query
/// defined in `query.sql`.
///
/// > 🐿️ This type definition was generated automatically using v-test of the
/// > [squirrel package](https://github.com/giacomocavalieri/squirrel).
///
pub type QueryRow {
QueryRow(res: BitArray)
}

/// Runs the `query` query
/// defined in `query.sql`.
///
/// > 🐿️ This function was generated automatically using v-test of
/// > the [squirrel package](https://github.com/giacomocavalieri/squirrel).
///
pub fn query(db) {
let decoder =
decode.into({
use res <- decode.parameter
QueryRow(res: res)
})
|> decode.field(0, decode.bit_array)

"select '{\"a\": 1}'::json as res"
|> pgo.execute(db, [], decode.from(decoder, _))
}
21 changes: 21 additions & 0 deletions birdie_snapshots/json_encoding.accepted
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
version: 1.1.8
title: json encoding
file: ./test/squirrel_test.gleam
test_name: json_encoding_test
---
import decode
import gleam/pgo

/// Runs the `query` query
/// defined in `query.sql`.
///
/// > 🐿️ This function was generated automatically using v-test of
/// > the [squirrel package](https://github.com/giacomocavalieri/squirrel).
///
pub fn query(db, arg_1) {
let decoder = decode.map(decode.dynamic, fn(_) { Nil })

"insert into jsons(json) values ($1)"
|> pgo.execute(db, [pgo.text(arg_1)], decode.from(decoder, _))
}
36 changes: 36 additions & 0 deletions birdie_snapshots/jsonb_decoding.accepted
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
version: 1.1.8
title: jsonb decoding
file: ./test/squirrel_test.gleam
test_name: jsonb_decoding_test
---
import decode
import gleam/pgo

/// A row you get from running the `query` query
/// defined in `query.sql`.
///
/// > 🐿️ This type definition was generated automatically using v-test of the
/// > [squirrel package](https://github.com/giacomocavalieri/squirrel).
///
pub type QueryRow {
QueryRow(res: BitArray)
}

/// Runs the `query` query
/// defined in `query.sql`.
///
/// > 🐿️ This function was generated automatically using v-test of
/// > the [squirrel package](https://github.com/giacomocavalieri/squirrel).
///
pub fn query(db) {
let decoder =
decode.into({
use res <- decode.parameter
QueryRow(res: res)
})
|> decode.field(0, decode.bit_array)

"select '{\"a\": 1}'::jsonb as res"
|> pgo.execute(db, [], decode.from(decoder, _))
}
21 changes: 21 additions & 0 deletions birdie_snapshots/jsonb_encoding.accepted
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
version: 1.1.8
title: jsonb encoding
file: ./test/squirrel_test.gleam
test_name: jsonb_encoding_test
---
import decode
import gleam/pgo

/// Runs the `query` query
/// defined in `query.sql`.
///
/// > 🐿️ This function was generated automatically using v-test of
/// > the [squirrel package](https://github.com/giacomocavalieri/squirrel).
///
pub fn query(db, arg_1) {
let decoder = decode.map(decode.dynamic, fn(_) { Nil })

"insert into jsons(jsonb) values($1)"
|> pgo.execute(db, [pgo.text(arg_1)], decode.from(decoder, _))
}
1 change: 1 addition & 0 deletions src/squirrel/internal/database/postgres.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ fn pg_to_gleam_type(type_: PgType) -> Result(gleam.Type, String) {
"text" | "char" | "bpchar" | "varchar" -> Ok(gleam.String)
"float4" | "float8" | "numeric" -> Ok(gleam.Float)
"int2" | "int4" | "int8" -> Ok(gleam.Int)
"json" | "jsonb" -> Ok(gleam.Json)
_ -> Error(name)
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/squirrel/internal/gleam.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub type Type {
Float
Bool
String
Json
}

/// The labelled field of a Gleam record.
Expand Down Expand Up @@ -82,7 +83,7 @@ pub fn identifier(
///
pub fn contains_option(type_: Type) -> Bool {
case type_ {
Int | Float | Bool | String -> False
Int | Float | Bool | String | Json -> False
Option(_) -> True
List(type_) -> contains_option(type_)
}
Expand Down
27 changes: 15 additions & 12 deletions src/squirrel/internal/query.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ fn gleam_type_to_decoder(type_: gleam.Type) -> String {
gleam.String -> "decode.string"
gleam.Option(type_) ->
"decode.optional(" <> gleam_type_to_decoder(type_) <> ")"
gleam.Json -> "decode.bit_array"
}
}

Expand All @@ -263,6 +264,19 @@ fn gleam_type_to_encoder(type_: gleam.Type, name: String) {
gleam.Float -> "pgo.float(" <> name <> ")"
gleam.Bool -> "pgo.bool(" <> name <> ")"
gleam.String -> "pgo.text(" <> name <> ")"
gleam.Json -> "pgo.text(" <> name <> ")"
}
}

fn gleam_type_to_field_type(type_: gleam.Type) -> Document {
case type_ {
gleam.List(type_) -> call("List", [gleam_type_to_field_type(type_)])
gleam.Option(type_) -> call("Option", [gleam_type_to_field_type(type_)])
gleam.Int -> doc.from_string("Int")
gleam.Float -> doc.from_string("Float")
gleam.Bool -> doc.from_string("Bool")
gleam.String -> doc.from_string("String")
gleam.Json -> doc.from_string("BitArray")
}
}

Expand All @@ -282,7 +296,7 @@ pub fn record(name: String, fields: List(gleam.Field)) -> Document {
list.map(fields, fn(field) {
let label = gleam.identifier_to_string(field.label)

[doc.from_string(label <> ": "), pretty_gleam_type(field.type_)]
[doc.from_string(label <> ": "), gleam_type_to_field_type(field.type_)]
|> doc.concat
|> doc.group
})
Expand All @@ -299,17 +313,6 @@ pub fn record(name: String, fields: List(gleam.Field)) -> Document {
|> doc.group
}

fn pretty_gleam_type(type_: gleam.Type) -> Document {
case type_ {
gleam.List(type_) -> call("List", [pretty_gleam_type(type_)])
gleam.Option(type_) -> call("Option", [pretty_gleam_type(type_)])
gleam.Int -> doc.from_string("Int")
gleam.Float -> doc.from_string("Float")
gleam.Bool -> doc.from_string("Bool")
gleam.String -> doc.from_string("String")
}
}

/// A pretty printed public function definition.
///
pub fn fun(name: String, args: List(String), body: List(Document)) -> Document {
Expand Down
34 changes: 34 additions & 0 deletions test/squirrel_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ fn setup_database() {
create table if not exists squirrel(
name text primary key,
acorns int
);
"
|> pgo.execute(db, [], dynamic.dynamic)

let assert Ok(_) =
"
create table if not exists jsons(
id bigserial primary key,
json json,
jsonb jsonb
)
"
|> pgo.execute(db, [], dynamic.dynamic)
Expand Down Expand Up @@ -151,6 +161,30 @@ pub fn string_encoding_test() {
|> birdie.snap(title: "string encoding")
}

pub fn json_decoding_test() {
"select '{\"a\": 1}'::json as res"
|> should_codegen
|> birdie.snap(title: "json decoding")
}

pub fn json_encoding_test() {
"insert into jsons(json) values ($1)"
|> should_codegen
|> birdie.snap(title: "json encoding")
}

pub fn jsonb_decoding_test() {
"select '{\"a\": 1}'::jsonb as res"
|> should_codegen
|> birdie.snap(title: "jsonb decoding")
}

pub fn jsonb_encoding_test() {
"insert into jsons(jsonb) values($1)"
|> should_codegen
|> birdie.snap(title: "jsonb encoding")
}

pub fn char_decoding_test() {
"select 'a'::char as res"
|> should_codegen
Expand Down

0 comments on commit a35968c

Please sign in to comment.