Реализация мультиметодов для вашего драйвера

Реализация мультиметодов позволяет вам воспользоваться преимуществами существующего кода драйвера Glarus BI, расширив эти методы для работы с вашей конкретной базой данных.

Давайте сначала сосредоточимся на основном файле драйвера для нашего Fox Pro „98 src/metabase/driver/foxpro98.clj. Взгляните на этот пример кода:

;; Define a namespace for the driver
(ns com.mycompany.metabase.driver.foxpro98
  (:require [metabase.driver :as driver]))

;; Can you include a different method here as an example?
(defmethod driver/display-name :foxpro98 [_]
  "Visual FoxPro '98")

Давайте пройдёмся по каждому блоку кода.

Пространства имён драйверов

;; Define a namespace for the driver
(ns com.mycompany.metabase.driver.foxpro98
  (:require [metabase.driver :as driver]))

Каждый драйвер Glarus BI находится в своём собственном пространстве имён

В данном случае это пространство имён com.mycompany.metabase.driver.foxpro98. Все основные драйверы Glarus BI находятся в пространствах имён metabase.driver.<name-goes-here>. Вероятно, лучше использовать имена, соответствующие соглашениям об именовании пакетов Java.

Многие драйверы разбиты на дополнительные пространства имён

Особенно крупные драйверы. Обычно драйвер имеет пространство имён query-processor (например, com.mycompany.metabase.driver.foxpro98.query-processor), которое содержит логику для преобразования запросов MBQL (запросов, созданных с помощью графического конструктора запросов Glarus BI) в собственные запросы (например, SQL). Процессор запросов часто является самой сложной частью драйвера, поэтому разделение этой логики может упростить работу с ним. Некоторые драйверы также имеют отдельное пространство имён sync, в котором есть реализации методов, используемых Glarus BI для синхронизации базы данных.

Инициализация драйвера

Все драйверы могут включать дополнительный код, который будет выполняться один раз (и только один раз) с использованием metabase.driver/initialize!, когда Glarus BI инициализирует драйвер, то есть до того, как драйвер впервые установит соединение с базой данных. (На самом деле Glarus BI использует metabase.driver/initialize! для ленивой загрузки драйвера.) Есть лишь несколько случаев, когда вы должны использовать metabase.driver/initialize, например, для выделения ресурсов или установки определённых системных свойств.

Мультиметоды metabase.driver

Пространство имён metabase.driver определяет ряд [мультиметодов](https://clojure.org/ reference/multimethods), а драйверы предоставляют для них реализации, как в нашем примере:

(defmethod driver/display-name :foxpro98 [_]
  "Visual FoxPro '98")

Все четыре основные функции драйвера Glarus BI, описанные выше, реализуются с помощью мультиметодов. Эти методы отправляют ключевое слово драйвера, в нашем случае :foxpro98. На самом деле, это то, чем является драйвер Glarus BI — ключевое слово! Здесь нет классов или объектов — только одно ключевое слово.

Вы можете просмотреть пространство имён metabase.driver для получения полного списка мультиметодов, которые вы могли бы реализовать. Прочтите строку документации для каждого метода и решите, нужно ли вам его реализовать. Большинство методов являются необязательными.

Список доступных мультиметодов драйвера

Чтобы быстро просмотреть список всех мультиметодов драйвера, вы можете запустить команду

clojure -M:run driver-methods

которая отобразит список всех пространств имён драйверов и мультиметодов (sql, sql-jdbc, тестовых расширений и других).

Если вы также хотите увидеть строки документации для методов, запустите:

clojure -M:run driver-methods docs

Родительские драйверы

Многие драйверы совместно используют детали реализации, и написание полных реализаций для методов синхронизации и т.п. потребовало бы большого количества повторяющегося кода. Таким образом, многие функции высокого уровня частично или полностью реализованы в общих «родительских» драйверах, таких как наиболее распространённый родитель :sql-jdbc. «Родительский» драйвер аналогичен суперклассу в объектно-ориентированном программировании.

Вы можете определить родителя драйвера, указав его в манифесте плагина.

Родители, такие как :sql-jdbc, задуманы как общий абстрактный «базовый класс» для драйверов, которые могут использовать большую часть своей реализации; в случае :sql-jdbc он предназначен для драйверов на основе SQL, которые под капотом используют драйвер JDBC. :sql-jdbc и другие родительские компоненты предоставляют реализации для многих методов, необходимых для реализации четырёх основных функций. драйвера Glarus BI. На самом деле, :sql-jdbc предоставляет реализации таких вещей, как driver/execute-prepared-statement!, поэтому драйверу, использующему его в качестве родителя, не нужно предоставлять его самому. Однако различные родительские драйверы определяют свои собственные мультиметоды для реализации.

Известные родительские драйверы

  • :sql-jdbc можно использовать в качестве родителя для баз данных на основе SQL с драйвером JDBC.

    • :sql-jdbc реализует большинство из четырёх основных функций, но вместо этого вы должны реализовать мультиметоды sql-jdbc, найденные в пространствах имён metabase.driver.sql-jdbc.*, а также некоторые методы metabase. driver.sql.* пространства имён.

  • :sql сам является родителем :sql-jdbc; его можно использовать для баз данных на основе SQL, которые не имеют драйвер JDBC, например BigQuery.

    • :sql реализует значительную часть функциональности драйвера, но вы должны реализовать некоторые методы из пространств имён metabase.driver.sql.*, чтобы использовать его.

  • Драйверы, использующие API Google, такие как BigQuery и Google Analytics, могут использовать драйвер :google в качестве родителя.

  • Некоторые драйверы используют другие «конкретные» драйверы в качестве своих родителей. Например, :redshift использует :postgres в качестве родителя, предоставляя реализации методов только для переопределения методов postgres, где это необходимо.

Вызов реализации родительского драйвера

Вы можете получить реализацию родительского драйвера для метода, используя get-method:

(defmethod driver/mbql->native :bigquery [driver query]
  ((get-method driver/mbql-native :sql) driver query))

Это эквивалент вызова super.someMethod() в объектно-ориентированном программировании.

Вы должны передать аргумент драйвера родительской реализации как есть, чтобы все методы, вызываемые этим методом, использовали правильную реализацию. Вот два способа вызова родительских методов, которых вам следует избегать:

(defmethod driver/mbql->native :bigquery [_ query]
  ;; BAD! If :sql's implementation of mbql->native calls any other methods, it won't use the :bigquery implementation
  ((get-method driver/mbql->native :sql) :sql query))

также избегать:

(defmethod driver/mbql->native :bigquery [_ query]
  ;; BAD! If someone else creates a driver using :bigquery as a parent, any methods called by :sql's implementation
  ;; of mbql->native will use :bigquery method implementations instead of custom ones for that driver
  ((get-method driver/mbql->native :sql) :bigquery query))

Несколько родителей

Внимательные читатели возможно заметили, что BigQuery упоминается как имеющий как :sql, так и :google в качестве родителя. Это множественное наследование разрешено и даже крайне полезно! Вы можете определить драйвер с несколькими родителями следующим образом:

(driver/register! :bigquery, :parent #{:sql :google})

В некоторых случаях оба родителя могут предоставить реализацию метода; чтобы исправить эту двусмысленность, просто предоставьте реализацию для своего драйвера и передайте её предпочтительной реализации родительского драйвера, как описано выше.

Для драйверов, поставляемых в виде подключаемого модуля, вы должны зарегистрировать методы в манифесте подключаемого модуля.

Работа с драйвером из REPL и в CIDER

Необходимость устанавливать metabase-core локально и создавать драйверы uberjar была бы неприятной, особенно если вам приходилось повторять это, чтобы проверить каждое изменение. К счастью, вы можете запускать команды, как если бы всё было частью одного гигантского проекта:

Чтобы начать работу с драйвером в REPL, выполните:

clojure -A:dev:drivers:drivers-dev

Вам нужно будет пересобрать драйвер и установить его в каталог ./plugins, а также перезапустить Glarus BI после внесения изменений.