io.snappydata.thrift.server.SnappyDataServiceImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of snappydata-store-core Show documentation
Show all versions of snappydata-store-core Show documentation
TIBCO ComputeDB store based off Pivotal GemFireXD
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License. See accompanying
* LICENSE file.
*/
/*
* Changes for SnappyData data platform.
*
* Portions Copyright (c) 2018 SnappyData, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License. See accompanying
* LICENSE file.
*/
package io.snappydata.thrift.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.sql.*;
import java.sql.Date;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import com.gemstone.gemfire.InternalGemFireError;
import com.gemstone.gemfire.SystemFailure;
import com.gemstone.gemfire.cache.TransactionFlag;
import com.gemstone.gemfire.internal.concurrent.ConcurrentTLongObjectHashMap;
import com.gemstone.gemfire.internal.shared.ClientSharedUtils;
import com.gemstone.gemfire.internal.shared.SystemProperties;
import com.gemstone.gemfire.internal.size.ReflectionSingleObjectSizer;
import com.gemstone.gnu.trove.THashMap;
import com.gemstone.gnu.trove.TObjectProcedure;
import com.pivotal.gemfirexd.Attribute;
import com.pivotal.gemfirexd.internal.engine.GfxdConstants;
import com.pivotal.gemfirexd.internal.engine.Misc;
import com.pivotal.gemfirexd.internal.engine.ddl.catalog.GfxdSystemProcedures;
import com.pivotal.gemfirexd.internal.engine.distributed.GfxdDistributionAdvisor;
import com.pivotal.gemfirexd.internal.engine.distributed.utils.GemFireXDUtils;
import com.pivotal.gemfirexd.internal.engine.sql.conn.GfxdHeapThresholdListener;
import com.pivotal.gemfirexd.internal.engine.store.GemFireStore;
import com.pivotal.gemfirexd.internal.iapi.jdbc.EngineConnection;
import com.pivotal.gemfirexd.internal.iapi.jdbc.EngineLOB;
import com.pivotal.gemfirexd.internal.iapi.jdbc.EnginePreparedStatement;
import com.pivotal.gemfirexd.internal.iapi.jdbc.EngineStatement;
import com.pivotal.gemfirexd.internal.iapi.jdbc.WrapperEngineBLOB;
import com.pivotal.gemfirexd.internal.iapi.jdbc.WrapperEngineCLOB;
import com.pivotal.gemfirexd.internal.iapi.reference.Property;
import com.pivotal.gemfirexd.internal.iapi.services.i18n.MessageService;
import com.pivotal.gemfirexd.internal.iapi.services.io.ApplicationObjectInputStream;
import com.pivotal.gemfirexd.internal.iapi.sql.ResultColumnDescriptor;
import com.pivotal.gemfirexd.internal.iapi.sql.StatementType;
import com.pivotal.gemfirexd.internal.iapi.sql.conn.LanguageConnectionContext;
import com.pivotal.gemfirexd.internal.iapi.sql.conn.StatementContext;
import com.pivotal.gemfirexd.internal.iapi.store.access.xa.XAXactId;
import com.pivotal.gemfirexd.internal.iapi.types.DataTypeDescriptor;
import com.pivotal.gemfirexd.internal.iapi.types.DataTypeUtilities;
import com.pivotal.gemfirexd.internal.iapi.types.TypeId;
import com.pivotal.gemfirexd.internal.iapi.util.IdUtil;
import com.pivotal.gemfirexd.internal.impl.jdbc.EmbedDatabaseMetaData;
import com.pivotal.gemfirexd.internal.impl.jdbc.EmbedResultSet;
import com.pivotal.gemfirexd.internal.impl.jdbc.EmbedResultSetMetaData;
import com.pivotal.gemfirexd.internal.impl.jdbc.Util;
import com.pivotal.gemfirexd.internal.jdbc.EmbedXAConnection;
import com.pivotal.gemfirexd.internal.jdbc.EmbeddedXADataSource40;
import com.pivotal.gemfirexd.internal.jdbc.InternalDriver;
import com.pivotal.gemfirexd.internal.shared.common.SharedUtils;
import com.pivotal.gemfirexd.internal.shared.common.error.ExceptionSeverity;
import com.pivotal.gemfirexd.internal.shared.common.reference.JDBC40Translation;
import com.pivotal.gemfirexd.internal.shared.common.reference.SQLState;
import io.snappydata.thrift.*;
import io.snappydata.thrift.RowIdLifetime;
import io.snappydata.thrift.common.BufferedBlob;
import io.snappydata.thrift.common.Converters;
import io.snappydata.thrift.common.OptimizedElementArray;
import io.snappydata.thrift.common.ThriftUtils;
import io.snappydata.thrift.internal.ClientBlob;
import io.snappydata.thrift.server.ConnectionHolder.ResultSetHolder;
import io.snappydata.thrift.server.ConnectionHolder.StatementHolder;
import org.apache.thrift.ProcessFunction;
import org.apache.thrift.TApplicationException;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TMessage;
import org.apache.thrift.protocol.TMessageType;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolUtil;
import org.apache.thrift.protocol.TType;
import org.apache.thrift.transport.TTransport;
/**
* Server-side implementation of thrift SnappyDataService (see snappydata.thrift).
*/
public final class SnappyDataServiceImpl extends LocatorServiceImpl implements
SnappyDataService.Iface {
final GfxdHeapThresholdListener thresholdListener;
final SecureRandom rand;
final ConcurrentTLongObjectHashMap connectionMap;
final ConcurrentTLongObjectHashMap statementMap;
final ConcurrentTLongObjectHashMap resultSetMap;
final ConcurrentHashMap clientTrackerMap;
final ConcurrentHashMap clientSocketTrackerMap;
final AtomicLong currentConnectionId;
final AtomicLong currentStatementId;
final AtomicLong currentCursorId;
volatile boolean recordStatementStartTime;
/**
* stores the current client's hostname + ID for a new openConnection
*/
static final ThreadLocal currentClientHostId = new ThreadLocal<>();
/**
* stores whether a closeConnection call has requested to also close the socket
*/
static final ThreadLocal closeClientSocket = new ThreadLocal<>();
private static final int INVALID_ID = snappydataConstants.INVALID_ID;
static final Converters.ObjectInputStreamCreator javaObjectCreator =
new Converters.ObjectInputStreamCreator() {
@Override
public ObjectInputStream create(InputStream stream) throws IOException {
return new ApplicationObjectInputStream(
stream, Misc.getMemStore().getDatabase().getClassFactory());
}
};
public SnappyDataServiceImpl(String address, int port) {
super(address, port);
final GemFireStore store = Misc.getMemStoreBooting();
this.thresholdListener = store.thresholdListener();
this.rand = new SecureRandom();
// Force the random generator to seed itself.
final byte[] someBytes = new byte[55];
this.rand.nextBytes(someBytes);
this.connectionMap = new ConcurrentTLongObjectHashMap<>();
this.statementMap = new ConcurrentTLongObjectHashMap<>();
this.resultSetMap = new ConcurrentTLongObjectHashMap<>();
this.clientTrackerMap = new ConcurrentHashMap<>();
this.clientSocketTrackerMap = new ConcurrentHashMap<>();
this.currentConnectionId = new AtomicLong(1);
this.currentStatementId = new AtomicLong(1);
this.currentCursorId = new AtomicLong(1);
this.recordStatementStartTime = false;
}
/**
* Custom Processor implementation to handle closeConnection by closing
* server-side connection cleanly.
*/
public static final class Processor extends
SnappyDataService.Processor {
private final SnappyDataServiceImpl inst;
private final HashMap> fnMap;
public Processor(SnappyDataServiceImpl inst) {
super(inst);
this.inst = inst;
this.fnMap = new HashMap<>(super.getProcessMapView());
}
@Override
public final boolean process(final TProtocol in, final TProtocol out)
throws TException {
final TMessage msg = in.readMessageBegin();
final ProcessFunction fn = fnMap.get(msg.name);
if (fn != null) {
fn.process(msg.seqid, in, out, this.inst);
Class> fnClass = fn.getClass();
// register socket for a client in its tracker on an openConnection
if (fnClass == SnappyDataService.Processor.openConnection.class) {
String clientHostId = currentClientHostId.get();
currentClientHostId.remove();
ClientTracker tracker = ClientTracker.addOrGetTracker(
clientHostId, inst);
if (tracker != null) {
tracker.addClientSocket(in.getTransport(), inst);
}
} else if (fnClass ==
SnappyDataService.Processor.closeConnection.class) {
Boolean closeSocket = closeClientSocket.get();
closeClientSocket.remove();
// terminate socket on receiving closeConnection with closeSocket=true
// direct class comparison like above should be the fastest way
return !(closeSocket != null && closeSocket);
}
return true;
} else {
TProtocolUtil.skip(in, TType.STRUCT);
in.readMessageEnd();
TApplicationException x = new TApplicationException(
TApplicationException.UNKNOWN_METHOD, "Invalid method name: '" +
msg.name + "'");
out.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION,
msg.seqid));
x.write(out);
out.writeMessageEnd();
out.getTransport().flush();
return true;
}
}
public void clientSocketClosed(final TTransport clientTransport) {
// deregister the socket for client and check if all sockets
// from that client have been closed
ClientTracker.removeClientSocket(clientTransport, inst);
}
}
/**
* {@inheritDoc}
*/
@Override
public ConnectionProperties openConnection(OpenConnectionArgs arguments)
throws SnappyException {
currentClientHostId.remove();
ConnectionHolder connHolder;
try {
Properties props = new Properties();
String clientHost = null, clientId = null;
boolean forXA = false;
if (arguments != null) {
if (arguments.isSetUserName()) {
props.put(Attribute.USERNAME_ATTR, arguments.getUserName());
}
if (arguments.isSetPassword()) {
props.put(Attribute.PASSWORD_ATTR, arguments.getPassword());
}
if (arguments.isSetProperties()) {
props.putAll(arguments.getProperties());
}
clientHost = arguments.getClientHostName();
clientId = arguments.getClientID();
forXA = arguments.isSetForXA() && arguments.isForXA();
}
final String protocol;
// default route-query to true on client connections
GemFireStore memStore = Misc.getMemStoreBooting();
if (memStore.isSnappyStore()) {
String routeQuery = props.getProperty(Attribute.ROUTE_QUERY);
if (routeQuery == null || routeQuery.isEmpty()) {
props.setProperty(Attribute.ROUTE_QUERY, "true");
} else if (!Boolean.parseBoolean(routeQuery) && memStore.isRLSEnabled()) {
throw Util.generateCsSQLException(SQLState.SECURITY_EXCEPTION_ENCOUNTERED,
null, new IllegalStateException("Row level security (" + Property.SNAPPY_ENABLE_RLS +
") does not allow smart connector mode or with route-query=false"));
}
protocol = Attribute.SNAPPY_PROTOCOL;
} else {
protocol = Attribute.PROTOCOL;
}
EngineConnection conn;
EmbedXAConnection xaConn;
// initialize an XAConnection if required
if (forXA) {
EmbeddedXADataSource40 ds = new EmbeddedXADataSource40();
String user = null, password = null;
if (!props.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (String key : props.stringPropertyNames()) {
if (key.equalsIgnoreCase(Attribute.USERNAME_ATTR) ||
key.equalsIgnoreCase(Attribute.USERNAME_ALT_ATTR)) {
user = props.getProperty(key);
continue;
} else if (key.equalsIgnoreCase(Attribute.PASSWORD_ATTR)) {
password = props.getProperty(key);
continue;
}
if (sb.length() > 0) {
sb.append(';');
}
sb.append(key).append('=').append(props.getProperty(key));
}
if (sb.length() > 0) {
ds.setConnectionAttributes(sb.toString());
}
}
if (user != null) {
xaConn = (EmbedXAConnection)ds.getXAConnection(user, password);
} else {
xaConn = (EmbedXAConnection)ds.getXAConnection();
}
conn = (EngineConnection)xaConn.getConnection();
// autocommit has to be false for XA connections
xaConn.checkAutoCommit(false);
conn.setAutoCommit(false);
// set RC isolation level by default
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
} else {
conn = (EngineConnection)InternalDriver.activeDriver()
.connect(protocol, props, Converters.getJdbcIsolation(
snappydataConstants.DEFAULT_TRANSACTION_ISOLATION));
conn.setAutoCommit(snappydataConstants.DEFAULT_AUTOCOMMIT);
xaConn = null;
}
while (true) {
final long connId = getNextId(this.currentConnectionId);
connHolder = new ConnectionHolder(conn, xaConn, arguments, connId,
props, this.rand);
if (this.connectionMap.putIfAbsent(connId, connHolder) == null) {
ConnectionProperties connProps = new ConnectionProperties(connId,
clientHost, clientId);
connProps.setToken(connHolder.getToken());
connProps.setDefaultSchema(conn.getCurrentSchemaName());
// setup tracker for this client and put in ThreadLocal so that
// processor can make the entry for current TSocket
final String clientHostId = connHolder.getClientHostId();
ClientTracker tracker = ClientTracker.addOrGetTracker(clientHostId,
this);
if (tracker != null) {
tracker.addClientConnection(connId);
currentClientHostId.set(clientHostId);
}
return connProps;
}
}
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public void closeConnection(long connId, boolean closeSocket,
ByteBuffer token) {
try {
ConnectionHolder connHolder = this.connectionMap.getPrimitive(connId);
if (connHolder != null) {
if (connHolder.sameToken(token)) {
connHolder.close(this, false);
this.connectionMap.removePrimitive(connId);
// also remove from client tracker map
ClientTracker tracker = ClientTracker.addOrGetTracker(
connHolder.getClientHostId(), this);
if (tracker != null) {
tracker.removeClientConnection(connId);
}
closeClientSocket.set(closeSocket);
} else {
throw tokenMismatchException(token, "closeConnection [connId="
+ connId + ']');
}
}
} catch (Throwable t) {
if (!ignoreNonFatalException(t)) {
logger.info("Unexpected exception in closeConnection for CONNID=" +
connId, t);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void bulkClose(List entities) {
if (entities == null) {
return;
}
for (EntityId entity : entities) {
try {
long id = entity.id;
ByteBuffer token = entity.token;
switch (entity.type) {
case snappydataConstants.BULK_CLOSE_RESULTSET:
closeResultSet(id, token);
break;
case snappydataConstants.BULK_CLOSE_LOB:
freeLob(entity.connId, id, token);
break;
case snappydataConstants.BULK_CLOSE_STATEMENT:
closeStatement(id, token);
break;
case snappydataConstants.BULK_CLOSE_CONNECTION:
closeConnection(id, false, token);
break;
default:
// ignore; this is a oneway function
break;
}
} catch (SnappyException se) {
if (SQLState.NET_CONNECT_AUTH_FAILED.substring(0, 5).equals(
se.getExceptionData().getSqlState())) {
logger.warn("AUTH exception in bulkClose (continuing with other " +
"close operations): " + se);
}
} catch (Throwable t) {
if (!ignoreNonFatalException(t)) {
logger.info("Unexpected exception in bulkClose (continuing with other " +
"close operations): " + t);
}
}
}
}
void forceCloseConnection(long connId) {
try {
// remove upfront from map in any case
ConnectionHolder connHolder = (ConnectionHolder)this.connectionMap
.removePrimitive(connId);
if (connHolder != null) {
connHolder.close(this, true);
}
} catch (Throwable t) {
checkSystemFailure(t);
logger.info("Unexpected exception in forceCloseConnection for CONNID=" +
connId, t);
}
}
@Override
public void stop() {
// first close all open client connections being served by this instance
try {
final long[] connIds = this.connectionMap.keys();
for (long connId : connIds) {
ConnectionHolder connHolder = (ConnectionHolder)this.connectionMap
.removePrimitive(connId);
if (connHolder != null) {
try {
connHolder.close(this, true);
} catch (Throwable t) {
checkSystemFailure(t);
logger.info("Unexpected exception in connection close in stop " +
"for CONNID=" + connId, t);
}
}
}
} catch (Throwable t) {
checkSystemFailure(t);
logger.info("Unexpected exception in stop", t);
}
super.stop();
}
/**
* Helper method Validate the connection Id. If Id not found in the map throw
* the connection unavailable exception.
*
* @return ConnectionHolder if found in the map.
*/
private ConnectionHolder getValidConnection(long connId, ByteBuffer token)
throws SnappyException {
ConnectionHolder connHolder = this.connectionMap.getPrimitive(connId);
if (connHolder != null) {
if (connHolder.sameToken(token)) {
return connHolder;
} else {
throw tokenMismatchException(token, "getConnection [connId=" + connId
+ ']');
}
} else {
SnappyExceptionData exData = new SnappyExceptionData();
exData.setReason("No connection with ID="
+ ConnectionHolder.getTokenAsString(token));
exData.setSqlState(SQLState.NO_CURRENT_CONNECTION);
exData.setErrorCode(ExceptionSeverity.STATEMENT_SEVERITY);
throw new SnappyException(exData, getServerInfo());
}
}
private XAResource getXAResource(ConnectionHolder connHolder)
throws SQLException, XAException {
EmbedXAConnection xaConn;
if ((xaConn = connHolder.getXAConnection()) != null) {
return xaConn.getXAResource();
} else {
throw new XAException(XAException.XAER_PROTO);
}
}
private StatementHolder getStatement(ByteBuffer token, long stmtId,
boolean isPrepared, String op) throws SnappyException {
StatementHolder stmtHolder;
if ((stmtHolder = this.statementMap.getPrimitive(stmtId)) != null) {
if (stmtHolder.getConnectionHolder().sameToken(token)) {
if (!isPrepared
|| stmtHolder.getStatement() instanceof PreparedStatement) {
return stmtHolder;
} else {
throw statementNotFoundException(stmtId, op, true);
}
} else {
throw tokenMismatchException(token, op);
}
} else {
throw statementNotFoundException(stmtId, op, isPrepared);
}
}
private StatementHolder getStatementForResultSet(ByteBuffer token,
long cursorId, String op) throws SnappyException {
StatementHolder stmtHolder;
if ((stmtHolder = this.resultSetMap.getPrimitive(cursorId)) != null) {
if (stmtHolder.getConnectionHolder().sameToken(token)) {
return stmtHolder;
} else {
throw tokenMismatchException(token, op);
}
} else {
throw resultSetNotFoundException(cursorId, op);
}
}
SnappyException tokenMismatchException(ByteBuffer token, final String op) {
SnappyExceptionData exData = new SnappyExceptionData();
String message = token != null && token.hasRemaining()
? "connection token " + ConnectionHolder.getTokenAsString(token) +
" mismatch for operation " + op
: "No connection token passed for operation " + op;
exData.setReason(MessageService.getTextMessage(
SQLState.NET_CONNECT_AUTH_FAILED, message));
exData.setSqlState(SQLState.NET_CONNECT_AUTH_FAILED.substring(0, 5));
exData.setErrorCode(ExceptionSeverity.SESSION_SEVERITY);
return new SnappyException(exData, getServerInfo());
}
SnappyException resultSetNotFoundException(long cursorId, String op) {
SnappyExceptionData exData = new SnappyExceptionData();
exData.setReason("No result set open with ID=" + cursorId
+ " for operation " + op);
exData.setSqlState(SQLState.LANG_RESULT_SET_NOT_OPEN.substring(0, 5));
exData.setErrorCode(ExceptionSeverity.STATEMENT_SEVERITY);
return new SnappyException(exData, getServerInfo());
}
SnappyException statementNotFoundException(long stmtId, String op,
boolean isPrepared) {
SnappyExceptionData exData = new SnappyExceptionData();
exData.setReason("No " + (isPrepared ? "prepared " : "")
+ "statement with ID=" + stmtId + " for operation " + op);
exData.setSqlState(SQLState.LANG_DEAD_STATEMENT);
exData.setErrorCode(ExceptionSeverity.STATEMENT_SEVERITY);
return new SnappyException(exData, getServerInfo());
}
@Override
protected String getServerInfo() {
return "Server=" + this.hostAddress + '[' + this.hostPort + "] Thread="
+ Thread.currentThread().getName();
}
private static long getNextId(final AtomicLong id) {
while (true) {
long currentId = id.get();
long nextId = currentId + 1;
if (nextId == INVALID_ID) {
nextId++;
}
if (id.compareAndSet(currentId, nextId)) {
return currentId;
}
}
}
/**
* Returns the resultType if set if not set returns the default
* java.sql.ResultSet.TYPE_FORWARD_ONLY
*/
static int getResultType(StatementAttrs attrs) {
int rsType;
if (attrs != null && attrs.isSetResultSetType()) {
rsType = attrs.getResultSetType();
} else {
rsType = snappydataConstants.DEFAULT_RESULTSET_TYPE;
}
switch (rsType) {
case snappydataConstants.RESULTSET_TYPE_FORWARD_ONLY:
return ResultSet.TYPE_FORWARD_ONLY;
case snappydataConstants.RESULTSET_TYPE_INSENSITIVE:
return ResultSet.TYPE_SCROLL_INSENSITIVE;
case snappydataConstants.RESULTSET_TYPE_SENSITIVE:
return ResultSet.TYPE_SCROLL_SENSITIVE;
default:
throw new InternalGemFireError("unknown resultSet type "
+ attrs.getResultSetType());
}
}
/**
* Returns the Concurrency associated with the statement if set the the input
* object if not set by default returns java.sql.ResultSet.CONCUR_READ_ONLY
*/
static int getResultSetConcurrency(StatementAttrs attrs) {
return attrs != null && attrs.isSetUpdatable() && attrs.isUpdatable()
? ResultSet.CONCUR_UPDATABLE : ResultSet.CONCUR_READ_ONLY;
}
/**
* Returns the Holdability associated with the statement if set the the input
* object if not set by default returns #ResultS
*/
static int getResultSetHoldability(StatementAttrs attrs) {
return attrs != null && attrs.isSetHoldCursorsOverCommit()
&& attrs.isHoldCursorsOverCommit() ? ResultSet.HOLD_CURSORS_OVER_COMMIT
: ResultSet.CLOSE_CURSORS_AT_COMMIT;
}
private static BlobChunk getAsLastChunk(Blob blob, int length)
throws SQLException {
if (blob instanceof BufferedBlob) {
return ((BufferedBlob)blob).getAsLastChunk();
} else {
return new BlobChunk(ByteBuffer.wrap(blob.getBytes(1, length)), true);
}
}
private BlobChunk handleBlob(Blob blob, ConnectionHolder connHolder,
StatementAttrs attrs) throws SQLException {
final long length = blob.length();
if (length > Integer.MAX_VALUE) {
throw Util.generateCsSQLException(SQLState.BLOB_TOO_LARGE_FOR_CLIENT,
Long.toString(length), Long.toString(Integer.MAX_VALUE));
}
BlobChunk chunk = new BlobChunk().setOffset(0).setTotalLength(length);
final int chunkSize;
if (attrs != null && attrs.isSetLobChunkSize()) {
chunkSize = attrs.lobChunkSize;
} else {
chunkSize = snappydataConstants.DEFAULT_LOB_CHUNKSIZE;
}
if (chunkSize > 0 && chunkSize < length) {
chunk.chunk = ByteBuffer.wrap(blob.getBytes(1, chunkSize));
chunk.setLast(false);
// need to add explicit mapping for the LOB in this case
long lobId;
if (blob instanceof EngineLOB) {
lobId = ((EngineLOB)blob).getLocator();
} else {
lobId = new WrapperEngineBLOB(connHolder.getConnection(), blob)
.getLocator();
}
chunk.setLobId(lobId);
} else {
chunk = getAsLastChunk(blob, (int)length);
blob.free();
}
return chunk;
}
private ClobChunk handleClob(Clob clob, ConnectionHolder connHolder,
StatementAttrs attrs) throws SQLException {
final long length = clob.length();
if (length > Integer.MAX_VALUE) {
throw Util.generateCsSQLException(SQLState.BLOB_TOO_LARGE_FOR_CLIENT,
Long.toString(length), Long.toString(Integer.MAX_VALUE));
}
ClobChunk chunk = new ClobChunk().setOffset(0)
.setTotalLength(length);
final int chunkSize;
if (attrs != null && attrs.isSetLobChunkSize()) {
chunkSize = attrs.lobChunkSize;
} else {
chunkSize = snappydataConstants.DEFAULT_LOB_CHUNKSIZE;
}
if (chunkSize > 0 && chunkSize < length) {
chunk.setChunk(clob.getSubString(1, chunkSize)).setLast(false);
// need to add explicit mapping for the LOB in this case
long lobId;
if (clob instanceof EngineLOB) {
lobId = ((EngineLOB)clob).getLocator();
} else {
lobId = new WrapperEngineCLOB(connHolder.getConnection(), clob)
.getLocator();
}
chunk.setLobId(lobId);
} else {
chunk.setChunk(clob.getSubString(1, (int)length)).setLast(true);
clob.free();
}
return chunk;
}
/**
* Set a column value in a Row.
*
* The java version of Row overrides the thrift one to make use of
* {@link OptimizedElementArray} to reduce overhead/objects while still
* keeping serialization compatible with thrift Row.
*
*/
private long setColumnValue(ResultSet rs, SnappyType colType,
int columnPosition, ConnectionHolder connHolder, StatementAttrs attrs,
Row result) throws SQLException {
final int index = columnPosition - 1;
switch (colType) {
case BOOLEAN:
boolean boolValue = rs.getBoolean(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setBoolean(index, boolValue);
return 1;
}
case TINYINT:
byte byteValue = rs.getByte(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setByte(index, byteValue);
return 1;
}
case SMALLINT:
short shortValue = rs.getShort(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setShort(index, shortValue);
return 2;
}
case INTEGER:
int intValue = rs.getInt(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setInt(index, intValue);
return 4;
}
case BIGINT:
long longValue = rs.getLong(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setLong(index, longValue);
return 8;
}
case FLOAT:
float fltValue = rs.getFloat(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setFloat(index, fltValue);
return 4;
}
case DOUBLE:
double dblValue = rs.getDouble(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setDouble(index, dblValue);
return 8;
}
case CHAR:
case VARCHAR:
case LONGVARCHAR:
String strValue = rs.getString(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setObject(index, strValue, colType);
return ((long)(ReflectionSingleObjectSizer.OBJECT_SIZE +
strValue.length())) << 1L;
}
case BLOB:
Blob blob = rs.getBlob(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
BlobChunk chunk = handleBlob(blob, connHolder, attrs);
result.setObject(index, chunk, SnappyType.BLOB);
return ReflectionSingleObjectSizer.OBJECT_SIZE * 3 +
chunk.chunk.limit() + 12 /* for remaining fields */;
}
case CLOB:
case JSON:
case SQLXML:
Clob clob = rs.getClob(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
ClobChunk chunk = handleClob(clob, connHolder, attrs);
result.setObject(index, chunk, colType);
return ReflectionSingleObjectSizer.OBJECT_SIZE * 3 +
chunk.chunk.length() + 12 /* for remaining fields */;
}
case DECIMAL:
BigDecimal bd = rs.getBigDecimal(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
if (connHolder.useStringForDecimal()) {
String s = bd.toPlainString();
result.setObject(index, s, SnappyType.VARCHAR);
return ((long)(ReflectionSingleObjectSizer.OBJECT_SIZE +
s.length())) << 1L;
} else {
result.setObject(index, bd, SnappyType.DECIMAL);
return ReflectionSingleObjectSizer.OBJECT_SIZE * 3 +
(bd.precision() << 2);
}
}
case DATE:
Date dtVal = rs.getDate(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setDateTime(index, dtVal);
return 8;
}
case TIME:
Time timeVal = rs.getTime(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setDateTime(index, timeVal);
return 8;
}
case TIMESTAMP:
java.sql.Timestamp tsVal = rs.getTimestamp(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setTimestamp(index, tsVal);
return 8;
}
case BINARY:
case VARBINARY:
case LONGVARBINARY:
byte[] byteArray = rs.getBytes(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setObject(index, byteArray, colType);
return ReflectionSingleObjectSizer.OBJECT_SIZE + byteArray.length;
}
case NULLTYPE:
result.setNull(index);
return 1;
case JAVA_OBJECT:
Object o = rs.getObject(columnPosition);
if (rs.wasNull()) {
result.setNull(index);
return 1;
} else {
result.setObject(index, new Converters.JavaObjectWrapper(
o, columnPosition), SnappyType.JAVA_OBJECT);
}
// hard-code some fixed size
return 128;
case ARRAY:
case MAP:
case STRUCT:
// TODO: proper implementation of above three
default:
throw Util.generateCsSQLException(SQLState.DATA_TYPE_NOT_SUPPORTED,
Util.typeName(Converters.getJdbcType(colType)));
}
}
/**
* Get an output column value from CallableStatement in a Row.
*
* The java version of Row overrides the thrift one to make use of
* {@link OptimizedElementArray} to reduce overhead/objects while still
* keeping serialization compatible with thrift Row.
*
*/
private ColumnValue getColumnValue(CallableStatement cstmt,
int paramPosition, int paramType, ConnectionHolder connHolder,
StatementAttrs attrs) throws SQLException {
ColumnValue cv = new ColumnValue();
switch (paramType) {
case Types.BOOLEAN:
boolean boolValue = cstmt.getBoolean(paramPosition);
if (boolValue || !cstmt.wasNull()) {
cv.setBool_val(boolValue);
break;
} else {
cv.setNull_val(true);
break;
}
case Types.TINYINT:
byte byteValue = cstmt.getByte(paramPosition);
if (byteValue != 0 || !cstmt.wasNull()) {
cv.setByte_val(byteValue);
break;
} else {
cv.setNull_val(true);
break;
}
case Types.SMALLINT:
short shortValue = cstmt.getShort(paramPosition);
if (shortValue != 0 || !cstmt.wasNull()) {
cv.setI16_val(shortValue);
break;
} else {
cv.setNull_val(true);
break;
}
case Types.INTEGER:
int intValue = cstmt.getInt(paramPosition);
if (intValue != 0 || !cstmt.wasNull()) {
cv.setI32_val(intValue);
break;
} else {
cv.setNull_val(true);
break;
}
case Types.BIGINT:
long longValue = cstmt.getLong(paramPosition);
if (longValue != 0 || !cstmt.wasNull()) {
cv.setI64_val(longValue);
break;
} else {
cv.setNull_val(true);
break;
}
case Types.REAL:
float fltValue = cstmt.getFloat(paramPosition);
if (fltValue != 0.0f || !cstmt.wasNull()) {
cv.setFloat_val(Float.floatToIntBits(fltValue));
break;
} else {
cv.setNull_val(true);
break;
}
case Types.DOUBLE:
// map JDBC FLOAT types to double since it can have precision
// more than what float can hold
case Types.FLOAT:
double dblValue = cstmt.getDouble(paramPosition);
if (dblValue != 0.0 || !cstmt.wasNull()) {
cv.setDouble_val(dblValue);
break;
} else {
cv.setNull_val(true);
break;
}
case Types.CHAR:
case Types.VARCHAR:
case Types.LONGVARCHAR:
case Types.NCHAR:
case Types.NVARCHAR:
case Types.LONGNVARCHAR:
String strValue = cstmt.getString(paramPosition);
if (strValue != null) {
cv.setString_val(strValue);
break;
} else {
cv.setNull_val(true);
break;
}
case Types.BLOB:
Blob blob = cstmt.getBlob(paramPosition);
if (blob != null) {
cv.setBlob_val(handleBlob(blob, connHolder, attrs));
break;
} else {
cv.setNull_val(true);
break;
}
case Types.CLOB:
case JDBC40Translation.JSON:
case Types.SQLXML:
Clob clob = cstmt.getClob(paramPosition);
if (clob != null) {
cv.setClob_val(handleClob(clob, connHolder, attrs));
break;
} else {
cv.setNull_val(true);
break;
}
case Types.DECIMAL:
case Types.NUMERIC:
BigDecimal bd = cstmt.getBigDecimal(paramPosition);
if (bd != null) {
if (connHolder.useStringForDecimal()) {
cv.setString_val(bd.toPlainString());
} else {
cv.setDecimal_val(Converters.getDecimal(bd));
}
break;
} else {
cv.setNull_val(true);
break;
}
case Types.DATE:
Date dtVal = cstmt.getDate(paramPosition);
if (dtVal != null) {
cv.setDate_val(Converters.getDateTime(dtVal));
break;
} else {
cv.setNull_val(true);
break;
}
case Types.TIME:
Time timeVal = cstmt.getTime(paramPosition);
if (timeVal != null) {
cv.setTime_val(Converters.getDateTime(timeVal));
break;
} else {
cv.setNull_val(true);
break;
}
case Types.TIMESTAMP:
java.sql.Timestamp tsVal = cstmt.getTimestamp(paramPosition);
if (tsVal != null) {
cv.setTimestamp_val(Converters.getTimestampNanos(tsVal));
break;
} else {
cv.setNull_val(true);
break;
}
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
byte[] byteArray = cstmt.getBytes(paramPosition);
if (byteArray != null) {
cv.setBinary_val(byteArray);
break;
} else {
cv.setNull_val(true);
break;
}
case Types.NULL:
cstmt.getObject(paramPosition);
cv.setNull_val(cstmt.wasNull());
break;
case Types.JAVA_OBJECT:
Object o = cstmt.getObject(paramPosition);
if (o == null) {
cv.setNull_val(true);
} else {
cv.setJava_val(Converters.getJavaObjectAsBytes(o, paramPosition));
}
break;
case Types.ARRAY:
case Types.STRUCT:
case JDBC40Translation.MAP:
// TODO: proper implementation of above three
default:
throw Util.generateCsSQLException(SQLState.DATA_TYPE_NOT_SUPPORTED,
Util.typeName(paramType));
}
return cv;
}
private ArrayList getRowSetMetaData(
final ResultSetMetaData rsmd, final int columnCount,
final boolean useStringForDecimal) throws SQLException {
final ArrayList descriptors =
new ArrayList<>(columnCount);
if (rsmd instanceof EmbedResultSetMetaData) {
// using somewhat more efficient methods for EmbedResultSetMetaData
final EmbedResultSetMetaData ersmd = (EmbedResultSetMetaData)rsmd;
final boolean tableReadOnly = ersmd.isTableReadOnly();
String columnName, schemaName, tableName, fullTableName;
String typeName, className;
int jdbcType, scale;
SnappyType type;
for (int colIndex = 1; colIndex <= columnCount; colIndex++) {
ResultColumnDescriptor rcd = ersmd.getColumnDescriptor(colIndex);
DataTypeDescriptor dtd = rcd.getType();
TypeId typeId = dtd.getTypeId();
jdbcType = typeId.getJDBCTypeId();
type = Converters.getThriftSQLType(jdbcType, useStringForDecimal);
ColumnDescriptor columnDesc = new ColumnDescriptor();
columnName = rcd.getName();
if (columnName != null) {
columnDesc.setName(columnName);
}
schemaName = rcd.getSourceSchemaName();
tableName = rcd.getSourceTableName();
fullTableName = schemaName != null ? (schemaName + '.' + tableName)
: tableName;
columnDesc.setFullTableName(fullTableName);
int nullable = DataTypeUtilities.isNullable(dtd);
if (nullable == ResultSetMetaData.columnNullable) {
columnDesc.setNullable(true);
} else if (nullable == ResultSetMetaData.columnNoNulls) {
columnDesc.setNullable(false);
}
if (!tableReadOnly) {
if (rcd.updatableByCursor()) {
columnDesc.setUpdatable(true);
}
if (ersmd.isDefiniteWritable_(colIndex)) {
columnDesc.setDefinitelyUpdatable(true);
}
}
if (rcd.isAutoincrement()) {
columnDesc.setAutoIncrement(true);
}
columnDesc.setType(type);
columnDesc
.setPrecision((short)DataTypeUtilities.getDigitPrecision(dtd));
scale = dtd.getScale();
if (scale != 0) {
columnDesc.setScale((short)scale);
}
if (jdbcType == Types.JAVA_OBJECT) {
typeName = typeId.getSQLTypeName();
className = typeId.getResultSetMetaDataTypeName();
columnDesc.setUdtTypeAndClassName(typeName
+ (className != null ? ":" + className : ""));
}
descriptors.add(columnDesc);
}
} else {
String schemaName, tableName, fullTableName;
String typeName, className;
String prevSchemaName = null, prevTableName = null;
String prevTypeName = null, prevClassName = null;
int jdbcType, scale;
for (int colIndex = 1; colIndex <= columnCount; colIndex++) {
ColumnDescriptor columnDesc = new ColumnDescriptor();
columnDesc.setName(rsmd.getColumnName(colIndex));
schemaName = rsmd.getSchemaName(colIndex);
tableName = rsmd.getTableName(colIndex);
if (colIndex > 1) {
if ((schemaName != null && !schemaName.equals(prevSchemaName))
|| (tableName != null && !tableName.equals(prevTableName))) {
fullTableName = schemaName != null ? (schemaName + '.' + tableName)
: tableName;
columnDesc.setFullTableName(fullTableName);
}
} else {
fullTableName = schemaName != null ? (schemaName + '.' + tableName)
: tableName;
columnDesc.setFullTableName(fullTableName);
}
prevSchemaName = schemaName;
prevTableName = tableName;
int nullable = rsmd.isNullable(colIndex);
if (nullable == ResultSetMetaData.columnNullable) {
columnDesc.setNullable(true);
} else if (nullable == ResultSetMetaData.columnNoNulls) {
columnDesc.setNullable(false);
}
if (rsmd.isDefinitelyWritable(colIndex)) {
columnDesc.setDefinitelyUpdatable(true);
}
if (rsmd.isWritable(colIndex)) {
columnDesc.setUpdatable(true);
}
if (rsmd.isAutoIncrement(colIndex)) {
columnDesc.setAutoIncrement(true);
}
jdbcType = rsmd.getColumnType(colIndex);
columnDesc.setType(Converters.getThriftSQLType(jdbcType,
useStringForDecimal));
columnDesc.setPrecision((short)rsmd.getPrecision(colIndex));
scale = rsmd.getScale(colIndex);
if (scale != 0) {
columnDesc.setScale((short)scale);
}
if (jdbcType == Types.JAVA_OBJECT) {
typeName = rsmd.getColumnTypeName(colIndex);
className = rsmd.getColumnClassName(colIndex);
if ((typeName != null && !typeName.equals(prevTypeName))
|| (className != null && !className.equals(prevClassName))) {
columnDesc.setUdtTypeAndClassName(typeName
+ (className != null ? ":" + className : ""));
}
prevTypeName = typeName;
prevClassName = className;
}
descriptors.add(columnDesc);
}
}
return descriptors;
}
/**
* Encapsulates the ResultSet of statement execution as thrift RowSet.
*/
private RowSet getRowSet(final Statement stmt, StatementHolder stmtHolder,
final ResultSet rs, long cursorId, ResultSetHolder holder,
final long connId, final StatementAttrs attrs, int offset,
final boolean offsetIsAbsolute, final boolean fetchReverse,
final int fetchSize, final ConnectionHolder connHolder,
final String sql) throws SnappyException {
boolean isLastBatch = true;
try {
RowSet result = createEmptyRowSet().setConnId(connId);
final boolean isForwardOnly = rs.getType() == ResultSet.TYPE_FORWARD_ONLY;
// first fill in the metadata
final ResultSetMetaData rsmd = rs.getMetaData();
final int columnCount = rsmd.getColumnCount();
final ArrayList descriptors = getRowSetMetaData(rsmd,
columnCount, connHolder.useStringForDecimal());
if (holder == null) { // skip sending descriptors for scrollCursor
result.setMetadata(descriptors);
}
// now fill in the values
final int batchSize;
final boolean moveForward = !fetchReverse &&
(attrs == null || !attrs.fetchReverse);
boolean hasMoreRows = false;
byte flags = 0;
long startTime = 0;
if (fetchSize > 0) {
batchSize = fetchSize;
} else if (attrs != null && attrs.isSetBatchSize()) {
batchSize = attrs.batchSize;
} else {
batchSize = snappydataConstants.DEFAULT_RESULTSET_BATCHSIZE;
}
if (offsetIsAbsolute) {
// +ve offset is 0, 1, ... while absolute numbers from 1
hasMoreRows = rs.absolute(offset >= 0 ? ++offset : offset);
if (!hasMoreRows) {
// check if cursor moved to before first or after last
if (offset > 0) {
flags |= snappydataConstants.ROWSET_AFTER_LAST;
// can still fetch after last in reverse
if (!moveForward) {
// move one previous to land on last row
hasMoreRows = rs.previous();
}
} else {
flags |= snappydataConstants.ROWSET_BEFORE_FIRST;
// can still fetch before first in forward
if (moveForward) {
// move one next to land on last row
hasMoreRows = rs.next();
}
}
}
// getRow() required for -ve offset
result.setOffset(Math.max(0, rs.getRow() - 1));
} else if (offset == 0) {
if (holder == null) { // first call to create result set
result.setOffset(0);
} else {
result.setOffset(holder.rsOffset);
}
} else {
hasMoreRows = rs.relative(offset);
if (!hasMoreRows) {
// check if cursor moved to before first or after last
if (offset > 0) {
flags |= snappydataConstants.ROWSET_AFTER_LAST;
// can still fetch after last in reverse
if (!moveForward) {
// move one previous to land on last row
hasMoreRows = rs.previous();
}
} else {
flags |= snappydataConstants.ROWSET_BEFORE_FIRST;
// can still fetch before first in forward
if (moveForward) {
// move one next to land on last row
hasMoreRows = rs.next();
}
}
}
result.setOffset(Math.max(0, rs.getRow() - 1));
}
final EngineConnection conn = connHolder.getConnection();
final List rows = result.getRows();
// empty row just to help in creating fast clones
final Row templateRow = new Row(descriptors);
EngineStatement estmt = null;
long estimatedSize = 0L;
int nrows = 0;
if (rs instanceof EmbedResultSet) {
final EmbedResultSet ers = (EmbedResultSet)rs;
estmt = (EngineStatement)stmt;
synchronized (conn.getConnectionSynchronization()) {
LanguageConnectionContext lcc = conn.getLanguageConnectionContext();
ers.setupContextStack(false);
ers.pushStatementContext(lcc, true);
try {
// skip the first move in case cursor was already positioned by an
// explicit call to absolute or relative
if (offset == 0) {
hasMoreRows = moveForward ? ers.lightWeightNext() : ers
.lightWeightPrevious();
}
while (hasMoreRows) {
Row eachRow = new Row(templateRow, true, true);
for (int colIndex = 1; colIndex <= columnCount; colIndex++) {
estimatedSize += setColumnValue(ers, descriptors.get(
colIndex - 1).type, colIndex, connHolder, attrs, eachRow);
}
rows.add(eachRow);
if (((++nrows) % GemFireXDUtils.DML_SAMPLE_INTERVAL) == 0) {
// throttle the processing and sends if CRITICAL_UP has been reached
if (throttleIfCritical()) {
isLastBatch = false;
break;
}
getCancelCriterion().checkCancelInProgress(null);
long currentTime = System.nanoTime();
if (startTime > 0) {
if ((currentTime - startTime) >=
(1000000L * GemFireXDUtils.DML_MAX_CHUNK_MILLIS)) {
isLastBatch = false;
break;
}
} else {
startTime = currentTime;
}
}
if (nrows >= batchSize ||
estimatedSize > GemFireXDUtils.DML_MAX_CHUNK_SIZE) {
isLastBatch = false;
break;
}
hasMoreRows = moveForward ? ers.lightWeightNext() : ers
.lightWeightPrevious();
}
} finally {
StatementContext context = lcc.getStatementContext();
if (lcc.getStatementDepth() > 0) {
lcc.popStatementContext(context, null);
} else if (context != null && context.inUse()) {
context.clearInUse();
}
ers.restoreContextStack();
}
}
} else {
if (offset == 0) {
hasMoreRows = moveForward ? rs.next() : rs.previous();
}
while (hasMoreRows) {
final Row eachRow = new Row(templateRow, true, true);
for (int colIndex = 1; colIndex <= columnCount; colIndex++) {
estimatedSize += setColumnValue(rs, descriptors.get(
colIndex - 1).type, colIndex, connHolder, attrs, eachRow);
}
rows.add(eachRow);
if (((++nrows) % GemFireXDUtils.DML_SAMPLE_INTERVAL) == 0) {
// throttle the processing and sends if CRITICAL_UP has been reached
if (throttleIfCritical()) {
isLastBatch = false;
break;
}
getCancelCriterion().checkCancelInProgress(null);
long currentTime = System.nanoTime();
if (startTime > 0) {
if ((currentTime - startTime) >=
(1000000L * GemFireXDUtils.DML_MAX_CHUNK_MILLIS)) {
isLastBatch = false;
break;
}
} else {
startTime = currentTime;
}
}
if (nrows >= batchSize ||
estimatedSize > GemFireXDUtils.DML_MAX_CHUNK_SIZE) {
isLastBatch = false;
break;
}
hasMoreRows = moveForward ? rs.next() : rs.previous();
}
}
/*
// reverse the list if traversing backwards (client always expects the
// results in forward order that it will traverse in reverse if required)
if (!moveForward) {
Collections.reverse(rows);
}
*/
if (isLastBatch) flags |= snappydataConstants.ROWSET_LAST_BATCH;
final boolean dynamicResults = estmt != null && estmt.hasDynamicResults();
if (dynamicResults) flags |= snappydataConstants.ROWSET_HAS_MORE_ROWSETS;
result.setFlags(flags);
fillWarnings(result, rs);
// send cursorId for scrollable, partial resultsets or open LOBs
if (isLastBatch && isForwardOnly && !conn.hasLOBs() &&
!dynamicResults) {
if (stmtHolder == null || cursorId == INVALID_ID) {
rs.close();
} else {
stmtHolder.closeResultSet(cursorId, this);
}
// setup the Statement for reuse
if (holder == null && estmt != null && !estmt.isPrepared()) {
connHolder.setStatementForReuse(estmt);
}
result.setCursorId(INVALID_ID);
result.setStatementId(INVALID_ID);
} else {
if (holder == null) {
cursorId = getNextId(this.currentCursorId);
if (stmtHolder == null) {
// upon creation the first ResultSet information will go into
// StatementHolder itself, hence (holder = stmtHolder) below
holder = stmtHolder = registerResultSet(cursorId, rs, connHolder,
stmt, attrs, sql);
} else {
holder = registerResultSet(cursorId, rs, stmtHolder);
}
}
result.setCursorId(cursorId);
result.setStatementId(stmtHolder.getStatementId());
// update the tracked resultset offset for next scrollCursor call
if (moveForward) {
holder.rsOffset = result.offset + rows.size();
} else {
holder.rsOffset = Math.max(0, result.offset - rows.size());
}
}
return result;
} catch (SQLException e) {
throw SnappyException(e);
}
}
private boolean isLast(RowSet rs) {
return rs == null || (rs.flags & snappydataConstants.ROWSET_LAST_BATCH) != 0;
}
private boolean throttleIfCritical() {
if (this.thresholdListener.isCritical()) {
// throttle the processing and sends
try {
for (int tries = 1; tries <= 5; tries++) {
Thread.sleep(4);
if (!this.thresholdListener.isCritical()) {
break;
}
}
} catch (InterruptedException ie) {
getCancelCriterion().checkCancelInProgress(ie);
}
return true;
} else {
return false;
}
}
/**
* Create an empty row set.
*/
private static RowSet createEmptyRowSet() {
RowSet rs = new RowSet();
rs.setRows(new ArrayList());
return rs;
}
/**
* Create an empty statement result.
*/
private StatementResult createEmptyStatementResult() {
return new StatementResult();
}
private StatementHolder registerResultSet(long cursorId, ResultSet rs,
ConnectionHolder connHolder, Statement stmt, StatementAttrs attrs,
String sql) {
final StatementHolder stmtHolder;
if (stmt != null) {
final long stmtId = getNextId(this.currentStatementId);
stmtHolder = connHolder.registerResultSet(stmt, attrs, stmtId, rs,
cursorId, sql, recordStatementStartTime);
this.statementMap.putPrimitive(stmtId, stmtHolder);
} else {
stmtHolder = connHolder.registerResultSet(null, null, INVALID_ID, rs,
cursorId, sql, recordStatementStartTime);
}
this.resultSetMap.putPrimitive(cursorId, stmtHolder);
return stmtHolder;
}
private ResultSetHolder registerResultSet(long cursorId, ResultSet rs,
StatementHolder stmtHolder) {
ResultSetHolder holder;
final long stmtId = stmtHolder.getStatementId();
if (stmtId != INVALID_ID) {
holder = stmtHolder.getConnectionHolder().registerResultSet(stmtHolder,
rs, cursorId);
this.statementMap.putPrimitive(stmtId, stmtHolder);
} else {
holder = stmtHolder.addResultSet(rs, cursorId);
}
this.resultSetMap.putPrimitive(cursorId, stmtHolder);
return holder;
}
private boolean processPendingTransactionAttributes(StatementAttrs attrs,
EngineConnection conn) throws SnappyException {
if (attrs == null) {
return false;
}
Map pendingTXAttrs = attrs
.getPendingTransactionAttrs();
if (pendingTXAttrs != null && !pendingTXAttrs.isEmpty()) {
beginOrAlterTransaction(conn, snappydataConstants.TRANSACTION_NO_CHANGE,
pendingTXAttrs, false);
}
if (attrs.possibleDuplicate) {
conn.setPossibleDuplicate(true);
return true;
} else {
return false;
}
}
private void cleanupResultSet(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
// ignored
}
}
}
private void cleanupStatement(EngineStatement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
// ignored
}
}
}
private void checkSystemFailure(Throwable t) {
final Error err;
if (t instanceof Error && SystemFailure.isJVMFailureError(
err = (Error)t)) {
SystemFailure.initiateFailure(err);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw err;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (see above). However, there is
// _still_ a possibility that you are dealing with a cascading
// error condition, so you also need to check to see if the JVM
// is still usable.
SystemFailure.checkFailure();
}
private boolean ignoreNonFatalException(Throwable t) {
checkSystemFailure(t);
// ignore for node going down
return GemFireXDUtils.nodeFailureException(t);
}
private StatementAttrs applyMergeAttributes(StatementAttrs source,
StatementAttrs target, EngineConnection conn, Statement stmt)
throws SQLException {
if (target == null) {
target = source;
} else if (source != null) {
// copy over the attributes used by getRowSet()
if (source.isSetBatchSize() && !target.isSetBatchSize()) {
target.setBatchSize(source.getBatchSize());
}
if (source.isSetFetchReverse() && !target.isSetFetchReverse()) {
target.setFetchReverse(source.isFetchReverse());
}
if (source.isSetLobChunkSize() && !target.isSetLobChunkSize()) {
target.setLobChunkSize(source.getLobChunkSize());
}
if (source.isSetBucketIds() && !target.isSetBucketIds()) {
target.setBucketIds(source.getBucketIds());
target.setBucketIdsTable(source.getBucketIdsTable());
}
if (source.isSetRetainBucketIds() && !target.isSetRetainBucketIds()) {
target.setRetainBucketIds(source.isRetainBucketIds());
}
if (source.isSetMetadataVersion() && !target.isSetMetadataVersion()) {
target.setMetadataVersion(source.getMetadataVersion());
}
if (source.isSetSnapshotTransactionId() && !target.isSetSnapshotTransactionId()) {
target.setSnapshotTransactionId(source.getSnapshotTransactionId());
}
}
if (target != null) {
// apply the attributes to statement and connection
if (stmt != null) {
if (target.isSetTimeout()) {
stmt.setQueryTimeout(target.getTimeout());
}
if (target.isSetMaxRows()) {
stmt.setMaxRows(target.getMaxRows());
}
if (target.isSetMaxFieldSize()) {
stmt.setMaxFieldSize(target.getMaxFieldSize());
}
if (target.isSetCursorName()) {
stmt.setCursorName(target.getCursorName());
}
}
if (target.isSetBucketIds()) {
GfxdSystemProcedures.setBucketsForLocalExecution(
target.getBucketIdsTable(), target.getBucketIds(),
target.isRetainBucketIds(), conn.getLanguageConnectionContext());
}
if (target.isSetMetadataVersion()) {
final GfxdDistributionAdvisor.GfxdProfile profile = GemFireXDUtils.
getGfxdProfile(Misc.getMyId());
final int actualVersion = profile.getRelationDestroyVersion();
final int metadataVersion = target.getMetadataVersion();
if (metadataVersion != -1 && actualVersion != metadataVersion) {
throw Util.generateCsSQLException(
SQLState.SNAPPY_RELATION_DESTROY_VERSION_MISMATCH);
}
}
if (target.isSetSnapshotTransactionId()) {
GfxdSystemProcedures.useSnapshotTXId(target.getSnapshotTransactionId(),
conn.getLanguageConnectionContext());
}
}
return target;
}
/**
* {@inheritDoc}
*/
@Override
public StatementResult execute(long connId, String sql,
Map outputParams, StatementAttrs attrs,
ByteBuffer token) throws SnappyException {
ConnectionHolder connHolder = null;
EngineConnection conn = null;
EngineStatement stmt = null;
ResultSet rs = null;
boolean posDup = false;
try {
connHolder = getValidConnection(connId, token);
conn = connHolder.getConnection();
final String initialDefaultSchema = conn.getCurrentSchemaName();
// first process any pending TX & other flags in StatementAttrs
posDup = processPendingTransactionAttributes(attrs, conn);
// Check if user has provided output parameters i.e. CallableStatement
if (outputParams != null && !outputParams.isEmpty()) {
// TODO: implement this case by adding support for output parameters
// to EngineStatement itself (actually outputParams is not required
// at all by the execution engine itself)
// Also take care of open LOBs so avoid closing statement
// for autocommit==true
throw notImplementedException("unprepared CALL with output");
} else { // Case : New statement Object
stmt = connHolder.createNewStatement(attrs);
}
StatementHolder stmtHolder = connHolder.newStatementHolder(stmt, attrs,
getNextId(this.currentStatementId), sql, recordStatementStartTime,
"EXECUTING");
connHolder.setActiveStatement(stmtHolder);
applyMergeAttributes(null, attrs, conn, stmt);
// Now we have valid statement object and valid connect id.
// Create new empty StatementResult.
StatementResult sr = createEmptyStatementResult();
RowSet rowSet;
if (stmt.execute(sql)) { // Case : result is a ResultSet
stmtHolder.setStatus("FILLING RESULT SET");
rs = stmt.getResultSet();
rowSet = getRowSet(stmt, stmtHolder, rs, INVALID_ID, null, connId,
attrs, 0, false, false, 0, connHolder, sql);
sr.setResultSet(rowSet);
} else { // Case : result is update count
stmtHolder.setStatus("FILLING UPDATE COUNT");
sr.setUpdateCount(stmt.getUpdateCount());
rs = stmt.getGeneratedKeys();
if (rs != null) {
rowSet = getRowSet(stmt, null, rs, INVALID_ID, null, connId, attrs,
0, false, false, 0, connHolder, "getGeneratedKeys");
sr.setGeneratedKeys(rowSet);
}
String newDefaultSchema = conn.getCurrentSchemaName();
// noinspection StringEquality
if (initialDefaultSchema != newDefaultSchema) {
sr.setNewDefaultSchema(newDefaultSchema);
}
}
connHolder.clearActiveStatement(stmt);
// don't attempt stmt cleanup after this point since we are reusing it
final EngineStatement st = stmt;
stmt = null;
fillWarnings(sr, st);
if (rs == null) {
// setup the Statement for reuse
connHolder.setStatementForReuse(st);
}
if (posDup) {
conn.setPossibleDuplicate(false);
}
return sr;
} catch (Throwable t) {
if (posDup && conn != null) {
conn.setPossibleDuplicate(false);
}
cleanupResultSet(rs);
if (stmt != null) {
connHolder.clearActiveStatement(stmt);
cleanupStatement(stmt);
}
checkSystemFailure(t);
throw SnappyException(t);
} finally {
if (conn != null && attrs != null && attrs.isSetBucketIds()) {
conn.getLanguageConnectionContext().setExecuteLocally(
null, null, false, null);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public UpdateResult executeUpdate(long connId, List sqls,
StatementAttrs attrs, ByteBuffer token) throws SnappyException {
ConnectionHolder connHolder = null;
EngineConnection conn = null;
EngineStatement stmt = null;
ResultSet rs = null;
UpdateResult result;
boolean posDup = false;
try {
connHolder = getValidConnection(connId, token);
conn = connHolder.getConnection();
final String initialDefaultSchema = conn.getCurrentSchemaName();
// first process any pending TX & other flags in StatementAttrs
posDup = processPendingTransactionAttributes(attrs, conn);
stmt = connHolder.createNewStatement(attrs);
final boolean singleUpdate = sqls.size() == 1;
StatementHolder stmtHolder = connHolder.newStatementHolder(stmt, attrs,
getNextId(this.currentStatementId), sqls, recordStatementStartTime,
singleUpdate ? "EXECUTING UPDATE" : "EXECUTING BATCH UPDATE");
connHolder.setActiveStatement(stmtHolder);
applyMergeAttributes(null, attrs, conn, stmt);
if (singleUpdate) {
int updateCount = stmt.executeUpdate(sqls.get(0));
stmtHolder.setStatus("FILLING UPDATE COUNT");
result = new UpdateResult();
result.setUpdateCount(updateCount);
} else {
stmt.clearBatch();
for (String sql : sqls) {
stmt.addBatch(sql);
}
int[] batchUpdateCounts = stmt.executeBatch();
stmtHolder.setStatus("FILLING BATCH UPDATE COUNTS");
result = new UpdateResult();
for (int count : batchUpdateCounts) {
result.addToBatchUpdateCounts(count);
}
}
rs = stmt.getGeneratedKeys();
if (rs != null) {
RowSet rowSet = getRowSet(stmt, null, rs, INVALID_ID, null, connId,
attrs, 0, false, false, 0, connHolder, "getGeneratedKeys");
result.setGeneratedKeys(rowSet);
}
connHolder.clearActiveStatement(stmt);
String newDefaultSchema = conn.getCurrentSchemaName();
// noinspection StringEquality
if (initialDefaultSchema != newDefaultSchema) {
result.setNewDefaultSchema(newDefaultSchema);
}
// don't attempt stmt cleanup after this point since we are reusing it
final EngineStatement st = stmt;
stmt = null;
fillWarnings(result, st);
if (rs == null) {
// setup the Statement for reuse
connHolder.setStatementForReuse(st);
}
if (posDup) {
conn.setPossibleDuplicate(false);
}
return result;
} catch (Throwable t) {
if (posDup && conn != null) {
conn.setPossibleDuplicate(false);
}
cleanupResultSet(rs);
if (stmt != null) {
connHolder.clearActiveStatement(stmt);
cleanupStatement(stmt);
}
checkSystemFailure(t);
throw SnappyException(t);
} finally {
if (stmt != null && sqls != null && sqls.size() > 1) {
stmt.forceClearBatch();
}
if (conn != null && attrs != null && attrs.isSetBucketIds()) {
conn.getLanguageConnectionContext().setExecuteLocally(
null, null, false, null);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public RowSet executeQuery(long connId, final String sql,
StatementAttrs attrs, ByteBuffer token) throws SnappyException {
ConnectionHolder connHolder = null;
EngineConnection conn = null;
EngineStatement stmt = null;
ResultSet rs = null;
boolean posDup = false;
try {
connHolder = getValidConnection(connId, token);
conn = connHolder.getConnection();
// first process any pending TX & other flags in StatementAttrs
posDup = processPendingTransactionAttributes(attrs, conn);
stmt = connHolder.createNewStatement(attrs);
StatementHolder stmtHolder = connHolder.newStatementHolder(stmt, attrs,
getNextId(this.currentStatementId), sql, recordStatementStartTime,
"EXECUTING QUERY");
connHolder.setActiveStatement(stmtHolder);
applyMergeAttributes(null, attrs, conn, stmt);
rs = stmt.executeQuery(sql);
stmtHolder.setStatus("FILLING RESULT SET");
RowSet rowSet = getRowSet(stmt, stmtHolder, rs, INVALID_ID, null, connId,
attrs, 0, false, false, 0, connHolder, sql);
connHolder.clearActiveStatement(stmt);
// don't attempt stmt cleanup after this point since we are reusing it
stmt = null;
if (posDup) {
conn.setPossibleDuplicate(false);
}
return rowSet;
} catch (Throwable t) {
if (posDup && conn != null) {
conn.setPossibleDuplicate(false);
}
cleanupResultSet(rs);
if (stmt != null) {
connHolder.clearActiveStatement(stmt);
cleanupStatement(stmt);
}
checkSystemFailure(t);
throw SnappyException(t);
} finally {
if (conn != null && (attrs != null && attrs.isSetBucketIds())) {
conn.getLanguageConnectionContext().setExecuteLocally(
null, null, false, null);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public PrepareResult prepareStatement(long connId, String sql,
Map outputParams, StatementAttrs attrs,
ByteBuffer token) throws SnappyException {
ConnectionHolder connHolder = null;
EngineConnection conn = null;
PreparedStatement pstmt = null;
StatementHolder stmtHolder = null;
boolean posDup = false;
try {
connHolder = getValidConnection(connId, token);
conn = connHolder.getConnection();
// first process any pending TX & other flags in StatementAttrs
posDup = processPendingTransactionAttributes(attrs, conn);
long pstmtId;
ArrayList pmDescs;
SnappyExceptionData sqlw = null;
applyMergeAttributes(null, attrs, conn, null);
final int resultSetType = getResultType(attrs);
final int resultSetConcurrency = getResultSetConcurrency(attrs);
final int resultSetHoldability = getResultSetHoldability(attrs);
if (attrs != null && attrs.isSetRequireAutoIncCols()
&& attrs.requireAutoIncCols) {
if (outputParams != null && !outputParams.isEmpty()) {
throw newSnappyException(SQLState.REQUIRES_CALLABLE_STATEMENT,
"AUTOINC not valid with output parameters: " + sql);
}
final List autoIncCols = attrs.autoIncColumns;
final List autoIncColNames;
int nCols;
if (autoIncCols != null && (nCols = autoIncCols.size()) > 0) {
int[] aiCols = new int[nCols];
for (int index = 0; index < nCols; index++) {
aiCols[index] = autoIncCols.get(index);
}
pstmt = conn.prepareStatement(sql, resultSetType,
resultSetConcurrency, resultSetHoldability, aiCols);
} else if ((autoIncColNames = attrs.autoIncColumnNames) != null
&& (nCols = autoIncColNames.size()) > 0) {
pstmt = conn.prepareStatement(sql, resultSetType,
resultSetConcurrency, resultSetHoldability,
autoIncColNames.toArray(new String[nCols]));
} else {
pstmt = conn.prepareStatement(sql, resultSetType,
resultSetConcurrency, resultSetHoldability,
Statement.RETURN_GENERATED_KEYS);
}
} else {
CallableStatement cstmt;
pstmt = cstmt = conn.prepareCall(sql, resultSetType,
resultSetConcurrency, resultSetHoldability);
registerOutputParameters(cstmt, outputParams);
}
// prepared result consists of ID + ParameterMetaData
ParameterMetaData pmd = pstmt.getParameterMetaData();
int numParams = pmd.getParameterCount();
pmDescs = new ArrayList<>(numParams);
int pmType, mode, nullable, scale;
SnappyType type;
String typeName, className;
for (int paramPosition = 1; paramPosition <= numParams; paramPosition++) {
pmType = pmd.getParameterType(paramPosition);
ColumnDescriptor pmDesc = new ColumnDescriptor();
type = Converters.getThriftSQLType(pmType);
mode = pmd.getParameterMode(paramPosition);
switch (mode) {
case ParameterMetaData.parameterModeIn:
pmDesc.setParameterIn(true);
break;
case ParameterMetaData.parameterModeOut:
pmDesc.setParameterOut(true);
break;
case ParameterMetaData.parameterModeInOut:
pmDesc.setParameterIn(true);
pmDesc.setParameterOut(true);
break;
default:
pmDesc.setParameterIn(true);
break;
}
nullable = pmd.isNullable(paramPosition);
if (nullable == ParameterMetaData.parameterNullable) {
pmDesc.setNullable(true);
} else if (nullable == ParameterMetaData.parameterNoNulls) {
pmDesc.setNullable(false);
}
pmDesc.setType(type);
pmDesc.setPrecision((short)pmd.getPrecision(paramPosition));
scale = pmd.getScale(paramPosition);
if (scale != 0) {
pmDesc.setScale((short)scale);
}
if (pmType == Types.JAVA_OBJECT) {
typeName = pmd.getParameterTypeName(paramPosition);
className = pmd.getParameterClassName(paramPosition);
if (className != null) {
pmDesc.setUdtTypeAndClassName(typeName + ':' + className);
} else {
pmDesc.setUdtTypeAndClassName(typeName);
}
}
pmDescs.add(pmDesc);
}
// any warnings
SQLWarning warnings = pstmt.getWarnings();
if (warnings != null) {
sqlw = snappyWarning(warnings);
}
pstmtId = getNextId(this.currentStatementId);
stmtHolder = connHolder.registerPreparedStatement(pstmt, attrs, pstmtId,
sql, recordStatementStartTime);
this.statementMap.putPrimitive(pstmtId, stmtHolder);
stmtHolder.setStatus("FILLING PREPARE RESULT");
byte statementType;
if (pstmt instanceof EnginePreparedStatement) {
switch (((EnginePreparedStatement)pstmt).getStatementType()) {
case StatementType.UNKNOWN:
statementType = snappydataConstants.STATEMENT_TYPE_SELECT;
break;
case StatementType.UPDATE:
statementType = snappydataConstants.STATEMENT_TYPE_UPDATE;
break;
case StatementType.INSERT:
case StatementType.BULK_INSERT_REPLACE:
statementType = snappydataConstants.STATEMENT_TYPE_INSERT;
break;
case StatementType.DELETE:
statementType = snappydataConstants.STATEMENT_TYPE_DELETE;
break;
case StatementType.CALL_STATEMENT:
case StatementType.DISTRIBUTED_PROCEDURE_CALL:
statementType = snappydataConstants.STATEMENT_TYPE_CALL;
break;
default:
statementType = snappydataConstants.STATEMENT_TYPE_DDL;
break;
}
} else {
String firstToken = ClientSharedUtils.getStatementToken(sql, 0);
if (firstToken == null || firstToken.equalsIgnoreCase("select")) {
statementType = snappydataConstants.STATEMENT_TYPE_SELECT;
} else if (firstToken.equalsIgnoreCase("update")) {
statementType = snappydataConstants.STATEMENT_TYPE_UPDATE;
} else if (firstToken.equalsIgnoreCase("insert")) {
statementType = snappydataConstants.STATEMENT_TYPE_INSERT;
} else if (firstToken.equalsIgnoreCase("delete")) {
statementType = snappydataConstants.STATEMENT_TYPE_DELETE;
} else if (firstToken.equalsIgnoreCase("call")) {
statementType = snappydataConstants.STATEMENT_TYPE_CALL;
} else {
statementType = snappydataConstants.STATEMENT_TYPE_DDL;
}
}
PrepareResult result = new PrepareResult(pstmtId, statementType, pmDescs);
if (sqlw != null) {
result.setWarnings(sqlw);
}
// fill in ResultSet meta-data
ResultSetMetaData rsmd = pstmt.getMetaData();
if (rsmd != null) {
result.setResultSetMetaData(getRowSetMetaData(rsmd,
rsmd.getColumnCount(), connHolder.useStringForDecimal()));
}
if (posDup) {
conn.setPossibleDuplicate(false);
}
return result;
} catch (Throwable t) {
if (posDup && conn != null) {
conn.setPossibleDuplicate(false);
}
if (pstmt != null) {
if (stmtHolder != null) {
connHolder.closeStatement(stmtHolder, this);
}
try {
pstmt.close();
} catch (Exception e) {
// ignore
}
}
checkSystemFailure(t);
throw SnappyException(t);
} finally {
if (pstmt != null) {
connHolder.clearActiveStatement(pstmt);
}
}
}
private void registerOutputParameters(CallableStatement cstmt,
Map outputParams) throws SQLException {
if (outputParams != null && !outputParams.isEmpty()) {
OutputParameter outParam;
int jdbcType;
for (Map.Entry param : outputParams
.entrySet()) {
outParam = param.getValue();
jdbcType = Converters.getJdbcType(outParam.type);
int paramPosition = param.getKey();
if (outParam.isSetScale()) {
cstmt.registerOutParameter(paramPosition, jdbcType, outParam.scale);
} else if (outParam.isSetTypeName()) {
cstmt.registerOutParameter(paramPosition, jdbcType, outParam.typeName);
} else {
cstmt.registerOutParameter(paramPosition, jdbcType);
}
}
}
}
private void fillOutputParameterValues(CallableStatement cstmt,
ParameterMetaData pmd, Map outputParams,
ConnectionHolder connHolder, StatementAttrs attrs,
StatementResult stmtResult) throws SQLException {
final int numParams = pmd.getParameterCount();
final HashMap outParams =
new HashMap<>(outputParams.size());
int paramMode;
for (int paramPosition = 1; paramPosition <= numParams; paramPosition++) {
paramMode = pmd.getParameterMode(paramPosition);
if (paramMode == ParameterMetaData.parameterModeInOut ||
paramMode == ParameterMetaData.parameterModeOut) {
outParams.put(paramPosition, getColumnValue(cstmt, paramPosition,
pmd.getParameterType(paramPosition), connHolder, attrs));
}
}
stmtResult.setProcedureOutParams(outParams);
}
private void updateParameters(Row params, PreparedStatement pstmt,
ParameterMetaData pmd, EngineConnection conn) throws SQLException {
if (params != null) {
final int numParams = params.size();
for (int paramPosition = 1; paramPosition <= numParams; paramPosition++) {
final int index = paramPosition - 1;
// skip output-only parameters
if (pmd != null && pmd.getParameterMode(
paramPosition) == ParameterMetaData.parameterModeOut) {
continue;
}
final int paramType = params.getType(index);
Object paramVal;
switch (Math.abs(paramType)) {
case 9: // CHAR
case 10: // VARCHAR
case 11: // LONGVARCHAR
if (paramType > 0) {
pstmt.setString(paramPosition, (String)params.getObject(index));
} else {
pstmt.setNull(paramPosition, Converters.getJdbcType(
SnappyType.findByValue(paramType)));
}
break;
case 4: // INTEGER
if (paramType > 0) {
pstmt.setInt(paramPosition, params.getInt(index));
} else {
pstmt.setNull(paramPosition, Types.INTEGER);
}
break;
case 5: // BIGINT
if (paramType > 0) {
pstmt.setLong(paramPosition, params.getLong(index));
} else {
pstmt.setNull(paramPosition, Types.BIGINT);
}
break;
case 12: // DATE
if (paramType > 0) {
pstmt.setDate(paramPosition, params.getDate(index));
} else {
pstmt.setNull(paramPosition, Types.DATE);
}
break;
case 14: // TIMESTAMP
if (paramType > 0) {
pstmt.setTimestamp(paramPosition, params.getTimestamp(index));
} else {
pstmt.setNull(paramPosition, Types.TIMESTAMP);
}
break;
case 7: // DOUBLE
if (paramType > 0) {
pstmt.setDouble(paramPosition, params.getDouble(index));
} else {
pstmt.setNull(paramPosition, Types.DOUBLE);
}
break;
case 8: // DECIMAL
if (paramType > 0) {
pstmt.setBigDecimal(paramPosition,
(BigDecimal)params.getObject(index));
} else {
pstmt.setNull(paramPosition, Types.DECIMAL);
}
break;
case 6: // FLOAT
if (paramType > 0) {
pstmt.setFloat(paramPosition, params.getFloat(index));
} else {
pstmt.setNull(paramPosition, Types.REAL);
}
break;
case 3: // SMALLINT
if (paramType > 0) {
pstmt.setShort(paramPosition, params.getShort(index));
} else {
pstmt.setNull(paramPosition, Types.SMALLINT);
}
break;
case 1: // BOOLEAN
if (paramType > 0) {
pstmt.setBoolean(paramPosition, params.getBoolean(index));
} else {
pstmt.setNull(paramPosition, Types.BOOLEAN);
}
break;
case 2: // TINYINT
if (paramType > 0) {
pstmt.setByte(paramPosition, params.getByte(index));
} else {
pstmt.setNull(paramPosition, Types.TINYINT);
}
break;
case 13: // TIME
if (paramType > 0) {
pstmt.setTime(paramPosition, params.getTime(index));
} else {
pstmt.setNull(paramPosition, Types.TIME);
}
break;
case 18: // BLOB
if (paramType <= 0) {
pstmt.setNull(paramPosition, Types.BLOB);
} else if ((paramVal = params.getObject(index)) instanceof byte[]) {
pstmt.setBytes(paramPosition, (byte[])paramVal);
} else {
BlobChunk chunk = (BlobChunk)paramVal;
Blob blob;
// if blob chunks were sent separately, then lookup that blob
// else create a new one
if (chunk.isSetLobId()) {
Object lob = conn.getLOBMapping(chunk.lobId);
if (lob instanceof Blob) {
blob = (Blob)lob;
} else {
throw Util.generateCsSQLException(
SQLState.LOB_LOCATOR_INVALID);
}
} else if (chunk.last) {
// set as a Blob
pstmt.setBlob(paramPosition, new ClientBlob(chunk.chunk));
break;
} else {
blob = conn.createBlob();
}
long offset = 1;
if (chunk.isSetOffset()) {
offset += chunk.offset;
}
// TODO: need an EmbedBlob that can deal directly with BlobChunks
blob.setBytes(offset, chunk.getChunk());
// free any direct buffer immediately
chunk.free();
pstmt.setBlob(paramPosition, blob);
}
break;
case 19: // CLOB
case 20: // SQLXML
case 25: // JSON
if (paramType <= 0) {
pstmt.setNull(paramPosition, paramType == 25 /* JSON */
? JDBC40Translation.JSON : Types.CLOB);
} else if ((paramVal = params.getObject(index)) instanceof String) {
pstmt.setString(paramPosition, (String)paramVal);
} else {
Clob clob;
ClobChunk chunk = (ClobChunk)paramVal;
// if clob chunks were sent separately, then lookup that clob
// else create a new one
if (chunk.isSetLobId()) {
Object lob = conn.getLOBMapping(chunk.lobId);
if (lob instanceof Clob) {
clob = (Clob)lob;
} else {
throw Util.generateCsSQLException(
SQLState.LOB_LOCATOR_INVALID);
}
} else if (chunk.last) {
// set as a normal String
pstmt.setString(paramPosition, chunk.getChunk());
break;
} else {
clob = conn.createClob();
}
long offset = 1;
if (chunk.isSetOffset()) {
offset += chunk.offset;
}
clob.setString(offset, chunk.getChunk());
pstmt.setClob(paramPosition, clob);
}
break;
case 15: // BINARY
case 16: // VARBINARY
case 17: // LONGVARBINARY
if (paramType > 0) {
pstmt.setBytes(paramPosition, (byte[])params.getObject(index));
} else {
pstmt.setNull(paramPosition, Converters.getJdbcType(
SnappyType.findByValue(paramType)));
}
break;
case 24: // NULLTYPE
pstmt.setNull(paramPosition, Types.NULL);
break;
case 26: // JAVA_OBJECT
if (paramType > 0) {
pstmt.setObject(paramPosition, ((Converters.JavaObjectWrapper)
params.getObject(index)).getDeserialized(paramPosition,
javaObjectCreator));
} else {
pstmt.setNull(paramPosition, Types.JAVA_OBJECT);
}
break;
default:
SnappyType type = SnappyType.findByValue(paramType);
throw Util.generateCsSQLException(SQLState.DATA_TYPE_NOT_SUPPORTED,
type != null ? type.toString() : Integer.toString(paramType));
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public StatementResult executePrepared(long stmtId, final Row params,
final Map outputParams, StatementAttrs attrs,
ByteBuffer token) throws SnappyException {
ConnectionHolder connHolder = null;
EngineConnection conn = null;
PreparedStatement pstmt = null;
CallableStatement cstmt = null;
ParameterMetaData pmd = null;
ResultSet rs = null;
RowSet rowSet = null;
// prepared statement executions do not have posDup handling since
// client-side will need to do prepare+execute after failure so only
// prepare needs to handle posDup
try {
// Validate the statement Id
StatementHolder stmtHolder = getStatement(token, stmtId, true,
"executePrepared");
connHolder = stmtHolder.getConnectionHolder();
final long connId = connHolder.getConnectionId();
conn = connHolder.getConnection();
final String initialDefaultSchema = conn.getCurrentSchemaName();
pstmt = (PreparedStatement)stmtHolder.getStatement();
stmtHolder.setStatus("EXECUTING PREPARED");
stmtHolder.incrementAccessFrequency();
connHolder.setActiveStatement(stmtHolder);
attrs = applyMergeAttributes(stmtHolder.getStatementAttrs(), attrs,
conn, pstmt);
if (outputParams != null && !outputParams.isEmpty()) {
if (pstmt instanceof CallableStatement) {
cstmt = (CallableStatement)pstmt;
pmd = cstmt.getParameterMetaData();
registerOutputParameters(cstmt, outputParams);
stmtHolder.setStatus("EXECUTING PREPARED CALL");
} else {
throw newSnappyException(SQLState.REQUIRES_CALLABLE_STATEMENT,
stmtHolder.getSQL());
}
}
StatementResult stmtResult = createEmptyStatementResult();
// clear any existing return parameters first
pstmt.clearParameters();
updateParameters(params, pstmt, pmd, conn);
final boolean resultType = pstmt.execute();
if (resultType) { // Case : result is a ResultSet
stmtHolder.setStatus("FILLING RESULT SET");
rs = pstmt.getResultSet();
rowSet = getRowSet(pstmt, stmtHolder, rs, INVALID_ID, null,
connId, attrs, 0, false, false, 0, connHolder,
null /* already set */);
stmtResult.setResultSet(rowSet);
// set the output parameters if required
if (cstmt != null) {
fillOutputParameterValues(cstmt, pmd, outputParams, connHolder,
attrs, stmtResult);
}
} else { // Case : result is update count
stmtHolder.setStatus("FILLING UPDATE COUNT");
stmtResult.setUpdateCount(pstmt.getUpdateCount());
rs = pstmt.getGeneratedKeys();
if (rs != null) {
rowSet = getRowSet(pstmt, stmtHolder, rs, INVALID_ID, null,
connId, attrs, 0, false, false, 0, connHolder,
"getGeneratedKeys");
stmtResult.setGeneratedKeys(rowSet);
}
String newDefaultSchema = conn.getCurrentSchemaName();
// noinspection StringEquality
if (initialDefaultSchema != newDefaultSchema) {
stmtResult.setNewDefaultSchema(newDefaultSchema);
}
}
// set the output parameters if required
if (cstmt != null) {
fillOutputParameterValues(cstmt, pmd, outputParams, connHolder,
attrs, stmtResult);
}
fillWarnings(stmtResult, pstmt);
return stmtResult;
} catch (Throwable t) {
cleanupResultSet(rs);
checkSystemFailure(t);
throw SnappyException(t);
} finally {
if (pstmt != null) {
try {
if (isLast(rowSet)) {
pstmt.clearParameters();
}
} catch (Throwable t) {
// ignore exceptions at this point
checkSystemFailure(t);
}
connHolder.clearActiveStatement(pstmt);
}
if (conn != null && attrs != null && attrs.isSetBucketIds()) {
conn.getLanguageConnectionContext().setExecuteLocally(
null, null, false, null);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public UpdateResult executePreparedUpdate(long stmtId,
final Row params, StatementAttrs attrs, ByteBuffer token)
throws SnappyException {
ConnectionHolder connHolder = null;
EngineConnection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
RowSet rowSet = null;
// prepared statement executions do not have posDup handling since
// client-side will need to do prepare+execute after failure so only
// prepare needs to handle posDup
try {
StatementHolder stmtHolder = getStatement(token, stmtId, true,
"executePreparedUpdate");
connHolder = stmtHolder.getConnectionHolder();
final long connId = connHolder.getConnectionId();
conn = connHolder.getConnection();
final String initialDefaultSchema = conn.getCurrentSchemaName();
pstmt = (PreparedStatement)stmtHolder.getStatement();
stmtHolder.setStatus("EXECUTING PREPARED UPDATE");
stmtHolder.incrementAccessFrequency();
connHolder.setActiveStatement(stmtHolder);
attrs = applyMergeAttributes(stmtHolder.getStatementAttrs(), attrs,
conn, pstmt);
// clear any existing parameters first
pstmt.clearParameters();
updateParameters(params, pstmt, null, conn);
int updateCount = pstmt.executeUpdate();
stmtHolder.setStatus("FILLING UPDATE COUNT");
UpdateResult result = new UpdateResult();
result.setUpdateCount(updateCount);
rs = pstmt.getGeneratedKeys();
if (rs != null) {
rowSet = getRowSet(pstmt, stmtHolder, rs, INVALID_ID, null,
connId, attrs, 0, false, false, 0, connHolder, "getGeneratedKeys");
result.setGeneratedKeys(rowSet);
}
String newDefaultSchema = conn.getCurrentSchemaName();
// noinspection StringEquality
if (initialDefaultSchema != newDefaultSchema) {
result.setNewDefaultSchema(newDefaultSchema);
}
fillWarnings(result, pstmt);
return result;
} catch (Throwable t) {
cleanupResultSet(rs);
checkSystemFailure(t);
throw SnappyException(t);
} finally {
if (pstmt != null) {
try {
if (isLast(rowSet)) {
pstmt.clearParameters();
}
} catch (Throwable t) {
// ignore exceptions at this point
checkSystemFailure(t);
}
connHolder.clearActiveStatement(pstmt);
}
if (conn != null && attrs != null && attrs.isSetBucketIds()) {
conn.getLanguageConnectionContext().setExecuteLocally(
null, null, false, null);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public RowSet executePreparedQuery(long stmtId, final Row params,
StatementAttrs attrs, ByteBuffer token) throws SnappyException {
ConnectionHolder connHolder = null;
EngineConnection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
RowSet rowSet = null;
// prepared statement executions do not have posDup handling since
// client-side will need to do prepare+execute after failure so only
// prepare needs to handle posDup
try {
StatementHolder stmtHolder = getStatement(token, stmtId, true,
"executePreparedQuery");
connHolder = stmtHolder.getConnectionHolder();
final long connId = connHolder.getConnectionId();
conn = connHolder.getConnection();
pstmt = (PreparedStatement)stmtHolder.getStatement();
stmtHolder.setStatus("EXECUTING PREPARED QUERY");
stmtHolder.incrementAccessFrequency();
connHolder.setActiveStatement(stmtHolder);
attrs = applyMergeAttributes(stmtHolder.getStatementAttrs(), attrs,
conn, pstmt);
// clear any existing parameters first
pstmt.clearParameters();
updateParameters(params, pstmt, null, conn);
rs = pstmt.executeQuery();
stmtHolder.setStatus("FILLING RESULT SET");
rowSet = getRowSet(pstmt, stmtHolder, rs, INVALID_ID, null, connId,
attrs, 0, false, false, 0, connHolder, null /* already set */);
return rowSet;
} catch (Throwable t) {
cleanupResultSet(rs);
checkSystemFailure(t);
throw SnappyException(t);
} finally {
if (pstmt != null) {
try {
if (isLast(rowSet)) {
pstmt.clearParameters();
}
} catch (Throwable t) {
// ignore exceptions at this point
checkSystemFailure(t);
}
connHolder.clearActiveStatement(pstmt);
}
if (conn != null && attrs != null && attrs.isSetBucketIds()) {
conn.getLanguageConnectionContext().setExecuteLocally(
null, null, false, null);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public UpdateResult executePreparedBatch(long stmtId, List paramsBatch,
StatementAttrs attrs, ByteBuffer token) throws SnappyException {
ConnectionHolder connHolder = null;
EngineConnection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
RowSet rowSet = null;
// prepared statement executions do not have posDup handling since
// client-side will need to do prepare+execute after failure so only
// prepare needs to handle posDup
try {
StatementHolder stmtHolder = getStatement(token, stmtId, true,
"executePreparedBatch");
connHolder = stmtHolder.getConnectionHolder();
final long connId = connHolder.getConnectionId();
conn = connHolder.getConnection();
pstmt = (PreparedStatement)stmtHolder.getStatement();
stmtHolder.setStatus("EXECUTING PREPARED BATCH");
stmtHolder.incrementAccessFrequency();
connHolder.setActiveStatement(stmtHolder);
attrs = applyMergeAttributes(stmtHolder.getStatementAttrs(), attrs,
conn, pstmt);
// clear any existing parameters first
pstmt.clearParameters();
pstmt.clearBatch();
for (Row params : paramsBatch) {
updateParameters(params, pstmt, null, conn);
pstmt.addBatch();
}
int[] batchUpdateCounts = pstmt.executeBatch();
stmtHolder.setStatus("FILLING BATCH UPDATE COUNTS");
UpdateResult result = new UpdateResult();
for (int count : batchUpdateCounts) {
result.addToBatchUpdateCounts(count);
}
rs = pstmt.getGeneratedKeys();
if (rs != null) {
rowSet = getRowSet(pstmt, stmtHolder, rs, INVALID_ID, null,
connId, attrs, 0, false, false, 0, connHolder, "getGeneratedKeys");
result.setGeneratedKeys(rowSet);
}
fillWarnings(result, pstmt);
return result;
} catch (Throwable t) {
cleanupResultSet(rs);
checkSystemFailure(t);
throw SnappyException(t);
} finally {
if (pstmt != null) {
try {
if (isLast(rowSet)) {
pstmt.clearParameters();
}
} catch (Throwable t) {
// ignore exceptions at this point
checkSystemFailure(t);
}
try {
pstmt.clearBatch();
} catch (Throwable t) {
// ignore exceptions at this point
checkSystemFailure(t);
}
connHolder.clearActiveStatement(pstmt);
}
if (conn != null && attrs != null && attrs.isSetBucketIds()) {
conn.getLanguageConnectionContext().setExecuteLocally(
null, null, false, null);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public StatementResult prepareAndExecute(long connId, String sql,
final List paramsBatch, Map outputParams,
StatementAttrs attrs, ByteBuffer token) throws SnappyException {
PrepareResult prepResult = prepareStatement(connId, sql, outputParams,
attrs, token);
ConnectionHolder connHolder = null;
final boolean posDup = (attrs != null && attrs.possibleDuplicate);
try {
StatementResult sr;
if (posDup) {
connHolder = getValidConnection(connId, token);
connHolder.getConnection().setPossibleDuplicate(true);
}
if (paramsBatch.size() == 1) {
sr = executePrepared(prepResult.statementId, paramsBatch.get(0),
outputParams, attrs, token);
// also copy the single update to list of updates
// just in case user always reads the list of updates
if (sr.updateCount >= 0) {
sr.setBatchUpdateCounts(Collections.singletonList(sr.updateCount));
}
} else {
UpdateResult ur = executePreparedBatch(prepResult.statementId,
paramsBatch, attrs, token);
sr = new StatementResult();
sr.setBatchUpdateCounts(ur.getBatchUpdateCounts());
sr.setGeneratedKeys(ur.getGeneratedKeys());
sr.setNewDefaultSchema(ur.getNewDefaultSchema());
sr.setWarnings(ur.getWarnings());
}
sr.setPreparedResult(prepResult);
return sr;
} finally {
if (posDup && connHolder != null) {
connHolder.getConnection().setPossibleDuplicate(false);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public byte beginTransaction(long connId, byte isolationLevel,
Map flags, ByteBuffer token)
throws SnappyException {
return beginOrAlterTransaction(getValidConnection(connId, token)
.getConnection(), isolationLevel, flags, true);
}
/**
* {@inheritDoc}
*/
@Override
public void setTransactionAttributes(long connId,
Map flags, ByteBuffer token)
throws SnappyException {
if (flags != null && !flags.isEmpty()) {
beginOrAlterTransaction(getValidConnection(connId, token).getConnection(),
snappydataConstants.TRANSACTION_NO_CHANGE, flags, false);
}
}
/**
* {@inheritDoc}
*/
@Override
public Map getTransactionAttributes(
long connId, ByteBuffer token) throws SnappyException {
try {
EngineConnection conn = getValidConnection(connId, token).getConnection();
final EnumMap txAttrs = ThriftUtils
.newTransactionFlags();
EnumSet txFlags = conn.getTransactionFlags();
txAttrs.put(TransactionAttribute.AUTOCOMMIT, conn.getAutoCommit());
txAttrs.put(TransactionAttribute.READ_ONLY_CONNECTION,
conn.isReadOnly());
if (txFlags != null) {
txAttrs.put(TransactionAttribute.DISABLE_BATCHING,
txFlags.contains(TransactionFlag.DISABLE_BATCHING));
txAttrs.put(TransactionAttribute.SYNC_COMMITS,
txFlags.contains(TransactionFlag.SYNC_COMMITS));
txAttrs.put(TransactionAttribute.WAITING_MODE,
txFlags.contains(TransactionFlag.WAITING_MODE));
} else {
txAttrs.put(TransactionAttribute.DISABLE_BATCHING, false);
txAttrs.put(TransactionAttribute.SYNC_COMMITS, false);
txAttrs.put(TransactionAttribute.WAITING_MODE, false);
}
return txAttrs;
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
private byte beginOrAlterTransaction(EngineConnection conn,
byte isolationLevel, Map flags,
boolean commitExisting) throws SnappyException {
try {
EnumSet txFlags = null;
Boolean autoCommit = null;
Boolean readOnly = null;
boolean hasTXFlags = false;
if (flags != null && flags.size() > 0) {
txFlags = EnumSet.noneOf(TransactionFlag.class);
for (Map.Entry e : flags.entrySet()) {
TransactionAttribute flag = e.getKey();
switch (flag) {
case AUTOCOMMIT:
autoCommit = e.getValue();
break;
case DISABLE_BATCHING:
if (e.getValue()) {
txFlags.add(TransactionFlag.DISABLE_BATCHING);
}
hasTXFlags = true;
break;
case WAITING_MODE:
if (e.getValue()) {
txFlags.add(TransactionFlag.WAITING_MODE);
}
hasTXFlags = true;
break;
case SYNC_COMMITS:
if (e.getValue()) {
txFlags.add(TransactionFlag.SYNC_COMMITS);
}
hasTXFlags = true;
break;
case READ_ONLY_CONNECTION:
readOnly = e.getValue();
break;
}
}
if (!hasTXFlags) {
txFlags = null;
}
}
if (readOnly != null) {
conn.setReadOnly(readOnly);
}
if (autoCommit != null) {
conn.setAutoCommit(autoCommit);
}
if (isolationLevel != snappydataConstants.TRANSACTION_NO_CHANGE) {
conn.setTransactionIsolation(Converters.getJdbcIsolation(
isolationLevel), txFlags);
isolationLevel = Converters.getThriftTransactionIsolation(
conn.getTransactionIsolation());
} else {
if (commitExisting) {
conn.commit();
}
if (txFlags != null) {
LanguageConnectionContext lcc = conn.getLanguageConnectionContext();
lcc.setTXFlags(txFlags);
}
}
return isolationLevel;
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public void commitTransaction(long connId, final boolean startNewTransaction,
Map flags, ByteBuffer token)
throws SnappyException {
try {
EngineConnection conn = getValidConnection(connId, token).getConnection();
if (flags != null && !flags.isEmpty()) {
beginOrAlterTransaction(conn, snappydataConstants.TRANSACTION_NO_CHANGE,
flags, false);
}
conn.commit();
LanguageConnectionContext lcc = conn.getLanguageConnectionContext();
if (lcc != null) {
lcc.clearExecuteLocally();
}
// JDBC starts a new transaction immediately; we need to set the isolation
// explicitly to NONE to avoid that
if (!startNewTransaction) {
conn.setTransactionIsolation(Connection.TRANSACTION_NONE);
}
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public void rollbackTransaction(long connId,
final boolean startNewTransaction,
Map flags, ByteBuffer token)
throws SnappyException {
try {
EngineConnection conn = getValidConnection(connId, token).getConnection();
if (flags != null && !flags.isEmpty()) {
beginOrAlterTransaction(conn, snappydataConstants.TRANSACTION_NO_CHANGE,
flags, false);
}
conn.rollback();
LanguageConnectionContext lcc = conn.getLanguageConnectionContext();
if (lcc != null) {
lcc.clearExecuteLocally();
}
// JDBC starts a new transaction immediately; we need to set the isolation
// explicitly to NONE to avoid that
if (!startNewTransaction) {
conn.setTransactionIsolation(Connection.TRANSACTION_NONE);
}
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public RowSet scrollCursor(long cursorId, int offset,
boolean offsetIsAbsolute, boolean fetchReverse, int fetchSize,
ByteBuffer token) throws SnappyException {
ConnectionHolder connHolder = null;
Statement stmt = null;
StatementAttrs attrs;
RowSet rowSet = null;
try {
StatementHolder stmtHolder = getStatementForResultSet(token,
cursorId, "scrollCursor");
connHolder = stmtHolder.getConnectionHolder();
final long connId = connHolder.getConnectionId();
ResultSetHolder holder = stmtHolder.findResultSet(cursorId);
if (holder != null) {
stmt = stmtHolder.getStatement();
stmtHolder.setStatus("SCROLLING CURSOR");
stmtHolder.incrementAccessFrequency();
connHolder.setActiveStatement(stmtHolder);
attrs = stmtHolder.getStatementAttrs();
rowSet = getRowSet(stmt, stmtHolder, holder.resultSet,
holder.rsCursorId, holder, connId, attrs, offset,
offsetIsAbsolute, fetchReverse, fetchSize, connHolder, null);
return rowSet;
} else {
throw resultSetNotFoundException(cursorId, "scrollCursor");
}
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
} finally {
// eagerly clear the parameters for last batch in FORWARD_ONLY cursor
try {
if (isLast(rowSet) && stmt != null &&
stmt.getResultSetType() == ResultSet.TYPE_FORWARD_ONLY &&
stmt instanceof PreparedStatement) {
((PreparedStatement)stmt).clearParameters();
}
} catch (Throwable t) {
// ignore exceptions at this point
checkSystemFailure(t);
}
if (connHolder != null) {
connHolder.clearActiveStatement(stmt);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void executeCursorUpdate(long cursorId,
List operations,
List changedRows, List> changedColumnsList,
List changedRowIndexes, ByteBuffer token) throws SnappyException {
// TODO Auto-generated method stub
throw notImplementedException("executeCursorUpdate");
}
/**
* {@inheritDoc}
*/
@Override
public void startXATransaction(long connId, TransactionXid xid,
int timeoutInSeconds, int flags, ByteBuffer token)
throws SnappyException {
try {
ConnectionHolder connHolder = getValidConnection(connId, token);
XAResource xaResource = getXAResource(connHolder);
xaResource.setTransactionTimeout(timeoutInSeconds);
XAXactId xaXid = new XAXactId(xid.getFormatId(), xid.getGlobalId(),
xid.getBranchQualifier());
xaResource.start(xaXid, flags);
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
@Override
public int prepareXATransaction(long connId, TransactionXid xid,
ByteBuffer token) throws SnappyException {
try {
ConnectionHolder connHolder = getValidConnection(connId, token);
XAResource xaResource = getXAResource(connHolder);
XAXactId xaXid = new XAXactId(xid.getFormatId(), xid.getGlobalId(),
xid.getBranchQualifier());
return xaResource.prepare(xaXid);
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
@Override
public void commitXATransaction(long connId, TransactionXid xid,
boolean onePhase, ByteBuffer token) throws SnappyException {
try {
ConnectionHolder connHolder = getValidConnection(connId, token);
XAResource xaResource = getXAResource(connHolder);
XAXactId xaXid = new XAXactId(xid.getFormatId(), xid.getGlobalId(),
xid.getBranchQualifier());
xaResource.commit(xaXid, onePhase);
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
@Override
public void rollbackXATransaction(long connId, TransactionXid xid,
ByteBuffer token) throws SnappyException {
try {
ConnectionHolder connHolder = getValidConnection(connId, token);
XAResource xaResource = getXAResource(connHolder);
XAXactId xaXid = new XAXactId(xid.getFormatId(), xid.getGlobalId(),
xid.getBranchQualifier());
xaResource.rollback(xaXid);
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
@Override
public void forgetXATransaction(long connId, TransactionXid xid,
ByteBuffer token) throws SnappyException {
try {
ConnectionHolder connHolder = getValidConnection(connId, token);
XAResource xaResource = getXAResource(connHolder);
XAXactId xaXid = new XAXactId(xid.getFormatId(), xid.getGlobalId(),
xid.getBranchQualifier());
xaResource.forget(xaXid);
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
@Override
public void endXATransaction(long connId, TransactionXid xid,
int flags, ByteBuffer token) throws SnappyException {
try {
ConnectionHolder connHolder = getValidConnection(connId, token);
XAResource xaResource = getXAResource(connHolder);
XAXactId xaXid = new XAXactId(xid.getFormatId(), xid.getGlobalId(),
xid.getBranchQualifier());
xaResource.end(xaXid, flags);
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
@Override
public List recoverXATransaction(long connId, int flag,
ByteBuffer token) throws SnappyException {
try {
ConnectionHolder connHolder = getValidConnection(connId, token);
XAResource xaResource = getXAResource(connHolder);
Xid[] result = xaResource.recover(flag);
if (result != null && result.length > 0) {
final ArrayList xids = new ArrayList<>(result.length);
for (Xid xid : result) {
xids.add(new TransactionXid().setFormatId(xid.getFormatId())
.setGlobalId(xid.getGlobalTransactionId())
.setBranchQualifier(xid.getBranchQualifier()));
}
return xids;
} else {
return new ArrayList<>(0);
}
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public RowSet getNextResultSet(long cursorId, byte otherResultSetBehaviour,
ByteBuffer token) throws SnappyException {
ConnectionHolder connHolder = null;
Statement stmt = null;
ResultSet rs = null;
try {
StatementHolder stmtHolder = getStatementForResultSet(token, cursorId,
"getNextResultSet");
stmt = stmtHolder.getStatement();
if (stmt == null) {
// cannot return null result over thrift, so return empty RowSet;
// empty descriptors will indicate absence of result to clients
return createEmptyRowSet();
}
connHolder = stmtHolder.getConnectionHolder();
stmtHolder.setStatus("CLOSE PREVIOUS RESULT SET");
stmtHolder.incrementAccessFrequency();
connHolder.setActiveStatement(stmtHolder);
// close the previous ResultSet
stmtHolder.closeResultSet(cursorId, this);
stmtHolder.setStatus("GET NEXT RESULT SET");
final boolean moreResults;
if (otherResultSetBehaviour == 0) {
moreResults = stmt.getMoreResults();
} else {
final int current;
switch (otherResultSetBehaviour) {
case snappydataConstants.NEXTRS_CLOSE_CURRENT_RESULT:
current = EngineStatement.CLOSE_CURRENT_RESULT;
break;
case snappydataConstants.NEXTRS_KEEP_CURRENT_RESULT:
current = EngineStatement.KEEP_CURRENT_RESULT;
break;
default:
current = EngineStatement.CLOSE_ALL_RESULTS;
break;
}
moreResults = stmt.getMoreResults(current);
}
if (moreResults) {
rs = stmt.getResultSet();
stmtHolder.setStatus("FILLING NEXT RESULT SET");
return getRowSet(stmt, stmtHolder, rs, INVALID_ID, null,
connHolder.getConnectionId(), stmtHolder.getStatementAttrs(), 0,
false, false, 0, connHolder, null /* already set */);
} else {
// cannot return null result over thrift, so return empty RowSet;
// empty descriptors will indicate absence of result to clients
return createEmptyRowSet();
}
} catch (Throwable t) {
cleanupResultSet(rs);
checkSystemFailure(t);
throw SnappyException(t);
} finally {
if (connHolder != null) {
connHolder.clearActiveStatement(stmt);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public BlobChunk getBlobChunk(long connId, long lobId, long offset,
int chunkSize, boolean freeLobAtEnd, ByteBuffer token)
throws SnappyException {
try {
EngineConnection conn = getValidConnection(connId, token).getConnection();
Object lob = conn.getLOBMapping(lobId);
if (lob instanceof Blob) {
Blob blob = (Blob)lob;
long length = blob.length() - offset;
if (length > Integer.MAX_VALUE) {
throw Util.generateCsSQLException(SQLState.BLOB_TOO_LARGE_FOR_CLIENT,
Long.toString(length), Long.toString(Integer.MAX_VALUE));
}
BlobChunk chunk = new BlobChunk().setLobId(lobId).setOffset(offset);
if (chunkSize > 0 && chunkSize < length) {
chunk.chunk = ByteBuffer.wrap(blob.getBytes(offset + 1, chunkSize));
chunk.setLast(false);
} else {
chunk = getAsLastChunk(blob, (int)length);
if (freeLobAtEnd) {
conn.removeLOBMapping(lobId);
blob.free();
}
}
return chunk;
} else {
throw Util.generateCsSQLException(SQLState.LOB_LOCATOR_INVALID);
}
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public ClobChunk getClobChunk(long connId, long lobId, long offset,
int chunkSize, boolean freeLobAtEnd, ByteBuffer token)
throws SnappyException {
try {
EngineConnection conn = getValidConnection(connId, token).getConnection();
Object lob = conn.getLOBMapping(lobId);
if (lob instanceof Clob) {
Clob clob = (Clob)lob;
long length = clob.length() - offset;
if (length > Integer.MAX_VALUE) {
throw Util.generateCsSQLException(SQLState.BLOB_TOO_LARGE_FOR_CLIENT,
Long.toString(length), Long.toString(Integer.MAX_VALUE));
}
ClobChunk chunk = new ClobChunk().setLobId(lobId).setOffset(offset);
if (chunkSize > 0 && chunkSize < length) {
chunk.setChunk(clob.getSubString(offset + 1, chunkSize)).setLast(
false);
} else {
chunk.setChunk(clob.getSubString(offset + 1, (int)length)).setLast(
true);
if (freeLobAtEnd) {
conn.removeLOBMapping(lobId);
clob.free();
}
}
return chunk;
} else {
throw Util.generateCsSQLException(SQLState.LOB_LOCATOR_INVALID);
}
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public void closeResultSet(long cursorId, ByteBuffer token)
throws SnappyException {
if (cursorId == INVALID_ID) {
return;
}
ConnectionHolder connHolder = null;
Statement stmt = null;
try {
StatementHolder stmtHolder = getStatementForResultSet(token, cursorId,
"closeResultSet");
connHolder = stmtHolder.getConnectionHolder();
stmtHolder.setStatus("CLOSING RESULT SET");
connHolder.setActiveStatement(stmtHolder);
stmt = stmtHolder.getStatement();
stmtHolder.closeResultSet(cursorId, this);
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
} finally {
if (connHolder != null) {
connHolder.clearActiveStatement(stmt);
}
}
}
private void checkDBOwner(long connId, ByteBuffer token, String module)
throws SnappyException {
// check for valid token
ConnectionHolder connHolder = getValidConnection(connId, token);
String authId;
try {
authId = IdUtil.getUserAuthorizationId(connHolder.getUserName());
} catch (Exception e) {
throw SnappyException(e);
}
if (!Misc.getMemStore().getDatabase().getDataDictionary()
.getAuthorizationDatabaseOwner().equals(authId)) {
throw newSnappyException(SQLState.LOGIN_FAILED,
"administrator access required for " + module);
}
}
/**
* {@inheritDoc}
*/
@Override
public List fetchActiveConnections(long connId,
ByteBuffer token) throws SnappyException {
// only allow admin user
checkDBOwner(connId, token, "fetchActiveConnections");
final ArrayList activeConns = new ArrayList<>(
this.connectionMap.size());
this.connectionMap.forEachValue(new TObjectProcedure() {
@Override
public boolean execute(Object h) {
final ConnectionHolder connHolder = (ConnectionHolder)h;
ConnectionProperties props = new ConnectionProperties(connHolder
.getConnectionId(), connHolder.getClientHostName(), connHolder
.getClientID());
props.setUserName(connHolder.getUserName());
activeConns.add(props);
return true;
}
});
return activeConns;
}
/**
* {@inheritDoc}
*/
@Override
public Map fetchActiveStatements(long connId, ByteBuffer token)
throws SnappyException {
// only allow admin user
checkDBOwner(connId, token, "fetchActiveStatements");
@SuppressWarnings("unchecked")
final Map activeStmts = new THashMap(this.statementMap.size());
this.statementMap.forEachValue(new TObjectProcedure() {
@Override
public boolean execute(Object h) {
final StatementHolder stmtHolder = (StatementHolder)h;
activeStmts.put(stmtHolder.getStatementId(),
String.valueOf(stmtHolder.getSQL()));
return true;
}
});
return activeStmts;
}
/**
* {@inheritDoc}
*/
@Override
public void cancelStatement(long stmtId, ByteBuffer token)
throws SnappyException {
try {
StatementHolder stmtHolder = getStatement(token, stmtId, false,
"cancelStatement");
stmtHolder.getStatement().cancel();
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public void cancelCurrentStatement(long connId, ByteBuffer token)
throws SnappyException {
try {
ConnectionHolder connHolder = getValidConnection(connId, token);
// check for the current active statement
Statement activeStatement = connHolder.uniqueActiveStatement(true);
if (activeStatement != null) {
activeStatement.cancel();
}
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public void closeStatement(long stmtId, ByteBuffer token)
throws SnappyException {
ConnectionHolder connHolder = null;
Statement stmt = null;
try {
StatementHolder stmtHolder = getStatement(token, stmtId, false,
"closeStatement");
connHolder = stmtHolder.getConnectionHolder();
stmt = stmtHolder.getStatement();
stmtHolder.setStatus("CLOSING STATEMENT");
connHolder.setActiveStatement(stmtHolder);
connHolder.closeStatement(stmtHolder, this);
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
} finally {
if (connHolder != null) {
connHolder.clearActiveStatement(stmt);
}
}
}
private SnappyExceptionData snappyWarning(SQLWarning warnings)
throws SQLException {
SnappyExceptionData warningData = new SnappyExceptionData(
warnings.getMessage(), warnings.getErrorCode())
.setSqlState(warnings.getSQLState());
ArrayList nextWarnings = null;
SQLWarning next = warnings.getNextWarning();
if (next != null) {
nextWarnings = new ArrayList<>();
do {
nextWarnings.add(new SnappyExceptionData(next.getMessage(),
next.getErrorCode()).setSqlState(next.getSQLState()));
} while ((next = next.getNextWarning()) != null);
}
//SnappyExceptionData sqlw = new SnappyExceptionData(warningData);
//sqlw.setNextWarnings(nextWarnings);
if (nextWarnings != null) {
warningData.setReason(warningData.getReason() + nextWarnings.toString());
}
return warningData;
}
private void fillWarnings(StatementResult sr, Statement stmt)
throws SQLException {
SQLWarning warnings = stmt.getWarnings();
if (warnings != null) {
sr.setWarnings(snappyWarning(warnings));
}
}
private void fillWarnings(UpdateResult ur, Statement stmt)
throws SQLException {
SQLWarning warnings = stmt.getWarnings();
if (warnings != null) {
ur.setWarnings(snappyWarning(warnings));
}
}
private void fillWarnings(RowSet rs, ResultSet resultSet)
throws SQLException {
SQLWarning warnings = resultSet.getWarnings();
if (warnings != null) {
rs.setWarnings(snappyWarning(warnings));
}
}
private SnappyException internalException(String message) {
SnappyExceptionData exData = new SnappyExceptionData();
exData.setReason(message);
exData.setSqlState(SQLState.JAVA_EXCEPTION);
exData.setErrorCode(ExceptionSeverity.NO_APPLICABLE_SEVERITY);
return new SnappyException(exData, getServerInfo());
}
private SnappyException notImplementedException(String method) {
SnappyExceptionData exData = new SnappyExceptionData();
exData.setReason("ASSERT: " + method + "() not implemented");
exData.setSqlState(SQLState.JDBC_METHOD_NOT_SUPPORTED_BY_SERVER);
exData.setErrorCode(ExceptionSeverity.STATEMENT_SEVERITY);
return new SnappyException(exData, getServerInfo());
}
/**
* {@inheritDoc}
*/
@Override
public long sendBlobChunk(BlobChunk chunk, long connId, ByteBuffer token)
throws SnappyException {
try {
EngineConnection conn = getValidConnection(connId, token).getConnection();
long lobId;
Blob blob;
if (chunk.isSetLobId()) {
lobId = chunk.lobId;
Object lob = conn.getLOBMapping(lobId);
if (lob instanceof Blob) {
blob = (Blob)lob;
} else {
throw Util.generateCsSQLException(SQLState.LOB_LOCATOR_INVALID);
}
} else {
blob = conn.createBlob();
lobId = ((EngineLOB)blob).getLocator();
}
long offset = 1;
if (chunk.isSetOffset()) {
offset += chunk.offset;
}
// TODO: need an EmbedBlob that can deal directly with BlobChunks
blob.setBytes(offset, chunk.getChunk());
// free any direct buffer immediately
chunk.free();
return lobId;
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public long sendClobChunk(ClobChunk chunk, long connId, ByteBuffer token)
throws SnappyException {
try {
EngineConnection conn = getValidConnection(connId, token).getConnection();
long lobId;
Clob clob;
if (chunk.isSetLobId()) {
lobId = chunk.lobId;
Object lob = conn.getLOBMapping(lobId);
if (lob instanceof Clob) {
clob = (Clob)lob;
} else {
throw Util.generateCsSQLException(SQLState.LOB_LOCATOR_INVALID);
}
} else {
clob = conn.createClob();
lobId = ((EngineLOB)clob).getLocator();
}
long offset = 1;
if (chunk.isSetOffset()) {
offset += chunk.offset;
}
clob.setString(offset, chunk.getChunk());
return lobId;
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public void freeLob(long connId, long lobId, ByteBuffer token)
throws SnappyException {
try {
EngineConnection conn = getValidConnection(connId, token).getConnection();
Object lob = conn.getLOBMapping(lobId);
if (lob instanceof EngineLOB) {
((EngineLOB)lob).free();
conn.removeLOBMapping(lobId);
} else {
throw Util.generateCsSQLException(SQLState.LOB_LOCATOR_INVALID);
}
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public ServiceMetaData getServiceMetaData(long connId, ByteBuffer token)
throws SnappyException {
try {
final ConnectionHolder connHolder = getValidConnection(connId, token);
EmbedDatabaseMetaData dmd = (EmbedDatabaseMetaData)connHolder
.getConnection().getMetaData();
ServiceMetaData metadata = new ServiceMetaData()
.setCatalogSeparator(dmd.getCatalogSeparator())
.setCatalogTerm(dmd.getCatalogTerm())
.setDateTimeFunctions(toList(dmd.getTimeDateFunctions()))
.setDefaultResultSetHoldabilityHoldCursorsOverCommit(dmd
.getResultSetHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT)
.setDefaultResultSetType(
snappydataConstants.RESULTSET_TYPE_FORWARD_ONLY)
.setDefaultTransactionIsolation(
snappydataConstants.DEFAULT_TRANSACTION_ISOLATION)
.setExtraNameCharacters(dmd.getExtraNameCharacters())
.setCatalogAtStart(dmd.isCatalogAtStart())
.setIdentifierQuote(dmd.getIdentifierQuoteString())
.setJdbcMajorVersion(dmd.getJDBCMajorVersion())
.setJdbcMinorVersion(dmd.getJDBCMinorVersion())
.setMaxBinaryLiteralLength(dmd.getMaxBinaryLiteralLength())
.setMaxCatalogNameLength(dmd.getMaxCatalogNameLength())
.setMaxCharLiteralLength(dmd.getMaxCharLiteralLength())
.setMaxColumnNameLength(dmd.getMaxColumnNameLength())
.setMaxColumnsInGroupBy(dmd.getMaxColumnsInGroupBy())
.setMaxColumnsInIndex(dmd.getMaxColumnsInIndex())
.setMaxColumnsInOrderBy(dmd.getMaxColumnsInOrderBy())
.setMaxColumnsInSelect(dmd.getMaxColumnsInSelect())
.setMaxColumnsInTable(dmd.getMaxColumnsInTable())
.setMaxConnections(dmd.getMaxConnections())
.setMaxCursorNameLength(dmd.getMaxCursorNameLength())
.setMaxIndexLength(dmd.getMaxIndexLength())
.setMaxOpenStatements(dmd.getMaxStatements())
.setMaxProcedureNameLength(dmd.getMaxProcedureNameLength())
.setMaxRowSize(dmd.getMaxRowSize())
.setMaxSchemaNameLength(dmd.getMaxSchemaNameLength())
.setMaxStatementLength(dmd.getMaxStatementLength())
.setMaxTableNameLength(dmd.getMaxTableNameLength())
.setMaxTableNamesInSelect(dmd.getMaxTablesInSelect())
.setMaxUserNameLength(dmd.getMaxUserNameLength())
.setNumericFunctions(toList(dmd.getNumericFunctions()))
.setProcedureTerm(dmd.getProcedureTerm())
.setProductMajorVersion(dmd.getDatabaseMajorVersion())
.setProductMinorVersion(dmd.getDatabaseMinorVersion())
.setProductName(dmd.getDatabaseProductName())
.setProductVersion(dmd.getDatabaseProductVersion())
.setSchemaTerm(dmd.getSchemaTerm())
.setSearchStringEscape(dmd.getSearchStringEscape())
.setSqlKeywords(toList(dmd.getSQLKeywords()))
.setSqlStateIsXOpen(
dmd.getSQLStateType() == DatabaseMetaData.sqlStateXOpen)
.setStringFunctions(toList(dmd.getStringFunctions()))
.setSystemFunctions(toList(dmd.getSystemFunctions()));
// populate the TransactionAttribute defaults
final EnumMap txDefaults = ThriftUtils
.newTransactionFlags();
final SystemProperties sysProps = SystemProperties.getServerInstance();
txDefaults.put(TransactionAttribute.AUTOCOMMIT,
snappydataConstants.DEFAULT_AUTOCOMMIT);
txDefaults.put(TransactionAttribute.DISABLE_BATCHING, sysProps
.getBoolean(Property.GFXD_DISABLE_TX_BATCHING,
!GfxdConstants.GFXD_TX_BATCHING_DEFAULT));
txDefaults.put(TransactionAttribute.READ_ONLY_CONNECTION,
dmd.isReadOnly());
txDefaults.put(TransactionAttribute.SYNC_COMMITS, sysProps.getBoolean(
Property.GFXD_TX_SYNC_COMMITS,
GfxdConstants.GFXD_TX_SYNC_COMMITS_DEFAULT));
txDefaults.put(TransactionAttribute.WAITING_MODE, sysProps.getBoolean(
Property.GFXD_ENABLE_TX_WAIT_MODE,
GfxdConstants.GFXD_TX_WAIT_MODE_DEFAULT));
metadata.setTransactionDefaults(txDefaults);
RowIdLifetime rowIdLifeTime;
switch (dmd.getRowIdLifetime()) {
case ROWID_VALID_OTHER:
rowIdLifeTime = RowIdLifetime.ROWID_VALID_OTHER;
break;
case ROWID_VALID_FOREVER:
rowIdLifeTime = RowIdLifetime.ROWID_VALID_FOREVER;
break;
case ROWID_VALID_SESSION:
rowIdLifeTime = RowIdLifetime.ROWID_VALID_SESSION;
break;
case ROWID_VALID_TRANSACTION:
rowIdLifeTime = RowIdLifetime.ROWID_VALID_TRANSACTION;
break;
case ROWID_UNSUPPORTED:
default:
rowIdLifeTime = RowIdLifetime.ROWID_UNSUPPORTED;
break;
}
metadata.setRowIdLifeTime(rowIdLifeTime);
// supported features
HashSet supportedFeatures = new HashSet<>();
if (dmd.supportsAlterTableWithAddColumn()) {
supportedFeatures.add(ServiceFeature.ALTER_TABLE_ADD_COLUMN);
}
if (dmd.supportsAlterTableWithDropColumn()) {
supportedFeatures.add(ServiceFeature.ALTER_TABLE_DROP_COLUMN);
}
if (dmd.supportsANSI92EntryLevelSQL()) {
supportedFeatures.add(ServiceFeature.SQL_GRAMMAR_ANSI92_ENTRY);
}
if (dmd.supportsANSI92FullSQL()) {
supportedFeatures.add(ServiceFeature.SQL_GRAMMAR_ANSI92_FULL);
}
if (dmd.supportsANSI92IntermediateSQL()) {
supportedFeatures.add(ServiceFeature.SQL_GRAMMAR_ANSI92_INTERMEDIATE);
}
if (dmd.supportsBatchUpdates()) {
supportedFeatures.add(ServiceFeature.BATCH_UPDATES);
}
if (dmd.supportsCatalogsInDataManipulation()) {
supportedFeatures.add(ServiceFeature.CATALOGS_IN_DMLS);
}
if (dmd.supportsCatalogsInIndexDefinitions()) {
supportedFeatures.add(ServiceFeature.CATALOGS_IN_INDEX_DEFS);
}
if (dmd.supportsCatalogsInPrivilegeDefinitions()) {
supportedFeatures.add(ServiceFeature.CATALOGS_IN_PRIVILEGE_DEFS);
}
if (dmd.supportsCatalogsInProcedureCalls()) {
supportedFeatures.add(ServiceFeature.CATALOGS_IN_PROCEDURE_CALLS);
}
if (dmd.supportsCatalogsInTableDefinitions()) {
supportedFeatures.add(ServiceFeature.CATALOGS_IN_TABLE_DEFS);
}
if (dmd.supportsColumnAliasing()) {
supportedFeatures.add(ServiceFeature.COLUMN_ALIASING);
}
if (dmd.supportsConvert()) {
supportedFeatures.add(ServiceFeature.CONVERT);
}
if (dmd.supportsCoreSQLGrammar()) {
supportedFeatures.add(ServiceFeature.SQL_GRAMMAR_CORE);
}
if (dmd.supportsCorrelatedSubqueries()) {
supportedFeatures.add(ServiceFeature.SUBQUERIES_CORRELATED);
}
if (dmd.supportsDataDefinitionAndDataManipulationTransactions()) {
supportedFeatures.add(ServiceFeature.TRANSACTIONS_BOTH_DMLS_AND_DDLS);
}
if (dmd.supportsDataManipulationTransactionsOnly()) {
supportedFeatures.add(ServiceFeature.TRANSACTIONS_DMLS_ONLY);
}
if (dmd.supportsDifferentTableCorrelationNames()) {
supportedFeatures.add(ServiceFeature.TABLE_CORRELATION_NAMES_DIFFERENT);
}
if (dmd.supportsExpressionsInOrderBy()) {
supportedFeatures.add(ServiceFeature.ORDER_BY_EXPRESSIONS);
}
if (dmd.supportsExtendedSQLGrammar()) {
supportedFeatures.add(ServiceFeature.SQL_GRAMMAR_EXTENDED);
}
if (dmd.supportsFullOuterJoins()) {
supportedFeatures.add(ServiceFeature.OUTER_JOINS_FULL);
}
if (dmd.supportsGetGeneratedKeys()) {
supportedFeatures.add(ServiceFeature.GENERATED_KEYS_RETRIEVAL);
}
if (dmd.supportsGroupBy()) {
supportedFeatures.add(ServiceFeature.GROUP_BY);
}
if (dmd.supportsGroupByBeyondSelect()) {
supportedFeatures.add(ServiceFeature.GROUP_BY_BEYOND_SELECT);
}
if (dmd.supportsGroupByUnrelated()) {
supportedFeatures.add(ServiceFeature.GROUP_BY_UNRELATED);
}
if (dmd.supportsIntegrityEnhancementFacility()) {
supportedFeatures.add(ServiceFeature.INTEGRITY_ENHANCEMENT);
}
if (dmd.supportsLikeEscapeClause()) {
supportedFeatures.add(ServiceFeature.LIKE_ESCAPE);
}
if (dmd.supportsLimitedOuterJoins()) {
supportedFeatures.add(ServiceFeature.OUTER_JOINS_LIMITED);
}
if (dmd.supportsMinimumSQLGrammar()) {
supportedFeatures.add(ServiceFeature.SQL_GRAMMAR_MINIMUM);
}
if (dmd.supportsMixedCaseIdentifiers()) {
supportedFeatures.add(ServiceFeature.MIXEDCASE_IDENTIFIERS);
}
if (dmd.supportsMixedCaseQuotedIdentifiers()) {
supportedFeatures.add(ServiceFeature.MIXEDCASE_QUOTED_IDENTIFIERS);
}
if (dmd.supportsMultipleOpenResults()) {
supportedFeatures.add(ServiceFeature.MULTIPLE_RESULTSETS);
}
if (dmd.supportsMultipleTransactions()) {
supportedFeatures.add(ServiceFeature.MULTIPLE_TRANSACTIONS);
}
if (dmd.supportsNamedParameters()) {
supportedFeatures.add(ServiceFeature.CALLABLE_NAMED_PARAMETERS);
}
if (dmd.supportsNonNullableColumns()) {
supportedFeatures.add(ServiceFeature.NON_NULLABLE_COLUMNS);
}
if (dmd.supportsOpenCursorsAcrossCommit()) {
supportedFeatures.add(ServiceFeature.OPEN_CURSORS_ACROSS_COMMIT);
}
if (dmd.supportsOpenCursorsAcrossRollback()) {
supportedFeatures.add(ServiceFeature.OPEN_CURSORS_ACROSS_ROLLBACK);
}
if (dmd.supportsOpenStatementsAcrossCommit()) {
supportedFeatures.add(ServiceFeature.OPEN_STATEMENTS_ACROSS_COMMIT);
}
if (dmd.supportsOpenStatementsAcrossRollback()) {
supportedFeatures.add(ServiceFeature.OPEN_STATEMENTS_ACROSS_ROLLBACK);
}
if (dmd.supportsOrderByUnrelated()) {
supportedFeatures.add(ServiceFeature.ORDER_BY_UNRELATED);
}
if (dmd.supportsOuterJoins()) {
supportedFeatures.add(ServiceFeature.OUTER_JOINS);
}
if (dmd.supportsPositionedDelete()) {
supportedFeatures.add(ServiceFeature.POSITIONED_DELETE);
}
if (dmd.supportsPositionedUpdate()) {
supportedFeatures.add(ServiceFeature.POSITIONED_UPDATE);
}
if (dmd.supportsResultSetHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT)) {
supportedFeatures
.add(ServiceFeature.RESULTSET_HOLDABILITY_CLOSE_CURSORS_AT_COMMIT);
}
if (dmd.supportsResultSetHoldability(
ResultSet.HOLD_CURSORS_OVER_COMMIT)) {
supportedFeatures
.add(ServiceFeature.RESULTSET_HOLDABILITY_HOLD_CURSORS_OVER_COMMIT);
}
if (dmd.supportsResultSetType(ResultSet.TYPE_FORWARD_ONLY)) {
supportedFeatures.add(ServiceFeature.RESULTSET_FORWARD_ONLY);
}
if (dmd.supportsResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE)) {
supportedFeatures.add(ServiceFeature.RESULTSET_SCROLL_INSENSITIVE);
}
if (dmd.supportsResultSetType(ResultSet.TYPE_SCROLL_SENSITIVE)) {
supportedFeatures.add(ServiceFeature.RESULTSET_SCROLL_SENSITIVE);
}
if (dmd.supportsSavepoints()) {
supportedFeatures.add(ServiceFeature.TRANSACTIONS_SAVEPOINTS);
}
if (dmd.supportsSchemasInDataManipulation()) {
supportedFeatures.add(ServiceFeature.SCHEMAS_IN_DMLS);
}
if (dmd.supportsSchemasInIndexDefinitions()) {
supportedFeatures.add(ServiceFeature.SCHEMAS_IN_INDEX_DEFS);
}
if (dmd.supportsSchemasInPrivilegeDefinitions()) {
supportedFeatures.add(ServiceFeature.SCHEMAS_IN_PRIVILEGE_DEFS);
}
if (dmd.supportsSchemasInProcedureCalls()) {
supportedFeatures.add(ServiceFeature.SCHEMAS_IN_PROCEDURE_CALLS);
}
if (dmd.supportsSchemasInTableDefinitions()) {
supportedFeatures.add(ServiceFeature.SCHEMAS_IN_TABLE_DEFS);
}
if (dmd.supportsSelectForUpdate()) {
supportedFeatures.add(ServiceFeature.SELECT_FOR_UPDATE);
}
if (dmd.supportsStatementPooling()) {
supportedFeatures.add(ServiceFeature.STATEMENT_POOLING);
}
if (dmd.supportsStoredFunctionsUsingCallSyntax()) {
supportedFeatures.add(ServiceFeature.STORED_FUNCTIONS_USING_CALL);
}
if (dmd.supportsStoredProcedures()) {
supportedFeatures.add(ServiceFeature.STORED_PROCEDURES);
}
if (dmd.supportsSubqueriesInComparisons()) {
supportedFeatures.add(ServiceFeature.SUBQUERIES_IN_COMPARISONS);
}
if (dmd.supportsSubqueriesInExists()) {
supportedFeatures.add(ServiceFeature.SUBQUERIES_IN_EXISTS);
}
if (dmd.supportsSubqueriesInIns()) {
supportedFeatures.add(ServiceFeature.SUBQUERIES_IN_INS);
}
if (dmd.supportsSubqueriesInQuantifieds()) {
supportedFeatures.add(ServiceFeature.SUBQUERIES_IN_QUANTIFIEDS);
}
if (dmd.supportsTableCorrelationNames()) {
supportedFeatures.add(ServiceFeature.TABLE_CORRELATION_NAMES);
}
if (dmd.supportsTransactions()) {
supportedFeatures.add(ServiceFeature.TRANSACTIONS);
}
if (dmd.supportsUnion()) {
supportedFeatures.add(ServiceFeature.UNION);
}
if (dmd.supportsUnionAll()) {
supportedFeatures.add(ServiceFeature.UNION_ALL);
}
if (dmd.allProceduresAreCallable()) {
supportedFeatures.add(ServiceFeature.ALL_PROCEDURES_CALLABLE);
}
if (dmd.allTablesAreSelectable()) {
supportedFeatures.add(ServiceFeature.ALL_TABLES_SELECTABLE);
}
if (dmd.autoCommitFailureClosesAllResultSets()) {
supportedFeatures
.add(ServiceFeature.AUTOCOMMIT_FAILURE_CLOSES_ALL_RESULTSETS);
}
if (dmd.dataDefinitionCausesTransactionCommit()) {
supportedFeatures.add(ServiceFeature.TRANSACTIONS_DDLS_IMPLICIT_COMMIT);
}
if (dmd.dataDefinitionIgnoredInTransactions()) {
supportedFeatures.add(ServiceFeature.TRANSACTIONS_DDLS_IGNORED);
}
if (dmd.doesMaxRowSizeIncludeBlobs()) {
supportedFeatures.add(ServiceFeature.MAX_ROWSIZE_INCLUDES_BLOBSIZE);
}
if (dmd.generatedKeyAlwaysReturned()) {
supportedFeatures.add(ServiceFeature.GENERATED_KEYS_ALWAYS_RETURNED);
}
if (dmd.locatorsUpdateCopy()) {
supportedFeatures.add(ServiceFeature.LOB_UPDATES_COPY);
}
if (dmd.nullPlusNonNullIsNull()) {
supportedFeatures.add(ServiceFeature.NULL_CONCAT_NON_NULL_IS_NULL);
}
if (dmd.nullsAreSortedAtEnd()) {
supportedFeatures.add(ServiceFeature.NULLS_SORTED_END);
}
if (dmd.nullsAreSortedAtStart()) {
supportedFeatures.add(ServiceFeature.NULLS_SORTED_START);
}
if (dmd.nullsAreSortedHigh()) {
supportedFeatures.add(ServiceFeature.NULLS_SORTED_HIGH);
}
if (dmd.nullsAreSortedLow()) {
supportedFeatures.add(ServiceFeature.NULLS_SORTED_LOW);
}
if (dmd.storesLowerCaseIdentifiers()) {
supportedFeatures.add(ServiceFeature.STORES_LOWERCASE_IDENTIFIERS);
}
if (dmd.storesLowerCaseQuotedIdentifiers()) {
supportedFeatures
.add(ServiceFeature.STORES_LOWERCASE_QUOTED_IDENTIFIERS);
}
if (dmd.storesMixedCaseIdentifiers()) {
supportedFeatures.add(ServiceFeature.STORES_MIXEDCASE_IDENTIFIERS);
}
if (dmd.storesMixedCaseQuotedIdentifiers()) {
supportedFeatures
.add(ServiceFeature.STORES_MIXEDCASE_QUOTED_IDENTIFIERS);
}
if (dmd.storesUpperCaseIdentifiers()) {
supportedFeatures.add(ServiceFeature.STORES_UPPERCASE_IDENTIFIERS);
}
if (dmd.storesUpperCaseQuotedIdentifiers()) {
supportedFeatures
.add(ServiceFeature.STORES_UPPERCASE_QUOTED_IDENTIFIERS);
}
if (dmd.usesLocalFilePerTable()) {
supportedFeatures.add(ServiceFeature.USES_LOCAL_FILE_PER_TABLE);
}
if (dmd.usesLocalFiles()) {
supportedFeatures.add(ServiceFeature.USES_LOCAL_FILES);
}
metadata.setSupportedFeatures(supportedFeatures);
// CONVERT support
final int[] allTypes = new int[]{Types.ARRAY, Types.BIGINT,
Types.BINARY, Types.BIT, Types.BLOB, Types.BOOLEAN, Types.CHAR,
Types.CLOB, Types.DATALINK, Types.DATE, Types.DECIMAL,
Types.DISTINCT, Types.DOUBLE, Types.FLOAT, Types.INTEGER,
Types.JAVA_OBJECT, Types.LONGNVARCHAR, Types.LONGVARBINARY,
Types.LONGVARCHAR, Types.NCHAR, Types.NCLOB, Types.NULL,
Types.NUMERIC, Types.NVARCHAR, Types.OTHER, Types.REAL, Types.REF,
Types.ROWID, Types.SMALLINT, Types.SQLXML, Types.STRUCT, Types.TIME,
Types.TIMESTAMP, Types.TINYINT, Types.VARBINARY, Types.VARCHAR,
JDBC40Translation.MAP, JDBC40Translation.JSON};
Map> convertMap =
new HashMap<>();
for (int fromType : allTypes) {
HashSet supportedConverts = new HashSet<>();
for (int toType : allTypes) {
if (dmd.supportsConvert(fromType, toType)) {
supportedConverts.add(Converters.getThriftSQLType(toType));
}
}
if (!supportedConverts.isEmpty()) {
convertMap.put(Converters.getThriftSQLType(fromType),
supportedConverts);
}
}
metadata.setSupportedCONVERT(convertMap);
// lastly features with parameters
HashMap> featureParameters =
new HashMap<>();
ArrayList supportedValues = new ArrayList<>(4);
final int[] isolationLevels = new int[]{Connection.TRANSACTION_NONE,
Connection.TRANSACTION_READ_UNCOMMITTED,
Connection.TRANSACTION_READ_COMMITTED,
Connection.TRANSACTION_REPEATABLE_READ,
Connection.TRANSACTION_SERIALIZABLE};
for (int isolationLevel : isolationLevels) {
if (dmd.supportsTransactionIsolationLevel(isolationLevel)) {
supportedValues.add((int)Converters
.getThriftTransactionIsolation(isolationLevel));
}
}
if (!supportedValues.isEmpty()) {
featureParameters.put(
ServiceFeatureParameterized.TRANSACTIONS_SUPPORT_ISOLATION,
supportedValues);
supportedValues = new ArrayList<>(4);
}
final int[] rsTypes = new int[]{ResultSet.TYPE_FORWARD_ONLY,
ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.TYPE_SCROLL_SENSITIVE};
for (int rsType : rsTypes) {
if (dmd.supportsResultSetType(rsType)) {
supportedValues.add(Converters.getThriftResultSetType(rsType));
}
}
if (!supportedValues.isEmpty()) {
featureParameters.put(
ServiceFeatureParameterized.RESULTSET_TYPE, supportedValues);
supportedValues = new ArrayList<>(4);
}
final int[] concurTypes = new int[]{ResultSet.CONCUR_READ_ONLY,
ResultSet.CONCUR_UPDATABLE};
for (int concurType : concurTypes) {
ServiceFeatureParameterized thriftConcurType =
(concurType == ResultSet.CONCUR_READ_ONLY
? ServiceFeatureParameterized.RESULTSET_CONCURRENCY_READ_ONLY
: ServiceFeatureParameterized.RESULTSET_CONCURRENCY_UPDATABLE);
for (int rsType : rsTypes) {
if (dmd.supportsResultSetConcurrency(rsType, concurType)) {
supportedValues.add(Converters.getThriftResultSetType(rsType));
}
}
if (!supportedValues.isEmpty()) {
featureParameters.put(thriftConcurType, supportedValues);
supportedValues = new ArrayList<>(4);
}
}
for (int rsType : rsTypes) {
if (dmd.ownUpdatesAreVisible(rsType)) {
supportedValues.add(Converters.getThriftResultSetType(rsType));
}
}
if (!supportedValues.isEmpty()) {
featureParameters.put(ServiceFeatureParameterized
.RESULTSET_OWN_UPDATES_VISIBLE, supportedValues);
supportedValues = new ArrayList<>(4);
}
for (int rsType : rsTypes) {
if (dmd.ownDeletesAreVisible(rsType)) {
supportedValues.add(Converters.getThriftResultSetType(rsType));
}
}
if (!supportedValues.isEmpty()) {
featureParameters.put(ServiceFeatureParameterized
.RESULTSET_OWN_DELETES_VISIBLE, supportedValues);
supportedValues = new ArrayList<>(4);
}
for (int rsType : rsTypes) {
if (dmd.ownInsertsAreVisible(rsType)) {
supportedValues.add(Converters.getThriftResultSetType(rsType));
}
}
if (!supportedValues.isEmpty()) {
featureParameters.put(ServiceFeatureParameterized
.RESULTSET_OWN_INSERTS_VISIBLE, supportedValues);
supportedValues = new ArrayList<>(4);
}
for (int rsType : rsTypes) {
if (dmd.othersUpdatesAreVisible(rsType)) {
supportedValues.add(Converters.getThriftResultSetType(rsType));
}
}
if (!supportedValues.isEmpty()) {
featureParameters.put(ServiceFeatureParameterized
.RESULTSET_OTHERS_UPDATES_VISIBLE, supportedValues);
supportedValues = new ArrayList<>(4);
}
for (int rsType : rsTypes) {
if (dmd.othersDeletesAreVisible(rsType)) {
supportedValues.add(Converters.getThriftResultSetType(rsType));
}
}
if (!supportedValues.isEmpty()) {
featureParameters.put(ServiceFeatureParameterized
.RESULTSET_OTHERS_DELETES_VISIBLE, supportedValues);
supportedValues = new ArrayList<>(4);
}
for (int rsType : rsTypes) {
if (dmd.othersInsertsAreVisible(rsType)) {
supportedValues.add(Converters.getThriftResultSetType(rsType));
}
}
if (!supportedValues.isEmpty()) {
featureParameters.put(ServiceFeatureParameterized
.RESULTSET_OTHERS_INSERTS_VISIBLE, supportedValues);
supportedValues = new ArrayList<>(4);
}
for (int rsType : rsTypes) {
if (dmd.updatesAreDetected(rsType)) {
supportedValues.add(Converters.getThriftResultSetType(rsType));
}
}
if (!supportedValues.isEmpty()) {
featureParameters.put(ServiceFeatureParameterized
.RESULTSET_UPDATES_DETECTED, supportedValues);
supportedValues = new ArrayList<>(4);
}
for (int rsType : rsTypes) {
if (dmd.deletesAreDetected(rsType)) {
supportedValues.add(Converters.getThriftResultSetType(rsType));
}
}
if (!supportedValues.isEmpty()) {
featureParameters.put(ServiceFeatureParameterized
.RESULTSET_DELETES_DETECTED, supportedValues);
supportedValues = new ArrayList<>(4);
}
for (int rsType : rsTypes) {
if (dmd.insertsAreDetected(rsType)) {
supportedValues.add(Converters.getThriftResultSetType(rsType));
}
}
if (!supportedValues.isEmpty()) {
featureParameters.put(ServiceFeatureParameterized
.RESULTSET_INSERTS_DETECTED, supportedValues);
}
metadata.setFeaturesWithParams(featureParameters);
return metadata;
} catch (Throwable t) {
checkSystemFailure(t);
throw SnappyException(t);
}
}
private ArrayList toList(String csv) {
final ArrayList strings = new ArrayList<>();
SharedUtils.splitCSV(csv, SharedUtils.stringAggregator, strings,
Boolean.TRUE);
return strings;
}
/**
* {@inheritDoc}
*/
@Override
public RowSet getSchemaMetaData(ServiceMetaDataCall schemaCall,
ServiceMetaDataArgs args) throws SnappyException {
ResultSet rs = null;
try {
final ConnectionHolder connHolder = getValidConnection(args.connId,
args.token);
EmbedDatabaseMetaData dmd = (EmbedDatabaseMetaData)connHolder
.getConnection().getMetaData();
boolean isODBC = args.getDriverType() == snappydataConstants.DRIVER_ODBC;
switch (schemaCall) {
case ATTRIBUTES:
rs = dmd.getAttributes(null, args.getSchema(), args.getTypeName(),
args.getAttributeName());
break;
case CATALOGS:
rs = dmd.getCatalogs();
break;
case CLIENTINFOPROPS:
rs = dmd.getClientInfoProperties();
break;
case COLUMNPRIVILEGES:
rs = dmd.getColumnPrivileges(null, args.getSchema(), args.getTable(),
args.getColumnName());
break;
case COLUMNS:
if (isODBC) {
rs = dmd.getColumnsForODBC(null, args.getSchema(), args.getTable(),
args.getColumnName());
} else {
rs = dmd.getColumns(null, args.getSchema(), args.getTable(),
args.getColumnName());
}
break;
case CROSSREFERENCE:
if (isODBC) {
rs = dmd.getCrossReferenceForODBC(null, args.getSchema(),
args.getTable(), null, args.getForeignSchema(),
args.getForeignTable());
} else {
rs = dmd.getCrossReference(null, args.getSchema(), args.getTable(),
null, args.getForeignSchema(), args.getForeignTable());
}
break;
case EXPORTEDKEYS:
rs = dmd.getExportedKeys(null, args.getSchema(), args.getTable());
break;
case FUNCTIONCOLUMNS:
rs = dmd.getFunctionColumns(null, args.getSchema(),
args.getFunctionName(), args.getColumnName());
break;
case FUNCTIONS:
rs = dmd.getFunctions(null, args.getSchema(), args.getFunctionName());
break;
case IMPORTEDKEYS:
rs = dmd.getImportedKeys(null, args.getSchema(), args.getTable());
break;
case PRIMARYKEYS:
rs = dmd.getPrimaryKeys(null, args.getSchema(), args.getTable());
break;
case PROCEDURECOLUMNS:
if (isODBC) {
rs = dmd.getProcedureColumnsForODBC(null, args.getSchema(),
args.getProcedureName(), args.getColumnName());
} else {
rs = dmd.getProcedureColumns(null, args.getSchema(),
args.getProcedureName(), args.getColumnName());
}
break;
case PROCEDURES:
if (isODBC) {
rs = dmd.getProceduresForODBC(null, args.getSchema(),
args.getProcedureName());
} else {
rs = dmd.getProcedures(null, args.getSchema(),
args.getProcedureName());
}
break;
case PSEUDOCOLUMNS:
rs = dmd.getPseudoColumns(null, args.getSchema(), args.getTable(),
args.getColumnName());
break;
case SCHEMAS:
if (args.getSchema() != null) {
rs = dmd.getSchemas(null, args.getSchema());
} else {
rs = dmd.getSchemas();
}
break;
case SUPERTABLES:
rs = dmd.getSuperTables(null, args.getSchema(), args.getTypeName());
break;
case SUPERTYPES:
rs = dmd.getSuperTypes(null, args.getSchema(), args.getTypeName());
break;
case TABLEPRIVILEGES:
rs = dmd.getTablePrivileges(null, args.getSchema(), args.getTable());
break;
case TABLES:
List tableTypes = args.getTableTypes();
String[] types = null;
if (tableTypes != null && !tableTypes.isEmpty()) {
types = tableTypes.toArray(new String[tableTypes.size()]);
}
// check for schema fetch with ODBC SQLTables('', '%', '')
if (isODBC && "%".equals(args.getSchema()) &&
args.getTable() != null && args.getTable().isEmpty()) {
rs = dmd.getTableSchemas();
} else {
rs = dmd.getTables(null, args.getSchema(), args.getTable(), types);
}
break;
case TABLETYPES:
rs = dmd.getTableTypes();
break;
case TYPEINFO:
if (isODBC) {
rs = dmd.getTypeInfoForODBC((short)(args.isSetTypeId() ? Converters
.getJdbcType(args.getTypeId()) : 0));
} else {
rs = dmd.getTypeInfo();
}
break;
case VERSIONCOLUMNS:
if (isODBC) {
rs = dmd.getVersionColumnsForODBC(null, args.getSchema(),
args.getTable());
} else {
rs = dmd.getVersionColumns(null, args.getSchema(), args.getTable());
}
break;
default:
throw internalException("unexpected metadata call: " + schemaCall);
}
return getRowSet(null, null, rs, INVALID_ID, null, args.connId, null, 0,
false, false, 0, connHolder, "getSchemaMetaData");
} catch (Throwable t) {
cleanupResultSet(rs);
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public RowSet getIndexInfo(ServiceMetaDataArgs args, boolean unique,
boolean approximate) throws SnappyException {
ResultSet rs = null;
try {
final ConnectionHolder connHolder = getValidConnection(args.connId,
args.token);
EmbedDatabaseMetaData dmd = (EmbedDatabaseMetaData)connHolder
.getConnection().getMetaData();
boolean isODBC = args.getDriverType() == snappydataConstants.DRIVER_ODBC;
rs = isODBC ? dmd.getIndexInfoForODBC(null, args.getSchema(),
args.getTable(), unique, approximate) : dmd.getIndexInfo(null,
args.getSchema(), args.getTable(), unique, approximate);
return getRowSet(null, null, rs, INVALID_ID, null, args.connId, null, 0,
false, false, 0, connHolder, "getIndexInfo");
} catch (Throwable t) {
cleanupResultSet(rs);
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public RowSet getUDTs(ServiceMetaDataArgs args, List types)
throws SnappyException {
ResultSet rs = null;
try {
final ConnectionHolder connHolder = getValidConnection(args.connId,
args.token);
EmbedDatabaseMetaData dmd = (EmbedDatabaseMetaData)connHolder
.getConnection().getMetaData();
int[] sqlTypes = null;
if (types != null && !types.isEmpty()) {
sqlTypes = new int[types.size()];
for (int index = 0; index < types.size(); index++) {
sqlTypes[index] = Converters.getJdbcType(types.get(index));
}
}
rs = dmd.getUDTs(null, args.getSchema(), args.getTypeName(), sqlTypes);
return getRowSet(null, null, rs, INVALID_ID, null, args.connId, null, 0,
false, false, 0, connHolder, "getUDTs");
} catch (Throwable t) {
cleanupResultSet(rs);
checkSystemFailure(t);
throw SnappyException(t);
}
}
/**
* {@inheritDoc}
*/
@Override
public RowSet getBestRowIdentifier(ServiceMetaDataArgs args, int scope,
boolean nullable) throws SnappyException {
ResultSet rs = null;
try {
final ConnectionHolder connHolder = getValidConnection(args.connId,
args.token);
EmbedDatabaseMetaData dmd = (EmbedDatabaseMetaData)connHolder
.getConnection().getMetaData();
boolean isODBC = args.getDriverType() == snappydataConstants.DRIVER_ODBC;
rs = isODBC ? dmd.getBestRowIdentifierForODBC(null, args.getSchema(),
args.getTable(), scope, nullable) : dmd.getBestRowIdentifier(null,
args.getSchema(), args.getTable(), scope, nullable);
return getRowSet(null, null, rs, INVALID_ID, null, args.connId, null, 0,
false, false, 0, connHolder, "getBestRowIdentifier");
} catch (Throwable t) {
cleanupResultSet(rs);
checkSystemFailure(t);
throw SnappyException(t);
}
}
}