All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pivotal.gemfirexd.internal.client.am.CallableLocatorProcedures Maven / Gradle / Ivy

/*
 
   Derby - Class com.pivotal.gemfirexd.internal.client.am.CallableLocatorProcedures
 
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You 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.
 
 */

/*
 * Changes for GemFireXD distributed data platform (some marked by "GemStone changes")
 *
 * Portions 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.
 */

package com.pivotal.gemfirexd.internal.client.am;

import com.pivotal.gemfirexd.internal.shared.common.error.ExceptionUtil;
import com.pivotal.gemfirexd.internal.shared.common.reference.SQLState;
// GemStone changes BEGIN
import com.pivotal.gemfirexd.internal.shared.common.sanity.SanityManager;
// GemStone changes END

/**
 * Contains the necessary methods to call the stored procedure that
 * operate on LOBs identified by locators.  An instance of this class
 * will be initialized with a Connection parameter and
 * all calls will be made on that connection.
 * 

* The class makes sure that each procedure call is only prepared once * per instance. Hence, it will keep references to * CallableStatement objects for procedures that have * been called through this instance. This makes it possible to * prepare each procedure call only once per Connection. *

* Since LOBs can not be parameters to stored procedures, the * framework should make sure that calls involving a byte[] or String * that does not fit in a VARCHAR (FOR BIT DATA), are split into * several calls each operating on a fragment of the LOB. * * @see Connection#locatorProcedureCall for an example of how to use * this class. */ public /* GemStone addition */ class CallableLocatorProcedures { //caches the information from a Stored Procedure //call as to whether locator support is available in //the server or not. boolean isLocatorSupportAvailable = true; // One member variable for each stored procedure that can be called. // Used to be able to only prepare each procedure call once per connection. private CallableStatement blobCreateLocatorCall; private CallableStatement blobReleaseLocatorCall; private CallableStatement blobGetPositionFromLocatorCall; private CallableStatement blobGetPositionFromBytesCall; private CallableStatement blobGetLengthCall; private CallableStatement blobGetBytesCall; private CallableStatement blobSetBytesCall; private CallableStatement blobTruncateCall; private CallableStatement clobCreateLocatorCall; private CallableStatement clobReleaseLocatorCall; private CallableStatement clobGetPositionFromStringCall; private CallableStatement clobGetPositionFromLocatorCall; private CallableStatement clobGetLengthCall; private CallableStatement clobGetSubStringCall; private CallableStatement clobSetStringCall; private CallableStatement clobTruncateCall; /** * The connection to be used when calling the stored procedures. */ private final Connection connection; /** * Max size of byte[] and String parameters to procedures */ private static final int VARCHAR_MAXWIDTH = 32672; //Constant representing an invalid locator value private static final int INVALID_LOCATOR = -1; // GemStone changes BEGIN /** * Stores the last server that failed that lead to failover * and causes the LOB methods to fail on the new server. */ public volatile String failedServer; // GemStone changes END /** * Create an instance to be used for calling locator-based stored * procedures. * * @param conn the connection to be used to prepare calls. */ CallableLocatorProcedures(Connection conn) { this.connection = conn; } /** * Allocates an empty BLOB on server and returns its locator. Any * subsequent operations on this BLOB value will be stored in temporary * space on the server. * * @throws com.pivotal.gemfirexd.internal.client.am.SqlException * @return locator that identifies the created BLOB. */ int blobCreateLocator() throws SqlException { //The information on whether the locator support //is available is cached in the boolean //isLocatorSupportAvailable. If this is false //we can return -1 if (!isLocatorSupportAvailable) { return INVALID_LOCATOR; } try { if (blobCreateLocatorCall == null || !blobCreateLocatorCall.openOnClient_) { blobCreateLocatorCall = connection.prepareCallX ("? = CALL SYSIBM.BLOBCREATELOCATOR()", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, connection.holdability()); blobCreateLocatorCall .registerOutParameterX(1, java.sql.Types.INTEGER); // Make sure this statement does not commit user transaction blobCreateLocatorCall.isAutoCommittableStatement_ = false; } blobCreateLocatorCall.executeX(); } catch(SqlException sqle) { //An exception has occurred while calling the stored procedure //used to create the locator value. //We verify to see if this SqlException has a SQLState of //42Y03(SQLState.LANG_NO_SUCH_METHOD_ALIAS) //(corresponding to the stored procedure not being found) //This means that locator support is not available. //This information is cached so that each time to determine //if locator support is available we do not have to make a //round trip to the server. if (sqle.getSQLState().compareTo (ExceptionUtil.getSQLStateFromIdentifier (SQLState.LANG_NO_SUCH_METHOD_ALIAS)) == 0) { isLocatorSupportAvailable = false; return INVALID_LOCATOR; } else { //The SqlException has not occurred because of the //stored procedure not being found. Hence we simply throw //it back. throw sqle; } } // GemStone changes BEGIN final int locator = blobCreateLocatorCall.getIntX(1); if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Created " + "new locator for BLOB" + locator + " for agent " + connection.agent_); } return locator; /* (original code) return blobCreateLocatorCall.getIntX(1); */ // GemStone changes END } /** * This method frees the BLOB and releases the resources that it * holds. (E.g., temporary space used to store this BLOB on the server.) * @param locator locator that designates the BLOB to be released. * @throws com.pivotal.gemfirexd.internal.client.am.SqlException */ void blobReleaseLocator(int locator) throws SqlException { if (blobReleaseLocatorCall == null || !blobReleaseLocatorCall.openOnClient_) { blobReleaseLocatorCall = connection.prepareCallX ("CALL SYSIBM.BLOBRELEASELOCATOR(?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); // Make sure this statement does not commit user transaction blobReleaseLocatorCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Releasing " + "BLOB using locator " + locator + " for agent " + connection.agent_); } // GemStone changes END blobReleaseLocatorCall.setIntX(1, locator); try { blobReleaseLocatorCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Blob.free()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } } /** * Retrieves the byte position in the BLOB value designated by this * locator at which pattern given by * searchLocator begins. The search begins at position * fromPosition. * @param locator locator that identifies the BLOB to be searched. * @param searchLocator locator designating the BLOB value for which to * search * @param fromPosition the position in the BLOB value * at which to begin searching; the first position is 1 * @throws com.pivotal.gemfirexd.internal.client.am.SqlException * @return the position at which the pattern begins, else -1 */ long blobGetPositionFromLocator(int locator, int searchLocator, long fromPosition) throws SqlException { if (blobGetPositionFromLocatorCall == null || !blobGetPositionFromLocatorCall.openOnClient_) { blobGetPositionFromLocatorCall = connection.prepareCallX ("? = CALL SYSIBM.BLOBGETPOSITIONFROMLOCATOR(?, ?, ?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); blobGetPositionFromLocatorCall .registerOutParameterX(1, java.sql.Types.BIGINT); // Make sure this statement does not commit user transaction blobGetPositionFromLocatorCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Getting " + "BLOB position using locator " + locator + " searchLocator=" + searchLocator + " fromPosition=" + fromPosition + " for agent " + connection.agent_); } // GemStone changes END blobGetPositionFromLocatorCall.setIntX(2, locator); blobGetPositionFromLocatorCall.setIntX(3, searchLocator); blobGetPositionFromLocatorCall.setLongX(4, fromPosition); try { blobGetPositionFromLocatorCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Blob.position()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } return blobGetPositionFromLocatorCall.getLongX(1); } /** * Retrieves the byte position at which the specified byte array * searchLiteral begins within the BLOB value * identified by locator. The search for * searchLiteral begins at position fromPosition. *

* If searchLiteral is longer than the maximum length of a * VARCHAR FOR BIT DATA, it will be split into smaller fragments, and * repeated procedure calls will be made to perform the entire search * * @param locator locator that identifies the BLOB to be searched. * @param searchLiteral the byte array for which to search * @param fromPosition the position at which to begin searching; the * first position is 1 * @throws com.pivotal.gemfirexd.internal.client.am.SqlException * @return the position at which the pattern appears, else -1 */ long blobGetPositionFromBytes(int locator, byte[] searchLiteral, long fromPosition) throws SqlException { long blobLength = -1; // Will be fetched from server if needed int patternLength = searchLiteral.length; // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Getting " + "BLOB position from bytes using locator " + locator + " fromPosition=" + fromPosition + " for agent " + connection.agent_); } // GemStone changes END // If searchLiteral needs to be partitioned, // we may have to try several start positions do { long foundAt = blobGetPositionFromBytes(locator, fromPosition, searchLiteral, 0, VARCHAR_MAXWIDTH); // If searchLiteral is longer than VARCHAR_MAXWIDTH, // we need to check the rest boolean tryAgain = false; if ((patternLength > VARCHAR_MAXWIDTH) && (foundAt > 0)) { // First part of searchLiteral matched, check rest int comparedSoFar = VARCHAR_MAXWIDTH; while (comparedSoFar < patternLength) { int numBytesThisRound = Math.min(patternLength - comparedSoFar, VARCHAR_MAXWIDTH); long pos = blobGetPositionFromBytes(locator, foundAt + comparedSoFar, searchLiteral, comparedSoFar, numBytesThisRound); if (pos != (foundAt + comparedSoFar)) { // This part did not match // Try to find a later match for the same prefix tryAgain = true; fromPosition = foundAt + 1; break; } comparedSoFar += numBytesThisRound; } } if (!tryAgain) return foundAt; // Need Blob length in order to determine when to stop if (blobLength < 0) { blobLength = blobGetLength(locator); } } while (fromPosition + patternLength <= blobLength); return -1; // No match } /** * Retrieves the byte position at which the specified part of the byte * array searchLiteral begins within the BLOB * value identified by locator. The search for * searchLiteral begins at position fromPosition. *

* This is a helper function used by blobGetPositionFromBytes(int, byte[], * long) for each call to the BLOBGETPOSITIONFROMBYTES procedure. * * @param locator locator that identifies the BLOB to be searched. * @param searchLiteral the byte array for which to search * @param fromPosition the position at which to begin searching; the * first position is 1 * @param offset the offset into the array searchLiteral at * which the pattern to search for starts * @param length the number of bytes from the array of bytes * searchLiteral to use for the pattern to search * for. It is assumed that this length is smaller than the maximum * size of a VARCHAR FOR BIT DATA column. Otherwise, an exception * will be thrown. * @throws com.pivotal.gemfirexd.internal.client.am.SqlException * @return the position at which the pattern appears, else -1 */ private long blobGetPositionFromBytes(int locator, long fromPosition, byte[] searchLiteral, int offset, int length) throws SqlException { if (blobGetPositionFromBytesCall == null || !blobGetPositionFromBytesCall.openOnClient_) { blobGetPositionFromBytesCall = connection.prepareCallX ("? = CALL SYSIBM.BLOBGETPOSITIONFROMBYTES(?, ?, ?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); blobGetPositionFromBytesCall .registerOutParameterX(1, java.sql.Types.BIGINT); // Make sure this statement does not commit user transaction blobGetPositionFromBytesCall.isAutoCommittableStatement_ = false; } byte[] bytesToBeCompared = searchLiteral; int numBytes = Math.min(searchLiteral.length - offset, length); if (numBytes != bytesToBeCompared.length) { // Need an array that contains just what is to be sent bytesToBeCompared = new byte[numBytes]; System.arraycopy(searchLiteral, offset, bytesToBeCompared, 0, numBytes); } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Getting " + "BLOB position from bytes2 using locator " + locator + " fromPosition=" + fromPosition + " offset=" + offset + " length=" + length + " for agent " + connection.agent_); } // GemStone changes END blobGetPositionFromBytesCall.setIntX(2, locator); blobGetPositionFromBytesCall.setBytesX(3, bytesToBeCompared); blobGetPositionFromBytesCall.setLongX(4, fromPosition); try { blobGetPositionFromBytesCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Blob.position()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } return blobGetPositionFromBytesCall.getLongX(1); } /** * Returns the number of bytes in the BLOB value * designated by this sourceLocator. * * @param sourceLocator locator that identifies the BLOB * @throws com.pivotal.gemfirexd.internal.client.am.SqlException * @return length of the BLOB in bytes */ long blobGetLength(int sourceLocator) throws SqlException { if (blobGetLengthCall == null || !blobGetLengthCall.openOnClient_) { blobGetLengthCall = connection.prepareCallX ("? = CALL SYSIBM.BLOBGETLENGTH(?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); blobGetLengthCall.registerOutParameterX(1, java.sql.Types.BIGINT); // Make sure this statement does not commit user transaction blobGetLengthCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Getting " + "BLOB length using locator " + sourceLocator + " for agent " + connection.agent_); } // GemStone changes END blobGetLengthCall.setIntX(2, sourceLocator); try { blobGetLengthCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Blob.getLength()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } return blobGetLengthCall.getLongX(1); } /** * Retrieves all or part of the BLOB value that is identified * by sourceLocator, as an array of bytes. This * byte array contains up to forLength * consecutive bytes starting at position fromPosition. *

* If forLength is larger than the maximum length of a VARCHAR * FOR BIT DATA, the reading of the BLOB will be split into repeated * procedure calls. * * @param sourceLocator locator that identifies the Blob to operate on * @param fromPosition the ordinal position of the first byte in the * BLOB value to be extracted; the first byte is at * position 1 * @param forLength the number of consecutive bytes to be copied; the value * for length must be 0 or greater. Specifying a length that goes * beyond the end of the BLOB (i.e., fromPosition + forLength * > blob.length()), will result in an error. * @throws com.pivotal.gemfirexd.internal.client.am.SqlException * @return a byte array containing up to forLength consecutive * bytes from the BLOB value designated by * sourceLocator, starting with the byte at position * fromPosition */ byte[] blobGetBytes(int sourceLocator, long fromPosition, int forLength) throws SqlException { if (forLength == 0) return new byte[0]; if (blobGetBytesCall == null || !blobGetBytesCall.openOnClient_) { blobGetBytesCall = connection.prepareCallX ("? = CALL SYSIBM.BLOBGETBYTES(?, ?, ?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); blobGetBytesCall.registerOutParameterX(1, java.sql.Types.VARBINARY); // Make sure this statement does not commit user transaction blobGetBytesCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Getting " + "BLOB bytes using locator " + sourceLocator + " fromPosition=" + fromPosition + " forLength=" + forLength + " for agent " + connection.agent_); } // GemStone changes END byte retVal[] = null; int gotSoFar = 0; while (gotSoFar < forLength) { blobGetBytesCall.setIntX(2, sourceLocator); blobGetBytesCall.setLongX(3, fromPosition + gotSoFar); blobGetBytesCall.setIntX(4, forLength - gotSoFar); try { blobGetBytesCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Blob.getBytes()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } byte[] result = blobGetBytesCall.getBytesX(1); if (gotSoFar == 0) { // First round of reading if (result.length == forLength) { // Got everything return result; } else { // Blob is probably greater than MAX VARCHAR length, need to // read in parts, create array for putting pieces together retVal = new byte[forLength]; } } // If not able to read more, stop if (result.length == 0) break; System.arraycopy(result, 0, retVal, gotSoFar, result.length); gotSoFar += result.length; } return retVal; } /** * Writes all or part of the given byte array to the * BLOB value designated by sourceLocator. * Writing starts at position fromPosition in the * BLOB value; forLength bytes from the given * byte array are written. If the end of the Blob value is * reached while writing the array of bytes, then the length of the * Blob value will be increased to accomodate the extra bytes. *

* If forLength is larger than the maximum length of a VARCHAR * FOR BIT DATA, the writing to the BLOB value will be split into repeated * procedure calls. * * @param sourceLocator locator that identifies the Blob to operated on * @param fromPosition the position in the BLOB value at which * to start writing; the first position is 1 * @param forLength the number of bytes to be written to the * BLOB value from the array of bytes * bytes. Specifying a length that goes beyond the end * of the BLOB (i.e., fromPosition + forLength > * blob.length(), will result in an error. * @param bytes the array of bytes to be written * @throws com.pivotal.gemfirexd.internal.client.am.SqlException */ void blobSetBytes(int sourceLocator, long fromPosition, int forLength, byte[] bytes) throws SqlException { if (blobSetBytesCall == null || !blobSetBytesCall.openOnClient_) { blobSetBytesCall = connection.prepareCallX ("CALL SYSIBM.BLOBSETBYTES(?, ?, ?, ?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); // Make sure this statement does not commit user transaction blobSetBytesCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Setting " + "BLOB bytes using locator " + sourceLocator + " fromPosition=" + fromPosition + " forLength=" + forLength + " for agent " + connection.agent_); } // GemStone changes END int sentSoFar = 0; byte[] bytesToBeSent = bytes; while (sentSoFar < forLength) { // Only send what can fit in a VARCHAR FOR BIT DATA parameter int numBytesThisRound = Math.min(forLength - sentSoFar, VARCHAR_MAXWIDTH); if (numBytesThisRound != bytesToBeSent.length) { // Need an array that contains just what is to be sent bytesToBeSent = new byte[numBytesThisRound]; } if (bytesToBeSent != bytes) { // Need to copy from original array System.arraycopy(bytes, sentSoFar, bytesToBeSent, 0, numBytesThisRound); } blobSetBytesCall.setIntX(1, sourceLocator); blobSetBytesCall.setLongX(2, fromPosition + sentSoFar); blobSetBytesCall.setIntX(3, numBytesThisRound); blobSetBytesCall.setBytesX(4, bytesToBeSent); try { blobSetBytesCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Blob.setBytes()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } sentSoFar += numBytesThisRound; } } /** * Truncates the BLOB value identified by * sourceLocator to be length bytes. *

* Note: If the value specified for length is greater * than the length+1 of the BLOB value then an * SqlException will be thrown. * * @param sourceLocator locator identifying the Blob to be truncated * @param length the length, in bytes, to which the BLOB value * should be truncated * @throws com.pivotal.gemfirexd.internal.client.am.SqlException */ void blobTruncate(int sourceLocator, long length) throws SqlException { if (blobTruncateCall == null || !blobTruncateCall.openOnClient_) { blobTruncateCall = connection.prepareCallX ("CALL SYSIBM.BLOBTRUNCATE(?, ?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); // Make sure this statement does not commit user transaction blobTruncateCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Truncate " + "BLOB using locator " + sourceLocator + " to length=" + length + " for agent " + connection.agent_); } // GemStone changes END blobTruncateCall.setIntX(1, sourceLocator); blobTruncateCall.setLongX(2, length); try { blobTruncateCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Blob.truncate()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } } /** * Allocates an empty CLOB on server and returns its locator. Any * subsequent operations on this CLOB value will be stored in temporary * space on the server. * * @throws com.pivotal.gemfirexd.internal.client.am.SqlException * @return locator that identifies the created CLOB. */ int clobCreateLocator() throws SqlException { //The information on whether the locator support //is available is cached in the boolean //isLocatorSupportAvailable. If this is false //we can return -1 if (!isLocatorSupportAvailable) { return INVALID_LOCATOR; } try { if (clobCreateLocatorCall == null || !clobCreateLocatorCall.openOnClient_) { clobCreateLocatorCall = connection.prepareCallX ("? = CALL SYSIBM.CLOBCREATELOCATOR()", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); clobCreateLocatorCall .registerOutParameterX(1, java.sql.Types.INTEGER); // Make sure this statement does not commit user transaction clobCreateLocatorCall.isAutoCommittableStatement_ = false; } clobCreateLocatorCall.executeX(); } catch(SqlException sqle) { //An exception has occurred while calling the stored procedure //used to create the locator value. //We verify to see if this SqlException has a SQLState of //42Y03(SQLState.LANG_NO_SUCH_METHOD_ALIAS) //(corresponding to the stored procedure not being found) //This means that locator support is not available. //This information is cached so that each time to determine //if locator support is available we do not have to make a //round trip to the server. if (sqle.getSQLState().compareTo (ExceptionUtil.getSQLStateFromIdentifier (SQLState.LANG_NO_SUCH_METHOD_ALIAS)) == 0) { isLocatorSupportAvailable = false; return INVALID_LOCATOR; } else { //The SqlException has not occurred because of the //stored procedure not being found. Hence we simply throw //it back. throw sqle; } } // GemStone changes BEGIN final int locator = clobCreateLocatorCall.getIntX(1); if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Created " + "new locator for CLOB" + locator + " for agent " + connection.agent_); } return locator; /* (original code) return clobCreateLocatorCall.getIntX(1); */ // GemStone changes END } /** * This method frees the CLOB and releases the resources that it * holds. (E.g., temporary space used to store this CLOB on the server.) * @param locator locator that designates the CLOB to be released. * @throws com.pivotal.gemfirexd.internal.client.am.SqlException */ void clobReleaseLocator(int locator) throws SqlException { if (clobReleaseLocatorCall == null || !clobReleaseLocatorCall.openOnClient_) { clobReleaseLocatorCall = connection.prepareCallX ("CALL SYSIBM.CLOBRELEASELOCATOR(?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); // Make sure this statement does not commit user transaction clobReleaseLocatorCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Releasing " + "CLOB using locator " + locator + " for agent " + connection.agent_); } // GemStone changes END clobReleaseLocatorCall.setIntX(1, locator); try { clobReleaseLocatorCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Clob.free()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } } /** * Retrieves the character position at which the specified substring * searchLiteral begins within the CLOB value * identified by locator. The search for * searchLiteral begins at position fromPosition. *

* If searchLiteral is longer than the maximum length of a * VARCHAR, it will be split into smaller fragments, and * repeated procedure calls will be made to perform the entire search * * @param locator locator that identifies the CLOB to be searched. * @param searchLiteral the substring for which to search * @param fromPosition the position at which to begin searching; the * first position is 1 * @throws com.pivotal.gemfirexd.internal.client.am.SqlException * @return the position at which the pattern appears, else -1 */ long clobGetPositionFromString(int locator, String searchLiteral, long fromPosition) throws SqlException { // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Getting " + "CLOB position from string using locator " + locator + " searchLiteral=" + searchLiteral + " fromPosition=" + fromPosition + " for agent " + connection.agent_); } // GemStone changes END long clobLength = -1; // Will be fetched from server if needed int patternLength = searchLiteral.length(); do { long foundAt = clobGetPositionFromString(locator, fromPosition, searchLiteral, 0, VARCHAR_MAXWIDTH); // If searchLiteral is longer than VARCHAR_MAXWIDTH, // we need to check the rest boolean tryAgain = false; if ((patternLength > VARCHAR_MAXWIDTH) && (foundAt > 0)) { // First part of searchLiteral matched, check rest int comparedSoFar = VARCHAR_MAXWIDTH; while (comparedSoFar < patternLength) { int numCharsThisRound = Math.min(patternLength - comparedSoFar, VARCHAR_MAXWIDTH); long pos = clobGetPositionFromString(locator, foundAt+comparedSoFar, searchLiteral, comparedSoFar, numCharsThisRound); if (pos != (foundAt + comparedSoFar)) { // This part did not match // Try to find a later match for the same prefix tryAgain = true; fromPosition = foundAt + 1; break; } comparedSoFar += numCharsThisRound; } } if (!tryAgain) return foundAt; // Need Clob length in order to determine when to stop if (clobLength < 0) { clobLength = clobGetLength(locator); } } while (fromPosition + patternLength <= clobLength); return -1; // No match } /** * * Retrieves the character position at which the specified part of the * substring searchLiteral begins within the CLOB * value identified by locator. The search for * searchLiteral begins at position fromPosition. *

* This is a helper function used by clobGetPositionFromString(int, * String, long) for each call to the CLOBGETPOSITIONFROMSTRING procedure. * * @param locator locator that identifies the CLOB to be searched. * @param searchLiteral the substring for which to search * @param fromPosition the position at which to begin searching; the * first position is 1 * @param offset the offset into the string searchLiteral at * which the pattern to search for starts * @param length the number of characters from the string * searchLiteral to use for the pattern to search * for. It is assumed that this length is smaller than the maximum * size of a VARCHAR column. Otherwise, an exception will be * thrown. * @throws com.pivotal.gemfirexd.internal.client.am.SqlException * @return the position at which the pattern appears, else -1 */ private long clobGetPositionFromString(int locator, long fromPosition, String searchLiteral, int offset, int length) throws SqlException { if (clobGetPositionFromStringCall == null || !clobGetPositionFromStringCall.openOnClient_) { clobGetPositionFromStringCall = connection.prepareCallX ("? = CALL SYSIBM.CLOBGETPOSITIONFROMSTRING(?, ?, ?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); clobGetPositionFromStringCall .registerOutParameterX(1, java.sql.Types.BIGINT); // Make sure this statement does not commit user transaction clobGetPositionFromStringCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Getting " + "CLOB position from string2 using locator " + locator + " searchLiteral=" + searchLiteral + " fromPosition=" + fromPosition + " offset=" + offset + " length=" + length + " for agent " + connection.agent_); } // GemStone changes END String stringToBeCompared = searchLiteral; int numChars = Math.min(searchLiteral.length() - offset, length); if (numChars != stringToBeCompared.length()) { // Need a String that contains just what is to be sent stringToBeCompared = searchLiteral.substring(offset, offset + numChars); } clobGetPositionFromStringCall.setIntX(2, locator); clobGetPositionFromStringCall.setStringX(3, stringToBeCompared); clobGetPositionFromStringCall.setLongX(4, fromPosition); try { clobGetPositionFromStringCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Clob.position()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } return clobGetPositionFromStringCall.getLongX(1); } /** * Retrieves the character position in the CLOB value designated by this * locator at which substring given by * searchLocator begins. The search begins at position * fromPosition. * @param locator locator that identifies the CLOB to be searched. * @param searchLocator locator designating the CLOB value for which to * search * @param fromPosition the position in the CLOB value * at which to begin searching; the first position is 1 * @throws com.pivotal.gemfirexd.internal.client.am.SqlException * @return the position at which the pattern begins, else -1 */ long clobGetPositionFromLocator(int locator, int searchLocator, long fromPosition) throws SqlException { if (clobGetPositionFromLocatorCall == null || !clobGetPositionFromLocatorCall.openOnClient_) { clobGetPositionFromLocatorCall = connection.prepareCallX ("? = CALL SYSIBM.CLOBGETPOSITIONFROMLOCATOR(?, ?, ?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); clobGetPositionFromLocatorCall .registerOutParameterX(1, java.sql.Types.BIGINT); // Make sure this statement does not commit user transaction clobGetPositionFromLocatorCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Getting " + "CLOB position using locator " + locator + " searchLocator=" + searchLocator + " fromPosition=" + fromPosition + " for agent " + connection.agent_); } // GemStone changes END clobGetPositionFromLocatorCall.setIntX(2, locator); clobGetPositionFromLocatorCall.setIntX(3, searchLocator); clobGetPositionFromLocatorCall.setLongX(4, fromPosition); try { clobGetPositionFromLocatorCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Clob.position()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } return clobGetPositionFromLocatorCall.getLongX(1); } /** * Returns the number of character in the CLOB value * designated by this sourceLocator. * * @param sourceLocator locator that identifies the CLOB * @throws com.pivotal.gemfirexd.internal.client.am.SqlException * @return length of the CLOB in characters */ long clobGetLength(int sourceLocator) throws SqlException { if (clobGetLengthCall == null || !clobGetLengthCall.openOnClient_) { clobGetLengthCall = connection.prepareCallX ("? = CALL SYSIBM.CLOBGETLENGTH(?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); clobGetLengthCall.registerOutParameterX(1, java.sql.Types.BIGINT); // Make sure this statement does not commit user transaction clobGetLengthCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Getting " + "CLOB length using locator " + sourceLocator + " for agent " + connection.agent_); } // GemStone changes END clobGetLengthCall.setIntX(2, sourceLocator); try { clobGetLengthCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Clob.getLength()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } return clobGetLengthCall.getLongX(1); } /** * Retrieves all or part of the CLOB value that is identified * by sourceLocator, as a String. This * String contains up to forLength consecutive * characters starting at position fromPosition. *

* If forLength is larger than the maximum length of a * VARCHAR, the reading of the CLOB will be split into repeated procedure * calls. * * @param sourceLocator locator that identifies the CLOB to operate on * @param fromPosition the ordinal position of the first character in the * CLOB value to be extracted; the first character is * at position 1 * @param forLength the number of consecutive characters to be copied; the * value for length must be 0 or greater. Specifying a length that * goes beyond the end of the CLOB (i.e., fromPosition + * forLength > clob.length(), will result in an error. * @throws com.pivotal.gemfirexd.internal.client.am.SqlException * @return a string containing up to forLength consecutive * characters from the CLOB value designated by * sourceLocator, starting with the character at * position fromPosition */ String clobGetSubString(int sourceLocator, long fromPosition, int forLength) throws SqlException { if (forLength == 0) return ""; if (clobGetSubStringCall == null || !clobGetSubStringCall.openOnClient_) { clobGetSubStringCall = connection.prepareCallX ("? = CALL SYSIBM.CLOBGETSUBSTRING(?, ?, ?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); clobGetSubStringCall .registerOutParameterX(1, java.sql.Types.VARCHAR); // Make sure this statement does not commit user transaction clobGetSubStringCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Getting " + "CLOB substring using locator " + sourceLocator + " fromPosition=" + fromPosition + " forLength=" + forLength + " for agent " + connection.agent_); } // GemStone changes END StringBuilder retVal = null; int gotSoFar = 0; while (gotSoFar < forLength) { clobGetSubStringCall.setIntX(2, sourceLocator); clobGetSubStringCall.setLongX(3, fromPosition + gotSoFar); clobGetSubStringCall.setIntX(4, forLength - gotSoFar); try { clobGetSubStringCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Clob.read()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } String result = clobGetSubStringCall.getStringX(1); if (gotSoFar == 0) { // First round of reading if (result.length() == forLength) { // Got everything return result; } else { // Clob is probably greater than MAX VARCHAR length, // need to read it in parts, // create StringBuilder for putting pieces together retVal = new StringBuilder(forLength); } } // If not able to read more, stop if (result.length() == 0) break; retVal.append(result); gotSoFar += result.length(); } return retVal.toString(); } /** * Writes all or part of the given String to the * CLOB value designated by sourceLocator. * Writing starts at position fromPosition in the * CLOB value; forLength characters from the * given string are written. If the end of the Clob value is * reached while writing the string, then the length of the * Clob value will be increased to accomodate the extra * characters. *

* If forLength is larger than the maximum length of a * VARCHAR, the writing to the CLOB value will be split into repeated * procedure calls. * * @param sourceLocator locator that identifies the Clob to operated on * @param fromPosition the position in the CLOB value at which * to start writing; the first position is 1 * @param forLength the number of characters to be written to the * CLOB value from the string string. * Specifying a length that goes beyond the end of the CLOB (i.e., * fromPosition + forLength > clob.length(), will * result in an error. * @param string the string to be written * @throws com.pivotal.gemfirexd.internal.client.am.SqlException */ void clobSetString(int sourceLocator, long fromPosition, int forLength, String string) throws SqlException { if (clobSetStringCall == null || !clobSetStringCall.openOnClient_) { clobSetStringCall = connection.prepareCallX ("CALL SYSIBM.CLOBSETSTRING(?, ?, ?, ?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); // Make sure this statement does not commit user transaction clobSetStringCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Setting " + "CLOB string using locator " + sourceLocator + " fromPosition=" + fromPosition + " forLength=" + forLength + " for agent " + connection.agent_); } // GemStone changes END int sentSoFar = 0; String stringToBeSent = string; while (sentSoFar < forLength) { // Only send what can fit in a VARCHAR parameter int numCharsThisRound = Math.min(forLength - sentSoFar, VARCHAR_MAXWIDTH); if (numCharsThisRound < string.length()) { // Need a String that contains just what is to be sent stringToBeSent = string.substring(sentSoFar, sentSoFar+numCharsThisRound); } clobSetStringCall.setIntX(1, sourceLocator); clobSetStringCall.setLongX(2, fromPosition + sentSoFar); clobSetStringCall.setIntX(3, numCharsThisRound); clobSetStringCall.setStringX(4, stringToBeSent); try { clobSetStringCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Clob.setString()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } sentSoFar += numCharsThisRound; } } /** * Truncates the CLOB value identified by * sourceLocator to be length characters. *

* Note: If the value specified for length is greater * than the length+1 of the CLOB value then an * SqlException will be thrown. * * @param sourceLocator locator identifying the Clob to be truncated * @param length the length, in characters, to which the CLOB * value should be truncated * @throws com.pivotal.gemfirexd.internal.client.am.SqlException */ void clobTruncate(int sourceLocator, long length) throws SqlException { if (clobTruncateCall == null || !clobTruncateCall.openOnClient_) { clobTruncateCall = connection.prepareCallX ("CALL SYSIBM.CLOBTRUNCATE(?, ?)", java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY, java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT); // Make sure this statement does not commit user transaction clobTruncateCall.isAutoCommittableStatement_ = false; } // GemStone changes BEGIN if (SanityManager.TraceClientHA) { SanityManager.DEBUG_PRINT(SanityManager.TRACE_CLIENT_HA, "Truncate " + "CLOB using locator " + sourceLocator + " to length=" + length + " for agent " + connection.agent_); } // GemStone changes END clobTruncateCall.setIntX(1, sourceLocator); clobTruncateCall.setLongX(2, length); try { clobTruncateCall.executeX(); } catch (SqlException sqle) { // GemStone changes BEGIN sqle = handleInvalidLocator(sqle, "Clob.truncate()"); /* (original code) sqle = handleInvalidLocator(sqle); */ // GemStone changes END throw sqle; } } /** * If the given exception indicates that locator was not valid, we * assume the locator has been garbage-collected due to * transaction commit, and wrap the exception in an exception with * SQL state LOB_OBJECT_INVALID. * @param sqle Exception to be checked * @return If sqle indicates that locator was * invalid, an SqlException with SQL state * LOB_OBJECT_INVALID. Otherwise, the * incoming exception is returned. */ private SqlException handleInvalidLocator(SqlException sqle, String method /* GemStone addition */) { SqlException ex = sqle; while (ex != null) { if (ex.getSQLState().compareTo (ExceptionUtil.getSQLStateFromIdentifier (SQLState.LOB_LOCATOR_INVALID)) == 0) { // GemStone changes BEGIN // if there has been a failover then wrap in GFXD_NODE_SHUTDOWN if (this.failedServer != null) { return new SqlException(this.connection.agent_.logWriter_, new ClientMessageId(SQLState.GFXD_NODE_SHUTDOWN), new Object[] { this.failedServer, method }, sqle); } // GemStone changes END return new SqlException(connection.agent_.logWriter_, new ClientMessageId(SQLState.LOB_OBJECT_INVALID), null, sqle); } ex = ex.getNextException(); } // LOB_LOCATOR_INVALID not found, return original exception return sqle; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy