Skip to content

Commit 92cbde1

Browse files
committed
Introduce PseudoUtf8Type for virtual tables to search on types that are otherwise treated as strings for pretty printing;
Use this facility to make Accord debug virtual tables more useful patch by Benedict; reviewed by Alex Petrov for CASSANDRA-20755
1 parent fd2e11f commit 92cbde1

File tree

10 files changed

+326
-54
lines changed

10 files changed

+326
-54
lines changed

src/java/org/apache/cassandra/cql3/CQL3Type.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,13 @@ public AbstractType<?> getType()
166166
@Override
167167
public String toCQLLiteral(ByteBuffer buffer)
168168
{
169-
// *always* use the 'blob' syntax to express custom types in CQL
170-
return Native.BLOB.toCQLLiteral(buffer);
169+
CQL3Type asCql3 = type.asCQL3Type();
170+
if (asCql3 instanceof Custom)
171+
{
172+
// use the 'blob' syntax to express custom types in CQL if not overridden
173+
return Native.BLOB.toCQLLiteral(buffer);
174+
}
175+
return asCql3.toCQLLiteral(buffer);
171176
}
172177

173178
@Override
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.cassandra.db.marshal;
19+
20+
import java.nio.ByteBuffer;
21+
import java.nio.charset.CharacterCodingException;
22+
import java.nio.charset.StandardCharsets;
23+
24+
import org.apache.cassandra.cql3.CQL3Type;
25+
import org.apache.cassandra.cql3.terms.Constants;
26+
import org.apache.cassandra.cql3.terms.Term;
27+
import org.apache.cassandra.serializers.MarshalException;
28+
import org.apache.cassandra.transport.ProtocolVersion;
29+
import org.apache.cassandra.utils.ByteBufferUtil;
30+
import org.apache.cassandra.utils.JsonUtils;
31+
32+
public abstract class PseudoUtf8Type extends StringType
33+
{
34+
PseudoUtf8Type() {super(ComparisonType.CUSTOM);} // singleton
35+
36+
abstract String describe();
37+
38+
public ByteBuffer fromString(String source)
39+
{
40+
return decompose(source);
41+
}
42+
43+
@Override
44+
public Term fromJSONObject(Object parsed) throws MarshalException
45+
{
46+
try
47+
{
48+
return new Constants.Value(fromString((String) parsed));
49+
}
50+
catch (ClassCastException exc)
51+
{
52+
throw new MarshalException(String.format(
53+
"Expected a %s string, but got a %s: %s", describe(), parsed.getClass().getSimpleName(), parsed));
54+
}
55+
}
56+
57+
@Override
58+
public String toJSONString(ByteBuffer buffer, ProtocolVersion protocolVersion)
59+
{
60+
try
61+
{
62+
return '"' + JsonUtils.quoteAsJsonString(ByteBufferUtil.string(buffer, StandardCharsets.UTF_8)) + '"';
63+
}
64+
catch (CharacterCodingException exc)
65+
{
66+
throw new AssertionError(describe() + " value contained non-utf8 characters: ", exc);
67+
}
68+
}
69+
70+
@Override
71+
public boolean isCompatibleWith(AbstractType<?> previous)
72+
{
73+
// Anything that is ascii is also utf8, and they both use bytes comparison
74+
return previous == AsciiType.instance || previous == UTF8Type.instance || previous instanceof PseudoUtf8Type;
75+
}
76+
77+
@Override
78+
public CQL3Type asCQL3Type()
79+
{
80+
return CQL3Type.Native.TEXT;
81+
}
82+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.cassandra.db.marshal;
19+
20+
import java.nio.ByteBuffer;
21+
22+
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
23+
import org.apache.cassandra.dht.Token;
24+
import org.apache.cassandra.serializers.MarshalException;
25+
import org.apache.cassandra.serializers.TypeSerializer;
26+
import org.apache.cassandra.serializers.UTF8Serializer;
27+
import org.apache.cassandra.utils.ByteBufferUtil;
28+
29+
import static org.apache.cassandra.config.DatabaseDescriptor.getPartitioner;
30+
31+
public class TokenUtf8Type extends PseudoUtf8Type
32+
{
33+
public static final TokenUtf8Type instance = new TokenUtf8Type();
34+
static final TypeSerializer<String> tokenSerializer = new UTF8Serializer()
35+
{
36+
@Override
37+
public <V> void validate(V value, ValueAccessor<V> accessor) throws MarshalException
38+
{
39+
super.validate(value, accessor);
40+
String str = deserialize(value, accessor);
41+
if (null == getPartitioner().getTokenFactory().fromString(str))
42+
throw new MarshalException("Invalid Token: " + str);
43+
}
44+
};
45+
46+
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
47+
private static final ByteBuffer MASKED_VALUE = ByteBufferUtil.EMPTY_BYTE_BUFFER;
48+
49+
TokenUtf8Type() {} // singleton
50+
51+
String describe() { return "Token"; }
52+
53+
@Override
54+
public TypeSerializer<String> getSerializer()
55+
{
56+
return tokenSerializer;
57+
}
58+
59+
@Override
60+
public <VL, VR> int compareCustom(VL left, ValueAccessor<VL> accessorL, VR right, ValueAccessor<VR> accessorR)
61+
{
62+
String leftStr = UTF8Serializer.instance.deserialize(left, accessorL);
63+
String rightStr = UTF8Serializer.instance.deserialize(right, accessorR);
64+
Token leftToken = getPartitioner().getTokenFactory().fromString(leftStr);
65+
Token rightToken = getPartitioner().getTokenFactory().fromString(rightStr);
66+
return leftToken.compareTo(rightToken);
67+
}
68+
69+
@Override
70+
public ArgumentDeserializer getArgumentDeserializer()
71+
{
72+
return ARGUMENT_DESERIALIZER;
73+
}
74+
75+
@Override
76+
public ByteBuffer getMaskedValue()
77+
{
78+
return MASKED_VALUE;
79+
}
80+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.cassandra.db.marshal;
19+
20+
import java.nio.ByteBuffer;
21+
22+
import accord.primitives.TxnId;
23+
import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
24+
import org.apache.cassandra.serializers.MarshalException;
25+
import org.apache.cassandra.serializers.TypeSerializer;
26+
import org.apache.cassandra.serializers.UTF8Serializer;
27+
import org.apache.cassandra.utils.ByteBufferUtil;
28+
29+
public class TxnIdUtf8Type extends PseudoUtf8Type
30+
{
31+
public static final TxnIdUtf8Type instance = new TxnIdUtf8Type();
32+
static final TypeSerializer<String> txnIdSerializer = new UTF8Serializer()
33+
{
34+
@Override
35+
public <V> void validate(V value, ValueAccessor<V> accessor) throws MarshalException
36+
{
37+
super.validate(value, accessor);
38+
String str = deserialize(value, accessor);
39+
if (null == TxnId.tryParse(str))
40+
throw new MarshalException("Invalid TxnId: " + str);
41+
}
42+
};
43+
44+
private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new DefaultArgumentDeserializer(instance);
45+
private static final ByteBuffer MASKED_VALUE = ByteBufferUtil.EMPTY_BYTE_BUFFER;
46+
47+
TxnIdUtf8Type() {} // singleton
48+
49+
String describe() { return "TxnId"; }
50+
51+
@Override
52+
public TypeSerializer<String> getSerializer()
53+
{
54+
return txnIdSerializer;
55+
}
56+
57+
@Override
58+
public <VL, VR> int compareCustom(VL left, ValueAccessor<VL> accessorL, VR right, ValueAccessor<VR> accessorR)
59+
{
60+
String leftStr = UTF8Serializer.instance.deserialize(left, accessorL);
61+
String rightStr = UTF8Serializer.instance.deserialize(right, accessorR);
62+
TxnId leftId = TxnId.parse(leftStr);
63+
TxnId rightId = TxnId.parse(rightStr);
64+
return leftId.compareTo(rightId);
65+
}
66+
67+
@Override
68+
public ArgumentDeserializer getArgumentDeserializer()
69+
{
70+
return ARGUMENT_DESERIALIZER;
71+
}
72+
73+
@Override
74+
public ByteBuffer getMaskedValue()
75+
{
76+
return MASKED_VALUE;
77+
}
78+
}

src/java/org/apache/cassandra/db/marshal/TypeParser.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.apache.cassandra.dht.IPartitioner;
3636
import org.apache.cassandra.exceptions.ConfigurationException;
3737
import org.apache.cassandra.exceptions.SyntaxException;
38+
import org.apache.cassandra.service.StorageService;
3839
import org.apache.cassandra.utils.ByteBufferUtil;
3940
import org.apache.cassandra.utils.FBUtilities;
4041
import org.apache.cassandra.utils.Pair;
@@ -465,6 +466,11 @@ private static AbstractType<?> getAbstractType(String compareWith, TypeParser pa
465466
{
466467
String className = compareWith.contains(".") ? compareWith : "org.apache.cassandra.db.marshal." + compareWith;
467468
Class<? extends AbstractType<?>> typeClass = FBUtilities.<AbstractType<?>>classForName(className, "abstract-type");
469+
if (PseudoUtf8Type.class.isAssignableFrom(typeClass))
470+
{
471+
if (StorageService.instance.isDaemonSetupCompleted())
472+
throw new ConfigurationException(typeClass.getName() + " is reserved for internal functionality");
473+
}
468474
try
469475
{
470476
Method method = typeClass.getDeclaredMethod("getInstance", TypeParser.class);

src/java/org/apache/cassandra/db/marshal/UTF8Type.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public boolean isCompatibleWith(AbstractType<?> previous)
8080
{
8181
// Anything that is ascii is also utf8, and they both use bytes
8282
// comparison
83-
return this == previous || previous == AsciiType.instance;
83+
return this == previous || previous == AsciiType.instance || previous instanceof PseudoUtf8Type;
8484
}
8585

8686
@Override

0 commit comments

Comments
 (0)