honeysql

Discussion of https://github.com/seancorfield/honeysql :slightly_smiling_face:
2020-12-17T00:43:41.240900Z

Hi! what’s the correct way to tell insert-into to treat a column from the map a as a vector? (i am trying to save it as a jsonb.) ex:

{:insert-into :cal_holiday_calendar,
 :values
 [{:name "tuesdays",
   :code "_tuesdays",
   :status {:v :active},
   :weekend {:v :sat-sun},
   :workdays [2 3 4]}]}
i want workdays to be stored as json in my db (postgres). (Raw sql with next.jdbc works just fine). honeysql throws
java.lang.AssertionError: Assert failed: Alias should have two parts[2 3 4]
(= 2 (count x)) 

seancorfield 2020-12-17T01:01:06.241500Z

@jmayaalv What JDBC library are you using with HoneySQL?

2020-12-17T01:01:15.241800Z

next.jdbc

seancorfield 2020-12-17T01:01:51.242500Z

next.jdbc has a bunch of functions in next.jdbc.types for wrapping values to "hint" how they should be stored into the JDBC parameters.

seancorfield 2020-12-17T01:02:04.242800Z

(make sure you're using a recent version of next.jdbc!)

seancorfield 2020-12-17T01:03:55.244700Z

So :workdays (as-array [2 3 4]) might work. HoneySQL should treat that as an opaque value and next.jdbc should use the type hint to set the parameter.

2020-12-17T01:05:01.245300Z

perfect! thank you!

seancorfield 2020-12-17T01:07:14.245600Z

LMK if it works.

2020-12-17T01:12:37.246100Z

tested with as-array and got

Cannot cast an instance of clojure.lang.PersistentVector to type
  Types.ARRAY

seancorfield 2020-12-17T01:49:34.246700Z

Hmm, OK. So have a read of the next.jdbc docs that talk about using arrays in PostgreSQL (Tips & Tricks).

seancorfield 2020-12-17T01:49:40.247Z

I assume you're using PG?

2020-12-17T08:11:08.249600Z

yes, i am using Postgres. If understand correctly this would save the object as Postgres’ Array but i can’t really change the type of the column right now need to store it as json (jsonb).

2020-12-17T08:14:12.250300Z

and can’t extend SettableParameter as java arrays don’t have a type 😕

valerauko 2020-12-17T15:02:09.250500Z

I'd expect this to work

(extend-protocol SettableParameter
  (Class/forName "[Lfoo.bar.MyClass;")
  (set-parameter [a b c] ,,,))

2020-12-17T15:24:13.250700Z

scary but worth trying 🙂 thanks. I’ll confirm if it works in a while 🙂

Yehonathan Sharvit 2020-12-17T17:13:17.252200Z

@seancorfield will honeysql be safe against sql injection when we call sql/format with {:inline true} ?

seancorfield 2020-12-17T17:15:51.252300Z

The array is a Clojure vector, not a Java array.

seancorfield 2020-12-17T17:18:29.254600Z

@viebel In general that's never going to be safe. When I work on the documentation, I'll make that clear. The code can be updated to escape ' so that a string of "it's" will correctly convert to a legal SQL string (I don't think I've gotten that part done yet), but it can only do so much escaping. You should only use :inline when you have a very specific need to do that and you can trust the incoming data.

Yehonathan Sharvit 2020-12-17T18:36:21.255100Z

Could you give me an example of a malicious honeysql map ?

seancorfield 2020-12-17T18:43:21.255600Z

I thought this might be (in HoneySQL v2) but it seems that I do escape ' already:

user=> (-> (select :*) (from :table) (where [:= :id "foo'; drop table user"]) (h/format {:inline true}))
["SELECT * FROM table WHERE id = 'foo''; drop table user'"]

seancorfield 2020-12-17T18:44:59.256200Z

With HoneySQL v1, that is dangerous:

user=> (-> (select :*) (from :table) (where [:= :id "'foo'; drop table user"]) (h/format :parameterizer :none))
["SELECT * FROM table WHERE id = 'foo'; drop table user"]
because no escaping is done.

seancorfield 2020-12-17T18:46:19.257Z

Note: the string is slightly different in those two examples. Here's v2 presented with that string:

user=> (-> (select :*) (from :table) (where [:= :id "'foo'; drop table user"]) (h/format {:inline true}))
["SELECT * FROM table WHERE id = '''foo''; drop table user'"]

seancorfield 2020-12-17T18:48:05.258Z

(not valid SQL -- I'm not sure if someone could construct a string parameter that would produce valid SQL and also be an injection attack but I wouldn't bet on it either way)

Yehonathan Sharvit 2020-12-17T19:01:57.258600Z

Yeah. I tried the same. But in V2 I was not able to unescape the string quotes.

Yehonathan Sharvit 2020-12-17T19:02:06.258900Z

For the sake of curiosity: Could it be proved that V2 is safe?

seancorfield 2020-12-17T19:12:42.259400Z

I'll leave that as an "exercise for the reader" 🙂

Yehonathan Sharvit 2020-12-18T11:13:59.267600Z

Yeah, But it’s smart string concatenation that quotes the variables depending on the type

seancorfield 2020-12-17T19:15:14.260500Z

(I would be extremely wary of using :inline true except in very restrictive situations -- I mean, why would you want to inline the parameters in the first place?)

Jerome Gallinari 2020-12-17T23:08:31.261200Z

Hi, I need to call a Postgres stored procedure that expects a text[] parameter, but can't get it to work. Maybe you can enlighten me? If I try the following, here's what I get:

(def param-names ["HOST_MNO"])
  (def session-auth-id "1234")
  (def offer-id 88060)
  (def addon-name "MASTER_MNO")
  (println (-> (honeysql.helpers/select :*)
               (honeysql.helpers/from (honeysql.core/call :meta_db.get_offer_param_value_list :?session-auth-id :?offer-id :?addon-name :?param-name-array))
               (honeysql.core/format :params {:session-auth-id session-auth-id :offer-id offer-id :addon-name addon-name :param-name-array (honeysql.types/array param-names)})))

["SELECT * FROM meta_db.get_offer_param_value_list(?, ?, ?, ?)" "1234" 88060 "MASTER_MNO" #sql/array (["HOST_MNO"])]
nil
Any chance I could get something like:
["SELECT * FROM meta_db.get_offer_param_value_list(?, ?, ?, ARRAY[?])" "1234" 88060 "MASTER_MNO" "HOST_MNO"]
? Thanks.

Jerome Gallinari 2020-12-18T09:17:25.267200Z

Finally gave up on using a bindable param for the array.

user> (println (-> (honeysql.helpers/select :*)
               (honeysql.helpers/from (honeysql.core/call :meta_db.get_offer_param_value_list :?session-auth-id :?offer-id :?addon-name (honeysql.types/array param-names)))
               (honeysql.core/format :params {:session-auth-id session-auth-id :offer-id offer-id :addon-name addon-name})))
["SELECT * FROM meta_db.get_offer_param_value_list(?, ?, ?, ARRAY[?]") "1234" 88060 "MASTER_MNO" "HOST_MNO"]