Skip to content
Snippets Groups Projects
Commit 71a58264 authored by Cam Saul's avatar Cam Saul Committed by Daniel Higginbotham
Browse files

Make sure query templates containing newlines are parsed properly (#11532)

Make sure query templates containing newlines are parsed properly [ci drivers]
parent 708bd396
No related branches found
No related tags found
No related merge requests found
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
["]]" :optional-end] ["]]" :optional-end]
;; param-begin should only match the last two opening brackets in a sequence of > 2, e.g. ;; param-begin should only match the last two opening brackets in a sequence of > 2, e.g.
;; [{$match: {{{x}}, field: 1}}] should parse to ["[$match: {" (param "x") ", field: 1}}]"] ;; [{$match: {{{x}}, field: 1}}] should parse to ["[$match: {" (param "x") ", field: 1}}]"]
[#"(.*?)(\{\{(?!\{))(.*)" :param-begin] [#"(?s)(.*?)(\{\{(?!\{))(.*)" :param-begin]
["}}" :param-end]])) ["}}" :param-end]]))
(defn- param [& [k & more]] (defn- param [& [k & more]]
......
...@@ -70,6 +70,10 @@ ...@@ -70,6 +70,10 @@
(let [query "SELECT array_dims(1 || '[0:1]={2,3}'::int[])"] (let [query "SELECT array_dims(1 || '[0:1]={2,3}'::int[])"]
{query [query]}) {query [query]})
"Queries with newlines (#11526)"
{"SELECT count(*)\nFROM products\nWHERE category = {{category}}"
["SELECT count(*)\nFROM products\nWHERE category = " (param "category")]}
"JSON queries that contain non-param fragments like '}}'" "JSON queries that contain non-param fragments like '}}'"
{"{x: {y: \"{{param}}\"}}" ["{x: {y: \"" (param "param") "\"}}"] {"{x: {y: \"{{param}}\"}}" ["{x: {y: \"" (param "param") "\"}}"]
"{$match: {{{date}}, field: 1}}}" ["{$match: {" (param "date") ", field: 1}}}"]}}] "{$match: {{{date}}, field: 1}}}" ["{$match: {" (param "date") ", field: 1}}}"]}}]
......
...@@ -698,6 +698,21 @@ ...@@ -698,6 +698,21 @@
:default "2017-11-14" :default "2017-11-14"
:widget-type :date/all-options}}}})) :widget-type :date/all-options}}}}))
(deftest newlines-test
(testing "Make sure queries with newlines are parsed correctly (#11526)"
(is (= [[1]]
(mt/rows
(qp/process-query
{:database (mt/id)
:type "native"
:native {:query "SELECT count(*)\nFROM venues\n WHERE name = {{name}}"
:template-tags {:name {:name "name"
:display_name "Name"
:type "text"
:required true
:default "Fred 62"}}}
:parameters []}))))))
;;; ------------------------------- Multiple Value Support (comma-separated or array) -------------------------------- ;;; ------------------------------- Multiple Value Support (comma-separated or array) --------------------------------
...@@ -705,15 +720,15 @@ ...@@ -705,15 +720,15 @@
(testing "Make sure using commas in numeric params treats them as separate IDs (#5457)" (testing "Make sure using commas in numeric params treats them as separate IDs (#5457)"
(is (= "SELECT * FROM USERS where id IN (1, 2, 3)" (is (= "SELECT * FROM USERS where id IN (1, 2, 3)"
(-> (qp/process-query (-> (qp/process-query
{:database (mt/id) {:database (mt/id)
:type "native" :type "native"
:native {:query "SELECT * FROM USERS [[where id IN ({{ids_list}})]]" :native {:query "SELECT * FROM USERS [[where id IN ({{ids_list}})]]"
:template-tags {"ids_list" {:name "ids_list" :template-tags {"ids_list" {:name "ids_list"
:display-name "Ids list" :display-name "Ids list"
:type :number}}} :type :number}}}
:parameters [{:type "category" :parameters [{:type "category"
:target [:variable [:template-tag "ids_list"]] :target [:variable [:template-tag "ids_list"]]
:value "1,2,3"}]}) :value "1,2,3"}]})
:data :native_form :query)))) :data :native_form :query))))
(testing "make sure you can now also pass multiple values in by passing an array of values" (testing "make sure you can now also pass multiple values in by passing an array of values"
(is (= {:query "SELECT * FROM CATEGORIES where name IN (?, ?, ?)" (is (= {:query "SELECT * FROM CATEGORIES where name IN (?, ?, ?)"
......
...@@ -78,44 +78,56 @@ ...@@ -78,44 +78,56 @@
{:$project {"_id" false, "count" true}}]) {:$project {"_id" false, "count" true}}])
:collection (name table)})) :collection (name table)}))
(defn- count-query [table field->type+value] (defn- count-query [table field->type+value {:keys [defaults?]}]
{:database (mt/id) {:database (mt/id)
:type :native :type :native
:native (assoc (native-count-query driver/*driver* table field->type+value) :native (assoc (native-count-query driver/*driver* table field->type+value)
:template-tags (into {} (for [[field [param-type]] field->type+value] :template-tags (into {} (for [[field [param-type v]] field->type+value]
[field {:name (name field) [field (cond-> {:name (name field)
:display-name (name field) :display-name (name field)
:type (or (namespace param-type) :type (or (namespace param-type)
(name param-type))}]))) (name param-type))}
:parameters (for [[field [param-type v]] field->type+value] defaults? (assoc :default v))])))
{:type param-type :parameters (when-not defaults?
:target [:variable [:template-tag (name field)]] (for [[field [param-type v]] field->type+value]
:value v})}) {:type param-type
:target [:variable [:template-tag (name field)]]
(defn- count= [expected table field->type+value] :value v}))})
(let [query (count-query table field->type+value)]
(testing (str "\nquery =\n" (u/pprint-to-str query))
(is (= expected
(ffirst
(mt/formatted-rows [int]
(qp/process-query query))))
(format "count with of %s with %s should be %d"
(name table)
(str/join " and " (for [[field [_ v]] field->type+value]
(format "%s = %s" (name field) v)))
expected)))))
(deftest param-test (deftest param-test
(mt/test-drivers (mt/normal-drivers-with-feature :native-parameters) (mt/test-drivers (mt/normal-drivers-with-feature :native-parameters)
(testing "text params" (doseq [[message {:keys [expected-count table param-name param-type value exclude-drivers]}]
(count= 1 {"text params" {:expected-count 1
:venues {:name [:text "In-N-Out Burger"]})) :table :venues
(testing "number params" :param-name :name
(count= 22 :param-type :text
:venues {:price [:number "1"]})) :value "In-N-Out Burger"}
;; FIXME — This is not currently working on SQLite, probably because SQLite's implementation of temporal types is "number params" {:expected-count 22
;; wacko. :table :venues
(when-not (= driver/*driver* :sqlite) :param-name :price
(testing "date params" :param-type :number
(count= 1 :value "1"}
:users {:last_login [:date/single "2014-08-02T09:30Z"]}))))) "date params" {:expected-count 1
;; FIXME — This is not currently working on SQLite, probably because SQLite's
;; implementation of temporal types is wacko.
:exclude-drivers #{:sqlite}
:table :users
:param-name :last_login
:param-type :date/single
:value "2014-08-02T09:30Z"}}
:when (not (contains? exclude-drivers driver/*driver*))]
(testing (str "\n" message)
(doseq [[message options] {"Query with all supplied parameters" nil
"Query using default values" {:defaults? true}}]
(testing (str "\n" message)
(let [query (count-query table {param-name [param-type value]} options)]
(testing (str "\nquery =\n" (u/pprint-to-str query))
(is (= expected-count
(ffirst
(mt/formatted-rows [int]
(qp/process-query query))))
(format "count with of %s with %s = %s should be %d"
(name table)
(name param-name)
value
expected-count))))))))))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment