Apache Calcite — различия между версиями

Материал из Национальной библиотеки им. Н. Э. Баумана
Строка 32: Строка 32:
 
Apache Calcite принимает запросы SQL и генерирует расширенную реляционную алгебру, используя настраиваемый оптимизатор на основе затрат. Несколько проектов уже используют Calcite для разбора SQL и оптимизации запросов.
 
Apache Calcite принимает запросы SQL и генерирует расширенную реляционную алгебру, используя настраиваемый оптимизатор на основе затрат. Несколько проектов уже используют Calcite для разбора SQL и оптимизации запросов.
  
Одной из основных сильных сторон Calcite является его высокомодульная структура, которая позволяет использовать несколько точек интеграции и творческого использования. Он предлагает строитель реляционной алгебры, который делает возможным переход на другой синтаксический анализатор SQL (или добавление внешнего интерфейса, отличного от SQL).
+
Одной из сильных сторон Calcite является его высокомодульная структура, которая позволяет использовать несколько точек интеграции и творческого использования. Он предлагает строитель реляционной алгебры, который делает возможным переход на другой синтаксический анализатор SQL (или добавление внешнего интерфейса, отличного от SQL).
  
В нашем продукте нам нужны функции времени выполнения, которые по умолчанию не распознаются Calcite. Например, тригонометрические функции необходимы для гео-проекций на лету, используемых для рендеринга карты точек. К счастью, Calcite позволяет указывать такие функции, и они становятся первоклассными гражданами с соответствующей проверкой типов.
+
В нашем продукте нам нужны функции времени выполнения, которые по умолчанию не распознаются Calcite. Например, тригонометрические функции необходимы для гео-проекций, используемых для отрисовки точечной карты. К счастью, Calcite позволяет указывать такие функции, и они становятся первоклассными функциями с соответствующей проверкой типов.
  
 
Calcite также включает в себя высокопроизводительный и гибкий оптимизатор на основе затрат, который может применять высокоуровневые преобразования к реляционной алгебре на основе шаблонов запросов и статистики. Например, он может протолкнуть часть фильтра через соединение, чтобы уменьшить размер ввода.
 
Calcite также включает в себя высокопроизводительный и гибкий оптимизатор на основе затрат, который может применять высокоуровневые преобразования к реляционной алгебре на основе шаблонов запросов и статистики. Например, он может протолкнуть часть фильтра через соединение, чтобы уменьшить размер ввода.

Версия 13:43, 7 января 2019

Apache Calcite
Apache Calcite Logo
Разработчики: Apache Software Foundation
Выпущена: June 27, 2014
Написана на: Java
Операционная система: Кросс-платформенное
Локализация: Английский
Тип ПО: База данных
Лицензия: Apache License 2.0
Веб-сайт calcite.apache.org

Apache Calcite - это динамическая структура управления данными. Calcite содержит множество элементов, которые составляют типичную систему управления базами данных, но в нем отсутствуют некоторые ключевые функции: хранение данных, алгоритмы обработки данных и хранилище для хранения метаданных. Как платформа, Calcite не хранит свои собственные данные или метаданные, но вместо этого позволяет внешним данным и метаданным получать доступ с помощью плагинов. [Источник 1]

Apache Calcite принимает запросы SQL и генерирует расширенную реляционную алгебру, используя настраиваемый оптимизатор на основе затрат. Несколько проектов уже используют Calcite для разбора SQL и оптимизации запросов.

Одной из сильных сторон Calcite является его высокомодульная структура, которая позволяет использовать несколько точек интеграции и творческого использования. Он предлагает строитель реляционной алгебры, который делает возможным переход на другой синтаксический анализатор SQL (или добавление внешнего интерфейса, отличного от SQL).

В нашем продукте нам нужны функции времени выполнения, которые по умолчанию не распознаются Calcite. Например, тригонометрические функции необходимы для гео-проекций, используемых для отрисовки точечной карты. К счастью, Calcite позволяет указывать такие функции, и они становятся первоклассными функциями с соответствующей проверкой типов.

Calcite также включает в себя высокопроизводительный и гибкий оптимизатор на основе затрат, который может применять высокоуровневые преобразования к реляционной алгебре на основе шаблонов запросов и статистики. Например, он может протолкнуть часть фильтра через соединение, чтобы уменьшить размер ввода. [Источник 2]

Загрузка и установка

Вам нужна Java (1,7 или выше; предпочтительно 1,8), git и maven (3.2.1 или более поздняя версия). [Источник 3]

$ git clone https://github.com/apache/calcite.git
$ cd calcite
$ mvn install -DskipTests -Dcheckstyle.skip=true
$ cd example/csv

Первые запросы

Теперь давайте подключимся к Calcite, используя sqlline, оболочку SQL, которая включена в этот проект. [Источник 4]

$ ./sqlline
sqlline> !connect jdbc:calcite:model=target/test-classes/model.json admin admin

(Если вы используете Windows, команда sqlline.bat)

Выполните запрос метаданных:

sqlline> !tables
+------------+--------------+-------------+---------------+----------+------+
| TABLE_CAT  | TABLE_SCHEM  | TABLE_NAME  |  TABLE_TYPE   | REMARKS  | TYPE |
+------------+--------------+-------------+---------------+----------+------+
| null       | SALES        | DEPTS       | TABLE         | null     | null |
| null       | SALES        | EMPS        | TABLE         | null     | null |
| null       | SALES        | HOBBIES     | TABLE         | null     | null |
| null       | metadata     | COLUMNS     | SYSTEM_TABLE  | null     | null |
| null       | metadata     | TABLES      | SYSTEM_TABLE  | null     | null |
+------------+--------------+-------------+---------------+----------+------+

(Также есть другие команды для запроса метаданных JDBC, такие как !columns и !describe)

Как видите, в системе есть 5 таблиц: таблицы EMPS, DEPTS и HOBBIES в текущей SALES схеме, COLUMNS и TABLES в metadata схеме системы. Системные таблицы всегда присутствуют в Calcite, но другие таблицы предоставляются конкретной реализацией схемы; в этом случае, EMPS и DEPTS таблицы на основе EMPS.csv и DEPTS.csv файлов в target/test-classes каталоге.

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

sqlline> SELECT * FROM emps;
+--------+--------+---------+---------+----------------+--------+-------+---+
| EMPNO  |  NAME  | DEPTNO  | GENDER  |      CITY      | EMPID  |  AGE  | S |
+--------+--------+---------+---------+----------------+--------+-------+---+
| 100    | Fred   | 10      |         |                | 30     | 25    | t |
| 110    | Eric   | 20      | M       | San Francisco  | 3      | 80    | n |
| 110    | John   | 40      | M       | Vancouver      | 2      | null  | f |
| 120    | Wilma  | 20      | F       |                | 1      | 5     | n |
| 130    | Alice  | 40      | F       | Vancouver      | 2      | null  | f |
+--------+--------+---------+---------+----------------+--------+-------+---+

Теперь JOIN и GROUP BY:

sqlline> SELECT d.name, COUNT(*)
. . . .> FROM emps AS e JOIN depts AS d ON e.deptno = d.deptno
. . . .> GROUP BY d.name;
+------------+---------+
|    NAME    | EXPR$1  |
+------------+---------+
| Sales      | 1       |
| Marketing  | 2       |
+------------+---------+

Наконец, оператор VALUES генерирует одну строку и представляет собой удобный способ проверки выражений и встроенных функций SQL:

sqlline> VALUES CHAR_LENGTH('Hello, ' || 'world!');
+---------+
| EXPR$0  |
+---------+
| 13      |
+---------+

Схема открытия

Как Calcite нашел эти таблицы? Помните, ядро ​​Calcite ничего не знает о файлах CSV. (Будучи «базой данных без слоя хранения», Calcite не знает ни о каких форматах файлов.) Calcite знает об этих таблицах, потому что мы сказали ему запускать код в проекте calcite-example-csv.

В этой цепочке есть пара шагов. Сначала мы определяем схему на основе класса фабрики схем в файле модели. Затем фабрика схем создает схему, а схема создает несколько таблиц, каждая из которых знает, как получить данные путем сканирования файла CSV. Наконец, после того, как Calcite проанализировал запрос и запланировал использование этих таблиц, Calcite вызывает таблицы для чтения данных во время выполнения запроса. Теперь давайте посмотрим на эти шаги более подробно.

В строке подключения JDBC мы указали путь к модели в формате JSON. Вот модель:

{
  version: '1.0',
  defaultSchema: 'SALES',
  schemas: [
    {
      name:  'SALES',
      type:  'custom',
      factory:  'org.apache.calcite.adapter.csv.CsvSchemaFactory',
      operand: {
        directory:  'target/test-classes/sales'
      }
    }
  ]
}

Модель определяет единственную схему под названием «SALES». Схема основана на подключаемом классе org.apache.calcite.adapter.csv.CsvSchemaFactory, который является частью проекта calcite-example-csv и реализует интерфейс Calcite SchemaFactory. Его create метод создает схему, передавая directory аргумент из файла модели:

public Schema create(SchemaPlus parentSchema, String name,
    Map<String, Object> operand) {
  String directory = (String) operand.get("directory");
  String flavorName = (String) operand.get("flavor");
  CsvTable.Flavor flavor;
  if (flavorName == null) {
    flavor = CsvTable.Flavor.SCANNABLE;
  } else {
    flavor = CsvTable.Flavor.valueOf(flavorName.toUpperCase());
  }
  return new CsvSchema(
      new File(directory),
      flavor);
}

Управляемая моделью, фабрика схем создает единственную схему под названием «SALES». Схема является экземпляром org.apache.calcite.adapter.csv.CsvSchema и реализует схему интерфейса Calcite .

CsvSchema создает таблицы, которые являются экземплярами CsvTable и его подклассов.

Вот соответствующий код CsvSchema, переопределяющий getTableMap() метод в AbstractSchema базовом классе.

protected Map<String, Table> getTableMap() {
  // Look for files in the directory ending in ".csv", ".csv.gz", ".json",
  // ".json.gz".
  File[] files = directoryFile.listFiles(
      new FilenameFilter() {
        public boolean accept(File dir, String name) {
          final String nameSansGz = trim(name, ".gz");
          return nameSansGz.endsWith(".csv")
              || nameSansGz.endsWith(".json");
        }
      });
  if (files == null) {
    System.out.println("directory " + directoryFile + " not found");
    files = new File[0];
  }
  // Build a map from table name to table; each file becomes a table.
  final ImmutableMap.Builder<String, Table> builder = ImmutableMap.builder();
  for (File file : files) {
    String tableName = trim(file.getName(), ".gz");
    final String tableNameSansJson = trimOrNull(tableName, ".json");
    if (tableNameSansJson != null) {
      JsonTable table = new JsonTable(file);
      builder.put(tableNameSansJson, table);
      continue;
    }
    tableName = trim(tableName, ".csv");
    final Table table = createTable(file);
    builder.put(tableName, table);
  }
  return builder.build();
}

/** Creates different sub-type of table based on the "flavor" attribute. */
private Table createTable(File file) {
  switch (flavor) {
  case TRANSLATABLE:
    return new CsvTranslatableTable(file, null);
  case SCANNABLE:
    return new CsvScannableTable(file, null);
  case FILTERABLE:
    return new CsvFilterableTable(file, null);
  default:
    throw new AssertionError("Unknown flavor " + flavor);
  }
}

Схема сканирует каталог и находит все файлы, имя которых заканчивается на «.csv», и создает для них таблицы. В этом случае, каталог target/test-classes/sales и содержит файлы EMPS.csv и DEPTS.csv, которые стали таблицами EMPS и DEPTS.

Таблицы в схемах

Обратите внимание, что нам не нужно было определять какие-либо таблицы в модели; схема генерирует таблицы автоматически.

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

Давайте посмотрим, как создать важный и полезный тип таблицы, а именно представление.

Когда вы пишете запрос, представление выглядит как таблица, но оно не хранит данные. Он получает свой результат, выполняя запрос. Представление расширяется во время планирования запроса, поэтому планировщик запросов может часто выполнять оптимизацию, например удаление выражений из предложения SELECT, которые не используются в конечном результате.

Вот схема, которая определяет представление таблицы:

{
  version:  '1.0',
  defaultSchema: 'SALES',
  schemas: [
    {
      name: 'SALES',
      type:  'custom',
      factory:  'org.apache.calcite.adapter.csv.CsvSchemaFactory',
      operand: {
        directory:  'target/test-classes/sales'
      },
      tables: [
        {
          name:  'FEMALE_EMPS',
          type:  'view',
          sql:  'SELECT * FROM emps WHERE gender = \'F\''
        }
      ]
    }
  ]
}

Строка type: 'view' помечается FEMALE_EMPS как представление, в отличие от обычной таблицы или пользовательской таблицы. Обратите внимание, что одинарные кавычки в определении представления пишутся с помощью обратной косой черты, как обычно для JSON.

JSON не позволяет легко создавать длинные строки, поэтому Calcite поддерживает альтернативный синтаксис. Если ваше представление имеет длинную инструкцию SQL, вы можете вместо этого предоставить список строк, а не одну строку:

{
  name:  'FEMALE_EMPS',
  type:  'view',
  sql: [
     'SELECT * FROM emps',
     'WHERE gender = \'F\''
  ]
}

Теперь мы определили представление, мы можем использовать его в запросах так же, как если бы оно было таблицей:

sqlline> SELECT e.name, d.name FROM female_emps AS e JOIN depts AS d on e.deptno = d.deptno;
+--------+------------+
|  NAME  |    NAME    |
+--------+------------+
| Wilma  | Marketing  |
+--------+------------+

Пользовательские таблицы

Пользовательские таблицы - это таблицы, реализация которых определяется пользовательским кодом. Им не нужно жить по индивидуальной схеме.

Есть пример в model-with-custom-table.json:

{
  version: '1.0',
  defaultSchema: 'CUSTOM_TABLE',
  schemas: [
    {
      name: 'CUSTOM_TABLE',
      tables: [
        {
          name: 'EMPS',
          type: 'custom',
          factory: 'org.apache.calcite.adapter.csv.CsvTableFactory',
          operand: {
            file: 'target/test-classes/sales/EMPS.csv.gz',
            flavor: "scannable"
          }
        }
      ]
    }
  ]
}

Мы можем запросить таблицу обычным способом:

sqlline> !connect jdbc:calcite:model=target/test-classes/model-with-custom-table.json admin admin
sqlline> SELECT empno, name FROM custom_table.emps;
+--------+--------+
| EMPNO  |  NAME  |
+--------+--------+
| 100    | Fred   |
| 110    | Eric   |
| 110    | John   |
| 120    | Wilma  |
| 130    | Alice  |
+--------+--------+

Схема является обычной и содержит пользовательскую таблицу, основанную на org.apache.calcite.adapter.csv.CsvTableFactory, которая реализует интерфейс Calcite TableFactory. Его create метод создает экземпляр CsvScannableTable, передавая file аргумент из файла модели:

public CsvTable create(SchemaPlus schema, String name,
    Map<String, Object> map, RelDataType rowType) {
  String fileName = (String) map.get("file");
  final File file = new File(fileName);
  final RelProtoDataType protoRowType =
      rowType != null ? RelDataTypeImpl.proto(rowType) : null;
  return new CsvScannableTable(file, protoRowType);
}

Реализация пользовательской таблицы часто является более простой альтернативой реализации пользовательской схемы. Оба подхода могут в итоге создать похожую реализацию Table интерфейса, но для пользовательской таблицы вам не нужно реализовывать обнаружение метаданных. (CsvTableFactory создает CsvScannableTable, так же, как и CsvSchema, но реализация таблицы не сканирует файловую систему для файлов .csv.)

Пользовательские таблицы требуют больше работы для автора модели (автор должен явно указать каждую таблицу и ее файл), но также дают автору больше контроля (скажем, предоставляя различные параметры для каждой таблицы).

Комментарии в моделях

Модели могут включать в себя комментарии , используя /* ... */ и // синтаксис:

{
  version: '1.0',
  /* Multi-line
     comment. */
  defaultSchema: 'CUSTOM_TABLE',
  // Single-line comment.
  schemas: [
    ..
  ]
}

(Комментарии не являются стандартным JSON, но являются безвредным расширением.)

Оптимизация запросов

Реализации таблиц, которые мы видели до сих пор, хороши, если таблицы не содержат большого количества данных. Но если ваша таблица клиентов имеет, скажем, сто столбцов и миллион строк, вы бы предпочли, чтобы система не получала все данные для каждого запроса. Вы хотели бы, чтобы Calcite договорился с адаптером и нашел более эффективный способ доступа к данным.

Это согласование является простой формой оптимизации запросов. Calcite поддерживает оптимизацию запросов, добавляя правила планировщика . Правила планировщика работают путем поиска шаблонов в дереве разбора запросов (например, проекта поверх таблицы определенного типа) и замены соответствующих узлов в дереве новым набором узлов, которые реализуют оптимизацию.

Правила планировщика также расширяемы, как схемы и таблицы. Итак, если у вас есть хранилище данных, к которому вы хотите получить доступ через SQL, вы сначала определяете пользовательскую таблицу или схему, а затем определяете некоторые правила для обеспечения эффективного доступа.

Чтобы увидеть это в действии, давайте использовать правило планировщика для доступа к подмножеству столбцов из файла CSV. Давайте запустим один и тот же запрос для двух очень похожих схем:

sqlline> !connect jdbc:calcite:model=target/test-classes/model.json admin admin
sqlline> explain plan for select name from emps;
+-----------------------------------------------------+
| PLAN                                                |
+-----------------------------------------------------+
| EnumerableCalcRel(expr#0..9=[{inputs}], NAME=[$t1]) |
|   EnumerableTableScan(table=[[SALES, EMPS]])        |
+-----------------------------------------------------+
sqlline> !connect jdbc:calcite:model=target/test-classes/smart.json admin admin
sqlline> explain plan for select name from emps;
+-----------------------------------------------------+
| PLAN                                                |
+-----------------------------------------------------+
| EnumerableCalcRel(expr#0..9=[{inputs}], NAME=[$t1]) |
|   CsvTableScan(table=[[SALES, EMPS]])               |
+-----------------------------------------------------+

Что вызывает разницу в плане? В smart.json файле модели есть только одна дополнительная строка:

flavor: "translatable"

Это приводит CsvSchema к тому, что создается с помощью flavor = TRANSLATABLE, а его createTable метод создает экземпляры CsvTranslatableTable, а не a CsvScannableTable.

CsvTranslatableTable реализует TranslatableTable.toRel() метод для создания CsvTableScan. Сканирование таблицы - это листья дерева операторов запросов. Обычная реализация - это EnumerableTableScan, но мы создали особый подтип, который вызовет срабатывание правил.

Вот правило в целом:

public class CsvProjectTableScanRule extends RelOptRule {
  public static final CsvProjectTableScanRule INSTANCE =
      new CsvProjectTableScanRule();

  private CsvProjectTableScanRule() {
    super(
        operand(Project.class,
            operand(CsvTableScan.class, none())),
        "CsvProjectTableScanRule");
  }

  @Override
  public void onMatch(RelOptRuleCall call) {
    final Project project = call.rel(0);
    final CsvTableScan scan = call.rel(1);
    int[] fields = getProjectFields(project.getProjects());
    if (fields == null) {
      // Project contains expressions more complex than just field references.
      return;
    }
    call.transformTo(
        new CsvTableScan(
            scan.getCluster(),
            scan.getTable(),
            scan.csvTable,
            fields));
  }

  private int[] getProjectFields(List<RexNode> exps) {
    final int[] fields = new int[exps.size()];
    for (int i = 0; i < exps.size(); i++) {
      final RexNode exp = exps.get(i);
      if (exp instanceof RexInputRef) {
        fields[i] = ((RexInputRef) exp).getIndex();
      } else {
        return null; // not a simple projection
      }
    }
    return fields;
  }
}

Конструктор объявляет шаблон реляционных выражений, которые приведут к срабатыванию правила.

onMatch метод генерирует новое относительное выражение и призывает RelOptRuleCall.transformTo(), чтобы указать, что правило закончилось успешно.

Процесс оптимизации запросов

Во-первых, Calcite не выполняет правила в установленном порядке. Процесс оптимизации запросов следует за многими ветвями ветвящегося дерева, так же как программа игры в шахматы исследует много возможных последовательностей ходов. Если правила A и B совпадают с заданным разделом дерева операторов запросов, то Calcite может запустить оба.

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

Многие оптимизаторы имеют линейную схему оптимизации. Столкнувшись с выбором между правилом A и правилом B, как указано выше, такой оптимизатор должен выбрать немедленно. У него может быть такая политика, как «применить правило A ко всему дереву, затем применить правило B ко всему дереву» или применить политику на основе затрат, применяя правило, дающее более дешевый результат.

Calcite не требует таких компромиссов. Это позволяет легко комбинировать различные наборы правил. Если, скажем, вы хотите объединить правила для распознавания материализованных представлений с правилами для чтения из исходных систем CSV и JDBC, вы просто дадите Calcite набор всех правил и попросите его придерживаться его.

Calcite действительно использует модель стоимости. Модель затрат решает, какой план в конечном итоге использовать, а иногда и обрезать дерево поиска, чтобы не допустить взрыва пространства поиска, но никогда не заставляет вас выбирать между правилом A и правилом B.

Также модель затрат является подключаемой, как и статистика таблиц и операторов запросов, на которой она основана.

Адаптер JDBC

Адаптер JDBC отображает схему в источнике данных JDBC как схему Calcite.

Например, эта схема читает из базы данных MySQL «foodmart»:

{
  version: '1.0',
  defaultSchema: 'FOODMART',
  schemas: [
    {
      name: 'FOODMART',
      type: 'custom',
      factory: 'org.apache.calcite.adapter.jdbc.JdbcSchema$Factory',
      operand: {
        jdbcDriver: 'com.mysql.jdbc.Driver',
        jdbcUrl: 'jdbc:mysql://localhost/foodmart',
        jdbcUser: 'foodmart',
        jdbcPassword: 'foodmart'
      }
    }
  ]
}

(База данных FoodMart знакома тем из вас, кто использовал движок Mondrian OLAP, поскольку это основной набор данных испытаний Mondrian. Чтобы загрузить набор данных, следуйте инструкциям по установке Mondrian)

Текущие ограничения: адаптер JDBC в настоящее время выполняет только операции сканирования таблицы; вся остальная обработка (фильтрация, объединения и т. д.) происходит в Calcite. Наша цель - передать как можно больше обработки исходной системе, переводя синтаксис, типы данных и встроенные функции. Если запрос Calcite основан на таблицах из одной базы данных JDBC, в принципе весь запрос должен идти в эту базу данных. Если таблицы взяты из нескольких источников JDBC или представляют собой смесь JDBC и не-JDBC, Calcite будет использовать наиболее эффективный подход распределенного запроса, какой только может.

Клонирующий адаптер JDBC

Клонированный адаптер JDBC создает гибридную базу данных. Данные поступают из базы данных JDBC, но считываются в таблицы в памяти при первом обращении к каждой таблице. Calcite оценивает запросы на основе памяти, фактически кеша базы данных.

Например, следующая модель читает таблицы из базы данных MySQL «foodmart»:

{
  version: '1.0',
  defaultSchema: 'FOODMART_CLONE',
  schemas: [
    {
      name: 'FOODMART_CLONE',
      type: 'custom',
      factory: 'org.apache.calcite.adapter.clone.CloneSchema$Factory',
      operand: {
        jdbcDriver: 'com.mysql.jdbc.Driver',
        jdbcUrl: 'jdbc:mysql://localhost/foodmart',
        jdbcUser: 'foodmart',
        jdbcPassword: 'foodmart'
      }
    }
  ]
}

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

{
  version: '1.0',
  defaultSchema: 'FOODMART_CLONE',
  schemas: [
    {
      name: 'FOODMART',
      type: 'custom',
      factory: 'org.apache.calcite.adapter.jdbc.JdbcSchema$Factory',
      operand: {
        jdbcDriver: 'com.mysql.jdbc.Driver',
        jdbcUrl: 'jdbc:mysql://localhost/foodmart',
        jdbcUser: 'foodmart',
        jdbcPassword: 'foodmart'
      }
    },
    {
      name: 'FOODMART_CLONE',
      type: 'custom',
      factory: 'org.apache.calcite.adapter.clone.CloneSchema$Factory',
      operand: {
        source: 'FOODMART'
      }
    }
  ]
}

Вы можете использовать этот подход для создания схемы клонирования в любой схеме, а не только в JDBC.

Источники

  1. Apache Calcite overview [Электронный ресурс] // en.wikipedia.org URL: https://en.wikipedia.org/wiki/Apache_Calcite (дата обращения: 12.12.2018).
  2. Fast Query Analysis with Apache Calcite [Электронный ресурс] // omnisci.com URL: https://www.omnisci.com/blog/fast-and-flexible-query-analysis-at-mapd-with-apache-calcite-2/ (дата обращения: 12.12.2018).
  3. Apache Calcite download [Электронный ресурс] // github.com URL: https://github.com/apache/calcite (дата обращения: 12.12.2018).
  4. Apache Calcite queries [Электронный ресурс] // calcite.apache.org URL: https://calcite.apache.org/ (дата обращения: 12.12.2018).