diff --git a/modules/drivers/mongo/src/metabase/driver/mongo/query_processor.clj b/modules/drivers/mongo/src/metabase/driver/mongo/query_processor.clj index 00b55c0d763f914ede254b4c6f41542a358b6c7b..703d03b2971c11f6bbc9413a4bf20fd5ca859dcf 100644 --- a/modules/drivers/mongo/src/metabase/driver/mongo/query_processor.clj +++ b/modules/drivers/mongo/src/metabase/driver/mongo/query_processor.clj @@ -25,7 +25,8 @@ $multiply $ne $not $or $project $regex $second $size $skip $sort $strcasecmp $subtract $sum $toLower $year]] [schema.core :as s]) - (:import org.bson.types.ObjectId)) + (:import [org.bson.types ObjectId Binary] + org.bson.BsonBinarySubType)) ;;; +----------------------------------------------------------------------------------------------------------------+ ;;; | Schema | @@ -289,15 +290,32 @@ (defmethod ->rvalue nil [_] nil) +(defn- uuid->bsonbinary + [u] + (let [lo (.getLeastSignificantBits ^java.util.UUID u) + hi (.getMostSignificantBits ^java.util.UUID u) + ba (-> (java.nio.ByteBuffer/allocate 16) ; UUID is 128 bits-long + (.putLong hi) + (.putLong lo) + (.array))] + (Binary. BsonBinarySubType/UUID_STANDARD ba))) + (defmethod ->rvalue :value [[_ value {base-type :base_type}]] - (if (and (isa? base-type :type/MongoBSONID) - (some? value)) - ;; Passing nil or "" to the ObjectId constructor throws an exception - ;; "invalid hexadecimal representation of an ObjectId: []" so, just treat it as nil - (when (not= value "") - (ObjectId. (str value))) - value)) + (cond + ;; Passing nil or "" to the ObjectId or Binary constructor throws an exception + (or (nil? value) (= value "")) + value + + (isa? base-type :type/MongoBSONID) + (ObjectId. (str value)) + + (isa? base-type :type/UUID) + (-> (str value) + java.util.UUID/fromString + uuid->bsonbinary) + + :else value)) (defn- $date-from-string [s] {:$dateFromString {:dateString (str s)}}) diff --git a/modules/drivers/mongo/test/metabase/driver/mongo_test.clj b/modules/drivers/mongo/test/metabase/driver/mongo_test.clj index 444b453f8accff47abb10dba7a90209048866331..1f3fbd4bd994853b84a6e19a0a150090c7f99ccf 100644 --- a/modules/drivers/mongo/test/metabase/driver/mongo_test.clj +++ b/modules/drivers/mongo/test/metabase/driver/mongo_test.clj @@ -91,7 +91,7 @@ :dbname "metabase-test" :version "2.2134234.lol"} :expected false}] - :let [ssl-details (tdm/conn-details details)]] + :let [ssl-details (tdm/conn-details details)]] (testing (str "connect with " details) (is (= expected (let [db (db/insert! Database {:name "dummy", :engine "mongo", :details ssl-details})] @@ -264,42 +264,71 @@ {:order-by [:name]})] (into {} field)))))))))) - (tx/defdataset with-bson-ids [["birds" [{:field-name "name", :base-type :type/Text} - {:field-name "bird_id", :base-type :type/MongoBSONID}] - [["Rasta Toucan" (ObjectId. "012345678901234567890123")] - ["Lucky Pigeon" (ObjectId. "abcdefabcdefabcdefabcdef")] + {:field-name "bird_id", :base-type :type/MongoBSONID} + {:field-name "bird_uuid", :base-type :type/UUID}] + [["Rasta Toucan" (ObjectId. "012345678901234567890123") "11111111-1111-1111-1111-111111111111"] + ["Lucky Pigeon" (ObjectId. "abcdefabcdefabcdefabcdef") "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"] ["Unlucky Raven" nil]]]]) (deftest bson-ids-test (mt/test-driver :mongo - (testing "BSON IDs" - (is (= [[2 "Lucky Pigeon" (ObjectId. "abcdefabcdefabcdefabcdef")]] - (rows (mt/dataset with-bson-ids - (mt/run-mbql-query birds - {:filter [:= $bird_id "abcdefabcdefabcdefabcdef"]})))) - "Check that we support Mongo BSON ID and can filter by it (#1367)") - - (is (= [[3 "Unlucky Raven" nil]] - (rows (mt/dataset with-bson-ids - (mt/run-mbql-query birds - {:filter [:is-null $bird_id]})))) - "handle null ObjectId queries properly (#11134)") - - (is (= [[3 "Unlucky Raven" nil]] - (rows (mt/dataset with-bson-ids - (mt/run-mbql-query birds - {:filter [:is-empty $bird_id]})))) - "treat null ObjectId as empty (#15801)") - - (is (= [[1 "Rasta Toucan" (ObjectId. "012345678901234567890123")] - [2 "Lucky Pigeon" (ObjectId. "abcdefabcdefabcdefabcdef")]] - (rows (mt/dataset with-bson-ids - (mt/run-mbql-query birds - {:filter [:not-empty $bird_id]})))) - "treat non-null ObjectId as not-empty (#15801)")))) + (mt/dataset with-bson-ids + (testing "BSON IDs" + (testing "Check that we support Mongo BSON ID and can filter by it (#1367)" + (is (= [[2 "Lucky Pigeon" (ObjectId. "abcdefabcdefabcdefabcdef")]] + (rows (mt/run-mbql-query birds + {:filter [:= $bird_id "abcdefabcdefabcdefabcdef"] + :fields [$id $name $bird_id]}))))) + + (testing "handle null ObjectId queries properly (#11134)" + (is (= [[3 "Unlucky Raven" nil]] + (rows (mt/run-mbql-query birds + {:filter [:is-null $bird_id] + :fields [$id $name $bird_id]}))))) + + (testing "treat null ObjectId as empty (#15801)" + (is (= [[3 "Unlucky Raven" nil]] + (rows (mt/run-mbql-query birds + {:filter [:is-empty $bird_id] + :fields [$id $name $bird_id]}))))) + + (testing "treat non-null ObjectId as not-empty (#15801)" + (is (= [[1 "Rasta Toucan" (ObjectId. "012345678901234567890123")] + [2 "Lucky Pigeon" (ObjectId. "abcdefabcdefabcdefabcdef")]] + (rows (mt/run-mbql-query birds + {:filter [:not-empty $bird_id] + :fields [$id $name $bird_id]})))))) + + (testing "BSON UUIDs" + (testing "Check that we support Mongo BSON UUID and can filter by it" + (is (= [[2 "Lucky Pigeon" "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"]] + (rows (mt/run-mbql-query birds + {:filter [:= $bird_uuid "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"] + :fields [$id $name $bird_uuid]}))))) + + (testing "handle null UUID queries properly" + (is (= [[3 "Unlucky Raven" nil]] + (rows (mt/run-mbql-query birds + {:filter [:is-null $bird_uuid] + :fields [$id $name $bird_uuid]}))))) + + + (testing "treat null UUID as empty" + (is (= [[3 "Unlucky Raven" nil]] + (rows (mt/run-mbql-query birds + {:filter [:is-empty $bird_uuid] + :fields [$id $name $bird_uuid]})))))) + + (testing "treat non-null UUID as not-empty" + (is (= [[1 "Rasta Toucan" "11111111-1111-1111-1111-111111111111"] + [2 "Lucky Pigeon" "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"]] + (rows (mt/run-mbql-query birds + {:filter [:not-empty $bird_uuid] + :fields [$id $name $bird_uuid]})))))))) + (deftest bson-fn-call-forms-test (mt/test-driver :mongo