之前有分享过一篇笔记:Spark sql规则执行器RuleExecutor(源码解析) 里面有提到Analyzer、Optimizer定义了一系列 rule。
其中Analyzer定义了从【未解析的逻辑执行计划】生成【解析后的逻辑执行计划】的一系列规则,这篇笔记整理了一下这些规则都哪些。
基于spark3.2 branch
rule【规则】 | batch【表示一组同类的规则】 | strategy【迭代策略】 | 注释 |
---|---|---|---|
OptimizeUpdateFields | Substitution | fixedPoint | 此规则优化了“UpdateFields”表达式链,因此看起来更像优化规则。然而,当操作深度嵌套的模式时,`UpdateFields`表达式树可能非常复杂,无法进行分析。因此,我们需要在分析之初就优化“UpdateFields”。 |
CTESubstitution | Substitution | fixedPoint | 根据以下条件,使用节点进行分析,并用CTE参考或CTE定义替换子计划:1.如果处于传统模式,或者如果查询是SQL命令或DML语句,请替换为CTE定义,即内联CTE。2.否则,替换为CTE references`ctrelationref`s。在查询分析之后,将由规则`InlineCTE`决定是否内联。对于每个主查询和子查询,此替换后未内联的所有CTE定义都将分组在一个`WithCTE`节点下。任何不包含CTE或已内联所有CTE的主查询或子查询显然都不会有任何`WithCTE`节点。如果有的话,“WithCTE”节点将与最外层的“With”节点所在的位置相同。“WithCTE”节点中的CTE定义按解析顺序保存。这意味着,根据CTE定义对任何有效CTE查询的依赖性,可以保证CTE定义按拓扑顺序排列(即,给定CTE定义A和B,B引用A,A保证出现在B之前)。否则,它必须是无效的用户查询,关系解析规则稍后将抛出分析异常。 |
WindowsSubstitution | Substitution | fixedPoint | 用 WindowSpecDefinitions 替代子计划,其中 WindowSpecDefinition 代表的是窗口函数的规范。 |
EliminateUnions | Substitution | fixedPoint | 如果只有一个子项,则从计划中删除 Union 算子 |
SubstituteUnresolvedOrdinals | Substitution | fixedPoint | 将“order by”或“group by”中的序号替换为UnresolvedOrdinal表达式,其中UnresolvedOrdinal表示按order by或group by使用的未解析序号。例如:select a from table order by 1 和 select a from table group by 1 |
ResolveHints.DisableHints | Disable Hints | Once | 当配置项spark.sql.optimizer.disableHints被设置时删除spark时的所有hints。这将在Analyzer的最开始执行,以禁用hints功能。 |
ResolveHints.ResolveJoinStrategyHints | Hints | fixedPoint | 允许的join策略hint列表在JoinStrategyHint.strategies中定义。可以使用join策略hint指定一系列关系别名,例如“MERGE(a, c)”、“BROADCAST(a)”。join策略hint计划节点将插入到与指定名称匹配的任何关系(别名不同)、子查询或公共表表达式的顶部。hint解析的工作原理是递归遍历查询计划,找到与指定关系别名之一匹配的关系或子查询。遍历不会超出任何视图引用,包括子句或子查询别名。此规则必须发生在公共表表达式之前。允许的join策略有:1.BROADCAST(“BROADCAST”,“BROADCASTJOIN”,“MAPJOIN”)2.SHUFFLE_MERGE(“SHUFFLE_MERGE”,“MERGE”,“MERGEJOIN”)3.SHUFFLE_HASH(“SHUFFLE_HASH”)4.SHUFFLE_REPLICATE_NL(“SHUFFLE_REPLICATE_NL”),括号外为所属类别,括号为具体的hint字符串 |
ResolveHints.ResolveCoalesceHints | Hints | fixedPoint | COALESCE Hint允许下面几个名字:“COALESCE”,“REPARTITION”,“REPARTITION_BY_RANGE” |
LookupFunctions | Simple Sanity Check | Once | 检查未解析函数引用的函数标识符是否在函数注册表中定义。请注意,此规则不会尝试解析UnsolvedFunction。它只根据函数标识符执行简单的存在性检查,以快速识别未定义的函数,而不触发关系解析,这在某些情况下可能会导致潜在的昂贵的分区/schema发现过程。为了避免重复的外部函数查找,外部函数标识符将存储在本地哈希集externalFunctionNameSet中。 |
ResolveTableValuedFunctions(v1SessionCatalog) | Resolution | fixedPoint | 解析表值函数引用的规则。 |
ResolveNamespace(catalogManager) | Resolution | fixedPoint | 解析诸如SHOW TABLES、SHOW FUNCTIONS之类的规则。SHOW TABLES/SHOW TABLE EXTENDED/SHOW VIEWS/SHOW FUNCTIONS/ANALYZE TABLES |
ResolveCatalogs(catalogManager) | Resolution | fixedPoint | 从SQL语句中的多部分标识符解析catalog,如果解析的catalog不是session catalog,则将语句转换为相应的v2命令。 |
ResolveUserSpecifiedColumns | Resolution | fixedPoint | 解析用户指定的列。 |
ResolveInsertInto | Resolution | fixedPoint | 解析INSERT INTO语句。 |
ResolveRelations | Resolution | fixedPoint | 用catalog中的具体关系替换未解析的关系(表和视图),比如:包含UnresolvedRelation节点 |
ResolveTables | Resolution | fixedPoint | 用v2目录中的具体关系解析表关系。[[ResolveRelations]]仍然解析v1表。 |
ResolvePartitionSpec | Resolution | fixedPoint | 在分区相关命令中将UnresolvedPartitionSpec解析成ResolvedPartitionSpec。 |
ResolveAlterTableCommands | Resolution | fixedPoint | 根据alter table column命令的大小写敏感度,主要解析、规范化和重写列名的规则。 |
AddMetadataColumns | Resolution | fixedPoint | 当节点缺少已解析属性时,将元数据列添加到子关系的输出中。使用LogicalPlan.metadataOutput中的列解析对元数据列的引用。但在替换关系之前,关系的输出不包括元数据列。除非此规则将元数据添加到关系的输出中,否则analyzer将检测到没有任何内容生成列。此规则仅在节点已解析但缺少来自其子节点的输入时添加元数据列。这可以确保元数据列不会添加到计划中,除非使用它们。通过只检查已解析的节点,这可以确保已完成 * 扩展,以便 * 不会意外选择元数据列。此规则将运算符解析为向下,以避免过早地投射元数据列。 |
DeduplicateRelations | Resolution | fixedPoint | 删除LogicalPlan的任何重复关系。 |
ResolveReferences | Resolution | fixedPoint | 将UnresolvedAttribute替换为逻辑计划节点子节点的具体AttributeReference。 |
ResolveExpressionsWithNamePlaceholders | Resolution | fixedPoint | 解析包含名称占位符的表达式。 |
ResolveDeserializer | Resolution | fixedPoint | 将UnsolvedDeserializer替换为已解析为给定输入属性的反序列化表达式。 |
ResolveNewInstance | Resolution | fixedPoint | 如果要构造的对象是内部类,则通过查找外部作用域并向其添加外部作用域来解析NewInstance。 |
ResolveUpCast | Resolution | fixedPoint | 用Cast替换UpCast,如果转换可能会截断,则抛出异常。 |
ResolveGroupingAnalytics | Resolution | fixedPoint | 解析grouping函数。 |
ResolvePivot | Resolution | fixedPoint | 解析pivot(行转列) |
ResolveOrdinalInOrderByAndGroupBy | Resolution | fixedPoint | 在SQL的许多方言中,在order/sort by和group by子句中使用的顺序位置是有效的。此规则用于将序号位置转换为选择列表中的相应表达式。Spark 2.0中引入了这种支持。如果排序引用或分组依据表达式不是整数而是可折叠表达式,请忽略它们。当spark.sql.orderByOrdinal/spark.sql.groupByOrdinal设置为false,也忽略位置号。在Spark 2.0发布之前,order/sort by和group by子句中的字符对结果没有影响。 |
ResolveAggAliasInGroupBy | Resolution | fixedPoint | 将分组键中未解析的表达式替换为SELECT子句中已解析的表达式。此规则应在ResolveReferences应用之后运行。 |
ResolveMissingReferences | Resolution | fixedPoint | 在SQL的许多方言中,按SELECT子句中不存在的属性进行排序是有效的。此规则检测此类查询,并将所需属性添加到原始投影中,以便在排序过程中可用。添加另一个投影以在排序后删除这些属性。HAVING子句还可以使用SELECT中未显示的分组列。 |
ExtractGenerator | Resolution | fixedPoint | 从Project操作符的Project列表中提取Generator,并在Project下创建Generate操作符。此规则将在以下情况下引发AnalysisException:1.生成器嵌套在表达式中,例如SELECT explode(list) 1 FROM tbl。2.projectList中有多个生成器,例如SELECT explode(list), explode(list) FROM tbl。3.生成器可在其他非Project或Generate的运算符中找到,例如SELECT * FROM tbl SORT BY explode(list)。 |
ResolveGenerate | Resolution | fixedPoint | 重写表,生成需要以下一个或多个表达式才能解析的表达式:其输出的具体属性引用。从SELECT子句(即从Project)重新定位到Generate子句中。输出Attribute的名称是从封装Generator的Alias或MultiAlias表达式中提取的。 |
ResolveFunctions | Resolution | fixedPoint | 用具体的LogicalPlan替换UnresolvedFunc,用具体的Expression替换UnresolvedFunction。 |
ResolveAliases | Resolution | fixedPoint | 用具体的别名替换UnresolvedAlias。 |
ResolveSubquery | Resolution | fixedPoint | 此规则解析并重写表达式内的子查询。注:CTE在CTESubstitution中处理。 |
ResolveSubqueryColumnAliases | Resolution | fixedPoint | 用投影替换子查询的未解析列别名。 |
ResolveWindowOrder | Resolution | fixedPoint | 检查并添加顺序到 AggregateWindowFunction |
ResolveWindowFrame | Resolution | fixedPoint | 检查并为所有窗口功能添加适当的窗口框架 |
ResolveNaturalAndUsingJoin | Resolution | fixedPoint | 通过基于两侧的输出计算输出列来删除natural join或using join,然后在普通join上应用投影以消除natural join或using join。 |
ResolveOutputRelation | Resolution | fixedPoint | 从逻辑计划中的数据解析输出表的列。这条规则将会:1.按名称写入时对列重新排序;2.数据类型不匹配时插入强制转换;3.列名不匹配时插入别名;4.检测与输出表不兼容的计划并引发AnalysisException |
ExtractWindowExpressions | Resolution | fixedPoint | 从Project运算符的projectList和聚合运算符的aggregateExpressions中提取WindowExpressions,并为每个不同的WindowsSpecDefinition创建单独的窗口运算符。这条规则处理三种情况:1.Project列表中有WindowExpressions的Project;2.在其aggregateExpressions中包含WindowExpressions的聚合。3.一个Filter->Aggregate 模式代表HAVING子句表示GROUP BY,Aggregate 在其aggregateExpressions中有WindowExpressions。 |
GlobalAggregates | Resolution | fixedPoint | 将包含聚合表达式的投影转换为聚合。 |
ResolveAggregateFunctions | Resolution | fixedPoint | 此规则查找不在聚合运算符中的聚合表达式。例如,HAVING子句或ORDER BY子句中的那些。这些表达式被下推到基础聚合运算符,然后在原始运算符之后投影出去。 |
TimeWindowing | Resolution | fixedPoint | 使用“Expand”操作符将时间列映射到多个时间窗口。由于计算一个时间列可以映射到多少个窗口是非常重要的,因此我们高估了窗口的数量,并过滤掉时间列不在时间窗口内的行。 |
SessionWindowing | Resolution | fixedPoint | 将时间列匹配到会话窗口。 |
ResolveInlineTables | Resolution | fixedPoint | 使用LocalRelation替换UnresolvedInlineTable |
ResolveHigherOrderFunctions(catalogManager) | Resolution | fixedPoint | 从目录中解析高阶函数。这与常规函数解析不同,因为lambda函数只能在函数解析后解析;所以当所有子函数都是解析的或者是一个lambda函数时,我们需要解析高阶函数。 |
ResolveLambdaVariables | Resolution | fixedPoint | 解析高阶函数公开的lambda变量。此规则分为两个步骤:1.将高阶函数公开的匿名变量绑定到lambda函数的参数;这将创建命名和类型化的lambda变量。在此步骤中,将检查参数名称是否重复,并检查参数的数量。2.解析lambda函数的函数表达式树中使用的lambda变量。请注意,我们允许使用当前lambda之外的变量,这可以是在外部范围中定义的lambda函数,也可以是由计划的子级生成的属性。如果名称重复,则使用最内部作用域中定义的名称。 |
ResolveTimeZone | Resolution | fixedPoint | 将不带时区id的TimeZoneAwareExpression替换为会话本地时区的副本。 |
ResolveRandomSeed | Resolution | fixedPoint | 设置随机数生成的种子。 |
ResolveBinaryArithmetic | Resolution | fixedPoint | 关于加法:1.如果两边都是间隔,保持不变;2.否则,如果一边是日期,另一边是间隔,则将其转换为DateAddInterval;3.否则,如果一侧为interval,则将其转换为TimeAdd;4.否则,如果一面是date,则将其改为DateAdd;5.其他方面不变。关于减法:1.如果两边都是间隔,保持不变;2.否则,如果左侧为日期,右侧为间隔,则将其转换为DateAddInterval(l, -r);3.否则,如果右侧是区间,则将其转换为TimeAdd(l, -r);4.否则,如果一面是时间戳,则将其转换为SubtractTimestamps;5.否则,如果右边是date,则将其转换为DateDiff/Subtract Dates;6.否则,如果左侧是date,则将其转换为DateSub;7.否则,它将保持不变。关于乘法:1。如果一侧为间隔,则将其转换为MultiplyInterval;2.否则,将保持不变。关于除法:1。如果左侧为interval,则将其转为DivideInterval;2.否则,将保持不变。 |
ResolveUnion | Resolution | fixedPoint | 将union的不同子级解析为一组公共列。 |
typeCoercionRules | Resolution | fixedPoint | 当spark.sql.ansi.enabled设置为 true 的时候,采取 ANSI 的方式进行解析,这代表的是一组解析规则。 |
ResolveWithCTE | Resolution | fixedPoint | 使用相应CTE定义的resolve output属性更新CTE引用。 |
extendedResolutionRules | Resolution | fixedPoint | 方便重写来提供额外的规则。 |
RemoveTempResolvedColumn | Remove TempResolvedColumn | Once | 删除查询计划中的所有TempResolvedColumn。这是最后一种手段,以防主解析批处理中的某些规则无法删除TempResolvedColumn。我们应该在主解析批处理之后立即运行此规则。 |
ApplyCharTypePadding | Apply Char Padding | Once | 此规则为字符类型比较执行字符串填充。当比较char类型的列/字段与string literal或char类型的列/字段时,右键将较短的列/字段填充为较长的列/字段。 |
ResolveCommandsWithIfExists | Post-Hoc Resolution | Once | 表或临时视图未解析时处理命令的规则。这些命令支持一个标志“ifExists”,以便在关系未解决时不会失败。如果“ifExists”标志设置为true,逻辑计划会被解析成NoopCommand。 |
postHocResolutionRules | Post-Hoc Resolution | Once | 方便重写以提供进行事后解决的规则。请注意,这些规则将在单个批次中执行。该批处理将在正常解析批处理之后运行,并一次性执行其规则。 |
ResolveHints.RemoveAllHints | Remove Unresolved Hints | Once | 删除所有hints,用于删除用户提供的无效hints。这必须在执行所有其他hints规则之后执行。 |
PullOutNondeterministic | Nondeterministic | Once | 从不是Project或过滤器的LogicalPlan中提取不确定性表达式,将它们放入内部Project,最后将它们投射到外部Project。 |
HandleNullInputsForUDF | UDF | Once | 通过添加额外的If表达式来执行null检查,正确处理UDF的null原语输入。当用户使用基元参数定义UDF时,无法判断基元参数是否为null,因此这里我们假设基元输入是null可传播的,如果输入为null,我们应该返回null。 |
ResolveEncodersInUDF | UDF | Once | 通过明确给出属性来解析UDF的编码器。我们显式地给出属性,以便处理输入值的数据类型与编码器的内部模式不同的情况,这可能会导致数据丢失。例如,如果实际数据类型为Decimal(30,0),编码器不应将输入值转换为Decimal(38,18)。然后,解析的编码器将用于将internal row反序列化为Scala值。 |
UpdateAttributeNullability | UpdateNullability | Once | 通过使用其子输出属性的相应属性的可空性,更新已解析LogicalPlan中属性的可空性。之所以需要此步骤,是因为用户可以在Dataset API中使用已解析的AttributeReference,而外部联接可以更改AttributeReference的可空性。如果没有这个规则,可以为NULL的列的NULL字段实际上可以设置为non-NULL,这会导致非法优化(例如NULL传播)和错误答案。有关本案例的具体查询,请参阅SPARK-13484和SPARK-13801。 |
UpdateOuterReferences | Subquery | Once | 推送引用外部查询块的子查询中的聚合表达式下到外部查询块进行评估。下面的规则会更新这些外部引用作为AttributeReference引用parentouter查询块中的属性。 |
CleanupAliases | Cleanup | fixedPoint | 清除计划中不必要的别名。基本上,我们只需要将Alias作为Project(Project列表)或聚合(聚合表达式)或窗口(窗口表达式)中的顶级表达式。请注意,如果表达式具有不在其子表达式中的其他表达式参数,例如RuntimeReplacable,则此规则中的别名转换无法用于这些参数。 |
HandleAnalysisOnlyCommand | HandleAnalysisOnlyCommand | Once | 将命令标记为已分析的规则,以便删除其子命令以避免优化。此规则应在运行所有其他分析规则后运行。 |