diff --git a/src/views/sql/core.clj b/src/views/sql/core.clj index 583b2bc..7ec7dad 100644 --- a/src/views/sql/core.clj +++ b/src/views/sql/core.clj @@ -81,11 +81,21 @@ result#)))) (defn- execute-sql! - [db [sql & params :as sqlvec]] - (let [info (query-info sql)] - (if (:returning? info) - (jdbc/query db sqlvec) - (jdbc/execute! db sqlvec)))) + [db [sql & params :as sqlvec] returning?] + (if returning? + (jdbc/query db sqlvec) + (jdbc/execute! db sqlvec))) + +(defn- vexec!* + [db [sql & params :as sqlvec] {:keys [parse? namespace tables returning?] :as options}] + (let [tables (if parse? (query-tables sql) tables) + returning? (if parse? (:returning? (query-info sql)) returning?) + results (execute-sql! db sqlvec returning?) + hint (views/hint namespace tables hint-type)] + (if-let [tx-hints (:views-sql/hints db)] + (swap! tx-hints conj hint) + (views/put-hints! [hint])) + results)) (defn vexec! "Used to run any SQL insert/update/delete query on the database while ensuring @@ -96,15 +106,30 @@ Arguments are: - db: a clojure.java.jdbc database connection - - namespace (optional): a namespace that will be included in the hints sent out - sqlvec: a JDBC-style vector containing a SQL query string followed by any - parameters for the query." - ([db namespace [sql & params :as sqlvec]] - (let [results (execute-sql! db sqlvec) - hint (views/hint namespace (query-tables sql) hint-type)] - (if-let [tx-hints (:views-sql/hints db)] - (swap! tx-hints conj hint) - (views/put-hints! [hint])) - results)) - ([db [sql & params :as sqlvec]] - (vexec! db nil sqlvec))) + parameters for the query. + + Options are: + - namespace: a namespace that will be included in the hints sent out + + NOTE: + If the SQL being run cannot be parsed (e.g. due to use of database-specific + extensions, or other limitations of JSqlParser), you will need to manually + specify the list of table names (as keywords) that the SQL query will affect + as the optional tables argument. In addition, if the SQL query includes a + RETURNING clause, you should specify :returning true in the options given + to vexec!, since automatic detection of this will not work if the SQL cannot + be parsed." + {:arglists '([db sqlvec & options] + [db sqlvec tables & options])} + [db sqlvec & options] + (let [[first-option & [other-options]] options] + (if (or (nil? options) + (map? first-option)) + (vexec!* db sqlvec (-> first-option + (assoc :parse? true) + (dissoc :returning?))) + (vexec!* db sqlvec (assoc other-options + :tables (set first-option) + :parse? false))))) + diff --git a/src/views/sql/view.clj b/src/views/sql/view.clj index 361077c..90d715b 100644 --- a/src/views/sql/view.clj +++ b/src/views/sql/view.clj @@ -23,21 +23,50 @@ (when (>= time 1000) (warn id "took" time "msecs")) data)) (relevant? [_ namespace parameters hints] - (let [[sql & sqlparams] (apply query-fn parameters) - tables (query-tables sql) - nhints (filter #(and (= namespace (:namespace %)) - (= hint-type (:type %))) hints)] + (let [[sql & _] (apply query-fn parameters) + tables (if (:parse? options) + (query-tables sql) + (:tables options)) + nhints (filter #(and (= namespace (:namespace %)) + (= hint-type (:type %))) hints)] (boolean (some #(not-empty (intersection (:hint %) tables)) nhints))))) +(defn- view* + [id db-or-db-fn sql-fn {:keys [row-fn result-set-fn tables] + :or {row-fn identity + result-set-fn doall} + :as options}] + (SQLView. id db-or-db-fn sql-fn options)) + (defn view "Creates a SQL view that uses a JDBC database configuration. - db-or-db-fn - either a database connection map, or a function that will get passed - a namespace and should return a database connection map - sql-fn - a function that returns a JDBC-style vector containing a SELECT query - followed by any parameters. this query will be run whenever this view - needs to be refreshed." - [id db-or-db-fn sql-fn & {:keys [row-fn result-set-fn] :as options - :or {row-fn identity - result-set-fn doall}}] - (SQLView. id db-or-db-fn sql-fn options)) \ No newline at end of file + Arguments are: + - id: an id for this view that is unique within the view system + - db-or-db-fn: either a database connection map, or a function that will get + passed a namespace and should return a database connection map + - sql-fn: a function that returns a JDBC-style vector containing a SELECT + query followed by any parameters. this query will be run whenever + this view needs to be refreshed. + + Options are: + - row-fn: a function that if specified will be run against each row in the + view's result set before returning it. + - result-set-fn: a function that will be run against the entire view's result + set before returning it. + + NOTE: + If the SQL being run cannot be parsed (e.g. due to use of database-specific + extensions, or other limitations of JSqlParser), you will need to manually + specify the list of table names (as keywords) that the SQL query will affect + as the optional tables argument." + {:arglists '([id db-or-db-fn sql-fn & options] + [id db-or-db-fn sql-fn tables & options])} + [id db-or-db-fn sql-fn & options] + (let [[first-option & [other-options]] options] + (if (or (nil? options) + (map? first-option)) + (view* id db-or-db-fn sql-fn (assoc first-option :parse? true)) + (view* id db-or-db-fn sql-fn (assoc other-options + :tables (set first-option) + :parse? false)))))