From 1672a5b6c0d5c699acacac302f57b70bbe87647a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cam=20Sa=C3=BCl?= <cammsaul@gmail.com>
Date: Fri, 2 Jun 2017 12:32:59 -0700
Subject: [PATCH] =?UTF-8?q?Re=C3=B6rganize=20parameter-expanding=20QP=20mi?=
 =?UTF-8?q?ddleware=20a=20bit?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../query_processor/middleware/parameters.clj |  23 +++-
 .../parameters/dates.clj}                     |  83 +------------
 .../middleware/parameters/mbql.clj            |  44 +++++++
 .../parameters/sql.clj}                       |   9 +-
 test/metabase/api/database_test.clj           |  10 +-
 .../middleware/parameters/date_test.clj       |  44 +++++++
 .../parameters/mbql_test.clj}                 |  55 +--------
 .../parameters/sql_test.clj}                  | 110 +++++++++---------
 8 files changed, 184 insertions(+), 194 deletions(-)
 rename src/metabase/query_processor/{parameters.clj => middleware/parameters/dates.clj} (71%)
 create mode 100644 src/metabase/query_processor/middleware/parameters/mbql.clj
 rename src/metabase/query_processor/{sql_parameters.clj => middleware/parameters/sql.clj} (98%)
 create mode 100644 test/metabase/query_processor/middleware/parameters/date_test.clj
 rename test/metabase/query_processor/{parameters_test.clj => middleware/parameters/mbql_test.clj} (65%)
 rename test/metabase/query_processor/{sql_parameters_test.clj => middleware/parameters/sql_test.clj} (79%)

diff --git a/src/metabase/query_processor/middleware/parameters.clj b/src/metabase/query_processor/middleware/parameters.clj
index 88cf73a453b..53d25787bb1 100644
--- a/src/metabase/query_processor/middleware/parameters.clj
+++ b/src/metabase/query_processor/middleware/parameters.clj
@@ -2,20 +2,35 @@
   "Middleware for substituting parameters in queries."
   (:require [clojure.data :as data]
             [clojure.tools.logging :as log]
-            (metabase.query-processor [interface :as i]
-                                      [parameters :as params])
+            [metabase.query-processor.interface :as i]
+            [metabase.query-processor.middleware.parameters
+             [mbql :as mbql-params]
+             [sql :as sql-params]]
             [metabase.util :as u]))
 
+(defn- expand-parameters
+  "Expand any :parameters set on the QUERY-DICT and apply them to the query definition.
+   This function removes the :parameters attribute from the QUERY-DICT as part of its execution."
+  [{:keys [parameters], :as query-dict}]
+  ;; params in native queries are currently only supported for SQL drivers
+  (if (= :query (keyword (:type query-dict)))
+    (mbql-params/expand (dissoc query-dict :parameters) parameters)
+    (sql-params/expand query-dict)))
+
 (defn- substitute-parameters*
   "If any parameters were supplied then substitute them into the query."
   [query]
-  (u/prog1 (params/expand-parameters query)
+  (u/prog1 (expand-parameters query)
     (when (and (not i/*disable-qp-logging*)
                (not= <> query))
       (when-let [diff (second (data/diff query <>))]
         (log/debug (u/format-color 'cyan "\n\nPARAMS/SUBSTITUTED: %s\n%s" (u/emoji "😻") (u/pprint-to-str diff)))))))
 
 (defn substitute-parameters
-  "Substitute parameters in a "
+  "Substitute Dashboard or Card-supplied parameters in a query, replacing the param placeholers
+   with appropriate values and/or modifiying the query as appropriate.
+
+   (e.g. a SQL query with a param like `{{param}}` will have that part of the query replaced with an appropriate
+   snippet as well as any prepared statement args needed.)"
   [qp]
   (comp qp substitute-parameters*))
diff --git a/src/metabase/query_processor/parameters.clj b/src/metabase/query_processor/middleware/parameters/dates.clj
similarity index 71%
rename from src/metabase/query_processor/parameters.clj
rename to src/metabase/query_processor/middleware/parameters/dates.clj
index ae3a3cbbdaf..812e3961b8b 100644
--- a/src/metabase/query_processor/parameters.clj
+++ b/src/metabase/query_processor/middleware/parameters/dates.clj
@@ -1,18 +1,11 @@
-(ns metabase.query-processor.parameters
-  "Code for handling parameter substitution in MBQL queries."
+(ns metabase.query-processor.middleware.parameters.dates
+  "Shared code for handling datetime parameters, used by both MBQL and native params implementations."
   (:require [clj-time
              [core :as t]
              [format :as tf]]
-            [clojure.string :as s]
-            [medley.core :as m]
-            [metabase.driver :as driver]
-            [metabase.query-processor.sql-parameters :as native-params])
+            [medley.core :as m])
   (:import [org.joda.time DateTime DateTimeConstants]))
 
-;;; +-------------------------------------------------------------------------------------------------------+
-;;; |                                    DATE RANGES & PERIODS                                              |
-;;; +-------------------------------------------------------------------------------------------------------+
-
 ;; Both in MBQL and SQL parameter substitution a field value is compared to a date range, either relative or absolute.
 ;; Currently the field value is casted to a day (ignoring the time of day), so the ranges should have the same
 ;; granularity level.
@@ -69,6 +62,7 @@
   [date]
   (tf/parse (tf/formatters :date-opt-time) date))
 
+
 ;;; +-------------------------------------------------------------------------------------------------------+
 ;;; |                                    DATE STRING DECODERS                                               |
 ;;; +-------------------------------------------------------------------------------------------------------+
@@ -84,6 +78,7 @@
     (:date :date-1 :date-2) [[group-label (parse-absolute-date group-value)]]
     [[group-label group-value]]))
 
+
 (defn- regex->parser
   "Takes a regex and labels matching the regex capturing groups. Returns a parser which
   takes a parameter value, validates the value against regex and gives a map of labels
@@ -217,74 +212,8 @@
         (->> (execute-decoders absolute-date-string-decoders :range nil date-string)
              (m/map-vals (partial tf/unparse formatter-no-tz))))))
 
-(defn- date-string->filter
+(defn date-string->filter
   "Takes a string description of a date range such as 'lastmonth' or '2016-07-15~2016-08-6' and returns a
    corresponding MBQL filter clause for a given field reference."
   [date-string field-reference]
   (execute-decoders all-date-string-decoders :filter field-reference date-string))
-
-;;; +-------------------------------------------------------------------------------------------------------+
-;;; |                                             MBQL QUERIES                                              |
-;;; +-------------------------------------------------------------------------------------------------------+
-
-(defn- parse-param-value-for-type
-  "Convert PARAM-VALUE to a type appropriate for PARAM-TYPE.
-   The frontend always passes parameters in as strings, which is what we want in most cases; for numbers, instead convert the parameters to integers or floating-point numbers."
-  [param-type param-value]
-  (cond
-    ;; no conversion needed if PARAM-TYPE isn't :number or PARAM-VALUE isn't a string
-    (or (not= (keyword param-type) :number)
-        (not (string? param-value)))        param-value
-    ;; if PARAM-VALUE contains a period then convert to a Double
-    (re-find #"\." param-value)             (Double/parseDouble param-value)
-    ;; otherwise convert to a Long
-    :else                                   (Long/parseLong param-value)))
-
-(defn- build-filter-clause [{param-type :type, param-value :value, [_ field] :target}]
-  (let [param-value (parse-param-value-for-type param-type param-value)]
-    (cond
-      ;; default behavior (non-date filtering) is to use a simple equals filter
-      (not (s/starts-with? param-type "date")) ["=" field param-value]
-      ;; date range
-      :else (date-string->filter param-value field))))
-
-(defn- merge-filter-clauses [base addtl]
-  (cond
-    (and (seq base)
-         (seq addtl)) ["AND" base addtl]
-    (seq base)        base
-    (seq addtl)       addtl
-    :else             []))
-
-(defn- expand-params:mbql [query-dict [{:keys [target value], :as param} & rest]]
-  (cond
-    (not param)      query-dict
-    (or (not target)
-        (not value)) (recur query-dict rest)
-    :else            (let [filter-subclause (build-filter-clause param)
-                           query            (assoc-in query-dict [:query :filter] (merge-filter-clauses (get-in query-dict [:query :filter]) filter-subclause))]
-                       (recur query rest))))
-
-
-;;; +-------------------------------------------------------------------------------------------------------+
-;;; |                                             SQL QUERIES                                               |
-;;; +-------------------------------------------------------------------------------------------------------+
-
-(defn- expand-params:native [{:keys [driver] :as query}]
-  (if-not (driver/driver-supports? driver :native-parameters)
-    query
-    (native-params/expand-params query)))
-
-
-;;; +-------------------------------------------------------------------------------------------------------+
-;;; |                                              PUBLIC API                                               |
-;;; +-------------------------------------------------------------------------------------------------------+
-
-
-(defn expand-parameters
-  "Expand any :parameters set on the QUERY-DICT and apply them to the query definition.
-   This function removes the :parameters attribute from the QUERY-DICT as part of its execution."
-  [{:keys [parameters], :as query-dict}]
-  (if (= :query (keyword (:type query-dict)))
-    (expand-params:mbql (dissoc query-dict :parameters) parameters)
-    (expand-params:native query-dict)))
diff --git a/src/metabase/query_processor/middleware/parameters/mbql.clj b/src/metabase/query_processor/middleware/parameters/mbql.clj
new file mode 100644
index 00000000000..e13209b69c0
--- /dev/null
+++ b/src/metabase/query_processor/middleware/parameters/mbql.clj
@@ -0,0 +1,44 @@
+(ns metabase.query-processor.middleware.parameters.mbql
+  "Code for handling parameter substitution in MBQL queries."
+  (:require [clojure.string :as str]
+            [metabase.query-processor.middleware.parameters.dates :as date-params]))
+
+(defn- parse-param-value-for-type
+  "Convert PARAM-VALUE to a type appropriate for PARAM-TYPE.
+   The frontend always passes parameters in as strings, which is what we want in most cases; for numbers, instead convert the parameters to integers or floating-point numbers."
+  [param-type param-value]
+  (cond
+    ;; no conversion needed if PARAM-TYPE isn't :number or PARAM-VALUE isn't a string
+    (or (not= (keyword param-type) :number)
+        (not (string? param-value)))        param-value
+    ;; if PARAM-VALUE contains a period then convert to a Double
+    (re-find #"\." param-value)             (Double/parseDouble param-value)
+    ;; otherwise convert to a Long
+    :else                                   (Long/parseLong param-value)))
+
+(defn- build-filter-clause [{param-type :type, param-value :value, [_ field] :target}]
+  (let [param-value (parse-param-value-for-type param-type param-value)]
+    (cond
+      ;; default behavior (non-date filtering) is to use a simple equals filter
+      (not (str/starts-with? param-type "date")) ["=" field param-value]
+      ;; date range
+      :else (date-params/date-string->filter param-value field))))
+
+(defn- merge-filter-clauses [base addtl]
+  (cond
+    (and (seq base)
+         (seq addtl)) ["AND" base addtl]
+    (seq base)        base
+    (seq addtl)       addtl
+    :else             []))
+
+(defn expand
+  "Expand parameters for MBQL queries in QUERY-DICT (replacing Dashboard or Card-supplied params with the appropriate values in the queries themselves)."
+  [query-dict [{:keys [target value], :as param} & rest]]
+  (cond
+    (not param)      query-dict
+    (or (not target)
+        (not value)) (recur query-dict rest)
+    :else            (let [filter-subclause (build-filter-clause param)
+                           query            (assoc-in query-dict [:query :filter] (merge-filter-clauses (get-in query-dict [:query :filter]) filter-subclause))]
+                       (recur query rest))))
diff --git a/src/metabase/query_processor/sql_parameters.clj b/src/metabase/query_processor/middleware/parameters/sql.clj
similarity index 98%
rename from src/metabase/query_processor/sql_parameters.clj
rename to src/metabase/query_processor/middleware/parameters/sql.clj
index 6c762c40833..a086f09265a 100644
--- a/src/metabase/query_processor/sql_parameters.clj
+++ b/src/metabase/query_processor/middleware/parameters/sql.clj
@@ -1,4 +1,4 @@
-(ns metabase.query-processor.sql-parameters
+(ns metabase.query-processor.middleware.parameters.sql
   "Param substitution for *SQL* queries.
    This is a new implementation, fondly referred to as 'SQL parameters 2.0', written for v0.23.0.
    The new implementation uses prepared statement args instead of substituting them directly into the query,
@@ -8,6 +8,7 @@
             [honeysql.core :as hsql]
             [metabase.models.field :as field :refer [Field]]
             [metabase.query-processor.expand :as ql]
+            [metabase.query-processor.middleware.parameters.dates :as date-params]
             [metabase.util :as u]
             [metabase.util.schema :as su]
             [schema.core :as s]
@@ -205,7 +206,7 @@
 ;; for relative dates convert the param to a `DateRange` record type and call `->replacement-snippet-info` on it
 (s/defn ^:private ^:always-validate relative-date-dimension-value->replacement-snippet-info :- ParamSnippetInfo
   [value]
-  (->replacement-snippet-info (map->DateRange ((resolve 'metabase.query-processor.parameters/date-string->range) value *timezone*)))) ; TODO - get timezone from query dict
+  (->replacement-snippet-info (map->DateRange (date-params/date-string->range value *timezone*)))) ; TODO - get timezone from query dict
 
 (s/defn ^:private ^:always-validate dimension-value->equals-clause-sql :- ParamSnippetInfo
   [value]
@@ -402,8 +403,8 @@
   (merge native (when-let [param-snippets-info (seq (add-replacement-snippet-info (sql->params-snippets-info sql) param-key->value))]
                   (substitute sql param-snippets-info))))
 
-(defn expand-params
-  "Expand parameters inside a *native* QUERY."
+(defn expand
+  "Expand parameters inside a *SQL* QUERY."
   [query]
   (binding [*driver*   (:driver query)
             *timezone* (get-in query [:settings :report-timezone])]
diff --git a/test/metabase/api/database_test.clj b/test/metabase/api/database_test.clj
index ab97f890d3d..c22b310d80a 100644
--- a/test/metabase/api/database_test.clj
+++ b/test/metabase/api/database_test.clj
@@ -150,11 +150,11 @@
   "Delete all the randomly created Databases we've made so far. Optionally specify one or more IDs to SKIP."
   [& {:keys [skip]}]
   (db/delete! Database :id [:not-in (into (set skip)
-                                                  (for [engine datasets/all-valid-engines
-                                                        :let   [id (datasets/when-testing-engine engine
-                                                                     (:id (get-or-create-test-data-db! (driver/engine->driver engine))))]
-                                                        :when  id]
-                                                    id))]))
+                                          (for [engine datasets/all-valid-engines
+                                                :let   [id (datasets/when-testing-engine engine
+                                                             (:id (get-or-create-test-data-db! (driver/engine->driver engine))))]
+                                                :when  id]
+                                            id))]))
 
 
 ;; ## GET /api/database
diff --git a/test/metabase/query_processor/middleware/parameters/date_test.clj b/test/metabase/query_processor/middleware/parameters/date_test.clj
new file mode 100644
index 00000000000..4012819d7b9
--- /dev/null
+++ b/test/metabase/query_processor/middleware/parameters/date_test.clj
@@ -0,0 +1,44 @@
+(ns metabase.query-processor.middleware.parameters.date-test
+  (:require [expectations :refer :all]
+            [clj-time.core :as t]
+            [metabase.query-processor.middleware.parameters.dates :refer :all]))
+
+;; we hard code "now" to a specific point in time so that we can control the test output
+(defn- test-date->range [value]
+  (with-redefs-fn {#'clj-time.core/now (fn [] (t/date-time 2016 06 07 12 0 0))}
+    #(date-string->range value nil)))
+
+(expect {:end "2016-03-31", :start "2016-01-01"} (test-date->range "Q1-2016"))
+(expect {:end "2016-02-29", :start "2016-02-01"} (test-date->range "2016-02"))
+(expect {:end "2016-04-18", :start "2016-04-18"} (test-date->range "2016-04-18"))
+(expect {:end "2016-04-23", :start "2016-04-18"} (test-date->range "2016-04-18~2016-04-23"))
+(expect {:end "2016-04-23", :start "2016-04-18"} (test-date->range "2016-04-18~2016-04-23"))
+(expect {:start "2016-04-18"}                    (test-date->range "2016-04-18~"))
+(expect {:end "2016-04-18"}                      (test-date->range "~2016-04-18"))
+
+(expect {:end "2016-06-06", :start "2016-06-04"} (test-date->range "past3days"))
+(expect {:end "2016-06-06", :start "2016-05-31"} (test-date->range "past7days"))
+(expect {:end "2016-06-06", :start "2016-05-08"} (test-date->range "past30days"))
+(expect {:end "2016-05-31", :start "2016-04-01"} (test-date->range "past2months"))
+(expect {:end "2016-05-31", :start "2015-05-01"} (test-date->range "past13months"))
+(expect {:end "2015-12-31", :start "2015-01-01"} (test-date->range "past1years"))
+(expect {:end "2015-12-31", :start "2000-01-01"} (test-date->range "past16years"))
+
+(expect {:end "2016-06-10", :start "2016-06-08"} (test-date->range "next3days"))
+(expect {:end "2016-06-14", :start "2016-06-08"} (test-date->range "next7days"))
+(expect {:end "2016-07-07", :start "2016-06-08"} (test-date->range "next30days"))
+(expect {:end "2016-08-31", :start "2016-07-01"} (test-date->range "next2months"))
+(expect {:end "2017-07-31", :start "2016-07-01"} (test-date->range "next13months"))
+(expect {:end "2017-12-31", :start "2017-01-01"} (test-date->range "next1years"))
+(expect {:end "2032-12-31", :start "2017-01-01"} (test-date->range "next16years"))
+
+(expect {:end "2016-06-07", :start "2016-06-07"} (test-date->range "thisday"))
+(expect {:end "2016-06-11", :start "2016-06-05"} (test-date->range "thisweek"))
+(expect {:end "2016-06-30", :start "2016-06-01"} (test-date->range "thismonth"))
+(expect {:end "2016-12-31", :start "2016-01-01"} (test-date->range "thisyear"))
+
+(expect {:end "2016-06-04", :start "2016-05-29"} (test-date->range "lastweek"))
+(expect {:end "2016-05-31", :start "2016-05-01"} (test-date->range "lastmonth"))
+(expect {:end "2015-12-31", :start "2015-01-01"} (test-date->range "lastyear"))
+(expect {:end "2016-06-06", :start "2016-06-06"} (test-date->range "yesterday"))
+(expect {:end "2016-06-07", :start "2016-06-07"} (test-date->range "today"))
diff --git a/test/metabase/query_processor/parameters_test.clj b/test/metabase/query_processor/middleware/parameters/mbql_test.clj
similarity index 65%
rename from test/metabase/query_processor/parameters_test.clj
rename to test/metabase/query_processor/middleware/parameters/mbql_test.clj
index 225cd56fc4f..ef6cb90b51e 100644
--- a/test/metabase/query_processor/parameters_test.clj
+++ b/test/metabase/query_processor/middleware/parameters/mbql_test.clj
@@ -1,59 +1,16 @@
-(ns metabase.query-processor.parameters-test
+(ns metabase.query-processor.middleware.parameters.mbql-test
   "Tests for *MBQL* parameter substitution."
-  (:require [clj-time.core :as t]
-            [expectations :refer :all]
+  (:require [expectations :refer :all]
             [metabase
              [query-processor :as qp]
              [query-processor-test :refer [first-row format-rows-by non-timeseries-engines]]]
-            [metabase.query-processor
-             [expand :as ql]
-             [parameters :refer :all]]
+            [metabase.query-processor.expand :as ql]
+            [metabase.query-processor.middleware.parameters.mbql :refer :all]
             [metabase.test.data :as data]
             [metabase.test.data.datasets :as datasets]))
 
-;; we hard code "now" to a specific point in time so that we can control the test output
-(defn- test-date->range [value]
-  (with-redefs-fn {#'clj-time.core/now (fn [] (t/date-time 2016 06 07 12 0 0))}
-    #(date-string->range value nil)))
-
-(expect {:end "2016-03-31", :start "2016-01-01"} (test-date->range "Q1-2016"))
-(expect {:end "2016-02-29", :start "2016-02-01"} (test-date->range "2016-02"))
-(expect {:end "2016-04-18", :start "2016-04-18"} (test-date->range "2016-04-18"))
-(expect {:end "2016-04-23", :start "2016-04-18"} (test-date->range "2016-04-18~2016-04-23"))
-(expect {:end "2016-04-23", :start "2016-04-18"} (test-date->range "2016-04-18~2016-04-23"))
-(expect {:start "2016-04-18"}                    (test-date->range "2016-04-18~"))
-(expect {:end "2016-04-18"}                      (test-date->range "~2016-04-18"))
-
-(expect {:end "2016-06-06", :start "2016-06-04"} (test-date->range "past3days"))
-(expect {:end "2016-06-06", :start "2016-05-31"} (test-date->range "past7days"))
-(expect {:end "2016-06-06", :start "2016-05-08"} (test-date->range "past30days"))
-(expect {:end "2016-05-31", :start "2016-04-01"} (test-date->range "past2months"))
-(expect {:end "2016-05-31", :start "2015-05-01"} (test-date->range "past13months"))
-(expect {:end "2015-12-31", :start "2015-01-01"} (test-date->range "past1years"))
-(expect {:end "2015-12-31", :start "2000-01-01"} (test-date->range "past16years"))
-
-(expect {:end "2016-06-10", :start "2016-06-08"} (test-date->range "next3days"))
-(expect {:end "2016-06-14", :start "2016-06-08"} (test-date->range "next7days"))
-(expect {:end "2016-07-07", :start "2016-06-08"} (test-date->range "next30days"))
-(expect {:end "2016-08-31", :start "2016-07-01"} (test-date->range "next2months"))
-(expect {:end "2017-07-31", :start "2016-07-01"} (test-date->range "next13months"))
-(expect {:end "2017-12-31", :start "2017-01-01"} (test-date->range "next1years"))
-(expect {:end "2032-12-31", :start "2017-01-01"} (test-date->range "next16years"))
-
-(expect {:end "2016-06-07", :start "2016-06-07"} (test-date->range "thisday"))
-(expect {:end "2016-06-11", :start "2016-06-05"} (test-date->range "thisweek"))
-(expect {:end "2016-06-30", :start "2016-06-01"} (test-date->range "thismonth"))
-(expect {:end "2016-12-31", :start "2016-01-01"} (test-date->range "thisyear"))
-
-(expect {:end "2016-06-04", :start "2016-05-29"} (test-date->range "lastweek"))
-(expect {:end "2016-05-31", :start "2016-05-01"} (test-date->range "lastmonth"))
-(expect {:end "2015-12-31", :start "2015-01-01"} (test-date->range "lastyear"))
-(expect {:end "2016-06-06", :start "2016-06-06"} (test-date->range "yesterday"))
-(expect {:end "2016-06-07", :start "2016-06-07"} (test-date->range "today"))
-
-;;; +-------------------------------------------------------------------------------------------------------+
-;;; |                                             MBQL QUERIES                                              |
-;;; +-------------------------------------------------------------------------------------------------------+
+(defn- expand-parameters [query]
+  (expand (dissoc query :parameters) (:parameters query)))
 
 
 ;; adding a simple parameter
diff --git a/test/metabase/query_processor/sql_parameters_test.clj b/test/metabase/query_processor/middleware/parameters/sql_test.clj
similarity index 79%
rename from test/metabase/query_processor/sql_parameters_test.clj
rename to test/metabase/query_processor/middleware/parameters/sql_test.clj
index 2bd5f7aca3a..48271d23708 100644
--- a/test/metabase/query_processor/sql_parameters_test.clj
+++ b/test/metabase/query_processor/middleware/parameters/sql_test.clj
@@ -1,11 +1,11 @@
-(ns metabase.query-processor.sql-parameters-test
+(ns metabase.query-processor.middleware.parameters.sql-test
   (:require [clj-time.core :as t]
             [expectations :refer :all]
             [metabase
              [driver :as driver]
              [query-processor :as qp]
              [query-processor-test :refer [engines-that-support first-row format-rows-by]]]
-            [metabase.query-processor.sql-parameters :refer :all]
+            [metabase.query-processor.middleware.parameters.sql :refer :all]
             [metabase.test
              [data :as data]
              [util :as tu]]
@@ -17,8 +17,8 @@
 ;;; ------------------------------------------------------------ simple substitution -- {{x}} ------------------------------------------------------------
 
 (defn- substitute {:style/indent 1} [sql params]
-  (binding [metabase.query-processor.sql-parameters/*driver* (driver/engine->driver :h2)] ; apparently you can still bind private dynamic vars
-    ((resolve 'metabase.query-processor.sql-parameters/expand-query-params)
+  (binding [metabase.query-processor.middleware.parameters.sql/*driver* (driver/engine->driver :h2)] ; apparently you can still bind private dynamic vars
+    ((resolve 'metabase.query-processor.middleware.parameters.sql/expand-query-params)
      {:query sql}
      (into {} (for [[k v] params]
                 {k v})))))
@@ -182,7 +182,7 @@
 
 ;;; ------------------------------------------------------------ tests for value-for-tag ------------------------------------------------------------
 
-(tu/resolve-private-vars metabase.query-processor.sql-parameters value-for-tag)
+(tu/resolve-private-vars metabase.query-processor.middleware.parameters.sql value-for-tag)
 
 ;; variable -- specified
 (expect
@@ -192,7 +192,7 @@
 
 ;; variable -- unspecified
 (expect
-  #metabase.query_processor.sql_parameters.NoValue{}
+  #metabase.query_processor.middleware.parameters.sql.NoValue{}
   (value-for-tag {:name "id", :display_name "ID", :type "text"} nil))
 
 ;; variable -- default
@@ -238,8 +238,8 @@
 
 ;;; ------------------------------------------------------------ expansion tests: variables ------------------------------------------------------------
 
-(defn- expand-params* [query]
-  (-> (expand-params (assoc query :driver     (driver/engine->driver :h2)))
+(defn- expand* [query]
+  (-> (expand (assoc query :driver (driver/engine->driver :h2)))
       :native
       (select-keys [:query :params])))
 
@@ -247,59 +247,59 @@
 (expect
   {:query  "SELECT * FROM orders ;"
    :params []}
-  (expand-params* {:native     {:query "SELECT * FROM orders [[WHERE id = {{id}}]];"
-                                :template_tags {:id {:name "id", :display_name "ID", :type "number"}}}
-                   :parameters []}))
+  (expand* {:native     {:query "SELECT * FROM orders [[WHERE id = {{id}}]];"
+                         :template_tags {:id {:name "id", :display_name "ID", :type "number"}}}
+            :parameters []}))
 
 ;; unspecified *required* param
 (expect
   Exception
-  (expand-params {:native     {:query "SELECT * FROM orders [[WHERE id = {{id}}]];"
-                               :template_tags {:id {:name "id", :display_name "ID", :type "number", :required true}}}
-                  :parameters []}))
+  (expand {:native     {:query "SELECT * FROM orders [[WHERE id = {{id}}]];"
+                        :template_tags {:id {:name "id", :display_name "ID", :type "number", :required true}}}
+           :parameters []}))
 
 ;; default value
 (expect
   {:query  "SELECT * FROM orders WHERE id = 100;"
    :params []}
-  (expand-params* {:native     {:query "SELECT * FROM orders WHERE id = {{id}};"
-                                :template_tags {:id {:name "id", :display_name "ID", :type "number", :required true, :default "100"}}}
-                   :parameters []}))
+  (expand* {:native     {:query "SELECT * FROM orders WHERE id = {{id}};"
+                         :template_tags {:id {:name "id", :display_name "ID", :type "number", :required true, :default "100"}}}
+            :parameters []}))
 
 ;; specified param (numbers)
 (expect
   {:query  "SELECT * FROM orders WHERE id = 2;"
    :params []}
-  (expand-params* {:native     {:query "SELECT * FROM orders WHERE id = {{id}};"
-                                :template_tags {:id {:name "id", :display_name "ID", :type "number", :required true, :default "100"}}}
-                   :parameters [{:type "category", :target ["variable" ["template-tag" "id"]], :value "2"}]}))
+  (expand* {:native     {:query "SELECT * FROM orders WHERE id = {{id}};"
+                         :template_tags {:id {:name "id", :display_name "ID", :type "number", :required true, :default "100"}}}
+            :parameters [{:type "category", :target ["variable" ["template-tag" "id"]], :value "2"}]}))
 
 ;; specified param (date/single)
 (expect
   {:query  "SELECT * FROM orders WHERE created_at > ?;"
    :params [#inst "2016-07-19T00:00:00.000000000-00:00"]}
-  (expand-params* {:native     {:query "SELECT * FROM orders WHERE created_at > {{created_at}};"
-                                :template_tags {:created_at {:name "created_at", :display_name "Created At", :type "date"}}}
-                   :parameters [{:type "date/single", :target ["variable" ["template-tag" "created_at"]], :value "2016-07-19"}]}))
+  (expand* {:native     {:query "SELECT * FROM orders WHERE created_at > {{created_at}};"
+                         :template_tags {:created_at {:name "created_at", :display_name "Created At", :type "date"}}}
+            :parameters [{:type "date/single", :target ["variable" ["template-tag" "created_at"]], :value "2016-07-19"}]}))
 
 ;; specified param (text)
 (expect
   {:query  "SELECT * FROM products WHERE category = ?;"
    :params ["Gizmo"]}
-  (expand-params* {:native     {:query "SELECT * FROM products WHERE category = {{category}};"
-                                :template_tags {:category {:name "category", :display_name "Category", :type "text"}}}
-                   :parameters [{:type "category", :target ["variable" ["template-tag" "category"]], :value "Gizmo"}]}))
+  (expand* {:native     {:query "SELECT * FROM products WHERE category = {{category}};"
+                         :template_tags {:category {:name "category", :display_name "Category", :type "text"}}}
+            :parameters [{:type "category", :target ["variable" ["template-tag" "category"]], :value "Gizmo"}]}))
 
 
 ;;; ------------------------------------------------------------ expansion tests: dimensions ------------------------------------------------------------
 
 (defn- expand-with-dimension-param [dimension-param]
   (with-redefs [t/now (fn [] (t/date-time 2016 06 07 12 0 0))]
-    (expand-params* {:native     {:query "SELECT * FROM checkins WHERE {{date}};"
-                                  :template_tags {:date {:name "date", :display_name "Checkin Date", :type "dimension", :dimension ["field-id" (data/id :checkins :date)]}}}
-                     :parameters (when dimension-param
-                                   [(merge {:target ["dimension" ["template-tag" "date"]]}
-                                            dimension-param)])})))
+    (expand* {:native     {:query "SELECT * FROM checkins WHERE {{date}};"
+                           :template_tags {:date {:name "date", :display_name "Checkin Date", :type "dimension", :dimension ["field-id" (data/id :checkins :date)]}}}
+              :parameters (when dimension-param
+                            [(merge {:target ["dimension" ["template-tag" "date"]]}
+                                    dimension-param)])})))
 
 ;; dimension (date/single)
 (expect
@@ -496,53 +496,53 @@
    :template_tags {:created_at {:name "created_at", :display_name "Created At", :type "dimension", :dimension ["field-id" (data/id :checkins :date)]}},
    :params        [#inst "2017-03-01T00:00:00.000000000-00:00"
                    #inst "2017-03-31T00:00:00.000000000-00:00"]}
-  (:native (expand-params {:driver     (driver/engine->driver :h2)
-                           :native     {:query         "SELECT count(*) FROM CHECKINS WHERE {{created_at}}"
-                                        :template_tags {:created_at {:name "created_at", :display_name "Created At", :type "dimension", :dimension ["field-id" (data/id :checkins :date)]}}}
-                           :parameters [{:type "date/month-year", :target ["dimension" ["template-tag" "created_at"]], :value "2017-03"}]})))
+  (:native (expand {:driver     (driver/engine->driver :h2)
+                    :native     {:query         "SELECT count(*) FROM CHECKINS WHERE {{created_at}}"
+                                 :template_tags {:created_at {:name "created_at", :display_name "Created At", :type "dimension", :dimension ["field-id" (data/id :checkins :date)]}}}
+                    :parameters [{:type "date/month-year", :target ["dimension" ["template-tag" "created_at"]], :value "2017-03"}]})))
 
 (expect
   {:query         "SELECT count(*) FROM ORDERS"
    :template_tags {:price {:name "price", :display_name "Price", :type "number", :required false}}
    :params        []}
-  (:native (expand-params {:driver     (driver/engine->driver :h2)
-                           :native     {:query         "SELECT count(*) FROM ORDERS [[WHERE price > {{price}}]]"
-                                        :template_tags {:price {:name "price", :display_name "Price", :type "number", :required false}}}
-                           :parameters []})))
+  (:native (expand {:driver     (driver/engine->driver :h2)
+                    :native     {:query         "SELECT count(*) FROM ORDERS [[WHERE price > {{price}}]]"
+                                 :template_tags {:price {:name "price", :display_name "Price", :type "number", :required false}}}
+                    :parameters []})))
 
 (expect
   {:query         "SELECT count(*) FROM ORDERS WHERE price > 100"
    :template_tags {:price {:name "price", :display_name "Price", :type "number", :required false}}
    :params        []}
-  (:native (expand-params {:driver     (driver/engine->driver :h2)
-                           :native     {:query         "SELECT count(*) FROM ORDERS [[WHERE price > {{price}}]]"
-                                        :template_tags {:price {:name "price", :display_name "Price", :type "number", :required false}}}
-                           :parameters [{:type "category", :target ["variable" ["template-tag" "price"]], :value "100"}]})))
+  (:native (expand {:driver     (driver/engine->driver :h2)
+                    :native     {:query         "SELECT count(*) FROM ORDERS [[WHERE price > {{price}}]]"
+                                 :template_tags {:price {:name "price", :display_name "Price", :type "number", :required false}}}
+                    :parameters [{:type "category", :target ["variable" ["template-tag" "price"]], :value "100"}]})))
 
 (expect
   {:query         "SELECT count(*) FROM PRODUCTS WHERE TITLE LIKE ?"
    :template_tags {:x {:name "x", :display_name "X", :type "text", :required true, :default "%Toucan%"}}
    :params        ["%Toucan%"]}
-  (:native (expand-params {:driver     (driver/engine->driver :h2)
-                           :native     {:query         "SELECT count(*) FROM PRODUCTS WHERE TITLE LIKE {{x}}",
-                                        :template_tags {:x {:name "x", :display_name "X", :type "text", :required true, :default "%Toucan%"}}},
-                           :parameters [{:type "category", :target ["variable" ["template-tag" "x"]]}]})))
+  (:native (expand {:driver     (driver/engine->driver :h2)
+                    :native     {:query         "SELECT count(*) FROM PRODUCTS WHERE TITLE LIKE {{x}}",
+                                 :template_tags {:x {:name "x", :display_name "X", :type "text", :required true, :default "%Toucan%"}}},
+                    :parameters [{:type "category", :target ["variable" ["template-tag" "x"]]}]})))
 
 ;; make sure that you can use the same parameter multiple times (#4659)
 (expect
   {:query         "SELECT count(*) FROM products WHERE title LIKE ? AND subtitle LIKE ?"
    :template_tags {:x {:name "x", :display_name "X", :type "text", :required true, :default "%Toucan%"}}
    :params        ["%Toucan%" "%Toucan%"]}
-  (:native (expand-params {:driver     (driver/engine->driver :h2)
-                           :native     {:query         "SELECT count(*) FROM products WHERE title LIKE {{x}} AND subtitle LIKE {{x}}",
-                                        :template_tags {:x {:name "x", :display_name "X", :type "text", :required true, :default "%Toucan%"}}},
-                           :parameters [{:type "category", :target ["variable" ["template-tag" "x"]]}]})))
+  (:native (expand {:driver     (driver/engine->driver :h2)
+                    :native     {:query         "SELECT count(*) FROM products WHERE title LIKE {{x}} AND subtitle LIKE {{x}}",
+                                 :template_tags {:x {:name "x", :display_name "X", :type "text", :required true, :default "%Toucan%"}}},
+                    :parameters [{:type "category", :target ["variable" ["template-tag" "x"]]}]})))
 
 (expect
   {:query         "SELECT * FROM ORDERS WHERE true  AND ID = ? OR USER_ID = ?"
    :template_tags {:id {:name "id", :display_name "ID", :type "text"}}
    :params        ["2" "2"]}
-  (:native (expand-params {:driver     (driver/engine->driver :h2)
-                           :native     {:query         "SELECT * FROM ORDERS WHERE true [[ AND ID = {{id}} OR USER_ID = {{id}} ]]"
-                                        :template_tags {:id {:name "id", :display_name "ID", :type "text"}}}
-                           :parameters [{:type "category", :target ["variable" ["template-tag" "id"]], :value "2"}]})))
+  (:native (expand {:driver     (driver/engine->driver :h2)
+                    :native     {:query         "SELECT * FROM ORDERS WHERE true [[ AND ID = {{id}} OR USER_ID = {{id}} ]]"
+                                 :template_tags {:id {:name "id", :display_name "ID", :type "text"}}}
+                    :parameters [{:type "category", :target ["variable" ["template-tag" "id"]], :value "2"}]})))
-- 
GitLab