Skip to content
Snippets Groups Projects
Unverified Commit 80ba6b1a authored by Case Nelson's avatar Case Nelson Committed by GitHub
Browse files

Sqlite honey sql 2 (#28389)

* Convert sqlite driver to honey-sql-2

* Fix bad case statement

* We can't run native queries with AS for oracle

* Dont call ->honeysql on a honeysql-expr

* Fix bad call to h2x

* Identifier needs to be wrapped

* Update kondo config

* Fix test

* Remove hformat fn handler

* Rotate the Snowflake DB prefix AGAIN

* `sql.qp/format-honeysql` needs to wrap stuff in parens when generating SQL snippets

* Update tests to match new expected SQL format

* Sort namespaces

* Fix the flaky paging test

* Fix flaky `metabase.query-processor-test.timezones-test/filter-datetime-by-date-in-timezone-test`

* MySQL test fix :wrench:



* Try running Redshift with a beefier runner

---------

Co-authored-by: default avatarCam Saul <github@camsaul.com>
Co-authored-by: default avatarCam Saul <1455846+camsaul@users.noreply.github.com>
parent 57cb3caa
No related branches found
No related tags found
No related merge requests found
...@@ -597,14 +597,13 @@ ...@@ -597,14 +597,13 @@
;; "bigquery-cloud-sdk" ;; "bigquery-cloud-sdk"
;; [:and "presto" [:? [:or "-common" "-jdbc"]]] ;; [:and "presto" [:? [:or "-common" "-jdbc"]]]
;; "snowflake" ;; "snowflake"
;; "sqlite"
;; "sqlserver"] ;; "sqlserver"]
;; [:? "-test"] ;; [:? "-test"]
;; [:or #"\." #"$"]]] ;; [:or #"\." #"$"]]]
;; ".*")) ;; ".*"))
;; ;;
;; Please keep this form updated when you change the generated regex! <3 ;; Please keep this form updated when you change the generated regex! <3
{:pattern "^metabase\\.(?!util\\.(?:(?:honeysql-extensions)|(?:honey-sql-1)))(?!query-processor-test)(?!(?:(?:driver)|(?:test\\.data))\\.(?:(?:sql(?:-jdbc)?)|(?:(?:sql(?:-jdbc)?))|(?:bigquery-cloud-sdk)|(?:presto(?:(?:(?:-common)|(?:-jdbc)))?)|(?:snowflake)|(?:sqlite)|(?:sqlserver))(?:-test)?(?:(?:\\.)|(?:$))).*" {:pattern "^metabase\\.(?!util\\.(?:(?:honeysql-extensions)|(?:honey-sql-1)))(?!query-processor-test)(?!(?:(?:driver)|(?:test\\.data))\\.(?:(?:sql(?:-jdbc)?)|(?:(?:sql(?:-jdbc)?))|(?:bigquery-cloud-sdk)|(?:presto(?:(?:(?:-common)|(?:-jdbc)))?)|(?:snowflake)|(?:sqlserver))(?:-test)?(?:(?:\\.)|(?:$))).*"
:name honey-sql-2-namespaces}] :name honey-sql-2-namespaces}]
:config-in-ns :config-in-ns
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
(:require (:require
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.string :as str] [clojure.string :as str]
[honeysql.format :as hformat]
[java-time :as t] [java-time :as t]
[metabase.config :as config] [metabase.config :as config]
[metabase.driver :as driver] [metabase.driver :as driver]
...@@ -16,7 +15,7 @@ ...@@ -16,7 +15,7 @@
[metabase.driver.sql.query-processor :as sql.qp] [metabase.driver.sql.query-processor :as sql.qp]
[metabase.query-processor.error-type :as qp.error-type] [metabase.query-processor.error-type :as qp.error-type]
[metabase.util.date-2 :as u.date] [metabase.util.date-2 :as u.date]
[metabase.util.honeysql-extensions :as hx] [metabase.util.honey-sql-2 :as h2x]
[metabase.util.i18n :refer [tru]] [metabase.util.i18n :refer [tru]]
[schema.core :as s]) [schema.core :as s])
(:import (:import
...@@ -28,6 +27,10 @@ ...@@ -28,6 +27,10 @@
(driver/register! :sqlite, :parent :sql-jdbc) (driver/register! :sqlite, :parent :sql-jdbc)
(defmethod sql.qp/honey-sql-version :sqlite
[_driver]
2)
;; SQLite does not support a lot of features, so do not show the options in the interface ;; SQLite does not support a lot of features, so do not show the options in the interface
(doseq [[feature supported?] {:right-join false (doseq [[feature supported?] {:right-join false
:full-join false :full-join false
...@@ -109,75 +112,68 @@ ...@@ -109,75 +112,68 @@
(defmethod sql-jdbc.sync/fallback-metadata-query :sqlite (defmethod sql-jdbc.sync/fallback-metadata-query :sqlite
[driver schema table] [driver schema table]
(sql.qp/format-honeysql driver {:select [:*] (sql.qp/format-honeysql driver {:select [:*]
:from [(sql.qp/->honeysql driver (hx/identifier :table schema table))] :from [[(h2x/identifier :table schema table)]]
:limit 1})) :limit 1}))
;; register the SQLite concatenation operator `||` with HoneySQL as `sqlite-concat` (def ^:private ->date (partial conj [:date]))
;; (def ^:private ->datetime (partial conj [:datetime]))
;; (hsql/format (hx/call :sqlite-concat :a :b)) -> "(a || b)" (def ^:private ->time (partial conj [:time]))
(defmethod hformat/fn-handler "sqlite-concat"
[_ & args]
(str "(" (str/join " || " (map hformat/to-sql args)) ")"))
(def ^:private ->date (partial hx/call :date))
(def ^:private ->datetime (partial hx/call :datetime))
(def ^:private ->time (partial hx/call :time))
(defn- strftime [format-str expr] (defn- strftime [format-str expr]
(hx/call :strftime (hx/literal format-str) expr)) [:strftime (h2x/literal format-str) expr])
;; See also the [SQLite Date and Time Functions Reference](http://www.sqlite.org/lang_datefunc.html). ;; See also the [SQLite Date and Time Functions Reference](http://www.sqlite.org/lang_datefunc.html).
(defmethod sql.qp/date [:sqlite :default] [_driver _unit expr] expr) (defmethod sql.qp/date [:sqlite :default] [_driver _unit expr] expr)
(defmethod sql.qp/date [:sqlite :second] (defmethod sql.qp/date [:sqlite :second]
[driver _ expr] [_driver _ expr]
(->datetime (strftime "%Y-%m-%d %H:%M:%S" (sql.qp/->honeysql driver expr)))) (->datetime (strftime "%Y-%m-%d %H:%M:%S" expr)))
(defmethod sql.qp/date [:sqlite :second-of-minute] (defmethod sql.qp/date [:sqlite :second-of-minute]
[driver _ expr] [_driver _ expr]
(hx/->integer (strftime "%S" (sql.qp/->honeysql driver expr)))) (h2x/->integer (strftime "%S" expr)))
(defmethod sql.qp/date [:sqlite :minute] (defmethod sql.qp/date [:sqlite :minute]
[driver _ expr] [_driver _ expr]
(->datetime (strftime "%Y-%m-%d %H:%M" (sql.qp/->honeysql driver expr)))) (->datetime (strftime "%Y-%m-%d %H:%M" expr)))
(defmethod sql.qp/date [:sqlite :minute-of-hour] (defmethod sql.qp/date [:sqlite :minute-of-hour]
[driver _ expr] [_driver _ expr]
(hx/->integer (strftime "%M" (sql.qp/->honeysql driver expr)))) (h2x/->integer (strftime "%M" expr)))
(defmethod sql.qp/date [:sqlite :hour] (defmethod sql.qp/date [:sqlite :hour]
[driver _ expr] [_driver _ expr]
(->datetime (strftime "%Y-%m-%d %H:00" (sql.qp/->honeysql driver expr)))) (->datetime (strftime "%Y-%m-%d %H:00" expr)))
(defmethod sql.qp/date [:sqlite :hour-of-day] (defmethod sql.qp/date [:sqlite :hour-of-day]
[driver _ expr] [_driver _ expr]
(hx/->integer (strftime "%H" (sql.qp/->honeysql driver expr)))) (h2x/->integer (strftime "%H" expr)))
(defmethod sql.qp/date [:sqlite :day] (defmethod sql.qp/date [:sqlite :day]
[driver _ expr] [_driver _ expr]
(->date (sql.qp/->honeysql driver expr))) (->date expr))
;; SQLite day of week (%w) is Sunday = 0 <-> Saturday = 6. We want 1 - 7 so add 1 ;; SQLite day of week (%w) is Sunday = 0 <-> Saturday = 6. We want 1 - 7 so add 1
(defmethod sql.qp/date [:sqlite :day-of-week] (defmethod sql.qp/date [:sqlite :day-of-week]
[driver _ expr] [_driver _ expr]
(sql.qp/adjust-day-of-week :sqlite (hx/->integer (hx/inc (strftime "%w" (sql.qp/->honeysql driver expr)))))) (sql.qp/adjust-day-of-week :sqlite (h2x/->integer (h2x/inc (strftime "%w" expr)))))
(defmethod sql.qp/date [:sqlite :day-of-month] (defmethod sql.qp/date [:sqlite :day-of-month]
[driver _ expr] [_driver _ expr]
(hx/->integer (strftime "%d" (sql.qp/->honeysql driver expr)))) (h2x/->integer (strftime "%d" expr)))
(defmethod sql.qp/date [:sqlite :day-of-year] (defmethod sql.qp/date [:sqlite :day-of-year]
[driver _ expr] [_driver _ expr]
(hx/->integer (strftime "%j" (sql.qp/->honeysql driver expr)))) (h2x/->integer (strftime "%j" expr)))
(defmethod sql.qp/date [:sqlite :week] (defmethod sql.qp/date [:sqlite :week]
[_ _ expr] [_ _ expr]
(let [week-extract-fn (fn [expr] (let [week-extract-fn (fn [expr]
;; Move back 6 days, then forward to the next Sunday ;; Move back 6 days, then forward to the next Sunday
(->date (sql.qp/->honeysql :sqlite expr) (->date expr
(hx/literal "-6 days") (h2x/literal "-6 days")
(hx/literal "weekday 0")))] (h2x/literal "weekday 0")))]
(sql.qp/adjust-start-of-week :sqlite week-extract-fn expr))) (sql.qp/adjust-start-of-week :sqlite week-extract-fn expr)))
(defmethod sql.qp/date [:sqlite :week-of-year-iso] (defmethod sql.qp/date [:sqlite :week-of-year-iso]
...@@ -189,12 +185,12 @@ ...@@ -189,12 +185,12 @@
:type qp.error-type/invalid-query}))) :type qp.error-type/invalid-query})))
(defmethod sql.qp/date [:sqlite :month] (defmethod sql.qp/date [:sqlite :month]
[driver _ expr] [_driver _ expr]
(->date (sql.qp/->honeysql driver expr) (hx/literal "start of month"))) (->date expr (h2x/literal "start of month")))
(defmethod sql.qp/date [:sqlite :month-of-year] (defmethod sql.qp/date [:sqlite :month-of-year]
[driver _ expr] [_driver _ expr]
(hx/->integer (strftime "%m" (sql.qp/->honeysql driver expr)))) (h2x/->integer (strftime "%m" expr)))
;; DATE(DATE(%s, 'start of month'), '-' || ((STRFTIME('%m', %s) - 1) % 3) || ' months') ;; DATE(DATE(%s, 'start of month'), '-' || ((STRFTIME('%m', %s) - 1) % 3) || ' months')
;; -> DATE(DATE('2015-11-16', 'start of month'), '-' || ((STRFTIME('%m', '2015-11-16') - 1) % 3) || ' months') ;; -> DATE(DATE('2015-11-16', 'start of month'), '-' || ((STRFTIME('%m', '2015-11-16') - 1) % 3) || ' months')
...@@ -203,30 +199,29 @@ ...@@ -203,30 +199,29 @@
;; -> DATE('2015-11-01', '-1 months') ;; -> DATE('2015-11-01', '-1 months')
;; -> '2015-10-01' ;; -> '2015-10-01'
(defmethod sql.qp/date [:sqlite :quarter] (defmethod sql.qp/date [:sqlite :quarter]
[driver _ expr] [_driver _ expr]
(let [v (sql.qp/->honeysql driver expr)] (->date
(->date (->date expr (h2x/literal "start of month"))
(->date v (hx/literal "start of month")) [:||
(hx/call :sqlite-concat (h2x/literal "-")
(hx/literal "-") (h2x/mod (h2x/dec (strftime "%m" expr))
(hx/mod (hx/dec (strftime "%m" v)) 3)
3) (h2x/literal " months")]))
(hx/literal " months")))))
;; q = (m + 2) / 3 ;; q = (m + 2) / 3
(defmethod sql.qp/date [:sqlite :quarter-of-year] (defmethod sql.qp/date [:sqlite :quarter-of-year]
[driver _ expr] [_driver _ expr]
(hx// (hx/+ (strftime "%m" (sql.qp/->honeysql driver expr)) (h2x// (h2x/+ (strftime "%m" expr)
2) 2)
3)) 3))
(defmethod sql.qp/date [:sqlite :year] (defmethod sql.qp/date [:sqlite :year]
[driver _ expr] [_driver _ expr]
(->date (sql.qp/->honeysql driver expr) (hx/literal "start of year"))) (->date expr (h2x/literal "start of year")))
(defmethod sql.qp/date [:sqlite :year-of-era] (defmethod sql.qp/date [:sqlite :year-of-era]
[driver _ expr] [_driver _ expr]
(hx/->integer (strftime "%Y" (sql.qp/->honeysql driver expr)))) (h2x/->integer (strftime "%Y" expr)))
(defmethod sql.qp/add-interval-honeysql-form :sqlite (defmethod sql.qp/add-interval-honeysql-form :sqlite
[_driver hsql-form amount unit] [_driver hsql-form amount unit]
...@@ -239,11 +234,11 @@ ...@@ -239,11 +234,11 @@
:month [1 "months"] :month [1 "months"]
:quarter [3 "months"] :quarter [3 "months"]
:year [1 "years"])] :year [1 "years"])]
(->datetime hsql-form (hx/literal (format "%+d %s" (* amount multiplier) sqlite-unit))))) (->datetime hsql-form (h2x/literal (format "%+d %s" (* amount multiplier) sqlite-unit)))))
(defmethod sql.qp/unix-timestamp->honeysql [:sqlite :seconds] (defmethod sql.qp/unix-timestamp->honeysql [:sqlite :seconds]
[_ _ expr] [_ _ expr]
(->datetime expr (hx/literal "unixepoch"))) (->datetime expr (h2x/literal "unixepoch")))
(defmethod sql.qp/cast-temporal-string [:sqlite :Coercion/ISO8601->DateTime] (defmethod sql.qp/cast-temporal-string [:sqlite :Coercion/ISO8601->DateTime]
[_driver _semantic_type expr] [_driver _semantic_type expr]
...@@ -280,26 +275,26 @@ ...@@ -280,26 +275,26 @@
(defmethod sql.qp/->honeysql [:sqlite :substring] (defmethod sql.qp/->honeysql [:sqlite :substring]
[driver [_ arg start length]] [driver [_ arg start length]]
(if length (if length
(hx/call :substr (sql.qp/->honeysql driver arg) (sql.qp/->honeysql driver start) (sql.qp/->honeysql driver length)) [:substr (sql.qp/->honeysql driver arg) (sql.qp/->honeysql driver start) (sql.qp/->honeysql driver length)]
(hx/call :substr (sql.qp/->honeysql driver arg) (sql.qp/->honeysql driver start)))) [:substr (sql.qp/->honeysql driver arg) (sql.qp/->honeysql driver start)]))
(defmethod sql.qp/->honeysql [:sqlite :concat] (defmethod sql.qp/->honeysql [:sqlite :concat]
[driver [_ & args]] [driver [_ & args]]
(apply (into
hx/call :sqlite-concat [:||]
(mapv (partial sql.qp/->honeysql driver) args))) (mapv (partial sql.qp/->honeysql driver) args)))
(defmethod sql.qp/->honeysql [:sqlite :floor] (defmethod sql.qp/->honeysql [:sqlite :floor]
[_driver [_ arg]] [_driver [_ arg]]
(hx/call :round (hx/call :- arg 0.5))) [:round (h2x/- arg 0.5)])
(defmethod sql.qp/->honeysql [:sqlite :ceil] (defmethod sql.qp/->honeysql [:sqlite :ceil]
[_driver [_ arg]] [_driver [_ arg]]
(hx/call :case [:case
;; if we're ceiling a whole number, just cast it to an integer ;; if we're ceiling a whole number, just cast it to an integer
;; [:ceil 1.0] should returns 1 ;; [:ceil 1.0] should returns 1
(hx/call := (hx/call :round arg) arg) (hx/->integer arg) [:= [:round arg] arg] (h2x/->integer arg)
:else (hx/call :round (hx/call :+ arg 0.5)))) :else [:round (h2x/+ arg 0.5)]])
;; See https://sqlite.org/lang_datefunc.html ;; See https://sqlite.org/lang_datefunc.html
...@@ -314,33 +309,33 @@ ...@@ -314,33 +309,33 @@
(defmethod sql.qp/->honeysql [:sqlite LocalDate] (defmethod sql.qp/->honeysql [:sqlite LocalDate]
[_ t] [_ t]
(hx/call :date (hx/literal (u.date/format-sql t)))) [:date (h2x/literal (u.date/format-sql t))])
(defmethod sql.qp/->honeysql [:sqlite LocalDateTime] (defmethod sql.qp/->honeysql [:sqlite LocalDateTime]
[driver t] [driver t]
(if (zero-time? t) (if (zero-time? t)
(sql.qp/->honeysql driver (t/local-date t)) (sql.qp/->honeysql driver (t/local-date t))
(hx/call :datetime (hx/literal (u.date/format-sql t))))) [:datetime (h2x/literal (u.date/format-sql t))]))
(defmethod sql.qp/->honeysql [:sqlite LocalTime] (defmethod sql.qp/->honeysql [:sqlite LocalTime]
[_ t] [_ t]
(hx/call :time (hx/literal (u.date/format-sql t)))) [:time (h2x/literal (u.date/format-sql t))])
(defmethod sql.qp/->honeysql [:sqlite OffsetDateTime] (defmethod sql.qp/->honeysql [:sqlite OffsetDateTime]
[driver t] [driver t]
(if (zero-time? t) (if (zero-time? t)
(sql.qp/->honeysql driver (t/local-date t)) (sql.qp/->honeysql driver (t/local-date t))
(hx/call :datetime (hx/literal (u.date/format-sql t))))) [:datetime (h2x/literal (u.date/format-sql t))]))
(defmethod sql.qp/->honeysql [:sqlite OffsetTime] (defmethod sql.qp/->honeysql [:sqlite OffsetTime]
[_ t] [_ t]
(hx/call :time (hx/literal (u.date/format-sql t)))) [:time (h2x/literal (u.date/format-sql t))])
(defmethod sql.qp/->honeysql [:sqlite ZonedDateTime] (defmethod sql.qp/->honeysql [:sqlite ZonedDateTime]
[driver t] [driver t]
(if (zero-time? t) (if (zero-time? t)
(sql.qp/->honeysql driver (t/local-date t)) (sql.qp/->honeysql driver (t/local-date t))
(hx/call :datetime (hx/literal (u.date/format-sql t))))) [:datetime (h2x/literal (u.date/format-sql t))]))
;; SQLite defaults everything to UTC ;; SQLite defaults everything to UTC
(defmethod driver.common/current-db-time-date-formatters :sqlite (defmethod driver.common/current-db-time-date-formatters :sqlite
...@@ -361,56 +356,55 @@ ...@@ -361,56 +356,55 @@
(defmethod sql.qp/current-datetime-honeysql-form :sqlite (defmethod sql.qp/current-datetime-honeysql-form :sqlite
[_] [_]
(hx/call :datetime (hx/literal :now))) [:datetime (h2x/literal :now)])
(defmethod sql.qp/datetime-diff [:sqlite :year] (defmethod sql.qp/datetime-diff [:sqlite :year]
[driver _unit x y] [driver _unit x y]
(hx// (sql.qp/datetime-diff driver :month x y) 12)) (h2x// (sql.qp/datetime-diff driver :month x y) 12))
(defmethod sql.qp/datetime-diff [:sqlite :quarter] (defmethod sql.qp/datetime-diff [:sqlite :quarter]
[driver _unit x y] [driver _unit x y]
(hx// (sql.qp/datetime-diff driver :month x y) 3)) (h2x// (sql.qp/datetime-diff driver :month x y) 3))
(defmethod sql.qp/datetime-diff [:sqlite :month] (defmethod sql.qp/datetime-diff [:sqlite :month]
[driver _unit x y] [driver _unit x y]
(let [extract (fn [unit x] (sql.qp/date driver unit x)) (let [extract (fn [unit x] (sql.qp/date driver unit x))
year-diff (hx/- (extract :year y) (extract :year x)) year-diff (h2x/- (extract :year y) (extract :year x))
month-of-year-diff (hx/- (extract :month-of-year y) (extract :month-of-year x)) month-of-year-diff (h2x/- (extract :month-of-year y) (extract :month-of-year x))
total-month-diff (hx/+ month-of-year-diff (hx/* year-diff 12))] total-month-diff (h2x/+ month-of-year-diff (h2x/* year-diff 12))]
(hx/+ total-month-diff (h2x/+ total-month-diff
;; total-month-diff counts month boundaries not whole months, so we need to adjust ;; total-month-diff counts month boundaries not whole months, so we need to adjust
;; if x<y but x>y in the month calendar then subtract one month ;; if x<y but x>y in the month calendar then subtract one month
;; if x>y but x<y in the month calendar then add one month ;; if x>y but x<y in the month calendar then add one month
(hx/call [:case
:case [:and [:< x y] [:> (extract :day-of-month x) (extract :day-of-month y)]]
(hx/call :and (hx/call :< x y) (hx/call :> (extract :day-of-month x) (extract :day-of-month y)))
-1 -1
(hx/call :and (hx/call :> x y) (hx/call :< (extract :day-of-month x) (extract :day-of-month y))) [:and [:> x y] [:< (extract :day-of-month x) (extract :day-of-month y)]]
1 1
:else 0)))) :else 0])))
(defmethod sql.qp/datetime-diff [:sqlite :week] (defmethod sql.qp/datetime-diff [:sqlite :week]
[driver _unit x y] [driver _unit x y]
(hx// (sql.qp/datetime-diff driver :day x y) 7)) (h2x// (sql.qp/datetime-diff driver :day x y) 7))
(defmethod sql.qp/datetime-diff [:sqlite :day] (defmethod sql.qp/datetime-diff [:sqlite :day]
[_driver _unit x y] [_driver _unit x y]
(hx/->integer (h2x/->integer
(hx/- (hx/call :julianday y (hx/literal "start of day")) (h2x/- [:julianday y (h2x/literal "start of day")]
(hx/call :julianday x (hx/literal "start of day"))))) [:julianday x (h2x/literal "start of day")])))
(defmethod sql.qp/datetime-diff [:sqlite :hour] (defmethod sql.qp/datetime-diff [:sqlite :hour]
[driver _unit x y] [driver _unit x y]
(hx// (sql.qp/datetime-diff driver :second x y) 3600)) (h2x// (sql.qp/datetime-diff driver :second x y) 3600))
(defmethod sql.qp/datetime-diff [:sqlite :minute] (defmethod sql.qp/datetime-diff [:sqlite :minute]
[driver _unit x y] [driver _unit x y]
(hx// (sql.qp/datetime-diff driver :second x y) 60)) (h2x// (sql.qp/datetime-diff driver :second x y) 60))
(defmethod sql.qp/datetime-diff [:sqlite :second] (defmethod sql.qp/datetime-diff [:sqlite :second]
[_driver _unit x y] [_driver _unit x y]
;; strftime strftime('%s', <timestring>) returns the unix time as an integer. ;; strftime strftime('%s', <timestring>) returns the unix time as an integer.
(hx/- (strftime "%s" y) (strftime "%s" x))) (h2x/- (strftime "%s" y) (strftime "%s" x)))
;; SQLite's JDBC driver is fussy and won't let you change connections to read-only after you create them. So skip that ;; SQLite's JDBC driver is fussy and won't let you change connections to read-only after you create them. So skip that
;; step. SQLite doesn't have a notion of session timezones so don't do that either. The only thing we're doing here from ;; step. SQLite doesn't have a notion of session timezones so don't do that either. The only thing we're doing here from
......
...@@ -216,7 +216,7 @@ ...@@ -216,7 +216,7 @@
(mt/test-driver :sqlite (mt/test-driver :sqlite
(mt/dataset sample-dataset (mt/dataset sample-dataset
(is (= '{:select [source.CATEGORY_2 AS CATEGORY_2 (is (= '{:select [source.CATEGORY_2 AS CATEGORY_2
count (*) AS count] COUNT (*) AS count]
:from [{:select [products.id AS id :from [{:select [products.id AS id
products.ean AS ean products.ean AS ean
products.title AS title products.title AS title
...@@ -225,9 +225,9 @@ ...@@ -225,9 +225,9 @@
products.price AS price products.price AS price
products.rating AS rating products.rating AS rating
products.created_at AS created_at products.created_at AS created_at
(products.category || ?) AS CATEGORY_2] products.category || ? AS CATEGORY_2]
:from [products]} :from [products]}
source] AS source]
:group-by [source.CATEGORY_2] :group-by [source.CATEGORY_2]
:order-by [source.CATEGORY_2 ASC] :order-by [source.CATEGORY_2 ASC]
:limit [1]} :limit [1]}
......
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