Apache Calcite教程-Schema,Table.自定义Schema,自定义Table

本文深入解析了Apache Calcite中的Schema概念及其核心类,如Schema、Table、SchemaFactory和TableFactory的使用。介绍了如何自定义Schema和Table,并提供了具体的实现代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Github

Apache Calcite - schema

schema其实就是表的结构(行列信息),
schema相关类主要存放在org.apache.calcite.schema包下,
此包中的接口定义SQL验证器使用的对象,以验证SQL抽象语法树并将标识符解析为对象。

schema - 常用类

本文只重点介绍Schema,Table,ScheamFactory,TableFactory的使用,其他类的使用会在日后文章中介绍

Schema

Schema为表和函数的命名空间,一个schema可以包括多个子schema

常用方法

  • Table getTable(String name); 根据表名称,获取表信息
  • Set getTableNames(); 获取全部表名称
  • Collection getFunctions(String name); 获取function信息
  • Set getFunctionNames(); 获取所有function名称
  • Schema getSubSchema(String name); 获取子schema
  • Set getSubSchemaNames(); 获取所有子schema的名称
AbstractSchema

Schema的默认实现类,实现了部分方法
用户自定义的schema一般基于这个类实现

SchemaPlus

Schema的包装类,用户使用的时候不需要实现这个类的接口

SchemaFactory

Schema的工厂类,模式工厂允许您在模型文件中包含自定义模式。
例如从一个json文件中共创建schema
用户一般基于这个类创建为自定义工厂

Table

表信息

  • RelDataType getRowType(RelDataTypeFactory typeFactory); 获取表的行类型
  • Statistic getStatistic(); 获取表的信息
  • Schema.TableType getJdbcTableType(); 获取jdbc的tableType
  • boolean isRolledUp(String column); 给定列是否可以累加
  • boolean rolledUpColumnValidInsideAgg(String column, SqlCall call, SqlNode parent, CalciteConnectionConfig config);
    给定列是否可以使用聚合函数
AbstractTable

Table接口的默认抽象类
用户一般看情况,是否基于这个类来实现,或者基于这个类的子类实现

ScannableTable

可以在不创建中间关系表达式的情况下扫描的表。

QueryableTable

可以将Table转换为Queryable

TranslatableTable

Table翻译成RelNode

FilterableTable

将过滤器传递进入表中,从而可以减少部分计算逻辑

ProjectableFilterableTable

将过滤器和投影传递进入表中,从而可以减少部分计算路基

StreamableTable

将表转换成一个流

AbstractQueryableTable

AbstractTableQueryableTable的抽象类

TableFactory

Table工厂类,模式工厂允许您在模型文件中包含自定义模式。
例如从一个json文件中共创建table
用户一般基于这个类创建为自定义工厂

Statistic

表的统计系统

Function

函数

ScalarFunction

带返回值的函数

TableFunction

执行期间的函数信息

FunctionParameter

函数参数

Apache Calcite - model

org.apache.calcite.model
提供JSON格式的模型文件或者YAML格式的模型的解析
用户可以通过实现SchemaFactoryTableFactory定义自己的模式

JsonXXX

配置信息的解析对应的实体类

ModelHandler

主要解析json和yaml文件
calcite主要通过driver建立连接时解析对应的schema和table.

Driver类调用org.apache.calcite.jdbc.Driver.createHandler,创建ModelHandler解析schema和table,之后将schema和table设置回connect

具体实现代码

列信息

/**
 * 列信息
 * @author quxiucheng
 * @date 2019-04-26 11:18:00
 */
@Data
public class TutorialColumn implements Serializable {
    /**
     * 字段名称
     */
    private String name;

    /**
     * 字段类型名称
     */
    private String type;

}

自定义table


import lombok.Data;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.schema.impl.AbstractTable;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.Pair;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * 自定义table
 * @author quxiucheng
 * @date 2019-04-26 11:17:00
 */
@Data
public class TutorialTable extends AbstractTable implements Serializable {
    /**
     * 表名称
     */
    private String name;

    /**
     * 列信息
     */
    private List<TutorialColumn> columnList;


    /**
     * 获取行类型
     * @param typeFactory
     * @return
     */
    @Override
    public RelDataType getRowType(RelDataTypeFactory typeFactory) {
        List<String> names = new ArrayList<>();
        List<RelDataType> types = new ArrayList<>();
        for (TutorialColumn sqlExecuteColumn : columnList) {
            names.add(sqlExecuteColumn.getName());
            RelDataType sqlType = typeFactory.createSqlType(SqlTypeName.get(sqlExecuteColumn.getType().toUpperCase()));
            types.add(sqlType);
        }
        return typeFactory.createStructType(Pair.zip(names, types));
    }
}

自定义schema


import com.google.common.collect.Maps;
import lombok.Data;
import lombok.NonNull;
import org.apache.calcite.schema.Table;
import org.apache.calcite.schema.impl.AbstractSchema;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * 自定义schema
 * @author quxiucheng
 * @date 2019-04-26 11:16:00
 */
@Data
public class TutorialTableSchema extends AbstractSchema implements Serializable {

    /**
     * schema名称
     */
    private String name;

    /**
     * table信息
     */
    private List<TutorialTable> tableList;

    public TutorialTableSchema(@NonNull String name, @NonNull List<TutorialTable> tableList) {
        this.name = name;
        this.tableList = tableList;
    }


    /**
     * 获取该schema中所有的表信息
     *
     * @return
     */
    @Override
    protected Map<String, Table> getTableMap() {
        Map<String, Table> tableMap = Maps.newHashMap();
        for (TutorialTable table : this.tableList) {
            tableMap.put(table.getName(), table);
        }
        return tableMap;
    }

}

table工厂


import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.schema.Table;
import org.apache.calcite.schema.TableFactory;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * table工厂
 * @author quxiucheng
 * @date 2019-04-26 11:20:00
 */
public class TutorialTableFactory implements TableFactory {

    /**
     * yaml解析器
     */
    private static final ObjectMapper YAML_MAPPER = new YAMLMapper();

    /**
     * 创建table
     * @param schema
     * @param name
     * @param operand
     * @param rowType
     * @return
     */
    @Override
    public Table create(SchemaPlus schema, String name, Map operand, RelDataType rowType) {
        try {
            String ddl = (String) operand.get("ddl");
            return YAML_MAPPER.readValue(new File(ddl), TutorialTable.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

schema工厂


import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaFactory;
import org.apache.calcite.schema.SchemaPlus;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * schema工厂类
 * @author quxiucheng
 * @date 2019-04-26 11:19:00
 */
public class TutorialSchemaFactory implements SchemaFactory {

    private static final ObjectMapper JSON_MAPPER = new ObjectMapper()
            .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
            .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
            .configure(JsonParser.Feature.ALLOW_COMMENTS, true);

    /**
     * 创建schema
     * @param parentSchema
     * @param name
     * @param operand
     * @return
     */
    @Override
    public Schema create(SchemaPlus parentSchema, String name, Map<String, Object> operand) {
        try {
            List<TutorialTable> tableList = new ArrayList<>();

            ArrayList tables = (ArrayList) operand.get("tables");
            for (Object table : tables) {
                String ddl = (String) ((HashMap) table).get("ddl");
                TutorialTable tutorialTable = JSON_MAPPER.readValue(new File(ddl), TutorialTable.class);
                tableList.add(tutorialTable);

            }
            return new TutorialTableSchema(name, tableList);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}

model文件

{
  "version": "1.0",
  "defaultSchema": "tutorial",
  "schemas": [ {
    "name": "tutorial",
    "type": "custom",
    "factory": "com.github.quxiucheng.calcite.schema.tutorial.TutorialSchemaFactory",
    "operand": {
      "tables": [
        {
        "ddl": "calcite-tutorial-3-schema/src/main/resources/user/user.json",
        "data": "calcite-tutorial-3-schema/src/main/resources/user/user.txt"
      },{
        "ddl": "calcite-tutorial-3-schema/src/main/resources/role/role.json",
        "data": "calcite-tutorial-3-schema/src/main/resources/role/role.txt"
      }
      ]
    }
  } ]
}

后记

本文只介绍schema相关的信息,不介绍如何进行查询等,等信息

  1. type等类在日后type中介绍.
  2. relXXX等相关实体类在日后转换关系表达式介绍.
  3. Enumerable,Queryable等实体信息在日后查询时介绍

代码实例

https://github.com/quxiucheng/apache-calcite-tutorial/tree/master/calcite-tutorial-3-schema

Exception in thread "main" org.apache.flink.table.api.ValidationException: Unable to create a source for reading table 'default_catalog.default_database.parquet_table'. Table options are: 'connector'='filesystem' 'format'='parquet' 'path'='C:\\Users\\zhaoxiaohao\\Desktop\\part-00000-8a667537-3cab-4688-8f88-75ad27db7735.c000.snappy.parquet' at org.apache.flink.table.factories.FactoryUtil.createDynamicTableSource(FactoryUtil.java:228) at org.apache.flink.table.factories.FactoryUtil.createDynamicTableSource(FactoryUtil.java:253) at org.apache.flink.table.planner.plan.schema.CatalogSourceTable.createDynamicTableSource(CatalogSourceTable.java:175) at org.apache.flink.table.planner.plan.schema.CatalogSourceTable.toRel(CatalogSourceTable.java:115) at org.apache.calcite.sql2rel.SqlToRelConverter.toRel(SqlToRelConverter.java:4033) at org.apache.calcite.sql2rel.SqlToRelConverter.convertIdentifier(SqlToRelConverter.java:2903) at org.apache.calcite.sql2rel.SqlToRelConverter.convertFrom(SqlToRelConverter.java:2463) at org.apache.calcite.sql2rel.SqlToRelConverter.convertFrom(SqlToRelConverter.java:2377) at org.apache.calcite.sql2rel.SqlToRelConverter.convertFrom(SqlToRelConverter.java:2322) at org.apache.calcite.sql2rel.SqlToRelConverter.convertSelectImpl(SqlToRelConverter.java:729) at org.apache.calcite.sql2rel.SqlToRelConverter.convertSelect(SqlToRelConverter.java:715) at org.apache.calcite.sql2rel.SqlToRelConverter.convertQueryRecursive(SqlToRelConverter.java:3879) at org.apache.calcite.sql2rel.SqlToRelConverter.convertQuery(SqlToRelConverter.java:619) at org.apache.flink.table.planner.calcite.FlinkPlannerImpl.org$apache$flink$table$planner$calcite$FlinkPlannerImpl$$rel(FlinkPlannerImpl.scala:230) at org.apache.flink.table.planner.calcite.FlinkPlannerImpl.rel(FlinkPlannerImpl.scala:205) at org.apache.flink.table.planner.operations.SqlNodeConvertContext.toRelRoot(SqlNodeConvertContext.java:69) at org.apache.flink.table.planner.operations.converters.SqlQueryConverter.convertSqlNode(SqlQueryConverter.java:48) at org.apache.flink.table.planner.operations.converters.SqlNodeConverters.convertSqlNode(SqlNodeConverters.java:74) at org.apache.flink.table.planner.operations.SqlNodeToOperationConversion.convertValidatedSqlNode(SqlNodeToOperationConversion.java:270) at org.apache.flink.table.planner.operations.SqlNodeToOperationConversion.convert(SqlNodeToOperationConversion.java:260) at org.apache.flink.table.planner.delegation.ParserImpl.parse(ParserImpl.java:106) at org.apache.flink.table.api.internal.TableEnvironmentImpl.sqlQuery(TableEnvironmentImpl.java:708) at org.example.WordCountStream.main(WordCountStream.java:38) Caused by: org.apache.flink.table.api.ValidationException: Cannot discover a connector using option: 'connector'='filesystem' at org.apache.flink.table.factories.FactoryUtil.enrichNoMatchingConnectorError(FactoryUtil.java:807) at org.apache.flink.table.factories.FactoryUtil.discoverTableFactory(FactoryUtil.java:781) at org.apache.flink.table.factories.FactoryUtil.createDynamicTableSource(FactoryUtil.java:224) ... 22 more Caused by: org.apache.flink.table.api.ValidationException: Could not find any factory for identifier 'filesystem' that implements 'org.apache.flink.table.factories.DynamicTableFactory' in the classpath. Available factory identifiers are: blackhole datagen print at org.apache.flink.table.factories.FactoryUtil.discoverFactory(FactoryUtil.java:617) at org.apache.flink.table.factories.FactoryUtil.enrichNoMatchingConnectorError(FactoryUtil.java:803) ... 24 more 已与地址为 ''127.0.0.1:59729',传输: '套接字'' 的目标虚拟机断开连接 进程已结束,退出代码为 1 Exception in thread "main" org.apache.flink.table.api.ValidationException: Unable to create a source for reading table 'default_catalog.default_database.parquet_table'. Table options are: 'connector'='filesystem' 'format'='parquet' 'path'='C:\\Users\\zhaoxiaohao\\Desktop\\part-00000-8a667537-3cab-4688-8f88-75ad27db7735.c000.snappy.parquet' at org.apache.flink.table.factories.FactoryUtil.createDynamicTableSource(FactoryUtil.java:228) at org.apache.flink.table.factories.FactoryUtil.createDynamicTableSource(FactoryUtil.java:253) at org.apache.flink.table.planner.plan.schema.CatalogSourceTable.createDynamicTableSource(CatalogSourceTable.java:175) at org.apache.flink.table.planner.plan.schema.CatalogSourceTable.toRel(CatalogSourceTable.java:115) at org.apache.calcite.sql2rel.SqlToRelConverter.toRel(SqlToRelConverter.java:4033) at org.apache.calcite.sql2rel.SqlToRelConverter.convertIdentifier(SqlToRelConverter.java:2903) at org.apache.calcite.sql2rel.SqlToRelConverter.convertFrom(SqlToRelConverter.java:2463) at org.apache.calcite.sql2rel.SqlToRelConverter.convertFrom(SqlToRelConverter.java:2377) at org.apache.calcite.sql2rel.SqlToRelConverter.convertFrom(SqlToRelConverter.java:2322) at org.apache.calcite.sql2rel.SqlToRelConverter.convertSelectImpl(SqlToRelConverter.java:729) at org.apache.calcite.sql2rel.SqlToRelConverter.convertSelect(SqlToRelConverter.java:715) at org.apache.calcite.sql2rel.SqlToRelConverter.convertQueryRecursive(SqlToRelConverter.java:3879) at org.apache.calcite.sql2rel.SqlToRelConverter.convertQuery(SqlToRelConverter.java:619) at org.apache.flink.table.planner.calcite.FlinkPlannerImpl.org$apache$flink$table$planner$calcite$FlinkPlannerImpl$$rel(FlinkPlannerImpl.scala:230) at org.apache.flink.table.planner.calcite.FlinkPlannerImpl.rel(FlinkPlannerImpl.scala:205) at org.apache.flink.table.planner.operations.SqlNodeConvertContext.toRelRoot(SqlNodeConvertContext.java:69) at org.apache.flink.table.planner.operations.converters.SqlQueryConverter.convertSqlNode(SqlQueryConverter.java:48) at org.apache.flink.table.planner.operations.converters.SqlNodeConverters.convertSqlNode(SqlNodeConverters.java:74) at org.apache.flink.table.planner.operations.SqlNodeToOperationConversion.convertValidatedSqlNode(SqlNodeToOperationConversion.java:270) at org.apache.flink.table.planner.operations.SqlNodeToOperationConversion.convert(SqlNodeToOperationConversion.java:260) at org.apache.flink.table.planner.delegation.ParserImpl.parse(ParserImpl.java:106) at org.apache.flink.table.api.internal.TableEnvironmentImpl.sqlQuery(TableEnvironmentImpl.java:708) at org.example.WordCountStream.main(WordCountStream.java:38) Caused by: org.apache.flink.table.api.ValidationException: Cannot discover a connector using option: 'connector'='filesystem' at org.apache.flink.table.factories.FactoryUtil.enrichNoMatchingConnectorError(FactoryUtil.java:807) at org.apache.flink.table.factories.FactoryUtil.discoverTableFactory(FactoryUtil.java:781) at org.apache.flink.table.factories.FactoryUtil.createDynamicTableSource(FactoryUtil.java:224) ... 22 more Caused by: org.apache.flink.table.api.ValidationException: Could not find any factory for identifier 'filesystem' that implements 'org.apache.flink.table.factories.DynamicTableFactory' in the classpath. Available factory identifiers are: blackhole datagen print at org.apache.flink.table.factories.FactoryUtil.discoverFactory(FactoryUtil.java:617) at org.apache.flink.table.factories.FactoryUtil.enrichNoMatchingConnectorError(FactoryUtil.java:803) ... 24 more 已与地址为 ''127.0.0.1:59729',传输: '套接字'' 的目标虚拟机断开连接 进程已结束,退出代码为 1
最新发布
06-11
<think>嗯,用户这次提出了一个具体的Flink连接器问题,关于filesystem连接器无法被识别的ValidationException。从历史对话看,用户之前已经接触过Kafka连接器缺失的解决方案,说明ta正在搭建Flink的流处理环境,且可能涉及多数据源(Kafka、文件系统)的整合。用户引用的四条信息很有价值:引用[2]直接展示了报错信息,引用[3]提供了依赖下载方案,引用[4]补充了SQLClient的特殊要求。不过要注意到用户明确表示“上述引用仅供参考”,说明ta更希望获得针对性解决方案而非通用参考。深层需求可能是:用户正在配置一个包含文件系统连接的生产环境,但被基础依赖缺失卡住了进度。考虑到引用中提到hive、kafka等连接器,实际应用场景可能是异构数据源同步或实时数仓。技术定位很清晰:Flink在classpath中找不到filesystem连接器实现。核心原因应该是:1)基础依赖flink-connector-files未安装(与常见误区不同,filesystem连接器在1.12+后独立于flink-dist包)2)若使用SQLClient,可能未同步加载依赖3)YARN集群环境下依赖分发问题解决方案需要分层说明:基础层必须下载flink-connector-files_xxx.jar,这个信息需强调Scala版本匹配(用户历史操作显示ta在用Scala2.11)部署层要区分YARNsession模式与standalone模式的处理差异(用户引用[1]提到yarn-session,这点很关键)排查层需要提供jar包验证指令,毕竟用户可能漏下其他依赖如format相关的jar特别注意用户上次提问时给过wget示例(引用[3]),这次应延续相同操作习惯。YARN环境要补充preferred方式(-t参数分发依赖),避免用户重复踩坑。最后要主动预防后续问题——csv格式依赖缺失是常见连锁问题,提前预警能减少用户二次提问。考虑用户技术背景,可以安全跳过maven依赖管理方案,直接提供wget指令更实用。</think> 针对Flink中因缺少文件系统连接器依赖导致的 `ValidationException: 'connector'='filesystem' 无法发现连接器` 问题,以下是完整解决方案: --- ### **根本原因** 文件系统连接器(filesystem connector)的实现类未出现在Flink的classpath中。Flink 1.11+ 版本开始要求显式添加连接器依赖包。 --- ### **解决方案** #### **1. 添加必需依赖包** 需确保以下文件系统连接器JAR包存在于 `${FLINK_HOME}/lib` 目录: ```bash cd ${FLINK_HOME}/lib # 下载filesystem连接器核心包(注意Scala版本匹配) wget https://repo1.maven.org/maven2/org/apache/flink/flink-connector-files_2.11/1.13.6/flink-connector-files_2.11-1.13.6.jar # 附加添加常用格式包(如CSV/JSON) wget https://repo1.maven.org/maven2/org/apache/flink/flink-csv/1.13.6/flink-csv-1.13.6.jar wget https://repo1.maven.org/maven2/org/apache/flink/flink-json/1.13.6/flink-json-1.13.6.jar ``` #### **2. YARN Session模式额外操作** 在YARN集群运行时需通过 `-t` 参数分发依赖: ```bash flink run -t yarn-session -d \ -C file://${FLINK_HOME}/lib/flink-connector-files_2.11-1.13.6.jar \ -C file://${FLINK_HOME}/lib/flink-csv-1.13.6.jar \ -c com.your.MainClass yourJob.jar ``` #### **3. Flink SQL Client验证** 在SQL Client中执行 `SHOW JARS;` 确认依赖加载: ```sql -- 创建测试表验证 CREATE TABLE fs_table ( id INT, data STRING ) WITH ( 'connector' = 'filesystem', 'path' = 'file:///tmp/test', 'format' = 'csv' ); ``` --- ### **关键检查点** | 检查项 | 操作指引 | |------------------------|-------------------------------------------------------------------------| | **JAR包完整性** | 确认 `${FLINK_HOME}/lib` 下存在 `flink-connector-files_*.jar` | | **版本兼容性** | Flink版本与连接器版本需严格匹配(如1.13.6) | | **集群分发** | YARN模式需通过 `-C` 或 `-t` 参数显式传输依赖 | | **文件路径权限** | 确保 `path` 参数指向的目录有读写权限(本地路径需加 `file://` 前缀) | | **Format关联性** | 需额外添加格式包(如CSV/JSON/Parquet)避免二次报错 [^3] | --- ### **预防措施** ```mermaid graph TD A[创建表源] -->|filesystem| B{依赖检查} B -->|缺少JAR| C[添加flink-connector-files] B -->|版本不符| D[调整至匹配版本] B -->|格式缺失| E[添加flink-csv/flink-json] C --> F[重启集群] D --> F E --> F ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值