honeysql

reefersleep 2025-02-25T13:17:41.920569Z

I experienced something odd today where I have a postgresql table with a column of type smallint[] (an array of smallints). When I try to insert into it with honeysql, it works when I cast a list of ints to :_int4, but not :_smallint, :_int or :_integer - postgres doesn't know (!) those types.

reefersleep 2025-02-26T09:06:02.395969Z

Hm, the text on avoiding using e.g. _int4 in queries in DDL is a little light on the warning, and a little vague on exact directions, from what I read here: https://www.postgresql.org/docs/current/sql-createtype.html "It may be advisable to avoid using type and table names that begin with underscore. While the server will change generated array type names to avoid collisions with user-given names, there is still risk of confusion, particularly with old client software that may assume that type names beginning with underscores always represent arrays." When? Where? What if I align my usage of the names with "old client software" i.e. only use underscored names for internally generated array types? But all in all, I think I get the picture that scalar[] is just clearer/less ambiguous overall.

p-himik 2025-02-26T09:30:25.997249Z

Yes, just not using _ and not even thinking in that direction is the best way forward. :)

🍻 1
p-himik 2025-02-25T13:20:12.656389Z

Why the underscore?

reefersleep 2025-02-25T13:20:27.910129Z

idk, I'm a passer-by in this code

reefersleep 2025-02-25T13:20:32.841319Z

Maybe it's something we've set up

reefersleep 2025-02-25T13:20:48.562929Z

It seems to signify "array of". We also have examples of :_varchar

p-himik 2025-02-25T13:21:05.429919Z

But that's an internal detail of PostgreSQL, AFAIK. Don't use it, use proper array types instead.

reefersleep 2025-02-25T13:21:30.402099Z

Yeah I don't understand why we use it, tbh.

reefersleep 2025-02-25T13:21:49.873069Z

I never saw it in documentation or stackoverflow/similar sites

reefersleep 2025-02-25T13:21:58.588149Z

only smallint[]

p-himik 2025-02-25T13:22:24.541529Z

It is documented. But it's still not something a regular user should use. From the docs: > Writing code that depends on this convention is therefore deprecated.

reefersleep 2025-02-25T13:22:39.666759Z

Thanks 🙂

reefersleep 2025-02-25T13:22:58.780579Z

Who needs ChatGPT when there's @p-himik 😄

😄 1
reefersleep 2025-02-25T13:24:24.444459Z

ahh... I guess because you can't do keywords with [] in them?

reefersleep 2025-02-25T13:24:45.331129Z

Maybe someone saw that as a limitation and thought :_varchar will be fine

p-himik 2025-02-25T13:25:14.942149Z

What exactly is the query that uses _?

p-himik 2025-02-25T13:25:52.959719Z

HoneySQL has some ways to create [], but maybe somehow they aren't suitable for your query.

reefersleep 2025-02-25T13:30:07.312319Z

The end query is something like

["insert into x (my_col) values (CAST(? AS _INT4))"
 "{1 2 3 4 5 6}"]

reefersleep 2025-02-25T13:30:34.635769Z

I can't use "smallint[]" directly, that makes honey confused

reefersleep 2025-02-25T13:31:31.067049Z

... but I can call (keyword "smallint[]")

reefersleep 2025-02-25T13:31:38.509009Z

let's see if honey likes that

reefersleep 2025-02-25T13:35:32.137339Z

Honey seems to. It becomes

["insert into x (my_col) values (CAST(? AS SMALLINT[]))"
 "{1 2 3 4 5 6}"]

reefersleep 2025-02-25T13:35:40.693959Z

but the query hasn't returned yet 😄

p-himik 2025-02-25T13:35:46.510879Z

Is the "{1 2 3 4 5 6}" value only available as a string? Is it not available as a proper seq of numbers?

reefersleep 2025-02-25T13:36:15.933199Z

yeah it is

reefersleep 2025-02-25T13:36:35.667219Z

before creating the query

reefersleep 2025-02-25T13:38:30.449729Z

we have some code like [:cast (str "{" (str/join "," my-seq) "}") :_int4]

reefersleep 2025-02-25T13:39:14.945089Z

(That creates honey EDN syntax, before formatting to a prepared statement)

p-himik 2025-02-25T13:39:51.063989Z

Wait, so you do have my-seq. Why not use it directly instead of creating a string?

p-himik 2025-02-25T13:40:15.277229Z

(honey.sql/format {:insert-into [[:x :my-col]]
                   :values      [[[:array [1 2 3 4 5 6] :integer]]]})
=> ["INSERT INTO X(my_col) VALUES (ARRAY[?, ?, ?, ?, ?, ?]::INTEGER[])" 1 2 3 4 5 6]

reefersleep 2025-02-25T13:40:43.858539Z

I'll give it a go

p-himik 2025-02-25T13:41:48.881549Z

If you want to avoid multiple parameters:

(honey.sql/format {:insert-into [[:x :my-col]]
                   :values      [[(into-array Integer/TYPE [1 2 3 4 5 6])]]})
=> ["INSERT INTO X(my_col) VALUES (?)" #object["[I" 0x14b9a1b2 "[I@14b9a1b2"]]

p-himik 2025-02-25T13:43:02.109939Z

The :integer in the next to last query is needed to make sure you don't have just ARRAY[] when the seq is empty. For empty arrays, PostgreSQL must know the type of the array itself as it cannot be deduced from the values inside the array.

reefersleep 2025-02-25T13:46:09.356569Z

ok, my colleague returned and she had the idea to use :_int2 which seems to match smallint in the documentation, and that works. But I still want to try your thing

p-himik 2025-02-25T13:47:08.222489Z

Yeah, I would definitely recommend to avoid :_int2 and other such things when there are alternatives. Especially when you have to manually construct a string only for PostgreSQL to destructure it later. What's even the point of using _ if there are all around better alternatives?

reefersleep 2025-02-25T13:54:33.589999Z

It works great with [[:array '(1 2 3 4) :integer] [:array '("hey" "my" "friend") :varchar]]! I'm a happy camper 😄

p-himik 2025-02-25T13:55:09.454789Z

Also can use [...] instead of '(...).

🙌 1
p-himik 2025-02-25T13:55:55.915849Z

I myself still prefer into-array, especially when there are many, many values. It's not fun to see a thousand ? in the logs where just one would do.

p-himik 2025-02-25T13:56:17.775919Z

Oh, and it's amenable to caching, unlike plain seqs.

reefersleep 2025-02-25T13:58:27.208779Z

Ah, good point. I'll do some refactoring

reefersleep 2025-02-25T14:01:31.469449Z

there's the same amount of question marks

reefersleep 2025-02-25T14:02:17.486509Z

just (?) instead of (ARRAY[?]::VARCHAR[]) (so shorter, yes, but still 1-1 question marks)

reefersleep 2025-02-25T14:03:26.924169Z

how do you do (into-array STRING/TYPE ...?

reefersleep 2025-02-25T14:04:06.074089Z

ah, just "String"

p-himik 2025-02-25T14:04:08.312099Z

No, there's not 1-1 question marks, there's 1-n question marks. I sent you two queries - one with six ?, the other with just one.

p-himik 2025-02-25T14:05:11.466949Z

Integer is for boxed ints, Integer/TYPE is for primitive ints. Strings don't have that distinction.

reefersleep 2025-02-25T14:11:22.314039Z

Ah, but my attempt to do your code that produced a lot of ?'s didn't on my machine.

p-himik 2025-02-25T14:27:18.205049Z

Can you send the exact code and the result?

reefersleep 2025-02-25T15:37:55.329659Z

lemme try

reefersleep 2025-02-25T15:43:15.361079Z

I stand corrected 🙂

reefersleep 2025-02-25T15:43:57.424589Z

I was confused. I had only one item in my seq, so of course there'd be only one ?.

reefersleep 2025-02-25T15:50:56.036909Z

Hm, but regarding using into-array as a means to have prettier logs, when I just print the formatted hsql, the numbers are unrecognizable. I mean, it probably looks fine in the db logs, but certainly not the prepared statement.

reefersleep 2025-02-25T15:58:06.184809Z

(-> (hsql-hlp/insert-into :x)
      (hsql-hlp/values [{:my-col [(into-array Integer/TYPE [1 2 3 4 5 6])]}])
      (hsql/format))
=> ["INSERT INTO x (my_col) VALUES ((?))" #object["[I" 0x1efeb66e "[I@1efeb66e"]]

reefersleep 2025-02-25T15:58:49.918509Z

In any case, thank you very much for the help, this was a day of Postgresql enlightenments for me 😄

👍 1
p-himik 2025-02-25T16:32:13.204309Z

Well, plain printing should probably not be used for logging. ;) I have a relatively complex query logging function that excises sensitive data, truncates arguments to avoid tens of thousands of items in logs, and expands arrays as well.

👍 1
reefersleep 2025-02-25T13:18:13.648849Z

And I can't figure out if it's an issue with honey or with our specific postgres setup.

reefersleep 2025-02-25T13:18:53.882299Z

Thought someone here might know something about it, either way 🙂