db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kahat...@apache.org
Subject svn commit: r1167017 - in /db/derby/code/trunk/java: build/org/apache/derbyBuild/ client/org/apache/derby/client/am/ drda/org/apache/derby/impl/drda/ shared/org/apache/derby/shared/common/reference/ testing/org/apache/derbyTesting/functionTests/tests/d...
Date Fri, 09 Sep 2011 06:47:30 GMT
Author: kahatlen
Date: Fri Sep  9 06:47:30 2011
New Revision: 1167017

URL: http://svn.apache.org/viewvc?rev=1167017&view=rev
Log:
DERBY-5236: Client driver silently truncates strings that exceed 32KB

Add a java.sql.DataTruncation warning to the result if a string was
truncated.

Modified:
    db/derby/code/trunk/java/build/org/apache/derbyBuild/MessageBundleTest.java
    db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlException.java
    db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlWarning.java
    db/derby/code/trunk/java/client/org/apache/derby/client/am/Sqlca.java
    db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java
    db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java
    db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAStatement.java
    db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/PrepareStatementTest.java

Modified: db/derby/code/trunk/java/build/org/apache/derbyBuild/MessageBundleTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/build/org/apache/derbyBuild/MessageBundleTest.java?rev=1167017&r1=1167016&r2=1167017&view=diff
==============================================================================
--- db/derby/code/trunk/java/build/org/apache/derbyBuild/MessageBundleTest.java (original)
+++ db/derby/code/trunk/java/build/org/apache/derbyBuild/MessageBundleTest.java Fri Sep  9
06:47:30 2011
@@ -30,8 +30,6 @@ import java.util.ResourceBundle;
 import java.util.Locale;
 import java.util.Iterator;
 
-import java.lang.Exception;
-
 
 /**
  * This class does everything we can to validate that the messages_en.properties
@@ -170,8 +168,11 @@ public class MessageBundleTest {
                 // messages.xml:
                 // XCL32: will never be exposed to users (see DERBY-1414)
                 // XSAX1: shared SQLState explains; not exposed to users. 
+                // 01004: automatically assigned by java.sql.DataTruncation and
+                //        never used to generate a message
                 if (!(sqlStateId.equalsIgnoreCase("XCL32.S") ||
-                    sqlStateId.equalsIgnoreCase("XSAX1"))) {
+                      sqlStateId.equalsIgnoreCase("XSAX1")   ||
+                      sqlStateId.equalsIgnoreCase("01004"))) {
                 // Don't fail out on the first one, we want to catch
                 // all of them.  Just note there was a failure and continue
                     failbuild=true;

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlException.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlException.java?rev=1167017&r1=1167016&r2=1167017&view=diff
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlException.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlException.java Fri Sep 
9 06:47:30 2011
@@ -22,7 +22,6 @@
 package org.apache.derby.client.am;
 
 import java.sql.SQLException;
-import java.util.TreeMap;
 
 import org.apache.derby.shared.common.i18n.MessageUtil;
 import org.apache.derby.shared.common.error.ExceptionUtil;
@@ -281,6 +280,13 @@ public class SqlException extends Except
         this.sqlca_ = sqlca;
         messageNumber_ = number;
         sqlstate_ = sqlca.getSqlState(number);
+
+        // If the SQLState indicates that this is a java.sql.DataTruncation
+        // type of exception, generate one right away.
+        if (SQLState.DATA_TRUNCATION_READ.equals(sqlstate_)) {
+            wrappedException_ = sqlca.getDataTruncation();
+        }
+
         int nextMsg = number + 1;
         if (chain && (sqlca.numberOfMessages() > nextMsg)) {
             setThrowable(new SqlException(sqlca, nextMsg, true));

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlWarning.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlWarning.java?rev=1167017&r1=1167016&r2=1167017&view=diff
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlWarning.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlWarning.java Fri Sep  9
06:47:30 2011
@@ -92,6 +92,10 @@ public class SqlWarning extends SqlExcep
      */
     public SQLWarning getSQLWarning()
     {
+        if (wrappedException_ != null) {
+            return (SQLWarning) wrappedException_;
+        }
+
         SQLWarning sqlw = new SQLWarning(getMessage(), getSQLState(), 
             getErrorCode());
 

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/Sqlca.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/Sqlca.java?rev=1167017&r1=1167016&r2=1167017&view=diff
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/Sqlca.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/Sqlca.java Fri Sep  9 06:47:30
2011
@@ -21,6 +21,7 @@
 
 package org.apache.derby.client.am;
 
+import java.sql.DataTruncation;
 import org.apache.derby.shared.common.reference.SQLState;
 import org.apache.derby.client.net.Typdef;
 
@@ -58,6 +59,9 @@ public abstract class Sqlca {
      */
     private static final String sqlErrmcDelimiter__ = "\u0014\u0014\u0014";
 
+    /** Token delimiter for SQLERRMC. */
+    private final static String SQLERRMC_TOKEN_DELIMITER = "\u0014";
+
     // JDK stack trace calls e.getMessage(), so we must set some state on the sqlca that
says return tokens only.
     private boolean returnTokensOnlyInMessageText_ = false;
 
@@ -365,6 +369,25 @@ public abstract class Sqlca {
         }
         return false;
     }
+
+    /**
+     * Get a {@code java.sql.DataTruncation} warning based on the information
+     * in this SQLCA.
+     *
+     * @return a {@code java.sql.DataTruncation} instance
+     */
+    DataTruncation getDataTruncation() {
+        // The network server has serialized all the parameters needed by
+        // the constructor in the SQLERRMC field.
+        String[] tokens = getSqlErrmc().split(SQLERRMC_TOKEN_DELIMITER);
+        return new DataTruncation(
+                Integer.parseInt(tokens[0]),                // index
+                Boolean.valueOf(tokens[1]).booleanValue(),  // parameter
+                Boolean.valueOf(tokens[2]).booleanValue(),  // read
+                Integer.parseInt(tokens[3]),                // dataSize
+                Integer.parseInt(tokens[4]));               // transferSize
+    }
+
     // ------------------- helper methods ----------------------------------------
 
     private void processSqlErrmcTokens(byte[] tokenBytes) {

Modified: db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java?rev=1167017&r1=1167016&r2=1167017&view=diff
==============================================================================
--- db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java (original)
+++ db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DDMWriter.java Fri Sep  9 06:47:30
2011
@@ -32,6 +32,7 @@ import java.nio.CharBuffer;
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.CoderResult;
 import java.nio.charset.CodingErrorAction;
+import java.sql.DataTruncation;
 import java.sql.SQLException;
 import java.util.Arrays;
 
@@ -182,6 +183,35 @@ class DDMWriter
 		this.dssTrace = dssTrace;
 	}
 
+    /**
+     * Get the current position in the output buffer.
+     * @return current position
+     */
+    protected int getBufferPosition() {
+        return buffer.position();
+    }
+
+    /**
+     * Change the current position in the output buffer.
+     * @param position new position
+     */
+    protected void setBufferPosition(int position) {
+        buffer.position(position);
+    }
+
+    /**
+     * Get a copy of a subsequence of the output buffer, starting at the
+     * specified position and ending at the current buffer position.
+     *
+     * @param startPos the position of the first byte to copy
+     * @return all bytes from {@code startPos} up to the current position
+     */
+    protected byte[] getBufferContents(int startPos) {
+        byte[] bytes = new byte[buffer.position() - startPos];
+        System.arraycopy(buffer.array(), startPos, bytes, 0, bytes.length);
+        return bytes;
+    }
+
 	/**
 	 * set protocol to CMD protocol
 	 */
@@ -1120,7 +1150,7 @@ class DDMWriter
 	 */
 	protected void writeLDString(String s) throws DRDAProtocolException
 	{
-		writeLDString(s,0);
+		writeLDString(s, 0, null, false);
 	}
 
 	/**
@@ -1191,9 +1221,15 @@ class DDMWriter
 	 *
 	 * @param s              value to be written with integer
 	 * @param index          column index to put in warning
+     * @param stmt           the executing statement (null if not invoked as
+     *                       part of statement execution)
+     * @param isParameter    true if the value written is for an output
+     *                       parameter in a procedure call
 	 * @exception DRDAProtocolException
 	 */
-	protected void writeLDString(String s, int index) throws DRDAProtocolException
+	protected void writeLDString(String s, int index, DRDAStatement stmt,
+                                 boolean isParameter)
+            throws DRDAProtocolException
 	{
 		// Position on which to write the length of the string (in bytes). The
 		// actual writing of the length is delayed until we have encoded the
@@ -1221,8 +1257,29 @@ class DDMWriter
             while (isContinuationByte(buffer.get(stringPos + byteLength))) {
                 byteLength--;
             }
+
+            // Check how many chars that were truncated.
+            int truncatedChars = 0;
+            for (int i = stringPos + byteLength; i < buffer.position(); i++) {
+                if (!isContinuationByte(buffer.get(i))) {
+                    truncatedChars++;
+                }
+            }
+
             // Set the buffer position right after the truncated string.
             buffer.position(stringPos + byteLength);
+
+            // If invoked as part of statement execution, add a warning about
+            // the string being truncated.
+            if (stmt != null) {
+                DataTruncation dt = new DataTruncation(
+                        index,
+                        isParameter,
+                        true,  // this is a warning for a read operation
+                        s.length(),                   // dataSize
+                        s.length() - truncatedChars); // transferSize
+                stmt.addTruncationWarning(dt);
+            }
         }
 
         // Go back and write the length in bytes.

Modified: db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java?rev=1167017&r1=1167016&r2=1167017&view=diff
==============================================================================
--- db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java (original)
+++ db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAConnThread.java Fri Sep 
9 06:47:30 2011
@@ -31,6 +31,7 @@ import java.io.UnsupportedEncodingExcept
 import java.math.BigDecimal;
 import java.sql.CallableStatement;
 import java.sql.Connection;
+import java.sql.DataTruncation;
 import java.sql.ParameterMetaData;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -6179,6 +6180,8 @@ class DRDAConnThread extends Thread {
 		
 		if (se instanceof EmbedSQLException  && ! severe)
 			sqlerrmc = buildTokenizedSqlerrmc(se);
+        else if (se instanceof DataTruncation)
+            sqlerrmc = buildDataTruncationSqlerrmc((DataTruncation) se);
 		else {
 			// If this is not an EmbedSQLException or is a severe excecption where
 			// we have no hope of succussfully calling the SYSIBM.SQLCAMESSAGE send
@@ -6264,6 +6267,21 @@ class DRDAConnThread extends Thread {
 		return sqlerrmc;
 	}
 
+    /**
+     * Build the SQLERRMC for a {@code java.sql.DataTruncation} warning.
+     * Serialize all the fields of the {@code DataTruncation} instance in the
+     * order in which they appear in the parameter list of the constructor.
+     *
+     * @param dt the {@code DataTruncation} instance to serialize
+     * @return the SQLERRMC string with all fields of the warning
+     */
+    private String buildDataTruncationSqlerrmc(DataTruncation dt) {
+        return dt.getIndex() + SQLERRMC_TOKEN_DELIMITER +
+               dt.getParameter() + SQLERRMC_TOKEN_DELIMITER +
+               dt.getRead() + SQLERRMC_TOKEN_DELIMITER +
+               dt.getDataSize() + SQLERRMC_TOKEN_DELIMITER +
+               dt.getTransferSize();
+    }
 	
 	/**
 	 * Write SQLCAXGRP
@@ -7082,11 +7100,19 @@ class DRDAConnThread extends Thread {
 				}
 			}
 
+            // Save the position where we start writing the warnings in case
+            // we need to add more warnings later.
+            final int sqlcagrpStart = writer.getBufferPosition();
+
 			if (sqlw == null)
                 writeSQLCAGRP(nullSQLState, 0, -1, -1);
 			else
 				writeSQLCAGRP(sqlw, sqlw.getErrorCode(), 1, -1);
 
+            // Save the position right after the warnings so we know where to
+            // insert more warnings later.
+            final int sqlcagrpEnd = writer.getBufferPosition();
+
 			// if we were asked not to return data, mark QRYDTA null; do not
 			// return yet, need to make rowCount right
 			// if the row has been deleted return QRYDTA null (delete hole)
@@ -7125,8 +7151,8 @@ class DRDAConnThread extends Thread {
 						case  DRDAConstants.DRDA_TYPE_NLOBCMIXED:
 							EXTDTAInputStream extdtaStream=  
 								EXTDTAInputStream.getEXTDTAStream(rs, i, drdaType);
-							writeFdocaVal(i,extdtaStream, drdaType,
-										  precision,scale,extdtaStream.isNull(),stmt);
+                            writeFdocaVal(i, extdtaStream, drdaType, precision,
+                                    scale, extdtaStream.isNull(), stmt, false);
 							break;
 						case DRDAConstants.DRDA_TYPE_NINTEGER:
 							int ival = rs.getInt(i);
@@ -7182,12 +7208,14 @@ class DRDAConnThread extends Thread {
 							if (SanityManager.DEBUG)
 								trace("====== writing char/varchar/mix :"+ valStr + ":");
 							writeFdocaVal(i, valStr, drdaType,
-										  precision,scale,rs.wasNull(),stmt);
+										  precision, scale, rs.wasNull(),
+                                          stmt, false);
 							break;
 						default:
                             val = getObjectForWriteFdoca(rs, i, drdaType);
                             writeFdocaVal(i, val, drdaType,
-										  precision,scale,rs.wasNull(),stmt);
+										  precision, scale, rs.wasNull(),
+                                          stmt, false);
 					}
 				}
 				else
@@ -7208,13 +7236,33 @@ class DRDAConnThread extends Thread {
                         val = getObjectForWriteFdoca(
                                 (CallableStatement) stmt.ps, i, drdaType);
 						valNull = (val == null);
-						writeFdocaVal(i,val,drdaType,precision, scale, valNull,stmt);
+						writeFdocaVal(i, val, drdaType, precision, scale,
+                                      valNull, stmt, true);
 					}
 					else
-						writeFdocaVal(i,null,drdaType,precision,scale,true,stmt);
+						writeFdocaVal(i, null, drdaType, precision, scale,
+                                      true, stmt, true);
 
 				}
 			}
+
+            DataTruncation truncated = stmt.getTruncationWarnings();
+            if (truncated != null) {
+                // Some of the data was truncated, so we need to add a
+                // truncation warning. Save a copy of the row data, then move
+                // back to the SQLCAGRP section and overwrite it with the new
+                // warnings, and finally re-insert the row data after the new
+                // SQLCAGRP section.
+                byte[] data = writer.getBufferContents(sqlcagrpEnd);
+                writer.setBufferPosition(sqlcagrpStart);
+                if (sqlw != null) {
+                    truncated.setNextWarning(sqlw);
+                }
+                writeSQLCAGRP(truncated, CodePoint.SVRCOD_WARNING, 1, -1);
+                writer.writeBytes(data);
+                stmt.clearTruncationWarnings();
+            }
+
 			// does all this fit in one QRYDTA
 			if (writer.getDSSLength() > blksize)
 			{
@@ -7875,6 +7923,7 @@ class DRDAConnThread extends Thread {
    * @param drdaType  FD:OCA DRDA Type from FdocaConstants
    * @param precision Precision
    * @param stmt       Statement being processed
+   * @param isParam   True when writing a value for a procedure parameter
    *
    * @exception DRDAProtocolException  
    * 
@@ -7885,8 +7934,8 @@ class DRDAConnThread extends Thread {
 
 	protected void writeFdocaVal(int index, Object val, int drdaType,
 								 int precision, int scale, boolean valNull,
-								 
-								 DRDAStatement stmt) throws DRDAProtocolException, SQLException
+								 DRDAStatement stmt, boolean isParam)
+            throws DRDAProtocolException, SQLException
 	{
 		writeNullability(drdaType,valNull);
 
@@ -7949,7 +7998,7 @@ class DRDAConnThread extends Thread {
 				case DRDAConstants.DRDA_TYPE_NLONGMIX:
 					//WriteLDString and generate warning if truncated
 					// which will be picked up by checkWarning()
-					writer.writeLDString(val.toString(), index);
+					writer.writeLDString(val.toString(), index, stmt, isParam);
 					break;
 				case DRDAConstants.DRDA_TYPE_NLOBBYTES:
 				case DRDAConstants.DRDA_TYPE_NLOBCMIXED:
@@ -7985,7 +8034,7 @@ class DRDAConnThread extends Thread {
 				default:
 					if (SanityManager.DEBUG) 
 						trace("ndrdaType is: "+ndrdaType);
-					writer.writeLDString(val.toString(), index);
+					writer.writeLDString(val.toString(), index, stmt, isParam);
 			}
 		}
 	}

Modified: db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAStatement.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAStatement.java?rev=1167017&r1=1167016&r2=1167017&view=diff
==============================================================================
--- db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAStatement.java (original)
+++ db/derby/code/trunk/java/drda/org/apache/derby/impl/drda/DRDAStatement.java Fri Sep  9
06:47:30 2011
@@ -39,6 +39,7 @@ import java.util.StringTokenizer;
 import java.util.Vector;
 import java.lang.reflect.Array;
 
+import java.sql.DataTruncation;
 import org.apache.derby.iapi.jdbc.BrokeredConnection;
 import org.apache.derby.iapi.jdbc.BrokeredPreparedStatement;
 import org.apache.derby.iapi.jdbc.EnginePreparedStatement;
@@ -102,6 +103,12 @@ class DRDAStatement
 	private ArrayList resultSetKeyList;  // ordered list of hash keys
 	private int numResultSets = 0;  
 
+    /**
+     * A chain of warnings indicating whether some of the data values returned
+     * by this statement had to be truncated before being sent to the client.
+     */
+    private DataTruncation truncationWarnings;
+
 	/** This class is used to keep track of the statement's parameters
 	 * as they are received from the client. It uses arrays to track
 	 * the DRDA type, the length in bytes and the externalness of each
@@ -343,6 +350,33 @@ class DRDAStatement
 		return stmt;
 	}
 
+    /**
+     * Add a warning about data having been truncated.
+     * @param w the warning to add
+     */
+    protected void addTruncationWarning(DataTruncation w) {
+        if (truncationWarnings == null) {
+            truncationWarnings = w;
+        } else {
+            truncationWarnings.setNextWarning(w);
+        }
+    }
+
+    /**
+     * Get the chain of truncation warnings added to this statement.
+     * @return chain of truncation warnings, possibly {@code null}
+     */
+    protected DataTruncation getTruncationWarnings() {
+        return truncationWarnings;
+    }
+
+    /**
+     * Clear the chain of truncation warnings for this statement.
+     */
+    protected void clearTruncationWarnings() {
+        truncationWarnings = null;
+    }
+
 	/**Set resultSet defaults to match 
 	 * the statement defaults sent on EXCSQLSTT
 	 * This might be overridden on OPNQRY or CNTQRY
@@ -1033,6 +1067,7 @@ class DRDAStatement
 		ps = null;
 		stmtPmeta = null;
 		stmt = null;
+        truncationWarnings = null;
 		rslsetflg = null;
 		procName = null;
 		outputTypes = null;
@@ -1071,6 +1106,7 @@ class DRDAStatement
 		outputTypes = null;
 		outputExpected = false;
 		stmt = null;
+        truncationWarnings = null;
 		
 		currentDrdaRs.reset();
 		resultSetTable = null;

Modified: db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java?rev=1167017&r1=1167016&r2=1167017&view=diff
==============================================================================
--- db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
(original)
+++ db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
Fri Sep  9 06:47:30 2011
@@ -668,6 +668,8 @@ public interface SQLState {
 
 	String LANG_TOO_MANY_DYNAMIC_RESULTS_RETURNED					   = "0100E";
 
+    // State used by java.sql.DataTruncation for truncation in read operations.
+    String DATA_TRUNCATION_READ = "01004";
 
 	// Invalid role specification: standard says class 0P, no subclass.
 	String ROLE_INVALID_SPECIFICATION                                  = "0P000";

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/PrepareStatementTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/PrepareStatementTest.java?rev=1167017&r1=1167016&r2=1167017&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/PrepareStatementTest.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/PrepareStatementTest.java
Fri Sep  9 06:47:30 2011
@@ -22,13 +22,17 @@
 package org.apache.derbyTesting.functionTests.tests.derbynet;
 
 import java.sql.BatchUpdateException;
+import java.sql.CallableStatement;
+import java.sql.DataTruncation;
 import java.sql.Date;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.SQLWarning;
 import java.sql.Statement;
 import java.sql.Time;
 import java.sql.Timestamp;
+import java.sql.Types;
 
 import java.math.BigDecimal;
 import java.io.ByteArrayInputStream;
@@ -1350,7 +1354,15 @@ public class PrepareStatementTest extend
         ps.setString(1, s3);
         if (usingDerbyNetClient()) {
             String expected = s3.substring(0, s3.length() - 1);
-            JDBC.assertSingleValueResultSet(ps.executeQuery(), expected);
+            ResultSet rs = ps.executeQuery();
+            assertTrue("Empty result", rs.next());
+            assertDataTruncation(
+                    new String[] { expected },
+                    new String[] { rs.getString(1) },
+                    1, false, true, s3.length(), expected.length(),
+                    rs.getWarnings());
+            assertFalse("Too many rows", rs.next());
+            rs.close();
         } else {
             // Embedded is OK. No truncation.
             JDBC.assertSingleValueResultSet(ps.executeQuery(), s3);
@@ -1363,7 +1375,15 @@ public class PrepareStatementTest extend
         ps.setString(1, s4);
         if (usingDerbyNetClient()) {
             String expected = s4.substring(0, s4.length() - 1);
-            JDBC.assertSingleValueResultSet(ps.executeQuery(), expected);
+            ResultSet rs = ps.executeQuery();
+            assertTrue("Empty result", rs.next());
+            assertDataTruncation(
+                    new String[] { expected },
+                    new String[] { rs.getString(1) },
+                    1, false, true, s4.length(), expected.length(),
+                    rs.getWarnings());
+            assertFalse("Too many rows", rs.next());
+            rs.close();
         } else {
             // Embedded is OK. No truncation.
             JDBC.assertSingleValueResultSet(ps.executeQuery(), s4);
@@ -1377,11 +1397,91 @@ public class PrepareStatementTest extend
         ps2.setString(2, s4);
         if (usingDerbyNetClient()) {
             String expected = s4.substring(0, s4.length() - 1);
-            String[][] expectedRow = {{expected, expected}};
-            JDBC.assertFullResultSet(ps2.executeQuery(), expectedRow);
+            ResultSet rs = ps2.executeQuery();
+            assertTrue("Empty result", rs.next());
+            // We should actually have received two warnings here, but the
+            // network client driver currently only supports one warning.
+            assertDataTruncation(
+                    new String[] { expected, expected },
+                    new String[] { rs.getString(1), rs.getString(2) },
+                    1, false, true, s4.length(), expected.length(),
+                    rs.getWarnings());
+            assertFalse("Too many rows", rs.next());
+            rs.close();
         } else {
             String[][] expectedRow = {{s4, s4}};
             JDBC.assertFullResultSet(ps2.executeQuery(), expectedRow);
         }
+
+        // Now test 64KB in a procedure call. Will be truncated to 64KB-1 on
+        // the network client.
+        Statement s = createStatement();
+        s.execute("create procedure derby_5236_proc" +
+                  "(in x varchar(32672), out y varchar(32672))" +
+                  "language java parameter style java external name '" +
+                  getClass().getName() + ".copyString'");
+        CallableStatement cs = prepareCall("call derby_5236_proc(?,?)");
+        cs.setString(1, s3);
+        cs.registerOutParameter(2, Types.VARCHAR);
+        cs.execute();
+        if (usingDerbyNetClient()) {
+            assertDataTruncation(
+                    new String[] { s3.substring(0, s3.length() - 1) },
+                    new String[] { cs.getString(2) },
+                    2, true, true, s3.length(), s3.length() - 1,
+                    cs.getWarnings());
+        } else {
+            assertEquals(s3, cs.getString(2));
+        }
+    }
+
+    /**
+     * Copy a string value from {@code in} to {@code out[0}}. Used as a
+     * stored procedure in {@link #testLongColumn()}.
+     *
+     * @param in stored procedure input parameter
+     * @param out stored procedure output parameter
+     */
+    public static void copyString(String in, String[] out) {
+        out[0] = in;
+    }
+
+    /**
+     * Assert that data returned from the server was truncated, and that the
+     * proper warning came with the result.
+     *
+     * @param expectedRow the expected values
+     * @param actualRow   the actual values returned
+     * @param index       the expected column/parameter index in the warning
+     * @param parameter   whether the values came from a procedure parameter
+     * @param read        whether the values came from a read operation
+     * @param dataSize    the expected full size of the truncated value
+     * @param transferSize the expected size of the value after truncation
+     * @param warning     the received warning
+     */
+    private static void assertDataTruncation(
+            String[] expectedRow, String[] actualRow,
+            int index, boolean parameter, boolean read,
+            int dataSize, int transferSize, SQLWarning warning) {
+        assertEquals("Wrong number of columns",
+                     expectedRow.length, actualRow.length);
+        assertNotNull("Expected data truncation warning", warning);
+        for (int i = 0; i < expectedRow.length; i++) {
+            assertEquals("column #" + (i + 1), expectedRow[i], actualRow[i]);
+
+            if (warning instanceof DataTruncation) {
+                DataTruncation dt = (DataTruncation) warning;
+                assertEquals("index", index, dt.getIndex());
+                assertEquals("parameter", parameter, dt.getParameter());
+                assertEquals("read", read, dt.getRead());
+                assertEquals("dataSize", dataSize, dt.getDataSize());
+                assertEquals("transferSize", transferSize, dt.getTransferSize());
+            } else {
+                fail("Unexpected warning", warning);
+            }
+
+            assertNull("Chained warnings not expected on network client",
+                       warning.getNextWarning());
+        }
     }
 }



Mime
View raw message