假设您只对逻辑计划感兴趣,您提到的查询的关系代数如下:
LogicalProject(id=[$0], $f1=[$SCALAR_QUERY({
LogicalProject(is_active=[$0])
LogicalFilter(condition=[AND(<=($1, 2021-06-01), >=($2, 2021-06-01))])
LogicalTableFunctionScan(invocation=[UNNEST($1)], rowType=[RecordType(BOOLEAN is_active, DATE start_date, DATE end_date)])
})])
LogicalTableScan(table=[[scott, my_table]])
这里是关于如何创建 RelNode 的高级概述:
-
您需要先定义表格的行类型,即my_table:
1.1 你的行应该是这样的:
RecordType(INTEGER id, RecordType:peek(BOOLEAN is_active, DATE start_date, DATE end_date) MULTISET history)
1.2 需要观察history的数据类型。它需要遵守UNNEST sql 函数的定义。为了解释这篇文章,我考虑了MULTISET 数据类型。
1.3 假设您只对逻辑计划感兴趣,因此您可以创建一个简单的表,如下所示:
Table tableImpl = new AbstractTable() {
@Override
public RelDataType getRowType(RelDataTypeFactory typeFactory) {
RelDataType id = typeFactory.createSqlType(SqlTypeName.INTEGER);
List<RelDataType> historyRecordDataTypes = Arrays.asList(
typeFactory.createSqlType(SqlTypeName.BOOLEAN),
typeFactory.createSqlType(SqlTypeName.DATE),
typeFactory.createSqlType(SqlTypeName.DATE)
);
List<String> historyRecordFieldNames = Arrays.asList("is_active", "start_date", "end_date");
RelDataType history = typeFactory.createStructType(StructKind.PEEK_FIELDS, historyRecordDataTypes, historyRecordFieldNames);
return typeFactory.createStructType(Arrays.asList(id, typeFactory.createMultisetType(history, -1L)), Arrays.asList("id", "history"));
}
};
- 创建表并定义其
rowType 后,您需要将其填充到
schemPlus。让我们在 calcite 测试套件中的模式 scott 中添加上面创建的表。
rootSchema.getParentSchema().getSubSchema("scott")).add("my_table", tableImpl)
- 创建自定义表并填充到
schemaPlus后,首先需要创建my_table的表扫描
builder.scan("my_table");
- 然后你需要创建未嵌套子查询的rel。您需要为此使用
RelBuilder#functionScan(...) API。根据帖子中提到的查询,unnestRelNode 将如下:
builder.functionScan(SqlStdOperatorTable.UNNEST, 0, builder.field("history"));
List<RexNode> innerQueryFilterPredicates = Arrays.asList(
// start_date <= "2021-06-01"
builder.call(
SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
builder.field("start_date"), builder.getRexBuilder().makeDateLiteral(new DateString("2021-06-01"))),
// end_date >= "2021-06-01" OR end_date IS NULL
builder.call(
SqlStdOperatorTable.OR,
// end_date >= "2021-06-01"
builder.call(
SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
builder.field("end_date"), builder.getRexBuilder().makeDateLiteral(new DateString("2021-06-01"))
),
// end_date IS NULL
builder.call(
SqlStdOperatorTable.IS_NULL, builder.field("end_date")
)
)
);
RelNode unnestRel = builder.filter(builder.call(SqlStdOperatorTable.AND, innerQueryFilterPredicates))
.project(builder.field("is_active"))
.build();
- 最后,为了创建最终的 RelNode,您需要将
unnestRel 变成一个标量子查询:
RelNode finalRel = builder.project(builder.field("id"), builder.scalarQuery(b -> unnestRel))
.build();