/** * Returns a mutation that will delete the row with primary key {@code key}. Exactly equivalent to * {@code delete(table, KeySet.singleKey(key))}. */ public static Mutation delete(String table, Key key) { return delete(table, KeySet.singleKey(key)); }
@Nullable @Override public final Struct readRow(String table, Key key, Iterable<String> columns) { try (ResultSet resultSet = read(table, KeySet.singleKey(key), columns)) { return consumeSingleRow(resultSet); } }
@Nullable @Override public final Struct readRowUsingIndex( String table, String index, Key key, Iterable<String> columns) { try (ResultSet resultSet = readUsingIndex(table, index, KeySet.singleKey(key), columns)) { return consumeSingleRow(resultSet); } }
@Test public void serializationSingleKeyString() { KeySet keySet = KeySet.singleKey(Key.of("abc")); checkProto(keySet, "keys { values { string_value: 'abc' } }"); }
@Test public void serializationSingleKeyMultiPart() { KeySet keySet = KeySet.singleKey(Key.of("a", false)); checkProto(keySet, "keys { values { string_value: 'a' } values { bool_value: false } }"); }
@Test public void serializationEmpty() { KeySet keySet = KeySet.singleKey(Key.of()); checkProto(keySet, "keys {}"); }
@Test public void serializationSingleKeyInt64() { KeySet keySet = KeySet.singleKey(Key.of(1234L)); checkProto(keySet, "keys { values { string_value: '1234' } }"); }
@Test public void serializationSingleKeyFloat64() { KeySet keySet = KeySet.singleKey(Key.of(2.0)); checkProto(keySet, "keys { values { number_value: 2.0 } }"); }
@Test public void serializationSingleKeyBool() { KeySet keySet = KeySet.singleKey(Key.of(true)); checkProto(keySet, "keys { values { bool_value: true } }"); }
@Test public void serializationSingleKeyNull() { KeySet keySet = KeySet.singleKey(Key.of((String) null)); checkProto(keySet, "keys { values { null_value: NULL_VALUE } }"); }
@Test public void serializationSingleKeyBytes() { KeySet keySet = KeySet.singleKey(Key.of(ByteArray.copyFrom(new byte[] {'a'}))); checkProto(keySet, "keys { values { string_value: 'YQ==' } }"); }
@Test public void delete() { KeySet keySet = KeySet.singleKey(Key.of("k1")); Mutation m = Mutation.delete("T1", keySet); assertThat(m.getOperation()).isEqualTo(Mutation.Op.DELETE); assertThat(m.getKeySet()).isEqualTo(keySet); assertThat(m.toString()).isEqualTo("delete(T1{[k1]})"); }
@Test public void singleKey() { KeySet set = KeySet.singleKey(Key.of("a", "b", "c")); assertThat(set.isAll()).isFalse(); assertThat(set.getKeys()).containsExactly(Key.of("a", "b", "c")); assertThat(set.getRanges()).isEmpty(); }
@Test public void testToString() { assertThat(KeySet.all().toString()).isEqualTo("{<all>}"); assertThat(KeySet.singleKey(Key.of("x")).toString()).isEqualTo("{[x]}"); assertThat(KeySet.range(KeyRange.closedOpen(Key.of("a"), Key.of("z"))).toString()) .isEqualTo("{[[a],[z])}"); }
@Test public void cursorErrorDeferred() { // Error should be deferred until next(). This gives consistent behavior with respect to // non-blocking implementations (e.g., gRPC). ResultSet resultSet = client .singleUse(TimestampBound.strong()) .read("BadTableName", KeySet.singleKey(Key.of("k1")), ALL_COLUMNS); expectedException.expect(isSpannerException(ErrorCode.NOT_FOUND)); expectedException.expectMessage("BadTableName"); resultSet.next(); }
@Test public void toBuilder() { KeySet set = KeySet.singleKey(Key.of(1)).toBuilder().addKey(Key.of(2)).build(); assertThat(set.isAll()).isFalse(); assertThat(set.getKeys()).containsExactly(Key.of(1), Key.of(2)).inOrder(); assertThat(set.getRanges()).isEmpty(); set = KeySet.range(KeyRange.closedOpen(Key.of("a"), Key.of("b"))) .toBuilder() .addRange(KeyRange.closedOpen(Key.of("c"), Key.of("d"))) .build(); assertThat(set.isAll()).isFalse(); assertThat(set.getKeys()).isEmpty(); assertThat(set.getRanges()) .containsExactly( KeyRange.closedOpen(Key.of("a"), Key.of("b")), KeyRange.closedOpen(Key.of("c"), Key.of("d"))) .inOrder(); set = KeySet.all().toBuilder().addKey(Key.of(1)).build(); assertThat(set.isAll()).isTrue(); assertThat(set.getKeys()).containsExactly(Key.of(1)); assertThat(set.getRanges()).isEmpty(); }
@Test public void equalsAndHashCode() { EqualsTester tester = new EqualsTester(); // Equality, not identity. tester.addEqualityGroup( Mutation.newInsertBuilder("T1").build(), Mutation.newInsertBuilder("T1").build()); // Operation types are distinguished. tester.addEqualityGroup(Mutation.newInsertOrUpdateBuilder("T1").build()); tester.addEqualityGroup(Mutation.newUpdateBuilder("T1").build()); tester.addEqualityGroup(Mutation.newReplaceBuilder("T1").build()); // Table is distinguished. tester.addEqualityGroup(Mutation.newInsertBuilder("T2").build()); // Columns/values are distinguished (but by equality, not identity). tester.addEqualityGroup( Mutation.newInsertBuilder("T1").set("C").to("V").build(), Mutation.newInsertBuilder("T1").set("C").to("V").build()); // Deletes consider the key set. tester.addEqualityGroup(Mutation.delete("T1", KeySet.all())); tester.addEqualityGroup( Mutation.delete("T1", KeySet.singleKey(Key.of("k"))), Mutation.delete("T1", Key.of("k"))); tester.testEquals(); }
@Test public void equalsAndHashCode() { EqualsTester tester = new EqualsTester(); tester.addEqualityGroup(KeySet.newBuilder().build()); tester.addEqualityGroup(KeySet.all(), KeySet.newBuilder().setAll().build()); tester.addEqualityGroup( KeySet.singleKey(Key.of("a")), KeySet.newBuilder().addKey(Key.of("a")).build()); tester.addEqualityGroup( KeySet.range(KeyRange.closedOpen(Key.of("a"), Key.of("b"))), KeySet.newBuilder().addRange(KeyRange.closedOpen(Key.of("a"), Key.of("b"))).build()); tester.addEqualityGroup(KeySet.newBuilder().addKey(Key.of(1)).addKey(Key.of(2)).build()); // We currently consider order, although this doesn't affect visible results. tester.addEqualityGroup(KeySet.newBuilder().addKey(Key.of(2)).addKey(Key.of(1)).build()); tester.addEqualityGroup(KeySet.newBuilder().setAll().addKey(Key.of("a")).build()); tester.addEqualityGroup( KeySet.newBuilder() .addKey(Key.of("a")) .addRange(KeyRange.closedOpen(Key.of("a"), Key.of("b"))) .build()); tester.testEquals(); }
@Test public void rangeReads() { checkRange(Source.BASE_TABLE, KeySet.singleKey(Key.of("k1")), 1); checkRange(Source.BASE_TABLE, KeyRange.closedOpen(Key.of("k3"), Key.of("k5")), 3, 4); checkRange(Source.BASE_TABLE, KeyRange.closedClosed(Key.of("k3"), Key.of("k5")), 3, 4, 5); checkRange(Source.BASE_TABLE, KeyRange.openClosed(Key.of("k3"), Key.of("k5")), 4, 5); checkRange(Source.BASE_TABLE, KeyRange.openOpen(Key.of("k3"), Key.of("k5")), 4); // Partial key specification. checkRange(Source.BASE_TABLE, KeyRange.closedClosed(Key.of("k7"), Key.of()), 7, 8, 9); checkRange(Source.BASE_TABLE, KeyRange.openClosed(Key.of("k7"), Key.of()), 8, 9); checkRange(Source.BASE_TABLE, KeyRange.closedOpen(Key.of(), Key.of("k11")), 0, 1, 10); checkRange(Source.BASE_TABLE, KeyRange.closedClosed(Key.of(), Key.of("k11")), 0, 1, 10, 11); // The following produce empty ranges. // TODO(user): Consider a multi-part key to illustrate partial key behavior. checkRange(Source.BASE_TABLE, KeyRange.closedOpen(Key.of("k7"), Key.of())); checkRange(Source.BASE_TABLE, KeyRange.openOpen(Key.of("k7"), Key.of())); checkRange(Source.BASE_TABLE, KeyRange.openOpen(Key.of(), Key.of("k11"))); checkRange(Source.BASE_TABLE, KeyRange.openClosed(Key.of(), Key.of("k11"))); // Prefix is component-wise, not string prefix. checkRange(Source.BASE_TABLE, KeyRange.prefix(Key.of("k1")), 1); checkRange( Source.BASE_TABLE, KeyRange.closedOpen(Key.of("k1"), Key.of("k2")), 1, 10, 11, 12, 13, 14); checkRange(Source.BASE_TABLE, KeySet.all(), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); }
@Test public void indexRangeReads() { checkRange(Source.INDEX, KeySet.singleKey(Key.of("v1")), 1); checkRange(Source.INDEX, KeyRange.closedOpen(Key.of("v3"), Key.of("v5")), 3, 4); checkRange(Source.INDEX, KeyRange.closedClosed(Key.of("v3"), Key.of("v5")), 3, 4, 5); checkRange(Source.INDEX, KeyRange.openClosed(Key.of("v3"), Key.of("v5")), 4, 5); checkRange(Source.INDEX, KeyRange.openOpen(Key.of("v3"), Key.of("v5")), 4); // Partial key specification. checkRange(Source.INDEX, KeyRange.closedClosed(Key.of("v7"), Key.of()), 7, 8, 9); checkRange(Source.INDEX, KeyRange.openClosed(Key.of("v7"), Key.of()), 8, 9); checkRange(Source.INDEX, KeyRange.closedOpen(Key.of(), Key.of("v11")), 0, 1, 10); checkRange(Source.INDEX, KeyRange.closedClosed(Key.of(), Key.of("v11")), 0, 1, 10, 11); // The following produce empty ranges. checkRange(Source.INDEX, KeyRange.closedOpen(Key.of("v7"), Key.of())); checkRange(Source.INDEX, KeyRange.openOpen(Key.of("v7"), Key.of())); checkRange(Source.INDEX, KeyRange.openOpen(Key.of(), Key.of("v11"))); checkRange(Source.INDEX, KeyRange.openClosed(Key.of(), Key.of("v11"))); // Prefix is component-wise, not string prefix. checkRange(Source.INDEX, KeyRange.prefix(Key.of("v1")), 1); checkRange( Source.INDEX, KeyRange.closedOpen(Key.of("v1"), Key.of("v2")), 1, 10, 11, 12, 13, 14); checkRange(Source.INDEX, KeySet.all(), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14); // Read from an index with DESC ordering. checkRange(Source.DESC_INDEX, KeySet.all(), 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); }