18
18
package org .apache .spark .sql .catalyst .expressions
19
19
20
20
import org .apache .spark .sql .catalyst .analysis .{TypeCheckResult , UnresolvedAttribute }
21
+ import org .apache .spark .sql .catalyst .expressions .codegen .{EvaluatedExpression , CodeGenContext }
21
22
import org .apache .spark .sql .catalyst .trees
22
23
import org .apache .spark .sql .catalyst .trees .TreeNode
23
24
import org .apache .spark .sql .types ._
@@ -51,6 +52,51 @@ abstract class Expression extends TreeNode[Expression] {
51
52
/** Returns the result of evaluating this expression on a given input Row */
52
53
def eval (input : Row = null ): Any
53
54
55
+ /**
56
+ * Returns an [[EvaluatedExpression ]], which contains Java source code that
57
+ * can be used to generate the result of evaluating the expression on an input row.
58
+ * @param ctx a [[CodeGenContext ]]
59
+ */
60
+ def gen (ctx : CodeGenContext ): EvaluatedExpression = {
61
+ val nullTerm = ctx.freshName(" nullTerm" )
62
+ val primitiveTerm = ctx.freshName(" primitiveTerm" )
63
+ val objectTerm = ctx.freshName(" objectTerm" )
64
+ val ve = EvaluatedExpression (" " , nullTerm, primitiveTerm, objectTerm)
65
+ ve.code = genSource(ctx, ve)
66
+
67
+ // Only inject debugging code if debugging is turned on.
68
+ // val debugCode =
69
+ // if (debugLogging) {
70
+ // val localLogger = log
71
+ // val localLoggerTree = reify { localLogger }
72
+ // s"""
73
+ // $localLoggerTree.debug(
74
+ // ${this.toString} + ": " + (if (${ev.nullTerm}) "null" else ${ev.primitiveTerm}.toString))
75
+ // """
76
+ // } else {
77
+ // ""
78
+ // }
79
+
80
+ ve
81
+ }
82
+
83
+ /**
84
+ * Returns Java source code for this expression
85
+ */
86
+ def genSource (ctx : CodeGenContext , ev : EvaluatedExpression ): String = {
87
+ val e = this .asInstanceOf [Expression ]
88
+ ctx.references += e
89
+ s """
90
+ /* expression: ${this } */
91
+ Object ${ev.objectTerm} = expressions[ ${ctx.references.size - 1 }].eval(i);
92
+ boolean ${ev.nullTerm} = ${ev.objectTerm} == null;
93
+ ${ctx.primitiveForType(e.dataType)} ${ev.primitiveTerm} =
94
+ ${ctx.defaultPrimitive(e.dataType)};
95
+ if (! ${ev.nullTerm}) ${ev.primitiveTerm} =
96
+ ( ${ctx.termForType(e.dataType)}) ${ev.objectTerm};
97
+ """
98
+ }
99
+
54
100
/**
55
101
* Returns `true` if this expression and all its children have been resolved to a specific schema
56
102
* and input data types checking passed, and `false` if it still contains any unresolved
@@ -116,6 +162,41 @@ abstract class BinaryExpression extends Expression with trees.BinaryNode[Express
116
162
override def nullable : Boolean = left.nullable || right.nullable
117
163
118
164
override def toString : String = s " ( $left $symbol $right) "
165
+
166
+
167
+ /**
168
+ * Short hand for generating binary evaluation code, which depends on two sub-evaluations of
169
+ * the same type. If either of the sub-expressions is null, the result of this computation
170
+ * is assumed to be null.
171
+ *
172
+ * @param f a function from two primitive term names to a tree that evaluates them.
173
+ */
174
+ def evaluate (ctx : CodeGenContext ,
175
+ ev : EvaluatedExpression ,
176
+ f : (String , String ) => String ): String =
177
+ evaluateAs(left.dataType)(ctx, ev, f)
178
+
179
+ def evaluateAs (resultType : DataType )(ctx : CodeGenContext ,
180
+ ev : EvaluatedExpression ,
181
+ f : (String , String ) => String ): String = {
182
+ // TODO: Right now some timestamp tests fail if we enforce this...
183
+ if (left.dataType != right.dataType) {
184
+ // log.warn(s"${left.dataType} != ${right.dataType}")
185
+ }
186
+
187
+ val eval1 = left.gen(ctx)
188
+ val eval2 = right.gen(ctx)
189
+ val resultCode = f(eval1.primitiveTerm, eval2.primitiveTerm)
190
+
191
+ eval1.code + eval2.code +
192
+ s """
193
+ boolean ${ev.nullTerm} = ${eval1.nullTerm} || ${eval2.nullTerm};
194
+ ${ctx.primitiveForType(resultType)} ${ev.primitiveTerm} = ${ctx.defaultPrimitive(resultType)};
195
+ if(! ${ev.nullTerm}) {
196
+ ${ev.primitiveTerm} = ( ${ctx.primitiveForType(resultType)})( $resultCode);
197
+ }
198
+ """
199
+ }
119
200
}
120
201
121
202
abstract class LeafExpression extends Expression with trees.LeafNode [Expression ] {
@@ -124,6 +205,19 @@ abstract class LeafExpression extends Expression with trees.LeafNode[Expression]
124
205
125
206
abstract class UnaryExpression extends Expression with trees.UnaryNode [Expression ] {
126
207
self : Product =>
208
+ def castOrNull (ctx : CodeGenContext ,
209
+ ev : EvaluatedExpression ,
210
+ f : String => String , dataType : DataType ): String = {
211
+ val eval = child.gen(ctx)
212
+ eval.code +
213
+ s """
214
+ boolean ${ev.nullTerm} = ${eval.nullTerm};
215
+ ${ctx.primitiveForType(dataType)} ${ev.primitiveTerm} = ${ctx.defaultPrimitive(dataType)};
216
+ if (! ${ev.nullTerm}) {
217
+ ${ev.primitiveTerm} = ${f(eval.primitiveTerm)};
218
+ }
219
+ """
220
+ }
127
221
}
128
222
129
223
// TODO Semantically we probably not need GroupExpression
0 commit comments