sql

All things SQL and JDBC...
Nazral 2021-07-04T15:12:52.329Z

Hi, is there a way to parametrize a request with keywords rather than ? ? Because right now I am building a rather complicated request, and it would be much simpler if I could do WHERE foo = :foo + {:foo "bar"} as parameter

seancorfield 2021-07-04T15:34:58.329500Z

Use HoneySQL to build the query. It supports named parameters.

Nazral 2021-07-04T15:44:06.329700Z

ok thank you!

emccue 2021-07-04T15:44:55.330Z

@archibald.pontier_clo I have a helper fn for that if you want it

emccue 2021-07-04T15:45:09.330300Z

(honeysql is great tho)

emccue 2021-07-04T15:45:51.330700Z

;; ------------------------------------------------------------------------
(defn expand-named-parameters
  "Given a sql query like \"SELECT name FROM TABLE WHERE id = :id\"
  and a map of the form {:id ..., :other ...}, this will return
  a sqlvec representation of that query with the named parameters in
  the correct position"
  [sql-str params]
  (let [^Pattern replace-pattern  #":([a-zA-Z]|\-|[0-9])+"
        params-in-order (map first (re-seq replace-pattern sql-str))
        with-placeholders (string/replace sql-str replace-pattern "?")
        extract-param-value (fn [param]
                              (->> (keyword (.substring param 1 (count param)))
                                   (get params)))]
    (vec (cons with-placeholders
               (map extract-param-value params-in-order)))))

;; ------------------------------------------------------------------------
(defmacro expand-named-parameters-compile-time
  "Does the same work as [[expand-named-parameters]], but requires a
  string literal and a map literal in exchange for doing the parsing
  work at compile time."
  [sql-str params]
  (let [param-syms (map (fn [[name value]]
                          {:name name
                           :value value
                           :symbol (gensym)})
                        (seq params))
        param-map (into {} (map (juxt :name :symbol) param-syms))
        let-clause `(let ~(vec (mapcat (juxt :symbol :value) param-syms))
                      ~(expand-named-parameters sql-str param-map))]
    let-clause))

emccue 2021-07-04T15:46:47.331300Z

here thee go

emccue 2021-07-04T15:50:33.331900Z

If i were to package that into a library I would probably make the macro version be the less verbose one

emccue 2021-07-04T15:50:52.332400Z

and do some assertions to make sure you aren't mixing named and unnamed params

emccue 2021-07-04T15:51:15.333100Z

and give some better errors on bad input

Nazral 2021-07-04T16:00:44.334100Z

@emccue thank you, I'll check that out after I fix the massive security issue in my sql requests 😅

emccue 2021-07-04T16:02:47.334700Z

@archibald.pontier_clo lmk if you would want it as a library - I could stand to learn how to do that finally

Nazral 2021-07-04T16:29:23.334900Z

You should do it regardless then! But I like the function, I think it could be useful yes

curtis.summers 2021-07-04T18:15:33.335100Z

HugSQL provides this functionality: https://hugsql.org