You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flink.apache.org by tw...@apache.org on 2019/06/19 08:15:01 UTC

[flink] branch master updated: [FLINK-12874][table-common] Improve the semantics of zero length strings

This is an automated email from the ASF dual-hosted git repository.

twalthr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/master by this push:
     new 2a3ac58  [FLINK-12874][table-common] Improve the semantics of zero length strings
2a3ac58 is described below

commit 2a3ac580abf5b32c341fe921c1eacbb395b27f25
Author: Timo Walther <tw...@apache.org>
AuthorDate: Tue Jun 18 11:08:27 2019 +0200

    [FLINK-12874][table-common] Improve the semantics of zero length strings
    
    This closes #8781.
---
 .../flink/table/types/logical/BinaryType.java      | 12 ++++++
 .../apache/flink/table/types/logical/CharType.java | 12 ++++++
 .../flink/table/types/logical/VarBinaryType.java   | 35 +++++++++++++++++-
 .../flink/table/types/logical/VarCharType.java     | 34 ++++++++++++++++-
 .../apache/flink/table/types/LogicalTypesTest.java | 43 ++++++++++++++++++++--
 5 files changed, 131 insertions(+), 5 deletions(-)

diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/BinaryType.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/BinaryType.java
index 25dbcd4..8a013c6 100644
--- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/BinaryType.java
+++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/BinaryType.java
@@ -19,6 +19,7 @@
 package org.apache.flink.table.types.logical;
 
 import org.apache.flink.annotation.PublicEvolving;
+import org.apache.flink.table.api.TableException;
 import org.apache.flink.table.api.ValidationException;
 
 import java.util.Collections;
@@ -91,6 +92,8 @@ public final class BinaryType extends LogicalType {
 	 * For consistent behavior, the same logic applies to binary strings.
 	 *
 	 * <p>This method enables this special kind of binary string.
+	 *
+	 * <p>Zero-length binary strings have no serializable string representation.
 	 */
 	public static BinaryType ofEmptyLiteral() {
 		return new BinaryType(EMPTY_LITERAL_LENGTH, false);
@@ -107,6 +110,15 @@ public final class BinaryType extends LogicalType {
 
 	@Override
 	public String asSerializableString() {
+		if (length == EMPTY_LITERAL_LENGTH) {
+			throw new TableException(
+				"Zero-length binary strings have no serializable string representation.");
+		}
+		return withNullability(FORMAT, length);
+	}
+
+	@Override
+	public String asSummaryString() {
 		return withNullability(FORMAT, length);
 	}
 
diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/CharType.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/CharType.java
index 8385d05..fbff544 100644
--- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/CharType.java
+++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/CharType.java
@@ -19,6 +19,7 @@
 package org.apache.flink.table.types.logical;
 
 import org.apache.flink.annotation.PublicEvolving;
+import org.apache.flink.table.api.TableException;
 import org.apache.flink.table.api.ValidationException;
 
 import java.util.Collections;
@@ -93,6 +94,8 @@ public final class CharType extends LogicalType {
 	 * (i.e., to contain no characters) even though it is not permitted to declare a type that is zero.
 	 *
 	 * <p>This method enables this special kind of character string.
+	 *
+	 * <p>Zero-length character strings have no serializable string representation.
 	 */
 	public static CharType ofEmptyLiteral() {
 		return new CharType(EMPTY_LITERAL_LENGTH, false);
@@ -109,6 +112,15 @@ public final class CharType extends LogicalType {
 
 	@Override
 	public String asSerializableString() {
+		if (length == EMPTY_LITERAL_LENGTH) {
+			throw new TableException(
+				"Zero-length character strings have no serializable string representation.");
+		}
+		return withNullability(FORMAT, length);
+	}
+
+	@Override
+	public String asSummaryString() {
 		return withNullability(FORMAT, length);
 	}
 
diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/VarBinaryType.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/VarBinaryType.java
index 4928fd4..538d947 100644
--- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/VarBinaryType.java
+++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/VarBinaryType.java
@@ -19,6 +19,7 @@
 package org.apache.flink.table.types.logical;
 
 import org.apache.flink.annotation.PublicEvolving;
+import org.apache.flink.table.api.TableException;
 import org.apache.flink.table.api.ValidationException;
 
 import java.util.Collections;
@@ -33,10 +34,15 @@ import java.util.Set;
  * number of bytes. {@code n} must have a value between 1 and {@link Integer#MAX_VALUE} (both
  * inclusive). If no length is specified, {@code n} is equal to 1. {@code BYTES} is a synonym for
  * {@code VARBINARY(2147483647)}.
+ *
+ * <p>For enabling type inference of a zero-length binary string literal to a variable-length binary
+ * string, this type does also support {@code n} to be 0. However, this is not exposed through the API.
  */
 @PublicEvolving
 public final class VarBinaryType extends LogicalType {
 
+	public static final int EMPTY_LITERAL_LENGTH = 0;
+
 	public static final int MIN_LENGTH = 1;
 
 	public static final int MAX_LENGTH = Integer.MAX_VALUE;
@@ -75,17 +81,44 @@ public final class VarBinaryType extends LogicalType {
 		this(DEFAULT_LENGTH);
 	}
 
+	/**
+	 * Helper constructor for {@link #ofEmptyLiteral()} and {@link #copy(boolean)}.
+	 */
+	private VarBinaryType(int length, boolean isNullable) {
+		super(isNullable, LogicalTypeRoot.VARBINARY);
+		this.length = length;
+	}
+
+	/**
+	 * The SQL standard defines that character string literals are allowed to be zero-length strings
+	 * (i.e., to contain no characters) even though it is not permitted to declare a type that is zero.
+	 * For consistent behavior, the same logic applies to binary strings. This has also implications
+	 * on variable-length binary strings during type inference because any fixed-length binary string
+	 * should be convertible to a variable-length one.
+	 *
+	 * <p>This method enables this special kind of binary string.
+	 *
+	 * <p>Zero-length binary strings have no serializable string representation.
+	 */
+	public static VarBinaryType ofEmptyLiteral() {
+		return new VarBinaryType(EMPTY_LITERAL_LENGTH, false);
+	}
+
 	public int getLength() {
 		return length;
 	}
 
 	@Override
 	public LogicalType copy(boolean isNullable) {
-		return new VarBinaryType(isNullable, length);
+		return new VarBinaryType(length, isNullable);
 	}
 
 	@Override
 	public String asSerializableString() {
+		if (length == EMPTY_LITERAL_LENGTH) {
+			throw new TableException(
+				"Zero-length binary strings have no serializable string representation.");
+		}
 		return withNullability(FORMAT, length);
 	}
 
diff --git a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/VarCharType.java b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/VarCharType.java
index 1e1f04c..2c73ec6 100644
--- a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/VarCharType.java
+++ b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/VarCharType.java
@@ -19,6 +19,7 @@
 package org.apache.flink.table.types.logical;
 
 import org.apache.flink.annotation.PublicEvolving;
+import org.apache.flink.table.api.TableException;
 import org.apache.flink.table.api.ValidationException;
 
 import java.util.Collections;
@@ -34,11 +35,16 @@ import java.util.Set;
  * inclusive). If no length is specified, {@code n} is equal to 1. {@code STRING} is a synonym for
  * {@code VARCHAR(2147483647)}.
  *
+ * <p>For enabling type inference of a zero-length character string literal to a variable-length character
+ * string, this type does also support {@code n} to be 0. However, this is not exposed through the API.
+ *
  * <p>A conversion from and to {@code byte[]} assumes UTF-8 encoding.
  */
 @PublicEvolving
 public final class VarCharType extends LogicalType {
 
+	public static final int EMPTY_LITERAL_LENGTH = 0;
+
 	public static final int MIN_LENGTH = 1;
 
 	public static final int MAX_LENGTH = Integer.MAX_VALUE;
@@ -78,17 +84,43 @@ public final class VarCharType extends LogicalType {
 		this(DEFAULT_LENGTH);
 	}
 
+	/**
+	 * Helper constructor for {@link #ofEmptyLiteral()} and {@link #copy(boolean)}.
+	 */
+	private VarCharType(int length, boolean isNullable) {
+		super(isNullable, LogicalTypeRoot.VARCHAR);
+		this.length = length;
+	}
+
+	/**
+	 * The SQL standard defines that character string literals are allowed to be zero-length strings
+	 * (i.e., to contain no characters) even though it is not permitted to declare a type that is zero.
+	 * This has also implications on variable-length character strings during type inference because any
+	 * fixed-length character string should be convertible to a variable-length one.
+	 *
+	 * <p>This method enables this special kind of character string.
+	 *
+	 * <p>Zero-length character strings have no serializable string representation.
+	 */
+	public static VarCharType ofEmptyLiteral() {
+		return new VarCharType(EMPTY_LITERAL_LENGTH, false);
+	}
+
 	public int getLength() {
 		return length;
 	}
 
 	@Override
 	public LogicalType copy(boolean isNullable) {
-		return new VarCharType(isNullable, length);
+		return new VarCharType(length, isNullable);
 	}
 
 	@Override
 	public String asSerializableString() {
+		if (length == EMPTY_LITERAL_LENGTH) {
+			throw new TableException(
+				"Zero-length character strings have no serializable string representation.");
+		}
 		return withNullability(FORMAT, length);
 	}
 
diff --git a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/LogicalTypesTest.java b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/LogicalTypesTest.java
index bdc9d90..edf28cf 100644
--- a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/LogicalTypesTest.java
+++ b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/LogicalTypesTest.java
@@ -23,6 +23,7 @@ import org.apache.flink.api.common.typeinfo.Types;
 import org.apache.flink.api.java.tuple.Tuple;
 import org.apache.flink.api.java.tuple.Tuple2;
 import org.apache.flink.api.java.typeutils.runtime.kryo.KryoSerializer;
+import org.apache.flink.table.api.TableException;
 import org.apache.flink.table.api.ValidationException;
 import org.apache.flink.table.catalog.ObjectIdentifier;
 import org.apache.flink.table.expressions.TimeIntervalUnit;
@@ -61,7 +62,6 @@ import org.apache.flink.table.types.logical.ZonedTimestampType;
 import org.apache.flink.types.Row;
 import org.apache.flink.util.InstantiationUtil;
 
-import org.junit.Assert;
 import org.junit.Test;
 
 import java.math.BigDecimal;
@@ -564,6 +564,8 @@ public class LogicalTypesTest {
 		testJavaSerializability(anyType);
 
 		testConversions(anyType, new Class[]{Tuple2.class}, new Class[]{Tuple.class});
+
+		testInvalidStringSerializability(anyType);
 	}
 
 	@Test
@@ -603,6 +605,32 @@ public class LogicalTypesTest {
 		testJavaSerializability(symbolType);
 
 		testConversions(symbolType, new Class[]{TimeIntervalUnit.class}, new Class[]{TimeIntervalUnit.class});
+
+		testInvalidStringSerializability(symbolType);
+	}
+
+	@Test
+	public void testEmptyStringLiterals() {
+		final CharType charType = CharType.ofEmptyLiteral();
+		final VarCharType varcharType = VarCharType.ofEmptyLiteral();
+		final BinaryType binaryType = BinaryType.ofEmptyLiteral();
+		final VarBinaryType varBinaryType = VarBinaryType.ofEmptyLiteral();
+
+		// make the types nullable for testing
+		testEquality(charType.copy(true), new CharType(1));
+		testEquality(varcharType.copy(true), new VarCharType(1));
+		testEquality(binaryType.copy(true), new BinaryType(1));
+		testEquality(varBinaryType.copy(true), new VarBinaryType(1));
+
+		testStringSummary(charType, "CHAR(0) NOT NULL");
+		testStringSummary(varcharType, "VARCHAR(0) NOT NULL");
+		testStringSummary(binaryType, "BINARY(0) NOT NULL");
+		testStringSummary(varBinaryType, "VARBINARY(0) NOT NULL");
+
+		testInvalidStringSerializability(charType);
+		testInvalidStringSerializability(varcharType);
+		testInvalidStringSerializability(binaryType);
+		testInvalidStringSerializability(varBinaryType);
 	}
 
 	// --------------------------------------------------------------------------------------------
@@ -664,11 +692,20 @@ public class LogicalTypesTest {
 	}
 
 	private static void testStringSerializability(LogicalType serializableType, String serializableString) {
-		Assert.assertEquals(serializableString, serializableType.asSerializableString());
+		assertEquals(serializableString, serializableType.asSerializableString());
+	}
+
+	private static void testInvalidStringSerializability(LogicalType nonSerializableType) {
+		try {
+			final String serializedString = nonSerializableType.asSerializableString();
+			fail("No serializablility expected: " + serializedString);
+		} catch (TableException e) {
+			// ok
+		}
 	}
 
 	private static void testStringSummary(LogicalType type, String summaryString) {
-		Assert.assertEquals(summaryString, type.asSummaryString());
+		assertEquals(summaryString, type.asSummaryString());
 	}
 
 	private static void testConversions(LogicalType type, Class[] inputs, Class[] outputs) {