HBTlang-Mycat2.0

author:chenjunwen 2020-3-16

HBTlang语言 - 图1
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

样例

  1. /*+ mycat:run{
  2. "hbt":"innerJoin(`id0` = `id`,fromTable(db1,travelrecord).map(`id` as `id0`),fromTable(db1,company))"
  3. } */;
  4. /*+ mycat:run{
  5. "hbt":"fromTable(db1,travelrecord).orderBy(order(id,ASC))"
  6. } */;
  7. /*+ mycat:run{
  8. "hbt":"table(fields(fieldType(id,integer)),values(1,2,3))"
  9. } */;
  10. /*+ mycat:run{
  11. "hbt":"fromSql('prototype','select 1')"
  12. } */;
  13. /*+ mycat:run{
  14. "hbt":"table(fields(fieldType(id,integer),fieldType(id2,integer)),values(1,2)).map(id + id2,id)"
  15. } */;
  16. /*+ mycat:run{
  17. "hbt":"table(fields(fieldType(`1`,`integer`,true),fieldType(`2`,`integer`,true)),values(1,2)).rename(`2`,`1`)"
  18. } */;
  19. /*+ mycat:run{
  20. "hbt":"fromTable(db1,travelrecord).filter(`id` = 1)"
  21. } */;
  22. /*+ mycat:run{
  23. "hbt":"unionAll(fromSql('prototype','select 1'),fromSql('prototype','select 2'))"
  24. } */;
  25. /*+ mycat:run{
  26. "hbt":"unionAll(fromSql('prototype','select 1'),fromSql('prototype','select 1')).distinct()"
  27. } */;
  28. /*+ mycat:run{
  29. "hbt":"fromTable(db1,travelrecord).groupBy(keys(groupKey(`id`)),aggregating(avg(`id`))))"
  30. } */;
  31. /*+ mycat:run{
  32. "hbt":"fromTable(db1,travelrecord).groupBy(keys(groupKey(`id`,`user_id`)),aggregating(avg(`id`))))"
  33. } */;
  34. /*+ mycat:run{
  35. "hbt":"fromTable(db1,travelrecord).groupBy(keys(groupKey()),aggregating(sum(`id`)))"
  36. } */;
  37. /*+ mycat:run{
  38. "hbt":"unionDistinct(fromSql('prototype','select 1'),fromSql('prototype','select 1'))"
  39. } */;
  40. /*+ mycat:run{
  41. "hbt":"exceptDistinct(fromSql('prototype','select 1'),fromSql('prototype','select 1'))"
  42. } */;
  43. /*+ mycat:run{
  44. "hbt":"exceptAll(fromSql('prototype','select 2'),fromSql('prototype','select 2'))"
  45. } */;
  46. /*+ mycat:run{
  47. "hbt":"intersectAll(fromSql('prototype','select 1'),fromSql('prototype','select 2'))"
  48. } */;
  49. /*+ mycat:run{
  50. "hbt":"intersectDistinct(fromSql('prototype','select 2'),fromSql('prototype','select 2'))"
  51. } */;
  52. /*+ mycat:run{
  53. "hbt":"fromTable(db1,travelrecord).groupBy(keys(groupKey()),aggregating(max(`id`)))"
  54. } */;
  55. /*+ mycat:run{
  56. "hbt":"fromTable(db1,travelrecord).groupBy(keys(groupKey()),aggregating(count()))"
  57. } */;
  58. /*+ mycat:run{
  59. "hbt":"unionAll( fromSql(prototype,'SELECT COUNT(*) FROM `db1`.`travelrecord`'), fromSql(prototype,'SELECT COUNT(*) FROM (SELECT NULL FROM `db1`.`travelrecord` UNION ALL SELECT NULL FROM `db1`.`travelrecord` UNION ALL SELECT NULL FROM `db1`.`travelrecord`) AS `t2`')).groupBy(keys(groupKey()),aggregating(count()))"
  60. } */;
  61. /*+ mycat:run{
  62. "hbt":"fromTable(db1,travelrecord).orderBy(order(id,ASC))"
  63. } */;
  64. /*+ mycat:run{
  65. "hbt":"fromTable(db1,travelrecord).orderBy(order(id,DESC))"
  66. } */;
  67. /*+ mycat:run{
  68. "hbt":"fromTable(db1,travelrecord).orderBy(order(id,ASC)).limit(0,1)"
  69. } */;
  70. /*+ mycat:run{
  71. "hbt":"fromTable(db1,travelrecord).orderBy(order(id,ASC)).limit(1,1)"
  72. } */;
  73. /*+ mycat:run{
  74. "hbt":"leftJoin(`id0` = `id`,fromTable(db1,travelrecord).map(`id` as `id0`),fromTable(db1,company)).orderBy(order(id,ASC))"
  75. } */;
  76. /*+ mycat:run{
  77. "hbt":"rightJoin(`id0` = `id`,fromTable(db1,travelrecord).map(`id` as `id0`),fromTable(db1,company)).orderBy(order(id,ASC))"
  78. } */;
  79. /*+ mycat:run{
  80. "hbt":"semiJoin(`id0` = `id`,fromTable(db1,travelrecord).map(`id` as `id0`),fromTable(db1,company)).orderBy(order(id0,ASC))"
  81. } */;
  82. /*+ mycat:run{
  83. "hbt":"antiJoin(`id0` = `id`,fromTable(db1,travelrecord).map(`id` as `id0`),fromTable(db1,company)).orderBy(order(id0,ASC))"
  84. } */;
  85. /*+ mycat:run{
  86. "hbt":"antiJoin(`id0` = `id`,fromTable(db1,travelrecord).map(`id` as `id0`),fromTable(db1,company)).orderBy(order(id0,ASC))"
  87. } */;
  88. /*+ mycat:run{
  89. "hbt":"leftJoin(`$0` eq `$$3`,leftJoin(`$0` eq `$$3`,unionAll( fromSql(prototype,'SELECT * FROM `db1`.`travelrecord`'), fromSql(prototype,'SELECT * FROM `db1`.`travelrecord` UNION ALL SELECT * FROM `db1`.`travelrecord` UNION ALL SELECT * FROM `db1`.`travelrecord`')),fromSql(prototype,'SELECT `id`, `companyname`, `addressid`, CAST(`id` AS SIGNED) AS `id0` FROM `db1`.`company`')).map(`$0` as `id`,`$1` as `user_id`,`$2` as `traveldate`,`$3` as `fee`,`$4` as `days`,`$5` as `blob`,`$6` as `id0`,`$7` as `companyname`,`$8` as `addressid`),fromSql(prototype,'SELECT `id`, `companyname`, `addressid`, CAST(`id` AS SIGNED) AS `id0` FROM `db1`.`company`')).map(`$0` as `id`,`$1` as `user_id`,`$2` as `traveldate`,`$3` as `fee`,`$4` as `days`,`$5` as `blob`,`$6` as `id0`,`$7` as `companyname`,`$8` as `addressid`,`$9` as `id1`,`$10` as `companyname0`,`$11` as `addressid0`).orderBy(order(id,ASC))"
  90. } */;
  91. /*+ mycat:run{
  92. "hbt":"leftJoin(`$0` eq `$$0`,leftJoin(`$0` eq `$$0`,fromTable(db1,travelrecord), fromTable(db1,travelrecord)),fromTable(db1,company)).orderBy(order(id,ASC))"
  93. } */;
  94. /*+ mycat:run{
  95. "hbt":"filterFromTable(`id` = 1,db1,travelrecord).orderBy(order(id,ASC))"
  96. } */;
  97. /*+ mycat:run{
  98. "hbt":"fromRelToSql(prototype,fromTable('db1','travelrecord').filter(`id` = 1).map(`id`)).orderBy(order(id,ASC))"
  99. } */;

HBT(Human Brain Tech)简介

HBT在Mycat2中表现为关系表达式领域驱动语言(Relation DSL).

在设计上是Mycat2的运行时的中间语言,关于查询的HBT可以实现与SQL,其他查询优化器,查询编译器的关系表达式相互甚至与SQL DSL框架中的DSL转换.HBT也支持直接从文本和编写代码的方式构建.

使用HBT解决什么问题?

1.允许用户直接编写关系表达式实现功能,不同的SQL方言可以对应同一套关系表达式

2.运行用户运行自定义函数

3.免去优化过程,用户编写的关系表达式可以就是执行计划

4.允许使用函数宏展开关系表达式,比如给逻辑表函数宏指定分片范围自动扩展成对应的物理表

5.允许SQL与关系表达式一并编写,例如叶子是SQL,根是Mycat的运算节点

6.可移植到其他平台运行

7.使用DSL作为中间语言下发到其他Mycat节点上运行

8.方便验证测试

基本HBT表达式

fromTable

获得逻辑表的数据源

name 类型 参数数量 参数
fromTable rel 2 逻辑库的标识符,逻辑表的标识符

text

  1. fromTable(db1,travelrecord)

java

  1. //io.mycat.hbt.ast.HBTBuiltinHelper#fromTable(java.lang.String, java.lang.String)
  2. fromTable("db1", "travelrecord")

table

匿名表,一种字面量构成的数据源

名称 类型 参数数量 参数
table rel 至少两个 字段信息列表,字面量列表(一维列表)

text

  1. table(fields(fieldType(`1`,`integer`),fieldType(`2`,`varchar`)),values())
  2. table(fields(fieldType(id,`integer`)),values(1,2,3))

fieldType

列类型

列名

列类型

可空

精度

小数位数

三种构造方式
  1. fieldType(columnName,columnType)
  2. fieldType(columnName,columnType,columnNullable)
  3. fieldType(columnName,columnType,columnNullable,precision,scale)
  4. fieldType(String columnName, String columnType)
  5. \\columnNullable = true ,precision = null,scale = null
  6. fieldType(String columnName, String columnType, boolean columnNullable)
  7. \\precision = null,scale = null
  8. fieldType(String columnName, String columnType, boolean columnNullable,Integer precision, Integer scale)

字段类型(不区分大小写)

名称 precision(精度) - scale(小数位数) java类型
Bool no-no boolean
Tinyint no-no byte
Smallint no-no short
Integer no-no int
Bigint no-no long
Decimal no-no,yes-no BigDecimal
Float no-no float
Real no-no float
Double no-no double
Date no-no Date ‘xxxx-xx-xx’
Time no-no,yes-no Time ‘xx:xx:xx’
TimeWithLocalTimeZone no-no,yes-no Timestamp ‘xxxx-xx-xxxx:xx:xx’
Timestamp no-no,yes-no Timestamp ‘xxxx-xx-xxxx:xx:xx’
TimestampWithLocalTimeZone no-no,yes-no Timestamp ‘xxxx-xx-xxxx:xx:xx’
Char no-no String
Varchar no-no String
Binary no-no byte[]
Varbinary no-no byte[]

语法辅助

fields

列类型列表

多个fieldType组成

values

多个字面值组成

解析器会根据fieldType的数量自动把数列转成二维表

java

  1. //io.mycat.hbt.ast.HBTBuiltinHelper#table(java.util.List<FieldType>, java.util.List<Object>)
  2. table(Arrays.asList(fieldType("1", "integer"), fieldType("2", "varchar")), Arrays.asList())
  3. //()
  1. table(Arrays.asList(fieldType("id", "integer")), Arrays.asList(1,2,3))
  2. //(1),(2),(3)
  1. table(Arrays.asList(fieldType("id", "integer"),fieldType("id2", "integer")), Arrays.asList(1,2,3,4))
  2. //(1,2),(3,4)

map

名称 类型 参数数量 参数
map rel 至少两个 数据源,至少一个表达式

投影

text

  1. table(fields(fieldType(id,integer),fieldType(id2,integer)),values(1,2))
  2. .map(id2 as id4)//sugar
  3. map(table(fields(fieldType(id,integer),fieldType(id2,integer)),values(1,2)),id2 as id4)

text

  1. table(fields(fieldType(id,integer),fieldType(id2,integer))
  2. .map(id + id2,id)//sugar
  3. map(table(fields(fieldType(id,integer),fieldType(id2,integer)),values(1,2)),id + id2,id)

java

  1. //io.mycat.hbt.ast.HBTBuiltinHelper#map(Schema,List<Expr>)
  2. map(table(Arrays.asList(fieldType("id", "integer"), fieldType("id2", "integer")), Arrays.asList()),Arrays.asList(as(new Identifier("id2"), new Identifier("id4"))))
  3. map(table(Arrays.asList(fieldType("id", "integer"), fieldType("id2", "integer")), Arrays.asList()),
  4. Arrays.asList(add(new Identifier("id2"), new Identifier("id4")),newIdentifier("id")))

rename

名称 类型 参数数量 参数
rename rel 1+数据源的列数量 数据源,多个字段标识符(数据源的列数量)

map表达式的简化,对数据源全列名进行更名,该表达式是一个补充的表达式

text

  1. table(fields(fieldType(`1`,`integer`,true),fieldType(`2`,`integer`,true)),values(1,2)).rename(`2`,`1`)

java

  1. //io.mycat.hbt.ast.HBTBaseTest#rename(Schema, List<String>)
  2. rename(table(Arrays.asList(fieldType("1", "integer",true), fieldType("2", "integer",true)), Arrays.asList(1,2)), Arrays.asList("2", "1"))

filter

选择,根据布尔表达式对行进行过滤

名称 类型 参数数量 参数
filter rel 2 数据源,一个布尔表达式

text

  1. fromTable(db1,travelrecord).filter(`id` = 1)
  2. filter(fromTable(db1,travelrecord),`id` = 1)

java

  1. //io.mycat.hbt.ast.HBTBuiltinHelper#filter(Schema,Expr)
  2. filter(fromTable("db1", "travelrecord"), eq(new Identifier("id"), new Literal(1)))

Set

集合操作

名称 类型 参数数量 参数
集合操作 rel 2个 等于两个数据源
  1. unionAll
  2. unionDistinct
  3. 并,去重
  4. exceptAll
  5. exceptDistinct
  6. 除,去重
  7. intersectAll
  8. intersectDistinct
  9. 交,去重

text

  1. fromTable(db1,travelrecord) unionAll fromTable(db1,travelrecord)
  2. unionAll(fromTable(db1,travelrecord),fromTable(db1,travelrecord))

java

  1. //io.mycat.hbt.ast.HBTOp
  2. //io.mycat.hbt.ast.HBTOp#UNION_ALL
  3. //io.mycat.hbt.ast.HBTOp#UNION_DISTINCT
  4. //io.mycat.hbt.ast.HBTOp#EXCEPT_ALL
  5. //io.mycat.hbt.ast.HBTOp#EXCEPT_DISTINCT
  6. //io.mycat.hbt.ast.HBTOp#INTERSECT_ALL
  7. //io.mycat.hbt.ast.HBTOp#INTERSECT_DISTINCT
  8. //io.mycat.hbt.ast.HBTBuiltinHelper#set(io.mycat.hbt.HBTOp, java.util.List<io.mycat.hbt.ast.base.Schema>)
  9. set(HBTOp.UNION_ALL, Arrays.asList(fromTable("db1", "travelrecord"), fromTable("db1", "travelrecord")))

distinct

名称 类型 参数数量 参数
去重 rel 一个 一个数据源

去重,该表达式是补充的表达式

text

  1. fromTable(db1,travelrecord).distinct()
  2. distinct(fromTable(db1,travelrecord))

java

  1. io.mycat.hbt.ast.HBTBuiltinHelper#distinct(Schema)
  2. distinct(fromTable("db1", "travelrecord"))

GroupBy

分组

名称 类型 参数数量 参数
分组 rel 2 分组键,聚合函数

text

  1. fromTable(db1,travelrecord).groupBy(keys(groupKey(`id`)),aggregating(avg(`id`))))
  2. groupBy(fromTable(db1,travelrecord),keys(groupKey(`id`)),aggregating(avg(`id`)))

分组键

  1. groupKey(`id`)
  2. groupKey(`id`,`id2`)

由一个或者多个以数据源列名组成

聚合函数

基本构造参数

参数 描述
函数名 必须填写
参数列表 不同函数不同

avg
名称 类型 参数数量 参数
avg agg expr 1 列名标识符

扩展型聚合函数,会被优化器转换成sum()/count()

输入值要求是数字类型

count
名称 类型 参数数量 参数
count agg expr 任意 多个列名标识符或无

当有多个列名标识符作为参数的时候,,返回其值不为null 的输入行数

无参的时候返回输入的行数

max
名称 类型 参数数量 参数
max expr 1 列名标识符

输入值要求是数字类型
返回所有输入值中的最大值

min
名称 类型 参数数量 参数
min agg expr 1 列名标识符

输入值要求是数字类型

返回所有输入值中的最小值

sum
名称 类型 参数数量 参数
sum agg expr 1 列名标识符

输入值要求是数字类型

返回所有输入值的数值总和

语法辅助

keys

由一个或者多个分组键组成

一般只有一个groupKey

多个groupKey组成GROUPING SETS

  1. keys(groupKey(`id`),groupKey(`id2`))

aggregating

由一个或者多个聚合函数组成

java

  1. groupBy(fromTable("db1", "travelrecord"), Arrays.asList(groupKey(Arrays.asList(id(("id"))))),
  2. Arrays.asList(new AggregateCall("avg", Arrays.asList(id(("id")))).alias("a").distinct().approximate().ignoreNulls().filter(literal(true)).orderBy(Arrays.asList(order("user_id", Direction.DESC)))))

Join

Join操作

innerJoin

inner join

内连接

在表中存在至少一个匹配时,返回行。

leftJoin

left outer Join

左连接

从左表返回所有的行,即使在右表中没有匹配的行。

rightJoin

right outer Join

右连接

从右表返回所有的行,即使在左表中没有匹配的行

fullJoin

full outer Join

全连接

只要其中某个表存在匹配,就会返回行

semiJoin

left semi join

半连接,左半连接

当条件成立时,返回左表的行

antiJoin

anti semi join

反连接,反半连接

当条件不成立时,返回左表的行

文本

名称 类型 参数数量 参数
innerJoin,leftJoin,rightJoin,semiJoin,antiJoin rel 三个 条件:布尔表达式,左数据源,右数据源
  1. //innerJoin
  2. //leftJoin
  3. //rightJoin
  4. //semiJoin
  5. //antiJoin
  6. innerJoin(`id0` = `id`,fromTable(db1,travelrecord)
  7. .map(`id` as `id0`),fromTable(db1,travelrecord))

java

  1. new JoinSchema(HBTOp.INNER_JOIN,
  2. eq(id("id0"), id("id"))
  3. , map(fromTable("db1", "travelrecord"), Arrays.asList(as(new Identifier("id"), new Identifier("id0")))),
  4. fromTable("db1", "travelrecord")

列名冲突

两个数据源中字段名相同的情况下,使用as表达式建立别名后,才可以在条件里正确使用

行类型变更

join操作后,行类型是左数据源+右数据源的列,

比如上述的

fromTable(db1,travelrecord).map(id as id0),fromTable(db1,travelrecord)

行类型分别是(id0),(id,user_id)

innerJoin操作后变为(id0,id,user_id)

OrderBy

对数据源排序

名称 类型 参数数量 参数
OrderBy rel 1+order数量 数据源,多个order

文本

  1. fromTable(db1,travelrecord).orderBy(order(id,ASC), order(user_id,DESC))
  2. orderBy(fromTable(db1,travelrecord),order(id,ASC), order(user_id,DESC))

java

  1. orderBy(fromTable("db1", "travelrecord"), Arrays.asList(order("id", Direction.ASC), order("user_id", Direction.DESC)))

语法辅助

order(列名,asc或者desc,不区分大小写)

limit

要数据源返回的行的一部分

名称 类型 参数数量 参数
limit rel 1+多个order数量 数据源,offset,limit

文本

  1. fromTable(db1,travelrecord).limit(1,2)
  2. limit(fromTable(db1,travelrecord),1,2)

java

  1. limit(fromTable("db1", "travelrecord"), 1, 2)

HBT-扩展表达式

fromSql

sql作为数据源

名称 类型 参数数量 参数
fromSql rel 2或3 见下面

三个参数

1.列类型信息

2.查询目标名字

3.任意sql,返回的结果集的类型必须与给出的列类型一致

  1. fromSql(fields(fieldType(`id`,`integer`),fieldType(`user_id`,`integer`),targetName,'select * from db1.travelrecord')

两个参数

2.查询目标名字

3.sql,自动推导列信息,暂时,sql只能配置在元数据配置的dataNode中的表名

  1. fromSql(targetName,'select * from db1.travelrecord')

filterFromTable

1.强调可下推的谓词

2.避免逻辑表与物理表耦合

名称 类型 参数数量 参数
filterFromTable rel 3 布尔表达式,逻辑库标识符,逻辑表标识符

等价

  1. filterFromTable(`id` = 1,db1,travelrecord)//强调可下推
  2. <=>
  3. fromTable(`id` = 1,db1,travelrecord).filter(`id` = 1)//不强调下推

下推

  1. filterFromTable(`id` = 1,db1,travelrecord)//经过上下文分析生成查询的目标和查询的SQL
  2. =>
  3. fromSql('targetName','select * from db1.travelrecord where `id` = 1')//可能的SQL

文本

  1. filterFromTable(`id` = 1,db1,travelrecord)
  2. filterFromTable(`id` = 1 or `id` = 2 ...,db1,travelrecord)
  3. filterFromTable(1<= `id` and `id` <= 10000,db1,travelrecord)

布尔表达式必须是简单的形式,不支持任何计算,别名

fromRelToSql

转换HBT到SQL

极少数转换可能会失败,即使转换成功,SQL未必是数据库支持的,转换目标方言默认是mysql

名称 类型 参数数量 参数
fromRelToSql rel 2

文本

  1. fromRelToSql(targetName,fromTable('db1','travelrecord').filter(`id` = 1).map(`id`))
  2. =>等价转换
  3. fromSql(targetName,'select db1.travelrecord.id from db1.travelrecord where `id` = 1')

modifyFromSql

向目标执行返回值不是结果集的SQL

名称 类型 参数数量 参数
modifyFromSql update 2 目标标识符,sql

执行结果

updateCount

lastInsertId

文本

  1. modifyFromSql(targetName,'delete db1.travelrecord1')

mergeModify

合拼多个modifyFromSql结果

updateCount 求和

lastInsertId 取mergeModify中最大值

名称 类型 参数数量 参数
mergeModify update 一个或者多个modifyFromSql modifyFromSql

文本

  1. mergeModify(
  2. modifyFromSql(targetName,'delete from db1.travelrecord1'),
  3. modifyFromSql(targetName2,'delete from db2.travelrecord1')
  4. )

HBT-命令表达式

explain

1.显示数据源的列类型,辅助编写hbt

2.显示执行计划

文本

  1. explain filterFromTable(`id` = 1,db1,travelrecord)

HBT-标量表达式(运算符)

计算结果是一个特定数据类型的标量值的表达式

运算符函数名 描述 算符优先级 参数数量 布尔表达式
eq = 等于 10 2 y
ne <>,!= 不等于 10 2 y
gt > 大于 11 2 y
lt < 小于 11 2 y
gte >= 大于等于 11 2 y
lte <= 小于等于 11 2 y
add + 算术加法 13 2 n
minus - 算术减法 13 2 n
and 逻辑与 6 2 y
or 逻辑或 5 2 y
not 逻辑非 1 y
as 别名 3 2 n
cast 类型转换 2 n
ref 关联连接数据源引用 2 n

上述描述的运算符都是左结合,其他情况使用函数调用

当运算符参数数量为2并且具有优先级的时候可以使用中缀表达式编写文本,解析器自动完成转换为函数调用并解糖

  1. 1+2 => add(1,2)

需要用到其他函数,请提PR

支持中缀表达式编写文本的关系表达式,同理,也可以被解析器完成转换,

  1. fromTable(db1,travelrecord) unionAll fromTable(db1,travelrecord)
  2. =>
  3. unionAll(fromTable(db1,travelrecord),fromTable(db1,travelrecord))

算符优先级都是1,左结合

  1. unionAll,
  2. unionDistinct,
  3. exceptDistinct,
  4. exceptAll,
  5. rename,
  6. groupBy
  7. distinct,
  8. orderBy
  9. alias//仅用于聚合函数 ,
  10. distinct//用于关系表达式,聚合函数 ,
  11. approximate//仅用于聚合函数 ,
  12. ignoreNulls//仅用于聚合函数 ,
  13. filter//用于关系表达式,聚合函数 ,

类型转换与别名

  1. fromTable(db1,travelrecord).map(cast(id,float) as a)
名称 类型 参数数量 参数
cast expr 2 表达式,类型标识符(不接受待求值的表达式)
名称 类型 参数数量 参数
as expr 2 表达式,标识符(不接受待求值的表达式)

内置常用函数

名称 类型 参数数量 参数
lower expr 1 表达式(字符串类型)

把参数转成小写

名称 类型 参数数量 参数
upper expr 1 表达式(字符串类型)

把参数转成大写

名称 类型 参数数量 参数
isnull expr 1 表达式

表达式的结果是否null

名称 类型 参数数量 参数
nullif expr 2 表达式1,表达式2

如果两表达式的结果相同,则返回null

名称 类型 参数数量 参数
isnotnull expr 1 表达式

表达式的结果是否非null

HBT-列名引用

代数关系表达式中的表达式引用列名的方法以及存在的规则

1.在HBT里面,一般来说,一个数据源上的列名是唯一的,不能存在相同的列名,不能同时以$开头和以数字结尾

2.列表使用``包裹强调是标识符

  1. fromTable(db1,travelrecord).filter(`id` = 1)

3.在涉及两个数据源的情况下,需要遇上列名相同的情况,as表达式或者rename关系表达式把其中一个数据源的列名改变,一般风格是添上0为结尾

  1. innerJoin(`id0` = `id`,fromTable(db1,travelrecord)
  2. .map(`id` as `id0`),fromTable(db1,travelrecord))

4.支持下标引用列名

以$[0-9]格式,以下标引用第一个数据源的列名

  1. fromTable(db1,travelrecord).filter(`$0` = 1)//引用位置0的列表,即id

在join关系表达式下,以$$[0-9]格式,以下标引用第二个数据源的列名

  1. innerJoin(`$0` = `$$0`,fromTable(db1,travelrecord),fromTable(db1,travelrecord))

$0引用第一个数据源的id
$1引用第一个数据源的user_id
0引用第二个数据源的id
1引用第二个数据源的user_id
编译器会根据上下文判断列名是哪个数据源哪个下标,最终转换成下标表示,并解决列名冲突,在相同列名上添加0.

HBT-词法

HBT词法分析就是MySQL的词法
编码为UTF8
不支持字符串里嵌入二进制数据,使用X’4D2AFF’这种格式描述二进制数据
这里不再重复描述MySQL词法
词法可以区分字面量和标识符,其中字面量一般就是值

HBT-语法

基本形式

语法的设计选取了函数调用语法
function(arg1,arg2)
作为基本形式,任何语法糖都会语法分析器编译成该形式

语法糖

中缀表达式语法糖

在基本形式的基础上使用中缀表达式简化标量表达式的编写,尤其是and or表达式
id = 1 and user_id = 2 => and(eq(id,1),eq(user_id,2))

函数别名语法糖

为解决eq,lt等函数名并不接近sql常用的运算符= >的问题
语法分析器在判断它们是函数之后,会把它们的函数名指向最终的函数名

链式调用语法糖

在基本形式的基础上使用链式调用语法减少深层括号嵌套的影响
fromSql(aaaa).filter(bbbb) => filter(fromSql(aaaa),bbbb)

语法辅助(函数)

用于分隔语法节点层次

函数的参数

HBT语法上使用函数调用方式,但是实际上是使用文本转换成语法树表达builder模式
参数的实际含义取决于所在的函数名,参数位置
例如filter的第二个参数本质上是接收语法树而非表达式的语法树的求值结果

HBT类型转换

HBT无隐式的类型转换,一般使用cast显式编写
对于整型数值的算术运算需要保持精度的情况,比如avg运算,请在avg的参数表达式上先转换成float等浮点类型

高级用法(少数情况出现,一般不使用)

额外类型

以下类型未正式支持

名称 precision(精度) - scale(小数点后的位数) java类型
IntervalYear no-no
IntervalYearMonth no-no
IntervalMonth no-no
IntervalDay no-no,yes-no
IntervalDayHour no-no,yes-no
IntervalDayMinute no-no,yes-no
IntervalDaySecond no-no,yes-no
IntervalHour no-no,yes-no
IntervalHourMinute no-no,yes-no
IntervalMinute no-no,yes-no
IntervalMinuteSecond no-no,yes-no
IntervalSecond no-no,yes-no

聚合函数

额外构建参数,需要规则甚至执行器配合,不推荐使用,不在HBT的目标之内

参数 描述
distinct 如果distinct存在, 则聚合前进行distinct运算过滤,默认不开启
approximate 不同函数不同,默认不开启,用于特殊函数
ignoreNulls 如果ignoreNulls存在,则聚合前使用布尔表达式过滤,优化器会用自动添加Order表达式,默认不开启
filter 如果filter存在,则聚合前使用布尔表达式过滤,优化器会用自动添加Order表达式,默认不开启
orderKeys 如果orderKeys存在,则聚合前先排序,优化器会用自动添加filter表达式,默认不开启
alias 为聚合函数对应的列设置别名,默认不开启

完整构建语法样例
text
fromTable(db1,travelrecord)
.groupBy(keys(groupKey(columnName)),aggregating(avg(columnName).alias(a).distinct().approximate().ignoreNulls().filter(true).orderBy(order(user_id,DESC))))
java
groupBy(fromTable(“db1”, “travelrecord”), Arrays.asList(groupKey(Arrays.asList(id((“columnName”))))),
Arrays.asList(new AggregateCall(“avg”, Arrays.asList(id((“columnName”)))).alias(“a”).distinct().approximate().ignoreNulls().filter(literal(true))
.orderBy(Arrays.asList(order(“user_id”, Direction.DESC)))))

关联连接

对应关联子查询
循环获取左表的行,使用该行的值作为查询用到的参数重新获取右表数据源,之后进行内连接或者左连接
具体执行一般实现为NestedLoops

correlateInnerJoin

关联内连接

correlateLeftJoin

关联左连接
构建步骤
1.组合两个数据源而成为新的数据源,并为一个数据源建立别名供第二个数据源引用
correlateInnerJoin(别名,数据源1 ,数据源2)
correlateLeftJoin(别名,数据源1 ,数据源2)
2.第二个数据源引用别名
ref(别名,字段)
实际上别名就是数据源1
3.综上
text
correlateInnerJoin(t,
table(fields(fieldType(id0,integer,false)),values(1,2,3,4)) , fromTable(db1,travelrecord).filter(ref(t,id0) = id)))
java
Schema db0 = table(Arrays.asList(fieldType(“id0”, “Integer”,false)), Arrays.asList(1, 2, 3, 4));
Schema db1 = filter(fromTable(“db1”, “travelrecord”), eq(ref(“t”, “id0”), new Identifier(“id”)));
Schema schema = correlate(CORRELATE_INNER_JOIN, “t”, db0, db1);

文档更改记录

2020.3.16-2020-3-17 完成文档编写 (chenjunwen)