blob: 6344805a76f5c3c4c21b431e27ef671fe647d5d3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.internal.table;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
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 static org.mockito.Answers.RETURNS_DEEP_STUBS;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.ignite.internal.schema.Column;
import org.apache.ignite.internal.schema.NativeType;
import org.apache.ignite.internal.schema.NativeTypeSpec;
import org.apache.ignite.internal.schema.NativeTypes;
import org.apache.ignite.internal.schema.SchemaDescriptor;
import org.apache.ignite.internal.schema.SchemaTestUtils;
import org.apache.ignite.internal.storage.basic.ConcurrentHashMapPartitionStorage;
import org.apache.ignite.internal.table.distributed.storage.VersionedRowStore;
import org.apache.ignite.internal.table.impl.DummyInternalTableImpl;
import org.apache.ignite.internal.table.impl.DummySchemaManagerImpl;
import org.apache.ignite.internal.tx.TxManager;
import org.apache.ignite.internal.tx.impl.HeapLockManager;
import org.apache.ignite.internal.tx.impl.TxManagerImpl;
import org.apache.ignite.network.ClusterService;
import org.apache.ignite.table.KeyValueView;
import org.apache.ignite.table.mapper.Mapper;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
/**
* Basic table operations test.
*
* <p>TODO: IGNITE-14487 Add bulk operations tests.
* TODO: IGNITE-14487 Add async operations tests.
*/
public class KeyValueViewOperationsSimpleSchemaTest {
/**
* Creates table view.
*
* @return Table KV-view.
*/
private KeyValueView<Long, Long> kvView() {
return kvViewForValueType(NativeTypes.INT64, Long.class);
}
@Test
public void put() {
KeyValueView<Long, Long> tbl = kvView();
assertNull(tbl.get(1L));
// Put KV pair.
tbl.put(1L, 11L);
assertEquals(11L, tbl.get(1L));
assertEquals(11L, tbl.get(1L));
// Update KV pair.
tbl.put(1L, 22L);
assertEquals(22L, tbl.get(1L));
assertEquals(22L, tbl.get(1L));
// Remove KV pair.
tbl.put(1L, null);
assertNull(tbl.get(1L));
// Put KV pair.
tbl.put(1L, 33L);
assertEquals(33L, tbl.get(1L));
}
@Test
public void putIfAbsent() {
KeyValueView<Long, Long> tbl = kvView();
assertNull(tbl.get(1L));
// Insert new KV pair.
assertTrue(tbl.putIfAbsent(1L, 11L));
assertEquals(11L, tbl.get(1L));
// Update KV pair.
assertFalse(tbl.putIfAbsent(1L, 22L));
assertEquals(11L, tbl.get(1L));
}
@Test
public void getAndPut() {
KeyValueView<Long, Long> tbl = kvView();
assertNull(tbl.get(1L));
// Insert new tuple.
assertNull(tbl.getAndPut(1L, 11L));
assertEquals(11L, tbl.get(1L));
assertEquals(11L, tbl.getAndPut(1L, 22L));
assertEquals(22L, tbl.getAndPut(1L, 33L));
assertEquals(33L, tbl.get(1L));
}
@Test
public void contains() {
KeyValueView<Long, Long> tbl = kvView();
// Not-existed value.
assertFalse(tbl.contains(1L));
// Put KV pair.
tbl.put(1L, 11L);
assertTrue(tbl.contains(1L));
// Delete key.
assertTrue(tbl.remove(1L));
assertFalse(tbl.contains(1L));
// Put KV pair.
tbl.put(1L, 22L);
assertTrue(tbl.contains(1L));
// Delete key.
tbl.remove(2L);
assertFalse(tbl.contains(2L));
// Replace.
assertTrue(tbl.replace(1L, null));
assertTrue(tbl.contains(1L));
}
@Test
public void remove() {
KeyValueView<Long, Long> tbl = kvView();
// Put KV pair.
tbl.put(1L, 11L);
// Delete existed key.
assertEquals(11L, tbl.get(1L));
assertTrue(tbl.remove(1L));
assertNull(tbl.get(1L));
// Delete already deleted key.
assertFalse(tbl.remove(1L));
// Put KV pair.
tbl.put(1L, 22L);
assertEquals(22L, tbl.get(1L));
// Delete existed key.
assertTrue(tbl.remove(1L));
assertNull(tbl.get(1L));
// Delete not existed key.
assertNull(tbl.get(2L));
assertFalse(tbl.remove(2L));
}
@Test
public void removeExact() {
KeyValueView<Long, Long> tbl = kvView();
// Put KV pair.
tbl.put(1L, 11L);
assertEquals(11L, tbl.get(1L));
// Fails to delete KV pair with unexpected value.
assertFalse(tbl.remove(1L, 22L));
assertEquals(11L, tbl.get(1L));
// Delete KV pair with expected value.
assertTrue(tbl.remove(1L, 11L));
assertNull(tbl.get(1L));
// Once again.
assertFalse(tbl.remove(1L, 11L));
assertNull(tbl.get(1L));
// Try to remove non-existed key.
assertFalse(tbl.remove(1L, 11L));
assertNull(tbl.get(1L));
// Put KV pair.
tbl.put(1L, 22L);
assertEquals(22L, tbl.get(1L));
// Check value ignored.
tbl.remove(1L, null);
assertEquals(22L, tbl.get(1L));
// Delete KV pair with expected value.
assertTrue(tbl.remove(1L, 22L));
assertNull(tbl.get(1L));
assertFalse(tbl.remove(2L, 22L));
assertNull(tbl.get(2L));
}
@Test
public void replace() {
KeyValueView<Long, Long> tbl = kvView();
// Ignore replace operation for non-existed KV pair.
assertFalse(tbl.replace(1L, 11L));
assertNull(tbl.get(1L));
tbl.put(1L, 11L);
// Replace existed KV pair.
assertTrue(tbl.replace(1L, 22L));
assertEquals(22L, tbl.get(1L));
// Remove existed KV pair.
assertTrue(tbl.replace(1L, null));
assertNull(tbl.get(1L));
// Ignore replace operation for non-existed KV pair.
assertFalse(tbl.replace(3L, 33L));
assertNull(tbl.get(3L));
tbl.put(3L, 33L);
assertEquals(33L, tbl.get(3L));
// Remove non-existed KV pair.
assertFalse(tbl.replace(2L, null));
assertNull(tbl.get(2L));
assertThrows(Throwable.class, () -> tbl.replace(null, 33L));
}
@Test
public void replaceExact() {
KeyValueView<Long, Long> tbl = kvView();
// Upsert non-existed KV pair.
assertFalse(tbl.replace(1L, null, 11L));
assertNull(tbl.get(1L));
tbl.put(1L, 11L);
// Ignore replace operation for non-existed KV pair.
assertFalse(tbl.replace(2L, 11L, 22L));
assertNull(tbl.get(2L));
// Replace existed KV pair.
assertTrue(tbl.replace(1L, 11L, 22L));
assertEquals(22L, tbl.get(1L));
// Replace with null value.
assertTrue(tbl.replace(1L, 22L, null));
assertNull(tbl.get(1L));
// Replace null value.
assertTrue(tbl.replace(1L, null, 33L));
assertEquals(33L, tbl.get(1L));
// Remove non-existed KV pair.
assertFalse(tbl.replace(2L, null, null));
}
@Test
public void putGetAllTypes() {
Random rnd = new Random();
Long key = 42L;
List<NativeType> allTypes = List.of(
NativeTypes.INT8,
NativeTypes.INT16,
NativeTypes.INT32,
NativeTypes.INT64,
NativeTypes.FLOAT,
NativeTypes.DOUBLE,
NativeTypes.DATE,
NativeTypes.UUID,
NativeTypes.numberOf(20),
NativeTypes.decimalOf(25, 5),
NativeTypes.bitmaskOf(22),
NativeTypes.time(),
NativeTypes.datetime(),
NativeTypes.timestamp(),
NativeTypes.BYTES,
NativeTypes.STRING);
// Validate all types are tested.
assertEquals(Set.of(NativeTypeSpec.values()),
allTypes.stream().map(NativeType::spec).collect(Collectors.toSet()));
for (NativeType type : allTypes) {
final Object val = SchemaTestUtils.generateRandomValue(rnd, type);
assertFalse(type.mismatch(NativeTypes.fromObject(val)));
KeyValueViewImpl<Long, Object> kvView = kvViewForValueType(NativeTypes.fromObject(val),
(Class<Object>) val.getClass());
kvView.put(key, val);
if (val instanceof byte[]) {
assertArrayEquals((byte[]) val, (byte[]) kvView.get(key));
} else {
assertEquals(val, kvView.get(key));
}
}
}
/**
* Creates key-value view.
*
* @param type Value column native type.
* @param valueClass Value class.
*/
private <T> KeyValueViewImpl<Long, T> kvViewForValueType(NativeType type, Class<T> valueClass) {
ClusterService clusterService = Mockito.mock(ClusterService.class, RETURNS_DEEP_STUBS);
Mockito.when(clusterService.topologyService().localMember().address())
.thenReturn(DummyInternalTableImpl.ADDR);
TxManager txManager = new TxManagerImpl(clusterService, new HeapLockManager());
DummyInternalTableImpl table = new DummyInternalTableImpl(
new VersionedRowStore(new ConcurrentHashMapPartitionStorage(), txManager), txManager);
Mapper<Long> keyMapper = Mapper.of("id", Long.class);
Mapper<T> valMapper = Mapper.of("val", valueClass);
SchemaDescriptor schema = new SchemaDescriptor(
1,
new Column[]{new Column("id", NativeTypes.INT64, false)},
new Column[]{new Column("val", type, true)}
);
return new KeyValueViewImpl<>(
table,
new DummySchemaManagerImpl(schema),
keyMapper,
valMapper,
null
);
}
}