honeysql

Discussion of https://github.com/seancorfield/honeysql :slightly_smiling_face:
borkdude 2020-06-22T10:33:47.103400Z

how does one use honeysql-postgres's returning in combination with jdbc/execute! ? https://github.com/nilenso/honeysql-postgres

2020-06-22T10:39:37.105100Z

(jdbc/execute! conn (sql/format {:insert-into :my_table :values [{:a 1 :b 2}] :returning [:id :a :b]}) my-query-opts) should return a vector of maps with returned values.

borkdude 2020-06-22T10:40:07.105400Z

doesn't work for me. what's in my-query-opts?

nachos 2020-06-22T10:41:32.105900Z

{:return-keys true}

2020-06-22T10:42:04.106500Z

Stuff like :builder-fn would go in my-query-opts. (https://github.com/seancorfield/next-jdbc/blob/develop/doc/all-the-options.md#generating-rows-and-result-sets)

2020-06-22T10:42:18.107300Z

What error are you getting?

borkdude 2020-06-22T10:42:18.107400Z

so far I've used only hugsql, I'm trying to convert to honeysql for some of the queries

borkdude 2020-06-22T10:44:29.108100Z

:return-keys true does something, I'll try to see if I can get this to work with that option

2020-06-22T10:44:32.108300Z

Oops, forgot to throw conn in my above invocation

2020-06-22T10:45:12.108800Z

Have you required the honeysql format namespace?

2020-06-22T10:45:29.109200Z

I've used :returning consistently and never had a problem with it.

borkdude 2020-06-22T10:46:14.109500Z

I've required these:

[honeysql.core :as hsql]
   [honeysql.helpers :as h]
   [honeysql-postgres.format]
   [honeysql-postgres.helpers :as psqlh]
   

2020-06-22T10:46:39.109800Z

Okay, that looks good to me

borkdude 2020-06-22T10:47:07.110600Z

The query also looks good btw, but I don't get the keys from returning

nachos 2020-06-22T10:47:41.110900Z

@borkdude I think you need to require [honeysql-postgres.format :refer :all]

borkdude 2020-06-22T10:48:11.111500Z

If that's true, then honeysql-postgres made a design mistake πŸ˜‰

2020-06-22T10:48:35.111800Z

You don't need to do that

borkdude 2020-06-22T10:48:49.112300Z

anyway, this is at the end of my query RETURNING collection_uuid uri. Looks good

nachos 2020-06-22T10:49:00.112500Z

Oh sorry my bad. You don’t need to πŸ™‚

borkdude 2020-06-22T10:49:33.113400Z

but as the result with :return-keys true I only get a single map with {:uri ...} while I'm inserting multiple values...

2020-06-22T10:49:48.113700Z

Maybe something is off with your connection config?

borkdude 2020-06-22T10:50:22.114200Z

the records are inserted fine, it's just this returning stuff that doesn't work. I might return to hugsql because this is too time consuming

2020-06-22T10:52:09.114800Z

This is a working config if it helps: https://github.com/codonnell/mygiftlist-blog/blob/master/src/rocks/mygiftlist/db.clj

borkdude 2020-06-22T10:53:30.115400Z

I'm using the same connection that works with hugsql (has worked for years) so I don't think it's a config issue in the connection

borkdude 2020-06-22T10:55:59.115900Z

If :return-keys is provided, db-do-prepared-return-keys will be called
  instead of db-do-prepared, and the result will be a sequence of maps
  containing the generated keys.
then why on earth do I only get a single map with only half of the field in it...?

borkdude 2020-06-22T10:57:01.116100Z

(jdbc/execute! (svc/database) q {:return-keys true }) ;;=> {:uri #uuid "1b19090c-b475-11ea-9bb7-001b21bedfe8"}

borkdude 2020-06-22T10:57:15.116400Z

(the uri is also incorrect, the uri is not a uuid!)

2020-06-22T11:00:23.117700Z

That's really weird. hugsql isn't doing anything particularly magical: https://github.com/layerware/hugsql/blob/master/hugsql-adapter-next-jdbc/src/hugsql/adapter/next_jdbc.clj. (Just using :return-keys, it appears.)

kwrooijen 2020-06-22T11:01:46.118300Z

@borkdude Are you using next-jdbc?

borkdude 2020-06-22T11:01:50.118500Z

no

kwrooijen 2020-06-22T11:02:03.118900Z

Yeah, I had the same problem

kwrooijen 2020-06-22T11:02:10.119200Z

I fixed it by using next-jdbc

borkdude 2020-06-22T11:02:38.119500Z

do you think next-jdbc is backwards compatible?

borkdude 2020-06-22T11:02:52.119900Z

we have a pretty big surface area with previous-jdbc

kwrooijen 2020-06-22T11:03:04.120200Z

I honestly don't have enough experience with jdbc to answer that

borkdude 2020-06-22T11:06:16.121400Z

yeah, not ready to go into that rabbit hole right now

borkdude 2020-06-22T11:08:24.123200Z

instead of going through execute

borkdude 2020-06-22T11:08:33.123600Z

maybe for the same reason

2020-06-22T11:08:38.123700Z

Oh you're using clojure.java.jdbc! Have you tried using jdbc/query instead of jdbc/execute!? IIRC it always returns a sequence of maps, while jdbc/execute! has some magic behavior.

borkdude 2020-06-22T11:09:03.124100Z

@codonnell I'm inserting values. Should I be using jdbc/query in that case?

2020-06-22T11:10:20.124800Z

I guess up to you. I've never seen a ton of value in differentiating the calls; I'm going to look at the SQL to understand what's happening regardless.

borkdude 2020-06-22T11:11:47.126Z

@codonnell the SQL looks good. did you return multiple fields or just one? just one might work, but I'm returning multiple

2020-06-22T11:12:50.126700Z

It's been a little while since I've used clojure.java.jdbc; let me see if I can find an example from an older project. I'm pretty sure I've returned multiple values without issue.

borkdude 2020-06-22T11:14:46.127100Z

hmm, (jdbc/db-do-prepared-return-keys (svc/database) q) has the same issue: returns a single map with one key

borkdude 2020-06-22T11:17:59.127800Z

the documentation in https://github.com/nilenso/honeysql-postgres about returning isn't every explicit when it comes to returning multiple columns

2020-06-22T11:20:03.128500Z

If you look at the generated query, it should be clear whether the right SQL (with multiple columns) is generated or not.

borkdude 2020-06-22T11:21:57.129200Z

aha, it seems it's missing a comma. when I do (psqlh/returning (hsql/raw "collection_uuid, uri")) I get a map with all the fields. Still just one map though, not like 6 that I'm expecting

borkdude 2020-06-22T11:22:25.129600Z

the comma thing seems like a bug to me in honeysql-postgres

2020-06-22T11:23:52.130Z

Looks like it should be comma-separated looking at the code: https://github.com/nilenso/honeysql-postgres/blob/master/src/honeysql_postgres/format.cljc#L132

2020-06-22T11:24:34.130700Z

Maybe you're passing it a vector of columns instead of the columns as individual arguments?

2020-06-22T11:24:50.131100Z

I don't usually use the helpers, so not sure exactly how they're supposed to be invoked.

borkdude 2020-06-22T11:25:17.131400Z

I've tried all kinds of combinations.

2020-06-22T11:25:28.131800Z

Though that wouldn't fix the issue of only getting one return value back

borkdude 2020-06-22T11:25:37.132Z

With and without aliases. In a vector, as separate args, etc.

murtaza 2020-06-22T11:30:10.133700Z

@borkdude returning expects column names as parameters.

(jdbc/db-do-prepared-return-keys transaction (-> (insert-into table)
                                                 (values [record])
                                                 (psqlh/returning :field-a :field-b)
                                                 sql/format))

2020-06-22T11:32:33.135200Z

This mimimal repro works well for me: https://gist.github.com/codonnell/51d87e6a9f85607525142c21e8721e1a

borkdude 2020-06-22T11:32:34.135300Z

thanks, this now works! now the only remaining problem is that (jdbc/execute! ... {:return-keys true}) only returns one map, while I'm inserting 6 rows

borkdude 2020-06-22T11:36:14.137200Z

I'll try your repro, codonnell

murtaza 2020-06-22T11:42:03.138600Z

return-keys expects a collection of record fields you expect to be returned. Also, depending on your jdbc version, db-do-prepared-return-keys (which execute! ends up calling in the presence of return-keys) might be overwriting return-keys with true so it ends up returning a boolean based on the presence of a return value. https://github.com/clojure/java.jdbc/blob/37587dc32a29ea39ace9d944a250445619f528a3/src/main/clojure/clojure/java/jdbc.clj#L942

borkdude 2020-06-22T12:03:27.140200Z

@codonnell your repro works for me. I'm slightly confused why query is used to insert record, but if it works...

borkdude 2020-06-22T12:07:47.140600Z

and now also my own query works. thanks all, this was super confusing but now it works

borkdude 2020-06-22T12:08:43.141500Z

so: (jdbc/query conn q) (note: query, not execute!) with q having (psqlh/returning :u.collection_uuid :u.uri) in it and NOT using {:return-keys true}.

borkdude 2020-06-22T12:11:42.141700Z

❀️

2020-06-22T12:16:05.143700Z

:man-shrugging: I'm not familiar enough with jdbc to understand without spending a bunch of time reading clojure.java.jdbc source code. I just know query returns results effectively, so I have used it for queries that return results (even an insert with returning clause). FWIW the jdbc.next API is much simpler; there's just jdbc/execute! and jdbc/execute-one!. I like it much more.

borkdude 2020-06-22T12:17:43.144300Z

yeah, I look forward to using that in a next greenfield project, but I'm not ready to rewrite all our stuff using namespaced keywords etc

2020-06-22T12:18:43.145200Z

You can pass in options so a query returns unnamespaced keywords with jdbc.next

borkdude 2020-06-22T12:19:45.146100Z

that still requires me to pass in options everywhere πŸ™‚ maybe I can migrate query by query, assuming these libs can be used simultaneously

2020-06-22T12:22:58.147800Z

Can always write little wrapper functions like https://github.com/codonnell/mygiftlist-blog/blob/master/src/rocks/mygiftlist/db.clj#L52-L60. That said, I'm sure there's some corner cases that would need to be dealt with; I haven't upgraded an existing project from clojure.java.jdbc to jdbc.next personally.

2020-06-22T12:29:11.148600Z

Anyways, hope I'm not coming across as pushy. You should do whatever is best for your project. πŸ™‚

borkdude 2020-06-22T12:30:50.148900Z

not pushy, thanks for the help

1πŸ‘
Johannes F. Knauf 2020-06-22T12:57:13.151100Z

Hey #honeysql folks, I am looking for tooling for convenient, interactive development of SQL statements resp. honeysql query definitions. Currently I am using Oracle SQL Developer and manual translation to honeysql. It sucks. Some of the nice features I would like to have: β€’ preview with result paging β€’ auto-completion for table names, column names, ... β€’ separate sessions for multiple connections

borkdude 2020-06-22T12:59:21.151900Z

@johannes.f.knauf Probably a well supported Clojure editor + REPL comes closest to this

Johannes F. Knauf 2020-06-29T15:46:57.167Z

7 days ago I asked for alternatives to SQL Developer and consorts, which integrate better with my interactive development workflow. Not exactly what I was looking for, but still a step in the right direction: https://github.com/kostafey/ejc-sql First experiments look really promising.

borkdude 2020-06-29T15:50:15.167300Z

nice!

borkdude 2020-06-22T12:59:41.152200Z

Maybe in combination with REBL for paging

Johannes F. Knauf 2020-06-22T13:36:34.154900Z

How would I provide the auto-completion on DB-side data in that case?

borkdude 2020-06-22T14:10:51.155100Z

Well, if you use a keyword more than once your editor will likely autocomplete that, but I understand that's a much more basic feature than you would like. Maybe going the "generate a .sql file from Clojure using honeysql" route and then opening it in a capable SQL editor is better for your use case then?

Johannes F. Knauf 2020-06-22T14:53:28.155500Z

Hmm, I think there is some middle ground: Grabbing the schema definitions as a one-time script activity and using that for auto-completion -- similar to the good old CTAGS approach.

Johannes F. Knauf 2020-06-22T14:54:36.155700Z

I would rather like to get rid of the SQL-only editors, which do not allow me to do non-SQL data manipulation, visualisation, ....

borkdude 2020-06-22T14:56:14.155900Z

Yeah. You can find a project here which generates tags for Clojure vars: https://github.com/sogaiu/alc.index-defs Maybe it's helpful for getting started

borkdude 2020-06-22T14:57:35.156300Z

I can see myself writing a script with babashka and babashka-sql-pods for doing the same for Postgres. There is some work on an Oracle driver but I haven't heard from that person for a while

Johannes F. Knauf 2020-06-22T15:16:14.156500Z

Do you have some link to material to get me started? Sounds like a fun evening activity.

Johannes F. Knauf 2020-06-22T15:16:41.156700Z

Where would I have to integrate the oracle driver?

borkdude 2020-06-22T15:18:05.156900Z

@johannes.f.knauf To connect to a database from babashka, there is https://github.com/babashka/babashka-sql-pods But currently only Postgres and HSQLDB are supported. I have no Oracle database but I would welcome a PR to add Oracle

borkdude 2020-06-22T15:18:45.157300Z

I don't know if there are legal issues with distributing a binary that contains an Oracle driver, that would be good to check

Johannes F. Knauf 2020-06-22T15:22:26.157500Z

Yes, there are. Typically what you do is provide a sample snippet for users how to provide the ojdbc jar file e.g. from a local maven repo.

Johannes F. Knauf 2020-06-22T15:22:50.157700Z

Would that work? What dependency mechanism does babashka use? (Sorry if that is a stupid question. Never used it so far. Shame on me.)

borkdude 2020-06-22T15:23:48.157900Z

Yeah, I think people would just have to compile pod-babashka-oracle themselves.

borkdude 2020-06-22T15:24:09.158100Z

Babashka just uses Clojars/maven to gather dependencies and then creates a binary out of those.

borkdude 2020-06-22T15:24:20.158300Z

A pod is another standalone binary which can act as a library to babashka.

Johannes F. Knauf 2020-06-22T15:38:35.158500Z

I see. Sounds feasible.

borkdude 2020-06-22T15:39:16.158700Z

babashka itself also supports the honeysql library

seancorfield 2020-06-22T16:46:50.160200Z

@borkdude I wasn't awake for your big, long discussion but, yeah, you can absolutely use c.j.j and next.jdbc together -- we do at work as we're writing all new code with next.jdbc and still maintaining code based on c.j.j (we even use both in the same namespace in a few places).

seancorfield 2020-06-22T16:48:25.162100Z

The query vs execute! thing in c.j.j is to do with whether it expects to process a full result set or not -- and several of the functions in c.j.j call different functions in JDBC. That's all solved in next.jdbc which uses .execute() everywhere and will always return a full result set if JDBC makes one available (whereas c.j.j will ignore the result set in several situations).

borkdude 2020-06-22T17:36:29.162300Z

thanks