Skip to content

Commit

Permalink
Make ClientTypeSignature backward compatible
Browse files Browse the repository at this point in the history
Serialize type signatures using the legacy format
(type arguments + literal arguments) for backward compatibility with
old clients
  • Loading branch information
pnowojski authored and martint committed Jan 8, 2016
1 parent d7738ba commit 27145e0
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 17 deletions.
10 changes: 10 additions & 0 deletions presto-client/pom.xml
Expand Up @@ -36,6 +36,16 @@
<artifactId>jackson-annotations</artifactId>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

<dependency>
<groupId>io.airlift</groupId>
<artifactId>http-client</artifactId>
Expand Down
Expand Up @@ -38,7 +38,7 @@ public class ClientTypeSignature
{
private static final Pattern PATTERN = Pattern.compile(".*[<>,].*");
private final String rawType;
private final List<ClientTypeSignatureParameter> typeArguments;
private final List<ClientTypeSignatureParameter> arguments;

public ClientTypeSignature(TypeSignature typeSignature)
{
Expand All @@ -50,14 +50,14 @@ public ClientTypeSignature(TypeSignature typeSignature)
@JsonCreator
public ClientTypeSignature(
@JsonProperty("rawType") String rawType,
@JsonProperty("typeArguments") List<ClientTypeSignatureParameter> typeArguments)
@JsonProperty("arguments") List<ClientTypeSignatureParameter> arguments)
{
checkArgument(rawType != null, "rawType is null");
this.rawType = rawType;
checkArgument(!rawType.isEmpty(), "rawType is empty");
checkArgument(!PATTERN.matcher(rawType).matches(), "Bad characters in rawType type: %s", rawType);
checkArgument(typeArguments != null, "typeArguments is null");
this.typeArguments = unmodifiableList(new ArrayList<>(typeArguments));
checkArgument(arguments != null, "arguments is null");
this.arguments = unmodifiableList(new ArrayList<>(arguments));
}

@JsonProperty
Expand All @@ -67,9 +67,52 @@ public String getRawType()
}

@JsonProperty
public List<ClientTypeSignatureParameter> getTypeArguments()
public List<ClientTypeSignatureParameter> getArguments()
{
return typeArguments;
return arguments;
}

/**
* This field is deprecated and clients should switch to {@link #getArguments()}
*/
@Deprecated
@JsonProperty
public List<ClientTypeSignature> getTypeArguments()
{
List<ClientTypeSignature> result = new ArrayList<>();
for (ClientTypeSignatureParameter argument : arguments) {
switch (argument.getKind()) {
case TYPE_SIGNATURE:
result.add(argument.getTypeSignature());
break;
case NAMED_TYPE_SIGNATURE:
result.add(new ClientTypeSignature(argument.getNamedTypeSignature().getTypeSignature()));
break;
default:
return new ArrayList<>();
}
}
return result;
}

/**
* This field is deprecated and clients should switch to {@link #getArguments()}
*/
@Deprecated
@JsonProperty
public List<Object> getLiteralArguments()
{
List<Object> result = new ArrayList<>();
for (ClientTypeSignatureParameter argument : arguments) {
switch (argument.getKind()) {
case NAMED_TYPE_SIGNATURE:
result.add(argument.getNamedTypeSignature().getName());
break;
default:
return new ArrayList<>();
}
}
return result;
}

@Override
Expand All @@ -80,10 +123,10 @@ public String toString()
}
else {
StringBuilder typeName = new StringBuilder(rawType);
if (!typeArguments.isEmpty()) {
if (!arguments.isEmpty()) {
typeName.append("(");
boolean first = true;
for (ClientTypeSignatureParameter argument : typeArguments) {
for (ClientTypeSignatureParameter argument : arguments) {
if (!first) {
typeName.append(",");
}
Expand All @@ -99,13 +142,13 @@ public String toString()
@Deprecated
private String rowToString()
{
String types = typeArguments.stream()
String types = arguments.stream()
.map(ClientTypeSignatureParameter::getNamedTypeSignature)
.map(NamedTypeSignature::getTypeSignature)
.map(TypeSignature::toString)
.collect(Collectors.joining(","));

String fieldNames = typeArguments.stream()
String fieldNames = arguments.stream()
.map(ClientTypeSignatureParameter::getNamedTypeSignature)
.map(NamedTypeSignature::getName)
.map(name -> format("'%s'", name))
Expand All @@ -127,12 +170,12 @@ public boolean equals(Object o)
ClientTypeSignature other = (ClientTypeSignature) o;

return Objects.equals(this.rawType.toLowerCase(Locale.ENGLISH), other.rawType.toLowerCase(Locale.ENGLISH)) &&
Objects.equals(this.typeArguments, other.typeArguments);
Objects.equals(this.arguments, other.arguments);
}

@Override
public int hashCode()
{
return Objects.hash(rawType.toLowerCase(Locale.ENGLISH), typeArguments);
return Objects.hash(rawType.toLowerCase(Locale.ENGLISH), arguments);
}
}
Expand Up @@ -18,14 +18,22 @@
import com.facebook.presto.spi.type.TypeSignatureParameter;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import javax.annotation.concurrent.Immutable;

import java.io.IOException;
import java.util.Objects;

import static java.lang.String.format;

@Immutable
@JsonDeserialize(using = ClientTypeSignatureParameter.ClientTypeSignatureParameterDeserializer.class)
public class ClientTypeSignatureParameter
{
private final ParameterKind kind;
Expand All @@ -43,6 +51,7 @@ public ClientTypeSignatureParameter(TypeSignatureParameter typeParameterSignatur
break;
case NAMED_TYPE_SIGNATURE:
value = typeParameterSignature.getNamedTypeSignature();
break;
default:
throw new UnsupportedOperationException(format("Unknown kind [%s]", kind));
}
Expand Down Expand Up @@ -119,4 +128,33 @@ public int hashCode()
{
return Objects.hash(kind, value);
}

public static class ClientTypeSignatureParameterDeserializer extends JsonDeserializer<ClientTypeSignatureParameter>
{
private static final ObjectMapper MAPPER = new ObjectMapper();

@Override
public ClientTypeSignatureParameter deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException
{
JsonNode node = jp.getCodec().readTree(jp);
ParameterKind kind = MAPPER.readValue(MAPPER.treeAsTokens(node.get("kind")), ParameterKind.class);
JsonParser jsonValue = MAPPER.treeAsTokens(node.get("value"));
Object value;
switch (kind) {
case TYPE_SIGNATURE:
value = MAPPER.readValue(jsonValue, ClientTypeSignature.class);
break;
case NAMED_TYPE_SIGNATURE:
value = MAPPER.readValue(jsonValue, NamedTypeSignature.class);
break;
case LONG_LITERAL:
value = MAPPER.readValue(jsonValue, Long.class);
break;
default:
throw new UnsupportedOperationException(format("Unsupported kind [%s]", kind));
}
return new ClientTypeSignatureParameter(kind, value);
}
}
}
@@ -0,0 +1,62 @@
/*
* Licensed 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 com.facebook.presto.client;

import com.facebook.presto.spi.type.NamedTypeSignature;
import com.facebook.presto.spi.type.TypeSignature;
import com.facebook.presto.spi.type.TypeSignatureParameter;
import com.google.common.collect.ImmutableList;
import io.airlift.json.JsonCodec;
import io.airlift.json.JsonCodecFactory;
import io.airlift.json.ObjectMapperProvider;
import org.testng.annotations.Test;

import static com.facebook.presto.spi.type.BigintType.BIGINT;
import static org.testng.Assert.assertEquals;

public class TestClientTypeSignature
{
public static final JsonCodec<ClientTypeSignature> CLIENT_TYPE_SIGNATURE_CODEC;

static {
ObjectMapperProvider provider = new ObjectMapperProvider();
JsonCodecFactory codecFactory = new JsonCodecFactory(provider);
CLIENT_TYPE_SIGNATURE_CODEC = codecFactory.jsonCodec(ClientTypeSignature.class);
}

@Test
public void testJsonRoundTrip()
{
TypeSignature bigint = BIGINT.getTypeSignature();
assertJsonRoundTrip(new ClientTypeSignature(bigint));
assertJsonRoundTrip(new ClientTypeSignature(
"array",
ImmutableList.of(new ClientTypeSignatureParameter(TypeSignatureParameter.of(bigint)))));
assertJsonRoundTrip(new ClientTypeSignature(
"foo",
ImmutableList.of(new ClientTypeSignatureParameter(TypeSignatureParameter.of(42)))));
assertJsonRoundTrip(new ClientTypeSignature(
"row",
ImmutableList.of(
new ClientTypeSignatureParameter(TypeSignatureParameter.of(new NamedTypeSignature("foo", bigint))),
new ClientTypeSignatureParameter(TypeSignatureParameter.of(new NamedTypeSignature("bar", bigint))))));
}

private static void assertJsonRoundTrip(ClientTypeSignature signature)
{
String json = CLIENT_TYPE_SIGNATURE_CODEC.toJson(signature);
ClientTypeSignature copy = CLIENT_TYPE_SIGNATURE_CODEC.fromJson(json);
assertEquals(copy, signature);
}
}
Expand Up @@ -15,6 +15,7 @@

import com.facebook.presto.client.PrestoHeaders;
import com.facebook.presto.server.testing.TestingPrestoServer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.net.HttpHeaders;
import io.airlift.http.client.HttpClient;
import io.airlift.http.client.HttpStatus;
Expand Down Expand Up @@ -59,14 +60,17 @@ public void testExecute()
throws Exception
{
String expected = "{\"columns\":[" +
"{\"name\":\"foo\",\"type\":\"bigint\",\"typeSignature\":{\"rawType\":\"bigint\",\"typeArguments\":[]}}," +
"{\"name\":\"bar\",\"type\":\"varchar\",\"typeSignature\":{\"rawType\":\"varchar\",\"typeArguments\":[]}}]," +
"\"data\":[[123,\"abc\"]]}\n";
"{\"name\":\"foo\",\"type\":\"bigint\",\"typeSignature\":{\"rawType\":\"bigint\",\"arguments\":[],\"typeArguments\":[],\"literalArguments\":[]}}," +
"{\"name\":\"bar\",\"type\":\"varchar\",\"typeSignature\":{\"rawType\":\"varchar\",\"arguments\":[],\"typeArguments\":[],\"literalArguments\":[]}}," +
"{\"name\":\"baz\",\"type\":\"array(bigint)\",\"typeSignature\":{\"rawType\":\"array\",\"arguments\":[{\"kind\":\"TYPE_SIGNATURE\",\"value\":{\"rawType\":\"bigint\",\"arguments\":[],\"typeArguments\":[],\"literalArguments\":[]}}],\"typeArguments\":[{\"rawType\":\"bigint\",\"arguments\":[],\"typeArguments\":[],\"literalArguments\":[]}],\"literalArguments\":[]}}]," +
"\"data\":[[123,\"abc\",[42,44]]]}\n";

StringResponse response = executeQuery("SELECT 123 foo, 'abc' bar");
StringResponse response = executeQuery("SELECT 123 foo, 'abc' bar, CAST(JSON_PARSE('[42,44]') AS ARRAY<BIGINT>) baz");
assertEquals(response.getStatusCode(), HttpStatus.OK.code());
assertEquals(response.getHeader(HttpHeaders.CONTENT_TYPE), "application/json");
assertEquals(response.getBody(), expected);

ObjectMapper mapper = new ObjectMapper();
assertEquals(mapper.readTree(response.getBody()), mapper.readTree(expected));
}

private StringResponse executeQuery(String query)
Expand Down

0 comments on commit 27145e0

Please sign in to comment.