# Storm SQL 语言参考
Storm SQL 使用 Apache Calcite 来转换和评估 SQL 语句. Storm SQL 还采用了来自 Calcite 的 Rex 编译器,因此 Storm SQL 将处理由 Calcite 的默认 SQL 解析器识别的 SQL 方言。
本文基于 Calcite 官网的 SQL 参考手册, 移除了部分 Storm SQL 不支持的内容, 添加了一些 Storm SQL 支持的内容.
请先阅读 [Storm SQL integration](storm-sql.html) 页面, 了解 Storm SQL 支持哪些特性.
## 语法
Calcite 提供丰富的 SQL 语法. 但是 Storm SQL 并不是一个数据库系统, 它用于处理流式数据, 因此只支持语法的子集. Storm SQL 不会重定义 SQL 语法, 只优化 Calcite 提供的转换器, 因此 SQL 语句仍然基于 Calcite 的 SQL 语法进行转换.
SQL 语法表现为 类[BNF](http://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form) 的形式.
<figure class="highlight">
```
statement:
setStatement
| resetStatement
| explain
| describe
| insert
| update
| merge
| delete
| query
setStatement:
[ ALTER ( SYSTEM | SESSION ) ] SET identifier '=' expression
resetStatement:
[ ALTER ( SYSTEM | SESSION ) ] RESET identifier
| [ ALTER ( SYSTEM | SESSION ) ] RESET ALL
explain:
EXPLAIN PLAN
[ WITH TYPE | WITH IMPLEMENTATION | WITHOUT IMPLEMENTATION ]
[ EXCLUDING ATTRIBUTES | INCLUDING [ ALL ] ATTRIBUTES ]
FOR ( query | insert | update | merge | delete )
describe:
DESCRIBE DATABASE databaseName
| DESCRIBE CATALOG [ databaseName . ] catalogName
| DESCRIBE SCHEMA [ [ databaseName . ] catalogName ] . schemaName
| DESCRIBE [ TABLE ] [ [ [ databaseName . ] catalogName . ] schemaName . ] tableName [ columnName ]
| DESCRIBE [ STATEMENT ] ( query | insert | update | merge | delete )
insert:
( INSERT | UPSERT ) INTO tablePrimary
[ '(' column [, column ]* ')' ]
query
update:
UPDATE tablePrimary
SET assign [, assign ]*
[ WHERE booleanExpression ]
assign:
identifier '=' expression
merge:
MERGE INTO tablePrimary [ [ AS ] alias ]
USING tablePrimary
ON booleanExpression
[ WHEN MATCHED THEN UPDATE SET assign [, assign ]* ]
[ WHEN NOT MATCHED THEN INSERT VALUES '(' value [ , value ]* ')' ]
delete:
DELETE FROM tablePrimary [ [ AS ] alias ]
[ WHERE booleanExpression ]
query:
values
| WITH withItem [ , withItem ]* query
| {
select
| selectWithoutFrom
| query UNION [ ALL ] query
| query EXCEPT query
| query INTERSECT query
}
[ ORDER BY orderItem [, orderItem ]* ]
[ LIMIT { count | ALL } ]
[ OFFSET start { ROW | ROWS } ]
[ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ]
withItem:
name
[ '(' column [, column ]* ')' ]
AS '(' query ')'
orderItem:
expression [ ASC | DESC ] [ NULLS FIRST | NULLS LAST ]
select:
SELECT [ STREAM ] [ ALL | DISTINCT ]
{ * | projectItem [, projectItem ]* }
FROM tableExpression
[ WHERE booleanExpression ]
[ GROUP BY { groupItem [, groupItem ]* } ]
[ HAVING booleanExpression ]
[ WINDOW windowName AS windowSpec [, windowName AS windowSpec ]* ]
selectWithoutFrom:
SELECT [ ALL | DISTINCT ]
{ * | projectItem [, projectItem ]* }
projectItem:
expression [ [ AS ] columnAlias ]
| tableAlias . *
tableExpression:
tableReference [, tableReference ]*
| tableExpression [ NATURAL ] [ LEFT | RIGHT | FULL ] JOIN tableExpression [ joinCondition ]
joinCondition:
ON booleanExpression
| USING '(' column [, column ]* ')'
tableReference:
tablePrimary
[ [ AS ] alias [ '(' columnAlias [, columnAlias ]* ')' ] ]
tablePrimary:
[ [ catalogName . ] schemaName . ] tableName
'(' TABLE [ [ catalogName . ] schemaName . ] tableName ')'
| [ LATERAL ] '(' query ')'
| UNNEST '(' expression ')' [ WITH ORDINALITY ]
| [ LATERAL ] TABLE '(' [ SPECIFIC ] functionName '(' expression [, expression ]* ')' ')'
values:
VALUES expression [, expression ]*
groupItem:
expression
| '(' ')'
| '(' expression [, expression ]* ')'
| CUBE '(' expression [, expression ]* ')'
| ROLLUP '(' expression [, expression ]* ')'
| GROUPING SETS '(' groupItem [, groupItem ]* ')'
windowRef:
windowName
| windowSpec
windowSpec:
[ windowName ]
'('
[ ORDER BY orderItem [, orderItem ]* ]
[ PARTITION BY expression [, expression ]* ]
[
RANGE numericOrIntervalExpression { PRECEDING | FOLLOWING }
| ROWS numericExpression { PRECEDING | FOLLOWING }
]
')'
```
</figure>
在 _merge_ 中, 必须有 WHEN MATCH 或者 WHEN NOT MATCH 从句的其中之一.
在 _orderItem_ 中, 如果 _expression_ 是一个正整数 _n_, 他表示 SELECT 从句中的第 _n_ 个项目.
聚合查询是包含 GROUP BY 或 HAVING 的查询, 子句或 SELECT 子句中的聚合函数。 在 SELECT 中,汇总查询的 HAVING 和 ORDER BY 子句,所有表达式必须在当前组内是恒定的(即分组常量由GROUP BY子句定义,或常量)或聚合函数或常量和聚合的组合功能。 聚合和分组功能只能出现在聚合查询,并且仅在 SELECT,HAVING 或 ORDER BY 子句中。
一个标量子查询是像表达式一样使用的子查询. 如果子查询没有返回行, 值为 NULL; 如果返回多行, 就是错误的.
IN, EXISTS 和 标量子查询可以在任何使用 expression 的地方使用(比如 SELECT 从句, WHERE 从句, JOIN 后面的 ON 从句, 或者作为一个聚合函数的参数).
一个 IN, EXISTS 或者标量子查询可能是相关的; 意思是, 可以引用封闭查询的 FROM 从句中的表。
_selectWithoutFrom_ 等价于 VALUES, 但是并非标准 SQL, 只在允许在特定的[conformance levels](/org/apache/calcite/sql/validate/SqlConformance.html#isFromRequired--)上.
## 关键词
下列是一个 SQL 的关键词列表. 这个列表页来自于 Calcite SQL 的参考手册.
A, **ABS**, ABSOLUTE, ACTION, ADA, ADD, ADMIN, AFTER, **ALL**, **ALLOCATE**, **ALLOW**, **ALTER**, ALWAYS, **AND**, **ANY**, **ARE**, **ARRAY**, **AS**, ASC, **ASENSITIVE**, ASSERTION, ASSIGNMENT, **ASYMMETRIC**, **AT**, **ATOMIC**, ATTRIBUTE, ATTRIBUTES, **AUTHORIZATION**, **AVG**, BEFORE, **BEGIN**, BERNOULLI, **BETWEEN**, **BIGINT**, **BINARY**, **BIT**, **BLOB**, **BOOLEAN**, **BOTH**, BREADTH, **BY**, C, **CALL**, **CALLED**, **CARDINALITY**, CASCADE, **CASCADED**, **CASE**, **CAST**, CATALOG, CATALOG_NAME, **CEIL**, **CEILING**, CENTURY, CHAIN, **CHAR**, **CHARACTER**, CHARACTERISTICTS, CHARACTERS, **CHARACTER_LENGTH**, CHARACTER_SET_CATALOG, CHARACTER_SET_NAME, CHARACTER_SET_SCHEMA, **CHAR_LENGTH**, **CHECK**, CLASS_ORIGIN, **CLOB**, **CLOSE**, **COALESCE**, COBOL, **COLLATE**, COLLATION, COLLATION_CATALOG, COLLATION_NAME, COLLATION_SCHEMA, **COLLECT**, **COLUMN**, COLUMN_NAME, COMMAND_FUNCTION, COMMAND_FUNCTION_CODE, **COMMIT**, COMMITTED, **CONDITION**, CONDITION_NUMBER, **CONNECT**, CONNECTION, CONNECTION_NAME, **CONSTRAINT**, CONSTRAINTS, CONSTRAINT_CATALOG, CONSTRAINT_NAME, CONSTRAINT_SCHEMA, CONSTRUCTOR, CONTAINS, CONTINUE, **CONVERT**, **CORR**, **CORRESPONDING**, **COUNT**, **COVAR_POP**, **COVAR_SAMP**, **CREATE**, **CROSS**, **CUBE**, **CUME_DIST**, **CURRENT**, **CURRENT_CATALOG**, **CURRENT_DATE**, **CURRENT_DEFAULT_TRANSFORM_GROUP**, **CURRENT_PATH**, **CURRENT_ROLE**, **CURRENT_SCHEMA**, **CURRENT_TIME**, **CURRENT_TIMESTAMP**, **CURRENT_TRANSFORM_GROUP_FOR_TYPE**, **CURRENT_USER**, **CURSOR**, CURSOR_NAME, **CYCLE**, DATA, DATABASE, **DATE**, DATETIME_INTERVAL_CODE, DATETIME_INTERVAL_PRECISION, **DAY**, **DEALLOCATE**, **DEC**, DECADE, **DECIMAL**, **DECLARE**, **DEFAULT**, DEFAULTS, DEFERRABLE, DEFERRED, DEFINED, DEFINER, DEGREE, **DELETE**, **DENSE_RANK**, DEPTH, **DEREF**, DERIVED, DESC, **DESCRIBE**, DESCRIPTION, DESCRIPTOR, **DETERMINISTIC**, DIAGNOSTICS, **DISALLOW**, **DISCONNECT**, DISPATCH, **DISTINCT**, DOMAIN, **DOUBLE**, DOW, DOY, **DROP**, **DYNAMIC**, DYNAMIC_FUNCTION, DYNAMIC_FUNCTION_CODE, **EACH**, **ELEMENT**, **ELSE**, **END**, **END-EXEC**, EPOCH, EQUALS, **ESCAPE**, **EVERY**, **EXCEPT**, EXCEPTION, EXCLUDE, EXCLUDING, **EXEC**, **EXECUTE**, **EXISTS**, **EXP**, **EXPLAIN**, **EXTEND**, **EXTERNAL**, **EXTRACT**, **FALSE**, **FETCH**, **FILTER**, FINAL, FIRST, **FIRST_VALUE**, **FLOAT**, **FLOOR**, FOLLOWING, **FOR**, **FOREIGN**, FORTRAN, FOUND, FRAC_SECOND, **FREE**, **FROM**, **FULL**, **FUNCTION**, **FUSION**, G, GENERAL, GENERATED, **GET**, **GLOBAL**, GO, GOTO, **GRANT**, GRANTED, **GROUP**, **GROUPING**, **HAVING**, HIERARCHY, **HOLD**, **HOUR**, **IDENTITY**, IMMEDIATE, IMPLEMENTATION, **IMPORT**, **IN**, INCLUDING, INCREMENT, **INDICATOR**, INITIALLY, **INNER**, **INOUT**, INPUT, **INSENSITIVE**, **INSERT**, INSTANCE, INSTANTIABLE, **INT**, **INTEGER**, **INTERSECT**, **INTERSECTION**, **INTERVAL**, **INTO**, INVOKER, **IS**, ISOLATION, JAVA, **JOIN**, K, KEY, KEY_MEMBER, KEY_TYPE, LABEL, **LANGUAGE**, **LARGE**, LAST, **LAST_VALUE**, **LATERAL**, **LEADING**, **LEFT**, LENGTH, LEVEL, LIBRARY, **LIKE**, **LIMIT**, **LN**, **LOCAL**, **LOCALTIME**, **LOCALTIMESTAMP**, LOCATOR, **LOWER**, M, MAP, **MATCH**, MATCHED, **MAX**, MAXVALUE, **MEMBER**, **MERGE**, MESSAGE_LENGTH, MESSAGE_OCTET_LENGTH, MESSAGE_TEXT, **METHOD**, MICROSECOND, MILLENNIUM, **MIN**, **MINUTE**, MINVALUE, **MOD**, **MODIFIES**, **MODULE**, **MONTH**, MORE, **MULTISET**, MUMPS, NAME, NAMES, **NATIONAL**, **NATURAL**, **NCHAR**, **NCLOB**, NESTING, **NEW**, **NEXT**, **NO**, **NONE**, **NORMALIZE**, NORMALIZED, **NOT**, **NULL**, NULLABLE, **NULLIF**, NULLS, NUMBER, **NUMERIC**, OBJECT, OCTETS, **OCTET_LENGTH**, **OF**, **OFFSET**, **OLD**, **ON**, **ONLY**, **OPEN**, OPTION, OPTIONS, **OR**, **ORDER**, ORDERING, ORDINALITY, OTHERS, **OUT**, **OUTER**, OUTPUT, **OVER**, **OVERLAPS**, **OVERLAY**, OVERRIDING, PAD, **PARAMETER**, PARAMETER_MODE, PARAMETER_NAME, PARAMETER_ORDINAL_POSITION, PARAMETER_SPECIFIC_CATALOG, PARAMETER_SPECIFIC_NAME, PARAMETER_SPECIFIC_SCHEMA, PARTIAL, **PARTITION**, PASCAL, PASSTHROUGH, PATH, **PERCENTILE_CONT**, **PERCENTILE_DISC**, **PERCENT_RANK**, PLACING, PLAN, PLI, **POSITION**, **POWER**, PRECEDING, **PRECISION**, **PREPARE**, PRESERVE, **PRIMARY**, PRIOR, PRIVILEGES, **PROCEDURE**, PUBLIC, QUARTER, **RANGE**, **RANK**, READ, **READS**, **REAL**, **RECURSIVE**, **REF**, **REFERENCES**, **REFERENCING**, **REGR_AVGX**, **REGR_AVGY**, **REGR_COUNT**, **REGR_INTERCEPT**, **REGR_R2**, **REGR_SLOPE**, **REGR_SXX**, **REGR_SXY**, **REGR_SYY**, RELATIVE, **RELEASE**, REPEATABLE, **RESET**, RESTART, RESTRICT, **RESULT**, **RETURN**, RETURNED_CARDINALITY, RETURNED_LENGTH, RETURNED_OCTET_LENGTH, RETURNED_SQLSTATE, **RETURNS**, **REVOKE**, **RIGHT**, ROLE, **ROLLBACK**, **ROLLUP**, ROUTINE, ROUTINE_CATALOG, ROUTINE_NAME, ROUTINE_SCHEMA, **ROW**, **ROWS**, ROW_COUNT, **ROW_NUMBER**, **SAVEPOINT**, SCALE, SCHEMA, SCHEMA_NAME, **SCOPE**, SCOPE_CATALOGS, SCOPE_NAME, SCOPE_SCHEMA, **SCROLL**, **SEARCH**, **SECOND**, SECTION, SECURITY, **SELECT**, SELF, **SENSITIVE**, SEQUENCE, SERIALIZABLE, SERVER, SERVER_NAME, SESSION, **SESSION_USER**, **SET**, SETS, **SIMILAR**, SIMPLE, SIZE, **SMALLINT**, **SOME**, SOURCE, SPACE, **SPECIFIC**, **SPECIFICTYPE**, SPECIFIC_NAME, **SQL**, **SQLEXCEPTION**, **SQLSTATE**, **SQLWARNING**, SQL_TSI_DAY, SQL_TSI_FRAC_SECOND, SQL_TSI_HOUR, SQL_TSI_MICROSECOND, SQL_TSI_MINUTE, SQL_TSI_MONTH, SQL_TSI_QUARTER, SQL_TSI_SECOND, SQL_TSI_WEEK, SQL_TSI_YEAR, **SQRT**, **START**, STATE, STATEMENT, **STATIC**, **STDDEV_POP**, **STDDEV_SAMP**, **STREAM**, STRUCTURE, STYLE, SUBCLASS_ORIGIN, **SUBMULTISET**, SUBSTITUTE, **SUBSTRING**, **SUM**, **SYMMETRIC**, **SYSTEM**, **SYSTEM_USER**, **TABLE**, **TABLESAMPLE**, TABLE_NAME, TEMPORARY, **THEN**, TIES, **TIME**, **TIMESTAMP**, TIMESTAMPADD, TIMESTAMPDIFF, **TIMEZONE_HOUR**, **TIMEZONE_MINUTE**, **TINYINT**, **TO**, TOP_LEVEL_COUNT, **TRAILING**, TRANSACTION, TRANSACTIONS_ACTIVE, TRANSACTIONS_COMMITTED, TRANSACTIONS_ROLLED_BACK, TRANSFORM, TRANSFORMS, **TRANSLATE**, **TRANSLATION**, **TREAT**, **TRIGGER**, TRIGGER_CATALOG, TRIGGER_NAME, TRIGGER_SCHEMA, **TRIM**, **TRUE**, TYPE, **UESCAPE**, UNBOUNDED, UNCOMMITTED, UNDER, **UNION**, **UNIQUE**, **UNKNOWN**, UNNAMED, **UNNEST**, **UPDATE**, **UPPER**, **UPSERT**, USAGE, **USER**, USER_DEFINED_TYPE_CATALOG, USER_DEFINED_TYPE_CODE, USER_DEFINED_TYPE_NAME, USER_DEFINED_TYPE_SCHEMA, **USING**, **VALUE**, **VALUES**, **VARBINARY**, **VARCHAR**, **VARYING**, **VAR_POP**, **VAR_SAMP**, VERSION, VIEW, WEEK, **WHEN**, **WHENEVER**, **WHERE**, **WIDTH_BUCKET**, **WINDOW**, **WITH**, **WITHIN**, **WITHOUT**, WORK, WRAPPER, WRITE, XML, **YEAR**, ZONE.
## 标识符
标识符是在 SQL 查询中使用的表名, 列, 和其他元数据元素.
未被引号括起来的标识符, 比如 emp, 必须以字母打头且只能包含字母, 数字, 下划线. 他们会隐式的转换为大写.
被引号引起来的标识符, 例如 `"Employee Name"`, 以双引号开始和结束. 他们可以包含几乎任何字符, 包括空白和其他标点. 如果你想在标识符中包含一个双引号, 使用双引号进行转义, 像这样: `"An employee called ""Fred""."`.
在 Calcite 中, 与引用的对象的名称匹配的标识符是大小写敏感的. 但是记住, 被引号括起来的标识符会在匹配前隐式转化为大写, 如果它引用的对象的名称是使用未被引号括起来的标识符创建的, 它的名称也会被转换成大写.
## 数据类型
### 标量类型
| Data type | Description | Range and examples |
| --- | --- | --- |
| BOOLEAN | Logical values | Values: TRUE, FALSE, UNKNOWN |
| TINYINT | 1 byte signed integer | Range is -255 to 256 |
| SMALLINT | 2 byte signed integer | Range is -32768 to 32767 |
| INTEGER, INT | 4 byte signed integer | Range is -2147483648 to 2147483647 |
| BIGINT | 8 byte signed integer | Range is -9223372036854775808 to 9223372036854775807 |
| DECIMAL(p, s) | Fixed point | Example: 123.45 is a DECIMAL(5, 2) value. |
| NUMERIC | Fixed point | |
| REAL, FLOAT | 4 byte floating point | 6 decimal digits precision |
| DOUBLE | 8 byte floating point | 15 decimal digits precision |
| CHAR(n), CHARACTER(n) | Fixed-width character string | 'Hello', '' (empty string), _latin1'Hello', n'Hello', _UTF16'Hello', 'Hello' 'there' (literal split into multiple parts) |
| VARCHAR(n), CHARACTER VARYING(n) | Variable-length character string | As CHAR(n) |
| BINARY(n) | Fixed-width binary string | x'45F0AB', x'' (empty binary string), x'AB' 'CD' (multi-part binary string literal) |
| VARBINARY(n), BINARY VARYING(n) | Variable-length binary string | As BINARY(n) |
| DATE | Date | Example: DATE '1969-07-20' |
| TIME | Time of day | Example: TIME '20:17:40' |
| TIMESTAMP [ WITHOUT TIME ZONE ] | Date and time | Example: TIMESTAMP '1969-07-20 20:17:40' |
| TIMESTAMP WITH TIME ZONE | Date and time with time zone | Example: TIMESTAMP '1969-07-20 20:17:40 America/Los Angeles' |
| INTERVAL timeUnit [ TO timeUnit ] | Date time interval | Examples: INTERVAL '1:5' YEAR TO MONTH, INTERVAL '45' DAY |
| Anchored interval | Date time interval | Example: (DATE '1969-07-20', DATE '1972-08-29') |
Where:
<figure class="highlight">
```
timeUnit:
MILLENNIUM | CENTURY | DECADE | YEAR | QUARTER | MONTH | WEEK | DOY | DOW | DAY | HOUR | MINUTE | SECOND | EPOCH
```
</figure>
注意:
* DATE, TIME, TIMESTAMP 是不带时区的. 也没有比如UTC(像Java那样)或者本地时区作为默认时区. 它需要用户或者应用提供时区的处理.
### 非标量类型
| 类型 | 描述 |
| --- | --- |
| ANY | 一个类型未知的值 |
| ROW | 一列或者多列组成的行 |
| MAP | 键值映射的集合 |
| MULTISET | 可能包含重复内容的未排序的集合 |
| ARRAY | 有序的,可能包含重复的连续集合 |
| CURSOR | 查询结果的游标 |
## 运算符和函数
### 运算符优先级
运算符优先级和结合性, 从高到低.
| 运算符 | 优先级 |
| --- | --- |
| . | 左 |
| [](array%20element) | 左 |
| + - (unary plus, minus) | 右 |
| * / | 左 |
| + - | 左 |
| BETWEEN, IN, LIKE, SIMILAR | - |
| < > = <= >= <> != | 左 |
| IS NULL, IS FALSE, IS NOT TRUE etc. | - |
| NOT | 右 |
| AND | 左 |
| OR | 左 |
### 比较运算符
| 语法 | 描述 |
| --- | --- |
| value1 = value2 | 相等 |
| value1 <> value2 | 不等 |
| value1 != value2 | 不等 (only available at some conformance levels) |
| value1 > value2 | 大于 |
| value1 >= value2 | 大于等于 |
| value1 < value2 | 小于 |
| value1 <= value2 | 小于等于 |
| value IS NULL | _value_ 是否为 null |
| value IS NOT NULL | _value_ 是否不为 null |
| value1 IS DISTINCT FROM value2 | 两个值是否不等, null 值认为是相等 |
| value1 IS NOT DISTINCT FROM value2 | 两个值是否相等, null 值认为是相等 |
| value1 BETWEEN value2 AND value3 | Whether _value1_ is greater than or equal to _value2_ and less than or equal to _value3_ |
| value1 NOT BETWEEN value2 AND value3 | _value1_ 是否小于 _value2_ 或大于 _value3_ |
| string1 LIKE string2 [ ESCAPE string3 ] | _string1_ 与模式 _string2_ 是否匹配 |
| string1 NOT LIKE string2 [ ESCAPE string3 ] | _string1_ 与模式 _string2_ 是否不匹配 |
| string1 SIMILAR TO string2 [ ESCAPE string3 ] | _string1_ 与正则 _string2_ 是否匹配 |
| string1 NOT SIMILAR TO string2 [ ESCAPE string3 ] | _string1_ 与正则 _string2_ 是否不匹配 |
| value IN (value [, value]* ) | _value_ 是否与列表中的每一个值都相等 |
| value NOT IN (value [, value]* ) | _value_ 是否与列表中的每一个值都不等 |
Not supported yet on Storm SQL: 目前 Storm SQL 中不支持的运算符:
| 语法 | 描述 |
| --- | --- |
| value IN (sub-query) | 是否 _value_ 等于 _sub-query_ 返回的行 |
| value NOT IN (sub-query) | 是否 _value_ 不等于 _sub-query_ 返回的行 |
| EXISTS (sub-query) | 是否 _sub-query_ 返回至少一个行 |
Storm SQL 当前不支持子查询, 因此上面的操作不能正常工作. 这个问题会在不久的将来修复.
### 逻辑运算符
| 语法 | 描述 |
| --- | --- |
| boolean1 OR boolean2 | 或 |
| boolean1 AND boolean2 | 且 |
| NOT boolean | 是否为True ; _boolean_ 为 UNKNOWN 则返回 UNKNOWN |
| boolean IS FALSE | 是否为False; _boolean_ 为 UNKNOWN 则返回 UNKNOWN |
| boolean IS NOT FALSE | 是否不为False; _boolean_ 为 UNKNOWN 则返回 UNKNOWN |
| boolean IS TRUE | 是否为True; _boolean_ 为 UNKNOWN 则返回 UNKNOWN |
| boolean IS NOT TRUE | 是否不为True; _boolean_ 为 UNKNOWN 则返回 UNKNOWN |
| boolean IS UNKNOWN | 判断是否是 UNKNOWN |
| boolean IS NOT UNKNOWN | 判断是否不是 UNKNOWN |
### 数学运算符和函数
| 语法 | 描述 |
| --- | --- |
| + numeric | 返回 _numeric_ |
| :- numeric | 返回负 _numeric_ |
| numeric1 + numeric2 | 返回 _numeric1_ 加 _numeric2_ |
| numeric1 - numeric2 | 返回 _numeric1_ 减 _numeric2_ |
| numeric1 * numeric2 | 返回 _numeric1_ 乘以 _numeric2_ |
| numeric1 / numeric2 | 返回 _numeric1_ 除以 _numeric2_ |
| POWER(numeric1, numeric2) | 返回 _numeric1_ 的 _numeric2_ 次方 |
| ABS(numeric) | 返回绝对值 _numeric_ |
| MOD(numeric, numeric) | 取余 _numeric1_ 除以 _numeric2_. 如果被除数为负, 则余数为负 |
| SQRT(numeric) | 返回平方根 _numeric_ |
| LN(numeric) | 返回 _numeric_ 的自然对数 (底数 _e_) |
| LOG10(numeric) | 返回 _numeric_ 的常用对数 (底数 10) |
| EXP(numeric) | 返回 _e_ 的 _numeric_ 次方 |
| CEIL(numeric) | 向上取整 _numeric_, 返回大于或者等于 _numeric_ 的最小整数 |
| FLOOR(numeric) | 向下取整 _numeric_, 返回小于或者等于 _numeric_ 的最小整数 |
### 字符串运算符和函数
| 语法 | 描述 |
| --- | --- |
| string || string | 连接2个字符串 |
| CHAR_LENGTH(string) | 返回字符串长度 |
| CHARACTER_LENGTH(string) | 与 CHAR_LENGTH(_string_) 等价 |
| UPPER(string) | 转换为大写 |
| LOWER(string) | 转换为小写 |
| POSITION(string1 IN string2) | 返回 _string1_ 在 _string2_ 中首次出现位置 |
| TRIM( { BOTH | LEADING | TRAILING } string1 FROM string2) | 从 _string2_ 的 头/尾/两头 移除 _string1_ 的最长匹配 |
| OVERLAY(string1 PLACING string2 FROM integer [ FOR integer2 ]) | 用 _string2_ 替换 _string1_ |
| SUBSTRING(string FROM integer) | 从给定的位置开始返回一个子串 |
| SUBSTRING(string FROM integer FOR integer) | 从给定位置返回一个指定长度的子串 |
| INITCAP(string) | 将 _string_ 中每个单词首字母大写其他字母小写. 单词是由空白字符分隔开的字符序列 |
未实现的:
* SUBSTRING(string FROM regexp FOR regexp)
### 二进制字符串操作符和函数
| 语法 | 描述 |
| --- | --- |
| binary || binary | 连接2个二进制字符串. |
| POSITION(binary1 IN binary2) | 返回二进制串 _binary1_ 在 _binary2_ 中首次出现位置 |
| OVERLAY(binary1 PLACING binary2 FROM integer [ FOR integer2 ]) | 替换 |
已知的 bug:
| 语法 | 描述 |
| --- | --- |
| SUBSTRING(binary FROM integer) | 从指定的位置截取 |
| SUBSTRING(binary FROM integer FOR integer) | 从指定位置截取指定长度的串 |
Calcite 1.9.0 有bug, 当编译 SUBSTRING 函数的时候会抛出异常. 这个问题会在后续版本中修复.
### Date/time 函数
| 语法 | 描述 |
| --- | --- |
| EXTRACT(timeUnit FROM datetime) | 返回时间中的指定字段 |
| FLOOR(datetime TO timeUnit) | 根据 timeUnit 向下取整 |
| CEIL(datetime TO timeUnit) | 根据 timeUnit 向上取整 |
未实现的:
* EXTRACT(timeUnit FROM interval)
* CEIL(interval)
* FLOOR(interval)
* datetime - datetime timeUnit [ TO timeUnit ]
* interval OVERLAPS interval
* + interval
* - interval
* interval + interval
* interval - interval
* interval / interval
* datetime + interval
* datetime - interval
Storm SQL 特有的:
| 语法 | 描述 |
| --- | --- |
| LOCALTIME | 以类型 TIME 返回当前会话时区的当前时间 |
| LOCALTIME(precision) | 以类型 TIME 返回当前会话时区的当前时间, 精度precision |
| LOCALTIMESTAMP | 以类型 TIMESTAMP 返回当前会话时区的当前时间 |
| LOCALTIMESTAMP(precision) | 以类型 TIMESTAMP 返回当前会话时区的当前时间, 精度precision |
| CURRENT_TIME | 以类型 TIMESTAMP WITH TIME ZONE 返回当前会话时区的当前时间 |
| CURRENT_DATE | 以类型 DATE 返回当前会话时区的当前时间 |
| CURRENT_TIMESTAMP | 以类型 TIMESTAMP WITH TIME ZONE 返回当前会话时区的当前时间 |
SQL标准规定,上述运算符在评估查询时应返回相同的值。 Storm SQL将每个查询转换为Trident拓扑并运行,因此在评估SQL语句时,技术上当前的日期/时间应该是固定的。 由于这个限制,当创建Trident拓扑时,当前日期/时间将被修复,并且这些运算符应该在拓扑生命周期中返回相同的值。
### 系统函数
Storm SQL 当前不支持的函数:
| 语法 | 描述 |
| --- | --- |
| USER | 等价于CURRENT_USER |
| CURRENT_USER | 当前执行上下文的用户名 |
| SESSION_USER | 会话的用户名 |
| SYSTEM_USER | 返回系统标识的当前数据存储的用户 user |
| CURRENT_PATH | 返回表示当前查找范围的字符串,用于引用用户定义的例程和类型 |
| CURRENT_ROLE | 返回当前活动 role |
这些操作符并不代表 Storm SQL 的运行时,所以除非我们找到正确的语义,否则这些操作可能永远不会被支持。
### 条件函数和操作符
| 语法 | 描述 |
| --- | --- |
| CASE value
WHEN value1 [, value11 ]* THEN result1
[ WHEN valueN [, valueN1 ]* THEN resultN ]*
[ ELSE resultZ ]
END | 简单 case 语句 |
| CASE
WHEN condition1 THEN result1
[ WHEN conditionN THEN resultN ]*
[ ELSE resultZ ]
END | 搜索 case 语句 |
| NULLIF(value, value) | 值相同返回.
例如, `NULLIF(5, 5)` 返回 NULL; `NULLIF(5, 0)` 返回 5. |
| COALESCE(value, value [, value ]* ) | 前一个值为 null 则返回后一个值.
例如, `COALESCE(NULL, 5)` 返回 5. |
### 类型转换
| 语法 | 描述 |
| --- | --- |
| CAST(value AS type) | 把值转换为给定的类型. |
### 值构造器
| 语法 | 描述 |
| --- | --- |
| ROW (value [, value]* ) | 从值列表中创建一个行. |
| map '[' key ']' | 根据 key 返回 value. |
| array '[' index ']' | 返回某个位置的 array 的元素值. |
| ARRAY '[' value [, value ]* ']' | 从值列表中创建一个 array. |
| MAP '[' key, value [, key, value ]* ']' | 从 key-value 列表中创建一个 map. |
### 集合函数
| 语法 | 描述 |
| --- | --- |
| ELEMENT(value) | 返回 array 或 multiset 的唯一元素; 如果集合为空,则为null; 如果它有多个元素,则抛出异常。 |
| CARDINALITY(value) | 返回 array 或 multiset 的元素数. |
另请参考: UNNEST关系运算符将集合转换为关系.
### JDBC 函数转义
#### 数字
| 语法 | 描述 |
| --- | --- |
| {fn ABS(numeric)} | 返回 _numeric_ 的绝对值 |
| {fn EXP(numeric)} | 返回 _e_ 的 _numeric_ 次方 |
| {fn LOG(numeric)} | 返回 _numeric_ 的自然对数 (底数为 _e_) |
| {fn LOG10(numeric)} | 返回 _numeric_ 的以10为底的对数 |
| {fn MOD(numeric1, numeric2)} | 返回 _numeric1_ 被 _numeric2_ 除的余数. 被除数 _numeric1_ 为负数的时候余数为负 |
| {fn POWER(numeric1, numeric2)} | 返回 _numeric1_ 的 _numeric2_ 次方 |
未实现的:
* {fn ACOS(numeric)} - Returns the arc cosine of _numeric_
* {fn ASIN(numeric)} - Returns the arc sine of _numeric_
* {fn ATAN(numeric)} - Returns the arc tangent of _numeric_
* {fn ATAN2(numeric, numeric)}
* {fn CEILING(numeric)} - Rounds _numeric_ up, and returns the smallest number that is greater than or equal to _numeric_
* {fn COS(numeric)} - Returns the cosine of _numeric_
* {fn COT(numeric)}
* {fn DEGREES(numeric)} - Converts _numeric_ from radians to degrees
* {fn FLOOR(numeric)} - Rounds _numeric_ down, and returns the largest number that is less than or equal to _numeric_
* {fn PI()} - Returns a value that is closer than any other value to _pi_
* {fn RADIANS(numeric)} - Converts _numeric_ from degrees to radians
* {fn RAND(numeric)}
* {fn ROUND(numeric, numeric)}
* {fn SIGN(numeric)}
* {fn SIN(numeric)} - Returns the sine of _numeric_
* {fn SQRT(numeric)} - Returns the square root of _numeric_
* {fn TAN(numeric)} - Returns the tangent of _numeric_
* {fn TRUNCATE(numeric, numeric)}
#### 字符串
| 字符串 | 描述 |
| --- | --- |
| {fn CONCAT(character, character)} | 连接字符串 |
| {fn LOCATE(string1, string2)} | 返回 _string2_ 在 _string1_ 中首次出现的位置. 如果指定了 _integer_ , 则从 _integer_ 为起点开搜索. |
| {fn INSERT(string1, start, length, string2)} | 把 _string2_ 插入 _string1_ |
| {fn LCASE(string)} | 返回小写 |
| {fn LENGTH(string)} | 返回字符数 |
| {fn SUBSTRING(string, offset, length)} | 字符串截取, 从 offset 的位置开始截取,长度为 length |
| {fn UCASE(string)} | 返回大写 |
已知的bug:
| 语法 | 描述 |
| --- | --- |
| {fn LOCATE(string1, string2 [, integer])} | 返回 _string2_ 在 _string1_ 中首次出现的位置. 如果指定了 _integer_ , 则从 _integer_ 为起点开搜索. |
| {fn LTRIM(string)} | 移除 _string_ 头部的空白字符 |
| {fn RTRIM(string)} | 移除 _string_ 尾部的空白字符 |
Calcite 1.9.0 在函数使用位置参数的时候会抛出异常, {fn LTRIM} 和 {fn RTRIM} 在编译 SQL 语句的时候. 这个能在将来的版本中修复.
未实现的:
* {fn ASCII(string)} - 转换单个字符的字符串为 ASCII 码, 为 0 - 255 之间的整数
* {fn CHAR(string)}
* {fn DIFFERENCE(string, string)}
* {fn LEFT(string, integer)}
* {fn REPEAT(string, integer)}
* {fn REPLACE(string, string, string)}
* {fn RIGHT(string, integer)}
* {fn SOUNDEX(string)}
* {fn SPACE(integer)}
#### Date/time
| 语法 | 描述 |
| --- | --- |
| {fn CURDATE()} | 等价于 `CURRENT_DATE` |
| {fn CURTIME()} | 等价于 `LOCALTIME` |
| {fn NOW()} | 等价于 `LOCALTIMESTAMP` |
| {fn QUARTER(date)} | 等价于 `EXTRACT(QUARTER FROM date)`. 返回 1 和 4 之间的整数. |
| {fn TIMESTAMPADD(timeUnit, count, timestamp)} | 添加 _count_ 个 _timeUnit_ 间隔到 timestamp |
| {fn TIMESTAMPDIFF(timeUnit, timestamp1, timestamp2)} | _timestamp1_ 减去 _timestamp2_ 结果存在 _timeUnit_ 中 |
未实现的:
* {fn DAYNAME(date)}
* {fn DAYOFMONTH(date)}
* {fn DAYOFWEEK(date)}
* {fn DAYOFYEAR(date)}
* {fn HOUR(time)}
* {fn MINUTE(time)}
* {fn MONTH(date)}
* {fn MONTHNAME(date)}
* {fn SECOND(time)}
* {fn WEEK(date)}
* {fn YEAR(date)}
#### 系统
未实现的:
* {fn DATABASE()}
* {fn IFNULL(value, value)}
* {fn USER(value, value)}
* {fn CONVERT(value, type)}
### 聚合函数(aggregrate functions)
Storm SQL 当前不支持聚合函数.
### 窗口函数(windowing functions)
Storm SQL 当前不支持窗口函数
### 分组函数(grouping functions)
Storm SQL 不支持分组函数
### 用户定义函数(User-defined functions)
用户可以使用 `CREATE FUNCTION` 语句定义 user defined function(标量). 例如, 下面的语句使用 `org.apache.storm.sql.TestUtils$MyPlus` 类定义了一个 `MYPLUS` 函数.
```
CREATE FUNCTION MYPLUS AS 'org.apache.storm.sql.TestUtils$MyPlus'
```
Storm SQL 通过检验定义的个方法来确定函数是标量还是聚合. 如果类中定义了一个 `evaluate` 方法, Storm SQL 把这个函数作为 `标量` 对待.
标量函数的类的例子:
```
public class MyPlus {
public static Integer evaluate(Integer x, Integer y) {
return x + y;
}
}
```
请注意, 用户在运行 Storm SQL runner 时候应当使用 `--jars` 或者 `--artifacts`, 来确保 UDFs 在 classpath 路径下可见.
## 外部数据源
### 指定外部数据源
在 StormSQL 中数据表现为一个外部表. 用户可以使用 `CREATE EXTERNAL TABLE` 语句指定数据源. `CREATE EXTERNAL TABLE` 的语法与 [Hive 数据定义语言](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL) 定义的语法紧密相关.
```
CREATE EXTERNAL TABLE table_name field_list
[ STORED AS
INPUTFORMAT input_format_classname
OUTPUTFORMAT output_format_classname
]
LOCATION location
[ TBLPROPERTIES tbl_properties ]
[ AS select_stmt ]
```
默认输入格式和输出格式都是 JSON. 我们会在将来的章节介绍 `supported formats`.
例如, 下面的语句指定了一个 Kafka spout 和 sink:
```
CREATE EXTERNAL TABLE FOO (ID INT PRIMARY KEY) LOCATION 'kafka://localhost:2181/brokers?topic=test' TBLPROPERTIES '{"producer":{"bootstrap.servers":"localhost:9092","acks":"1","key.serializer":"org.apache.org.apache.storm.kafka.IntSerializer","value.serializer":"org.apache.org.apache.storm.kafka.ByteBufferSerializer"}}'
```
请注意, 用户在运行 Storm SQL runner 时候应当使用 `--jars` 或者 `--artifacts`, 来确保 UDFs 在 classpath 可见.
### 植入外部数据源
用户通过实现 `ISqlTridentDataSource` 接口并使用 Java 的 service loader 机制进行注册,以植入外部数据源. 基于表的 URI 的模式来选择外部数据源. 更多细节请参考 `storm-sql-kafka` 的实现.
### 支持的格式
| 格式 | 输入格式类 | 输出格式类 | 需要属性 |
| --- | --- | --- | --- |
| JSON | org.apache.storm.sql.runtime.serde.json.JsonScheme | org.apache.storm.sql.runtime.serde.json.JsonSerializer | No |
| Avro | org.apache.storm.sql.runtime.serde.avro.AvroScheme | org.apache.storm.sql.runtime.serde.avro.AvroSerializer | Yes |
| CSV | org.apache.storm.sql.runtime.serde.csv.CsvScheme | org.apache.storm.sql.runtime.serde.csv.CsvSerializer | No |
| TSV | org.apache.storm.sql.runtime.serde.tsv.TsvScheme | org.apache.storm.sql.runtime.serde.tsv.TsvSerializer | No |
#### Avro
Avro 需要用户描述记录的模式(输入和输出). 模式应该在 `TBLPROPERTIES` 上描述. 输入格式需要描述给 `input.avro.schema`, 输出格式需要描述给 `output.avro.schema`. 模式字符串应当是一个转义后的 JSON, 因此 `TBLPROPERTIES` 是有效的 JSON.
示例 Schema 定义:
`"input.avro.schema": "{\"type\": \"record\", \"name\": \"large_orders\", \"fields\" : [ {\"name\": \"ID\", \"type\": \"int\"}, {\"name\": \"TOTAL\", \"type\": \"int\"} ]}"`
`"output.avro.schema": "{\"type\": \"record\", \"name\": \"large_orders\", \"fields\" : [ {\"name\": \"ID\", \"type\": \"int\"}, {\"name\": \"TOTAL\", \"type\": \"int\"} ]}"`
#### CSV
使用 [Standard RFC4180](https://tools.ietf.org/html/rfc4180) CSV Parser, 不需要任何其他的属性.
#### TSV
默认情况下, TSV 使用 `\t` 作为分隔符, 但是用户可以通过 `input.tsv.delimiter` 或者 `output.tsv.delimiter` 设置其他的分隔符.
### 可支持的数据源
| 数据源 | Artifact Name | 位置前缀 | 支持输入数据源 | 支持输出数据源 | 需要属性 |
| --- | --- | --- | --- | --- | --- |
| Socket | | `socket://host:port` | Yes | Yes | No |
| Kafka | org.apache.storm:storm-sql-kafka | `kafka://zkhost:port/broker_path?topic=topic` | Yes | Yes | Yes |
| Redis | org.apache.storm:storm-sql-redis | `redis://:[password]@host:port/[dbIdx]` | No | Yes | Yes |
| MongoDB | org.apache.stormg:storm-sql-mongodb | `mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]` | No | Yes | Yes |
| HDFS | org.apache.storm:storm-sql-hdfs | `hdfs://host:port/path-to-file` | No | Yes | Yes |
#### Socket
Socket 数据源是一个内置的特性, 因此用户无需在 `--artifacts` 选项中添加任何依赖.
请注意, Socket 数据源只是用于测试: 不能保证 exactly-once 和 at-least-once.
贴士: `netcat` 是一个 Socket 便捷工具: 用户可以使用 netcat 连接 Socket 数据源, 既可以作为输入也可以用作输出.
#### Kafka
Kafka 仅当用作输出数据源的时候需要定义下列属性:
* `producer`: 指定 Kafka Producer 配置 - 更多细节请参考 [Kafka producer configs](http://kafka.apache.org/documentation.html#producerconfigs).
* `bootstrap.servers` 必须在 `producer` 中定义这个值
请注意, `storm-sql-kafka` 需要用户提供 `storm-kafka` 依赖, `storm-kafka` 又依赖于 `kafka` , `kafka-clients`. 你可以在 `--artifacts` 选项中使用下列的工作引用, 并且在需要的时候修改依赖的版本
`org.apache.storm:storm-sql-kafka:2.0.0-SNAPSHOT,org.apache.storm:storm-kafka:2.0.0-SNAPSHOT,org.apache.kafka:kafka_2.10:0.8.2.2^org.slf4j:slf4j-log4j12,org.apache.kafka:kafka-clients:0.8.2.2`
#### Redis
Redis 数据源需要设置下列属性:
* `data.type`: 用于存储的数据类型 - 仅支持 `"STRING"` 和 `"HASH"`
* `data.additional.key`: key, 当数据类型同时需要 key 和 field 时设置 (field 作为字段使用)
* `redis.timeout`: 超时时间, 毫秒 (ex. `"3000"`)
* `use.redis.cluster`: 如果 Redis 是集群环境为 `"true"`, 否则为 `"false"`.
请注意, `storm-sql-redis` 需要用户提供 `storm-redis` 依赖. 你可以在 `--artifacts` 选项中使用下列的工作引用, 并且在需要的时候修改依赖的版本
`org.apache.storm:storm-sql-redis:2.0.0-SNAPSHOT,org.apache.storm:storm-redis:2.0.0-SNAPSHOT`
#### MongoDB
MongoDB 数据源需要设置以下属性
`{"collection.name": "storm_sql_mongo", "trident.ser.field": "serfield"}`
* `trident.ser.field`: 存储字段 - 记录会序列化并以 BSON 存储在字段中
* `collection.name`: 集合名称
请注意, `storm-sql-mongodb` 需要用户提供 `storm-mongodb` 依赖. 你可以在 `--artifacts` 选项中使用下列的工作引用, 并且在需要的时候修改依赖的版本
`org.apache.storm:storm-sql-mongodb:2.0.0-SNAPSHOT,org.apache.storm:storm-mongodb:2.0.0-SNAPSHOT`
当前不支持使用保留字段存储.
#### HDFS
HDFS 数据源需要设置下列属性
* `hdfs.file.path`: HDFS 文件路径
* `hdfs.file.name`: HDFS 文件名 - 参考 [SimpleFileNameFormat](http://github.com/apache/storm/blob/master%0A/external/storm-hdfs/src/main/java/org/apache/storm/hdfs/trident/format/SimpleFileNameFormat.java)
* `hdfs.rotation.size.kb`: HDFS FileSizeRotationPolicy 单位 KB
* `hdfs.rotation.time.seconds`: HDFS TimedRotationPolicy 单位 seconds
请注意 `hdfs.rotation.size.kb` 和 `hdfs.rotation.time.seconds` 只能采用其中一种来实现文件滚动.
还要注意 `storm-sql-hdfs` 需要用户提供 `storm-hdfs` 依赖. 你可以在 `--artifacts` 选项中使用下列的工作引用, 并且在需要的时候修改依赖的版本
`org.apache.storm:storm-sql-hdfs:2.0.0-SNAPSHOT,org.apache.storm:storm-hdfs:2.0.0-SNAPSHOT`
还有, 需要提供 hdfs 配置文件. 可以将 `core-site.xml` 和 `hdfs-site.xml` 文件放到 Storm 安装目录的 `conf` 目录下面.
- Storm 基础
- 概念
- Scheduler(调度器)
- Configuration
- Guaranteeing Message Processing
- 守护进程容错
- 命令行客户端
- Storm UI REST API
- 理解 Storm Topology 的 Parallelism(并行度)
- FAQ
- Layers on Top of Storm
- Storm Trident
- Trident 教程
- Trident API 综述
- Trident State
- Trident Spouts
- Trident RAS API
- Storm SQL
- Storm SQL 集成
- Storm SQL 示例
- Storm SQL 语言参考
- Storm SQL 内部实现
- Flux
- Storm 安装和部署
- 设置Storm集群
- 本地模式
- 疑难解答
- 在生产集群上运行 Topology
- Maven
- 安全地运行 Apache Storm
- CGroup Enforcement
- Pacemaker
- 资源感知调度器 (Resource Aware Scheduler)
- 用于分析 Storm 的各种内部行为的 Metrics
- Windows 用户指南
- Storm 中级
- 序列化
- 常见 Topology 模式
- Clojure DSL
- 使用没有jvm的语言编辑storm
- Distributed RPC
- Transactional Topologies
- Hooks
- Storm Metrics
- Storm 状态管理
- Windowing Support in Core Storm
- Joining Streams in Storm Core
- Storm Distributed Cache API
- Storm 调试
- 动态日志级别设置
- Storm Logs
- 动态员工分析
- 拓扑事件检查器
- Storm 与外部系统, 以及其它库的集成
- Storm Kafka Integration
- Storm Kafka 集成(0.10.x+)
- Storm HBase Integration
- Storm HDFS Integration
- Storm Hive 集成
- Storm Solr 集成
- Storm Cassandra 集成
- Storm JDBC 集成
- Storm JMS 集成
- Storm Redis 集成
- Azue Event Hubs 集成
- Storm Elasticsearch 集成
- Storm MQTT(Message Queuing Telemetry Transport, 消息队列遥测传输) 集成
- Storm MongoDB 集成
- Storm OpenTSDB 集成
- Storm Kinesis 集成
- Storm Druid 集成
- Storm and Kestrel
- Container, Resource Management System Integration
- Storm 高级
- 针对 Storm 定义一个不是 JVM 的 DSL
- 多语言协议
- Storm 内部实现
- 翻译进度