Skip to content

Commit b3d7bf1

Browse files
authored
IGNITE-25016 Relax Mapper requirements and allow unmapped columns (#5564)
1 parent 315d953 commit b3d7bf1

File tree

12 files changed

+152
-46
lines changed

12 files changed

+152
-46
lines changed

modules/client/src/main/java/org/apache/ignite/internal/client/compute/ClientCompute.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,8 @@ private static <K, T, R> CompletableFuture<SubmitResult> executeColocatedObjectK
364364
) {
365365
return executeColocatedInternal(
366366
t,
367-
(outputChannel, schema) -> ClientRecordSerializer.writeRecRaw(key, keyMapper, schema, outputChannel.out(), TuplePart.KEY),
367+
(outputChannel, schema) ->
368+
ClientRecordSerializer.writeRecRaw(key, keyMapper, schema, outputChannel.out(), TuplePart.KEY, true),
368369
ClientTupleSerializer.getPartitionAwarenessProvider(keyMapper, key),
369370
descriptor,
370371
arg

modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientKeyValueView.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -651,8 +651,8 @@ private HashMap<K, V> readGetAllResponse(ClientSchema schema, PayloadInputChanne
651651

652652
var res = new LinkedHashMap<K, V>(cnt);
653653

654-
Marshaller keyMarsh = schema.getMarshaller(keySer.mapper(), TuplePart.KEY);
655-
Marshaller valMarsh = schema.getMarshaller(valSer.mapper(), TuplePart.VAL);
654+
Marshaller keyMarsh = schema.getMarshaller(keySer.mapper(), TuplePart.KEY, false);
655+
Marshaller valMarsh = schema.getMarshaller(valSer.mapper(), TuplePart.VAL, false);
656656

657657
for (int i = 0; i < cnt; i++) {
658658
// TODO: Optimize (IGNITE-16022).

modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientRecordSerializer.java

+34-8
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,11 @@ Mapper<R> mapper() {
8585
* @param out Packer.
8686
* @param part Tuple part.
8787
* @param <R> Record type.
88+
* @param allowUnmappedFields Allow unmapped fields.
8889
*/
89-
public static <R> void writeRecRaw(@Nullable R rec, Mapper<R> mapper, ClientSchema schema, ClientMessagePacker out, TuplePart part) {
90-
writeRecRaw(rec, out, schema.getMarshaller(mapper, part), columnCount(schema, part));
90+
public static <R> void writeRecRaw(@Nullable R rec, Mapper<R> mapper, ClientSchema schema, ClientMessagePacker out, TuplePart part,
91+
boolean allowUnmappedFields) {
92+
writeRecRaw(rec, out, schema.getMarshaller(mapper, part, allowUnmappedFields), columnCount(schema, part));
9193
}
9294

9395
/**
@@ -99,7 +101,7 @@ public static <R> void writeRecRaw(@Nullable R rec, Mapper<R> mapper, ClientSche
99101
* @param columnCount Column count.
100102
* @param <R> Record type.
101103
*/
102-
static <R> void writeRecRaw(@Nullable R rec, ClientMessagePacker out, Marshaller marshaller, int columnCount) {
104+
private static <R> void writeRecRaw(@Nullable R rec, ClientMessagePacker out, Marshaller marshaller, int columnCount) {
103105
var builder = new BinaryTupleBuilder(columnCount);
104106
var noValueSet = new BitSet();
105107

@@ -109,8 +111,8 @@ static <R> void writeRecRaw(@Nullable R rec, ClientMessagePacker out, Marshaller
109111
out.packBinaryTuple(builder, noValueSet);
110112
}
111113

112-
void writeRecRaw(@Nullable R rec, ClientSchema schema, ClientMessagePacker out, TuplePart part) {
113-
writeRecRaw(rec, mapper, schema, out, part);
114+
private void writeRecRaw(@Nullable R rec, ClientSchema schema, ClientMessagePacker out, TuplePart part, boolean allowUnmappedFields) {
115+
writeRecRaw(rec, mapper, schema, out, part, allowUnmappedFields);
114116
}
115117

116118
void writeRec(
@@ -120,12 +122,24 @@ void writeRec(
120122
PayloadOutputChannel out,
121123
WriteContext ctx,
122124
TuplePart part
125+
) {
126+
writeRec(tx, rec, schema, out, ctx, part, false);
127+
}
128+
129+
void writeRec(
130+
@Nullable Transaction tx,
131+
@Nullable R rec,
132+
ClientSchema schema,
133+
PayloadOutputChannel out,
134+
WriteContext ctx,
135+
TuplePart part,
136+
boolean allowUnmappedFields
123137
) {
124138
out.out().packInt(tableId);
125139
writeTx(tx, out, ctx);
126140
out.out().packInt(schema.version());
127141

128-
writeRecRaw(rec, schema, out.out(), part);
142+
writeRecRaw(rec, schema, out.out(), part, allowUnmappedFields);
129143
}
130144

131145
void writeRecs(
@@ -141,7 +155,7 @@ void writeRecs(
141155
writeTx(tx, out, ctx);
142156
out.out().packInt(schema.version());
143157

144-
Marshaller marshaller = schema.getMarshaller(mapper, part);
158+
Marshaller marshaller = schema.getMarshaller(mapper, part, false);
145159
int columnCount = columnCount(schema, part);
146160

147161
writeRecRaw(rec, out.out(), marshaller, columnCount);
@@ -155,13 +169,25 @@ void writeRecs(
155169
PayloadOutputChannel out,
156170
WriteContext ctx,
157171
TuplePart part
172+
) {
173+
writeRecs(tx, recs, schema, out, ctx, part, false);
174+
}
175+
176+
void writeRecs(
177+
@Nullable Transaction tx,
178+
Collection<R> recs,
179+
ClientSchema schema,
180+
PayloadOutputChannel out,
181+
WriteContext ctx,
182+
TuplePart part,
183+
boolean allowUnmappedFields
158184
) {
159185
out.out().packInt(tableId);
160186
writeTx(tx, out, ctx);
161187
out.out().packInt(schema.version());
162188
out.out().packInt(recs.size());
163189

164-
Marshaller marshaller = schema.getMarshaller(mapper, part);
190+
Marshaller marshaller = schema.getMarshaller(mapper, part, allowUnmappedFields);
165191
int columnCount = columnCount(schema, part);
166192

167193
for (R rec : recs) {

modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientRecordView.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public CompletableFuture<R> getAsync(@Nullable Transaction tx, R keyRec) {
8383

8484
return tbl.doSchemaOutInOpAsync(
8585
ClientOp.TUPLE_GET,
86-
(s, w, n) -> ser.writeRec(tx, keyRec, s, w, n, TuplePart.KEY),
86+
(s, w, n) -> ser.writeRec(tx, keyRec, s, w, n, TuplePart.KEY, true),
8787
(s, r) -> ser.readValRec(keyRec, s, r.in()),
8888
null,
8989
ClientTupleSerializer.getPartitionAwarenessProvider(ser.mapper(), keyRec),
@@ -105,7 +105,7 @@ public CompletableFuture<List<R>> getAllAsync(@Nullable Transaction tx, Collecti
105105

106106
return tbl.doSchemaOutInOpAsync(
107107
ClientOp.TUPLE_GET_ALL,
108-
(s, w, n) -> ser.writeRecs(tx, keyRecs, s, w, n, TuplePart.KEY),
108+
(s, w, n) -> ser.writeRecs(tx, keyRecs, s, w, n, TuplePart.KEY, true),
109109
(s, r) -> ser.readRecs(s, r.in(), true, TuplePart.KEY_AND_VAL),
110110
Collections.emptyList(),
111111
ClientTupleSerializer.getPartitionAwarenessProvider(ser.mapper(), keyRecs),
@@ -126,7 +126,7 @@ public CompletableFuture<Boolean> containsAsync(@Nullable Transaction tx, R key)
126126

127127
return tbl.doSchemaOutOpAsync(
128128
ClientOp.TUPLE_CONTAINS_KEY,
129-
(s, w, n) -> ser.writeRec(tx, key, s, w, n, TuplePart.KEY),
129+
(s, w, n) -> ser.writeRec(tx, key, s, w, n, TuplePart.KEY, true),
130130
r -> r.in().unpackBoolean(),
131131
ClientTupleSerializer.getPartitionAwarenessProvider(ser.mapper(), key),
132132
tx);
@@ -149,7 +149,7 @@ public CompletableFuture<Boolean> containsAllAsync(@Nullable Transaction tx, Col
149149

150150
return tbl.doSchemaOutOpAsync(
151151
ClientOp.TUPLE_CONTAINS_ALL_KEYS,
152-
(s, w, n) -> ser.writeRecs(tx, keys, s, w, n, TuplePart.KEY),
152+
(s, w, n) -> ser.writeRecs(tx, keys, s, w, n, TuplePart.KEY, true),
153153
r -> r.in().unpackBoolean(),
154154
ClientTupleSerializer.getPartitionAwarenessProvider(ser.mapper(), keys),
155155
tx);
@@ -332,7 +332,7 @@ public CompletableFuture<Boolean> deleteAsync(@Nullable Transaction tx, R keyRec
332332

333333
return tbl.doSchemaOutOpAsync(
334334
ClientOp.TUPLE_DELETE,
335-
(s, w, n) -> ser.writeRec(tx, keyRec, s, w, n, TuplePart.KEY),
335+
(s, w, n) -> ser.writeRec(tx, keyRec, s, w, n, TuplePart.KEY, true),
336336
r -> r.in().unpackBoolean(),
337337
ClientTupleSerializer.getPartitionAwarenessProvider(ser.mapper(), keyRec),
338338
tx);
@@ -370,7 +370,7 @@ public CompletableFuture<R> getAndDeleteAsync(@Nullable Transaction tx, R keyRec
370370

371371
return tbl.doSchemaOutInOpAsync(
372372
ClientOp.TUPLE_GET_AND_DELETE,
373-
(s, w, n) -> ser.writeRec(tx, keyRec, s, w, n, TuplePart.KEY),
373+
(s, w, n) -> ser.writeRec(tx, keyRec, s, w, n, TuplePart.KEY, true),
374374
(s, r) -> ser.readValRec(keyRec, s, r.in()),
375375
null,
376376
ClientTupleSerializer.getPartitionAwarenessProvider(ser.mapper(), keyRec),
@@ -394,7 +394,7 @@ public CompletableFuture<List<R>> deleteAllAsync(@Nullable Transaction tx, Colle
394394

395395
return tbl.doSchemaOutInOpAsync(
396396
ClientOp.TUPLE_DELETE_ALL,
397-
(s, w, n) -> ser.writeRecs(tx, keyRecs, s, w, n, TuplePart.KEY),
397+
(s, w, n) -> ser.writeRecs(tx, keyRecs, s, w, n, TuplePart.KEY, true),
398398
(s, r) -> ser.readRecs(s, r.in(), false, TuplePart.KEY),
399399
Collections.emptyList(),
400400
ClientTupleSerializer.getPartitionAwarenessProvider(ser.mapper(), keyRecs),

modules/client/src/main/java/org/apache/ignite/internal/client/table/ClientSchema.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,9 @@ <T> Marshaller getMarshaller(Mapper mapper, TuplePart part, boolean allowUnmappe
235235
case KEY:
236236
return marshallers.getKeysMarshaller(marshallerSchema(), mapper, true, allowUnmappedFields);
237237
case VAL:
238-
return marshallers.getValuesMarshaller(marshallerSchema(), mapper, true, allowUnmappedFields);
238+
return marshallers.getValuesMarshaller(marshallerSchema(), mapper, false, allowUnmappedFields);
239239
case KEY_AND_VAL:
240-
return marshallers.getRowMarshaller(marshallerSchema(), mapper, true, allowUnmappedFields);
240+
return marshallers.getRowMarshaller(marshallerSchema(), mapper, false, allowUnmappedFields);
241241
default:
242242
throw new AssertionError("Unexpected tuple part: " + part);
243243
}

modules/client/src/test/java/org/apache/ignite/client/AbstractClientTableTest.java

+7
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,13 @@ protected static class CompositeKeyPojo {
219219
public long gid;
220220
}
221221

222+
/** Partial column set. */
223+
protected static class IncompleteValPojo {
224+
public byte zbyte;
225+
public String zstring;
226+
public byte[] zbytes;
227+
}
228+
222229
/** Columns of all types. */
223230
protected static class AllColumnsPojo {
224231
public long gid;

modules/client/src/test/java/org/apache/ignite/client/ClientKeyValueViewTest.java

+51-12
Original file line numberDiff line numberDiff line change
@@ -93,35 +93,52 @@ public void testPrimitivePutBinaryGet() {
9393
}
9494

9595
@Test
96-
public void testMissingValueColumnsThrowException() {
96+
public void testMissingValueColumns() {
9797
Table table = fullTable();
9898
KeyValueView<Tuple, Tuple> kvView = table.keyValueView();
99-
KeyValueView<IncompletePojo, IncompletePojo> pojoView = table.keyValueView(IncompletePojo.class, IncompletePojo.class);
99+
KeyValueView<CompositeKeyPojo, IncompleteValPojo> pojoView = table.keyValueView(CompositeKeyPojo.class, IncompleteValPojo.class);
100100

101101
kvView.put(null, allColumnsTableKey(1), allColumnsTableVal("x", true));
102102

103-
var key = new IncompletePojo();
103+
var key = new CompositeKeyPojo();
104104
key.id = "1";
105105
key.gid = 1;
106106

107-
Throwable e = assertThrowsWithCause(
108-
() -> pojoView.get(null, key),
109-
IgniteException.class,
110-
"Failed to deserialize server response: No mapped object field found for column 'ZBOOLEAN'"
111-
);
112-
assertThat(Arrays.asList(e.getStackTrace()), anyOf(hasToString(containsString("ClientKeyValueView"))));
107+
IncompleteValPojo res = pojoView.get(null, key);
108+
109+
assertEquals(11, res.zbyte);
110+
assertEquals("x", res.zstring);
111+
assertArrayEquals(new byte[]{1, 2}, res.zbytes);
112+
113+
pojoView.put(null, key, res);
114+
Tuple val = kvView.get(null, allColumnsTableKey(1));
115+
116+
assertEquals(
117+
Tuple.create().set("zbyte", (byte) 11).set("zstring", "x").set("zbytes", new byte[]{1, 2})
118+
.set("zboolean", null)
119+
.set("zshort", null)
120+
.set("zint", null)
121+
.set("zlong", null)
122+
.set("zfloat", null)
123+
.set("zdouble", null)
124+
.set("zdate", null)
125+
.set("ztime", null)
126+
.set("ztimestamp", null)
127+
.set("zdecimal", null)
128+
.set("zuuid", null),
129+
val);
113130
}
114131

115132
@Test
116133
public void testAllColumnsBinaryPutPojoGet() {
117134
Table table = fullTable();
118-
KeyValueView<IncompletePojo, AllColumnsValPojo> pojoView = table.keyValueView(
119-
Mapper.of(IncompletePojo.class),
135+
KeyValueView<CompositeKeyPojo, AllColumnsValPojo> pojoView = table.keyValueView(
136+
Mapper.of(CompositeKeyPojo.class),
120137
Mapper.of(AllColumnsValPojo.class));
121138

122139
table.recordView().upsert(null, allColumnsTableVal("foo", false));
123140

124-
var key = new IncompletePojo();
141+
var key = new CompositeKeyPojo();
125142
key.gid = (int) (long) DEFAULT_ID;
126143
key.id = String.valueOf(DEFAULT_ID);
127144

@@ -209,6 +226,28 @@ public void testMissingKeyColumnThrowsException() {
209226
assertThat(Arrays.asList(e.getStackTrace()), anyOf(hasToString(containsString("ClientKeyValueView"))));
210227
}
211228

229+
@Test
230+
public void testExtraKeyColumnThrowsException() {
231+
KeyValueView<IncompletePojo, AllColumnsValPojoNullable> pojoView = fullTable()
232+
.keyValueView(IncompletePojo.class, AllColumnsValPojoNullable.class);
233+
234+
Throwable e = assertThrowsWithCause(
235+
() -> pojoView.get(null, new IncompletePojo()),
236+
IgniteException.class,
237+
"Fields [zbyte, zbytes, zstring] of type org.apache.ignite.client.AbstractClientTableTest$IncompletePojo are not mapped"
238+
+ " to columns"
239+
);
240+
assertThat(Arrays.asList(e.getStackTrace()), anyOf(hasToString(containsString("ClientKeyValueView"))));
241+
242+
e = assertThrowsWithCause(
243+
() -> pojoView.put(null, new IncompletePojo(), new AllColumnsValPojoNullable()),
244+
IgniteException.class,
245+
"Fields [zbyte, zbytes, zstring] of type org.apache.ignite.client.AbstractClientTableTest$IncompletePojo are not mapped"
246+
+ " to columns"
247+
);
248+
assertThat(Arrays.asList(e.getStackTrace()), anyOf(hasToString(containsString("ClientKeyValueView"))));
249+
}
250+
212251
@Test
213252
public void testNullablePrimitiveFields() {
214253
KeyValueView<CompositeKeyPojo, AllColumnsValPojoNullable> pojoView = fullTable().keyValueView(

modules/client/src/test/java/org/apache/ignite/client/ClientRecordViewTest.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public void testPrimitivePutBinaryGet() {
9191
}
9292

9393
@Test
94-
public void testMissingValueColumnsThrowException() {
94+
public void testMissingValueColumns() {
9595
Table table = fullTable();
9696
KeyValueView<Tuple, Tuple> kvView = table.keyValueView();
9797
RecordView<IncompletePojo> pojoView = table.recordView(IncompletePojo.class);
@@ -102,10 +102,11 @@ public void testMissingValueColumnsThrowException() {
102102
key.id = "1";
103103
key.gid = 1;
104104

105-
// This POJO does not have fields for all table columns, which is not allowed (to avoid unexpected data loss).
106-
IgniteException ex = assertThrows(IgniteException.class, () -> pojoView.get(null, key));
107-
assertEquals("Failed to deserialize server response: No mapped object field found for column 'ZBOOLEAN'", ex.getMessage());
108-
assertThat(Arrays.asList(ex.getStackTrace()), anyOf(hasToString(containsString("ClientRecordView"))));
105+
IncompletePojo res = pojoView.get(null, key);
106+
107+
assertEquals(11, res.zbyte);
108+
assertEquals("x", res.zstring);
109+
assertArrayEquals(new byte[]{1, 2}, res.zbytes);
109110
}
110111

111112
@Test

modules/marshaller-common/src/main/java/org/apache/ignite/internal/marshaller/Marshaller.java

+4
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ private static PojoMarshaller pojoMarshaller(
151151
Set<String> fieldSet = new TreeSet<>(fields);
152152
for (MarshallerColumn col : cols) {
153153
String fieldName = mapper.fieldForColumn(col.name());
154+
if (fieldName == null) {
155+
assert !requireAllFields;
156+
continue;
157+
}
154158
fieldSet.remove(fieldName);
155159
}
156160

modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientMarshallingTest.java

+21-4
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,16 @@ public void testKvMissingKeyPojoFields() {
144144

145145
@Test
146146
public void testKvMissingValPojoFields() {
147-
Table table = ignite().tables().table(TABLE_NAME);
148-
var kvPojoView = table.keyValueView(Integer.class, MissingFieldPojo.class);
147+
String tableName = "tableWithExtraField";
148+
ignite().sql().execute(null, "CREATE TABLE " + tableName + " (KEY INT PRIMARY KEY, VAL VARCHAR, EXTRA VARCHAR)");
149+
Table table = ignite().tables().table(tableName);
150+
151+
var kvPojoView = table.keyValueView(Integer.class, MissingFieldPojo2.class);
149152

150-
Throwable ex = assertThrowsWithCause(() -> kvPojoView.put(null, 1, new MissingFieldPojo()), IllegalArgumentException.class);
151-
assertEquals("No mapped object field found for column 'VAL'", ex.getMessage());
153+
kvPojoView.put(null, 1, new MissingFieldPojo2("x"));
154+
MissingFieldPojo2 val = kvPojoView.get(null, 1);
155+
156+
assertEquals("x", val.val);
152157
}
153158

154159
@Test
@@ -392,6 +397,18 @@ private static class MissingFieldPojo {
392397
public int unknown;
393398
}
394399

400+
private static class MissingFieldPojo2 {
401+
public String val;
402+
403+
MissingFieldPojo2() {
404+
// No-op.
405+
}
406+
407+
public MissingFieldPojo2(String val) {
408+
this.val = val;
409+
}
410+
}
411+
395412
private static class IncompatibleFieldPojo {
396413
public String key; // Must be int.
397414
public BigDecimal val;

modules/schema/src/main/java/org/apache/ignite/internal/schema/marshaller/reflection/KvMarshallerImpl.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public KvMarshallerImpl(SchemaDescriptor schema, MarshallersProvider marshallers
7474

7575
MarshallerSchema marshallerSchema = schema.marshallerSchema();
7676
keyMarsh = marshallers.getKeysMarshaller(marshallerSchema, keyMapper, true, false);
77-
valMarsh = marshallers.getValuesMarshaller(marshallerSchema, valueMapper, true, false);
77+
valMarsh = marshallers.getValuesMarshaller(marshallerSchema, valueMapper, false, false);
7878
keyPositions = schema.keyColumns().stream().mapToInt(Column::positionInRow).toArray();
7979
valPositions = schema.valueColumns().stream().mapToInt(Column::positionInRow).toArray();
8080
}

0 commit comments

Comments
 (0)