honeysql

Discussion of https://github.com/seancorfield/honeysql :slightly_smiling_face:
maxp 2020-10-02T05:24:31.121700Z

Now I pass jsonb data as

(->
        (h/insert-into :tbl)
        (h/values [{:json-field :?data}])
        (sql/format {:data {:a 1}}))
is there a way to do it in values? something like
(->
        (h/insert-into :tbl)
        (h/values [{:json-field #sql/??? {:a 1}}])
        (sql/format)

seancorfield 2020-10-02T05:28:09.122400Z

I don't believe there's any way to do that in 1.0. It's something I plan to address in 2.0.

seancorfield 2020-10-02T05:30:07.124100Z

I'm just in the process of updating next.jdbc so the next.jdbc.types wrappers will pass through HoneySQL 1.0 untouched (they have been vectors with metadata, which HoneySQL tried to unwrap as values -- they're changing to functions which I've tested that HoneySQL will pass through as-is).

seancorfield 2020-10-02T05:31:01.125Z

Once that new version of next.jdbc is out, you could probably use one of the as-* wrappers inline to achieve this @maxp

dominicm 2020-10-02T11:41:49.126400Z

@seancorfield could you do something like data_readers for the new honey sql? Where they all get slurped up to find the namespaces that need loading for extensions?

dominicm 2020-10-03T20:49:02.145800Z

Hmm, total hack: use data_readers.cljc and have the same namespace call register!

🤦 1
p-himik 2020-10-02T13:28:27.128700Z

Just encountered a problem with JOIN ordering:

(sql/format (-> (hh/select :a)
                (hh/from [:table1 :t1])
                (hh/left-join [:table2 :t2] [:= :t1.id :t2.id])
                (hh/join [:table3 :t3] [:= :t2.x :t3.x])))
=> ["SELECT a FROM table1 t1 INNER JOIN table3 t3 ON t2.x = t3.x LEFT JOIN table2 t2 ON t1.id = t2.id"
Notice how the formatted SQL string mentions t2 before it's defined because the LEFT JOIN clause ended up after the INNER JOIN clause.

p-himik 2020-10-02T13:29:17.129Z

Since different joins can depend on each other, seems like there should be a single :join array or maps where each map has a key like :join-kind. This way, it would be possible to preserve ordering.

kwrooijen 2020-10-02T13:29:31.129300Z

Hey, I'm using the honeysql.types/array function to create an array in Postgres. However when the array is empty I get a type cast error. I need to specifiy the type as follows:

array[]::text[]
I'm not sure how to add the type casting though?

p-himik 2020-10-02T13:30:48.129400Z

(sql/format (sql/call :cast :x (sql/raw "int[]")))
=> ["CAST(x AS int[])"]

p-himik 2020-10-02T13:30:57.129600Z

Alternatively, just use sql/raw to use the :: syntax.

kwrooijen 2020-10-02T13:31:21.129800Z

Thanks, I'll try this out!

p-himik 2020-10-02T13:35:25.130Z

I just glanced at the v2 branch, and seems like it carries the join ordering logic over, so I'd guess the issue is still there.

kwrooijen 2020-10-02T13:43:29.130200Z

Works like a charm, thanks

👍 1
p-himik 2020-10-02T14:13:47.130500Z

Worked around it with this for now:

(fmt/register-clause! :ordered-join (dec (:join @fmt/clause-store)))

(defhelper ordered-join [m clauses]
  (assoc m :ordered-join clauses))

(defmethod fmt/format-clause :ordered-join [[_ join-groups] _]
  (fmt/space-join (map #(apply fmt/format-join %)
                       (partition 3 join-groups))))

p-himik 2020-10-02T14:24:11.130700Z

Created https://github.com/seancorfield/honeysql/issues/277 to keep track of it.

seancorfield 2020-10-02T15:28:58.131Z

My plan for V2 was to very specifically get rid of the whole data_readers stuff that is part of V1. Can you elaborate on what you're suggesting here @dominicm?

seancorfield 2020-10-02T15:30:15.131200Z

@p-himik I thought most databases allow the joins/aliases in any order?

p-himik 2020-10-02T15:30:57.131400Z

Apparently not - PostgreSQL didn't allow me to refer to the t2 alias before it was declared.

seancorfield 2020-10-02T15:32:12.131600Z

Looks like MySQL barfs on it too... OK, I'll have a think about that...

seancorfield 2020-10-02T15:34:51.131800Z

In V2 that becomes [:cast :x [:raw "int[]"]]

🙌 1
dominicm 2020-10-02T16:24:30.132100Z

@seancorfield A while ago I made a comment about the problem with multi-methods is that they have to be loaded. In some contexts, there's no natural/incidental name to require and use which would then create the right match on the multi-method. honeysql-postgres is an example of this, it doesn't do anything except trigger side-effects, and it's really easy to forget to load the ns from every place you use it. Alex at the time suggested that libraries should provide a "loading mechanism" for multi-methods when that was the case, e.g. how data_readers.cljc is implemented. Search the classpath for honeysql/extensions.edn and load the namespaces listed within.

seancorfield 2020-10-02T17:03:27.132300Z

Well, HoneySQL V2 has register-*! functions that you call at app startup to add clauses, operators, and other syntax, so any extension library would need to call those and expose it's own initialization function that you'd need to call. I'll create a ticket for auto-initialization via an EDN file so that can be automated. Interesting idea.

seancorfield 2020-10-02T17:07:53.132500Z

https://github.com/seancorfield/honeysql/issues/278

seancorfield 2020-10-02T19:01:46.134200Z

FYI, the v2 branch of honeysql now has :?foo support and [:param :foo] (as the new version of #sql/param :foo or (sql/param :foo)) and there's a first cut of [:raw "SQL"] taking just a string -- still deciding what exactly to do about that.

seancorfield 2020-10-02T19:06:48.137900Z

Going back to @maxp’s question earlier, something that would make that sort of thing easier -- and I'm somewhat surprised v1 doesn't already have this -- is a way to identify a structured value that should be treated as a parameter (i.e., a way to prevent interpretation of [..`]` or {..`}` etc). My initial thought is [:lift <some-expr>] since it would "lift" the expression out as a parameter but I don't know how that reads for other folks? (well, my initial thought was really to use [:param <some-expr>] for it but given v1's use of sql/param for named parameters would make things very confusing!).

seancorfield 2020-10-02T20:38:28.141400Z

An issue raised by @camsaul on GitHub is worth discussing here: there are situations where some DBs need an extra (..`)` around either part of a statement or part of an expression, to disambiguate how clauses should bind (in SQL, not in HoneySQL, so it isn't purely a clause priority issue). My solution to this is to add [:nest ...] for expressions and {:nest ...} for statements (as a very low priority (fake) "clause"). How do folks feel about that? It should rarely be necessary but should allow you to take direct control when needed, without complicating things. Open to suggestions on the name and/or alternate approaches.