changed recordbuilder to check for conflicting open field names when it writes a built record; fixed test cases that use duplicate open fields
git-svn-id: https://asterixdb.googlecode.com/svn/branches/asterix_stabilization_issue_173@1209 eaa15691-b419-025a-1212-ee371bd00084
diff --git a/asterix-app/src/test/resources/runtimets/queries/custord/order_q_04.aql b/asterix-app/src/test/resources/runtimets/queries/custord/order_q_04.aql
index b2d55d9..e2fd5b6 100644
--- a/asterix-app/src/test/resources/runtimets/queries/custord/order_q_04.aql
+++ b/asterix-app/src/test/resources/runtimets/queries/custord/order_q_04.aql
@@ -26,4 +26,4 @@
let $c2 := {{ $o.orderstatus, $o.clerk}}
let $c3 := [$o.heList, $o.openlist, $o.loc, $o.line, $o.poly, $o.lastorder]
let $c4 := [$o.heList, $o.openlist, $o.loc, $o.line, $o.poly, $o.lastorder]
-return { "orderid": $o.oid, "ordertot":$o.total, "list": $c3, "item1": $c3[0], "item1": $c3[?], "item2": $c3[1], "item5": $c3[5], "item10": $c3[10]}
+return { "orderid": $o.oid, "ordertot":$o.total, "list": $c3, "item1": $c3[0], "item2": $c3[1], "item5": $c3[5], "item10": $c3[10]}
diff --git a/asterix-app/src/test/resources/runtimets/queries/custord/order_q_05.aql b/asterix-app/src/test/resources/runtimets/queries/custord/order_q_05.aql
index 5693312..481c1fd 100644
--- a/asterix-app/src/test/resources/runtimets/queries/custord/order_q_05.aql
+++ b/asterix-app/src/test/resources/runtimets/queries/custord/order_q_05.aql
@@ -24,4 +24,4 @@
for $o in dataset('Orders')
let $c1 := []
let $c2 := {{}}
-return { "orderid": $o.oid, "ordertot":$o.total, "emptyorderedlist": $c1, "emptyunorderedlist": $c2, "olist_item1": $c1[0], "olist_item1": $c1[?], "olist_item5": $c1[4], "ulist_item1": $c2[?]}
+return { "orderid": $o.oid, "ordertot":$o.total, "emptyorderedlist": $c1, "emptyunorderedlist": $c2, "olist_item1": $c1[0], "olist_item5": $c1[4], "ulist_item1": $c2[?]}
diff --git a/asterix-app/src/test/resources/runtimets/results/custord/order_q_04.adm b/asterix-app/src/test/resources/runtimets/results/custord/order_q_04.adm
index f22ea7f..3992f7d 100644
--- a/asterix-app/src/test/resources/runtimets/results/custord/order_q_04.adm
+++ b/asterix-app/src/test/resources/runtimets/results/custord/order_q_04.adm
@@ -1,4 +1,4 @@
-{ "orderid": 1000, "ordertot": 97.20656f, "list": [ [ "1.0f", "yassser" ], [ 11, 14, "yasir", 1.6f ], point("10.1,11.1"), line("10.1,11.1 10.2,11.2"), polygon("1.2,1.3 2.1,2.5 3.5,3.6 4.6,4.8"), null ], "item1": [ "1.0f", "yassser" ], "item1": [ "1.0f", "yassser" ], "item2": [ 11, 14, "yasir", 1.6f ], "item5": null, "item10": null }
-{ "orderid": 10, "ordertot": 7.206f, "list": [ [ 1.0f, "5.2f", "60" ], [ 13231, "foo", null, 13.25d, 13.2f ], point("10.1,11.1"), line("10.1,11.1 10.2,11.2"), polygon("1.2,1.3 2.1,2.5 3.5,3.6 4.6,4.8"), null ], "item1": [ 1.0f, "5.2f", "60" ], "item1": [ 1.0f, "5.2f", "60" ], "item2": [ 13231, "foo", null, 13.25d, 13.2f ], "item5": null, "item10": null }
-{ "orderid": 100, "ordertot": 124.26f, "list": [ [ 1.3f, 5.2f, "60", 12.32f ], [ 10, 2.0f, 3.0d, 40 ], point("10.1,11.1"), line("10.1,11.1 10.2,11.2"), polygon("1.2,1.3 2.1,2.5 3.5,3.6 4.6,4.8"), null ], "item1": [ 1.3f, 5.2f, "60", 12.32f ], "item1": [ 1.3f, 5.2f, "60", 12.32f ], "item2": [ 10, 2.0f, 3.0d, 40 ], "item5": null, "item10": null }
-{ "orderid": 10, "ordertot": 14.2326f, "list": [ [ 2.4f, "15" ], [ 110 ], point("10.1,11.1"), line("10.1,11.1 10.2,11.2"), polygon("1.2,1.3 2.1,2.5 3.5,3.6 4.6,4.8"), { "oid": 75, "total": 87.61863f } ], "item1": [ 2.4f, "15" ], "item1": [ 2.4f, "15" ], "item2": [ 110 ], "item5": { "oid": 75, "total": 87.61863f }, "item10": null }
+{ "orderid": 1000, "ordertot": 97.20656f, "list": [ [ "1.0f", "yassser" ], [ 11, 14, "yasir", 1.6f ], point("10.1,11.1"), line("10.1,11.1 10.2,11.2"), polygon("1.2,1.3 2.1,2.5 3.5,3.6 4.6,4.8"), null ], "item1": [ "1.0f", "yassser" ], "item2": [ 11, 14, "yasir", 1.6f ], "item5": null, "item10": null }
+{ "orderid": 10, "ordertot": 7.206f, "list": [ [ 1.0f, "5.2f", "60" ], [ 13231, "foo", null, 13.25d, 13.2f ], point("10.1,11.1"), line("10.1,11.1 10.2,11.2"), polygon("1.2,1.3 2.1,2.5 3.5,3.6 4.6,4.8"), null ], "item1": [ 1.0f, "5.2f", "60" ], "item2": [ 13231, "foo", null, 13.25d, 13.2f ], "item5": null, "item10": null }
+{ "orderid": 100, "ordertot": 124.26f, "list": [ [ 1.3f, 5.2f, "60", 12.32f ], [ 10, 2.0f, 3.0d, 40 ], point("10.1,11.1"), line("10.1,11.1 10.2,11.2"), polygon("1.2,1.3 2.1,2.5 3.5,3.6 4.6,4.8"), null ], "item1": [ 1.3f, 5.2f, "60", 12.32f ], "item2": [ 10, 2.0f, 3.0d, 40 ], "item5": null, "item10": null }
+{ "orderid": 10, "ordertot": 14.2326f, "list": [ [ 2.4f, "15" ], [ 110 ], point("10.1,11.1"), line("10.1,11.1 10.2,11.2"), polygon("1.2,1.3 2.1,2.5 3.5,3.6 4.6,4.8"), { "oid": 75, "total": 87.61863f } ], "item1": [ 2.4f, "15" ], "item2": [ 110 ], "item5": { "oid": 75, "total": 87.61863f }, "item10": null }
diff --git a/asterix-app/src/test/resources/runtimets/results/custord/order_q_05.adm b/asterix-app/src/test/resources/runtimets/results/custord/order_q_05.adm
index dd3420e..4d8b37c 100644
--- a/asterix-app/src/test/resources/runtimets/results/custord/order_q_05.adm
+++ b/asterix-app/src/test/resources/runtimets/results/custord/order_q_05.adm
@@ -1,4 +1,4 @@
-{ "orderid": 1000, "ordertot": 97.20656f, "emptyorderedlist": [ ], "emptyunorderedlist": {{ }}, "olist_item1": null, "olist_item1": null, "olist_item5": null, "ulist_item1": null }
-{ "orderid": 10, "ordertot": 7.206f, "emptyorderedlist": [ ], "emptyunorderedlist": {{ }}, "olist_item1": null, "olist_item1": null, "olist_item5": null, "ulist_item1": null }
-{ "orderid": 100, "ordertot": 124.26f, "emptyorderedlist": [ ], "emptyunorderedlist": {{ }}, "olist_item1": null, "olist_item1": null, "olist_item5": null, "ulist_item1": null }
-{ "orderid": 10, "ordertot": 14.2326f, "emptyorderedlist": [ ], "emptyunorderedlist": {{ }}, "olist_item1": null, "olist_item1": null, "olist_item5": null, "ulist_item1": null }
+{ "orderid": 1000, "ordertot": 97.20656f, "emptyorderedlist": [ ], "emptyunorderedlist": {{ }}, "olist_item1": null, "olist_item5": null, "ulist_item1": null }
+{ "orderid": 10, "ordertot": 7.206f, "emptyorderedlist": [ ], "emptyunorderedlist": {{ }}, "olist_item1": null, "olist_item5": null, "ulist_item1": null }
+{ "orderid": 100, "ordertot": 124.26f, "emptyorderedlist": [ ], "emptyunorderedlist": {{ }}, "olist_item1": null, "olist_item5": null, "ulist_item1": null }
+{ "orderid": 10, "ordertot": 14.2326f, "emptyorderedlist": [ ], "emptyunorderedlist": {{ }}, "olist_item1": null, "olist_item5": null, "ulist_item1": null }
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/builders/IARecordBuilder.java b/asterix-om/src/main/java/edu/uci/ics/asterix/builders/IARecordBuilder.java
index 544b532..cb7f902 100644
--- a/asterix-om/src/main/java/edu/uci/ics/asterix/builders/IARecordBuilder.java
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/builders/IARecordBuilder.java
@@ -18,7 +18,9 @@
import java.io.DataOutput;
import java.io.IOException;
+import edu.uci.ics.asterix.common.exceptions.AsterixException;
import edu.uci.ics.asterix.om.types.ARecordType;
+import edu.uci.ics.hyracks.api.exceptions.HyracksDataException;
import edu.uci.ics.hyracks.data.std.api.IValueReference;
/**
@@ -59,8 +61,9 @@
* Whether to write a typetag as part of the record's serialized
* representation.
* @throws IOException
+ * @throws AsterixException
*/
- public void write(DataOutput out, boolean writeTypeTag) throws IOException;
+ public void write(DataOutput out, boolean writeTypeTag) throws IOException, HyracksDataException;
public int getFieldId(String fieldName);
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/builders/RecordBuilder.java b/asterix-om/src/main/java/edu/uci/ics/asterix/builders/RecordBuilder.java
index 03f0e20..92c03b1 100644
--- a/asterix-om/src/main/java/edu/uci/ics/asterix/builders/RecordBuilder.java
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/builders/RecordBuilder.java
@@ -1,6 +1,8 @@
package edu.uci.ics.asterix.builders;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;
@@ -9,32 +11,21 @@
import edu.uci.ics.asterix.om.types.ARecordType;
import edu.uci.ics.asterix.om.types.ATypeTag;
import edu.uci.ics.asterix.om.util.NonTaggedFormatUtil;
+import edu.uci.ics.hyracks.api.dataflow.value.IBinaryComparator;
import edu.uci.ics.hyracks.api.dataflow.value.IBinaryHashFunction;
+import edu.uci.ics.hyracks.api.exceptions.HyracksDataException;
+import edu.uci.ics.hyracks.data.std.accessors.PointableBinaryComparatorFactory;
import edu.uci.ics.hyracks.data.std.accessors.PointableBinaryHashFunctionFactory;
import edu.uci.ics.hyracks.data.std.api.IValueReference;
import edu.uci.ics.hyracks.data.std.primitive.UTF8StringPointable;
+import edu.uci.ics.hyracks.data.std.util.ByteArrayAccessibleOutputStream;
+import edu.uci.ics.hyracks.dataflow.common.data.marshalling.UTF8StringSerializerDeserializer;
public class RecordBuilder implements IARecordBuilder {
- private int openPartOffset;
+ private final static int DEFAULT_NUM_OPEN_FIELDS = 10;
+ private final static byte SER_NULL_TYPE_TAG = ATypeTag.NULL.serialize();
+ private final static byte RECORD_TYPE_TAG = ATypeTag.RECORD.serialize();
- private ARecordType recType;
-
- private ByteArrayOutputStream closedPartOutputStream;
- private int[] closedPartOffsets;
- private int numberOfClosedFields;
- private byte[] nullBitMap;
- private int nullBitMapSize;
-
- private ByteArrayOutputStream openPartOutputStream;
- private long[] openPartOffsets;
- private long[] tempOpenPartOffsets;
-
- private int numberOfOpenFields;
-
- private int fieldNameHashCode;
- private final IBinaryHashFunction utf8HashFunction;
-
- // for write()
private int openPartOffsetArraySize;
private byte[] openPartOffsetArray;
private int offsetPosition;
@@ -42,25 +33,40 @@
private boolean isOpen;
private boolean isNullable;
private int numberOfSchemaFields;
- private final static byte SER_NULL_TYPE_TAG = ATypeTag.NULL.serialize();
- private final static byte RECORD_TYPE_TAG = ATypeTag.RECORD.serialize();
+
+ private int openPartOffset;
+ private ARecordType recType;
+
+ private final IBinaryHashFunction utf8HashFunction;
+ private final IBinaryComparator utf8Comparator;
+
+ private final ByteArrayOutputStream closedPartOutputStream;
+ private int[] closedPartOffsets;
+ private int numberOfClosedFields;
+ private byte[] nullBitMap;
+ private int nullBitMapSize;
+
+ private final ByteArrayAccessibleOutputStream openPartOutputStream;
+ private long[] openPartOffsets;
+ private int[] openFieldNameLengths;
+
+ private int numberOfOpenFields;
public RecordBuilder() {
this.closedPartOutputStream = new ByteArrayOutputStream();
this.numberOfClosedFields = 0;
- this.openPartOutputStream = new ByteArrayOutputStream();
- this.openPartOffsets = new long[20];
- this.tempOpenPartOffsets = new long[20];
-
+ this.openPartOutputStream = new ByteArrayAccessibleOutputStream();
+ this.openPartOffsets = new long[DEFAULT_NUM_OPEN_FIELDS];
+ this.openFieldNameLengths = new int[DEFAULT_NUM_OPEN_FIELDS];
this.numberOfOpenFields = 0;
- this.fieldNameHashCode = 0;
this.utf8HashFunction = new PointableBinaryHashFunctionFactory(UTF8StringPointable.FACTORY)
.createBinaryHashFunction();
+ this.utf8Comparator = new PointableBinaryComparatorFactory(UTF8StringPointable.FACTORY)
+ .createBinaryComparator();
- // for write()
this.openPartOffsetArray = null;
this.openPartOffsetArraySize = 0;
this.offsetPosition = 0;
@@ -137,21 +143,21 @@
@Override
public void addField(IValueReference name, IValueReference value) {
if (numberOfOpenFields == openPartOffsets.length) {
- tempOpenPartOffsets = openPartOffsets;
- openPartOffsets = new long[numberOfOpenFields + 20];
- for (int i = 0; i < tempOpenPartOffsets.length; i++)
- openPartOffsets[i] = tempOpenPartOffsets[i];
+ openPartOffsets = Arrays.copyOf(openPartOffsets, openPartOffsets.length + DEFAULT_NUM_OPEN_FIELDS);
+ openFieldNameLengths = Arrays.copyOf(openFieldNameLengths, openFieldNameLengths.length
+ + DEFAULT_NUM_OPEN_FIELDS);
}
- fieldNameHashCode = utf8HashFunction.hash(name.getByteArray(), name.getStartOffset() + 1, name.getLength());
+ int fieldNameHashCode = utf8HashFunction.hash(name.getByteArray(), name.getStartOffset() + 1, name.getLength());
openPartOffsets[this.numberOfOpenFields] = fieldNameHashCode;
openPartOffsets[this.numberOfOpenFields] = (openPartOffsets[numberOfOpenFields] << 32);
- openPartOffsets[numberOfOpenFields++] += openPartOutputStream.size();
+ openPartOffsets[numberOfOpenFields] += openPartOutputStream.size();
+ openFieldNameLengths[numberOfOpenFields++] = name.getLength() - 1;
openPartOutputStream.write(name.getByteArray(), name.getStartOffset() + 1, name.getLength() - 1);
openPartOutputStream.write(value.getByteArray(), value.getStartOffset(), value.getLength());
}
@Override
- public void write(DataOutput out, boolean writeTypeTag) throws IOException {
+ public void write(DataOutput out, boolean writeTypeTag) throws HyracksDataException, IOException {
int h = headerSize;
int recordLength;
// prepare the open part
@@ -163,13 +169,27 @@
openPartOffsetArray = new byte[openPartOffsetArraySize];
Arrays.sort(this.openPartOffsets, 0, numberOfOpenFields);
+ if (numberOfOpenFields > 1) {
+ byte[] openBytes = openPartOutputStream.getByteArray();
+ for (int i = 1; i < numberOfOpenFields; i++) {
+ if (utf8Comparator.compare(openBytes, (int) openPartOffsets[i - 1], openFieldNameLengths[i - 1],
+ openBytes, (int) openPartOffsets[i], openFieldNameLengths[i]) == 0) {
+ String field = UTF8StringSerializerDeserializer.INSTANCE
+ .deserialize(new DataInputStream(new ByteArrayInputStream(openBytes,
+ (int) openPartOffsets[i], openFieldNameLengths[i])));
+ throw new HyracksDataException("Open fields " + (i - 1) + " and " + i
+ + " have the same field name \"" + field + "\"");
+ }
+ }
+ }
openPartOffset = h + numberOfSchemaFields * 4 + closedPartOutputStream.size();
+ int fieldNameHashCode;
for (int i = 0; i < numberOfOpenFields; i++) {
fieldNameHashCode = (int) (openPartOffsets[i] >> 32);
SerializerDeserializerUtil.writeIntToByteArray(openPartOffsetArray, (int) fieldNameHashCode,
offsetPosition);
- int fieldOffset = (int) ((openPartOffsets[i] << 64) >> 64);
+ int fieldOffset = (int) openPartOffsets[i];
SerializerDeserializerUtil.writeIntToByteArray(openPartOffsetArray, fieldOffset + openPartOffset + 4
+ openPartOffsetArraySize, offsetPosition + 4);
offsetPosition += 8;