IGNITE-22040: KeyValue/RecordView. Improve error messages for constraint violation errors. (#3619)
diff --git a/modules/client/src/test/java/org/apache/ignite/client/ClientKeyValueViewTest.java b/modules/client/src/test/java/org/apache/ignite/client/ClientKeyValueViewTest.java
index 3af37ae..88bafa3 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/ClientKeyValueViewTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/ClientKeyValueViewTest.java
@@ -28,7 +28,6 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.math.BigDecimal;
@@ -105,10 +104,12 @@
key.id = "1";
key.gid = 1;
- IgniteException e = assertThrows(IgniteException.class, () -> pojoView.get(null, key));
- assertEquals("Failed to deserialize server response: No mapped object field found for column 'ZBOOLEAN'", e.getMessage());
+ Throwable e = assertThrowsWithCause(
+ () -> pojoView.get(null, key),
+ IgniteException.class,
+ "Failed to deserialize server response: No mapped object field found for column 'ZBOOLEAN'"
+ );
assertThat(Arrays.asList(e.getStackTrace()), anyOf(hasToString(containsString("ClientKeyValueView"))));
-
}
@Test
@@ -206,9 +207,11 @@
public void testMissingKeyColumnThrowsException() {
var kvView = defaultTable().keyValueView(NamePojo.class, NamePojo.class);
- IgniteException e = assertThrows(IgniteException.class, () -> kvView.get(null, new NamePojo()));
-
- assertThat(e.getMessage(), containsString("No mapped object field found for column 'ID'"));
+ Throwable e = assertThrowsWithCause(
+ () -> kvView.get(null, new NamePojo()),
+ IgniteException.class,
+ "No mapped object field found for column 'ID'"
+ );
assertThat(Arrays.asList(e.getStackTrace()), anyOf(hasToString(containsString("ClientKeyValueView"))));
}
@@ -510,9 +513,11 @@
var pojo = new DefaultValuesValPojo();
pojo.strNonNull = null;
- var ex = assertThrows(IgniteException.class, () -> pojoView.put(null, 1, pojo));
-
- assertTrue(ex.getMessage().contains("null was passed, but column is not nullable"), ex.getMessage());
+ var ex = assertThrowsWithCause(
+ () -> pojoView.put(null, 1, pojo),
+ IgniteException.class,
+ "Column 'STRNONNULL' does not allow NULLs"
+ );
assertThat(Arrays.asList(ex.getStackTrace()), anyOf(hasToString(containsString("ClientKeyValueView"))));
}
diff --git a/modules/client/src/test/java/org/apache/ignite/client/ClientRecordViewTest.java b/modules/client/src/test/java/org/apache/ignite/client/ClientRecordViewTest.java
index 0e78078..0c9243f 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/ClientRecordViewTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/ClientRecordViewTest.java
@@ -527,7 +527,7 @@
var ex = assertThrows(IgniteException.class, () -> pojoView.upsert(null, pojo));
- assertTrue(ex.getMessage().contains("null was passed, but column is not nullable"), ex.getMessage());
+ assertThat(ex.getMessage(), containsString("Column 'STRNONNULL' does not allow NULLs"));
assertThat(Arrays.asList(ex.getStackTrace()), anyOf(hasToString(containsString("ClientRecordView"))));
}
}
diff --git a/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java b/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
index 77a5ab7..a23653e 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/ClientTableTest.java
@@ -400,7 +400,7 @@
var ex = assertThrows(IgniteException.class, () -> table.upsert(null, tuple));
- assertTrue(ex.getMessage().contains("null was passed, but column is not nullable"), ex.getMessage());
+ assertTrue(ex.getMessage().contains("Column 'STRNONNULL' does not allow NULLs"), ex.getMessage());
}
@Test
diff --git a/modules/marshaller-common/src/main/java/org/apache/ignite/internal/marshaller/FieldAccessor.java b/modules/marshaller-common/src/main/java/org/apache/ignite/internal/marshaller/FieldAccessor.java
index d6712b5..8340ac4 100644
--- a/modules/marshaller-common/src/main/java/org/apache/ignite/internal/marshaller/FieldAccessor.java
+++ b/modules/marshaller-common/src/main/java/org/apache/ignite/internal/marshaller/FieldAccessor.java
@@ -398,7 +398,7 @@
try {
write0(writer, obj);
} catch (Exception ex) {
- throw new MarshallerException("Failed to write field [id=" + colIdx + ']', ex);
+ throw new MarshallerException(ex.getMessage(), ex);
}
}
@@ -422,7 +422,7 @@
try {
read0(reader, obj);
} catch (Exception ex) {
- throw new MarshallerException("Failed to read field [id=" + colIdx + ']', ex);
+ throw new MarshallerException(ex.getMessage(), ex);
}
}
diff --git a/modules/marshaller-common/src/test/java/org/apache/ignite/internal/marshaller/FieldAccessorTest.java b/modules/marshaller-common/src/test/java/org/apache/ignite/internal/marshaller/FieldAccessorTest.java
index bb21ffc..a2c804b 100644
--- a/modules/marshaller-common/src/test/java/org/apache/ignite/internal/marshaller/FieldAccessorTest.java
+++ b/modules/marshaller-common/src/test/java/org/apache/ignite/internal/marshaller/FieldAccessorTest.java
@@ -294,7 +294,7 @@
assertThrowsWithCause(
() -> accessor.write(mocks.getFirst(), "Other string"),
MarshallerException.class,
- "Failed to write field [id=42]"
+ "class java.lang.String cannot be cast to class java.util.UUID"
);
}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/SchemaValidationTest.cs b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/SchemaValidationTest.cs
index a512f72..e10e12d 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/Table/SchemaValidationTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/Table/SchemaValidationTest.cs
@@ -119,8 +119,7 @@
};
var ex = Assert.ThrowsAsync<MarshallerException>(async () => await TableRequiredVal.RecordBinaryView.UpsertAsync(null, igniteTuple));
- StringAssert.StartsWith("Failed to set column (null was passed, but column is not null", ex!.Message);
- StringAssert.Contains("name=VAL", ex.Message);
+ StringAssert.StartsWith("Column 'VAL' does not allow NULLs", ex!.Message);
}
[Test]
@@ -149,8 +148,7 @@
var ex = Assert.ThrowsAsync<MarshallerException>(
async () => await TableRequiredVal.KeyValueBinaryView.PutAsync(null, keyTuple, valTuple));
- StringAssert.StartsWith("Failed to set column (null was passed, but column is not null", ex!.Message);
- StringAssert.Contains("name=VAL", ex.Message);
+ StringAssert.StartsWith("Column 'VAL' does not allow NULLs", ex!.Message);
}
[Test]
@@ -259,8 +257,7 @@
var ex = Assert.ThrowsAsync<MarshallerException>(
async () => await TableRequiredVal.GetRecordView<KeyPoco>().UpsertAsync(null, new KeyPoco()));
- StringAssert.StartsWith("Failed to set column (null was passed, but column is not null", ex!.Message);
- StringAssert.Contains("name=VAL", ex.Message);
+ StringAssert.StartsWith("Column 'VAL' does not allow NULLs", ex!.Message);
}
[Test]
@@ -278,8 +275,7 @@
var ex = Assert.ThrowsAsync<MarshallerException>(
async () => await TableRequiredVal.GetKeyValueView<long, KeyPoco>().PutAsync(null, 1L, new KeyPoco()));
- StringAssert.StartsWith("Failed to set column (null was passed, but column is not null", ex!.Message);
- StringAssert.Contains("name=VAL", ex.Message);
+ StringAssert.StartsWith("Column 'VAL' does not allow NULLs", ex!.Message);
}
[Test]
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientMarshallingTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientMarshallingTest.java
index 81bc047..b966841 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientMarshallingTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientMarshallingTest.java
@@ -19,6 +19,7 @@
import static org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrowsWithCause;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -164,9 +165,7 @@
var tupleView = table.recordView();
Throwable ex = assertThrowsWithCause(() -> tupleView.upsert(null, Tuple.create().set("KEY", 1)), IgniteException.class);
- assertThat(ex.getMessage(), startsWith(
- "Failed to set column (null was passed, but column is not nullable): "
- + "[col=Column [rowPosition=1, keyPosition=-1, valuePosition=0, colocationPosition=-1, name=VAL"));
+ assertThat(ex.getMessage(), containsString("Column 'VAL' does not allow NULLs"));
}
@Test
@@ -190,9 +189,7 @@
() -> tupleView.put(null, Tuple.create().set("KEY", 1), Tuple.create()),
IgniteException.class);
- assertThat(ex.getMessage(), startsWith(
- "Failed to set column (null was passed, but column is not nullable): "
- + "[col=Column [rowPosition=1, keyPosition=-1, valuePosition=0, colocationPosition=-1, name=VAL"));
+ assertThat(ex.getMessage(), containsString("Column 'VAL' does not allow NULLs"));
}
@Test
@@ -276,9 +273,7 @@
var tupleView = table.recordView();
Throwable ex = assertThrowsWithCause(() -> tupleView.upsert(null, Tuple.create().set("KEY", null)), IgniteException.class);
- assertThat(ex.getMessage(), startsWith(
- "Failed to set column (null was passed, but column is not nullable): "
- + "[col=Column [rowPosition=0, keyPosition=0, valuePosition=-1, colocationPosition=0, name=KEY"));
+ assertThat(ex.getMessage(), containsString("Column 'KEY' does not allow NULLs"));
}
@Test
@@ -291,9 +286,7 @@
Tuple rec = Tuple.create().set("KEY", 1).set("VAL", null);
Throwable ex = assertThrowsWithCause(() -> tupleView.upsert(null, rec), IgniteException.class);
- assertThat(ex.getMessage(), startsWith(
- "Failed to set column (null was passed, but column is not nullable): "
- + "[col=Column [rowPosition=1, keyPosition=-1, valuePosition=0, colocationPosition=-1, name=VAL"));
+ assertThat(ex.getMessage(), containsString("Column 'VAL' does not allow NULLs"));
}
@Test
@@ -317,9 +310,7 @@
() -> tupleView.put(null, Tuple.create().set("KEY", 1), Tuple.create().set("VAL", null)),
IgniteException.class);
- assertThat(ex.getMessage(), startsWith(
- "Failed to set column (null was passed, but column is not nullable): "
- + "[col=Column [rowPosition=1, keyPosition=-1, valuePosition=0, colocationPosition=-1, name=VAL"));
+ assertThat(ex.getMessage(), containsString("Column 'VAL' does not allow NULLs"));
}
private static class TestPojo2 {
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/Column.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/Column.java
index ec7ca7f..2f371a6 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/Column.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/Column.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.schema;
+import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
+
import org.apache.ignite.internal.tostring.IgniteToStringExclude;
import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.internal.type.NativeType;
@@ -199,8 +201,7 @@
*/
public void validate(@Nullable Object val) {
if (val == null && !nullable) {
- throw new IllegalArgumentException("Failed to set column (null was passed, but column is not nullable): "
- + "[col=" + this + ']');
+ throw new SchemaMismatchException(nullConstraintViolationMessage(name));
}
NativeType objType = NativeTypes.fromObject(val);
@@ -239,4 +240,14 @@
public String toString() {
return S.toString(Column.class, this);
}
+
+ /**
+ * Returns an error message for NOT NULL constraint violation.
+ *
+ * @param columnName Column name.
+ * @return Error message.
+ */
+ public static String nullConstraintViolationMessage(String columnName) {
+ return format("Column '{}' does not allow NULLs", columnName);
+ }
}
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/row/RowAssembler.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/row/RowAssembler.java
index 8dedf88..1ce7eb3 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/row/RowAssembler.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/row/RowAssembler.java
@@ -165,8 +165,8 @@
*/
public RowAssembler appendNull() throws SchemaMismatchException {
if (!columns.get(curCol).nullable()) {
- throw new SchemaMismatchException(
- "Failed to set column (null was passed, but column is not nullable): " + columns.get(curCol));
+ String name = columns.get(curCol).name();
+ throw new SchemaMismatchException(Column.nullConstraintViolationMessage(name));
}
builder.appendNull();
diff --git a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItNotNullConstraintClientTest.java b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItNotNullConstraintClientTest.java
index dec43c7..9fe2090 100644
--- a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItNotNullConstraintClientTest.java
+++ b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItNotNullConstraintClientTest.java
@@ -21,8 +21,6 @@
import org.apache.ignite.client.IgniteClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
/**
* NOT NULL constraint test for thin client API.
@@ -47,33 +45,4 @@
protected Ignite ignite() {
return client;
}
-
- // TODO https://issues.apache.org/jira/browse/IGNITE-22040 is resolved error messages for client and server API should be the same.
- @Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-22040")
- @Override
- public void testKeyValueView() {
- super.testKeyValueView();
- }
-
- @Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-22040")
- @Override
- public void testRecordView() {
- super.testRecordView();
- }
-
- @Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-22040")
- @Override
- public void testKeyValueViewDataStreamer() {
- super.testRecordView();
- }
-
- @Test
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-22040")
- @Override
- public void testRecordViewDataStreamer() {
- super.testRecordView();
- }
}
diff --git a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItNotNullConstraintTest.java b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItNotNullConstraintTest.java
index d9b6e0d..ee98b8a 100644
--- a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItNotNullConstraintTest.java
+++ b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItNotNullConstraintTest.java
@@ -143,14 +143,14 @@
KeyValueView<Integer, Integer> view = table.keyValueView(Integer.class, Integer.class);
assertThrows(MarshallerException.class, () -> {
view.put(null, 1, null);
- }, "Failed to write field [id=1]");
+ }, "Column 'VAL' does not allow NULLs");
}
{
KeyValueView<Tuple, Tuple> view = table.keyValueView();
assertThrows(MarshallerException.class, () -> {
view.put(null, Tuple.create(Map.of("id", 1)), Tuple.create());
- }, "Failed to set column (null was passed, but column is not nullable)");
+ }, "Column 'VAL' does not allow NULLs");
}
{
@@ -158,7 +158,7 @@
assertThrows(MarshallerException.class, () -> {
Val val = new Val();
view.put(null, 1, val);
- }, "Failed to write field [id=0]");
+ }, "Column 'VAL' does not allow NULLs");
}
}
@@ -174,7 +174,7 @@
assertThrows(MarshallerException.class, () -> {
Rec rec = new Rec();
view.insert(null, rec);
- }, "Failed to write field [id=0]");
+ }, "Column 'ID' does not allow NULLs");
}
{
@@ -183,14 +183,14 @@
Rec rec = new Rec();
rec.id = 42;
view.insert(null, rec);
- }, "Failed to write field [id=1]");
+ }, "Column 'VAL' does not allow NULLs");
}
{
RecordView<Tuple> view = table.recordView();
assertThrows(MarshallerException.class, () -> {
view.insert(null, Tuple.create(Map.of("id", 1)));
- }, "Failed to set column (null was passed, but column is not nullable)");
+ }, "Column 'VAL' does not allow NULLs");
}
}
@@ -203,19 +203,18 @@
{
KeyValueView<Tuple, Tuple> view = table.keyValueView();
- checkDataStreamer(view, new SimpleEntry<>(Tuple.create(Map.of("id", 1)), Tuple.create()),
- "Failed to set column (null was passed, but column is not nullable)");
+ checkDataStreamer(view, new SimpleEntry<>(Tuple.create(Map.of("id", 1)), Tuple.create()), "Column 'VAL' does not allow NULLs");
}
{
KeyValueView<Integer, Integer> view = table.keyValueView(Integer.class, Integer.class);
- checkDataStreamer(view, new SimpleEntry<>(1, null), "Failed to write field [id=1]");
+ checkDataStreamer(view, new SimpleEntry<>(1, null), "Column 'VAL' does not allow NULLs");
}
{
KeyValueView<Integer, Val> view = table.keyValueView(Integer.class, Val.class);
Val val = new Val();
- checkDataStreamer(view, new SimpleEntry<>(1, val), "Failed to write field [id=0]");
+ checkDataStreamer(view, new SimpleEntry<>(1, val), "Column 'VAL' does not allow NULLs");
}
}
@@ -227,20 +226,20 @@
{
RecordView<Tuple> view = table.recordView();
- checkDataStreamer(view, Tuple.create(Map.of("id", 1)), "Failed to set column (null was passed, but column is not nullable)");
+ checkDataStreamer(view, Tuple.create(Map.of("id", 1)), "Column 'VAL' does not allow NULLs");
}
{
RecordView<Rec> view = table.recordView(Rec.class);
Rec rec = new Rec();
- checkDataStreamer(view, rec, "Failed to write field [id=0]");
+ checkDataStreamer(view, rec, "Column 'ID' does not allow NULLs");
}
{
RecordView<Rec> view = table.recordView(Rec.class);
Rec rec = new Rec();
rec.id = 1;
- checkDataStreamer(view, rec, "Failed to write field [id=1]");
+ checkDataStreamer(view, rec, "Column 'VAL' does not allow NULLs");
}
}
diff --git a/modules/sql-engine/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties b/modules/sql-engine/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
index 140e83d..2671960 100644
--- a/modules/sql-engine/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ b/modules/sql-engine/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -16,5 +16,6 @@
#
# Overrides messages used by Apache Calcite.
-#
+# This message should be the same as or very similar to the one produced by
+# org.apache.ignite.internal.schema.Column::nullConstraintViolationMessage
ColumnNotNullable=Column ''{0}'' does not allow NULLs
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/KeyValueViewImpl.java b/modules/table/src/main/java/org/apache/ignite/internal/table/KeyValueViewImpl.java
index 78b719d..f521a79 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/KeyValueViewImpl.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/KeyValueViewImpl.java
@@ -50,7 +50,6 @@
import org.apache.ignite.internal.thread.PublicApiThreading;
import org.apache.ignite.internal.tx.InternalTransaction;
import org.apache.ignite.internal.util.IgniteUtils;
-import org.apache.ignite.lang.IgniteException;
import org.apache.ignite.lang.NullableValue;
import org.apache.ignite.lang.UnexpectedNullValueException;
import org.apache.ignite.sql.IgniteSql;
@@ -500,7 +499,7 @@
try {
return marsh.marshal(key);
} catch (MarshallerException e) {
- throw new IgniteException(e);
+ throw new org.apache.ignite.lang.MarshallerException(e);
}
}
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/SchemaValidationTest.java b/modules/table/src/test/java/org/apache/ignite/internal/table/SchemaValidationTest.java
index 7d280be..582d7fc 100644
--- a/modules/table/src/test/java/org/apache/ignite/internal/table/SchemaValidationTest.java
+++ b/modules/table/src/test/java/org/apache/ignite/internal/table/SchemaValidationTest.java
@@ -100,7 +100,7 @@
RecordView<Tuple> tbl = createTable(schema).recordView();
// Check not-nullable column.
- assertThrowsWithCause(IllegalArgumentException.class, () -> tbl.insert(null, Tuple.create().set("id", null)));
+ assertThrowsWithCause(SchemaMismatchException.class, () -> tbl.insert(null, Tuple.create().set("id", null)));
// Check length of the string column
assertThrowsWithCause(InvalidTypeException.class,