Skip to content
Snippets Groups Projects
Unverified Commit a26cba2a authored by Howon Lee's avatar Howon Lee Committed by GitHub
Browse files

Port fixes for Postgresql JSON issues to MySQL (#23758)

#22967 and boat#161 also apply to MySQL JSON implementation. This PR ports the fixes for those (in #23278 and #22997 respectively) to MySQL.

There are many other JSON implementation fixes which don't need a separate port because we moved the describe-table mechanism to the SQL JDBC driver instead of just the Postgres driver.
parent a29e1877
No related branches found
No related tags found
No related merge requests found
......@@ -167,6 +167,17 @@
"and cache field values?"))
:visible-if {"let-user-control-scheduling" true}})
(def json-unfolding
"Map representing the `json-unfolding` option in a DB connection form"
{:name "json-unfolding"
:display-name (deferred-tru "Unfold JSON Columns")
:type :boolean
:visible-if {"advanced-options" true}
:description (deferred-tru
(str "We unfold JSON columns into component fields."
"This is on by default but you can turn it off if performance is slow."))
:default true})
(def refingerprint
"Map representing the `refingerprint` option in a DB connection form."
{:name "refingerprint"
......
......@@ -4,6 +4,7 @@
[clojure.set :as set]
[clojure.string :as str]
[clojure.tools.logging :as log]
[clojure.walk :as walk]
[honeysql.core :as hsql]
[honeysql.format :as hformat]
[java-time :as t]
......@@ -27,7 +28,8 @@
[metabase.util.honeysql-extensions :as hx]
[metabase.util.i18n :refer [deferred-tru trs]])
(:import [java.sql DatabaseMetaData ResultSet ResultSetMetaData Types]
[java.time LocalDateTime OffsetDateTime OffsetTime ZonedDateTime]))
[java.time LocalDateTime OffsetDateTime OffsetTime ZonedDateTime]
metabase.util.honeysql_extensions.Identifier))
(comment
ddl.mysql/keep-me)
......@@ -38,7 +40,8 @@
(defmethod driver/display-name :mysql [_] "MySQL")
(defmethod driver/database-supports? [:mysql :nested-field-columns] [_ _ _] true)
(defmethod driver/database-supports? [:mysql :nested-field-columns] [_ _ database]
(or (get-in database [:details :json-unfolding]) true))
(defmethod driver/database-supports? [:mysql :persist-models] [_driver _feat _db] true)
......@@ -110,6 +113,7 @@
default-ssl-cert-details
driver.common/ssh-tunnel-preferences
driver.common/advanced-options-start
driver.common/json-unfolding
(assoc driver.common/additional-options
:placeholder "tinyInt1isBit=false")
driver.common/default-advanced-options]
......@@ -233,10 +237,9 @@
(hsql/call :char_length (sql.qp/->honeysql driver arg)))
(defmethod sql.qp/json-query :mysql
[_ identifier stored-field]
[_ unwrapped-identifier stored-field]
(letfn [(handle-name [x] (str "\"" (if (number? x) (str x) (name x)) "\""))]
(let [nfc-path (:nfc_path stored-field)
unwrapped-identifier (:form identifier)
parent-identifier (field/nfc-field->parent-identifier unwrapped-identifier stored-field)
jsonpath-query (format "$.%s" (str/join "." (map handle-name (rest nfc-path))))]
(reify
......@@ -254,7 +257,10 @@
(if (field/json-field? stored-field)
(if (::sql.qp/forced-alias opts)
(keyword (::add/source-alias opts))
(sql.qp/json-query :mysql identifier stored-field))
(walk/postwalk #(if (instance? Identifier %)
(sql.qp/json-query :mysql % stored-field)
%)
identifier))
identifier)))
;; Since MySQL doesn't have date_trunc() we fake it by formatting a date to an appropriate string and then converting
......
......@@ -166,12 +166,8 @@
:visible-if {"ssl-use-client-auth" true}}
driver.common/ssh-tunnel-preferences
driver.common/advanced-options-start
{:name "json-unfolding"
:display-name (trs "Unfold JSON Columns")
:type :boolean
:visible-if {"advanced-options" true}
:description (trs "We unfold JSON columns into component fields. This is on by default but you can turn it off if performance is slow.")
:default true}
driver.common/json-unfolding
(assoc driver.common/additional-options
:placeholder "prepareThreshold=0")
driver.common/default-advanced-options]
......
......@@ -443,7 +443,7 @@
{:name "big_json"}))))))))
(deftest json-query-test
(let [boop-identifier (hx/with-type-info (hx/identifier :field "boop" "bleh -> meh") {})]
(let [boop-identifier (:form (hx/with-type-info (hx/identifier :field "boop" "bleh -> meh") {}))]
(testing "Transforming MBQL query with JSON in it to mysql query works"
(let [boop-field {:nfc_path [:bleh :meh] :database_type "integer"}]
(is (= ["JSON_EXTRACT(boop.bleh, ?)" "$.\"meh\""]
......@@ -477,6 +477,25 @@
(:query compile-res)))
(is (= '("$.\"1234\"" "$.\"1234\"" "$.\"1234\"") (:params compile-res))))))))))
(deftest complicated-json-identifier-test
(mt/test-driver :mysql
(when (not (is-mariadb? (u/id (mt/db))))
(testing "Deal with complicated identifier (#22967, but for mysql)"
(mt/dataset json
(let [database (mt/db)
table (db/select-one Table :db_id (u/id database) :name "json")]
(sync/sync-table! table)
(let [field (db/select-one Field :table_id (u/id table) :name "json_bit → 1234")]
(mt/with-everything-store
(let [field-clause [:field (u/the-id field) {:binning
{:strategy :num-bins,
:num-bins 100,
:min-value 0.75,
:max-value 54.0,
:bin-width 0.75}}]]
(is (= ["((floor(((JSON_EXTRACT(json.json_bit, ?) - 0.75) / 0.75)) * 0.75) + 0.75)" "$.\"1234\""]
(hsql/format (sql.qp/->honeysql :mysql field-clause)))))))))))))
(deftest ddl.execute-with-timeout-test
(mt/test-driver :mysql
(mt/dataset json
......
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