зеркало из https://github.com/github/vitess-gh.git
java: checkstyle for client module
Signed-off-by: Ze'ev Klapow <zklapow@hubspot.com>
This commit is contained in:
Родитель
af4a648d7e
Коммит
4a36888bf5
|
@ -0,0 +1 @@
|
|||
target/
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
|
||||
<suppressions>
|
||||
<suppress files="[\\/]generated-sources[\\/]" checks=".*"/>
|
||||
<suppress files="[\\/]main[\\/]java[\\/].*\.java" id="JavadocParagraph"/>
|
||||
<suppress checks="JavadocParagraph" files=".*[\\/]src[\\/](test|main)[\\/].*\.java"/>
|
||||
<suppress checks="AbbreviationAsWordInName" files=".*[\\/]src[\\/](test|main)[\\/].*\.java"/>
|
||||
<suppress checks="JavadocMethod" files=".*[\\/]src[\\/](test|main)[\\/].*\.java"/>
|
||||
<suppress checks="SummaryJavadoc" files=".*[\\/]src[\\/](test|main)[\\/].*\.java"/>
|
||||
<suppress checks="OverloadMethodsDeclarationOrder" files=".*[\\/]src[\\/](test|main)[\\/].*\.java"/>
|
||||
</suppressions>
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -16,31 +16,41 @@
|
|||
|
||||
package io.vitess.client;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import io.vitess.proto.Vtrpc.CallerID;
|
||||
|
||||
import org.joda.time.Duration;
|
||||
import org.joda.time.Instant;
|
||||
|
||||
import io.vitess.proto.Vtrpc.CallerID;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Context is an immutable object that carries per-request info.
|
||||
*
|
||||
* <p>RPC frameworks like gRPC have their own Context implementations that
|
||||
* allow propagation of deadlines, cancellation, and end-user credentials
|
||||
* across RPC boundaries (between client and server). Since these
|
||||
* framework-specific Context implementations are not compatible with one
|
||||
* another, we provide our own Context class that wraps common features.
|
||||
* allow propagation of deadlines, cancellation, and end-user credentials across RPC boundaries
|
||||
* (between client and server). Since these framework-specific Context implementations are not
|
||||
* compatible with one another, we provide our own Context class that wraps common features.
|
||||
*
|
||||
* <p>In gRPC and other frameworks, the current Context is maintained in
|
||||
* thread-local storage, so it's implicitly available to any method that
|
||||
* needs it. In this Vitess client library, we pass Context as an explicit
|
||||
* parameter to methods that need it. This allows us to defer enforcement
|
||||
* of the specified request constraints until the request reaches the
|
||||
* underlying framework-specific Vitess client implementation, at which point
|
||||
* the native Context class can be used.
|
||||
* thread-local storage, so it's implicitly available to any method that needs it. In this Vitess
|
||||
* client library, we pass Context as an explicit parameter to methods that need it. This allows us
|
||||
* to defer enforcement of the specified request constraints until the request reaches the
|
||||
* underlying framework-specific Vitess client implementation, at which point the native Context
|
||||
* class can be used.
|
||||
*/
|
||||
public class Context {
|
||||
|
||||
private static final Context DEFAULT_CONTEXT = new Context();
|
||||
private Instant deadline;
|
||||
private CallerID callerId;
|
||||
|
||||
private Context() {
|
||||
}
|
||||
|
||||
private Context(Instant deadline, CallerID callerId) {
|
||||
this.deadline = deadline;
|
||||
this.callerId = callerId;
|
||||
}
|
||||
|
||||
// getDefault returns an empty context.
|
||||
public static Context getDefault() {
|
||||
|
@ -57,8 +67,8 @@ public class Context {
|
|||
}
|
||||
|
||||
/**
|
||||
* withDeadlineAfter returns a derived context with a maximum deadline
|
||||
* specified relative to the current time.
|
||||
* withDeadlineAfter returns a derived context with a maximum deadline specified relative to the
|
||||
* current time.
|
||||
*/
|
||||
public Context withDeadlineAfter(Duration duration) {
|
||||
return withDeadline(Instant.now().plus(duration));
|
||||
|
@ -90,14 +100,4 @@ public class Context {
|
|||
public CallerID getCallerId() {
|
||||
return callerId;
|
||||
}
|
||||
|
||||
private Instant deadline;
|
||||
private CallerID callerId;
|
||||
|
||||
private Context() {}
|
||||
|
||||
private Context(Instant deadline, CallerID callerId) {
|
||||
this.deadline = deadline;
|
||||
this.callerId = callerId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -21,20 +21,6 @@ import com.google.common.collect.ImmutableList;
|
|||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.primitives.UnsignedLong;
|
||||
import com.google.protobuf.ByteString;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.sql.SQLInvalidAuthorizationSpecException;
|
||||
import java.sql.SQLNonTransientException;
|
||||
import java.sql.SQLRecoverableException;
|
||||
import java.sql.SQLSyntaxErrorException;
|
||||
import java.sql.SQLTimeoutException;
|
||||
import java.sql.SQLTransientException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.vitess.client.cursor.Cursor;
|
||||
import io.vitess.client.cursor.CursorWithError;
|
||||
|
@ -48,11 +34,41 @@ import io.vitess.proto.Vtgate.BoundShardQuery;
|
|||
import io.vitess.proto.Vtgate.ExecuteEntityIdsRequest.EntityId;
|
||||
import io.vitess.proto.Vtrpc.RPCError;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.sql.SQLInvalidAuthorizationSpecException;
|
||||
import java.sql.SQLNonTransientException;
|
||||
import java.sql.SQLRecoverableException;
|
||||
import java.sql.SQLSyntaxErrorException;
|
||||
import java.sql.SQLTimeoutException;
|
||||
import java.sql.SQLTransientException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Proto contains methods for working with Vitess protobuf messages.
|
||||
*/
|
||||
public class Proto {
|
||||
|
||||
public static final Function<byte[], ByteString> BYTE_ARRAY_TO_BYTE_STRING =
|
||||
new Function<byte[], ByteString>() {
|
||||
@Override
|
||||
public ByteString apply(byte[] from) {
|
||||
return ByteString.copyFrom(from);
|
||||
}
|
||||
};
|
||||
public static final Function<Map.Entry<byte[], ?>, EntityId> MAP_ENTRY_TO_ENTITY_KEYSPACE_ID =
|
||||
new Function<Map.Entry<byte[], ?>, EntityId>() {
|
||||
@Override
|
||||
public EntityId apply(Map.Entry<byte[], ?> entry) {
|
||||
return buildEntityId(entry.getKey(), entry.getValue());
|
||||
}
|
||||
};
|
||||
private static final int MAX_DECIMAL_UNIT = 30;
|
||||
|
||||
/**
|
||||
|
@ -133,7 +149,7 @@ public class Proto {
|
|||
}
|
||||
try {
|
||||
return Integer.parseInt(errorMessage.substring(start, end));
|
||||
} catch (NumberFormatException e) {
|
||||
} catch (NumberFormatException exc) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -256,7 +272,8 @@ public class Proto {
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
public static List<CursorWithError> fromQueryResponsesToCursorList(List<Query.ResultWithError> resultWithErrorList) {
|
||||
public static List<CursorWithError> fromQueryResponsesToCursorList(
|
||||
List<Query.ResultWithError> resultWithErrorList) {
|
||||
ImmutableList.Builder<CursorWithError> builder = new ImmutableList.Builder<CursorWithError>();
|
||||
for (Query.ResultWithError resultWithError : resultWithErrorList) {
|
||||
builder.add(new CursorWithError(resultWithError));
|
||||
|
@ -264,26 +281,11 @@ public class Proto {
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
public static final Function<byte[], ByteString> BYTE_ARRAY_TO_BYTE_STRING =
|
||||
new Function<byte[], ByteString>() {
|
||||
@Override
|
||||
public ByteString apply(byte[] from) {
|
||||
return ByteString.copyFrom(from);
|
||||
}
|
||||
};
|
||||
|
||||
public static final Function<Map.Entry<byte[], ?>, EntityId> MAP_ENTRY_TO_ENTITY_KEYSPACE_ID =
|
||||
new Function<Map.Entry<byte[], ?>, EntityId>() {
|
||||
@Override
|
||||
public EntityId apply(Map.Entry<byte[], ?> entry) {
|
||||
return buildEntityId(entry.getKey(), entry.getValue());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a type and value in the type system used in query.proto.
|
||||
*/
|
||||
protected static class TypedValue {
|
||||
|
||||
Query.Type type;
|
||||
ByteString value;
|
||||
|
||||
|
@ -300,7 +302,7 @@ public class Proto {
|
|||
this.type = Query.Type.VARBINARY;
|
||||
this.value = ByteString.copyFrom((byte[]) value);
|
||||
} else if (value instanceof Integer || value instanceof Long || value instanceof Short
|
||||
|| value instanceof Byte ) {
|
||||
|| value instanceof Byte) {
|
||||
// Int32, Int64, Short, Byte
|
||||
this.type = Query.Type.INT64;
|
||||
this.value = ByteString.copyFromUtf8(value.toString());
|
||||
|
|
|
@ -3,33 +3,34 @@ package io.vitess.client;
|
|||
import java.io.File;
|
||||
|
||||
public class RefreshableVTGateConnection extends VTGateConnection {
|
||||
private final File keystoreFile;
|
||||
private final File truststoreFile;
|
||||
private volatile long keystoreMtime;
|
||||
private volatile long truststoreMtime;
|
||||
|
||||
public RefreshableVTGateConnection(RpcClient client,
|
||||
String keystorePath,
|
||||
String truststorePath) {
|
||||
super(client);
|
||||
this.keystoreFile = new File(keystorePath);
|
||||
this.truststoreFile = new File(truststorePath);
|
||||
this.keystoreMtime = this.keystoreFile.exists() ? this.keystoreFile.lastModified() : 0;
|
||||
this.truststoreMtime = this.truststoreFile.exists() ? this.truststoreFile.lastModified() : 0;
|
||||
}
|
||||
private final File keystoreFile;
|
||||
private final File truststoreFile;
|
||||
private volatile long keystoreMtime;
|
||||
private volatile long truststoreMtime;
|
||||
|
||||
public boolean checkKeystoreUpdates() {
|
||||
long keystoreMtime = keystoreFile.exists() ? keystoreFile.lastModified() : 0;
|
||||
long truststoreMtime = truststoreFile.exists() ? truststoreFile.lastModified() : 0;
|
||||
boolean modified = false;
|
||||
if (keystoreMtime > this.keystoreMtime) {
|
||||
modified = true;
|
||||
this.keystoreMtime = keystoreMtime;
|
||||
}
|
||||
if (truststoreMtime > this.truststoreMtime) {
|
||||
modified = true;
|
||||
this.truststoreMtime = truststoreMtime;
|
||||
}
|
||||
return modified;
|
||||
public RefreshableVTGateConnection(RpcClient client,
|
||||
String keystorePath,
|
||||
String truststorePath) {
|
||||
super(client);
|
||||
this.keystoreFile = new File(keystorePath);
|
||||
this.truststoreFile = new File(truststorePath);
|
||||
this.keystoreMtime = this.keystoreFile.exists() ? this.keystoreFile.lastModified() : 0;
|
||||
this.truststoreMtime = this.truststoreFile.exists() ? this.truststoreFile.lastModified() : 0;
|
||||
}
|
||||
|
||||
public boolean checkKeystoreUpdates() {
|
||||
long keystoreMtime = keystoreFile.exists() ? keystoreFile.lastModified() : 0;
|
||||
long truststoreMtime = truststoreFile.exists() ? truststoreFile.lastModified() : 0;
|
||||
boolean modified = false;
|
||||
if (keystoreMtime > this.keystoreMtime) {
|
||||
modified = true;
|
||||
this.keystoreMtime = keystoreMtime;
|
||||
}
|
||||
if (truststoreMtime > this.truststoreMtime) {
|
||||
modified = true;
|
||||
this.truststoreMtime = truststoreMtime;
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -17,8 +17,6 @@
|
|||
package io.vitess.client;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import java.io.Closeable;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import io.vitess.proto.Query.QueryResult;
|
||||
import io.vitess.proto.Vtgate;
|
||||
|
@ -51,10 +49,14 @@ import io.vitess.proto.Vtgate.StreamExecuteKeyspaceIdsRequest;
|
|||
import io.vitess.proto.Vtgate.StreamExecuteRequest;
|
||||
import io.vitess.proto.Vtgate.StreamExecuteShardsRequest;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* RpcClient defines a set of methods to communicate with VTGates.
|
||||
*/
|
||||
public interface RpcClient extends Closeable {
|
||||
|
||||
/**
|
||||
* Sends a single query using the VTGate V3 API.
|
||||
*
|
||||
|
@ -112,7 +114,8 @@ public interface RpcClient extends Closeable {
|
|||
* <a href="https://github.com/vitessio/vitess/blob/master/proto/vtgateservice.proto">proto</a>
|
||||
* definition for canonical documentation on this VTGate API.
|
||||
*/
|
||||
ListenableFuture<Vtgate.ExecuteBatchResponse> executeBatch(Context ctx, Vtgate.ExecuteBatchRequest request)
|
||||
ListenableFuture<Vtgate.ExecuteBatchResponse> executeBatch(Context ctx,
|
||||
Vtgate.ExecuteBatchRequest request)
|
||||
throws SQLException;
|
||||
|
||||
/**
|
||||
|
@ -139,9 +142,9 @@ public interface RpcClient extends Closeable {
|
|||
* Starts stream queries with the VTGate V3 API.
|
||||
*
|
||||
* <p>Note: Streaming queries are not asynchronous, because they typically shouldn't
|
||||
* be used from a latency-critical serving path anyway. This method will return as
|
||||
* soon as the request is initiated, but StreamIterator methods will block until the
|
||||
* next chunk of results is received from the server.
|
||||
* be used from a latency-critical serving path anyway. This method will return as soon as the
|
||||
* request is initiated, but StreamIterator methods will block until the next chunk of results is
|
||||
* received from the server.
|
||||
*
|
||||
* <p>See the
|
||||
* <a href="https://github.com/vitessio/vitess/blob/master/proto/vtgateservice.proto">proto</a>
|
||||
|
@ -154,9 +157,9 @@ public interface RpcClient extends Closeable {
|
|||
* Starts stream queries with multiple shards.
|
||||
*
|
||||
* <p>Note: Streaming queries are not asynchronous, because they typically shouldn't
|
||||
* be used from a latency-critical serving path anyway. This method will return as
|
||||
* soon as the request is initiated, but StreamIterator methods will block until the
|
||||
* next chunk of results is received from the server.
|
||||
* be used from a latency-critical serving path anyway. This method will return as soon as the
|
||||
* request is initiated, but StreamIterator methods will block until the next chunk of results is
|
||||
* received from the server.
|
||||
*
|
||||
* <p>See the
|
||||
* <a href="https://github.com/vitessio/vitess/blob/master/proto/vtgateservice.proto">proto</a>
|
||||
|
@ -169,9 +172,9 @@ public interface RpcClient extends Closeable {
|
|||
* Starts a list of stream queries with keyspace ids as bind variables.
|
||||
*
|
||||
* <p>Note: Streaming queries are not asynchronous, because they typically shouldn't
|
||||
* be used from a latency-critical serving path anyway. This method will return as
|
||||
* soon as the request is initiated, but StreamIterator methods will block until the
|
||||
* next chunk of results is received from the server.
|
||||
* be used from a latency-critical serving path anyway. This method will return as soon as the
|
||||
* request is initiated, but StreamIterator methods will block until the next chunk of results is
|
||||
* received from the server.
|
||||
*
|
||||
* <p>See the
|
||||
* <a href="https://github.com/vitessio/vitess/blob/master/proto/vtgateservice.proto">proto</a>
|
||||
|
@ -184,9 +187,9 @@ public interface RpcClient extends Closeable {
|
|||
* Starts stream query with a set of key ranges.
|
||||
*
|
||||
* <p>Note: Streaming queries are not asynchronous, because they typically shouldn't
|
||||
* be used from a latency-critical serving path anyway. This method will return as
|
||||
* soon as the request is initiated, but StreamIterator methods will block until the
|
||||
* next chunk of results is received from the server.
|
||||
* be used from a latency-critical serving path anyway. This method will return as soon as the
|
||||
* request is initiated, but StreamIterator methods will block until the next chunk of results is
|
||||
* received from the server.
|
||||
*
|
||||
* <p>See the
|
||||
* <a href="https://github.com/vitessio/vitess/blob/master/proto/vtgateservice.proto">proto</a>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -18,6 +18,7 @@ package io.vitess.client;
|
|||
|
||||
import com.google.common.util.concurrent.ForwardingListenableFuture.SimpleForwardingListenableFuture;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.sql.SQLException;
|
||||
|
@ -31,22 +32,22 @@ import java.util.concurrent.TimeoutException;
|
|||
*
|
||||
* <p>
|
||||
* When used as a {@link ListenableFuture}, the {@link SQLException} thrown by Vitess will be
|
||||
* wrapped in {@link ExecutionException}. You can retrieve it by calling
|
||||
* {@link ExecutionException#getCause()}.
|
||||
* wrapped in {@link ExecutionException}. You can retrieve it by calling {@link
|
||||
* ExecutionException#getCause()}.
|
||||
*
|
||||
* <p>
|
||||
* For users who want to get results synchronously, we provide {@link #checkedGet()} as a
|
||||
* convenience method. Unlike {@link #get()}, it throws only {@code SQLException}, so e.g.
|
||||
* {@code vtgateConn.execute(...).checkedGet()} behaves the same as our old synchronous API.
|
||||
* convenience method. Unlike {@link #get()}, it throws only {@code SQLException}, so e.g. {@code
|
||||
* vtgateConn.execute(...).checkedGet()} behaves the same as our old synchronous API.
|
||||
*
|
||||
* <p>
|
||||
* The additional methods are similar to the {@code CheckedFuture} interface (marked as beta), but
|
||||
* this class does not declare that it implements {@code CheckedFuture} because that interface is
|
||||
* not recommended for new projects. See the <a href=
|
||||
* "https://google.github.io/guava/releases/19.0/api/docs/com/google/common/util/concurrent/CheckedFuture.html">
|
||||
* not recommended for new projects. See the <a href= "https://google.github.io/guava/releases/19.0/api/docs/com/google/common/util/concurrent/CheckedFuture.html">
|
||||
* CheckedFuture docs</a> for more information.
|
||||
*/
|
||||
public class SQLFuture<V> extends SimpleForwardingListenableFuture<V> {
|
||||
|
||||
/**
|
||||
* Creates a SQLFuture that wraps the given ListenableFuture.
|
||||
*/
|
||||
|
@ -64,13 +65,11 @@ public class SQLFuture<V> extends SimpleForwardingListenableFuture<V> {
|
|||
public V checkedGet() throws SQLException {
|
||||
try {
|
||||
return get();
|
||||
} catch (InterruptedException e) {
|
||||
} catch (InterruptedException exc) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw mapException(e);
|
||||
} catch (CancellationException e) {
|
||||
throw mapException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw mapException(e);
|
||||
throw mapException(exc);
|
||||
} catch (CancellationException | ExecutionException exc) {
|
||||
throw mapException(exc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,33 +83,31 @@ public class SQLFuture<V> extends SimpleForwardingListenableFuture<V> {
|
|||
public V checkedGet(long timeout, TimeUnit unit) throws TimeoutException, SQLException {
|
||||
try {
|
||||
return get(timeout, unit);
|
||||
} catch (InterruptedException e) {
|
||||
} catch (InterruptedException exc) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw mapException(e);
|
||||
} catch (CancellationException e) {
|
||||
throw mapException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw mapException(e);
|
||||
throw mapException(exc);
|
||||
} catch (CancellationException | ExecutionException exc) {
|
||||
throw mapException(exc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates from an {@link InterruptedException}, {@link CancellationException} or
|
||||
* {@link ExecutionException} thrown by {@code get} to an exception of type {@code SQLException}
|
||||
* to be thrown by {@code checkedGet}.
|
||||
* Translates from an {@link InterruptedException}, {@link CancellationException} or {@link
|
||||
* ExecutionException} thrown by {@code get} to an exception of type {@code SQLException} to be
|
||||
* thrown by {@code checkedGet}.
|
||||
*
|
||||
* <p>
|
||||
* If {@code e} is an {@code InterruptedException}, the calling {@code checkedGet} method has
|
||||
* already restored the interrupt after catching the exception. If an implementation of
|
||||
* {@link #mapException(Exception)} wishes to swallow the interrupt, it can do so by calling
|
||||
* {@link Thread#interrupted()}.
|
||||
* If {@code exc} is an {@code InterruptedException}, the calling {@code checkedGet} method has
|
||||
* already restored the interrupt after catching the exception. If an implementation of {@link
|
||||
* #mapException(Exception)} wishes to swallow the interrupt, it can do so by calling {@link
|
||||
* Thread#interrupted()}.
|
||||
*/
|
||||
protected SQLException mapException(Exception e) {
|
||||
if (e instanceof ExecutionException) {
|
||||
protected SQLException mapException(Exception exc) {
|
||||
if (exc instanceof ExecutionException) {
|
||||
// To preserve both the stack trace and SQLException subclass type of the error
|
||||
// being wrapped, we use reflection to create a new instance of the particular
|
||||
// subclass of the original exception.
|
||||
Throwable cause = e.getCause();
|
||||
Throwable cause = exc.getCause();
|
||||
if (cause instanceof SQLException) {
|
||||
SQLException se = (SQLException) cause;
|
||||
try {
|
||||
|
@ -119,7 +116,7 @@ public class SQLFuture<V> extends SimpleForwardingListenableFuture<V> {
|
|||
.getClass()
|
||||
.getConstructor(String.class, String.class, int.class, Throwable.class);
|
||||
return (SQLException)
|
||||
constructor.newInstance(se.getMessage(), se.getSQLState(), se.getErrorCode(), e);
|
||||
constructor.newInstance(se.getMessage(), se.getSQLState(), se.getErrorCode(), exc);
|
||||
} catch (NoSuchMethodException
|
||||
| InstantiationException
|
||||
| IllegalAccessException
|
||||
|
@ -131,6 +128,6 @@ public class SQLFuture<V> extends SimpleForwardingListenableFuture<V> {
|
|||
}
|
||||
}
|
||||
|
||||
return new SQLException(e);
|
||||
return new SQLException(exc);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -20,29 +20,30 @@ import java.sql.SQLException;
|
|||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* An {@link java.util.Iterator Iterator}-like interface for accessing the results of a
|
||||
* Vitess streaming call.
|
||||
* An {@link java.util.Iterator Iterator}-like interface for accessing the results of a Vitess
|
||||
* streaming call.
|
||||
*
|
||||
* <p>It is similar to {@link java.util.Iterator}, but the hasNext() method is
|
||||
* understood to block until either a result is ready, an error occurs,
|
||||
* or there are no more results. Also, unlike Iterator, these methods
|
||||
* can throw SQLException.
|
||||
* understood to block until either a result is ready, an error occurs, or there are no more
|
||||
* results. Also, unlike Iterator, these methods can throw SQLException.
|
||||
*
|
||||
* <p>The {@link #close()} method should be called when done to free up threads that may be blocking
|
||||
* <p>The {@link #close()} method should be called when done to free up threads that may be
|
||||
* blocking
|
||||
* on the streaming connection.
|
||||
*
|
||||
* @param <E> the type of result returned by the iterator,
|
||||
* e.g. {@link io.vitess.proto.Query.QueryResult QueryResult}
|
||||
* @param <E> the type of result returned by the iterator, e.g. {@link
|
||||
* io.vitess.proto.Query.QueryResult QueryResult}
|
||||
*/
|
||||
public interface StreamIterator<E> extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* hasNext returns true if next() would return a value.
|
||||
*
|
||||
* <p>If no value is available, hasNext() will block until either:
|
||||
* <ul>
|
||||
* <li>A value becomes available (returns true),
|
||||
* <li>The stream completes successfully (returns false),
|
||||
* <li>An error occurs (throws exception).
|
||||
* <li>A value becomes available (returns true),
|
||||
* <li>The stream completes successfully (returns false),
|
||||
* <li>An error occurs (throws exception).
|
||||
* </ul>
|
||||
*/
|
||||
boolean hasNext() throws SQLException;
|
||||
|
@ -52,9 +53,9 @@ public interface StreamIterator<E> extends AutoCloseable {
|
|||
*
|
||||
* <p>If no value is available, next() will block until either:
|
||||
* <ul>
|
||||
* <li>A value becomes available (returns the value),
|
||||
* <li>The stream completes successfully (throws NoSuchElementException),
|
||||
* <li>An error occurs (throws other exception).
|
||||
* <li>A value becomes available (returns the value),
|
||||
* <li>The stream completes successfully (throws NoSuchElementException),
|
||||
* <li>An error occurs (throws other exception).
|
||||
* </ul>
|
||||
*/
|
||||
E next() throws NoSuchElementException, SQLException;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -16,14 +16,6 @@
|
|||
|
||||
package io.vitess.client;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.vitess.client.cursor.Cursor;
|
||||
import io.vitess.client.cursor.CursorWithError;
|
||||
import io.vitess.proto.Query;
|
||||
|
@ -35,6 +27,15 @@ import io.vitess.proto.Vtgate.BoundKeyspaceIdQuery;
|
|||
import io.vitess.proto.Vtgate.BoundShardQuery;
|
||||
import io.vitess.proto.Vtgate.SplitQueryResponse;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A synchronous wrapper around a VTGate connection.
|
||||
*
|
||||
|
@ -44,6 +45,7 @@ import io.vitess.proto.Vtgate.SplitQueryResponse;
|
|||
*/
|
||||
@Deprecated
|
||||
public class VTGateBlockingConn implements Closeable {
|
||||
|
||||
private final VTGateConn conn;
|
||||
|
||||
/**
|
||||
|
@ -76,15 +78,18 @@ public class VTGateBlockingConn implements Closeable {
|
|||
}
|
||||
|
||||
public Cursor executeShards(Context ctx, String query, String keyspace, Iterable<String> shards,
|
||||
Map<String, ?> bindVars, TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
return conn.executeShards(ctx, query, keyspace, shards, bindVars, tabletType, includedFields).checkedGet();
|
||||
Map<String, ?> bindVars, TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
return conn.executeShards(ctx, query, keyspace, shards, bindVars, tabletType, includedFields)
|
||||
.checkedGet();
|
||||
}
|
||||
|
||||
public Cursor executeKeyspaceIds(Context ctx, String query, String keyspace,
|
||||
Iterable<byte[]> keyspaceIds, Map<String, ?> bindVars, TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return conn.executeKeyspaceIds(ctx, query, keyspace, keyspaceIds, bindVars, tabletType, includedFields)
|
||||
return conn
|
||||
.executeKeyspaceIds(ctx, query, keyspace, keyspaceIds, bindVars, tabletType, includedFields)
|
||||
.checkedGet();
|
||||
}
|
||||
|
||||
|
@ -92,13 +97,15 @@ public class VTGateBlockingConn implements Closeable {
|
|||
Iterable<? extends KeyRange> keyRanges, Map<String, ?> bindVars, TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return conn.executeKeyRanges(ctx, query, keyspace, keyRanges, bindVars, tabletType, includedFields)
|
||||
return conn
|
||||
.executeKeyRanges(ctx, query, keyspace, keyRanges, bindVars, tabletType, includedFields)
|
||||
.checkedGet();
|
||||
}
|
||||
|
||||
public Cursor executeEntityIds(Context ctx, String query, String keyspace,
|
||||
String entityColumnName, Map<byte[], ?> entityKeyspaceIds, Map<String, ?> bindVars,
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return conn.executeEntityIds(ctx, query, keyspace, entityColumnName, entityKeyspaceIds,
|
||||
bindVars, tabletType, includedFields).checkedGet();
|
||||
}
|
||||
|
@ -111,36 +118,44 @@ public class VTGateBlockingConn implements Closeable {
|
|||
|
||||
public List<CursorWithError> executeBatch(Context ctx, ArrayList<String> queryList,
|
||||
@Nullable ArrayList<Map<String, ?>> bindVarsList, TabletType tabletType,
|
||||
boolean asTransaction, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
return conn.executeBatch(ctx, queryList, bindVarsList, tabletType, asTransaction, includedFields).checkedGet();
|
||||
boolean asTransaction, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return conn
|
||||
.executeBatch(ctx, queryList, bindVarsList, tabletType, asTransaction, includedFields)
|
||||
.checkedGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute multiple keyspace ID queries as a batch.
|
||||
*
|
||||
* @param asTransaction If true, automatically create a transaction (per shard) that encloses all
|
||||
* the batch queries.
|
||||
* @param asTransaction If true, automatically create a transaction (per shard) that encloses
|
||||
* all the batch queries.
|
||||
*/
|
||||
public List<Cursor> executeBatchShards(Context ctx, Iterable<? extends BoundShardQuery> queries,
|
||||
TabletType tabletType, boolean asTransaction, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
TabletType tabletType, boolean asTransaction,
|
||||
Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return conn.executeBatchShards(ctx, queries, tabletType, asTransaction, includedFields).checkedGet();
|
||||
return conn.executeBatchShards(ctx, queries, tabletType, asTransaction, includedFields)
|
||||
.checkedGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute multiple keyspace ID queries as a batch.
|
||||
*
|
||||
* @param asTransaction If true, automatically create a transaction (per shard) that encloses all
|
||||
* the batch queries.
|
||||
* @param asTransaction If true, automatically create a transaction (per shard) that encloses
|
||||
* all the batch queries.
|
||||
*/
|
||||
public List<Cursor> executeBatchKeyspaceIds(Context ctx,
|
||||
Iterable<? extends BoundKeyspaceIdQuery> queries, TabletType tabletType,
|
||||
boolean asTransaction, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
return conn.executeBatchKeyspaceIds(ctx, queries, tabletType, asTransaction, includedFields).checkedGet();
|
||||
boolean asTransaction, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return conn.executeBatchKeyspaceIds(ctx, queries, tabletType, asTransaction, includedFields)
|
||||
.checkedGet();
|
||||
}
|
||||
|
||||
public Cursor streamExecute(Context ctx, String query, Map<String, ?> bindVars,
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return conn.streamExecute(ctx, query, bindVars, tabletType, includedFields);
|
||||
}
|
||||
|
||||
|
@ -148,21 +163,24 @@ public class VTGateBlockingConn implements Closeable {
|
|||
Iterable<String> shards, Map<String, ?> bindVars, TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return conn.streamExecuteShards(ctx, query, keyspace, shards, bindVars, tabletType, includedFields);
|
||||
return conn
|
||||
.streamExecuteShards(ctx, query, keyspace, shards, bindVars, tabletType, includedFields);
|
||||
}
|
||||
|
||||
public Cursor streamExecuteKeyspaceIds(Context ctx, String query, String keyspace,
|
||||
Iterable<byte[]> keyspaceIds, Map<String, ?> bindVars, TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return conn.streamExecuteKeyspaceIds(ctx, query, keyspace, keyspaceIds, bindVars, tabletType, includedFields);
|
||||
return conn.streamExecuteKeyspaceIds(ctx, query, keyspace, keyspaceIds, bindVars, tabletType,
|
||||
includedFields);
|
||||
}
|
||||
|
||||
public Cursor streamExecuteKeyRanges(Context ctx, String query, String keyspace,
|
||||
Iterable<? extends KeyRange> keyRanges, Map<String, ?> bindVars, TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return conn.streamExecuteKeyRanges(ctx, query, keyspace, keyRanges, bindVars, tabletType, includedFields);
|
||||
return conn.streamExecuteKeyRanges(ctx, query, keyspace, keyRanges, bindVars, tabletType,
|
||||
includedFields);
|
||||
}
|
||||
|
||||
public VTGateBlockingTx begin(Context ctx) throws SQLException {
|
||||
|
|
|
@ -16,135 +16,162 @@
|
|||
|
||||
package io.vitess.client;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.vitess.client.cursor.Cursor;
|
||||
import io.vitess.client.cursor.CursorWithError;
|
||||
import io.vitess.proto.Query.SplitQueryRequest.Algorithm;
|
||||
import io.vitess.proto.Vtgate.SplitQueryResponse;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An asynchronous VTGate connection.
|
||||
* <p>
|
||||
* <p>All the information regarding this connection is maintained by {@code Session},
|
||||
* only one operation can be in flight at a time on a given instance.
|
||||
* The methods are {@code synchronized} only because the session cookie is updated asynchronously
|
||||
* when the RPC response comes back.</p>
|
||||
* only one operation can be in flight at a time on a given instance. The methods are {@code
|
||||
* synchronized} only because the session cookie is updated asynchronously when the RPC response
|
||||
* comes back.</p>
|
||||
* <p>
|
||||
* <p>After calling any method that returns a {@link SQLFuture}, you must wait for that future to
|
||||
* complete before calling any other methods on that {@code VTGateConnection} instance.
|
||||
* An {@link IllegalStateException} will be thrown if this constraint is violated.</p>
|
||||
* complete before calling any other methods on that {@code VTGateConnection} instance. An {@link
|
||||
* IllegalStateException} will be thrown if this constraint is violated.</p>
|
||||
* <p>
|
||||
* <p>All non-streaming calls on {@code VTGateConnection} are asynchronous. Use {@link VTGateBlockingConnection} if
|
||||
* <p>All non-streaming calls on {@code VTGateConnection} are asynchronous. Use {@link
|
||||
* VTGateBlockingConnection} if
|
||||
* you want synchronous calls.</p>
|
||||
*/
|
||||
public final class VTGateBlockingConnection implements Closeable {
|
||||
private final VTGateConnection vtGateConnection;
|
||||
|
||||
/**
|
||||
* Creates a VTGate connection with no specific parameters.
|
||||
* <p>
|
||||
* <p>In this mode, VTGate will use VSchema to resolve the keyspace for any unprefixed
|
||||
* table names. Note that this only works if the table name is unique across all keyspaces.</p>
|
||||
*
|
||||
* @param client RPC connection
|
||||
*/
|
||||
public VTGateBlockingConnection(RpcClient client) {
|
||||
vtGateConnection = new VTGateConnection(client);
|
||||
}
|
||||
private final VTGateConnection vtGateConnection;
|
||||
|
||||
/**
|
||||
* This method calls the VTGate to execute the query.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param query Sql Query to be executed.
|
||||
* @param bindVars Parameters to bind with sql.
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return Cursor
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public Cursor execute(Context ctx, String query, @Nullable Map<String, ?> bindVars, final VTSession vtSession) throws SQLException {
|
||||
return vtGateConnection.execute(ctx, query, bindVars, vtSession).checkedGet();
|
||||
}
|
||||
/**
|
||||
* Creates a VTGate connection with no specific parameters.
|
||||
* <p>
|
||||
* <p>In this mode, VTGate will use VSchema to resolve the keyspace for any unprefixed
|
||||
* table names. Note that this only works if the table name is unique across all keyspaces.</p>
|
||||
*
|
||||
* @param client RPC connection
|
||||
*/
|
||||
public VTGateBlockingConnection(RpcClient client) {
|
||||
vtGateConnection = new VTGateConnection(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calls the VTGate to execute list of queries as a batch.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param queryList List of sql queries to be executed.
|
||||
* @param bindVarsList <p>For each sql query it will provide a list of parameters to bind with.
|
||||
* If provided, should match the number of sql queries.</p>
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return List of Cursors
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public List<CursorWithError> executeBatch(Context ctx, List<String> queryList, @Nullable List<Map<String, ?>> bindVarsList, final VTSession vtSession) throws SQLException {
|
||||
return vtGateConnection.executeBatch(ctx, queryList, bindVarsList, vtSession).checkedGet();
|
||||
}
|
||||
/**
|
||||
* This method calls the VTGate to execute the query.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param query Sql Query to be executed.
|
||||
* @param bindVars Parameters to bind with sql.
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return Cursor
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public Cursor execute(Context ctx,
|
||||
String query,
|
||||
@Nullable Map<String, ?> bindVars,
|
||||
final VTSession vtSession) throws SQLException {
|
||||
return vtGateConnection.execute(ctx, query, bindVars, vtSession).checkedGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calls the VTGate to execute list of queries as a batch.
|
||||
* <p>
|
||||
* <p>If asTransaction is set to <code>true</code> then query execution will not change the session cookie.
|
||||
* Otherwise, query execution will become part of the session.</p>
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param queryList List of sql queries to be executed.
|
||||
* @param bindVarsList <p>For each sql query it will provide a list of parameters to bind with.
|
||||
* If provided, should match the number of sql queries.</p>
|
||||
* @param asTransaction To execute query without impacting session cookie.
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return List of Cursors
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public List<CursorWithError> executeBatch(Context ctx, List<String> queryList, @Nullable List<Map<String, ?>> bindVarsList, boolean asTransaction, final VTSession vtSession) throws SQLException {
|
||||
return vtGateConnection.executeBatch(ctx, queryList, bindVarsList, asTransaction, vtSession).checkedGet();
|
||||
}
|
||||
/**
|
||||
* This method calls the VTGate to execute list of queries as a batch.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param queryList List of sql queries to be executed.
|
||||
* @param bindVarsList <p>For each sql query it will provide a list of parameters to bind with. If
|
||||
* provided, should match the number of sql queries.</p>
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return List of Cursors
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public List<CursorWithError> executeBatch(Context ctx,
|
||||
List<String> queryList,
|
||||
@Nullable List<Map<String, ?>> bindVarsList,
|
||||
final VTSession vtSession) throws SQLException {
|
||||
return vtGateConnection.executeBatch(ctx, queryList, bindVarsList, vtSession).checkedGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be used execute select query to return response as a stream.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param query Sql Query to be executed.
|
||||
* @param bindVars Parameters to bind with sql.
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return Cursor
|
||||
* @throws SQLException Returns SQLException if there is any failure on VTGate.
|
||||
*/
|
||||
public Cursor streamExecute(Context ctx, String query, @Nullable Map<String, ?> bindVars, VTSession vtSession) throws SQLException {
|
||||
return vtGateConnection.streamExecute(ctx, query, bindVars, vtSession);
|
||||
}
|
||||
/**
|
||||
* This method calls the VTGate to execute list of queries as a batch.
|
||||
* <p>
|
||||
* <p>If asTransaction is set to <code>true</code> then query execution will not change the
|
||||
* session cookie.
|
||||
* Otherwise, query execution will become part of the session.</p>
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param queryList List of sql queries to be executed.
|
||||
* @param bindVarsList <p>For each sql query it will provide a list of parameters to bind with. If
|
||||
* provided, should match the number of sql queries.</p>
|
||||
* @param asTransaction To execute query without impacting session cookie.
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return List of Cursors
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public List<CursorWithError> executeBatch(Context ctx,
|
||||
List<String> queryList,
|
||||
@Nullable List<Map<String, ?>> bindVarsList,
|
||||
boolean asTransaction,
|
||||
final VTSession vtSession) throws SQLException {
|
||||
return vtGateConnection.executeBatch(ctx, queryList, bindVarsList, asTransaction, vtSession)
|
||||
.checkedGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method splits the query into small parts based on the splitColumn and Algorithm type provided.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param keyspace Keyspace to execute the query on.
|
||||
* @param query Sql Query to be executed.
|
||||
* @param bindVars Parameters to bind with sql.
|
||||
* @param splitColumns Column to be used to split the data.
|
||||
* @param splitCount Number of Partitions
|
||||
* @param numRowsPerQueryPart Limit the number of records per query part.
|
||||
* @param algorithm EQUAL_SPLITS or FULL_SCAN
|
||||
* @return Query Parts
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public List<SplitQueryResponse.Part> splitQuery(Context ctx, String keyspace, String query, @Nullable Map<String, ?> bindVars, Iterable<String> splitColumns,
|
||||
int splitCount, int numRowsPerQueryPart, Algorithm algorithm) throws SQLException {
|
||||
return vtGateConnection.splitQuery(ctx, keyspace, query, bindVars, splitColumns, splitCount, numRowsPerQueryPart, algorithm).checkedGet();
|
||||
}
|
||||
/**
|
||||
* This method should be used execute select query to return response as a stream.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param query Sql Query to be executed.
|
||||
* @param bindVars Parameters to bind with sql.
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return Cursor
|
||||
* @throws SQLException Returns SQLException if there is any failure on VTGate.
|
||||
*/
|
||||
public Cursor streamExecute(Context ctx,
|
||||
String query,
|
||||
@Nullable Map<String, ?> bindVars,
|
||||
VTSession vtSession) throws SQLException {
|
||||
return vtGateConnection.streamExecute(ctx, query, bindVars, vtSession);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
vtGateConnection.close();
|
||||
}
|
||||
/**
|
||||
* This method splits the query into small parts based on the splitColumn and Algorithm type
|
||||
* provided.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param keyspace Keyspace to execute the query on.
|
||||
* @param query Sql Query to be executed.
|
||||
* @param bindVars Parameters to bind with sql.
|
||||
* @param splitColumns Column to be used to split the data.
|
||||
* @param splitCount Number of Partitions
|
||||
* @param numRowsPerQueryPart Limit the number of records per query part.
|
||||
* @param algorithm EQUAL_SPLITS or FULL_SCAN
|
||||
* @return Query Parts
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public List<SplitQueryResponse.Part> splitQuery(Context ctx,
|
||||
String keyspace,
|
||||
String query,
|
||||
@Nullable Map<String, ?> bindVars,
|
||||
Iterable<String> splitColumns,
|
||||
int splitCount,
|
||||
int numRowsPerQueryPart,
|
||||
Algorithm algorithm) throws SQLException {
|
||||
return vtGateConnection
|
||||
.splitQuery(ctx, keyspace, query, bindVars, splitColumns, splitCount, numRowsPerQueryPart,
|
||||
algorithm).checkedGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
vtGateConnection.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -16,11 +16,6 @@
|
|||
|
||||
package io.vitess.client;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.vitess.client.cursor.Cursor;
|
||||
import io.vitess.client.cursor.CursorWithError;
|
||||
import io.vitess.proto.Query;
|
||||
|
@ -29,6 +24,12 @@ import io.vitess.proto.Topodata.TabletType;
|
|||
import io.vitess.proto.Vtgate.BoundKeyspaceIdQuery;
|
||||
import io.vitess.proto.Vtgate.BoundShardQuery;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A synchronous wrapper around a VTGate transaction.
|
||||
*
|
||||
|
@ -37,6 +38,7 @@ import io.vitess.proto.Vtgate.BoundShardQuery;
|
|||
*/
|
||||
@Deprecated
|
||||
public class VTGateBlockingTx {
|
||||
|
||||
private final VTGateTx tx;
|
||||
|
||||
/**
|
||||
|
@ -61,7 +63,8 @@ public class VTGateBlockingTx {
|
|||
TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return tx.executeShards(ctx, query, keyspace, shards, bindVars, tabletType, includedFields).checkedGet();
|
||||
return tx.executeShards(ctx, query, keyspace, shards, bindVars, tabletType, includedFields)
|
||||
.checkedGet();
|
||||
}
|
||||
|
||||
public Cursor executeKeyspaceIds(
|
||||
|
@ -73,7 +76,8 @@ public class VTGateBlockingTx {
|
|||
TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return tx.executeKeyspaceIds(ctx, query, keyspace, keyspaceIds, bindVars, tabletType, includedFields)
|
||||
return tx
|
||||
.executeKeyspaceIds(ctx, query, keyspace, keyspaceIds, bindVars, tabletType, includedFields)
|
||||
.checkedGet();
|
||||
}
|
||||
|
||||
|
@ -86,7 +90,9 @@ public class VTGateBlockingTx {
|
|||
TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return tx.executeKeyRanges(ctx, query, keyspace, keyRanges, bindVars, tabletType, includedFields).checkedGet();
|
||||
return tx
|
||||
.executeKeyRanges(ctx, query, keyspace, keyRanges, bindVars, tabletType, includedFields)
|
||||
.checkedGet();
|
||||
}
|
||||
|
||||
public Cursor executeEntityIds(
|
||||
|
@ -100,7 +106,8 @@ public class VTGateBlockingTx {
|
|||
Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
return tx.executeEntityIds(
|
||||
ctx, query, keyspace, entityColumnName, entityKeyspaceIds, bindVars, tabletType, includedFields)
|
||||
ctx, query, keyspace, entityColumnName, entityKeyspaceIds, bindVars, tabletType,
|
||||
includedFields)
|
||||
.checkedGet();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -24,14 +24,6 @@ import com.google.common.collect.Iterables;
|
|||
import com.google.common.util.concurrent.AsyncFunction;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLDataException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.vitess.client.cursor.Cursor;
|
||||
import io.vitess.client.cursor.CursorWithError;
|
||||
|
@ -70,12 +62,21 @@ import io.vitess.proto.Vtgate.StreamExecuteKeyspaceIdsRequest;
|
|||
import io.vitess.proto.Vtgate.StreamExecuteRequest;
|
||||
import io.vitess.proto.Vtgate.StreamExecuteShardsRequest;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLDataException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An asynchronous VTGate connection.
|
||||
*
|
||||
* <p>
|
||||
* See the <a href=
|
||||
* "https://github.com/vitessio/vitess/blob/master/java/example/src/main/java/io/vitess/example/VitessClientExample.java">VitessClientExample</a>
|
||||
* See the <a href= "https://github.com/vitessio/vitess/blob/master/java/example/src/main/java/io/vitess/example/VitessClientExample.java">VitessClientExample</a>
|
||||
* for a usage example.
|
||||
*
|
||||
* <p>
|
||||
|
@ -84,6 +85,7 @@ import io.vitess.proto.Vtgate.StreamExecuteShardsRequest;
|
|||
*/
|
||||
@Deprecated
|
||||
public final class VTGateConn implements Closeable {
|
||||
|
||||
private final RpcClient client;
|
||||
private final String keyspace;
|
||||
|
||||
|
@ -107,8 +109,8 @@ public final class VTGateConn implements Closeable {
|
|||
* The given {@code keyspace} will be used as the connection-wide default for {@code execute()}
|
||||
* and {@code streamExecute()} calls, since those do not specify the keyspace for each call. Like
|
||||
* the connection-wide default database of a MySQL connection, individual queries can still refer
|
||||
* to other keyspaces by prefixing table names. For example:
|
||||
* {@code "SELECT ... FROM keyspace.table ..."}
|
||||
* to other keyspaces by prefixing table names. For example: {@code "SELECT ... FROM
|
||||
* keyspace.table ..."}
|
||||
*/
|
||||
public VTGateConn(RpcClient client, String keyspace) {
|
||||
this.client = checkNotNull(client);
|
||||
|
@ -116,13 +118,14 @@ public final class VTGateConn implements Closeable {
|
|||
}
|
||||
|
||||
public SQLFuture<Cursor> execute(Context ctx, String query, @Nullable Map<String, ?> bindVars,
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
ExecuteRequest.Builder requestBuilder = ExecuteRequest.newBuilder()
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
.setKeyspaceShard(keyspace)
|
||||
.setTabletType(checkNotNull(tabletType))
|
||||
.setOptions(Query.ExecuteOptions.newBuilder()
|
||||
.setIncludedFields(includedFields));
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
.setKeyspaceShard(keyspace)
|
||||
.setTabletType(checkNotNull(tabletType))
|
||||
.setOptions(Query.ExecuteOptions.newBuilder()
|
||||
.setIncludedFields(includedFields));
|
||||
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
|
@ -205,7 +208,8 @@ public final class VTGateConn implements Closeable {
|
|||
|
||||
public SQLFuture<Cursor> executeKeyRanges(Context ctx, String query, String keyspace,
|
||||
Iterable<? extends KeyRange> keyRanges, @Nullable Map<String, ?> bindVars,
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
ExecuteKeyRangesRequest.Builder requestBuilder = ExecuteKeyRangesRequest.newBuilder()
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
.setKeyspace(checkNotNull(keyspace))
|
||||
|
@ -234,7 +238,8 @@ public final class VTGateConn implements Closeable {
|
|||
|
||||
public SQLFuture<Cursor> executeEntityIds(Context ctx, String query, String keyspace,
|
||||
String entityColumnName, Map<byte[], ?> entityKeyspaceIds, @Nullable Map<String, ?> bindVars,
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
ExecuteEntityIdsRequest.Builder requestBuilder = ExecuteEntityIdsRequest.newBuilder()
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
.setKeyspace(checkNotNull(keyspace))
|
||||
|
@ -263,39 +268,40 @@ public final class VTGateConn implements Closeable {
|
|||
directExecutor()));
|
||||
}
|
||||
|
||||
public SQLFuture<List<CursorWithError>> executeBatch(Context ctx, List<String> queryList,
|
||||
@Nullable List<Map<String, ?>> bindVarsList, TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
return executeBatch(ctx, queryList, bindVarsList, tabletType, false, includedFields);
|
||||
public SQLFuture<List<CursorWithError>> executeBatch(Context ctx, List<String> queryList,
|
||||
@Nullable List<Map<String, ?>> bindVarsList, TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
return executeBatch(ctx, queryList, bindVarsList, tabletType, false, includedFields);
|
||||
}
|
||||
|
||||
public SQLFuture<List<CursorWithError>> executeBatch(Context ctx, List<String> queryList,
|
||||
@Nullable List<Map<String, ?>> bindVarsList, TabletType tabletType,
|
||||
boolean asTransaction, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
List<Query.BoundQuery> queries = new ArrayList<>();
|
||||
|
||||
if (null != bindVarsList && bindVarsList.size() != queryList.size()) {
|
||||
throw new SQLDataException(
|
||||
"Size of SQL Query list does not match the bind variables list");
|
||||
}
|
||||
|
||||
public SQLFuture<List<CursorWithError>> executeBatch(Context ctx, List<String> queryList,
|
||||
@Nullable List<Map<String, ?>> bindVarsList, TabletType tabletType,
|
||||
boolean asTransaction, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
List<Query.BoundQuery> queries = new ArrayList<>();
|
||||
for (int i = 0; i < queryList.size(); ++i) {
|
||||
queries.add(i, Proto.bindQuery(checkNotNull(queryList.get(i)),
|
||||
bindVarsList == null ? null : bindVarsList.get(i)));
|
||||
}
|
||||
|
||||
if (null != bindVarsList && bindVarsList.size() != queryList.size()) {
|
||||
throw new SQLDataException(
|
||||
"Size of SQL Query list does not match the bind variables list");
|
||||
}
|
||||
Vtgate.ExecuteBatchRequest.Builder requestBuilder =
|
||||
Vtgate.ExecuteBatchRequest.newBuilder()
|
||||
.addAllQueries(checkNotNull(queries))
|
||||
.setKeyspaceShard(keyspace)
|
||||
.setTabletType(checkNotNull(tabletType))
|
||||
.setAsTransaction(asTransaction)
|
||||
.setOptions(Query.ExecuteOptions.newBuilder()
|
||||
.setIncludedFields(includedFields));
|
||||
|
||||
for (int i = 0; i < queryList.size(); ++i) {
|
||||
queries.add(i, Proto.bindQuery(checkNotNull(queryList.get(i)),
|
||||
bindVarsList == null ? null : bindVarsList.get(i)));
|
||||
}
|
||||
|
||||
Vtgate.ExecuteBatchRequest.Builder requestBuilder =
|
||||
Vtgate.ExecuteBatchRequest.newBuilder()
|
||||
.addAllQueries(checkNotNull(queries))
|
||||
.setKeyspaceShard(keyspace)
|
||||
.setTabletType(checkNotNull(tabletType))
|
||||
.setAsTransaction(asTransaction)
|
||||
.setOptions(Query.ExecuteOptions.newBuilder()
|
||||
.setIncludedFields(includedFields));
|
||||
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
|
||||
return new SQLFuture<>(
|
||||
transformAsync(
|
||||
|
@ -310,13 +316,13 @@ public final class VTGateConn implements Closeable {
|
|||
}
|
||||
},
|
||||
directExecutor()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Execute multiple keyspace ID queries as a batch.
|
||||
*
|
||||
* @param asTransaction If true, automatically create a transaction (per shard) that encloses all
|
||||
* the batch queries.
|
||||
* @param asTransaction If true, automatically create a transaction (per shard) that encloses
|
||||
* all the batch queries.
|
||||
*/
|
||||
public SQLFuture<List<Cursor>> executeBatchShards(Context ctx,
|
||||
Iterable<? extends BoundShardQuery> queries, TabletType tabletType, boolean asTransaction,
|
||||
|
@ -352,12 +358,13 @@ public final class VTGateConn implements Closeable {
|
|||
/**
|
||||
* Execute multiple keyspace ID queries as a batch.
|
||||
*
|
||||
* @param asTransaction If true, automatically create a transaction (per shard) that encloses all
|
||||
* the batch queries.
|
||||
* @param asTransaction If true, automatically create a transaction (per shard) that encloses
|
||||
* all the batch queries.
|
||||
*/
|
||||
public SQLFuture<List<Cursor>> executeBatchKeyspaceIds(Context ctx,
|
||||
Iterable<? extends BoundKeyspaceIdQuery> queries, TabletType tabletType,
|
||||
boolean asTransaction, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
boolean asTransaction, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
ExecuteBatchKeyspaceIdsRequest.Builder requestBuilder =
|
||||
ExecuteBatchKeyspaceIdsRequest.newBuilder()
|
||||
.addAllQueries(checkNotNull(queries))
|
||||
|
@ -386,7 +393,8 @@ public final class VTGateConn implements Closeable {
|
|||
}
|
||||
|
||||
public Cursor streamExecute(Context ctx, String query, @Nullable Map<String, ?> bindVars,
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
StreamExecuteRequest.Builder requestBuilder =
|
||||
StreamExecuteRequest.newBuilder()
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
|
@ -444,7 +452,8 @@ public final class VTGateConn implements Closeable {
|
|||
|
||||
public Cursor streamExecuteKeyRanges(Context ctx, String query, String keyspace,
|
||||
Iterable<? extends KeyRange> keyRanges, @Nullable Map<String, ?> bindVars,
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
StreamExecuteKeyRangesRequest.Builder requestBuilder = StreamExecuteKeyRangesRequest
|
||||
.newBuilder()
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -20,16 +20,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
import static com.google.common.util.concurrent.Futures.transformAsync;
|
||||
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLDataException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.util.concurrent.AsyncFunction;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
@ -47,216 +37,234 @@ import io.vitess.proto.Vtgate.SplitQueryRequest;
|
|||
import io.vitess.proto.Vtgate.SplitQueryResponse;
|
||||
import io.vitess.proto.Vtgate.StreamExecuteRequest;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLDataException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An asynchronous VTGate connection.
|
||||
* <p>
|
||||
* <p>All the information regarding this connection is maintained by {@code Session},
|
||||
* only one operation can be in flight at a time on a given instance.
|
||||
* The methods are {@code synchronized} only because the session cookie is updated asynchronously
|
||||
* when the RPC response comes back.</p>
|
||||
* only one operation can be in flight at a time on a given instance. The methods are {@code
|
||||
* synchronized} only because the session cookie is updated asynchronously when the RPC response
|
||||
* comes back.</p>
|
||||
* <p>
|
||||
* <p>After calling any method that returns a {@link SQLFuture}, you must wait for that future to
|
||||
* complete before calling any other methods on that {@code VTGateConnection} instance.
|
||||
* An {@link IllegalStateException} will be thrown if this constraint is violated.</p>
|
||||
* complete before calling any other methods on that {@code VTGateConnection} instance. An {@link
|
||||
* IllegalStateException} will be thrown if this constraint is violated.</p>
|
||||
* <p>
|
||||
* <p>All non-streaming calls on {@code VTGateConnection} are asynchronous. Use {@link VTGateBlockingConnection} if
|
||||
* you want synchronous calls.</p>
|
||||
* <p>All non-streaming calls on {@code VTGateConnection} are asynchronous. Use {@link
|
||||
* VTGateBlockingConnection} if you want synchronous calls.</p>
|
||||
*/
|
||||
public class VTGateConnection implements Closeable {
|
||||
private final RpcClient client;
|
||||
|
||||
/**
|
||||
* Creates a VTGate connection with no specific parameters.
|
||||
* <p>
|
||||
* <p>In this mode, VTGate will use VSchema to resolve the keyspace for any unprefixed
|
||||
* table names. Note that this only works if the table name is unique across all keyspaces.</p>
|
||||
*
|
||||
* @param client RPC connection
|
||||
*/
|
||||
public VTGateConnection(RpcClient client) {
|
||||
this.client = checkNotNull(client);
|
||||
private final RpcClient client;
|
||||
|
||||
/**
|
||||
* Creates a VTGate connection with no specific parameters.
|
||||
* <p>
|
||||
* <p>In this mode, VTGate will use VSchema to resolve the keyspace for any unprefixed
|
||||
* table names. Note that this only works if the table name is unique across all keyspaces.</p>
|
||||
*
|
||||
* @param client RPC connection
|
||||
*/
|
||||
public VTGateConnection(RpcClient client) {
|
||||
this.client = checkNotNull(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calls the VTGate to execute the query.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param query Sql Query to be executed.
|
||||
* @param bindVars Parameters to bind with sql.
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return SQL Future Cursor
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public SQLFuture<Cursor> execute(Context ctx, String query, @Nullable Map<String, ?> bindVars,
|
||||
final VTSession vtSession) throws SQLException {
|
||||
synchronized (this) {
|
||||
vtSession.checkCallIsAllowed("execute");
|
||||
ExecuteRequest.Builder requestBuilder = ExecuteRequest.newBuilder()
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
.setSession(vtSession.getSession());
|
||||
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
|
||||
SQLFuture<Cursor> call = new SQLFuture<>(
|
||||
transformAsync(client.execute(ctx, requestBuilder.build()),
|
||||
new AsyncFunction<ExecuteResponse, Cursor>() {
|
||||
@Override
|
||||
public ListenableFuture<Cursor> apply(ExecuteResponse response) throws Exception {
|
||||
vtSession.setSession(response.getSession());
|
||||
Proto.checkError(response.getError());
|
||||
return Futures.<Cursor>immediateFuture(new SimpleCursor(response.getResult()));
|
||||
}
|
||||
}, directExecutor()));
|
||||
vtSession.setLastCall(call);
|
||||
return call;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calls the VTGate to execute list of queries as a batch.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param queryList List of sql queries to be executed.
|
||||
* @param bindVarsList <p>For each sql query it will provide a list of parameters to bind with. If
|
||||
* provided, should match the number of sql queries.</p>
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return SQL Future with List of Cursors
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public SQLFuture<List<CursorWithError>> executeBatch(Context ctx, List<String> queryList,
|
||||
@Nullable List<Map<String, ?>> bindVarsList, final VTSession vtSession) throws SQLException {
|
||||
return executeBatch(ctx, queryList, bindVarsList, false, vtSession);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calls the VTGate to execute list of queries as a batch.
|
||||
* <p>
|
||||
* <p>If asTransaction is set to <code>true</code> then query execution will not change the
|
||||
* session cookie. Otherwise, query execution will become part of the session.</p>
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param queryList List of sql queries to be executed.
|
||||
* @param bindVarsList <p>For each sql query it will provide a list of parameters to bind with. If
|
||||
* provided, should match the number of sql queries.</p>
|
||||
* @param asTransaction To execute query without impacting session cookie.
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return SQL Future with List of Cursors
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public SQLFuture<List<CursorWithError>> executeBatch(Context ctx, List<String> queryList,
|
||||
@Nullable List<Map<String, ?>> bindVarsList, boolean asTransaction, final VTSession vtSession)
|
||||
throws SQLException {
|
||||
synchronized (this) {
|
||||
vtSession.checkCallIsAllowed("executeBatch");
|
||||
List<Query.BoundQuery> queries = new ArrayList<>();
|
||||
|
||||
if (null != bindVarsList && bindVarsList.size() != queryList.size()) {
|
||||
throw new SQLDataException(
|
||||
"Size of SQL Query list does not match the bind variables list");
|
||||
}
|
||||
|
||||
for (int i = 0; i < queryList.size(); ++i) {
|
||||
queries.add(i, Proto.bindQuery(checkNotNull(queryList.get(i)),
|
||||
bindVarsList == null ? null : bindVarsList.get(i)));
|
||||
}
|
||||
|
||||
Vtgate.ExecuteBatchRequest.Builder requestBuilder =
|
||||
Vtgate.ExecuteBatchRequest.newBuilder()
|
||||
.addAllQueries(checkNotNull(queries))
|
||||
.setSession(vtSession.getSession())
|
||||
.setAsTransaction(asTransaction);
|
||||
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
|
||||
SQLFuture<List<CursorWithError>> call = new SQLFuture<>(
|
||||
transformAsync(client.executeBatch(ctx, requestBuilder.build()),
|
||||
new AsyncFunction<Vtgate.ExecuteBatchResponse, List<CursorWithError>>() {
|
||||
@Override
|
||||
public ListenableFuture<List<CursorWithError>> apply(
|
||||
Vtgate.ExecuteBatchResponse response) throws Exception {
|
||||
vtSession.setSession(response.getSession());
|
||||
Proto.checkError(response.getError());
|
||||
return Futures.immediateFuture(
|
||||
Proto.fromQueryResponsesToCursorList(response.getResultsList()));
|
||||
}
|
||||
}, directExecutor()));
|
||||
vtSession.setLastCall(call);
|
||||
return call;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param query Sql Query to be executed.
|
||||
* @param bindVars Parameters to bind with sql.
|
||||
* @param vtSession Session to be used with the call.
|
||||
*/
|
||||
public Cursor streamExecute(Context ctx, String query, @Nullable Map<String, ?> bindVars,
|
||||
VTSession vtSession) throws SQLException {
|
||||
StreamExecuteRequest.Builder requestBuilder =
|
||||
StreamExecuteRequest.newBuilder()
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
.setSession(vtSession.getSession());
|
||||
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calls the VTGate to execute the query.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param query Sql Query to be executed.
|
||||
* @param bindVars Parameters to bind with sql.
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return SQL Future Cursor
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public SQLFuture<Cursor> execute(Context ctx, String query, @Nullable Map<String, ?> bindVars, final VTSession vtSession) throws SQLException {
|
||||
synchronized (this) {
|
||||
vtSession.checkCallIsAllowed("execute");
|
||||
ExecuteRequest.Builder requestBuilder = ExecuteRequest.newBuilder()
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
.setSession(vtSession.getSession());
|
||||
return new StreamCursor(client.streamExecute(ctx, requestBuilder.build()));
|
||||
}
|
||||
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
/**
|
||||
* This method splits the query into small parts based on the splitColumn and Algorithm type
|
||||
* provided.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param keyspace Keyspace to execute the query on.
|
||||
* @param query Sql Query to be executed.
|
||||
* @param bindVars Parameters to bind with sql.
|
||||
* @param splitColumns Column to be used to split the data.
|
||||
* @param splitCount Number of Partitions
|
||||
* @param numRowsPerQueryPart Limit the number of records per query part.
|
||||
* @param algorithm EQUAL_SPLITS or FULL_SCAN
|
||||
* @return SQL Future with Query Parts
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public SQLFuture<List<SplitQueryResponse.Part>> splitQuery(Context ctx, String keyspace,
|
||||
String query, @Nullable Map<String, ?> bindVars, Iterable<String> splitColumns,
|
||||
int splitCount, int numRowsPerQueryPart, Algorithm algorithm) throws SQLException {
|
||||
SplitQueryRequest.Builder requestBuilder =
|
||||
SplitQueryRequest.newBuilder()
|
||||
.setKeyspace(checkNotNull(keyspace))
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
.addAllSplitColumn(splitColumns)
|
||||
.setSplitCount(splitCount)
|
||||
.setNumRowsPerQueryPart(numRowsPerQueryPart)
|
||||
.setAlgorithm(algorithm);
|
||||
|
||||
SQLFuture<Cursor> call = new SQLFuture<>(
|
||||
transformAsync(client.execute(ctx, requestBuilder.build()),
|
||||
new AsyncFunction<ExecuteResponse, Cursor>() {
|
||||
@Override
|
||||
public ListenableFuture<Cursor> apply(ExecuteResponse response) throws Exception {
|
||||
vtSession.setSession(response.getSession());
|
||||
Proto.checkError(response.getError());
|
||||
return Futures.<Cursor>immediateFuture(new SimpleCursor(response.getResult()));
|
||||
}
|
||||
}, directExecutor()));
|
||||
vtSession.setLastCall(call);
|
||||
return call;
|
||||
}
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calls the VTGate to execute list of queries as a batch.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param queryList List of sql queries to be executed.
|
||||
* @param bindVarsList <p>For each sql query it will provide a list of parameters to bind with.
|
||||
* If provided, should match the number of sql queries.</p>
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return SQL Future with List of Cursors
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public SQLFuture<List<CursorWithError>> executeBatch(Context ctx, List<String> queryList, @Nullable List<Map<String, ?>> bindVarsList, final VTSession vtSession) throws SQLException {
|
||||
return executeBatch(ctx, queryList, bindVarsList, false, vtSession);
|
||||
}
|
||||
return new SQLFuture<>(
|
||||
transformAsync(client.splitQuery(ctx, requestBuilder.build()),
|
||||
new AsyncFunction<SplitQueryResponse, List<SplitQueryResponse.Part>>() {
|
||||
@Override
|
||||
public ListenableFuture<List<SplitQueryResponse.Part>> apply(
|
||||
SplitQueryResponse response) throws Exception {
|
||||
return Futures.immediateFuture(response.getSplitsList());
|
||||
}
|
||||
}, directExecutor()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method calls the VTGate to execute list of queries as a batch.
|
||||
* <p>
|
||||
* <p>If asTransaction is set to <code>true</code> then query execution will not change the session cookie.
|
||||
* Otherwise, query execution will become part of the session.</p>
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param queryList List of sql queries to be executed.
|
||||
* @param bindVarsList <p>For each sql query it will provide a list of parameters to bind with.
|
||||
* If provided, should match the number of sql queries.</p>
|
||||
* @param asTransaction To execute query without impacting session cookie.
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return SQL Future with List of Cursors
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public SQLFuture<List<CursorWithError>> executeBatch(Context ctx, List<String> queryList, @Nullable List<Map<String, ?>> bindVarsList, boolean asTransaction, final VTSession vtSession) throws SQLException {
|
||||
synchronized (this) {
|
||||
vtSession.checkCallIsAllowed("executeBatch");
|
||||
List<Query.BoundQuery> queries = new ArrayList<>();
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
client.close();
|
||||
}
|
||||
|
||||
if (null != bindVarsList && bindVarsList.size() != queryList.size()) {
|
||||
throw new SQLDataException(
|
||||
"Size of SQL Query list does not match the bind variables list");
|
||||
}
|
||||
|
||||
for (int i = 0; i < queryList.size(); ++i) {
|
||||
queries.add(i, Proto.bindQuery(checkNotNull(queryList.get(i)),
|
||||
bindVarsList == null ? null : bindVarsList.get(i)));
|
||||
}
|
||||
|
||||
Vtgate.ExecuteBatchRequest.Builder requestBuilder =
|
||||
Vtgate.ExecuteBatchRequest.newBuilder()
|
||||
.addAllQueries(checkNotNull(queries))
|
||||
.setSession(vtSession.getSession())
|
||||
.setAsTransaction(asTransaction);
|
||||
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
|
||||
SQLFuture<List<CursorWithError>> call = new SQLFuture<>(
|
||||
transformAsync(client.executeBatch(ctx, requestBuilder.build()),
|
||||
new AsyncFunction<Vtgate.ExecuteBatchResponse, List<CursorWithError>>() {
|
||||
@Override
|
||||
public ListenableFuture<List<CursorWithError>> apply(Vtgate.ExecuteBatchResponse response) throws Exception {
|
||||
vtSession.setSession(response.getSession());
|
||||
Proto.checkError(response.getError());
|
||||
return Futures.immediateFuture(
|
||||
Proto.fromQueryResponsesToCursorList(response.getResultsList()));
|
||||
}
|
||||
}, directExecutor()));
|
||||
vtSession.setLastCall(call);
|
||||
return call;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param query Sql Query to be executed.
|
||||
* @param bindVars Parameters to bind with sql.
|
||||
* @param vtSession Session to be used with the call.
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
public Cursor streamExecute(Context ctx, String query, @Nullable Map<String, ?> bindVars, VTSession vtSession) throws SQLException {
|
||||
StreamExecuteRequest.Builder requestBuilder =
|
||||
StreamExecuteRequest.newBuilder()
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
.setSession(vtSession.getSession());
|
||||
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
|
||||
return new StreamCursor(client.streamExecute(ctx, requestBuilder.build()));
|
||||
}
|
||||
/**
|
||||
* This method splits the query into small parts based on the splitColumn and Algorithm type provided.
|
||||
*
|
||||
* @param ctx Context on user and execution deadline if any.
|
||||
* @param keyspace Keyspace to execute the query on.
|
||||
* @param query Sql Query to be executed.
|
||||
* @param bindVars Parameters to bind with sql.
|
||||
* @param splitColumns Column to be used to split the data.
|
||||
* @param splitCount Number of Partitions
|
||||
* @param numRowsPerQueryPart Limit the number of records per query part.
|
||||
* @param algorithm EQUAL_SPLITS or FULL_SCAN
|
||||
* @return SQL Future with Query Parts
|
||||
* @throws SQLException If anything fails on query execution.
|
||||
*/
|
||||
public SQLFuture<List<SplitQueryResponse.Part>> splitQuery(Context ctx, String keyspace, String query, @Nullable Map<String, ?> bindVars, Iterable<String> splitColumns,
|
||||
int splitCount, int numRowsPerQueryPart, Algorithm algorithm) throws SQLException {
|
||||
SplitQueryRequest.Builder requestBuilder =
|
||||
SplitQueryRequest.newBuilder()
|
||||
.setKeyspace(checkNotNull(keyspace))
|
||||
.setQuery(Proto.bindQuery(checkNotNull(query), bindVars))
|
||||
.addAllSplitColumn(splitColumns)
|
||||
.setSplitCount(splitCount)
|
||||
.setNumRowsPerQueryPart(numRowsPerQueryPart)
|
||||
.setAlgorithm(algorithm);
|
||||
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
|
||||
return new SQLFuture<>(
|
||||
transformAsync(client.splitQuery(ctx, requestBuilder.build()),
|
||||
new AsyncFunction<SplitQueryResponse, List<SplitQueryResponse.Part>>() {
|
||||
@Override
|
||||
public ListenableFuture<List<SplitQueryResponse.Part>> apply(SplitQueryResponse response) throws Exception {
|
||||
return Futures.immediateFuture(response.getSplitsList());
|
||||
}
|
||||
}, directExecutor()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
client.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[VTGateConnection-%s client=%s]",
|
||||
Integer.toHexString(this.hashCode()),
|
||||
client.toString()
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[VTGateConnection-%s client=%s]",
|
||||
Integer.toHexString(this.hashCode()),
|
||||
client.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -24,12 +24,6 @@ import com.google.common.collect.Iterables;
|
|||
import com.google.common.util.concurrent.AsyncFunction;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import java.sql.SQLDataException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.vitess.client.cursor.Cursor;
|
||||
import io.vitess.client.cursor.CursorWithError;
|
||||
|
@ -60,6 +54,14 @@ import io.vitess.proto.Vtgate.RollbackRequest;
|
|||
import io.vitess.proto.Vtgate.RollbackResponse;
|
||||
import io.vitess.proto.Vtgate.Session;
|
||||
|
||||
import java.sql.SQLDataException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An asynchronous VTGate transaction session.
|
||||
*
|
||||
|
@ -82,6 +84,7 @@ import io.vitess.proto.Vtgate.Session;
|
|||
*/
|
||||
@Deprecated
|
||||
public class VTGateTx {
|
||||
|
||||
private final RpcClient client;
|
||||
private final String keyspace;
|
||||
private Session session;
|
||||
|
@ -94,7 +97,8 @@ public class VTGateTx {
|
|||
}
|
||||
|
||||
public synchronized SQLFuture<Cursor> execute(Context ctx, String query, Map<String, ?> bindVars,
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
checkCallIsAllowed("execute");
|
||||
ExecuteRequest.Builder requestBuilder =
|
||||
ExecuteRequest.newBuilder()
|
||||
|
@ -235,7 +239,8 @@ public class VTGateTx {
|
|||
|
||||
public synchronized SQLFuture<Cursor> executeEntityIds(Context ctx, String query, String keyspace,
|
||||
String entityColumnName, Map<byte[], ?> entityKeyspaceIds, Map<String, ?> bindVars,
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields) throws SQLException {
|
||||
TabletType tabletType, Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
checkCallIsAllowed("executeEntityIds");
|
||||
ExecuteEntityIdsRequest.Builder requestBuilder = ExecuteEntityIdsRequest.newBuilder()
|
||||
.setQuery(Proto.bindQuery(query, bindVars))
|
||||
|
@ -270,34 +275,34 @@ public class VTGateTx {
|
|||
return call;
|
||||
}
|
||||
|
||||
public SQLFuture<List<CursorWithError>> executeBatch(Context ctx, List<String> queryList,
|
||||
@Nullable List<Map<String, ?>> bindVarsList, TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
List<Query.BoundQuery> queries = new ArrayList<>();
|
||||
public SQLFuture<List<CursorWithError>> executeBatch(Context ctx, List<String> queryList,
|
||||
@Nullable List<Map<String, ?>> bindVarsList, TabletType tabletType,
|
||||
Query.ExecuteOptions.IncludedFields includedFields)
|
||||
throws SQLException {
|
||||
List<Query.BoundQuery> queries = new ArrayList<>();
|
||||
|
||||
if (null != bindVarsList && bindVarsList.size() != queryList.size()) {
|
||||
throw new SQLDataException(
|
||||
"Size of SQL Query list does not match the bind variables list");
|
||||
}
|
||||
if (null != bindVarsList && bindVarsList.size() != queryList.size()) {
|
||||
throw new SQLDataException(
|
||||
"Size of SQL Query list does not match the bind variables list");
|
||||
}
|
||||
|
||||
for (int i = 0; i < queryList.size(); ++i) {
|
||||
queries.add(i, Proto.bindQuery(checkNotNull(queryList.get(i)),
|
||||
bindVarsList == null ? null : bindVarsList.get(i)));
|
||||
}
|
||||
for (int i = 0; i < queryList.size(); ++i) {
|
||||
queries.add(i, Proto.bindQuery(checkNotNull(queryList.get(i)),
|
||||
bindVarsList == null ? null : bindVarsList.get(i)));
|
||||
}
|
||||
|
||||
Vtgate.ExecuteBatchRequest.Builder requestBuilder =
|
||||
Vtgate.ExecuteBatchRequest.newBuilder()
|
||||
.addAllQueries(checkNotNull(queries))
|
||||
.setKeyspaceShard(keyspace)
|
||||
.setTabletType(checkNotNull(tabletType))
|
||||
.setSession(session)
|
||||
.setOptions(Query.ExecuteOptions.newBuilder()
|
||||
.setIncludedFields(includedFields));
|
||||
Vtgate.ExecuteBatchRequest.Builder requestBuilder =
|
||||
Vtgate.ExecuteBatchRequest.newBuilder()
|
||||
.addAllQueries(checkNotNull(queries))
|
||||
.setKeyspaceShard(keyspace)
|
||||
.setTabletType(checkNotNull(tabletType))
|
||||
.setSession(session)
|
||||
.setOptions(Query.ExecuteOptions.newBuilder()
|
||||
.setIncludedFields(includedFields));
|
||||
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
|
||||
return new SQLFuture<>(
|
||||
transformAsync(
|
||||
|
@ -313,7 +318,7 @@ public class VTGateTx {
|
|||
}
|
||||
},
|
||||
directExecutor()));
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized SQLFuture<List<Cursor>> executeBatchShards(Context ctx,
|
||||
Iterable<? extends BoundShardQuery> queries, TabletType tabletType,
|
||||
|
@ -384,14 +389,14 @@ public class VTGateTx {
|
|||
return call;
|
||||
}
|
||||
|
||||
public synchronized SQLFuture<Void> commit(Context ctx) throws SQLException {
|
||||
return commit(ctx, false);
|
||||
}
|
||||
public synchronized SQLFuture<Void> commit(Context ctx) throws SQLException {
|
||||
return commit(ctx, false);
|
||||
}
|
||||
|
||||
public synchronized SQLFuture<Void> commit(Context ctx, boolean atomic) throws SQLException {
|
||||
checkCallIsAllowed("commit");
|
||||
CommitRequest.Builder requestBuilder = CommitRequest.newBuilder().setSession(session)
|
||||
.setAtomic(atomic);
|
||||
.setAtomic(atomic);
|
||||
if (ctx.getCallerId() != null) {
|
||||
requestBuilder.setCallerId(ctx.getCallerId());
|
||||
}
|
||||
|
|
|
@ -21,119 +21,122 @@ import io.vitess.proto.Vtgate;
|
|||
|
||||
/**
|
||||
* A persistence session state for each connection.
|
||||
*
|
||||
*/
|
||||
public class VTSession {
|
||||
private Vtgate.Session session;
|
||||
private SQLFuture<?> lastCall;
|
||||
|
||||
/**
|
||||
* Create session cookie.
|
||||
*
|
||||
* @param target In the format keyspace@shard:tabletType. Only provide the part what needs to be set.
|
||||
* @param options Additional parameters to be passed along the query to the underlying database engine.
|
||||
*/
|
||||
public VTSession(String target, Query.ExecuteOptions options) {
|
||||
this.session = Vtgate.Session.newBuilder()
|
||||
.setTargetString(null == target ? "" : target)
|
||||
.setOptions(null == options ? Query.ExecuteOptions.newBuilder().build() : options)
|
||||
.setAutocommit(true)
|
||||
.setInTransaction(false)
|
||||
.build();
|
||||
}
|
||||
private Vtgate.Session session;
|
||||
private SQLFuture<?> lastCall;
|
||||
|
||||
/**
|
||||
* Returns the persistent session cookie.
|
||||
*
|
||||
* @return Session
|
||||
*/
|
||||
public Vtgate.Session getSession() {
|
||||
return this.session;
|
||||
}
|
||||
/**
|
||||
* Create session cookie.
|
||||
*
|
||||
* @param target In the format keyspace@shard:tabletType. Only provide the part what needs to
|
||||
* be set.
|
||||
* @param options Additional parameters to be passed along the query to the underlying
|
||||
* database engine.
|
||||
*/
|
||||
public VTSession(String target, Query.ExecuteOptions options) {
|
||||
this.session = Vtgate.Session.newBuilder()
|
||||
.setTargetString(null == target ? "" : target)
|
||||
.setOptions(null == options ? Query.ExecuteOptions.newBuilder().build() : options)
|
||||
.setAutocommit(true)
|
||||
.setInTransaction(false)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method set the session cookie returned from VTGate.
|
||||
* <p>
|
||||
* <p>This method is not synchronized as the callee function is synchronized.</p>
|
||||
*
|
||||
* @param session Updated globalSession to be set.
|
||||
*/
|
||||
public void setSession(Vtgate.Session session) {
|
||||
this.session = session;
|
||||
}
|
||||
/**
|
||||
* Returns the persistent session cookie.
|
||||
*
|
||||
* @return Session
|
||||
*/
|
||||
public Vtgate.Session getSession() {
|
||||
return this.session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state of commit mode.
|
||||
*
|
||||
* @return autocommit state
|
||||
*/
|
||||
public boolean isAutoCommit() {
|
||||
return this.session.getAutocommit();
|
||||
}
|
||||
/**
|
||||
* This method set the session cookie returned from VTGate.
|
||||
* <p>
|
||||
* <p>This method is not synchronized as the callee function is synchronized.</p>
|
||||
*
|
||||
* @param session Updated globalSession to be set.
|
||||
*/
|
||||
public void setSession(Vtgate.Session session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the auto commit state.
|
||||
*
|
||||
* @param autoCommit true or false
|
||||
*/
|
||||
public void setAutoCommit(boolean autoCommit) {
|
||||
this.session = this.session.toBuilder().setAutocommit(autoCommit).build();
|
||||
}
|
||||
/**
|
||||
* Returns the current state of commit mode.
|
||||
*
|
||||
* @return autocommit state
|
||||
*/
|
||||
public boolean isAutoCommit() {
|
||||
return this.session.getAutocommit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether session is maintaining any transaction or not.
|
||||
*
|
||||
* @return true or false based on if session cookie is maintaining any transaction.
|
||||
*/
|
||||
public boolean isInTransaction() {
|
||||
return this.session.getShardSessionsCount() > 0;
|
||||
}
|
||||
/**
|
||||
* Set the auto commit state.
|
||||
*
|
||||
* @param autoCommit true or false
|
||||
*/
|
||||
public void setAutoCommit(boolean autoCommit) {
|
||||
this.session = this.session.toBuilder().setAutocommit(autoCommit).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this session's transaction isolation level.
|
||||
*
|
||||
* @return Transaction Isolation Level of the Session
|
||||
*/
|
||||
public Query.ExecuteOptions.TransactionIsolation getTransactionIsolation() {
|
||||
return this.session.getOptions().getTransactionIsolation();
|
||||
}
|
||||
/**
|
||||
* Returns whether session is maintaining any transaction or not.
|
||||
*
|
||||
* @return true or false based on if session cookie is maintaining any transaction.
|
||||
*/
|
||||
public boolean isInTransaction() {
|
||||
return this.session.getShardSessionsCount() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this session's transaction isolation level.
|
||||
*
|
||||
* @param Transaction Isolation Level of the Session
|
||||
*/
|
||||
public void setTransactionIsolation(Query.ExecuteOptions.TransactionIsolation isolation) {
|
||||
this.session = this.session.toBuilder()
|
||||
.setOptions(this.session.getOptions().toBuilder()
|
||||
.setTransactionIsolation(isolation)).build();
|
||||
}
|
||||
/**
|
||||
* Returns this session's transaction isolation level.
|
||||
*
|
||||
* @return Transaction Isolation Level of the Session
|
||||
*/
|
||||
public Query.ExecuteOptions.TransactionIsolation getTransactionIsolation() {
|
||||
return this.session.getOptions().getTransactionIsolation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last SQLFuture call made on this session.
|
||||
*
|
||||
* @param call - SQLFuture
|
||||
*/
|
||||
public void setLastCall(SQLFuture call) {
|
||||
this.lastCall = call;
|
||||
}
|
||||
/**
|
||||
* Sets this session's transaction isolation level.
|
||||
*
|
||||
* @param Transaction Isolation Level of the Session
|
||||
*/
|
||||
public void setTransactionIsolation(Query.ExecuteOptions.TransactionIsolation isolation) {
|
||||
this.session = this.session.toBuilder()
|
||||
.setOptions(this.session.getOptions().toBuilder()
|
||||
.setTransactionIsolation(isolation)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if the last SQLFuture call is complete or not.
|
||||
* <p>
|
||||
* <p>This should be called only in the start of the function
|
||||
* where we modify the session cookie after the response from VTGate.
|
||||
* This is to protect any possible loss of session modification like shard transaction.</p>
|
||||
*
|
||||
* @param call - The represents the callee function name.
|
||||
* @throws IllegalStateException - Throws IllegalStateException if lastCall has not completed.
|
||||
*/
|
||||
public void checkCallIsAllowed(String call) throws IllegalStateException {
|
||||
// Calls are not allowed to overlap.
|
||||
if (lastCall != null && !lastCall.isDone()) {
|
||||
throw new IllegalStateException("Can't call " + call
|
||||
+ "() until the last asynchronous call is done on this transaction.");
|
||||
}
|
||||
/**
|
||||
* Set the last SQLFuture call made on this session.
|
||||
*
|
||||
* @param call - SQLFuture
|
||||
*/
|
||||
public void setLastCall(SQLFuture call) {
|
||||
this.lastCall = call;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if the last SQLFuture call is complete or not.
|
||||
* <p>
|
||||
* <p>This should be called only in the start of the function
|
||||
* where we modify the session cookie after the response from VTGate. This is to protect any
|
||||
* possible loss of session modification like shard transaction.</p>
|
||||
*
|
||||
* @param call - The represents the callee function name.
|
||||
* @throws IllegalStateException - Throws IllegalStateException if lastCall has not
|
||||
* completed.
|
||||
*/
|
||||
public void checkCallIsAllowed(String call) throws IllegalStateException {
|
||||
// Calls are not allowed to overlap.
|
||||
if (lastCall != null && !lastCall.isDone()) {
|
||||
throw new IllegalStateException("Can't call " + call
|
||||
+ "() until the last asynchronous call is done on this transaction.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -18,23 +18,23 @@ package io.vitess.client.cursor;
|
|||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import io.vitess.proto.Query.Field;
|
||||
import io.vitess.proto.Query.QueryResult;
|
||||
|
||||
import java.sql.SQLDataException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
import io.vitess.proto.Query.Field;
|
||||
import io.vitess.proto.Query.QueryResult;
|
||||
|
||||
/**
|
||||
* Provides access to the result rows of a query.
|
||||
*
|
||||
* <p>{@code Cursor} wraps an underlying Vitess {@link QueryResult} object, converting column
|
||||
* values from the raw result values to Java types. In the case of streaming queries, the
|
||||
* {@link StreamCursor} implementation will also fetch more {@code QueryResult} objects as
|
||||
* necessary.
|
||||
* values from the raw result values to Java types. In the case of streaming queries, the {@link
|
||||
* StreamCursor} implementation will also fetch more {@code QueryResult} objects as necessary.
|
||||
*
|
||||
* <p>Similar to {@link java.sql.ResultSet}, a {@code Cursor} is initially positioned before the
|
||||
* first row, and the first call to {@link #next()} moves to the first row. The getter methods
|
||||
|
@ -42,19 +42,19 @@ import io.vitess.proto.Query.QueryResult;
|
|||
* should be called to free resources when done, regardless of whether all the rows were processed.
|
||||
*
|
||||
* <p>Each individual {@code Cursor} is not thread-safe; it must be protected if used concurrently.
|
||||
* However, two cursors from the same {@link io.vitess.client.VTGateConn VTGateConn} can be
|
||||
* accessed concurrently without additional synchronization.
|
||||
* However, two cursors from the same {@link io.vitess.client.VTGateConn VTGateConn} can be accessed
|
||||
* concurrently without additional synchronization.
|
||||
*/
|
||||
@NotThreadSafe
|
||||
public abstract class Cursor implements AutoCloseable {
|
||||
|
||||
/**
|
||||
* A pre-built {@link FieldMap}, shared by each {@link Row}.
|
||||
*
|
||||
* <p>Although {@link Cursor} is not supposed to be used by multiple threads,
|
||||
* the asynchronous API makes it unavoidable that a {@code Cursor} may be created
|
||||
* in one thread and then sent to another. We therefore declare {@code fieldMap}
|
||||
* as {@code volatile} to guarantee the value set by the constructor is seen by
|
||||
* all threads.
|
||||
* the asynchronous API makes it unavoidable that a {@code Cursor} may be created in one thread
|
||||
* and then sent to another. We therefore declare {@code fieldMap} as {@code volatile} to
|
||||
* guarantee the value set by the constructor is seen by all threads.
|
||||
*/
|
||||
private volatile FieldMap fieldMap;
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -24,25 +24,25 @@ import io.vitess.proto.Vtrpc;
|
|||
*/
|
||||
public class CursorWithError {
|
||||
|
||||
private final Cursor cursor;
|
||||
private final Vtrpc.RPCError error;
|
||||
private final Cursor cursor;
|
||||
private final Vtrpc.RPCError error;
|
||||
|
||||
public CursorWithError(Query.ResultWithError resultWithError) {
|
||||
if (!resultWithError.hasError() ||
|
||||
Vtrpc.Code.OK == resultWithError.getError().getCode()) {
|
||||
this.cursor = new SimpleCursor(resultWithError.getResult());
|
||||
this.error = null;
|
||||
} else {
|
||||
this.cursor = null;
|
||||
this.error = resultWithError.getError();
|
||||
}
|
||||
public CursorWithError(Query.ResultWithError resultWithError) {
|
||||
if (!resultWithError.hasError()
|
||||
|| Vtrpc.Code.OK == resultWithError.getError().getCode()) {
|
||||
this.cursor = new SimpleCursor(resultWithError.getResult());
|
||||
this.error = null;
|
||||
} else {
|
||||
this.cursor = null;
|
||||
this.error = resultWithError.getError();
|
||||
}
|
||||
}
|
||||
|
||||
public Cursor getCursor() {
|
||||
return cursor;
|
||||
}
|
||||
public Cursor getCursor() {
|
||||
return cursor;
|
||||
}
|
||||
|
||||
public Vtrpc.RPCError getError() {
|
||||
return error;
|
||||
}
|
||||
public Vtrpc.RPCError getError() {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,11 +20,15 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import io.vitess.proto.Query.Field;
|
||||
|
||||
import org.apache.commons.collections4.map.CaseInsensitiveMap;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import org.apache.commons.collections4.map.CaseInsensitiveMap;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -37,6 +41,7 @@ import org.apache.commons.collections4.map.CaseInsensitiveMap;
|
|||
* index is also used to find the value in a separate list.
|
||||
*/
|
||||
public class FieldMap {
|
||||
|
||||
private final List<Field> fields;
|
||||
private final Map<String, Integer> labelMap;
|
||||
private final Map<String, Integer> nameMap;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -21,6 +21,12 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.primitives.UnsignedLong;
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import io.vitess.mysql.DateTime;
|
||||
import io.vitess.proto.Query;
|
||||
import io.vitess.proto.Query.Field;
|
||||
import io.vitess.proto.Query.Type;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
@ -33,29 +39,26 @@ import java.text.ParseException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
import io.vitess.mysql.DateTime;
|
||||
import io.vitess.proto.Query;
|
||||
import io.vitess.proto.Query.Field;
|
||||
import io.vitess.proto.Query.Type;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
/**
|
||||
* Type-converting wrapper around raw {@link io.vitess.proto.Query.Row} proto.
|
||||
*
|
||||
* <p>
|
||||
* Usually you get Row objects from a {@link Cursor}, which builds them by combining
|
||||
* {@link io.vitess.proto.Query.Row} with the list of {@link Field}s from the corresponding
|
||||
* {@link io.vitess.proto.Query.QueryResult}.
|
||||
* Usually you get Row objects from a {@link Cursor}, which builds them by combining {@link
|
||||
* io.vitess.proto.Query.Row} with the list of {@link Field}s from the corresponding {@link
|
||||
* io.vitess.proto.Query.QueryResult}.
|
||||
*
|
||||
* <p>
|
||||
* Methods on {@code Row} are intended to be compatible with those on {@link java.sql.ResultSet}
|
||||
* where possible. This means {@code columnIndex} values start at 1 for the first column, and
|
||||
* {@code columnLabel} values are case-insensitive. If multiple columns have the same
|
||||
* case-insensitive {@code columnLabel}, the earliest one will be returned.
|
||||
* where possible. This means {@code columnIndex} values start at 1 for the first column, and {@code
|
||||
* columnLabel} values are case-insensitive. If multiple columns have the same case-insensitive
|
||||
* {@code columnLabel}, the earliest one will be returned.
|
||||
*/
|
||||
@NotThreadSafe
|
||||
public class Row {
|
||||
|
||||
private final FieldMap fieldMap;
|
||||
private final List<ByteString> values;
|
||||
private final Query.Row rawRow;
|
||||
|
@ -70,8 +73,8 @@ public class Row {
|
|||
private volatile boolean lastGetWasNull;
|
||||
|
||||
/**
|
||||
* Construct a Row from {@link io.vitess.proto.Query.Row} proto with a pre-built
|
||||
* {@link FieldMap}.
|
||||
* Construct a Row from {@link io.vitess.proto.Query.Row} proto with a pre-built {@link
|
||||
* FieldMap}.
|
||||
*
|
||||
* <p>
|
||||
* {@link Cursor} uses this to share a {@link FieldMap} among multiple rows.
|
||||
|
@ -95,9 +98,9 @@ public class Row {
|
|||
* Construct a Row manually (not from proto).
|
||||
*
|
||||
* <p>
|
||||
* The primary purpose of this Row class is to wrap the {@link io.vitess.proto.Query.Row}
|
||||
* proto, which stores values in a packed format. However, when writing tests you may want to
|
||||
* create a Row from unpacked data.
|
||||
* The primary purpose of this Row class is to wrap the {@link io.vitess.proto.Query.Row} proto,
|
||||
* which stores values in a packed format. However, when writing tests you may want to create a
|
||||
* Row from unpacked data.
|
||||
*
|
||||
* <p>
|
||||
* Note that {@link #getRowProto()} will return null in this case, so a Row created in this way
|
||||
|
@ -110,6 +113,102 @@ public class Row {
|
|||
this.values = values;
|
||||
}
|
||||
|
||||
private static Object convertFieldValue(Field field, ByteString value) throws SQLException {
|
||||
// Note: We don't actually know the charset in which the value is encoded.
|
||||
// For dates and numeric values, we just assume UTF-8 because they (hopefully) don't contain
|
||||
// anything outside 7-bit ASCII, which (hopefully) is a subset of the actual charset.
|
||||
// For strings, we return byte[] and the application is responsible for using the right charset.
|
||||
switch (field.getType()) {
|
||||
case DECIMAL:
|
||||
return new BigDecimal(value.toStringUtf8());
|
||||
case INT8: // fall through
|
||||
case UINT8: // fall through
|
||||
case INT16: // fall through
|
||||
case UINT16: // fall through
|
||||
case INT24: // fall through
|
||||
case UINT24: // fall through
|
||||
case INT32:
|
||||
return Integer.valueOf(value.toStringUtf8());
|
||||
case UINT32: // fall through
|
||||
case INT64:
|
||||
return Long.valueOf(value.toStringUtf8());
|
||||
case UINT64:
|
||||
return new BigInteger(value.toStringUtf8());
|
||||
case FLOAT32:
|
||||
return Float.valueOf(value.toStringUtf8());
|
||||
case FLOAT64:
|
||||
return Double.valueOf(value.toStringUtf8());
|
||||
case NULL_TYPE:
|
||||
return null;
|
||||
case DATE:
|
||||
// We don't get time zone information from the server,
|
||||
// so we use the default time zone.
|
||||
try {
|
||||
return DateTime.parseDate(value.toStringUtf8());
|
||||
} catch (ParseException exc) {
|
||||
throw new SQLDataException("Can't parse DATE: " + value.toStringUtf8(), exc);
|
||||
}
|
||||
case TIME:
|
||||
// We don't get time zone information from the server,
|
||||
// so we use the default time zone.
|
||||
try {
|
||||
return DateTime.parseTime(value.toStringUtf8());
|
||||
} catch (ParseException exc) {
|
||||
throw new SQLDataException("Can't parse TIME: " + value.toStringUtf8(), exc);
|
||||
}
|
||||
case DATETIME: // fall through
|
||||
case TIMESTAMP:
|
||||
// We don't get time zone information from the server,
|
||||
// so we use the default time zone.
|
||||
try {
|
||||
return DateTime.parseTimestamp(value.toStringUtf8());
|
||||
} catch (ParseException exc) {
|
||||
throw new SQLDataException("Can't parse TIMESTAMP: " + value.toStringUtf8(), exc);
|
||||
}
|
||||
case YEAR:
|
||||
return Short.valueOf(value.toStringUtf8());
|
||||
case ENUM: // fall through
|
||||
case SET:
|
||||
return value.toStringUtf8();
|
||||
case BIT: // fall through
|
||||
case TEXT: // fall through
|
||||
case BLOB: // fall through
|
||||
case VARCHAR: // fall through
|
||||
case VARBINARY: // fall through
|
||||
case CHAR: // fall through
|
||||
case BINARY:
|
||||
case GEOMETRY:
|
||||
case JSON:
|
||||
return value.toByteArray();
|
||||
default:
|
||||
throw new SQLDataException("unknown field type: " + field.getType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract cell values from the single-buffer wire format.
|
||||
*
|
||||
* <p>
|
||||
* See the docs for the {@code Row} message in {@code query.proto}.
|
||||
*/
|
||||
private static List<ByteString> extractValues(List<Long> lengths, ByteString buf) {
|
||||
List<ByteString> list = new ArrayList<ByteString>(lengths.size());
|
||||
|
||||
int start = 0;
|
||||
for (long len : lengths) {
|
||||
if (len < 0) {
|
||||
// This indicates a MySQL NULL value, to distinguish it from a zero-length string.
|
||||
list.add((ByteString) null);
|
||||
} else {
|
||||
// Lengths are returned as long, but ByteString.substring() only supports int.
|
||||
list.add(buf.substring(start, start + (int) len));
|
||||
start += len;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of columns.
|
||||
*/
|
||||
|
@ -212,8 +311,8 @@ public class Row {
|
|||
* Returns the column value, or 0 if the value is SQL NULL.
|
||||
*
|
||||
* <p>
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or
|
||||
* {@link #getObject(String,Class)}.
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or {@link
|
||||
* #getObject(String, Class)}.
|
||||
*
|
||||
* @param columnLabel case-insensitive column label
|
||||
*/
|
||||
|
@ -225,8 +324,8 @@ public class Row {
|
|||
* Returns the column value, or 0 if the value is SQL NULL.
|
||||
*
|
||||
* <p>
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or
|
||||
* {@link #getObject(int,Class)}.
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or {@link #getObject(int,
|
||||
* Class)}.
|
||||
*
|
||||
* @param columnIndex 1-based column number (0 is invalid)
|
||||
*/
|
||||
|
@ -246,8 +345,8 @@ public class Row {
|
|||
* @param columnIndex 1-based column number (0 is invalid)
|
||||
*/
|
||||
public UnsignedLong getULong(int columnIndex) throws SQLException {
|
||||
BigInteger l = getObject(columnIndex, BigInteger.class);
|
||||
return l == null ? null : UnsignedLong.fromLongBits(l.longValue());
|
||||
BigInteger longValue = getObject(columnIndex, BigInteger.class);
|
||||
return longValue == null ? null : UnsignedLong.fromLongBits(longValue.longValue());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -268,8 +367,8 @@ public class Row {
|
|||
* Returns the column value, or 0 if the value is SQL NULL.
|
||||
*
|
||||
* <p>
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or
|
||||
* {@link #getObject(String,Class)}.
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or {@link
|
||||
* #getObject(String, Class)}.
|
||||
*
|
||||
* @param columnLabel case-insensitive column label
|
||||
*/
|
||||
|
@ -281,8 +380,8 @@ public class Row {
|
|||
* Returns the column value, or 0 if the value is SQL NULL.
|
||||
*
|
||||
* <p>
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or
|
||||
* {@link #getObject(int,Class)}.
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or {@link #getObject(int,
|
||||
* Class)}.
|
||||
*
|
||||
* @param columnIndex 1-based column number (0 is invalid)
|
||||
*/
|
||||
|
@ -295,8 +394,8 @@ public class Row {
|
|||
* Returns the column value, or 0 if the value is SQL NULL.
|
||||
*
|
||||
* <p>
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or
|
||||
* {@link #getObject(String,Class)}.
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or {@link
|
||||
* #getObject(String, Class)}.
|
||||
*
|
||||
* @param columnLabel case-insensitive column label
|
||||
*/
|
||||
|
@ -308,8 +407,8 @@ public class Row {
|
|||
* Returns the column value, or 0 if the value is SQL NULL.
|
||||
*
|
||||
* <p>
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or
|
||||
* {@link #getObject(int,Class)}.
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or {@link #getObject(int,
|
||||
* Class)}.
|
||||
*
|
||||
* @param columnIndex 1-based column number (0 is invalid)
|
||||
*/
|
||||
|
@ -322,8 +421,8 @@ public class Row {
|
|||
* Returns the column value, or 0 if the value is SQL NULL.
|
||||
*
|
||||
* <p>
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or
|
||||
* {@link #getObject(String,Class)}.
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or {@link
|
||||
* #getObject(String, Class)}.
|
||||
*
|
||||
* @param columnLabel case-insensitive column label
|
||||
*/
|
||||
|
@ -335,8 +434,8 @@ public class Row {
|
|||
* Returns the column value, or 0 if the value is SQL NULL.
|
||||
*
|
||||
* <p>
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or
|
||||
* {@link #getObject(int,Class)}.
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or {@link #getObject(int,
|
||||
* Class)}.
|
||||
*
|
||||
* @param columnIndex 1-based column number (0 is invalid)
|
||||
*/
|
||||
|
@ -389,8 +488,8 @@ public class Row {
|
|||
}
|
||||
try {
|
||||
return DateTime.parseDate(rawValue.toStringUtf8(), cal);
|
||||
} catch (ParseException e) {
|
||||
throw new SQLDataException("Can't parse DATE: " + rawValue.toStringUtf8(), e);
|
||||
} catch (ParseException exc) {
|
||||
throw new SQLDataException("Can't parse DATE: " + rawValue.toStringUtf8(), exc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -438,8 +537,8 @@ public class Row {
|
|||
}
|
||||
try {
|
||||
return DateTime.parseTime(rawValue.toStringUtf8(), cal);
|
||||
} catch (ParseException e) {
|
||||
throw new SQLDataException("Can't parse TIME: " + rawValue.toStringUtf8(), e);
|
||||
} catch (ParseException exc) {
|
||||
throw new SQLDataException("Can't parse TIME: " + rawValue.toStringUtf8(), exc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -487,8 +586,8 @@ public class Row {
|
|||
}
|
||||
try {
|
||||
return DateTime.parseTimestamp(rawValue.toStringUtf8(), cal);
|
||||
} catch (ParseException e) {
|
||||
throw new SQLDataException("Can't parse TIMESTAMP: " + rawValue.toStringUtf8(), e);
|
||||
} catch (ParseException exc) {
|
||||
throw new SQLDataException("Can't parse TIMESTAMP: " + rawValue.toStringUtf8(), exc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -524,8 +623,8 @@ public class Row {
|
|||
* Returns the column value, or 0 if the value is SQL NULL.
|
||||
*
|
||||
* <p>
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or
|
||||
* {@link #getObject(String,Class)}.
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or {@link
|
||||
* #getObject(String, Class)}.
|
||||
*
|
||||
* @param columnLabel case-insensitive column label
|
||||
*/
|
||||
|
@ -537,8 +636,8 @@ public class Row {
|
|||
* Returns the column value, or 0 if the value is SQL NULL.
|
||||
*
|
||||
* <p>
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or
|
||||
* {@link #getObject(int,Class)}.
|
||||
* To distinguish between 0 and SQL NULL, use either {@link #wasNull()} or {@link #getObject(int,
|
||||
* Class)}.
|
||||
*
|
||||
* @param columnIndex 1-based column number (0 is invalid)
|
||||
*/
|
||||
|
@ -570,12 +669,15 @@ public class Row {
|
|||
*/
|
||||
@SuppressWarnings("unchecked") // by runtime check
|
||||
public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
|
||||
Object o = getObject(columnIndex);
|
||||
if (o != null && !type.isInstance(o)) {
|
||||
Object object = getObject(columnIndex);
|
||||
if (object != null && !type.isInstance(object)) {
|
||||
throw new SQLDataException(
|
||||
"type mismatch, expected: " + type.getName() + ", actual: " + o.getClass().getName());
|
||||
"type mismatch, expected: "
|
||||
+ type.getName()
|
||||
+ ", actual: "
|
||||
+ object.getClass().getName());
|
||||
}
|
||||
return (T) o;
|
||||
return (T) object;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -617,11 +719,9 @@ public class Row {
|
|||
* {@code wasNull()}.
|
||||
*
|
||||
* <p>
|
||||
* As an alternative to {@code wasNull()}, you can use {@link #getObject(int,Class)} (e.g.
|
||||
* {@code getObject(0, Long.class)} instead of {@code getLong(0)}) to get a wrapped {@code Long}
|
||||
* value that will be {@code null} if the column value was SQL NULL.
|
||||
*
|
||||
* @throws SQLException
|
||||
* As an alternative to {@code wasNull()}, you can use {@link #getObject(int, Class)} (e.g. {@code
|
||||
* getObject(0, Long.class)} instead of {@code getLong(0)}) to get a wrapped {@code Long} value
|
||||
* that will be {@code null} if the column value was SQL NULL.
|
||||
*/
|
||||
public boolean wasNull() throws SQLException {
|
||||
// Note: lastGetWasNull is currently set only in getRawValue(),
|
||||
|
@ -630,100 +730,4 @@ public class Row {
|
|||
// checking wasNull() after each get*().
|
||||
return lastGetWasNull;
|
||||
}
|
||||
|
||||
private static Object convertFieldValue(Field field, ByteString value) throws SQLException {
|
||||
// Note: We don't actually know the charset in which the value is encoded.
|
||||
// For dates and numeric values, we just assume UTF-8 because they (hopefully) don't contain
|
||||
// anything outside 7-bit ASCII, which (hopefully) is a subset of the actual charset.
|
||||
// For strings, we return byte[] and the application is responsible for using the right charset.
|
||||
switch (field.getType()) {
|
||||
case DECIMAL:
|
||||
return new BigDecimal(value.toStringUtf8());
|
||||
case INT8: // fall through
|
||||
case UINT8: // fall through
|
||||
case INT16: // fall through
|
||||
case UINT16: // fall through
|
||||
case INT24: // fall through
|
||||
case UINT24: // fall through
|
||||
case INT32:
|
||||
return Integer.valueOf(value.toStringUtf8());
|
||||
case UINT32: // fall through
|
||||
case INT64:
|
||||
return Long.valueOf(value.toStringUtf8());
|
||||
case UINT64:
|
||||
return new BigInteger(value.toStringUtf8());
|
||||
case FLOAT32:
|
||||
return Float.valueOf(value.toStringUtf8());
|
||||
case FLOAT64:
|
||||
return Double.valueOf(value.toStringUtf8());
|
||||
case NULL_TYPE:
|
||||
return null;
|
||||
case DATE:
|
||||
// We don't get time zone information from the server,
|
||||
// so we use the default time zone.
|
||||
try {
|
||||
return DateTime.parseDate(value.toStringUtf8());
|
||||
} catch (ParseException e) {
|
||||
throw new SQLDataException("Can't parse DATE: " + value.toStringUtf8(), e);
|
||||
}
|
||||
case TIME:
|
||||
// We don't get time zone information from the server,
|
||||
// so we use the default time zone.
|
||||
try {
|
||||
return DateTime.parseTime(value.toStringUtf8());
|
||||
} catch (ParseException e) {
|
||||
throw new SQLDataException("Can't parse TIME: " + value.toStringUtf8(), e);
|
||||
}
|
||||
case DATETIME: // fall through
|
||||
case TIMESTAMP:
|
||||
// We don't get time zone information from the server,
|
||||
// so we use the default time zone.
|
||||
try {
|
||||
return DateTime.parseTimestamp(value.toStringUtf8());
|
||||
} catch (ParseException e) {
|
||||
throw new SQLDataException("Can't parse TIMESTAMP: " + value.toStringUtf8(), e);
|
||||
}
|
||||
case YEAR:
|
||||
return Short.valueOf(value.toStringUtf8());
|
||||
case ENUM: // fall through
|
||||
case SET:
|
||||
return value.toStringUtf8();
|
||||
case BIT: // fall through
|
||||
case TEXT: // fall through
|
||||
case BLOB: // fall through
|
||||
case VARCHAR: // fall through
|
||||
case VARBINARY: // fall through
|
||||
case CHAR: // fall through
|
||||
case BINARY:
|
||||
case GEOMETRY:
|
||||
case JSON:
|
||||
return value.toByteArray();
|
||||
default:
|
||||
throw new SQLDataException("unknown field type: " + field.getType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract cell values from the single-buffer wire format.
|
||||
*
|
||||
* <p>
|
||||
* See the docs for the {@code Row} message in {@code query.proto}.
|
||||
*/
|
||||
private static List<ByteString> extractValues(List<Long> lengths, ByteString buf) {
|
||||
List<ByteString> list = new ArrayList<ByteString>(lengths.size());
|
||||
|
||||
int start = 0;
|
||||
for (long len : lengths) {
|
||||
if (len < 0) {
|
||||
// This indicates a MySQL NULL value, to distinguish it from a zero-length string.
|
||||
list.add((ByteString) null);
|
||||
} else {
|
||||
// Lengths are returned as long, but ByteString.substring() only supports int.
|
||||
list.add(buf.substring(start, start + (int) len));
|
||||
start += len;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -16,20 +16,22 @@
|
|||
|
||||
package io.vitess.client.cursor;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
import io.vitess.proto.Query;
|
||||
import io.vitess.proto.Query.Field;
|
||||
import io.vitess.proto.Query.QueryResult;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
/**
|
||||
* A {@link Cursor} that serves records from a single {@link QueryResult} object.
|
||||
*/
|
||||
@NotThreadSafe
|
||||
public class SimpleCursor extends Cursor {
|
||||
|
||||
private final QueryResult queryResult;
|
||||
private final Iterator<Query.Row> rowIterator;
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -16,24 +16,26 @@
|
|||
|
||||
package io.vitess.client.cursor;
|
||||
|
||||
import java.sql.SQLDataException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
import io.vitess.client.StreamIterator;
|
||||
import io.vitess.proto.Query;
|
||||
import io.vitess.proto.Query.Field;
|
||||
import io.vitess.proto.Query.QueryResult;
|
||||
|
||||
import java.sql.SQLDataException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
/**
|
||||
* A {@link Cursor} that serves records from the sequence of {@link QueryResult} objects
|
||||
* represented by a {@link StreamIterator}.
|
||||
* A {@link Cursor} that serves records from the sequence of {@link QueryResult} objects represented
|
||||
* by a {@link StreamIterator}.
|
||||
*/
|
||||
@NotThreadSafe
|
||||
public class StreamCursor extends Cursor {
|
||||
|
||||
private StreamIterator<QueryResult> streamIterator;
|
||||
private Iterator<Query.Row> rowIterator;
|
||||
|
||||
|
@ -103,8 +105,8 @@ public class StreamCursor extends Cursor {
|
|||
*
|
||||
* <p>Whereas the public {@link #next()} method advances the {@link Cursor} state to the next
|
||||
* {@link Row}, this method advances the internal state to the next {@link QueryResult}, which
|
||||
* contains a batch of rows. Specifically, we get the next {@link QueryResult} from
|
||||
* {@link #streamIterator}, and then set {@link #rowIterator} accordingly.
|
||||
* contains a batch of rows. Specifically, we get the next {@link QueryResult} from {@link
|
||||
* #streamIterator}, and then set {@link #rowIterator} accordingly.
|
||||
*
|
||||
* <p>If {@link #fields} is null, we assume the next {@link QueryResult} must contain the fields,
|
||||
* and set {@link #fields} from it.
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -21,8 +21,8 @@ import java.net.InetSocketAddress;
|
|||
|
||||
/**
|
||||
* <p>A wrapper type holding TLS-related fields for the
|
||||
* {@link io.vitess.client.RpcClientFactory#createTls(InetSocketAddress, TlsOptions)} method, so that
|
||||
* this method won't have an unwieldy number of direct parameters.</p>
|
||||
* {@link io.vitess.client.RpcClientFactory#createTls(InetSocketAddress, TlsOptions)} method, so
|
||||
* that this method won't have an unwieldy number of direct parameters.</p>
|
||||
*
|
||||
* <p>This path uses a builder pattern style:</p>
|
||||
*
|
||||
|
@ -39,6 +39,7 @@ import java.net.InetSocketAddress;
|
|||
* </blockquote>
|
||||
*/
|
||||
public class TlsOptions {
|
||||
|
||||
private File keyStore;
|
||||
private String keyStorePassword;
|
||||
private String keyAlias;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -17,6 +17,7 @@
|
|||
package io.vitess.mysql;
|
||||
|
||||
import com.google.common.math.IntMath;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
|
@ -29,10 +30,11 @@ import java.util.Calendar;
|
|||
* Utility methods for processing MySQL TIME, DATE, DATETIME, and TIMESTAMP.
|
||||
*
|
||||
* <p>These provide functionality similar to {@code valueOf()} and {@code toString()}
|
||||
* in {@link java.sql.Date} et al. The difference is that these support MySQL-specific
|
||||
* syntax like fractional seconds, negative times, and hours > 24 for elapsed time.
|
||||
* in {@link java.sql.Date} et al. The difference is that these support MySQL-specific syntax like
|
||||
* fractional seconds, negative times, and hours > 24 for elapsed time.
|
||||
*/
|
||||
public class DateTime {
|
||||
|
||||
private static final String DATE_FORMAT = "yyyy-MM-dd";
|
||||
private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
|
@ -139,7 +141,7 @@ public class DateTime {
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
} catch (NumberFormatException exc) {
|
||||
throw new ParseException("Invalid MYSQL TIME format: " + value, 0);
|
||||
}
|
||||
|
||||
|
@ -165,8 +167,8 @@ public class DateTime {
|
|||
* Format a {@link Time} as a MySQL TIME with the default time zone.
|
||||
*
|
||||
* <p>This should match {@link Time#toString()} for the values it supports.
|
||||
* For MySQL-specific syntax (like fractional seconds, negative times,
|
||||
* and hours > 24) the results will differ.
|
||||
* For MySQL-specific syntax (like fractional seconds, negative times, and hours > 24) the results
|
||||
* will differ.
|
||||
*/
|
||||
public static String formatTime(Time value) {
|
||||
return formatTime(value, Calendar.getInstance());
|
||||
|
@ -177,8 +179,8 @@ public class DateTime {
|
|||
*
|
||||
* <p>The range for TIME values is '-838:59:59.000000' to '838:59:59.000000'
|
||||
* <a href="http://dev.mysql.com/doc/refman/5.6/en/time.html">[1]</a>.
|
||||
* We don't enforce that range, but we do print >24 hours rather than
|
||||
* wrapping around to the next day.
|
||||
* We don't enforce that range, but we do print >24 hours rather than wrapping around to the next
|
||||
* day.
|
||||
*/
|
||||
public static String formatTime(Time value, Calendar cal) {
|
||||
long millis = value.getTime();
|
||||
|
@ -243,7 +245,7 @@ public class DateTime {
|
|||
}
|
||||
try {
|
||||
nanos = Integer.parseInt(fraction) * IntMath.pow(10, 9 - fraction.length());
|
||||
} catch (NumberFormatException e) {
|
||||
} catch (NumberFormatException exc) {
|
||||
throw new ParseException("Invalid MySQL TIMESTAMP format: " + value, dotIndex + 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -20,20 +20,23 @@ import static org.junit.Assert.assertEquals;
|
|||
|
||||
import com.google.common.primitives.UnsignedLong;
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import io.vitess.proto.Query;
|
||||
import io.vitess.proto.Query.BindVariable;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import io.vitess.proto.Query;
|
||||
import io.vitess.proto.Query.BindVariable;
|
||||
|
||||
@RunWith(value = Parameterized.class)
|
||||
public class BindVarTest {
|
||||
|
||||
private Object input;
|
||||
private BindVariable expected;
|
||||
|
||||
|
@ -50,9 +53,9 @@ public class BindVarTest {
|
|||
BindVariable.newBuilder().setType(Query.Type.VARCHAR)
|
||||
.setValue(ByteString.copyFromUtf8("hello world")).build()},
|
||||
// Bytes
|
||||
{new byte[] {1, 2, 3},
|
||||
{new byte[]{1, 2, 3},
|
||||
BindVariable.newBuilder().setType(Query.Type.VARBINARY)
|
||||
.setValue(ByteString.copyFrom(new byte[] {1, 2, 3})).build()},
|
||||
.setValue(ByteString.copyFrom(new byte[]{1, 2, 3})).build()},
|
||||
// Int
|
||||
{123,
|
||||
BindVariable.newBuilder().setType(Query.Type.INT64)
|
||||
|
@ -78,12 +81,12 @@ public class BindVarTest {
|
|||
BindVariable.newBuilder().setType(Query.Type.FLOAT64)
|
||||
.setValue(ByteString.copyFromUtf8("1.23")).build()},
|
||||
// List of Bytes
|
||||
{Arrays.asList(new byte[] {1, 2, 3}, new byte[] {4, 5, 6}),
|
||||
{Arrays.asList(new byte[]{1, 2, 3}, new byte[]{4, 5, 6}),
|
||||
BindVariable.newBuilder().setType(Query.Type.TUPLE)
|
||||
.addValues(Query.Value.newBuilder().setType(Query.Type.VARBINARY)
|
||||
.setValue(ByteString.copyFrom(new byte[] {1, 2, 3})).build())
|
||||
.setValue(ByteString.copyFrom(new byte[]{1, 2, 3})).build())
|
||||
.addValues(Query.Value.newBuilder().setType(Query.Type.VARBINARY)
|
||||
.setValue(ByteString.copyFrom(new byte[] {4, 5, 6})).build())
|
||||
.setValue(ByteString.copyFrom(new byte[]{4, 5, 6})).build())
|
||||
.build()},
|
||||
// Boolean
|
||||
{true,
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -20,91 +20,95 @@ import static org.junit.Assert.assertEquals;
|
|||
|
||||
import com.google.common.primitives.UnsignedLong;
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import io.vitess.proto.Query;
|
||||
import io.vitess.proto.Vtgate.ExecuteEntityIdsRequest.EntityId;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import io.vitess.proto.Query;
|
||||
import io.vitess.proto.Vtgate.ExecuteEntityIdsRequest.EntityId;
|
||||
|
||||
@RunWith(value = Parameterized.class)
|
||||
public class EntityIdTest {
|
||||
|
||||
private Object input;
|
||||
private EntityId expected;
|
||||
|
||||
private static final ByteString KEYSPACE_ID = ByteString.copyFrom(new byte[] {1, 2, 3});
|
||||
private static final ByteString KEYSPACE_ID = ByteString.copyFrom(new byte[]{1, 2, 3});
|
||||
|
||||
@Parameters
|
||||
public static Collection<Object[]> testParams() {
|
||||
Object[][] params = {
|
||||
// SQL NULL
|
||||
{
|
||||
null, EntityId.newBuilder().setKeyspaceId(KEYSPACE_ID).setType(Query.Type.NULL_TYPE).build()
|
||||
},
|
||||
// String
|
||||
{
|
||||
"hello world",
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.VARCHAR)
|
||||
.setValue(ByteString.copyFromUtf8("hello world"))
|
||||
.build()
|
||||
},
|
||||
// Bytes
|
||||
{
|
||||
new byte[] {1, 2, 3},
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.VARBINARY)
|
||||
.setValue(ByteString.copyFrom(new byte[] {1, 2, 3}))
|
||||
.build()
|
||||
},
|
||||
// Int
|
||||
{
|
||||
123,
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.INT64)
|
||||
.setValue(ByteString.copyFromUtf8("123"))
|
||||
.build()
|
||||
},
|
||||
{
|
||||
123L,
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.INT64)
|
||||
.setValue(ByteString.copyFromUtf8("123"))
|
||||
.build()
|
||||
},
|
||||
// Uint
|
||||
{
|
||||
UnsignedLong.fromLongBits(-1),
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.UINT64)
|
||||
.setValue(ByteString.copyFromUtf8("18446744073709551615"))
|
||||
.build()
|
||||
},
|
||||
// Float
|
||||
{
|
||||
1.23f,
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.FLOAT64)
|
||||
.setValue(ByteString.copyFromUtf8("1.23"))
|
||||
.build()
|
||||
},
|
||||
{
|
||||
1.23,
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.FLOAT64)
|
||||
.setValue(ByteString.copyFromUtf8("1.23"))
|
||||
.build()
|
||||
},
|
||||
// SQL NULL
|
||||
{
|
||||
null,
|
||||
EntityId.newBuilder().setKeyspaceId(KEYSPACE_ID).setType(Query.Type.NULL_TYPE).build()
|
||||
},
|
||||
// String
|
||||
{
|
||||
"hello world",
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.VARCHAR)
|
||||
.setValue(ByteString.copyFromUtf8("hello world"))
|
||||
.build()
|
||||
},
|
||||
// Bytes
|
||||
{
|
||||
new byte[]{1, 2, 3},
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.VARBINARY)
|
||||
.setValue(ByteString.copyFrom(new byte[]{1, 2, 3}))
|
||||
.build()
|
||||
},
|
||||
// Int
|
||||
{
|
||||
123,
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.INT64)
|
||||
.setValue(ByteString.copyFromUtf8("123"))
|
||||
.build()
|
||||
},
|
||||
{
|
||||
123L,
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.INT64)
|
||||
.setValue(ByteString.copyFromUtf8("123"))
|
||||
.build()
|
||||
},
|
||||
// Uint
|
||||
{
|
||||
UnsignedLong.fromLongBits(-1),
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.UINT64)
|
||||
.setValue(ByteString.copyFromUtf8("18446744073709551615"))
|
||||
.build()
|
||||
},
|
||||
// Float
|
||||
{
|
||||
1.23f,
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.FLOAT64)
|
||||
.setValue(ByteString.copyFromUtf8("1.23"))
|
||||
.build()
|
||||
},
|
||||
{
|
||||
1.23,
|
||||
EntityId.newBuilder()
|
||||
.setKeyspaceId(KEYSPACE_ID)
|
||||
.setType(Query.Type.FLOAT64)
|
||||
.setValue(ByteString.copyFromUtf8("1.23"))
|
||||
.build()
|
||||
},
|
||||
};
|
||||
return Arrays.asList(params);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -17,7 +17,9 @@
|
|||
package io.vitess.client;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -25,6 +27,7 @@ import org.junit.runners.JUnit4;
|
|||
|
||||
@RunWith(JUnit4.class)
|
||||
public class ProtoTest {
|
||||
|
||||
@Test
|
||||
public void testGetErrno() {
|
||||
final Map<String, Integer> testValues =
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -20,27 +20,6 @@ import com.google.common.base.Throwables;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.protobuf.ByteString;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLDataException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.sql.SQLInvalidAuthorizationSpecException;
|
||||
import java.sql.SQLNonTransientException;
|
||||
import java.sql.SQLRecoverableException;
|
||||
import java.sql.SQLSyntaxErrorException;
|
||||
import java.sql.SQLTimeoutException;
|
||||
import java.sql.SQLTransientException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import io.vitess.client.cursor.Cursor;
|
||||
import io.vitess.client.cursor.Row;
|
||||
|
@ -56,6 +35,28 @@ import io.vitess.proto.Topodata.TabletType;
|
|||
import io.vitess.proto.Vtgate.SplitQueryResponse;
|
||||
import io.vitess.proto.Vtrpc.CallerID;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLDataException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLIntegrityConstraintViolationException;
|
||||
import java.sql.SQLInvalidAuthorizationSpecException;
|
||||
import java.sql.SQLNonTransientException;
|
||||
import java.sql.SQLRecoverableException;
|
||||
import java.sql.SQLSyntaxErrorException;
|
||||
import java.sql.SQLTimeoutException;
|
||||
import java.sql.SQLTransientException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* RpcClientTest tests a given implementation of RpcClient against a mock vtgate server
|
||||
* (go/cmd/vtgateclienttest).
|
||||
|
@ -64,6 +65,7 @@ import io.vitess.proto.Vtrpc.CallerID;
|
|||
* vtgateclienttest server with the necessary parameters and then set 'client'.
|
||||
*/
|
||||
public abstract class RpcClientTest {
|
||||
|
||||
protected static RpcClient client;
|
||||
// ready is true when "vtgateclienttest" can accept RPCs. It is set by "waitForVtgateclienttest"
|
||||
// and reset to "false" at the start of each test class by "resetReady".
|
||||
|
@ -86,7 +88,8 @@ public abstract class RpcClientTest {
|
|||
|
||||
// ctx is used by all RPCs within one test method. A deadline is set to cap test execution.
|
||||
// (RPCs will fail with DEADLINE_EXCEEDED if they keep using "ctx" 5 seconds from now.)
|
||||
ctx = Context.getDefault().withDeadlineAfter(Duration.standardSeconds(5)).withCallerId(CALLER_ID);
|
||||
ctx = Context.getDefault().withDeadlineAfter(Duration.standardSeconds(5))
|
||||
.withCallerId(CALLER_ID);
|
||||
}
|
||||
|
||||
private static final String ECHO_PREFIX = "echo://";
|
||||
|
@ -111,20 +114,20 @@ public abstract class RpcClientTest {
|
|||
private static final String SHARDS_ECHO = "[-80 80-]";
|
||||
|
||||
private static final List<byte[]> KEYSPACE_IDS =
|
||||
Arrays.asList(new byte[] {1, 2, 3, 4}, new byte[] {5, 6, 7, 8});
|
||||
Arrays.asList(new byte[]{1, 2, 3, 4}, new byte[]{5, 6, 7, 8});
|
||||
private static final String KEYSPACE_IDS_ECHO = "[[1 2 3 4] [5 6 7 8]]";
|
||||
|
||||
private static final List<KeyRange> KEY_RANGES =
|
||||
Arrays.asList(KeyRange.newBuilder().setStart(ByteString.copyFrom(new byte[] {1, 2, 3, 4}))
|
||||
.setEnd(ByteString.copyFrom(new byte[] {5, 6, 7, 8})).build());
|
||||
Arrays.asList(KeyRange.newBuilder().setStart(ByteString.copyFrom(new byte[]{1, 2, 3, 4}))
|
||||
.setEnd(ByteString.copyFrom(new byte[]{5, 6, 7, 8})).build());
|
||||
private static final String KEY_RANGES_ECHO =
|
||||
"[start:\"\\001\\002\\003\\004\" end:\"\\005\\006\\007\\010\" ]";
|
||||
|
||||
private static final ImmutableMap<byte[], Object> ENTITY_KEYSPACE_IDS =
|
||||
new ImmutableMap.Builder<byte[], Object>()
|
||||
.put(new byte[] {1, 2, 3}, 123)
|
||||
.put(new byte[] {4, 5, 6}, 2.5)
|
||||
.put(new byte[] {7, 8, 9}, new byte[] {1, 2, 3})
|
||||
.put(new byte[]{1, 2, 3}, 123)
|
||||
.put(new byte[]{4, 5, 6}, 2.5)
|
||||
.put(new byte[]{7, 8, 9}, new byte[]{1, 2, 3})
|
||||
.build();
|
||||
private static final String ENTITY_KEYSPACE_IDS_ECHO =
|
||||
"[type:INT64 value:\"123\" keyspace_id:\"\\001\\002\\003\" type:FLOAT64 value:\"2.5\" keyspace_id:\"\\004\\005\\006\" type:VARBINARY value:\"\\001\\002\\003\" keyspace_id:\"\\007\\010\\t\" ]";
|
||||
|
@ -132,12 +135,13 @@ public abstract class RpcClientTest {
|
|||
private static final TabletType TABLET_TYPE = TabletType.REPLICA;
|
||||
private static final String TABLET_TYPE_ECHO = TABLET_TYPE.toString();
|
||||
private static final Query.ExecuteOptions.IncludedFields ALL_FIELDS = Query.ExecuteOptions.IncludedFields.ALL;
|
||||
private static final String OPTIONS_ALL_FIELDS_ECHO = "included_fields:" + ALL_FIELDS.toString() + " ";
|
||||
private static final String OPTIONS_ALL_FIELDS_ECHO =
|
||||
"included_fields:" + ALL_FIELDS.toString() + " ";
|
||||
|
||||
private static final ImmutableMap<String, Object> BIND_VARS = new ImmutableMap.Builder<String, Object>()
|
||||
.put("int", 123)
|
||||
.put("float", 2.5)
|
||||
.put("bytes", new byte[] {1, 2, 3})
|
||||
.put("bytes", new byte[]{1, 2, 3})
|
||||
.build();
|
||||
private static final String BIND_VARS_ECHO =
|
||||
"map[bytes:type:VARBINARY value:\"\\001\\002\\003\" float:type:FLOAT64 value:\"2.5\" int:type:INT64 value:\"123\" ]";
|
||||
|
@ -187,9 +191,6 @@ public abstract class RpcClientTest {
|
|||
*
|
||||
* We will constantly execute the "GetSrvKeyspace" RPC and return when the binary responded
|
||||
* successfully.
|
||||
*
|
||||
* @throws SQLException
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
private void waitForVtgateclienttest() throws SQLException, InterruptedException {
|
||||
if (ready) {
|
||||
|
@ -213,7 +214,9 @@ public abstract class RpcClientTest {
|
|||
throw e;
|
||||
}
|
||||
|
||||
System.out.format("Waiting until vtgateclienttest is ready and responds (got exception: %s)\n", rootCause);
|
||||
System.out
|
||||
.format("Waiting until vtgateclienttest is ready and responds (got exception: %s)\n",
|
||||
rootCause);
|
||||
Thread.sleep(100 /* milliseconds */);
|
||||
waited = true;
|
||||
}
|
||||
|
@ -221,7 +224,8 @@ public abstract class RpcClientTest {
|
|||
|
||||
if (waited) {
|
||||
double waitTimeSeconds = (DateTime.now().getMillis() - start.getMillis()) / 1000.0;
|
||||
System.out.format("Had to wait %.1f second(s) until vtgateclienttest was ready.\n", waitTimeSeconds);
|
||||
System.out.format("Had to wait %.1f second(s) until vtgateclienttest was ready.\n",
|
||||
waitTimeSeconds);
|
||||
}
|
||||
ready = true;
|
||||
}
|
||||
|
@ -237,7 +241,8 @@ public abstract class RpcClientTest {
|
|||
Assert.assertEquals(NONTX_V3_SESSION_ECHO, echo.get("session"));
|
||||
|
||||
echo = getEcho(
|
||||
conn.executeShards(ctx, ECHO_PREFIX + QUERY, KEYSPACE, SHARDS, BIND_VARS, TABLET_TYPE, ALL_FIELDS));
|
||||
conn.executeShards(ctx, ECHO_PREFIX + QUERY, KEYSPACE, SHARDS, BIND_VARS, TABLET_TYPE,
|
||||
ALL_FIELDS));
|
||||
Assert.assertEquals(CALLER_ID_ECHO, echo.get("callerId"));
|
||||
Assert.assertEquals(ECHO_PREFIX + QUERY, echo.get("query"));
|
||||
Assert.assertEquals(KEYSPACE, echo.get("keyspace"));
|
||||
|
@ -307,7 +312,8 @@ public abstract class RpcClientTest {
|
|||
public void testEchoStreamExecute() throws Exception {
|
||||
Map<String, String> echo;
|
||||
|
||||
echo = getEcho(conn.streamExecute(ctx, ECHO_PREFIX + QUERY, BIND_VARS, TABLET_TYPE, ALL_FIELDS));
|
||||
echo = getEcho(
|
||||
conn.streamExecute(ctx, ECHO_PREFIX + QUERY, BIND_VARS, TABLET_TYPE, ALL_FIELDS));
|
||||
Assert.assertEquals(CALLER_ID_ECHO, echo.get("callerId"));
|
||||
Assert.assertEquals(ECHO_PREFIX + QUERY, echo.get("query"));
|
||||
Assert.assertEquals(BIND_VARS_ECHO, echo.get("bindVars"));
|
||||
|
@ -363,7 +369,8 @@ public abstract class RpcClientTest {
|
|||
tx = conn.begin(ctx);
|
||||
|
||||
echo = getEcho(
|
||||
tx.executeShards(ctx, ECHO_PREFIX + QUERY, KEYSPACE, SHARDS, BIND_VARS, TABLET_TYPE, ALL_FIELDS));
|
||||
tx.executeShards(ctx, ECHO_PREFIX + QUERY, KEYSPACE, SHARDS, BIND_VARS, TABLET_TYPE,
|
||||
ALL_FIELDS));
|
||||
Assert.assertEquals(CALLER_ID_ECHO, echo.get("callerId"));
|
||||
Assert.assertEquals(ECHO_PREFIX + QUERY, echo.get("query"));
|
||||
Assert.assertEquals(KEYSPACE, echo.get("keyspace"));
|
||||
|
@ -462,7 +469,7 @@ public abstract class RpcClientTest {
|
|||
123,
|
||||
1000,
|
||||
Algorithm.FULL_SCAN)
|
||||
.get(0);
|
||||
.get(0);
|
||||
Assert.assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
|
@ -471,8 +478,8 @@ public abstract class RpcClientTest {
|
|||
SrvKeyspace expected = SrvKeyspace.newBuilder()
|
||||
.addPartitions(KeyspacePartition.newBuilder().setServedType(TabletType.REPLICA)
|
||||
.addShardReferences(ShardReference.newBuilder().setName("shard0").setKeyRange(KeyRange
|
||||
.newBuilder().setStart(ByteString.copyFrom(new byte[] {0x40, 0, 0, 0, 0, 0, 0, 0}))
|
||||
.setEnd(ByteString.copyFrom(new byte[] {(byte) 0x80, 0, 0, 0, 0, 0, 0, 0})).build())
|
||||
.newBuilder().setStart(ByteString.copyFrom(new byte[]{0x40, 0, 0, 0, 0, 0, 0, 0}))
|
||||
.setEnd(ByteString.copyFrom(new byte[]{(byte) 0x80, 0, 0, 0, 0, 0, 0, 0})).build())
|
||||
.build())
|
||||
.build())
|
||||
.setShardingColumnName("sharding_column_name")
|
||||
|
@ -484,6 +491,7 @@ public abstract class RpcClientTest {
|
|||
}
|
||||
|
||||
abstract static class Executable {
|
||||
|
||||
abstract void execute(String query) throws Exception;
|
||||
}
|
||||
|
||||
|
@ -521,6 +529,7 @@ public abstract class RpcClientTest {
|
|||
}
|
||||
|
||||
abstract static class TransactionExecutable {
|
||||
|
||||
abstract void execute(VTGateBlockingTx tx, String query) throws Exception;
|
||||
}
|
||||
|
||||
|
@ -602,7 +611,8 @@ public abstract class RpcClientTest {
|
|||
checkExecuteErrors(new Executable() {
|
||||
@Override
|
||||
void execute(String query) throws Exception {
|
||||
conn.executeKeyspaceIds(ctx, query, KEYSPACE, KEYSPACE_IDS, BIND_VARS, TABLET_TYPE, ALL_FIELDS);
|
||||
conn.executeKeyspaceIds(ctx, query, KEYSPACE, KEYSPACE_IDS, BIND_VARS, TABLET_TYPE,
|
||||
ALL_FIELDS);
|
||||
}
|
||||
});
|
||||
checkExecuteErrors(new Executable() {
|
||||
|
@ -647,20 +657,23 @@ public abstract class RpcClientTest {
|
|||
checkStreamExecuteErrors(new Executable() {
|
||||
@Override
|
||||
void execute(String query) throws Exception {
|
||||
conn.streamExecuteShards(ctx, query, KEYSPACE, SHARDS, BIND_VARS, TABLET_TYPE, ALL_FIELDS).next();
|
||||
}
|
||||
});
|
||||
checkStreamExecuteErrors(new Executable() {
|
||||
@Override
|
||||
void execute(String query) throws Exception {
|
||||
conn.streamExecuteKeyspaceIds(ctx, query, KEYSPACE, KEYSPACE_IDS, BIND_VARS, TABLET_TYPE, ALL_FIELDS)
|
||||
conn.streamExecuteShards(ctx, query, KEYSPACE, SHARDS, BIND_VARS, TABLET_TYPE, ALL_FIELDS)
|
||||
.next();
|
||||
}
|
||||
});
|
||||
checkStreamExecuteErrors(new Executable() {
|
||||
@Override
|
||||
void execute(String query) throws Exception {
|
||||
conn.streamExecuteKeyRanges(ctx, query, KEYSPACE, KEY_RANGES, BIND_VARS, TABLET_TYPE, ALL_FIELDS)
|
||||
conn.streamExecuteKeyspaceIds(ctx, query, KEYSPACE, KEYSPACE_IDS, BIND_VARS, TABLET_TYPE,
|
||||
ALL_FIELDS)
|
||||
.next();
|
||||
}
|
||||
});
|
||||
checkStreamExecuteErrors(new Executable() {
|
||||
@Override
|
||||
void execute(String query) throws Exception {
|
||||
conn.streamExecuteKeyRanges(ctx, query, KEYSPACE, KEY_RANGES, BIND_VARS, TABLET_TYPE,
|
||||
ALL_FIELDS)
|
||||
.next();
|
||||
}
|
||||
});
|
||||
|
@ -683,7 +696,8 @@ public abstract class RpcClientTest {
|
|||
checkTransactionExecuteErrors(new TransactionExecutable() {
|
||||
@Override
|
||||
void execute(VTGateBlockingTx tx, String query) throws Exception {
|
||||
tx.executeKeyspaceIds(ctx, query, KEYSPACE, KEYSPACE_IDS, BIND_VARS, TABLET_TYPE, ALL_FIELDS);
|
||||
tx.executeKeyspaceIds(ctx, query, KEYSPACE, KEYSPACE_IDS, BIND_VARS, TABLET_TYPE,
|
||||
ALL_FIELDS);
|
||||
}
|
||||
});
|
||||
checkTransactionExecuteErrors(new TransactionExecutable() {
|
||||
|
@ -703,7 +717,8 @@ public abstract class RpcClientTest {
|
|||
@Override
|
||||
void execute(VTGateBlockingTx tx, String query) throws Exception {
|
||||
tx.executeBatchShards(ctx,
|
||||
Arrays.asList(Proto.bindShardQuery(KEYSPACE, SHARDS, query, BIND_VARS)), TABLET_TYPE, ALL_FIELDS);
|
||||
Arrays.asList(Proto.bindShardQuery(KEYSPACE, SHARDS, query, BIND_VARS)), TABLET_TYPE,
|
||||
ALL_FIELDS);
|
||||
}
|
||||
});
|
||||
checkTransactionExecuteErrors(new TransactionExecutable() {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -21,6 +21,7 @@ import java.io.IOException;
|
|||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import vttest.Vttest.VTTestTopology;
|
||||
|
||||
|
@ -28,6 +29,7 @@ import vttest.Vttest.VTTestTopology;
|
|||
* Helper class to hold the configurations for VtGate setup used in integration tests
|
||||
*/
|
||||
public class TestEnv {
|
||||
|
||||
private VTTestTopology topology;
|
||||
private String keyspace;
|
||||
private String outputPath;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -19,22 +19,25 @@ package io.vitess.client;
|
|||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import io.vitess.proto.Query;
|
||||
import io.vitess.proto.Topodata.TabletType;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.log4j.LogManager;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.Assert;
|
||||
import vttest.Vttest.VTTestTopology;
|
||||
|
||||
import io.vitess.proto.Query;
|
||||
import io.vitess.proto.Topodata.TabletType;
|
||||
|
||||
public class TestUtil {
|
||||
|
||||
static final Logger logger = LogManager.getLogger(TestUtil.class.getName());
|
||||
public static final String PROPERTY_KEY_CLIENT_TEST_ENV = "vitess.client.testEnv";
|
||||
public static final String PROPERTY_KEY_CLIENT_TEST_PORT = "vitess.client.testEnv.portName";
|
||||
|
@ -60,10 +63,12 @@ public class TestUtil {
|
|||
continue;
|
||||
}
|
||||
try {
|
||||
Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
Type mapType = new TypeToken<Map<String, Object>>() {
|
||||
}.getType();
|
||||
Map<String, Object> map = new Gson().fromJson(line, mapType);
|
||||
testEnv.setPythonScriptProcess(p);
|
||||
testEnv.setPort(((Double)map.get(System.getProperty(PROPERTY_KEY_CLIENT_TEST_PORT))).intValue());
|
||||
testEnv.setPort(
|
||||
((Double) map.get(System.getProperty(PROPERTY_KEY_CLIENT_TEST_PORT))).intValue());
|
||||
return;
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.error("JsonSyntaxException parsing setup command output: " + line, e);
|
||||
|
@ -114,7 +119,7 @@ public class TestUtil {
|
|||
// Dial timeout
|
||||
Context ctx = Context.getDefault().withDeadlineAfter(Duration.millis(5000));
|
||||
return new VTGateBlockingConn(
|
||||
getRpcClientFactory().create(ctx, "localhost:" + testEnv.getPort()),
|
||||
getRpcClientFactory().create(ctx, "localhost:" + testEnv.getPort()),
|
||||
testEnv.getKeyspace());
|
||||
}
|
||||
|
||||
|
@ -132,7 +137,8 @@ public class TestUtil {
|
|||
bindVars.put("name", "name_" + id);
|
||||
bindVars.put("age", id % 10);
|
||||
bindVars.put("percent", id / 100.0);
|
||||
tx.execute(ctx, insertSql, bindVars, TabletType.MASTER, Query.ExecuteOptions.IncludedFields.ALL);
|
||||
tx.execute(ctx, insertSql, bindVars, TabletType.MASTER,
|
||||
Query.ExecuteOptions.IncludedFields.ALL);
|
||||
}
|
||||
tx.commit(ctx);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -18,6 +18,11 @@ package io.vitess.client.cursor;
|
|||
|
||||
import com.google.common.primitives.UnsignedLong;
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import io.vitess.proto.Query;
|
||||
import io.vitess.proto.Query.Field;
|
||||
import io.vitess.proto.Query.QueryResult;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.Date;
|
||||
|
@ -28,17 +33,15 @@ import java.util.Arrays;
|
|||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import io.vitess.proto.Query;
|
||||
import io.vitess.proto.Query.Field;
|
||||
import io.vitess.proto.Query.QueryResult;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class CursorTest {
|
||||
|
||||
private static final Calendar GMT = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
|
||||
@Test
|
||||
|
@ -58,16 +61,20 @@ public class CursorTest {
|
|||
@Test
|
||||
public void testFindColumnAlternateIndexes() throws Exception {
|
||||
try (Cursor cursor = new SimpleCursor(
|
||||
QueryResult.newBuilder().addFields(Field.newBuilder().setName("col1").setTable("Table1").build())
|
||||
.addFields(Field.newBuilder().setName("myAlias").setOrgName("boringColName").setTable("Table2").build())
|
||||
.build())) {
|
||||
QueryResult.newBuilder()
|
||||
.addFields(Field.newBuilder().setName("col1").setTable("Table1").build())
|
||||
.addFields(
|
||||
Field.newBuilder().setName("myAlias").setOrgName("boringColName").setTable("Table2")
|
||||
.build())
|
||||
.build())) {
|
||||
Assert.assertEquals(1, cursor.findColumn("Table1.col1"));
|
||||
Assert.assertEquals(1, cursor.findColumn("Table1.Col1"));
|
||||
Assert.assertEquals(2, cursor.findColumn("myAlias"));
|
||||
Assert.assertEquals(2, cursor.findColumn("Table2.myAlias"));
|
||||
Assert.assertEquals(2, cursor.findColumn("boringColName"));
|
||||
try {
|
||||
int idx = cursor.findColumn("Table2.boringColName"); // don't do what mysql-connector-j doesn't do
|
||||
int idx = cursor
|
||||
.findColumn("Table2.boringColName"); // don't do what mysql-connector-j doesn't do
|
||||
Assert.fail("no exception thrown for findColumn(\"Table2.boringColName\")");
|
||||
} catch (Exception ex) {
|
||||
Assert.assertEquals(SQLDataException.class, ex.getClass());
|
||||
|
@ -103,9 +110,10 @@ public class CursorTest {
|
|||
try (Cursor cursor = new SimpleCursor(QueryResult.newBuilder()
|
||||
.addFields(Field.newBuilder().setName("col1").setType(Query.Type.UINT64).build())
|
||||
.addFields(Field.newBuilder().setName("null").setType(Query.Type.UINT64).build())
|
||||
.addRows(Query.Row.newBuilder().addLengths("18446744073709551615".length()).addLengths(-1) // SQL
|
||||
// NULL
|
||||
.setValues(ByteString.copyFromUtf8("18446744073709551615")))
|
||||
.addRows(
|
||||
Query.Row.newBuilder().addLengths("18446744073709551615".length()).addLengths(-1) // SQL
|
||||
// NULL
|
||||
.setValues(ByteString.copyFromUtf8("18446744073709551615")))
|
||||
.build())) {
|
||||
Row row = cursor.next();
|
||||
Assert.assertNotNull(row);
|
||||
|
@ -121,9 +129,10 @@ public class CursorTest {
|
|||
try (Cursor cursor = new SimpleCursor(QueryResult.newBuilder()
|
||||
.addFields(Field.newBuilder().setName("col1").setType(Query.Type.UINT64).build())
|
||||
.addFields(Field.newBuilder().setName("null").setType(Query.Type.UINT64).build())
|
||||
.addRows(Query.Row.newBuilder().addLengths("18446744073709551615".length()).addLengths(-1) // SQL
|
||||
// NULL
|
||||
.setValues(ByteString.copyFromUtf8("18446744073709551615")))
|
||||
.addRows(
|
||||
Query.Row.newBuilder().addLengths("18446744073709551615".length()).addLengths(-1) // SQL
|
||||
// NULL
|
||||
.setValues(ByteString.copyFromUtf8("18446744073709551615")))
|
||||
.build())) {
|
||||
Row row = cursor.next();
|
||||
Assert.assertNotNull(row);
|
||||
|
@ -222,7 +231,7 @@ public class CursorTest {
|
|||
.addFields(Field.newBuilder().setName("col1").setType(type).build())
|
||||
.addFields(Field.newBuilder().setName("null").setType(type).build())
|
||||
.addRows(Query.Row.newBuilder().addLengths("2008-01-02".length()).addLengths(-1) // SQL
|
||||
// NULL
|
||||
// NULL
|
||||
.setValues(ByteString.copyFromUtf8("2008-01-02")))
|
||||
.build())) {
|
||||
Row row = cursor.next();
|
||||
|
@ -291,7 +300,7 @@ public class CursorTest {
|
|||
.addFields(Field.newBuilder().setName("col1").setType(type).build())
|
||||
.addFields(Field.newBuilder().setName("null").setType(type).build())
|
||||
.addRows(Query.Row.newBuilder().addLengths("hello world".length()).addLengths(-1) // SQL
|
||||
// NULL
|
||||
// NULL
|
||||
.setValues(ByteString.copyFromUtf8("hello world")))
|
||||
.build())) {
|
||||
Row row = cursor.next();
|
||||
|
@ -312,7 +321,7 @@ public class CursorTest {
|
|||
.addFields(Field.newBuilder().setName("col1").setType(type).build())
|
||||
.addFields(Field.newBuilder().setName("null").setType(type).build())
|
||||
.addRows(Query.Row.newBuilder().addLengths("1234.56789".length()).addLengths(-1) // SQL
|
||||
// NULL
|
||||
// NULL
|
||||
.setValues(ByteString.copyFromUtf8("1234.56789")))
|
||||
.build())) {
|
||||
Row row = cursor.next();
|
||||
|
@ -376,9 +385,9 @@ public class CursorTest {
|
|||
public void testGetBinaryInputStream() throws Exception {
|
||||
ByteString travel = ByteString.copyFromUtf8("მოგზაურობა");
|
||||
try (Cursor cursor = new SimpleCursor(QueryResult.newBuilder()
|
||||
.addFields(Field.newBuilder().setName("col1").setType(Query.Type.INT32).build())
|
||||
.addRows(Query.Row.newBuilder().addLengths(travel.size()).setValues(travel))
|
||||
.build())) {
|
||||
.addFields(Field.newBuilder().setName("col1").setType(Query.Type.INT32).build())
|
||||
.addRows(Query.Row.newBuilder().addLengths(travel.size()).setValues(travel))
|
||||
.build())) {
|
||||
Row row = cursor.next();
|
||||
Assert.assertNotNull(row);
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
|
@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
|
|||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
|
@ -27,12 +28,14 @@ import java.util.Calendar;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class DateTimeTest {
|
||||
|
||||
private static final Calendar GMT = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
|
||||
private static final Calendar PST = Calendar.getInstance(TimeZone.getTimeZone("GMT-8"));
|
||||
private static final Calendar IST = Calendar.getInstance(TimeZone.getTimeZone("GMT+0530"));
|
||||
|
|
23
java/pom.xml
23
java/pom.xml
|
@ -70,6 +70,7 @@
|
|||
<grpc.version>1.16.0</grpc.version>
|
||||
<protobuf.java.version>3.6.1</protobuf.java.version>
|
||||
<protobuf.protoc.version>3.6.1</protobuf.protoc.version>
|
||||
<checkstyle.plugin.version>3.0.0</checkstyle.plugin.version>
|
||||
</properties>
|
||||
|
||||
<!-- Add new dependencies here and then add it below or in your module. -->
|
||||
|
@ -253,6 +254,28 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>${checkstyle.plugin.version}</version>
|
||||
<configuration>
|
||||
<configLocation>google_checks.xml</configLocation>
|
||||
<suppressionsLocation>checkstyle-suppression.xml</suppressionsLocation>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
<failsOnError>false</failsOnError>
|
||||
<failOnViolation>true</failOnViolation>
|
||||
<violationSeverity>warning</violationSeverity>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>validate</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче