From 8db0a5da8ae45deeacca0b0af2b6767f6f67e206 Mon Sep 17 00:00:00 2001 From: yyin Date: Mon, 15 Sep 2025 18:27:46 +0800 Subject: [PATCH] feat(QTDI-1672): Allow disable nullable check in tck framework level (#1086) * feat(QTDI-1672): Allow disable nullable check in tck framework level (cherry picked from commit 914dc7481236c09443faca4128fcb6a4a1528cf2) --- .../sdk/component/api/record/Record.java | 2 + .../component/runtime/record/RecordImpl.java | 18 +++++-- .../runtime/record/RecordBuilderImplTest.java | 51 +++++++++++++++++++ 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/component-api/src/main/java/org/talend/sdk/component/api/record/Record.java b/component-api/src/main/java/org/talend/sdk/component/api/record/Record.java index 341eb178d0783..aa3472326d2f9 100644 --- a/component-api/src/main/java/org/talend/sdk/component/api/record/Record.java +++ b/component-api/src/main/java/org/talend/sdk/component/api/record/Record.java @@ -36,6 +36,8 @@ public interface Record { String RECORD_ERROR_SUPPORT = "talend.component.record.error.support"; + String RECORD_NULLABLE_CHECK = "talend.component.record.nullable.check"; + /** * @return the schema of this record. */ diff --git a/component-runtime-impl/src/main/java/org/talend/sdk/component/runtime/record/RecordImpl.java b/component-runtime-impl/src/main/java/org/talend/sdk/component/runtime/record/RecordImpl.java index 5ca694666e359..290a65069c92c 100644 --- a/component-runtime-impl/src/main/java/org/talend/sdk/component/runtime/record/RecordImpl.java +++ b/component-runtime-impl/src/main/java/org/talend/sdk/component/runtime/record/RecordImpl.java @@ -123,6 +123,9 @@ public Builder withNewSchema(final Schema newSchema) { // Entry creation can be optimized a bit but recent GC should not see it as a big deal public static class BuilderImpl implements Builder { + private final boolean skipNullCheck = + Boolean.parseBoolean(System.getProperty(Record.RECORD_NULLABLE_CHECK, "false")); + private final Map values = new HashMap<>(8); private final OrderedMap entries; @@ -327,7 +330,7 @@ private Schema.Entry validateTypeAgainstProvidedSchema(final String name, final "Entry '" + entry.getOriginalFieldName() + "' expected to be a " + entry.getType() + ", got a " + type); } - if (value == null && !entry.isNullable()) { + if (!skipNullCheck && value == null && !entry.isNullable()) { throw new IllegalArgumentException("Entry '" + entry.getOriginalFieldName() + "' is not nullable"); } return entry; @@ -357,7 +360,7 @@ public Record build() { .filter(it -> !it.isNullable() && !values.containsKey(it.getName())) .map(Schema.Entry::getName) .collect(joining(", ")); - if (!missing.isEmpty()) { + if (!skipNullCheck && !missing.isEmpty()) { throw new IllegalArgumentException("Missing entries: " + missing); } @@ -587,10 +590,15 @@ private Builder append(final Schema.Entry entry, final T value) { } else { realEntry = entry; } - if (value != null) { + + if (skipNullCheck) { values.put(realEntry.getName(), value); - } else if (!realEntry.isNullable()) { - throw new IllegalArgumentException(realEntry.getName() + " is not nullable but got a null value"); + } else { + if (value != null) { + values.put(realEntry.getName(), value); + } else if (!realEntry.isNullable()) { + throw new IllegalArgumentException(realEntry.getName() + " is not nullable but got a null value"); + } } if (this.entries != null) { diff --git a/component-runtime-impl/src/test/java/org/talend/sdk/component/runtime/record/RecordBuilderImplTest.java b/component-runtime-impl/src/test/java/org/talend/sdk/component/runtime/record/RecordBuilderImplTest.java index 0300000cf8e59..da3a12c8f8224 100644 --- a/component-runtime-impl/src/test/java/org/talend/sdk/component/runtime/record/RecordBuilderImplTest.java +++ b/component-runtime-impl/src/test/java/org/talend/sdk/component/runtime/record/RecordBuilderImplTest.java @@ -232,6 +232,57 @@ void notNullableNullBehavior() { .withString(new SchemaImpl.EntryImpl.BuilderImpl().withNullable(false).withName("test").build(), null)); } + @Test + void disableNullableCheck_no() { + RecordBuilderFactoryImpl recordBuilderFactory = new RecordBuilderFactoryImpl("test"); + Schema.Builder schemaBuilder = recordBuilderFactory.newSchemaBuilder(Type.RECORD); + Schema schema = schemaBuilder.withEntry( + recordBuilderFactory.newEntryBuilder().withType(Type.STRING).withName("c1").withNullable(false).build()) + .withEntry(recordBuilderFactory.newEntryBuilder() + .withName("c2") + .withType(Type.STRING) + .withNullable(true) + .build()) + .build(); + Record.Builder recordBuilder = recordBuilderFactory.newRecordBuilder(schema); + recordBuilder.withString("c1", "v1"); + recordBuilder.build(); + + Schema.Builder schemaBuilder2 = recordBuilderFactory.newSchemaBuilder(Type.RECORD); + Schema schema2 = schemaBuilder2.withEntry( + recordBuilderFactory.newEntryBuilder().withType(Type.STRING).withName("c1").withNullable(false).build()) + .withEntry(recordBuilderFactory.newEntryBuilder() + .withName("c2") + .withType(Type.STRING) + .withNullable(false) + .build()) + .build(); + Record.Builder recordBuilder2 = recordBuilderFactory.newRecordBuilder(schema2); + recordBuilder2.withString("c1", "v1"); + assertThrows(IllegalArgumentException.class, recordBuilder2::build); + } + + @Test + void disableNullableCheck_yes() { + System.setProperty(Record.RECORD_NULLABLE_CHECK, "true"); + RecordBuilderFactoryImpl recordBuilderFactory = new RecordBuilderFactoryImpl("test"); + Schema.Builder schemaBuilder = recordBuilderFactory.newSchemaBuilder(Type.RECORD); + Schema schema = schemaBuilder.withEntry( + recordBuilderFactory.newEntryBuilder().withType(Type.STRING).withName("c1").withNullable(false).build()) + .withEntry(recordBuilderFactory.newEntryBuilder() + .withName("c2") + .withType(Type.STRING) + .withNullable(false) + .build()) + .build(); + Record.Builder recordBuilder = recordBuilderFactory.newRecordBuilder(schema); + recordBuilder.withString("c1", "v1"); + recordBuilder.withString("c2", null); + Record recordOne = recordBuilder.build(); + assertNull(recordOne.getString("c2")); + System.setProperty(Record.RECORD_NULLABLE_CHECK, "false"); + } + @Test void dateTime() { final Schema schema = new SchemaImpl.BuilderImpl()