sql

All things SQL and JDBC...
seancorfield 2021-05-21T01:03:12.000800Z

I’ve had a few requests for the ability to log the SQL and parameters in next.jdbc so I’ve added an experimental feature on develop if folks want to try it out and provide feedback.

seancorfield 2021-05-21T01:04:50.002Z

I will probably change the first argument to the logger function to be the fully-qualified symbol, but I’m open to any and all feedback on other aspects of how you might want logging to work.

seancorfield 2021-05-21T01:11:53.006600Z

Automatically logging ResultSet data has all sorts of complications around it (size, plan doesn’t even produce a data structure to log, etc). However, having a second optional logging function that is called on the results of execute! and execute-one! is obviously possible if enough folks think it would be worth doing? There’s also the possibility of handling timing if the logging functions could coordinate. One possibility there is that the result of calling the first function on the sql-params could be passed as an argument to the second function (you wouldn’t be able to time plan reduction or operations performed on a PreparedStatement since those are not encapsulated within the library.

seancorfield 2021-05-21T01:13:25.007800Z

Along these lines:

(let [state (sql-logger `execute! sql-params)]
  (result-logger `execute! state (execute! connectable sql-params opts)))

seancorfield 2021-05-21T01:34:15.008300Z

An updated version with both sql and result logging, as an example:

dev=> (def lds (jdbc/with-logging ds 
 #_=>            (fn [sym sql-params] 
 #_=>              (prn sym sql-params) 
 #_=>              (System/nanoTime)) 
 #_=>            (fn [sym state result] 
 #_=>              (prn sym 
 #_=>                   (- (System/nanoTime) state) 
 #_=>                   (if (map? result) result (count result))))))
#'dev/lds
dev=> (sql/find-by-keys lds :foo {:name "Piyer"})
next.jdbc/execute! ["SELECT * FROM foo WHERE name = ?" "Piyer"]
next.jdbc/execute! 813049 1
[#:FOO{:NAME "Piyer"}]
dev=> 

2021-05-21T05:48:13.013100Z

Very interesting! At work we use a custom datomic DB in development which taps> each query, result and execution time. We were wondering if we could do something similar to next.jdbc. I'll take a look at it today and see if it fits the bill.

seancorfield 2021-05-21T05:55:42.013800Z

@jeroen.dejong Cool. Since you just pass in two “logging” functions, you should be able to do whatever you need in that respect.

1
seancorfield 2021-05-21T05:56:49.014400Z

(you can use the symbol passed in to determine whether or not the “result” function will be called)

2021-05-21T13:31:20.015600Z

Thanks for the pointers! It works really well. Nice to have this one back in the toolchain 🙂 (even if it's just experimental for now)

2021-05-21T13:37:46.015800Z

For datomic we actually allowed to pass a 'wrapper' fn; i.e. it worked like this:

(fn [f]
  (fn [arg-map]
    (println "before" arg-map)
    (let [res (f arg-map)]
      (println "after" res)
      res))
It allows for a little more freedom but the two fns are easier to work with 🙂

dakra 2021-05-21T15:45:15.018Z

How can I tell next.jdbc to escape keywords when inserting? E.g.

(jdbc/execute! ds ["
create table address (
  id int auto_increment primary key,
  `key` varchar(32),
  email varchar(255)
)"])

(next.jdbc.sql/insert! ds :address {:key "fail" :email "<mailto:test@blub.com|test@blub.com>"})
fails with sql syntax error in MariaDB.

dharrigan 2021-05-21T15:50:29.018200Z

Maybe this might help? <https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.2.659/doc/getting-started/friendly-sql-functions#table--column-entity-names>

dakra 2021-05-21T15:53:51.019200Z

Oh, wow, thanks. I swear I read 10 times already over this page but didn't see it :face_palm: passing :column-fn next.jdbc.quoted/mysql works 🙂

dharrigan 2021-05-21T15:53:59.019400Z

:thumbsup:

seancorfield 2021-05-21T16:37:35.019500Z

That’s a good structure for being able to transform the result — but I don’t want to put a burden on users to correctly return the result set structure, just because they’re doing logging. I want them to be able to pass in simple functions (like prn) and not have it break.

1➕