#🐿 squirrel - type safe SQL in Gleam
1 messages · Page 2 of 1
I think you're the only person who that is true for 😛
couldn't you also snapshot test against stuff after it's formatted?
tbf hayleigh this is also partially on you for omitting the --stdin line in your screenshot before /s
😂
Yeah I was going to say it's Hayleigh's fault here
Well if more formatting bugs start appearing in the future at least I know an easy way to fix them
But I'm ready to bet it won't be a problem for a long time
I find it a wee bit crazy that gleam format has like 3 options you can run it with and 2 of them would have simplified jaks life if he knew beforehand
To be 100% honest even if I knew I think I wouldn't have used it anyway 💀
we're all guilty of 'why use solution when engineer own solution do trick?'
What can I say, I love writing formatters
but aren't you... writing a formatter you already wrote once? 😂
ah but in a new language this time ig
It's more fun!!
think about how much the humble wheel could be improved, if only someone would reinvent it
this way is so much better, you can do all of this AND then compare the output vs your own formatting function to make sure they match
Will basic scaffolding of auth/ crud ever be part of the squirrel scope? If not, it might be a chance for some application (CLI) to take up this space, handling codegen for migrations and basic crud and applying migrations, specifically for squirrel
Nope nothing squirrel would ever do
@shadow shale you've got some explaining to do 
danielle@minnie ~/D/chat (main)> psql -h 192.168.64.14 -p 5432 -U chat-gen
psql (18beta1 (Postgres.app), server 17.5 (Debian 17.5-1.pgdg120+1))
Type "help" for help.
~# ^D\q
danielle@minnie ~/D/chat (main)> DATABASE_URL="postgres://chat-gen:[email protected]:5432/chat-gen?sslmode=disable" gleam run -m squirrel
Compiled in 0.03s
Running squirrel.main
Error: Cannot establish TCP connection
I couldn't connect to `192.168.64.14` at port 5432 because the connection timed
out.
I should probably give some context here: 192.168.64.14 is the IP address of a container
Spawned with Apple's new container CLI https://github.com/apple/container
Which uses Apple's own containerization library https://github.com/apple/containerization
figured it out
It takes too long to connect
and squirrel doesn't have a configurable timeout 
I have absolutely no idea why it takes so long to be honest
My assumption would be something in the networking stack that Apples new containerisation framework uses is slow
But upping the timeout from 1s to 5s works for me
Sooo what should I do?
app/toml configuration with default timeout?
while you're looking into this Jak, reckon you can bump to Mug 3?
Oh yeah! I’ll work on that
<3 love you
PostgreSQL URLs can include a connect timeout as a parameter
I've published 3.1.0
- @jagged nova you can now use the
PGCONNECT_TIMEOUTenv variable or theconnect_timeoutquery parameter to change the timeout (and I've raised the default to 5 seconds) - @viscid dagger I've updated the mug dependency to use the latest major!
Released v4.0.0, Squirrel now uses pog@4, and timestamp, date and time are all decoded using the gleam_time types!
pog
pog indeed
❯ gleam run -m squirrel
Compiled in 0.09s
Running squirrel.main
src/squirrel.gleam:191
Error(Nil)
src/squirrel.gleam:193
Error(Nil)
Error: Cannot authenticate
Invalid password for user `postgres`.
Hint: You can change the default password used to authenticate by setting the
`PGPASSWORD` environment variable.
I have this issue I did not have on another project, but can't find the reason why it does not work in this brand new project.
the two Error(Nil) are an addition of echo to print the env variables in the source code of squirrel:
let user = envoy.get("PGUSER") |> echo |> result.unwrap(default_user)
let password =
envoy.get("PGPASSWORD") |> echo |> result.unwrap(default_password)
any idea how I can unblock myself? I have a .env with the variables set:
PGHOST="localhost"
PGPORT=5432
PGDATABASE="postgres"
PGUSER="postgres"
PGPASSWORD="postgres"
PGCONNECT_TIMEOUT=5
Squirrel won't read .env files, are you somehow setting the env variables?
hhh faiil! my bad, i forgot envoy do read the env var but not the .env itself 🫣
No worries!
Hi! This is a great library, saves me great amount of time working with DB.
However I'm having trouble with numeric type in Postgres. Squirrel generates it as Option(Float) which is correct. But I get UnexpectedResultType([DecodeError("Float", "Int", ["0"])]) error when running query. I think some values that do not have fractional parts are interpreted as integers and give error.
I made workaround like this select cast(coalesce(amount, 0) as float) as amount. But this should work without this hack right?
Ah, interesting! Yeah I think it should work without coalescing, could you open an issue please?
ok
I've released v4.1.0!
- Now squirrel will pick up sql files that are in
testanddevas well assrc - Now squirrel supports
.sqlfiles usingdo $$blocks - Fixed a bug that would result in the
squirrel checkcommand randomly failing
I've released v4.2.0!
- Now squirrel also adds a doc comment at the top of all generated files, telling you where the queries come from
- When generating code squirrel will refuse to override existing files it didn't generate
I've released v4.4.1 (don't ask what happened to 4.3.0 and 4.4.0):
- now Squirrel is adding type annotations to the code it generates
4.3.0, I forgot to share it
4.4.0 I forgot to annotate the return type and rushed for a fix
I'm a bit out of the loop but I have projects using squirrel where I do some post processing to find & replace things like pog.calendar_date(arg_3) with pog.nullable(pog.calendar_date, arg_3) so that a nullable value is handled properly. This works because the types are inferred correctly after the find & replace and I suspect having concrete types will disrupt this. Have their been developments on this? Again, super out of the loop. How should I be managing nullable values?
Can you show the query?
Thanks for asking. I'll come back to you with that. Hopefully this evening.
No, thank you!
Ok, I have:
INSERT INTO
actionable_items (
name,
project_id,
today,
properties,
created_at
)
VALUES
($1, $2, $3, $4, NOW())
RETURNING
id;
Where:
\d actionable_items;
Table "public.actionable_items"
Column | Type | Collation | Nullable | Default
----------------------------+--------------------------+-----------+----------+------------------------------------------------------
id | integer | | not null | nextval('actionable_items_id_seq'::regclass)
name | character varying | | not null |
project_id | integer | | not null | nextval('actionable_items_project_id_seq'::regclass)
created_at | timestamp with time zone | | not null |
completed_at | timestamp with time zone | | |
deleted_at | timestamp with time zone | | |
today | date | | |
actionable | boolean | | not null | false
properties | jsonb | | not null | '{}'::jsonb
task_integration_source_id | integer | | |
integration_task_id | text | | |
Indexes:
"actionable_items_pkey" PRIMARY KEY, btree (id)
"actionable_items_unique_integration_ids" UNIQUE CONSTRAINT, btree (task_integration_source_id, integration_task_id)
Foreign-key constraints:
"actionable_items_project_id_fkey" FOREIGN KEY (project_id) REFERENCES projects(id)
"actionable_items_task_integration_source_id_fkey" FOREIGN KEY (task_integration_source_id) REFERENCES task_integration_sources(id)
Referenced by:
TABLE "up_next" CONSTRAINT "up_next_actionable_item_id_fkey" FOREIGN KEY (actionable_item_id) REFERENCES actionable_items(id)
And the generated code has:
|> pog.query
|> pog.parameter(pog.text(arg_1))
|> pog.parameter(pog.int(arg_2))
|> pog.parameter(pog.calendar_date(arg_3))
|> pog.parameter(pog.text(json.to_string(arg_4)))
|> pog.returning(decoder)
|> pog.execute(db)
Which indicates that squirrel doesn't consider the third argument today which is nullable in the table to be nullable.
I'm likely making some mistake but as I said I'm a bit out of the loop and I currently have a python script that runs after squirrel code gen to add the nullable in
So that's a know limitation, because of how postgres words query parameters cannot be inferred as nullable. There's two ways you could go about this
You could have your query insert null if you pass in a special zero value
insert into actionable_items (name, project_id, today, properties, created_at)
values (
$1,
$2,
case $3
when '1900-01-01' then null
else $3
end,
$4,
now()
)
returning id;
Or, given this query is quite short and simple, duplicate it! Have one version that adds null and one that adds the value and pick one in Gleam based on your needs
insert into actionable_items (name, project_id, today, properties, created_at)
values ($1, $2, null, $3, now())
returning id;
-- and another one in a different file
insert into actionable_items (name, project_id, today, properties, created_at)
values ($1, $2, $3, $4, now())
returning id;
Neither is ideal but it's the best one can do without doing any find and replace, which I think would be more tedious
Thanks for explaining. I appreciate you taking the time. Good to understand the situation. I think I would benefit if the docs covered it in some way. Either in the types or the FAQ section but you have a better idea about how often it comes up.
I've released Squirrel 4.5.0:
- now when there's an error in any of the queries squirrel will not do any code generation, which would usually lead the codebase in a broken state.
- I've added a nice hint for when someone tries to use a
timestamptzinstead of atimestamp
oooh very nice
Thank you ! Squirrel is one my favorite library 💜
Thank you!!
Do you have any thoughts/plans regarding solving this issue?
I think it can be solved with schema introspection or maybe additional annotations?
Nothing new besides what I’ve shared 😔
Schema introspection is not doable unless you implement a sql query parser I’m afraid
No worries, I was just interested in your perspective 🤗 And yeah I think you are right about parser
What is your opinion on using C psql query parser like https://github.com/pganalyze/libpg_query?
I don’t think adding aC dependency to squirrel would be the way to move forward with this
The ideal solution would be adding to postgres the kind of features we need to enable better typing in squirrel
Also parrot already implements that approach
Hello squirrels ! Anyone knows if there's a way to create a custom type such as type UserID = String so I could type my column id text not null primary key as a UserID ? 🤔 I'm afraid that's not possible, and that's okay I'll add my own wrapper on top of it.
I don't know if other people would be interested in that, and I'm afraid this would make the sql be non standard then… :/
Hello there! Nope there's no way to
Also relevant to point out that a type alias does not give you type safety, so if you really want to keep user ids from other strings to not mix those up you'd have to use a custom type wrapper:
pub type UserId {
UserId(String)
}
Oh my, I completely though that was the case 🤦 Thanks for the correction !
Also, insert queries don't have named parameters ? I see the code is generated as arg_1, arg_2 etc…
Yeah it's easy to get that wrong, happy to help! One other small note is that when you have acronyms convention is to use PascalCase: Html and not HTML, Json and not JSON, Id and not ID, ...
Sorry, some Go habbit got the better of me 😅
That's an unfortunate limitation we inherit form postgres, It doesn't allow to have named query parameters sadly. There's an open discussion on how squirrel could support it but so far no design is really worth it
In an ideal world Postgres will add support for named arguments and we could use those
Well, we have Gleam, nice libraries and nicer people, we're not that far from ideal world 💜
We have it so good in Gleam land 🥹💜
I've released Squirrel 4.7.0, there's a couple of bug fixes for code that was not formatted properly and now squirrel can try and generate better argument names for queries. For example:
select squirrel.name
from squirrel
where squirrel_id = $1
The generated function is going to have the following definition:
pub fn select_squirrel(
db: pog.Connection,
squirrel_id: Int
) -> Result(pog.Returned(GetLoginLinkRow), pog.QueryError) {
// ... the generated code ...
}
jak i have a nit
Tell me
maybe this has already been proposed and shot down, but wouldn't it be nicer to generate lables for the arguments?
it was shot down
I don't want to commit to that yet, because then any change in how labels are picked would be a breaking change for squirrel users
not any change
I expected that, just to lazy to look it up 😅
i tried arguing for this kwando but jak rules with an iron fist
it is okay to be wrong 😅
Understandable, I just think it would be an ergonomic win once you get more than like 3 arguments. I've messed up the ordering several times, especially for queries like my_query(pog.Connection, String, String, String, String, String, String, String)
mhm mhm
It would be a big win but we don’t have a way to reliably set names yet
ATM it’s a best effort to pick something appropriate-ish, but APIs should be designed and shouldn’t change between versions
Gimme magic comments that let me name them (don’t do this for me, I don’t actually use squirrel so I shouldn’t influence the design)
Yeah Louis is right that’s why it’s not doing labels (and probably never will until Postgres supports names query parameters)
There could be another package that is like Squirrel but adds additional non-PostgreSQL native functionality