From 65a949f90c6ebbf926db10ba6ea0d68aab87497d Mon Sep 17 00:00:00 2001 From: Nikos Epping Date: Mon, 15 Jan 2024 11:28:30 +0100 Subject: [PATCH 01/11] Use new honeysql2 imports --- src/metabase/driver/firebird.clj | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/metabase/driver/firebird.clj b/src/metabase/driver/firebird.clj index 9ab5f5f..65eccab 100644 --- a/src/metabase/driver/firebird.clj +++ b/src/metabase/driver/firebird.clj @@ -3,8 +3,8 @@ [set :as set] [string :as str]] [clojure.java.jdbc :as jdbc] - [honeysql.core :as hsql] - [honeysql.format :as hformat] + [honey.sql :as hsql] + [honey.sql :as hformat] [java-time :as t] [metabase.driver :as driver] [metabase.driver.common :as driver.common] @@ -15,8 +15,7 @@ [sync :as sql-jdbc.sync]] [metabase.driver.sql-jdbc.sync.common :as sql-jdbc.sync.common] [metabase.driver.sql.query-processor :as sql.qp] - [metabase.util - [honeysql-extensions :as hx]] + [metabase.util.honey-sql-2 :as hx] [metabase.util.ssh :as ssh]) (:import [java.sql DatabaseMetaData Time] [java.time LocalDate LocalDateTime LocalTime OffsetDateTime OffsetTime ZonedDateTime] From 5da98fd3c972fdd6e9abdffb0b2a6ce8b89a7dfd Mon Sep 17 00:00:00 2001 From: Nikos Epping Date: Mon, 15 Jan 2024 11:28:42 +0100 Subject: [PATCH 02/11] Define honeysql2 version --- src/metabase/driver/firebird.clj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/metabase/driver/firebird.clj b/src/metabase/driver/firebird.clj index 65eccab..af3deb9 100644 --- a/src/metabase/driver/firebird.clj +++ b/src/metabase/driver/firebird.clj @@ -34,6 +34,11 @@ :subname (str "//" host ":" port "/" db jdbc-flags)} (dissoc opts :host :port :db :jdbc-flags))) +;; use Honey SQL 2 +(defmethod sql.qp/honey-sql-version :firebird + [_driver] + 2) + ;; Obtain connection properties for connection to a Firebird database. (defmethod sql-jdbc.conn/connection-details->spec :firebird [_ details] (-> details From b04534c0ea85599ddbc92ac8ca88399bbf0beadb Mon Sep 17 00:00:00 2001 From: Nikos Epping Date: Mon, 15 Jan 2024 11:28:55 +0100 Subject: [PATCH 03/11] Replace hsql/call --- src/metabase/driver/firebird.clj | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/metabase/driver/firebird.clj b/src/metabase/driver/firebird.clj index af3deb9..7ae3c21 100644 --- a/src/metabase/driver/firebird.clj +++ b/src/metabase/driver/firebird.clj @@ -125,7 +125,7 @@ ;; Convert unix time to a timestamp (defmethod sql.qp/unix-timestamp->honeysql [:firebird :seconds] [_ _ expr] - (hsql/call :DATEADD (hsql/raw "SECOND") expr (hx/cast :TIMESTAMP (hx/literal "01-01-1970 00:00:00")))) + [:DATEADD (hsql/raw "SECOND") expr (hx/cast :TIMESTAMP (hx/literal "01-01-1970 00:00:00"))]) ;; Helpers for Date extraction ;; TODO: This can probably simplified a lot by using String concentation instead of @@ -151,7 +151,7 @@ 5 :YEAR)) ;; Replace the specified part of the timestamp (defn- replace-timestamp-part [input unit expr] - (hsql/call :replace input (hx/literal (get-unit-placeholder unit)) (hsql/call :extract unit expr))) + [:replace input (hx/literal (get-unit-placeholder unit)) [:extract unit expr]]) (defn- format-step [expr input step wanted-unit] (if (> step wanted-unit) @@ -172,26 +172,26 @@ (defmethod sql.qp/date [:firebird :default] [_ _ expr] expr) ;; Cast to TIMESTAMP if we need minutes or hours, since expr might be a DATE (defmethod sql.qp/date [:firebird :minute] [_ _ expr] (timestamp-trunc (hx/cast :TIMESTAMP expr) "YYYY-MM-DD hh:mm:00" 1)) -(defmethod sql.qp/date [:firebird :minute-of-hour] [_ _ expr] (hsql/call :extract :MINUTE (hx/cast :TIMESTAMP expr))) +(defmethod sql.qp/date [:firebird :minute-of-hour] [_ _ expr] [:extract :MINUTE (hx/cast :TIMESTAMP expr)]) (defmethod sql.qp/date [:firebird :hour] [_ _ expr] (timestamp-trunc (hx/cast :TIMESTAMP expr) "YYYY-MM-DD hh:00:00" 2)) -(defmethod sql.qp/date [:firebird :hour-of-day] [_ _ expr] (hsql/call :extract :HOUR (hx/cast :TIMESTAMP expr))) +(defmethod sql.qp/date [:firebird :hour-of-day] [_ _ expr] [:extract :HOUR (hx/cast :TIMESTAMP expr)]) ;; Cast to DATE to get rid of anything smaller than day (defmethod sql.qp/date [:firebird :day] [_ _ expr] (hx/cast :DATE expr)) ;; Firebird DOW is 0 (Sun) - 6 (Sat); increment this to be consistent with Java, H2, MySQL, and Mongo (1-7) -(defmethod sql.qp/date [:firebird :day-of-week] [_ _ expr] (hx/+ (hsql/call :extract :WEEKDAY (hx/cast :DATE expr)) 1)) -(defmethod sql.qp/date [:firebird :day-of-month] [_ _ expr] (hsql/call :extract :DAY expr)) +(defmethod sql.qp/date [:firebird :day-of-week] [_ _ expr] (hx/+ [:extract :WEEKDAY (hx/cast :DATE expr)] 1)) +(defmethod sql.qp/date [:firebird :day-of-month] [_ _ expr] [:extract :DAY expr]) ;; Firebird YEARDAY starts from 0; increment this -(defmethod sql.qp/date [:firebird :day-of-year] [_ _ expr] (hx/+ (hsql/call :extract :YEARDAY expr) 1)) +(defmethod sql.qp/date [:firebird :day-of-year] [_ _ expr] (hx/+ [:extract :YEARDAY expr] 1)) ;; Cast to DATE because we do not want units smaller than days ;; Use hsql/raw for DAY in dateadd because the keyword :WEEK gets surrounded with quotations -(defmethod sql.qp/date [:firebird :week] [_ _ expr] (hsql/call :dateadd (hsql/raw "DAY") (hx/- 0 (hsql/call :extract :WEEKDAY (hx/cast :DATE expr))) (hx/cast :DATE expr))) -(defmethod sql.qp/date [:firebird :week-of-year] [_ _ expr] (hsql/call :extract :WEEK expr)) +(defmethod sql.qp/date [:firebird :week] [_ _ expr] [:dateadd (hsql/raw "DAY") (hx/- 0 [:extract :WEEKDAY (hx/cast :DATE expr)]) (hx/cast :DATE expr)]) +(defmethod sql.qp/date [:firebird :week-of-year] [_ _ expr] [:extract :WEEK expr]) (defmethod sql.qp/date [:firebird :month] [_ _ expr] (date-trunc expr "YYYY-MM-01" 4)) -(defmethod sql.qp/date [:firebird :month-of-year] [_ _ expr] (hsql/call :extract :MONTH expr)) +(defmethod sql.qp/date [:firebird :month-of-year] [_ _ expr] [:extract :MONTH expr]) ;; Use hsql/raw for MONTH in dateadd because the keyword :MONTH gets surrounded with quotations -(defmethod sql.qp/date [:firebird :quarter] [_ _ expr] (hsql/call :dateadd (hsql/raw "MONTH") (hx/* (hx// (hx/- (hsql/call :extract :MONTH expr) 1) 3) 3) (date-trunc expr "YYYY-01-01" 5))) -(defmethod sql.qp/date [:firebird :quarter-of-year] [_ _ expr] (hx/+ (hx// (hx/- (hsql/call :extract :MONTH expr) 1) 3) 1)) -(defmethod sql.qp/date [:firebird :year] [_ _ expr] (hsql/call :extract :YEAR expr)) +(defmethod sql.qp/date [:firebird :quarter] [_ _ expr] [:dateadd (hsql/raw "MONTH") (hx/* (hx// (hx/- [:extract :MONTH expr] 1) 3) 3) (date-trunc expr "YYYY-01-01" 5)]) +(defmethod sql.qp/date [:firebird :quarter-of-year] [_ _ expr] (hx/+ (hx// (hx/- [:extract :MONTH expr] 1) 3) 1)) +(defmethod sql.qp/date [:firebird :year] [_ _ expr] [:extract :YEAR expr]) ;; Firebird 2.x doesn't support TRUE/FALSE, replacing them with 1 and 0 (defmethod sql.qp/->honeysql [:firebird Boolean] [_ bool] (if bool 1 0)) @@ -213,7 +213,7 @@ (defmethod sql.qp/add-interval-honeysql-form :firebird [driver hsql-form amount unit] (if (= unit :quarter) (recur driver hsql-form (hx/* amount 3) :month) - (hsql/call :dateadd (hsql/raw (name unit)) amount hsql-form))) + [:dateadd (hsql/raw (name unit)) amount hsql-form])) (defmethod sql.qp/current-datetime-honeysql-form :firebird [_] (hx/cast :timestamp (hx/literal :now))) @@ -229,7 +229,7 @@ (defmethod sql.qp/->honeysql [:firebird :stddev] [driver [_ field]] - (hsql/call :stddev_samp (sql.qp/->honeysql driver field))) + [:stddev_samp (sql.qp/->honeysql driver field)]) ;; MEGA HACK based on sqlite driver From f4fd8c8eaca47065d5f1c193676db2a677fa40fa Mon Sep 17 00:00:00 2001 From: Nikos Epping Date: Mon, 15 Jan 2024 11:30:50 +0100 Subject: [PATCH 04/11] Replace hsql/raw --- src/metabase/driver/firebird.clj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/metabase/driver/firebird.clj b/src/metabase/driver/firebird.clj index 7ae3c21..c729e5a 100644 --- a/src/metabase/driver/firebird.clj +++ b/src/metabase/driver/firebird.clj @@ -125,7 +125,7 @@ ;; Convert unix time to a timestamp (defmethod sql.qp/unix-timestamp->honeysql [:firebird :seconds] [_ _ expr] - [:DATEADD (hsql/raw "SECOND") expr (hx/cast :TIMESTAMP (hx/literal "01-01-1970 00:00:00"))]) + [:DATEADD [:raw "SECOND"] expr (hx/cast :TIMESTAMP (hx/literal "01-01-1970 00:00:00"))]) ;; Helpers for Date extraction ;; TODO: This can probably simplified a lot by using String concentation instead of @@ -183,13 +183,13 @@ ;; Firebird YEARDAY starts from 0; increment this (defmethod sql.qp/date [:firebird :day-of-year] [_ _ expr] (hx/+ [:extract :YEARDAY expr] 1)) ;; Cast to DATE because we do not want units smaller than days -;; Use hsql/raw for DAY in dateadd because the keyword :WEEK gets surrounded with quotations -(defmethod sql.qp/date [:firebird :week] [_ _ expr] [:dateadd (hsql/raw "DAY") (hx/- 0 [:extract :WEEKDAY (hx/cast :DATE expr)]) (hx/cast :DATE expr)]) +;; Use :raw for DAY in dateadd because the keyword :WEEK gets surrounded with quotations +(defmethod sql.qp/date [:firebird :week] [_ _ expr] [:dateadd [:raw "DAY"] (hx/- 0 [:extract :WEEKDAY (hx/cast :DATE expr)]) (hx/cast :DATE expr)]) (defmethod sql.qp/date [:firebird :week-of-year] [_ _ expr] [:extract :WEEK expr]) (defmethod sql.qp/date [:firebird :month] [_ _ expr] (date-trunc expr "YYYY-MM-01" 4)) (defmethod sql.qp/date [:firebird :month-of-year] [_ _ expr] [:extract :MONTH expr]) -;; Use hsql/raw for MONTH in dateadd because the keyword :MONTH gets surrounded with quotations -(defmethod sql.qp/date [:firebird :quarter] [_ _ expr] [:dateadd (hsql/raw "MONTH") (hx/* (hx// (hx/- [:extract :MONTH expr] 1) 3) 3) (date-trunc expr "YYYY-01-01" 5)]) +;; Use :raw for MONTH in dateadd because the keyword :MONTH gets surrounded with quotations +(defmethod sql.qp/date [:firebird :quarter] [_ _ expr] [:dateadd [:raw "MONTH"] (hx/* (hx// (hx/- [:extract :MONTH expr] 1) 3) 3) (date-trunc expr "YYYY-01-01" 5)]) (defmethod sql.qp/date [:firebird :quarter-of-year] [_ _ expr] (hx/+ (hx// (hx/- [:extract :MONTH expr] 1) 3) 1)) (defmethod sql.qp/date [:firebird :year] [_ _ expr] [:extract :YEAR expr]) @@ -213,7 +213,7 @@ (defmethod sql.qp/add-interval-honeysql-form :firebird [driver hsql-form amount unit] (if (= unit :quarter) (recur driver hsql-form (hx/* amount 3) :month) - [:dateadd (hsql/raw (name unit)) amount hsql-form])) + [:dateadd [:raw (name unit)] amount hsql-form])) (defmethod sql.qp/current-datetime-honeysql-form :firebird [_] (hx/cast :timestamp (hx/literal :now))) From 11a06ae8a4f0ac61cb234dc867e280d3f5c879bb Mon Sep 17 00:00:00 2001 From: Nikos Epping Date: Mon, 15 Jan 2024 11:31:40 +0100 Subject: [PATCH 05/11] Disable Firebird 2 workaround for substring (for now) --- src/metabase/driver/firebird.clj | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/metabase/driver/firebird.clj b/src/metabase/driver/firebird.clj index c729e5a..1b43df9 100644 --- a/src/metabase/driver/firebird.clj +++ b/src/metabase/driver/firebird.clj @@ -4,7 +4,6 @@ [string :as str]] [clojure.java.jdbc :as jdbc] [honey.sql :as hsql] - [honey.sql :as hformat] [java-time :as t] [metabase.driver :as driver] [metabase.driver.common :as driver.common] @@ -196,19 +195,20 @@ ;; Firebird 2.x doesn't support TRUE/FALSE, replacing them with 1 and 0 (defmethod sql.qp/->honeysql [:firebird Boolean] [_ bool] (if bool 1 0)) +;; TODO / Help Wanted: ;; Firebird 2.x doesn't support SUBSTRING arugments seperated by commas, but uses FROM and FOR keywords -(defmethod sql.qp/->honeysql [:firebird :substring] - [driver [_ arg start length]] - (let [col-name (hformat/to-sql (sql.qp/->honeysql driver arg))] - (if length - (reify - hformat/ToSql - (to-sql [_] - (str "substring(" col-name " FROM " start " FOR " length ")"))) - (reify - hformat/ToSql - (to-sql [_] - (str "substring(" col-name " FROM " start ")")))))) +;(defmethod sql.qp/->honeysql [:firebird :substring] +; [driver [_ arg start length]] +; (let [col-name (hformat/to-sql (sql.qp/->honeysql driver arg))] +; (if length +; (reify +; hformat/ToSql +; (to-sql [_] +; (str "substring(" col-name " FROM " start " FOR " length ")"))) +; (reify +; hformat/ToSql +; (to-sql [_] +; (str "substring(" col-name " FROM " start ")")))))) (defmethod sql.qp/add-interval-honeysql-form :firebird [driver hsql-form amount unit] (if (= unit :quarter) From 6cc9cd249c9d0f199433b3ac607a3cc06d6781be Mon Sep 17 00:00:00 2001 From: Nikos Epping Date: Mon, 15 Jan 2024 11:34:17 +0100 Subject: [PATCH 06/11] Replace driver/supports with driver/database-supports --- src/metabase/driver/firebird.clj | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/metabase/driver/firebird.clj b/src/metabase/driver/firebird.clj index 1b43df9..fb360e7 100644 --- a/src/metabase/driver/firebird.clj +++ b/src/metabase/driver/firebird.clj @@ -266,20 +266,20 @@ (sql.qp/->honeysql driver (t/local-date t)) (hx/cast :TIMESTAMP (t/format "yyyy-MM-dd HH:mm:ss.SSSS" t)))) -(defmethod driver/supports? [:firebird :basic-aggregations] [_ _] true) +(defmethod driver/database-supports? [:firebird :basic-aggregations] [_ _] true) -(defmethod driver/supports? [:firebird :expression-aggregations] [_ _] true) +(defmethod driver/database-supports? [:firebird :expression-aggregations] [_ _] true) -(defmethod driver/supports? [:firebird :standard-deviation-aggregations] [_ _] true) +(defmethod driver/database-supports? [:firebird :standard-deviation-aggregations] [_ _] true) -(defmethod driver/supports? [:firebird :foreign-keys] [_ _] true) +(defmethod driver/database-supports? [:firebird :foreign-keys] [_ _] true) -(defmethod driver/supports? [:firebird :nested-fields] [_ _] false) +(defmethod driver/database-supports? [:firebird :nested-fields] [_ _] false) -(defmethod driver/supports? [:firebird :set-timezone] [_ _] false) +(defmethod driver/database-supports? [:firebird :set-timezone] [_ _] false) -(defmethod driver/supports? [:firebird :nested-queries] [_ _] true) +(defmethod driver/database-supports? [:firebird :nested-queries] [_ _] true) -(defmethod driver/supports? [:firebird :binning] [_ _] false) +(defmethod driver/database-supports? [:firebird :binning] [_ _] false) -(defmethod driver/supports? [:firebird :case-sensitivity-string-filter-options] [_ _] false) +(defmethod driver/database-supports? [:firebird :case-sensitivity-string-filter-options] [_ _] false) From 4869dc00cdc4ec68904c6152badaee0149e9e360 Mon Sep 17 00:00:00 2001 From: Nikos Epping Date: Mon, 15 Jan 2024 11:37:18 +0100 Subject: [PATCH 07/11] Specify that :schemas are not supported --- src/metabase/driver/firebird.clj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/metabase/driver/firebird.clj b/src/metabase/driver/firebird.clj index fb360e7..e2990b5 100644 --- a/src/metabase/driver/firebird.clj +++ b/src/metabase/driver/firebird.clj @@ -283,3 +283,5 @@ (defmethod driver/database-supports? [:firebird :binning] [_ _] false) (defmethod driver/database-supports? [:firebird :case-sensitivity-string-filter-options] [_ _] false) + +(defmethod driver/database-supports? [:firebird :schemas] [_ _] false) From 60237de9e3b3d8578f3be3083e85a37b0fe133cb Mon Sep 17 00:00:00 2001 From: Nikos Epping Date: Mon, 15 Jan 2024 11:54:03 +0100 Subject: [PATCH 08/11] Remove deprecated current-db-time methods --- src/metabase/driver/firebird.clj | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/metabase/driver/firebird.clj b/src/metabase/driver/firebird.clj index e2990b5..5f737a6 100644 --- a/src/metabase/driver/firebird.clj +++ b/src/metabase/driver/firebird.clj @@ -218,15 +218,6 @@ (defmethod sql.qp/current-datetime-honeysql-form :firebird [_] (hx/cast :timestamp (hx/literal :now))) -(defmethod driver.common/current-db-time-date-formatters :firebird [_] - (driver.common/create-db-time-formatters "yyyy-MM-dd HH:mm:ss.SSSS")) - -(defmethod driver.common/current-db-time-native-query :firebird [_] - "SELECT CAST(CAST('Now' AS TIMESTAMP) AS VARCHAR(24)) FROM RDB$DATABASE") - -(defmethod driver/current-db-time :firebird [& args] - (apply driver.common/current-db-time args)) - (defmethod sql.qp/->honeysql [:firebird :stddev] [driver [_ field]] [:stddev_samp (sql.qp/->honeysql driver field)]) From 5c4b7849bbc609ddd51985b31ea9e923e96b9cf1 Mon Sep 17 00:00:00 2001 From: Nikos Epping Date: Mon, 15 Jan 2024 11:57:31 +0100 Subject: [PATCH 09/11] Set version=1.6.0 --- resources/metabase-plugin.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/metabase-plugin.yaml b/resources/metabase-plugin.yaml index ea2ad0a..ebb0fff 100644 --- a/resources/metabase-plugin.yaml +++ b/resources/metabase-plugin.yaml @@ -1,6 +1,6 @@ info: name: Metabase FirebirdSQL Driver - version: 1.0.0 + version: 1.6.0 description: Allows Metabase to connect to FirebirdSQL databases. driver: name: firebird From 13b1ec33adf2f6742e45dee50856c5613b878e8d Mon Sep 17 00:00:00 2001 From: Nikos Epping Date: Mon, 15 Jan 2024 13:14:33 +0100 Subject: [PATCH 10/11] Configure *warn-on-reflection* --- src/metabase/driver/firebird.clj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/metabase/driver/firebird.clj b/src/metabase/driver/firebird.clj index 5f737a6..2e4c795 100644 --- a/src/metabase/driver/firebird.clj +++ b/src/metabase/driver/firebird.clj @@ -20,6 +20,7 @@ [java.time LocalDate LocalDateTime LocalTime OffsetDateTime OffsetTime ZonedDateTime] [java.sql Connection DatabaseMetaData ResultSet])) +(set! *warn-on-reflection* true) (driver/register! :firebird, :parent :sql-jdbc) From a240bf29229f2ad7e6467c275680000ba06dd509 Mon Sep 17 00:00:00 2001 From: Nikos Epping Date: Mon, 15 Jan 2024 13:14:52 +0100 Subject: [PATCH 11/11] Refactor supported features --- src/metabase/driver/firebird.clj | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/metabase/driver/firebird.clj b/src/metabase/driver/firebird.clj index 2e4c795..08ae181 100644 --- a/src/metabase/driver/firebird.clj +++ b/src/metabase/driver/firebird.clj @@ -258,22 +258,16 @@ (sql.qp/->honeysql driver (t/local-date t)) (hx/cast :TIMESTAMP (t/format "yyyy-MM-dd HH:mm:ss.SSSS" t)))) -(defmethod driver/database-supports? [:firebird :basic-aggregations] [_ _] true) - -(defmethod driver/database-supports? [:firebird :expression-aggregations] [_ _] true) - -(defmethod driver/database-supports? [:firebird :standard-deviation-aggregations] [_ _] true) - -(defmethod driver/database-supports? [:firebird :foreign-keys] [_ _] true) - -(defmethod driver/database-supports? [:firebird :nested-fields] [_ _] false) - -(defmethod driver/database-supports? [:firebird :set-timezone] [_ _] false) - -(defmethod driver/database-supports? [:firebird :nested-queries] [_ _] true) - -(defmethod driver/database-supports? [:firebird :binning] [_ _] false) - -(defmethod driver/database-supports? [:firebird :case-sensitivity-string-filter-options] [_ _] false) - -(defmethod driver/database-supports? [:firebird :schemas] [_ _] false) +(doseq [[feature supported?] {; supported + :basic-aggregations true + :expression-aggregations true + :foreign-keys true + :nested-queries true + :standard-deviation-aggregations true + ; not supported + :binning false + :case-sensitivity-string-filter-options false + :nested-fields false + :schemas false + :set-timezone false}] + (defmethod driver/database-supports? [:firebird feature] [_driver _feature _db] supported?))