From b961ef04516cef031116e4ddb0d21d003c43d565 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Thu, 20 May 2021 18:37:42 -0700 Subject: [PATCH] Add optional result logging and state passthru --- CHANGELOG.md | 2 +- src/next/jdbc.clj | 27 +++++++++++++++++++-------- src/next/jdbc/sql_logging.clj | 26 +++++++++++++++++--------- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 511fd83..88d1078 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ Only accretive/fixative changes will be made from now on. * 1.2.next in progress - * Add `next.jdbc/with-logging` to create a wrapped connectable that will invoke a logging function with the SQL and parameters for each operation. _[needs docs and tests]_ + * _[Experimental! Will change in response to feedback!]_ Add `next.jdbc/with-logging` to create a wrapped connectable that will invoke logging functions with the SQL/parameters and optionally the result for each operation. _[needs docs and tests]_ * Update `test-runner`. * 1.2.659 -- 2021-05-05 diff --git a/src/next/jdbc.clj b/src/next/jdbc.clj index 6b26784..f417897 100644 --- a/src/next/jdbc.clj +++ b/src/next/jdbc.clj @@ -370,22 +370,33 @@ (opts/->DefaultOptions connectable opts)) (defn with-logging - "Given a connectable/transactable object and a logging function - that should be used on all operations on that object, return a new - wrapper object that can be used in its place. + "Given a connectable/transactable object and a sql/params logging + function and an optional result logging function that should be used + on all operations on that object, return a new wrapper object that can + be used in its place. - The logging function will be called with two arguments: + The sql/params logging function will be called with two arguments: * a symbol indicating which operation is being performed: - * `plan`, `execute-one!`, `execute!`, or `prepare` - * the vector containing the SQL string and its parameters + * `next.jdbc/plan`, `next.jdbc/execute-one!`, `next.jdbc/execute!`, + or `next.jdbc/prepare` + * the vector containing the SQL string and its parameters + Whatever the sql/params logging function returns will be passed as a + `state` argument to the optional result logging function. + + The result logging function, if provided, will be called with the + same symbol passed to the sql/params logging function, the `state` + returned by the sql/params logging function, and the result of the + `execute!` or `execute-one!` call. The result logging function is + not called for the `plan` or `prepare` call (since they do not produce + result sets directly). Bear in mind that `get-datasource`, `get-connection`, and `with-transaction` return plain Java objects, so if you call any of those on this wrapped object, you'll need to re-wrap the Java object `with-logging` again. See the Datasources, Connections & Transactions section of Getting Started for more details, and some examples of use with these functions." - [connectable opts] - (logger/->SQLLogging connectable opts)) + [connectable sql-logger & [result-logger]] + (logger/->SQLLogging connectable sql-logger result-logger)) (def snake-kebab-opts "A hash map of options that will convert Clojure identifiers to diff --git a/src/next/jdbc/sql_logging.clj b/src/next/jdbc/sql_logging.clj index 1f2fdb8..7ee1123 100644 --- a/src/next/jdbc/sql_logging.clj +++ b/src/next/jdbc/sql_logging.clj @@ -6,7 +6,7 @@ (set! *warn-on-reflection* true) -(defrecord SQLLogging [connectable logger]) +(defrecord SQLLogging [connectable sql-logger result-logger]) (extend-protocol p/Sourceable SQLLogging @@ -19,25 +19,33 @@ (p/get-connection (:connectable this) (merge (:options this) opts)))) +(defn- result-logger-helper [result this sym state] + (when-let [logger (:result-logger this)] + (logger sym state result))) + (extend-protocol p/Executable SQLLogging (-execute [this sql-params opts] - ((:logger this) 'plan sql-params) + ;; no result-logger call possible: + ((:sql-logger this) 'next.jdbc/plan sql-params) (p/-execute (:connectable this) sql-params (merge (:options this) opts))) (-execute-one [this sql-params opts] - ((:logger this) 'execute-one! sql-params) - (p/-execute-one (:connectable this) sql-params - (merge (:options this) opts))) + (let [state ((:sql-logger this) 'next.jdbc/execute-one! sql-params)] + (doto (p/-execute-one (:connectable this) sql-params + (merge (:options this) opts)) + (result-logger-helper this 'next.jdbc/execute-one! state)))) (-execute-all [this sql-params opts] - ((:logger this) 'execute! sql-params) - (p/-execute-all (:connectable this) sql-params - (merge (:options this) opts)))) + (let [state ((:sql-logger this) 'next.jdbc/execute! sql-params)] + (doto (p/-execute-all (:connectable this) sql-params + (merge (:options this) opts)) + (result-logger-helper this 'next.jdbc/execute! state))))) (extend-protocol p/Preparable SQLLogging (prepare [this sql-params opts] - ((:logger this) 'prepare sql-params) + ;; no result-logger call possible: + ((:sql-logger this) 'next.jdbc/prepare sql-params) (p/prepare (:connectable this) sql-params (merge (:options this) opts))))