0001: /*
0002:
0003: Derby - Class org.apache.derbyTesting.functionTests.tests.compatibility.JDBCDriverTest
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021: /**
0022: * <p>
0023: * This JUnit test verifies the compatibility of Derby clients and
0024: * servers across Derby version levels and supported VMs.
0025: * </p>
0026: *
0027: * @author Rick
0028: */package org.apache.derbyTesting.functionTests.tests.junitTests.compatibility;
0029:
0030: import java.io.*;
0031: import java.math.*;
0032: import java.sql.*;
0033: import java.util.*;
0034:
0035: import junit.framework.*;
0036:
0037: import org.apache.derbyTesting.functionTests.util.DerbyJUnitTest;
0038:
0039: public class JDBCDriverTest extends CompatibilitySuite {
0040: /////////////////////////////////////////////////////////////
0041: //
0042: // CONSTANTS
0043: //
0044: /////////////////////////////////////////////////////////////
0045:
0046: private static final String ALL_TYPES_TABLE = "allTypesTable";
0047: private static final String KEY_COLUMN = "keyCol";
0048:
0049: //
0050: // Data values to be stuffed into columns of ALL_TYPES_TABLE.
0051: //
0052: private static final byte[] SAMPLE_BYTES = new byte[] { (byte) 1,
0053: (byte) 2, (byte) 3, (byte) 4, (byte) 5 };
0054: private static final String SAMPLE_STRING = "hello";
0055:
0056: //
0057: // These funny constants are defined this way to make the salient
0058: // facts of the COERCIONS table leap out at you.
0059: //
0060: private static final boolean Y = true;
0061: private static final boolean _ = false;
0062:
0063: //
0064: // This table declares the datatypes supported by Derby and the earliest
0065: // versions of the Derby and the db2jcc client which support these
0066: // datatypes.
0067: //
0068: // If you add a type to this table, make sure you add a corresponding
0069: // column to the following row table. Also add a corresponding row to the
0070: // COERCIONS table.
0071: //
0072: private static final TypeDescriptor[] ALL_TYPES = {
0073: // 10.0 types
0074:
0075: new TypeDescriptor(Types.BIGINT, "bigint", IBM_2_4,
0076: DRB_10_0, VM_1_3),
0077: new TypeDescriptor(Types.BLOB, "blob", IBM_2_4, DRB_10_0,
0078: VM_1_3),
0079: new TypeDescriptor(Types.CHAR, "char(5)", IBM_2_4,
0080: DRB_10_0, VM_1_3),
0081: new TypeDescriptor(Types.BINARY, "char(5) for bit data",
0082: IBM_2_4, DRB_10_0, VM_1_3),
0083: new TypeDescriptor(Types.CLOB, "clob", IBM_2_4, DRB_10_0,
0084: VM_1_3),
0085: new TypeDescriptor(Types.DATE, "date", IBM_2_4, DRB_10_0,
0086: VM_1_3),
0087: new TypeDescriptor(Types.DECIMAL, "decimal", IBM_2_4,
0088: DRB_10_0, VM_1_3),
0089: new TypeDescriptor(Types.DOUBLE, "double", IBM_2_4,
0090: DRB_10_0, VM_1_3),
0091: new TypeDescriptor(Types.DOUBLE, "double precision",
0092: IBM_2_4, DRB_10_0, VM_1_3),
0093: new TypeDescriptor(Types.REAL, "float(23)", IBM_2_4,
0094: DRB_10_0, VM_1_3),
0095: new TypeDescriptor(Types.DOUBLE, "float", IBM_2_4,
0096: DRB_10_0, VM_1_3),
0097: new TypeDescriptor(Types.INTEGER, "integer", IBM_2_4,
0098: DRB_10_0, VM_1_3),
0099: new TypeDescriptor(Types.LONGVARCHAR, "long varchar",
0100: IBM_2_4, DRB_10_0, VM_1_3),
0101: new TypeDescriptor(Types.LONGVARBINARY,
0102: "long varchar for bit data", IBM_2_4, DRB_10_0,
0103: VM_1_3),
0104: new TypeDescriptor(Types.NUMERIC, "numeric", IBM_2_4,
0105: DRB_10_0, VM_1_3),
0106: new TypeDescriptor(Types.REAL, "real", IBM_2_4, DRB_10_0,
0107: VM_1_3),
0108: new TypeDescriptor(Types.SMALLINT, "smallint", IBM_2_4,
0109: DRB_10_0, VM_1_3),
0110: new TypeDescriptor(Types.TIME, "time", IBM_2_4, DRB_10_0,
0111: VM_1_3),
0112: new TypeDescriptor(Types.TIMESTAMP, "timestamp", IBM_2_4,
0113: DRB_10_0, VM_1_3),
0114: new TypeDescriptor(Types.VARCHAR, "varchar(5)", IBM_2_4,
0115: DRB_10_0, VM_1_3),
0116: new TypeDescriptor(Types.VARBINARY,
0117: "varchar(5) for bit data", IBM_2_4, DRB_10_0,
0118: VM_1_3), };
0119:
0120: //
0121: // This table needs to have the same number of entries as ALL_TYPES.
0122: // The testSanity() test case enforces this at run time.
0123: //
0124: private static final Object[] ROW_1 = {
0125: // 10.0 columns
0126:
0127: new Long(1L), new MyBlob(SAMPLE_BYTES), SAMPLE_STRING,
0128: SAMPLE_BYTES, new MyClob(SAMPLE_STRING),
0129: new java.sql.Date(1L), new BigDecimal(1.0),
0130: new Double(1.0), new Double(1.0), new Float((float) 1.0),
0131: new Double(1.0), new Integer(1), SAMPLE_STRING,
0132: SAMPLE_BYTES, new BigDecimal(1.0), new Float((float) 1.0),
0133: new Short((short) 1), new Time(1L), new Timestamp(1L),
0134: SAMPLE_STRING, SAMPLE_BYTES, };
0135:
0136: //
0137: // This table needs to have the same number of rows as ALL_TYPES.
0138: // Each row in this table needs to have the same number of columns as
0139: // rows in ALL_TYPES. The testSanity() test case enforces this at run time.
0140: // Note how the funny synonyms for true and false
0141: // make the salient facts of this table leap out at you.
0142: //
0143: // The ugly class name T_CN is an abbreviation which makes it possible to
0144: // squeeze this table onto a readable screen.
0145: //
0146: // Please read the introductory comment top-to-bottom. 'Y' means a coercion
0147: // is legal; '_' means it isn't.
0148: //
0149: private static final T_CN[] COERCIONS = {
0150: // B|B|C|B|C|D|D|D|R|I|L|L|N|R|S|T|T|V|V
0151: // I|L|H|I|L|A|E|O|E|N|O|O|U|E|M|I|I|A|A
0152: // G|O|A|N|O|T|C|U|A|T|N|N|M|A|A|M|M|R|R
0153: // I|B|R|A|B|E|I|B|L|E|G|G|E|L|L|E|E|C|B
0154: // N|-|-|R|-|-|M|L|-|G|V|V|R|-|L|-|S|H|I
0155: // T|-|-|Y|-|-|A|E|-|E|A|A|I|-|I|-|T|A|N
0156: // -|-|-|-|-|-|L|-|-|R|R|R|C|-|N|-|A|R|A
0157: // -|-|-|-|-|-|-|-|-|-|C|B|-|-|T|-|M|-|R
0158: // -|-|-|-|-|-|-|-|-|-|H|I|-|-|-|-|P|-|Y
0159: // -|-|-|-|-|-|-|-|-|-|A|N|-|-|-|-|-|-|-
0160: // -|-|-|-|-|-|-|-|-|-|R|A|-|-|-|-|-|-|-
0161: // -|-|-|-|-|-|-|-|-|-|-|R|-|-|-|-|-|-|-
0162: // -|-|-|-|-|-|-|-|-|-|-|Y|-|-|-|-|-|-|-
0163: new T_CN(Types.BIGINT, new boolean[] { Y, _, Y, _, _, _, _,
0164: Y, Y, Y, Y, _, Y, Y, Y, _, _, Y, _ }),
0165: new T_CN(Types.BLOB, new boolean[] { _, Y, _, _, _, _, _,
0166: _, _, _, _, _, _, _, _, _, _, _, _ }),
0167: new T_CN(Types.CHAR, new boolean[] { _, _, Y, _, _, _, _,
0168: _, _, _, Y, _, _, _, _, _, _, Y, _ }),
0169: new T_CN(Types.BINARY, new boolean[] { _, _, _, Y, _, _, _,
0170: _, _, _, _, Y, _, _, _, _, _, _, Y }),
0171: new T_CN(Types.CLOB, new boolean[] { _, _, _, _, Y, _, _,
0172: _, _, _, _, _, _, _, _, _, _, _, _ }),
0173: new T_CN(Types.DATE, new boolean[] { _, _, _, _, _, Y, _,
0174: _, _, _, _, _, _, _, _, _, _, _, _ }),
0175: new T_CN(Types.DECIMAL, new boolean[] { Y, _, _, _, _, _,
0176: Y, Y, Y, Y, Y, _, Y, Y, Y, _, _, Y, _ }),
0177: new T_CN(Types.DOUBLE, new boolean[] { Y, _, _, _, _, _, Y,
0178: Y, Y, Y, Y, _, Y, Y, Y, _, _, Y, _ }),
0179: new T_CN(Types.REAL, new boolean[] { Y, _, Y, _, _, _, Y,
0180: Y, Y, Y, Y, _, Y, Y, Y, _, _, Y, _ }),
0181: new T_CN(Types.INTEGER, new boolean[] { Y, _, Y, _, _, _,
0182: Y, Y, Y, Y, Y, _, Y, Y, Y, _, _, Y, _ }),
0183: new T_CN(Types.LONGVARCHAR, new boolean[] { _, _, Y, _, _,
0184: _, _, _, _, _, Y, _, _, _, _, _, _, Y, _ }),
0185: new T_CN(Types.LONGVARBINARY, new boolean[] { _, _, _, _,
0186: _, _, _, _, _, _, _, Y, _, _, _, _, _, _, Y }),
0187: new T_CN(Types.NUMERIC, new boolean[] { Y, _, Y, _, _, _,
0188: Y, Y, Y, Y, Y, _, Y, Y, Y, _, _, Y, _ }),
0189: new T_CN(Types.REAL, new boolean[] { Y, _, Y, _, _, _, Y,
0190: Y, Y, Y, Y, _, Y, Y, Y, _, _, Y, _ }),
0191: new T_CN(Types.SMALLINT, new boolean[] { Y, _, Y, _, _, _,
0192: Y, Y, Y, Y, Y, _, Y, Y, Y, _, _, Y, _ }),
0193: new T_CN(Types.TIME, new boolean[] { _, _, _, _, _, _, _,
0194: _, _, _, _, _, _, _, _, Y, _, _, _ }),
0195: new T_CN(Types.TIMESTAMP, new boolean[] { _, _, _, _, _, _,
0196: _, _, _, _, _, _, _, _, _, _, Y, _, _ }),
0197: new T_CN(Types.VARCHAR, new boolean[] { _, _, Y, _, _, _,
0198: _, _, _, _, Y, _, _, _, _, _, _, Y, _ }),
0199: new T_CN(Types.VARBINARY, new boolean[] { _, _, _, _, _, _,
0200: _, _, _, _, _, Y, _, _, _, _, _, _, Y }), };
0201:
0202: /////////////////////////////////////////////////////////////
0203: //
0204: // STATE
0205: //
0206: /////////////////////////////////////////////////////////////
0207:
0208: // map derby type name to type descriptor
0209: private static HashMap _types = new HashMap(); // maps Derby type names to TypeDescriptors
0210:
0211: // map jdbc type to index into COERCIONS
0212: private static HashMap _coercionIndex = new HashMap(); // maps jdbc types to legal coercions
0213:
0214: /////////////////////////////////////////////////////////////
0215: //
0216: // CONSTRUCTOR
0217: //
0218: /////////////////////////////////////////////////////////////
0219:
0220: public JDBCDriverTest() {
0221: }
0222:
0223: /////////////////////////////////////////////////////////////
0224: //
0225: // TEST ENTRY POINTS
0226: //
0227: /////////////////////////////////////////////////////////////
0228:
0229: /**
0230: * <p>
0231: * Sanity check the integrity of this test suite.
0232: * </p>
0233: */
0234: public void testSanity() {
0235: assertEquals("ALL_TYPES.length == ROW_1.length",
0236: ALL_TYPES.length, ROW_1.length);
0237:
0238: // make sure there we completely describe the coercibility of every jdbc type
0239: int coercionCount = COERCIONS.length;
0240: for (int i = 0; i < coercionCount; i++) {
0241: assertEquals("Coercion " + i, coercionCount, COERCIONS[i]
0242: .getCoercions().length);
0243: }
0244: }
0245:
0246: /**
0247: * <p>
0248: * Main test of jdbc drivers.
0249: * </p>
0250: */
0251: public void testJDBCDriver() throws Exception {
0252: Connection conn = getConnection();
0253:
0254: dropSchema(conn);
0255: createSchema(conn);
0256:
0257: datatypesTest(conn);
0258:
0259: close(conn);
0260: }
0261:
0262: /////////////////////////////////////////////////////////////
0263: //
0264: // TEST DATATYPES
0265: //
0266: /////////////////////////////////////////////////////////////
0267:
0268: //
0269: // Test that we can declare, insert, and select all datatypes that
0270: // are legal on the server. Test the metadata for these datatypes.
0271: //
0272: private void datatypesTest(Connection conn) throws Exception {
0273: TypeDescriptor[] types = ALL_TYPES;
0274: String tableName = ALL_TYPES_TABLE;
0275: Object[][] rows = new Object[][] { makeNullRow(types.length),
0276: ROW_1 };
0277:
0278: checkDBMetadata(conn, tableName);
0279: stuffTable(conn, tableName, types, rows);
0280: readTable(conn, tableName, types, rows, null);
0281: }
0282:
0283: //
0284: // Verify that we get the correct DatabaseMetaData for a table.
0285: //
0286: private void checkDBMetadata(Connection conn, String tableName)
0287: throws Exception {
0288: String normalizedSchema = DEFAULT_USER_NAME.toUpperCase();
0289: String normalizedTable = tableName.toUpperCase();
0290: DatabaseMetaData dbmd = conn.getMetaData();
0291:
0292: ResultSet rs = dbmd.getColumns(null, normalizedSchema,
0293: normalizedTable, "%");
0294:
0295: println("Pawing through database metadata for "
0296: + normalizedSchema + '.' + normalizedTable);
0297:
0298: while (rs.next()) {
0299: String columnName = rs.getString("COLUMN_NAME");
0300: int actualJdbcType = rs.getInt("DATA_TYPE");
0301: TypeDescriptor typeDesc = getType(columnName);
0302:
0303: if (columnName.equals(KEY_COLUMN)) {
0304: continue;
0305: }
0306:
0307: StringBuffer buffer = new StringBuffer();
0308:
0309: buffer.append("[ ");
0310: buffer.append(rs.getString("COLUMN_NAME"));
0311: buffer.append(",\t");
0312: buffer.append("type( " + rs.getInt("DATA_TYPE") + " ),\t");
0313: buffer.append(rs.getString("TYPE_NAME"));
0314: buffer.append(" ]");
0315:
0316: println(buffer.toString());
0317:
0318: assertEquals(columnName, ddmdTypeKludge(typeDesc
0319: .getJdbcType()), actualJdbcType);
0320: }
0321:
0322: close(rs);
0323: }
0324:
0325: //
0326: // Verify that we get the correct DatabaseMetaData for a procedure
0327: //
0328: private void checkProcMetadata(Connection conn, String procName,
0329: TypeDescriptor[] signature) throws Exception {
0330: String normalizedSchema = DEFAULT_USER_NAME.toUpperCase();
0331: String normalizedProc = procName.toUpperCase();
0332: DatabaseMetaData dbmd = conn.getMetaData();
0333:
0334: ResultSet rs = dbmd.getProcedureColumns(null, normalizedSchema,
0335: normalizedProc, "%");
0336:
0337: println("Pawing through database metadata for "
0338: + normalizedSchema + '.' + normalizedProc);
0339:
0340: while (rs.next()) {
0341: String columnName = rs.getString("COLUMN_NAME");
0342: int actualJdbcType = rs.getInt("DATA_TYPE");
0343: TypeDescriptor typeDesc = getType(signature, columnName);
0344:
0345: if (columnName.equals(KEY_COLUMN)) {
0346: continue;
0347: }
0348:
0349: StringBuffer buffer = new StringBuffer();
0350:
0351: buffer.append("[ ");
0352: buffer.append(rs.getString("COLUMN_NAME"));
0353: buffer.append(",\t");
0354: buffer.append("type( " + rs.getInt("DATA_TYPE") + " ),\t");
0355: buffer.append(rs.getString("TYPE_NAME"));
0356: buffer.append(" ]");
0357:
0358: println(buffer.toString());
0359:
0360: assertEquals(columnName, ddmdTypeKludge(typeDesc
0361: .getJdbcType()), actualJdbcType);
0362: }
0363:
0364: close(rs);
0365: }
0366:
0367: //
0368: // Stuff a table with rows
0369: //
0370: private void stuffTable(Connection conn, String tableName,
0371: TypeDescriptor[] types, Object[][] rows) throws Exception {
0372: PreparedStatement ps = makeInsert(conn, tableName, types);
0373: int rowCount = rows.length;
0374:
0375: for (int i = 0; i < rowCount; i++) {
0376: setRow(ps, i + 1, types, rows[i]);
0377: }
0378:
0379: close(ps);
0380: }
0381:
0382: private PreparedStatement makeInsert(Connection conn,
0383: String tableName, TypeDescriptor[] types) throws Exception {
0384: StringBuffer masterBuffer = new StringBuffer();
0385: StringBuffer columnBuffer = new StringBuffer();
0386: StringBuffer valuesBuffer = new StringBuffer();
0387: int columnNumber = 0;
0388: int valuesNumber = 0;
0389: int typeCount = types.length;
0390:
0391: beginColumnList(columnBuffer);
0392: beginColumnList(valuesBuffer);
0393:
0394: addColumn(columnBuffer, columnNumber++, doubleQuote(KEY_COLUMN));
0395: addColumn(valuesBuffer, valuesNumber++, "?");
0396:
0397: for (int i = 0; i < typeCount; i++) {
0398: TypeDescriptor type = types[i];
0399:
0400: if (getServerVersion().atLeast(type.getDerbyVersion())) {
0401: String typeName = type.getDerbyTypeName();
0402: String columnDesc = doubleQuote(typeName);
0403:
0404: addColumn(columnBuffer, columnNumber++, columnDesc);
0405: addColumn(valuesBuffer, valuesNumber++, "?");
0406: }
0407: }
0408:
0409: endColumnList(columnBuffer);
0410: endColumnList(valuesBuffer);
0411:
0412: masterBuffer.append("insert into " + tableName + "\n");
0413: masterBuffer.append(columnBuffer.toString());
0414: masterBuffer.append("values\n");
0415: masterBuffer.append(valuesBuffer.toString());
0416:
0417: PreparedStatement ps = prepare(conn, masterBuffer.toString());
0418:
0419: return ps;
0420: }
0421:
0422: //
0423: // Verify that we can select all legal datatypes in a table.
0424: //
0425: private void readTable(Connection conn, String tableName,
0426: TypeDescriptor[] types, Object[][] rows, List casts)
0427: throws Exception {
0428: PreparedStatement ps = readTableQuery(conn, tableName, types);
0429: ResultSet rs = ps.executeQuery();
0430:
0431: checkRSMD(rs);
0432: checkRows(rs, types, rows, casts);
0433:
0434: close(rs);
0435: close(ps);
0436: }
0437:
0438: //
0439: // Make the select query
0440: //
0441: private PreparedStatement readTableQuery(Connection conn,
0442: String tableName, TypeDescriptor[] types) throws Exception {
0443: StringBuffer buffer = new StringBuffer();
0444: int columnNumber = 0;
0445: int typeCount = types.length;
0446:
0447: buffer.append("select \n");
0448:
0449: addColumn(buffer, columnNumber++, doubleQuote(KEY_COLUMN));
0450:
0451: for (int i = 0; i < typeCount; i++) {
0452: TypeDescriptor type = types[i];
0453:
0454: if (getServerVersion().atLeast(type.getDerbyVersion())) {
0455: String typeName = type.getDerbyTypeName();
0456: String columnDesc = doubleQuote(typeName);
0457:
0458: addColumn(buffer, columnNumber++, columnDesc);
0459: }
0460: }
0461:
0462: buffer.append("\nfrom " + tableName + "\n");
0463: buffer.append("order by " + doubleQuote(KEY_COLUMN));
0464:
0465: PreparedStatement ps = prepare(conn, buffer.toString());
0466:
0467: return ps;
0468: }
0469:
0470: //
0471: // Verify that we get the correct ResultSetMetaData for all datatypes
0472: // which are legal on the server.
0473: //
0474: private void checkRSMD(ResultSet rs) throws Exception {
0475: ResultSetMetaData rsmd = rs.getMetaData();
0476: int columnCount = rsmd.getColumnCount();
0477: int firstTastyColumn = 0;
0478:
0479: println("ResultSetMetaData:\n");
0480:
0481: firstTastyColumn++; // skip uninteresting key column
0482:
0483: for (int i = firstTastyColumn; i < columnCount; i++) {
0484: StringBuffer buffer = new StringBuffer();
0485: int columnID = i + 1;
0486: String columnName = rsmd.getColumnName(columnID);
0487: TypeDescriptor typeDesc = getType(columnName);
0488: int expectedType = rsmdTypeKludge(typeDesc.getJdbcType());
0489: int actualType = rsmd.getColumnType(columnID);
0490:
0491: buffer.append("[ ");
0492: buffer.append(columnName);
0493: buffer.append(", type( ");
0494: buffer.append(actualType);
0495: buffer.append(" ), ");
0496: buffer.append(rsmd.getColumnTypeName(columnID));
0497: buffer.append(" ]\n");
0498:
0499: println(buffer.toString());
0500:
0501: assertEquals(columnName, expectedType, actualType);
0502: }
0503:
0504: }
0505:
0506: //
0507: // Verify that we select the values we
0508: // originally inserted into a table.
0509: //
0510: private void checkRows(ResultSet rs, TypeDescriptor[] types,
0511: Object[][] rows, List casts) throws Exception {
0512: int rowCount = rows.length;
0513:
0514: for (int i = 0; i < rowCount; i++) {
0515: rs.next();
0516: checkRow(rs, types, rows[i], casts);
0517: }
0518: }
0519:
0520: //
0521: // Verify that we select the values we
0522: // originally inserted into a row.
0523: //
0524: private void checkRow(ResultSet rs, TypeDescriptor[] types,
0525: Object[] row, List casts) throws Exception {
0526: int typeCount = types.length;
0527:
0528: for (int i = 0; i < typeCount; i++) {
0529: TypeDescriptor type = types[i];
0530:
0531: if (getServerVersion().atLeast(type.getDerbyVersion())) {
0532: String columnName = type.getDerbyTypeName();
0533: Object expectedValue = row[i];
0534: Object actualValue = getColumn(rs, columnName, type);
0535:
0536: println("Comparing column " + columnName + ": "
0537: + expectedValue + " to " + actualValue);
0538: compareObjects(columnName, expectedValue, actualValue);
0539:
0540: checkCoercions(rs, columnName, type, casts);
0541: }
0542: }
0543: }
0544:
0545: //
0546: // Verify all legal jdbc coercions of a data value.
0547: //
0548: private void checkCoercions(ResultSet rs, String columnName,
0549: TypeDescriptor type, List casts) throws Exception {
0550: T_CN coercionDesc = COERCIONS[getCoercionIndex(type
0551: .getJdbcType())];
0552: boolean[] coercions = coercionDesc.getCoercions();
0553: int count = coercions.length;
0554: int legalCoercions = 0;
0555:
0556: println("Checking coercions for " + columnName);
0557:
0558: for (int i = 0; i < count; i++) {
0559: if (coercions[i]) {
0560: legalCoercions++;
0561:
0562: int jdbcType = COERCIONS[i].getJdbcType();
0563: Object retval = getColumn(rs, columnName, jdbcType);
0564:
0565: if (casts != null) {
0566: casts.add(retval);
0567: }
0568:
0569: println("\t" + jdbcType + ":\t" + retval);
0570: }
0571:
0572: }
0573: // finally, try getObject()
0574:
0575: Object objval = rs.getObject(columnName);
0576: if (objval == null) {
0577: println("\tgetObject() = null");
0578: } else {
0579: StringBuffer buffer = new StringBuffer();
0580: buffer.append("\tgetObject() = ");
0581: buffer.append(objval.getClass().getName());
0582: buffer.append("( ");
0583: buffer.append(objval);
0584: buffer.append(" )");
0585: println(buffer.toString());
0586: }
0587: }
0588:
0589: //
0590: // This kludge compensates for the fact that the DRDA clients report
0591: // that NUMERIC columns are DECIMAL. See bug 584.
0592: //
0593: // In addition, booleans are handled oddly by down-rev clients.
0594: //
0595: private int rsmdTypeKludge(int originalJDbcType) {
0596: // The embedded client does the right thing.
0597: if (usingEmbeddedClient()
0598: && getServerVMVersion().atLeast(VM_1_4)) {
0599: return originalJDbcType;
0600: }
0601:
0602: switch (originalJDbcType) {
0603: //This kludge compensates for the fact that the DRDA clients report
0604: // that NUMERIC columns are DECIMAL. See bug 584.
0605: case Types.NUMERIC:
0606: if (usingEmbeddedClient()) {
0607: return originalJDbcType;
0608: } else {
0609: return Types.DECIMAL;
0610: }
0611:
0612: default:
0613: return originalJDbcType;
0614: }
0615: }
0616:
0617: //
0618: // This kludge compensates for the fact that servers return
0619: // different jdbc types depending on their vm.
0620: //
0621: private int ddmdTypeKludge(int originalJDbcType) {
0622: switch (originalJDbcType) {
0623: case JDBC_BOOLEAN:
0624: if (getServerVMVersion().atLeast(VM_1_4)) {
0625: return originalJDbcType;
0626: } else {
0627: return Types.BIT;
0628: }
0629:
0630: default:
0631: return originalJDbcType;
0632: }
0633: }
0634:
0635: //
0636: // Insert a row into the ALL_TYPES table. The row contains all datatypes
0637: // that are legal on the server.
0638: //
0639: private void setRow(PreparedStatement ps, int keyValue,
0640: TypeDescriptor[] types, Object[] row) throws Exception {
0641: int param = 1;
0642: int typeCount = types.length;
0643:
0644: ps.setInt(param++, keyValue);
0645:
0646: for (int i = 0; i < typeCount; i++) {
0647: TypeDescriptor type = types[i];
0648: Object value = row[i];
0649:
0650: if (getServerVersion().atLeast(type.getDerbyVersion())) {
0651: setParameter(ps, param++, type, value);
0652: }
0653: }
0654:
0655: ps.execute();
0656: }
0657:
0658: //
0659: // Add a row of null columns.
0660: //
0661: private Object[][] makeRows(Object[][] rows) {
0662: int count = rows.length;
0663: int columns = rows[0].length;
0664: Object[][] result = new Object[count + 1][];
0665: int idx = 0;
0666:
0667: result[idx++] = makeNullRow(columns);
0668:
0669: for (int i = 0; i < count; i++) {
0670: result[idx++] = rows[i];
0671: }
0672:
0673: return result;
0674: }
0675:
0676: private Object[] makeNullRow(int rowLength) {
0677: return new Object[rowLength];
0678: }
0679:
0680: //
0681: // Index the TypeDescriptors by Derby type name.
0682: //
0683: private void buildTypeMap() {
0684: int typeCount = ALL_TYPES.length;
0685:
0686: for (int i = 0; i < typeCount; i++) {
0687: putType(ALL_TYPES[i]);
0688: }
0689: }
0690:
0691: private void putType(TypeDescriptor type) {
0692: _types.put(type.getDerbyTypeName(), type);
0693: }
0694:
0695: //
0696: // Lookup TypeDescriptors by Derby type name.
0697: //
0698: private TypeDescriptor getType(String typeName) {
0699: if (_types.size() == 0) {
0700: buildTypeMap();
0701: }
0702:
0703: return (TypeDescriptor) _types.get(typeName);
0704: }
0705:
0706: //
0707: // Lookup TypeDescriptors by jdbc type
0708: //
0709: private TypeDescriptor getType(int jdbcType) {
0710: int count = ALL_TYPES.length;
0711:
0712: for (int i = 0; i < count; i++) {
0713: TypeDescriptor type = ALL_TYPES[i];
0714:
0715: if (type.getJdbcType() == jdbcType) {
0716: return type;
0717: }
0718: }
0719:
0720: return null;
0721: }
0722:
0723: //
0724: // Lookup TypeDescriptors by column name in an array of types
0725: //
0726: private TypeDescriptor getType(TypeDescriptor[] types,
0727: String typeName) {
0728: int count = types.length;
0729:
0730: for (int i = 0; i < count; i++) {
0731: TypeDescriptor type = types[i];
0732:
0733: if (type.getDerbyTypeName().equals(typeName)) {
0734: return type;
0735: }
0736: }
0737:
0738: return null;
0739: }
0740:
0741: //
0742: // Index legal coercions by jdbc type.
0743: //
0744: private void buildCoercionMap() {
0745: int count = COERCIONS.length;
0746:
0747: for (int i = 0; i < count; i++) {
0748: putCoercionIndex(i);
0749: }
0750: }
0751:
0752: private void putCoercionIndex(int index) {
0753: _coercionIndex.put(new Integer(COERCIONS[index].getJdbcType()),
0754: new Integer(index));
0755: }
0756:
0757: //
0758: // Lookup the legal coercions for a given jdbc type.
0759: //
0760: private int getCoercionIndex(int jdbcType) {
0761: if (_coercionIndex.size() == 0) {
0762: buildCoercionMap();
0763: }
0764:
0765: return ((Integer) _coercionIndex.get(new Integer(jdbcType)))
0766: .intValue();
0767: }
0768:
0769: /////////////////////////////////////////////////////////////
0770: //
0771: // MINIONS
0772: //
0773: /////////////////////////////////////////////////////////////
0774:
0775: ///////////////////
0776: //
0777: // TYPE MANAGEMENT
0778: //
0779: ///////////////////
0780:
0781: //////////////////
0782: //
0783: // SCHEMA MINIONS
0784: //
0785: //////////////////
0786:
0787: //
0788: // Create all the tables needed by our test cases.
0789: //
0790: private void createSchema(Connection conn) throws Exception {
0791: createTable(conn, ALL_TYPES_TABLE, ALL_TYPES);
0792: }
0793:
0794: //
0795: // Create a table modelling an array of datatypes.
0796: //
0797: private void createTable(Connection conn, String tableName,
0798: TypeDescriptor[] types) throws Exception {
0799: StringBuffer buffer = new StringBuffer();
0800: int columnNumber = 0;
0801: int typeCount = types.length;
0802:
0803: buffer.append("create table " + tableName + "\n");
0804: beginColumnList(buffer);
0805:
0806: addColumn(buffer, columnNumber++, doubleQuote(KEY_COLUMN)
0807: + "\tint");
0808:
0809: for (int i = 0; i < typeCount; i++) {
0810: TypeDescriptor type = types[i];
0811:
0812: if (getServerVersion().atLeast(type.getDerbyVersion())) {
0813: String typeName = type.getDerbyTypeName();
0814: String columnDesc = doubleQuote(typeName) + '\t'
0815: + typeName;
0816:
0817: addColumn(buffer, columnNumber++, columnDesc);
0818: }
0819: }
0820:
0821: endColumnList(buffer);
0822:
0823: PreparedStatement ps = prepare(conn, buffer.toString());
0824:
0825: ps.execute();
0826:
0827: close(ps);
0828: }
0829:
0830: //
0831: // Helper methods for declaring a table.
0832: //
0833: private void beginColumnList(StringBuffer buffer) {
0834: buffer.append("(\n");
0835: }
0836:
0837: private void endColumnList(StringBuffer buffer) {
0838: buffer.append("\n)\n");
0839: }
0840:
0841: private void addColumn(StringBuffer buffer, int columnNumber,
0842: String text) {
0843: if (columnNumber > 0) {
0844: buffer.append(",");
0845: }
0846:
0847: buffer.append("\n\t");
0848: buffer.append(text);
0849: }
0850:
0851: //
0852: // Drop the tables used by our test cases.
0853: //
0854: private void dropSchema(Connection conn) {
0855: dropTable(conn, ALL_TYPES_TABLE);
0856: }
0857:
0858: //
0859: // Logic for stuffing a data value into a column, given its type.
0860: //
0861: private void setParameter(PreparedStatement ps, int param,
0862: TypeDescriptor type, Object value) throws Exception {
0863: int jdbcType = type.getJdbcType();
0864:
0865: if (value != null) {
0866: setParameter(ps, param, jdbcType, value);
0867: return;
0868: } else if (clientSupports(type)) {
0869: ps.setNull(param, jdbcType);
0870:
0871: return;
0872: }
0873:
0874: // client does not support nulls of this type.
0875:
0876: fail("Unsupported Derby type: " + type.getDerbyTypeName());
0877: }
0878:
0879: //
0880: // Logic for verifying that a value was stuffed correctly.
0881: //
0882: private void checkParameter(ResultSet rs, int param, Object value)
0883: throws Exception {
0884: Object actualValue;
0885:
0886: if (value == null) {
0887: return;
0888: }
0889:
0890: println("Checking " + value.getClass().getName());
0891:
0892: if (value instanceof Boolean) {
0893: actualValue = new Boolean(rs.getBoolean(param));
0894: } else if (value instanceof Byte) {
0895: actualValue = new Byte(rs.getByte(param));
0896: } else if (value instanceof Short) {
0897: actualValue = new Short(rs.getShort(param));
0898: } else if (value instanceof Integer) {
0899: actualValue = new Integer(rs.getInt(param));
0900: } else if (value instanceof Long) {
0901: actualValue = new Long(rs.getLong(param));
0902: } else if (value instanceof Float) {
0903: actualValue = new Float(rs.getFloat(param));
0904: } else if (value instanceof Double) {
0905: actualValue = new Double(rs.getDouble(param));
0906: } else if (value instanceof String) {
0907: actualValue = rs.getString(param);
0908: } else if (value instanceof BigDecimal) {
0909: actualValue = rs.getBigDecimal(param);
0910: } else {
0911: actualValue = rs.getObject(param);
0912: }
0913:
0914: assertTrue(value.equals(actualValue));
0915: }
0916:
0917: // return true if the client supports this datatype
0918: private boolean clientSupports(TypeDescriptor type) {
0919: Version firstSupportedVersion;
0920:
0921: if (usingDB2Client()) {
0922: firstSupportedVersion = type.getDb2jccVersion();
0923: } else {
0924: firstSupportedVersion = type.getDerbyVersion();
0925: }
0926:
0927: if (firstSupportedVersion == null) {
0928: return false;
0929: } else {
0930: return getDriverVersion().atLeast(firstSupportedVersion);
0931: }
0932: }
0933:
0934: //
0935: // Get a data value from a column, given its type.
0936: //
0937: private Object getColumn(ResultSet rs, String columnName,
0938: TypeDescriptor type) throws Exception {
0939: int jdbcType = type.getJdbcType();
0940:
0941: return getColumn(rs, columnName, jdbcType);
0942: }
0943:
0944: //
0945: // Get a data value from a procedure's output arg, given its type.
0946: //
0947: private Object getOutArg(CallableStatement cs, int arg,
0948: TypeDescriptor type) throws Exception {
0949: int jdbcType = type.getJdbcType();
0950:
0951: return getOutArg(cs, arg, jdbcType);
0952: }
0953:
0954: //
0955: // SQL code generation minions
0956: //
0957: private String doubleQuote(String text) {
0958: return '"' + text + '"';
0959: }
0960:
0961: /////////////////////////////////////////////////////////////
0962: //
0963: // INNER CLASSES
0964: //
0965: /////////////////////////////////////////////////////////////
0966:
0967: /**
0968: * <p>
0969: * This helper class describes a legal datatype and the version of Derby
0970: * and db2jcc where the datatype first appears.
0971: * </p>
0972: */
0973: public static final class TypeDescriptor {
0974: private int _jdbcType;
0975: private String _derbyTypeName;
0976: private Version _db2jccVersion; // first db2jcc version which supports this type
0977: private Version _derbyVersion; // first derby version which supports this type
0978: private Version _vmVersion; // first vm (jdbc) version which supports this type
0979:
0980: public TypeDescriptor(int jdbcType, String derbyTypeName,
0981: Version db2jccVersion, Version derbyVersion,
0982: Version vmVersion) {
0983: _jdbcType = jdbcType;
0984: _derbyTypeName = derbyTypeName;
0985: _db2jccVersion = db2jccVersion;
0986: _derbyVersion = derbyVersion;
0987: _vmVersion = vmVersion;
0988: }
0989:
0990: public int getJdbcType() {
0991: return _jdbcType;
0992: }
0993:
0994: public String getDerbyTypeName() {
0995: return _derbyTypeName;
0996: }
0997:
0998: public Version getDb2jccVersion() {
0999: return _db2jccVersion;
1000: }
1001:
1002: public Version getDerbyVersion() {
1003: return _derbyVersion;
1004: }
1005:
1006: public Version getVMVersion() {
1007: return _vmVersion;
1008: }
1009: }
1010:
1011: /**
1012: * <p>
1013: * This helper class captures TypeCoercion logic. I have abbreviated it to
1014: * this ugly class name so that the COERCIONS table will fit on a readable screen.
1015: * </p>
1016: */
1017: public static final class T_CN {
1018: private int _jdbcType;
1019: private boolean[] _coercions;
1020:
1021: public T_CN(int jdbcType, boolean[] coercions) {
1022: _jdbcType = jdbcType;
1023: _coercions = coercions;
1024: }
1025:
1026: public int getJdbcType() {
1027: return _jdbcType;
1028: }
1029:
1030: public boolean[] getCoercions() {
1031: return _coercions;
1032: }
1033: }
1034:
1035: /**
1036: * <p>
1037: * A crude Blob implementation for datatype testing.
1038: * </p>
1039: */
1040: public static final class MyBlob implements Blob {
1041: private byte[] _bytes;
1042:
1043: public MyBlob(byte[] bytes) {
1044: _bytes = bytes;
1045: }
1046:
1047: public InputStream getBinaryStream() {
1048: return new ByteArrayInputStream(_bytes);
1049: }
1050:
1051: public byte[] getBytes(long position, int length) {
1052: return _bytes;
1053: }
1054:
1055: public long length() {
1056: return (long) _bytes.length;
1057: }
1058:
1059: public long position(Blob pattern, long start) {
1060: return 0L;
1061: }
1062:
1063: public long position(byte[] pattern, long start) {
1064: return 0L;
1065: }
1066:
1067: public boolean equals(Object other) {
1068: if (other == null) {
1069: return false;
1070: }
1071: if (!(other instanceof Blob)) {
1072: return false;
1073: }
1074:
1075: Blob that = (Blob) other;
1076:
1077: try {
1078: if (this .length() != that.length()) {
1079: return false;
1080: }
1081:
1082: InputStream this Stream = this .getBinaryStream();
1083: InputStream thatStream = that.getBinaryStream();
1084:
1085: while (true) {
1086: int nextByte = this Stream.read();
1087:
1088: if (nextByte < 0) {
1089: break;
1090: }
1091: if (nextByte != thatStream.read()) {
1092: return false;
1093: }
1094: }
1095: } catch (Exception e) {
1096: System.err.println(e.getMessage());
1097: e.printStackTrace();
1098: return false;
1099: }
1100:
1101: return true;
1102: }
1103:
1104: }
1105:
1106: /**
1107: * <p>
1108: * A crude Clob implementation for datatype testing.
1109: * </p>
1110: */
1111: public static final class MyClob implements Clob {
1112: private String _contents;
1113:
1114: public MyClob(String contents) {
1115: _contents = contents;
1116: }
1117:
1118: public InputStream getAsciiStream() {
1119: try {
1120: return new ByteArrayInputStream(_contents
1121: .getBytes("UTF-8"));
1122: } catch (Exception e) {
1123: return null;
1124: }
1125: }
1126:
1127: public Reader getCharacterStream() {
1128: return new CharArrayReader(_contents.toCharArray());
1129: }
1130:
1131: public String getSubString(long position, int length) {
1132: return _contents.substring((int) position, length);
1133: }
1134:
1135: public long length() {
1136: return (long) _contents.length();
1137: }
1138:
1139: public long position(Clob searchstr, long start) {
1140: return 0L;
1141: }
1142:
1143: public long position(String searchstr, long start) {
1144: return 0L;
1145: }
1146:
1147: public boolean equals(Object other) {
1148: if (other == null) {
1149: return false;
1150: }
1151: if (!(other instanceof Clob)) {
1152: return false;
1153: }
1154:
1155: Clob that = (Clob) other;
1156:
1157: try {
1158: if (this .length() != that.length()) {
1159: return false;
1160: }
1161:
1162: InputStream this Stream = this .getAsciiStream();
1163: InputStream thatStream = that.getAsciiStream();
1164:
1165: while (true) {
1166: int nextByte = this Stream.read();
1167:
1168: if (nextByte < 0) {
1169: break;
1170: }
1171: if (nextByte != thatStream.read()) {
1172: return false;
1173: }
1174: }
1175: } catch (Exception e) {
1176: System.err.println(e.getMessage());
1177: e.printStackTrace();
1178: return false;
1179: }
1180:
1181: return true;
1182: }
1183:
1184: }
1185:
1186: }
|