blob: a23653e336401dee39cda25bee6f9596ad8baa04 [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.client;
import static org.apache.ignite.internal.testframework.IgniteTestUtils.waitForCondition;
import static org.apache.ignite.lang.ErrorGroups.Client.TABLE_ID_NOT_FOUND_ERR;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.assertEquals;
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.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import org.apache.ignite.client.fakes.FakeIgniteTables;
import org.apache.ignite.client.fakes.FakeSchemaRegistry;
import org.apache.ignite.internal.testframework.IgniteTestUtils;
import org.apache.ignite.lang.IgniteException;
import org.apache.ignite.table.RecordView;
import org.apache.ignite.table.Table;
import org.apache.ignite.table.Tuple;
import org.junit.jupiter.api.Test;
/**
* Table tests.
*/
@SuppressWarnings("ZeroLengthArrayAllocation")
public class ClientTableTest extends AbstractClientTableTest {
@Test
public void testGetWithMissedKeyColumnThrowsException() {
var table = defaultTable().recordView();
var key = Tuple.create().set("name", "123");
var ex = assertThrows(IgniteException.class, () -> table.get(null, key));
assertTrue(ex.getMessage().contains("Missed key column: ID"),
ex.getMessage());
}
@Test
public void testUpsertGet() {
var table = defaultTable().recordView();
var tuple = tuple();
table.upsert(null, tuple);
Tuple key = tuple(DEFAULT_ID);
var resTuple = table.get(null, key);
assertEquals(DEFAULT_NAME, resTuple.stringValue("name"));
assertEquals(DEFAULT_ID, resTuple.longValue("id"));
assertEquals("foo", resTuple.valueOrDefault("bar", "foo"));
assertEquals(DEFAULT_NAME, resTuple.value(1));
assertEquals(DEFAULT_ID, resTuple.value(0));
assertEquals(2, resTuple.columnCount());
assertEquals("ID", resTuple.columnName(0));
assertEquals("NAME", resTuple.columnName(1));
var iter = tuple.iterator();
assertTrue(iter.hasNext());
assertEquals(DEFAULT_ID, iter.next());
assertTrue(iter.hasNext());
assertEquals(DEFAULT_NAME, iter.next());
assertFalse(iter.hasNext());
assertThrows(NoSuchElementException.class, iter::next);
assertTupleEquals(tuple, resTuple);
}
@Test
public void testUpsertGetAsync() {
var table = defaultTable().recordView();
var tuple = tuple(42L, "Jack");
var key = Tuple.create().set("id", 42L);
var resTuple = table.upsertAsync(null, tuple).thenCompose(t -> table.getAsync(null, key)).join();
assertEquals("Jack", resTuple.stringValue("name"));
assertEquals(42L, resTuple.longValue("id"));
assertTupleEquals(tuple, resTuple);
}
@Test
public void testGetReturningTupleWithUnknownSchemaRequestsNewSchema() throws Exception {
var embeddedView = defaultTable().recordView();
try (var client2 = startClient()) {
// Upsert from client, which will cache schema version 1.
FakeSchemaRegistry.setLastVer(1);
RecordView<Tuple> clientView = client2.tables().table(DEFAULT_TABLE).recordView();
clientView.upsert(null, tuple(-1L));
// Upsert from server with schema version 2.
FakeSchemaRegistry.setLastVer(2);
embeddedView.upsert(null, tuple());
// Get from client, which should force schema update and retry.
var resTuple = clientView.get(null, defaultTupleKey());
assertEquals(3, resTuple.columnCount());
assertEquals(2, resTuple.columnIndex("XYZ"));
assertEquals(DEFAULT_NAME, resTuple.stringValue("name"));
assertEquals(DEFAULT_ID, resTuple.longValue("id"));
}
}
@Test
public void testOperationWithoutTupleResultRequestsNewSchema() throws Exception {
AtomicLong idGen = new AtomicLong(1000L);
checkSchemaUpdate(recordView -> recordView.get(null, tuple(idGen.incrementAndGet())));
checkSchemaUpdate(recordView -> recordView.getAll(null, List.of(tuple(idGen.incrementAndGet()))));
checkSchemaUpdate(recordView -> recordView.upsert(null, tuple(idGen.incrementAndGet())));
checkSchemaUpdate(recordView -> recordView.upsertAll(null, List.of(tuple(idGen.incrementAndGet()))));
checkSchemaUpdate(recordView -> recordView.getAndUpsert(null, tuple(idGen.incrementAndGet())));
checkSchemaUpdate(recordView -> recordView.insert(null, tuple(idGen.incrementAndGet())));
checkSchemaUpdate(recordView -> recordView.insertAll(null, List.of(tuple(idGen.incrementAndGet()))));
checkSchemaUpdate(recordView -> recordView.replace(null, tuple(idGen.incrementAndGet())));
checkSchemaUpdate(recordView -> recordView.replace(null, tuple(idGen.incrementAndGet()), tuple(idGen.incrementAndGet())));
checkSchemaUpdate(recordView -> recordView.getAndReplace(null, tuple(idGen.incrementAndGet())));
checkSchemaUpdate(recordView -> recordView.delete(null, tuple(idGen.incrementAndGet())));
checkSchemaUpdate(recordView -> recordView.deleteExact(null, tuple(idGen.incrementAndGet())));
checkSchemaUpdate(recordView -> recordView.getAndDelete(null, tuple(idGen.incrementAndGet())));
checkSchemaUpdate(recordView -> recordView.deleteAll(null, List.of(tuple(idGen.incrementAndGet()))));
checkSchemaUpdate(recordView -> recordView.deleteAllExact(null, List.of(tuple(idGen.incrementAndGet()))));
}
@Test
public void testInsert() {
var table = defaultTable().recordView();
var tuple = tuple();
var tuple2 = tuple(DEFAULT_ID, "abc");
assertTrue(table.insert(null, tuple));
assertFalse(table.insert(null, tuple));
assertFalse(table.insert(null, tuple2));
var resTuple = table.get(null, defaultTupleKey());
assertTupleEquals(tuple, resTuple);
}
@Test
public void testInsertCustomTuple() {
var table = defaultTable().recordView();
var tuple = new CustomTuple(25L, "Foo");
assertTrue(table.insert(null, tuple));
assertFalse(table.insert(null, tuple));
var resTuple = table.get(null, new CustomTuple(25L));
assertTupleEquals(tuple, resTuple);
}
@Test
public void testGetAll() {
var table = defaultTable().recordView();
table.insert(null, tuple(1L, "1"));
table.insert(null, tuple(2L, "2"));
table.insert(null, tuple(3L, "3"));
List<Tuple> keys = Arrays.asList(tuple(1L), tuple(3L));
Tuple[] res = sortedTuples(table.getAll(null, keys));
assertEquals(2, res.length);
assertEquals(1L, res[0].longValue("id"));
assertEquals("1", res[0].stringValue("name"));
assertEquals(3L, res[1].longValue("id"));
assertEquals("3", res[1].stringValue("name"));
}
@Test
public void testUpsertAll() {
var table = defaultTable().recordView();
List<Tuple> data = Arrays.asList(tuple(1L, "1"), tuple(2L, "2"));
table.upsertAll(null, data);
assertEquals("1", table.get(null, tuple(1L)).stringValue("name"));
assertEquals("2", table.get(null, tuple(2L)).stringValue("name"));
List<Tuple> data2 = Arrays.asList(tuple(1L, "10"), tuple(3L, "30"));
table.upsertAll(null, data2);
assertEquals("10", table.get(null, tuple(1L)).stringValue("name"));
assertEquals("2", table.get(null, tuple(2L)).stringValue("name"));
assertEquals("30", table.get(null, tuple(3L)).stringValue("name"));
}
@Test
public void testInsertAll() {
var table = defaultTable().recordView();
List<Tuple> data = Arrays.asList(tuple(1L, "1"), tuple(2L, "2"));
var skippedTuples = table.insertAll(null, data);
assertEquals(0, skippedTuples.size());
assertEquals("1", table.get(null, tuple(1L)).stringValue("name"));
assertEquals("2", table.get(null, tuple(2L)).stringValue("name"));
List<Tuple> data2 = Arrays.asList(tuple(1L, "10"), tuple(3L, "30"));
var skippedTuples2 = table.insertAll(null, data2).toArray(new Tuple[0]);
assertEquals(1, skippedTuples2.length);
assertEquals(1L, skippedTuples2[0].longValue("id"));
assertEquals("1", table.get(null, tuple(1L)).stringValue("name"));
assertEquals("2", table.get(null, tuple(2L)).stringValue("name"));
assertEquals("30", table.get(null, tuple(3L)).stringValue("name"));
}
@Test
public void testReplace() {
var table = defaultTable().recordView();
table.insert(null, tuple(1L, "1"));
assertFalse(table.replace(null, tuple(3L, "3")));
assertNull(table.get(null, tuple(3L)));
assertTrue(table.replace(null, tuple(1L, "2")));
assertEquals("2", table.get(null, tuple(1L)).value("name"));
}
@Test
public void testReplaceExact() {
var table = defaultTable().recordView();
table.insert(null, tuple(1L, "1"));
assertFalse(table.replace(null, tuple(3L, "3"), tuple(3L, "4")));
assertNull(table.get(null, tuple(3L)));
assertFalse(table.replace(null, tuple(1L, "2"), tuple(1L, "3")));
assertTrue(table.replace(null, tuple(1L, "1"), tuple(1L, "3")));
assertEquals("3", table.get(null, tuple(1L)).value("name"));
}
@Test
public void testGetAndReplace() {
var table = defaultTable().recordView();
var tuple = tuple(1L, "1");
table.insert(null, tuple);
assertNull(table.getAndReplace(null, tuple(3L, "3")));
assertNull(table.get(null, tuple(3L)));
var replaceRes = table.getAndReplace(null, tuple(1L, "2"));
assertTupleEquals(tuple, replaceRes);
assertEquals("2", table.get(null, tuple(1L)).value("name"));
}
@Test
public void testDelete() {
var table = defaultTable().recordView();
table.insert(null, tuple(1L, "1"));
assertFalse(table.delete(null, tuple(2L)));
assertTrue(table.delete(null, tuple(1L)));
assertNull(table.get(null, tuple(1L)));
}
@Test
public void testDeleteExact() {
var table = defaultTable().recordView();
table.insert(null, tuple(1L, "1"));
table.insert(null, tuple(2L, "2"));
assertFalse(table.deleteExact(null, tuple(1L)));
assertFalse(table.deleteExact(null, tuple(1L, "x")));
assertTrue(table.deleteExact(null, tuple(1L, "1")));
assertFalse(table.deleteExact(null, tuple(2L)));
assertFalse(table.deleteExact(null, tuple(3L)));
assertNull(table.get(null, tuple(1L)));
assertNotNull(table.get(null, tuple(2L)));
}
@Test
public void testGetAndDelete() {
var table = defaultTable().recordView();
var tuple = tuple(1L, "1");
table.insert(null, tuple);
var deleted = table.getAndDelete(null, tuple(1L));
assertNull(table.getAndDelete(null, tuple(1L)));
assertNull(table.getAndDelete(null, tuple(2L)));
assertTupleEquals(tuple, deleted);
}
@Test
public void testDeleteAll() {
var table = defaultTable().recordView();
List<Tuple> data = Arrays.asList(tuple(1L, "1"), tuple(2L, "2"));
table.insertAll(null, data);
List<Tuple> toDelete = Arrays.asList(tuple(1L, "x"), tuple(3L, "y"), tuple(4L, "z"));
var skippedTuples = sortedTuples(table.deleteAll(null, toDelete));
assertEquals(2, skippedTuples.length);
assertNull(table.get(null, tuple(1L)));
assertNotNull(table.get(null, tuple(2L)));
assertEquals(3L, skippedTuples[0].longValue("id"));
assertEquals(-1, skippedTuples[0].columnIndex("name"));
assertEquals(4L, skippedTuples[1].longValue("id"));
assertEquals(-1, skippedTuples[1].columnIndex("name"));
}
@Test
public void testDeleteAllExact() {
var table = defaultTable().recordView();
List<Tuple> data = Arrays.asList(tuple(1L, "1"), tuple(2L, "2"));
table.insertAll(null, data);
List<Tuple> toDelete = Arrays.asList(tuple(1L, "1"), tuple(2L, "y"), tuple(3L, "z"));
var skippedTuples = sortedTuples(table.deleteAllExact(null, toDelete));
assertEquals(2, skippedTuples.length);
assertNull(table.get(null, tuple(1L)));
assertNotNull(table.get(null, tuple(2L)));
assertEquals(2L, skippedTuples[0].longValue("id"));
assertEquals("y", skippedTuples[0].stringValue("name"));
assertEquals(3L, skippedTuples[1].longValue("id"));
assertEquals("z", skippedTuples[1].stringValue("name"));
}
@Test
public void testColumnWithDefaultValueNotSetReturnsDefault() {
RecordView<Tuple> table = tableWithDefaultValues().recordView();
var tuple = Tuple.create()
.set("id", 1);
table.upsert(null, tuple);
var res = table.get(null, tuple);
assertEquals("def_str", res.stringValue("str"));
assertEquals("def_str2", res.stringValue("strNonNull"));
}
@Test
public void testNullableColumnWithDefaultValueSetNullReturnsNull() {
RecordView<Tuple> table = tableWithDefaultValues().recordView();
var tuple = Tuple.create()
.set("id", 1)
.set("str", null);
table.upsert(null, tuple);
var res = table.get(null, tuple);
assertNull(res.stringValue("str"));
}
@Test
public void testNonNullableColumnWithDefaultValueSetNullThrowsException() {
RecordView<Tuple> table = tableWithDefaultValues().recordView();
var tuple = Tuple.create()
.set("id", 1)
.set("strNonNull", null);
var ex = assertThrows(IgniteException.class, () -> table.upsert(null, tuple));
assertTrue(ex.getMessage().contains("Column 'STRNONNULL' does not allow NULLs"), ex.getMessage());
}
@Test
public void testColumnTypeMismatchThrowsException() {
var tuple = Tuple.create().set("id", "str");
var ex = assertThrows(IgniteException.class, () -> defaultTable().recordView().upsert(null, tuple));
assertEquals("Column's type mismatch [column=ID, expectedType=INT64, actualType=class java.lang.String]", ex.getMessage());
}
@Test
public void testGetFromDroppedTableThrowsException() {
((FakeIgniteTables) server.tables()).createTable("drop-me");
Table clientTable = client.tables().table("drop-me");
((FakeIgniteTables) server.tables()).dropTable("drop-me");
Tuple tuple = Tuple.create().set("id", 1);
var ex = assertThrows(IgniteException.class, () -> clientTable.recordView().get(null, tuple));
assertThat(ex.getMessage(), containsString("Table does not exist: "));
assertEquals(TABLE_ID_NOT_FOUND_ERR, ex.code());
}
private void checkSchemaUpdate(Consumer<RecordView<Tuple>> consumer) throws Exception {
try (var client2 = startClient()) {
var table = client2.tables().table(defaultTable().name());
Map<Integer, Object> schemas = IgniteTestUtils.getFieldValue(table, "schemas");
var recView = table.recordView();
assertEquals(0, schemas.size());
FakeSchemaRegistry.setLastVer(1);
consumer.accept(recView);
assertNull(schemas.get(2));
FakeSchemaRegistry.setLastVer(2);
consumer.accept(recView);
assertTrue(waitForCondition(() -> schemas.get(2) != null, 1000));
}
}
}