@@ -47,7 +47,7 @@ import org.apache.comet.serde.OperatorOuterClass.Operator
4747import org .apache .comet .serde .Types .{DataType => ProtoDataType }
4848import org .apache .comet .serde .Types .DataType ._
4949import org .apache .comet .serde .literals .CometLiteral
50- import org .apache .comet .serde .operator .{ CometLocalTableScan , CometProject , CometSort , CometSortOrder }
50+ import org .apache .comet .serde .operator ._
5151import org .apache .comet .shims .CometExprShim
5252
5353/**
@@ -61,6 +61,15 @@ object QueryPlanSerde extends Logging with CometExprShim {
6161 private val opSerdeMap : Map [Class [_ <: SparkPlan ], CometOperatorSerde [_]] =
6262 Map (
6363 classOf [ProjectExec ] -> CometProject ,
64+ classOf [FilterExec ] -> CometFilter ,
65+ classOf [LocalLimitExec ] -> CometLocalLimit ,
66+ classOf [GlobalLimitExec ] -> CometGlobalLimit ,
67+ classOf [ExpandExec ] -> CometExpand ,
68+ classOf [HashAggregateExec ] -> CometHashAggregate ,
69+ classOf [ObjectHashAggregateExec ] -> CometObjectHashAggregate ,
70+ classOf [BroadcastHashJoinExec ] -> CometBroadcastHashJoin ,
71+ classOf [ShuffledHashJoinExec ] -> CometShuffleHashJoin ,
72+ classOf [SortMergeJoinExec ] -> CometSortMergeJoin ,
6473 classOf [SortExec ] -> CometSort ,
6574 classOf [LocalTableScanExec ] -> CometLocalTableScan )
6675
@@ -924,51 +933,30 @@ object QueryPlanSerde extends Logging with CometExprShim {
924933 val builder = OperatorOuterClass .Operator .newBuilder().setPlanId(op.id)
925934 childOp.foreach(builder.addChildren)
926935
936+ // look for registered handler first
937+ val serde = opSerdeMap.get(op.getClass)
938+ serde match {
939+ case Some (handler) if isOperatorEnabled(handler, op) =>
940+ val opSerde = handler.asInstanceOf [CometOperatorSerde [SparkPlan ]]
941+ val maybeConverted = opSerde.convert(op, builder, childOp : _* )
942+ if (maybeConverted.isDefined) {
943+ return maybeConverted
944+ }
945+ case _ =>
946+ }
947+
948+ // now handle special cases that cannot be handled as a simple mapping from class name
949+ // and see if operator can be used as a sink
927950 op match {
928951
929952 // Fully native scan for V1
930953 case scan : CometScanExec if scan.scanImpl == CometConf .SCAN_NATIVE_DATAFUSION =>
931954 CometNativeScan .convert(scan, builder, childOp : _* )
932955
933- case filter : FilterExec if CometConf .COMET_EXEC_FILTER_ENABLED .get(conf) =>
934- CometFilter .convert(filter, builder, childOp : _* )
935-
936- case limit : LocalLimitExec if CometConf .COMET_EXEC_LOCAL_LIMIT_ENABLED .get(conf) =>
937- CometLocalLimit .convert(limit, builder, childOp : _* )
938-
939- case globalLimitExec : GlobalLimitExec
940- if CometConf .COMET_EXEC_GLOBAL_LIMIT_ENABLED .get(conf) =>
941- CometGlobalLimit .convert(globalLimitExec, builder, childOp : _* )
942-
943- case expand : ExpandExec if CometConf .COMET_EXEC_EXPAND_ENABLED .get(conf) =>
944- CometExpand .convert(expand, builder, childOp : _* )
945-
946956 case _ : WindowExec if CometConf .COMET_EXEC_WINDOW_ENABLED .get(conf) =>
947957 withInfo(op, " Window expressions are not supported" )
948958 None
949959
950- case aggregate : HashAggregateExec if CometConf .COMET_EXEC_AGGREGATE_ENABLED .get(conf) =>
951- CometHashAggregate .convert(aggregate, builder, childOp : _* )
952-
953- case aggregate : ObjectHashAggregateExec
954- if CometConf .COMET_EXEC_AGGREGATE_ENABLED .get(conf) =>
955- CometObjectHashAggregate .convert(aggregate, builder, childOp : _* )
956-
957- case join : BroadcastHashJoinExec
958- if CometConf .COMET_EXEC_BROADCAST_HASH_JOIN_ENABLED .get(conf) =>
959- CometBroadcastHashJoin .convert(join, builder, childOp : _* )
960-
961- case join : ShuffledHashJoinExec if CometConf .COMET_EXEC_HASH_JOIN_ENABLED .get(conf) =>
962- CometShuffleHashJoin .convert(join, builder, childOp : _* )
963-
964- case join : SortMergeJoinExec =>
965- if (CometConf .COMET_EXEC_SORT_MERGE_JOIN_ENABLED .get(conf)) {
966- CometSortMergeJoin .convert(join, builder, childOp : _* )
967- } else {
968- withInfo(join, " SortMergeJoin is not enabled" )
969- None
970- }
971-
972960 case op if isCometSink(op) =>
973961 val supportedTypes =
974962 op.output.forall(a => supportedDataType(a.dataType, allowComplex = true ))
@@ -1023,28 +1011,58 @@ object QueryPlanSerde extends Logging with CometExprShim {
10231011 None
10241012 }
10251013
1026- case op =>
1027- opSerdeMap.get(op.getClass) match {
1028- case Some (handler) =>
1029- handler.enabledConfig.foreach { enabledConfig =>
1030- if (! enabledConfig.get(op.conf)) {
1031- withInfo(
1032- op,
1033- s " Native support for operator ${op.getClass.getSimpleName} is disabled. " +
1034- s " Set ${enabledConfig.key}=true to enable it. " )
1035- return None
1036- }
1037- }
1038- handler.asInstanceOf [CometOperatorSerde [SparkPlan ]].convert(op, builder, childOp : _* )
1039- case _ =>
1040- // Emit warning if:
1041- // 1. it is not Spark shuffle operator, which is handled separately
1042- // 2. it is not a Comet operator
1043- if (! op.nodeName.contains(" Comet" ) && ! op.isInstanceOf [ShuffleExchangeExec ]) {
1044- withInfo(op, s " unsupported Spark operator: ${op.nodeName}" )
1045- }
1046- None
1014+ case _ =>
1015+ // Emit warning if:
1016+ // 1. it is not Spark shuffle operator, which is handled separately
1017+ // 2. it is not a Comet operator
1018+ if (serde.isEmpty && ! op.nodeName.contains(" Comet" ) &&
1019+ ! op.isInstanceOf [ShuffleExchangeExec ]) {
1020+ withInfo(op, s " unsupported Spark operator: ${op.nodeName}" )
10471021 }
1022+ None
1023+ }
1024+ }
1025+
1026+ private def isOperatorEnabled (handler : CometOperatorSerde [_], op : SparkPlan ): Boolean = {
1027+ val enabled = handler.enabledConfig.forall(_.get(op.conf))
1028+ val opName = op.getClass.getSimpleName
1029+ if (enabled) {
1030+ val opSerde = handler.asInstanceOf [CometOperatorSerde [SparkPlan ]]
1031+ opSerde.getSupportLevel(op) match {
1032+ case Unsupported (notes) =>
1033+ withInfo(op, notes.getOrElse(" " ))
1034+ false
1035+ case Incompatible (notes) =>
1036+ val allowIncompat = CometConf .isOperatorAllowIncompat(opName)
1037+ val incompatConf = CometConf .getOperatorAllowIncompatConfigKey(opName)
1038+ if (allowIncompat) {
1039+ if (notes.isDefined) {
1040+ logWarning(
1041+ s " Comet supports $opName when $incompatConf=true " +
1042+ s " but has notes: ${notes.get}" )
1043+ }
1044+ true
1045+ } else {
1046+ val optionalNotes = notes.map(str => s " ( $str) " ).getOrElse(" " )
1047+ withInfo(
1048+ op,
1049+ s " $opName is not fully compatible with Spark $optionalNotes. " +
1050+ s " To enable it anyway, set $incompatConf=true. " +
1051+ s " ${CometConf .COMPAT_GUIDE }. " )
1052+ false
1053+ }
1054+ case Compatible (notes) =>
1055+ if (notes.isDefined) {
1056+ logWarning(s " Comet supports $opName but has notes: ${notes.get}" )
1057+ }
1058+ true
1059+ }
1060+ } else {
1061+ withInfo(
1062+ op,
1063+ s " Native support for operator $opName is disabled. " +
1064+ s " Set ${handler.enabledConfig.get.key}=true to enable it. " )
1065+ false
10481066 }
10491067 }
10501068
@@ -1144,25 +1162,3 @@ object QueryPlanSerde extends Logging with CometExprShim {
11441162 }
11451163
11461164}
1147-
1148- sealed trait SupportLevel
1149-
1150- /**
1151- * Comet either supports this feature with full compatibility with Spark, or may have known
1152- * differences in some specific edge cases that are unlikely to be an issue for most users.
1153- *
1154- * Any compatibility differences are noted in the
1155- * [[https://datafusion.apache.org/comet/user-guide/compatibility.html Comet Compatibility Guide ]].
1156- */
1157- case class Compatible (notes : Option [String ] = None ) extends SupportLevel
1158-
1159- /**
1160- * Comet supports this feature but results can be different from Spark.
1161- *
1162- * Any compatibility differences are noted in the
1163- * [[https://datafusion.apache.org/comet/user-guide/compatibility.html Comet Compatibility Guide ]].
1164- */
1165- case class Incompatible (notes : Option [String ] = None ) extends SupportLevel
1166-
1167- /** Comet does not support this feature */
1168- case class Unsupported (notes : Option [String ] = None ) extends SupportLevel
0 commit comments