Skip to content

Commit f23622b

Browse files
authored
Fix Java EMITS UDF Scripts cannot emit null (#117)
1 parent 4649f93 commit f23622b

File tree

2 files changed

+154
-108
lines changed

2 files changed

+154
-108
lines changed

exaudfclient/base/javacontainer/ExaIteratorImpl.java

+119-108
Original file line numberDiff line numberDiff line change
@@ -70,126 +70,137 @@ public void emit(Object... values) throws ExaIterationException, ExaDataTypeExce
7070
if (insideRun && singleOutput)
7171
throw new ExaIterationException("emit() function is not allowed in scalar context");
7272

73-
if (values.length != exaMetadata.getOutputColumnCount()) {
73+
if(values == null){
74+
if(exaMetadata.getOutputColumnCount()==1){
75+
resultHandler.setNull(0);
76+
}else{
7477
String errorText = "emit() takes exactly " + exaMetadata.getOutputColumnCount();
7578
errorText += (exaMetadata.getOutputColumnCount() > 1) ? " arguments" : " argument";
76-
errorText += " (" + values.length + " given)";
79+
errorText += " (" + 1 + " given)";
7780
throw new ExaIterationException(errorText);
78-
}
79-
80-
for (int i = 0; i < values.length; i++) {
81-
if (values[i] == null) {
82-
resultHandler.setNull(i);
83-
}
84-
else if (values[i] instanceof Byte || values[i] instanceof Short || values[i] instanceof Integer) {
85-
Number val = (Number) values[i];
86-
if (outputColumnTypes[i].equals("INT32"))
87-
resultHandler.setInt32(i, val.intValue());
88-
else if (outputColumnTypes[i].equals("INT64"))
89-
resultHandler.setInt64(i, val.longValue());
90-
else if (outputColumnTypes[i].equals("NUMERIC"))
91-
resultHandler.setNumeric(i, val.toString());
92-
else if (outputColumnTypes[i].equals("DOUBLE"))
93-
resultHandler.setDouble(i, val.doubleValue());
94-
else
95-
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
96-
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
81+
}
82+
}else{
83+
if (values.length != exaMetadata.getOutputColumnCount()) {
84+
String errorText = "emit() takes exactly " + exaMetadata.getOutputColumnCount();
85+
errorText += (exaMetadata.getOutputColumnCount() > 1) ? " arguments" : " argument";
86+
errorText += " (" + values.length + " given)";
87+
throw new ExaIterationException(errorText);
9788
}
98-
else if (values[i] instanceof Long) {
99-
Long val = (Long) values[i];
100-
if (outputColumnTypes[i].equals("INT32")) {
101-
if (isConversionToIntegerSafe(val, exaMetadata.getOutputColumnName(i)))
102-
resultHandler.setInt32(i, val.intValue());
89+
90+
for (int i = 0; i < values.length; i++) {
91+
if (values[i] == null) {
92+
resultHandler.setNull(i);
10393
}
104-
else if (outputColumnTypes[i].equals("INT64"))
105-
resultHandler.setInt64(i, val.longValue());
106-
else if (outputColumnTypes[i].equals("NUMERIC"))
107-
resultHandler.setNumeric(i, val.toString());
108-
else if (outputColumnTypes[i].equals("DOUBLE"))
109-
resultHandler.setDouble(i, val.doubleValue());
110-
else
111-
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
112-
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
113-
}
114-
else if (values[i] instanceof Float || values[i] instanceof Double) {
115-
Number val = (Number) values[i];
116-
if (outputColumnTypes[i].equals("INT32")) {
117-
if (isConversionToIntegerSafe(val, exaMetadata.getOutputColumnName(i)))
94+
else if (values[i] instanceof Byte || values[i] instanceof Short || values[i] instanceof Integer) {
95+
Number val = (Number) values[i];
96+
if (outputColumnTypes[i].equals("INT32"))
11897
resultHandler.setInt32(i, val.intValue());
98+
else if (outputColumnTypes[i].equals("INT64"))
99+
resultHandler.setInt64(i, val.longValue());
100+
else if (outputColumnTypes[i].equals("NUMERIC"))
101+
resultHandler.setNumeric(i, val.toString());
102+
else if (outputColumnTypes[i].equals("DOUBLE"))
103+
resultHandler.setDouble(i, val.doubleValue());
104+
else
105+
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
106+
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
119107
}
120-
else if (outputColumnTypes[i].equals("INT64")) {
121-
if (isConversionToLongSafe(val, exaMetadata.getOutputColumnName(i)))
108+
else if (values[i] instanceof Long) {
109+
Long val = (Long) values[i];
110+
if (outputColumnTypes[i].equals("INT32")) {
111+
if (isConversionToIntegerSafe(val, exaMetadata.getOutputColumnName(i)))
112+
resultHandler.setInt32(i, val.intValue());
113+
}
114+
else if (outputColumnTypes[i].equals("INT64"))
122115
resultHandler.setInt64(i, val.longValue());
116+
else if (outputColumnTypes[i].equals("NUMERIC"))
117+
resultHandler.setNumeric(i, val.toString());
118+
else if (outputColumnTypes[i].equals("DOUBLE"))
119+
resultHandler.setDouble(i, val.doubleValue());
120+
else
121+
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
122+
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
123123
}
124-
else if (outputColumnTypes[i].equals("NUMERIC"))
125-
resultHandler.setNumeric(i, val.toString());
126-
else if (outputColumnTypes[i].equals("DOUBLE"))
127-
resultHandler.setDouble(i, val.doubleValue());
128-
else
129-
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
130-
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
131-
}
132-
else if (values[i] instanceof BigDecimal) {
133-
BigDecimal val = (BigDecimal) values[i];
134-
if (outputColumnTypes[i].equals("INT32"))
135-
resultHandler.setInt32(i, val.intValueExact());
136-
else if (outputColumnTypes[i].equals("INT64"))
137-
resultHandler.setInt64(i, val.longValueExact());
138-
else if (outputColumnTypes[i].equals("NUMERIC"))
139-
resultHandler.setNumeric(i, val.toString());
140-
else if (outputColumnTypes[i].equals("DOUBLE"))
141-
resultHandler.setDouble(i, val.doubleValue());
142-
else
143-
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
144-
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
145-
}
146-
else if (values[i] instanceof Boolean) {
147-
Boolean val = (Boolean) values[i];
148-
if (outputColumnTypes[i].equals("BOOLEAN"))
149-
resultHandler.setBoolean(i, val.booleanValue());
150-
else
151-
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
152-
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
153-
}
154-
else if (values[i] instanceof String) {
155-
String val = (String) values[i];
156-
if (outputColumnTypes[i].equals("STRING")) {
157-
byte[] utf8Bytes = null;
158-
try {
159-
utf8Bytes = val.getBytes("UTF-8");
160-
} catch (java.io.UnsupportedEncodingException ex) {
161-
throw new ExaDataTypeException("Column with name '" + exaMetadata.getOutputColumnName(i) + "' contains invalid UTF-8 data");
124+
else if (values[i] instanceof Float || values[i] instanceof Double) {
125+
Number val = (Number) values[i];
126+
if (outputColumnTypes[i].equals("INT32")) {
127+
if (isConversionToIntegerSafe(val, exaMetadata.getOutputColumnName(i)))
128+
resultHandler.setInt32(i, val.intValue());
129+
}
130+
else if (outputColumnTypes[i].equals("INT64")) {
131+
if (isConversionToLongSafe(val, exaMetadata.getOutputColumnName(i)))
132+
resultHandler.setInt64(i, val.longValue());
162133
}
163-
resultHandler.setString(i, utf8Bytes);
134+
else if (outputColumnTypes[i].equals("NUMERIC"))
135+
resultHandler.setNumeric(i, val.toString());
136+
else if (outputColumnTypes[i].equals("DOUBLE"))
137+
resultHandler.setDouble(i, val.doubleValue());
138+
else
139+
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
140+
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
141+
}
142+
else if (values[i] instanceof BigDecimal) {
143+
BigDecimal val = (BigDecimal) values[i];
144+
if (outputColumnTypes[i].equals("INT32"))
145+
resultHandler.setInt32(i, val.intValueExact());
146+
else if (outputColumnTypes[i].equals("INT64"))
147+
resultHandler.setInt64(i, val.longValueExact());
148+
else if (outputColumnTypes[i].equals("NUMERIC"))
149+
resultHandler.setNumeric(i, val.toString());
150+
else if (outputColumnTypes[i].equals("DOUBLE"))
151+
resultHandler.setDouble(i, val.doubleValue());
152+
else
153+
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
154+
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
155+
}
156+
else if (values[i] instanceof Boolean) {
157+
Boolean val = (Boolean) values[i];
158+
if (outputColumnTypes[i].equals("BOOLEAN"))
159+
resultHandler.setBoolean(i, val.booleanValue());
160+
else
161+
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
162+
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
163+
}
164+
else if (values[i] instanceof String) {
165+
String val = (String) values[i];
166+
if (outputColumnTypes[i].equals("STRING")) {
167+
byte[] utf8Bytes = null;
168+
try {
169+
utf8Bytes = val.getBytes("UTF-8");
170+
} catch (java.io.UnsupportedEncodingException ex) {
171+
throw new ExaDataTypeException("Column with name '" + exaMetadata.getOutputColumnName(i) + "' contains invalid UTF-8 data");
172+
}
173+
resultHandler.setString(i, utf8Bytes);
174+
}
175+
else
176+
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
177+
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
178+
}
179+
else if (values[i] instanceof Date) {
180+
Date val = (Date) values[i];
181+
if (outputColumnTypes[i].equals("DATE"))
182+
resultHandler.setDate(i, val.toString());
183+
else
184+
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
185+
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
186+
}
187+
else if (values[i] instanceof Timestamp) {
188+
Timestamp val = (Timestamp) values[i];
189+
if (outputColumnTypes[i].equals("TIMESTAMP"))
190+
resultHandler.setTimestamp(i, val.toString());
191+
else
192+
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
193+
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
194+
}
195+
else {
196+
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i)
197+
+ "' is of unsupported type " + values[i].getClass().getCanonicalName());
164198
}
165-
else
166-
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
167-
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
168-
}
169-
else if (values[i] instanceof Date) {
170-
Date val = (Date) values[i];
171-
if (outputColumnTypes[i].equals("DATE"))
172-
resultHandler.setDate(i, val.toString());
173-
else
174-
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
175-
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
176-
}
177-
else if (values[i] instanceof Timestamp) {
178-
Timestamp val = (Timestamp) values[i];
179-
if (outputColumnTypes[i].equals("TIMESTAMP"))
180-
resultHandler.setTimestamp(i, val.toString());
181-
else
182-
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i) + "' is of type "
183-
+ outputColumnTypes[i] + " but data given have type " + values[i].getClass().getCanonicalName());
184-
}
185-
else {
186-
throw new ExaDataTypeException("emit column '" + exaMetadata.getOutputColumnName(i)
187-
+ "' is of unsupported type " + values[i].getClass().getCanonicalName());
188-
}
189199

190-
String exMsg = resultHandler.checkException();
191-
if (exMsg != null && exMsg.length() > 0) {
192-
throw new ExaIterationException(exMsg);
200+
String exMsg = resultHandler.checkException();
201+
if (exMsg != null && exMsg.length() > 0) {
202+
throw new ExaIterationException(exMsg);
203+
}
193204
}
194205
}
195206

tests/test/java/bugs.py

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env python2.7
2+
3+
import os
4+
import sys
5+
6+
sys.path.append(os.path.realpath(__file__ + '/../../../lib'))
7+
8+
import udf
9+
from udf import useData, expectedFailure
10+
11+
class JavaBugsTest(udf.TestCase):
12+
def setUp(self):
13+
self.schema=self.__class__.__name__
14+
self.query('DROP SCHEMA %s CASCADE'%self.schema, ignore_errors=True)
15+
self.query('CREATE SCHEMA %s'%self.schema, ignore_errors=True)
16+
self.query('OPEN SCHEMA %s'%self.schema)
17+
18+
def test_java_emits_null(self):
19+
self.query(udf.fixindent('''
20+
CREATE OR REPLACE JAVA SCALAR SCRIPT JAVA_EMITS() EMITS(i INT) AS
21+
class JAVA_EMITS {
22+
static void run(ExaMetadata exa, ExaIterator ctx) throws Exception {
23+
ctx.emit(null);
24+
}
25+
}
26+
'''))
27+
self.query("select JAVA_EMITS();")
28+
29+
def tearDown(self):
30+
self.query('DROP SCHEMA %s CASCADE'%self.schema, ignore_errors=True)
31+
32+
33+
if __name__ == '__main__':
34+
udf.main()
35+

0 commit comments

Comments
 (0)