blob: b8c0550c237f246c24894ec6139278cca560c066 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pig.impl.streaming;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.io.WritableComparator;
import org.apache.pig.LoadCaster;
import org.apache.pig.PigStreamingBase;
import org.apache.pig.backend.executionengine.ExecException;
import org.apache.pig.builtin.ToDate;
import org.apache.pig.builtin.Utf8StorageConverter;
import org.apache.pig.data.BagFactory;
import org.apache.pig.data.DataBag;
import org.apache.pig.data.DataByteArray;
import org.apache.pig.data.DataType;
import org.apache.pig.data.Tuple;
import org.apache.pig.data.TupleFactory;
import org.apache.pig.data.WritableByteArray;
import org.apache.pig.impl.logicalLayer.schema.Schema;
import org.apache.pig.impl.logicalLayer.schema.Schema.FieldSchema;
import org.apache.pig.impl.util.StorageUtil;
import com.google.common.base.Charsets;
public class PigStreamingUDF extends PigStreamingBase {
private static final byte PRE_WRAP_DELIM = '|';
private static final byte POST_WRAP_DELIM = '_';
private static final StreamingDelimiters DELIMS =
new StreamingDelimiters(PRE_WRAP_DELIM, POST_WRAP_DELIM, false);
private FieldSchema topLevelFs;
private static TupleFactory tupleFactory = TupleFactory.getInstance();
private static BagFactory bagFactory = BagFactory.getInstance();
private WritableByteArray out;
public PigStreamingUDF() {
out = new WritableByteArray();
}
public PigStreamingUDF(FieldSchema topLevelFs) {
out = new WritableByteArray();
this.topLevelFs = topLevelFs;
}
@Override
public WritableByteArray serializeToBytes(Tuple t) throws IOException {
out.reset();
int sz;
Object field;
if (t == null) {
sz = 0;
} else {
sz = t.size();
}
for (int i=0; i < sz; i++) {
field = t.get(i);
StorageUtil.putField(out, field, DELIMS, true);
if (i != sz-1) {
out.write(DELIMS.getParamDelim());
}
}
byte[] recordDel = DELIMS.getRecordEnd();
out.write(recordDel, 0, recordDel.length);
return out;
}
@Override
public LoadCaster getLoadCaster() throws IOException {
return new Utf8StorageConverter();
}
@Override
public Tuple deserialize(byte[] bytes, int offset, int length) throws IOException {
Object o = deserialize(topLevelFs, bytes, 0 + offset, length - DELIMS.getRecordEnd().length); //Drop newline
return tupleFactory.newTuple(o);
}
public byte[] getRecordDelim() {
return DELIMS.getRecordEnd();
}
private Object deserialize(FieldSchema fs, byte[] bytes, int startIndex, int endIndex) throws IOException {
//If null, return null;
if (WritableComparator.compareBytes(
bytes, startIndex, DELIMS.getNull().length,
DELIMS.getNull(), 0, DELIMS.getNull().length) == 0) {
return null;
}
if (fs.type == DataType.BAG) {
return deserializeBag(fs, bytes, startIndex + 3, endIndex - 2);
} else if (fs.type == DataType.TUPLE) {
return deserializeTuple(fs, bytes, startIndex + 3, endIndex - 2);
} else if (fs.type == DataType.MAP) {
return deserializeMap(bytes, startIndex + 3, endIndex - 2);
}
if (fs.type == DataType.CHARARRAY) {
return extractString(bytes, startIndex, endIndex, true);
} else if (fs.type == DataType.BYTEARRAY) {
return new DataByteArray(bytes, startIndex, endIndex+1);
}
//Can we do this faster?
String val = extractString(bytes, startIndex, endIndex, false);
if (fs.type == DataType.LONG) {
return Long.valueOf(val);
} else if (fs.type == DataType.INTEGER) {
return Integer.valueOf(val);
} else if (fs.type == DataType.FLOAT) {
return Float.valueOf(val);
} else if (fs.type == DataType.DOUBLE) {
return Double.valueOf(val);
} else if (fs.type == DataType.BOOLEAN) {
return Boolean.valueOf(val);
} else if (fs.type == DataType.DATETIME) {
return ToDate.extractDateTime(val);
} else if (fs.type == DataType.BIGINTEGER) {
return new BigInteger(val);
} else if (fs.type == DataType.BIGDECIMAL) {
return new BigDecimal(val);
} else {
throw new ExecException("Can't deserialize type: " + DataType.findTypeName(fs.type));
}
}
private DataBag deserializeBag(FieldSchema fs, byte[] buf, int startIndex, int endIndex) throws IOException {
ArrayList<Tuple> protoBag = new ArrayList<Tuple>();
int depth = 0;
int fieldStart = startIndex;
for (int index = startIndex; index <= endIndex; index++) {
depth = DELIMS.updateDepth(buf, depth, index);
if ( StreamingDelimiters.isDelimiter(DELIMS.getFieldDelim(), buf, index, depth, endIndex)) {
protoBag.add((Tuple)deserialize(fs.schema.getField(0), buf, fieldStart, index - 1));
fieldStart = index + 3;
}
}
return bagFactory.newDefaultBag(protoBag);
}
private Tuple deserializeTuple(FieldSchema fs, byte[] buf, int startIndex, int endIndex) throws IOException {
Schema tupleSchema = fs.schema;
ArrayList<Object> protoTuple = new ArrayList<Object>(tupleSchema.size());
int depth = 0;
int fieldNum = 0;
int fieldStart = startIndex;
for (int index = startIndex; index <= endIndex; index++) {
depth = DELIMS.updateDepth(buf, depth, index);
if (StreamingDelimiters.isDelimiter(DELIMS.getFieldDelim(), buf, index, depth, endIndex)) {
protoTuple.add(deserialize(tupleSchema.getField(fieldNum), buf, fieldStart, index - 1));
fieldStart = index + 3;
fieldNum++;
}
}
return tupleFactory.newTupleNoCopy(protoTuple);
}
private Map<String, Object> deserializeMap(byte[] buf, int startIndex, int endIndex) throws IOException {
Map<String, Object> map = new HashMap<String, Object>();
int depth = 0;
int fieldStart = startIndex;
String key = null;
Object val = null;
for (int index = startIndex; index <= endIndex; index++) {
byte currChar = buf[index];
depth = DELIMS.updateDepth(buf, depth, index);
if (currChar == DELIMS.getMapKeyDelim() && depth == 0) {
key = extractString(buf, fieldStart, index - 1, true);
fieldStart = index + 1;
}
if (StreamingDelimiters.isDelimiter(DELIMS.getFieldDelim(), buf, index, depth, endIndex)) {
val = extractString(buf, fieldStart, index - 1, true);
if (key != null) map.put(key, val);
fieldStart = index + 3;
}
}
return map;
}
private String extractString(byte[] bytes, int startIndex, int endIndex, boolean useUtf8) {
int length = endIndex - startIndex + 1;
if (useUtf8) {
return new String(bytes, startIndex, length, Charsets.UTF_8);
} else {
return new String(bytes, startIndex, length, Charset.defaultCharset());
}
}
}